ci: split repo: make this repo frontend only again

This commit is contained in:
2024-03-09 00:08:09 +01:00
parent 5f56f34b4e
commit b132a5801b
308 changed files with 9 additions and 43867 deletions
+13
View File
@@ -0,0 +1,13 @@
#root {
min-height: 100vh;
display: flex;
flex-direction: column;
}
footer {
margin-top: auto;
}
.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
+7
View File
@@ -0,0 +1,7 @@
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App faro={undefined}/>);
});
+96
View File
@@ -0,0 +1,96 @@
import * as React from 'react';
import './App.css';
import {Faro} from "@grafana/faro-web-sdk";
import {createBrowserRouter, redirect, RouterProvider} from "react-router-dom";
import CreateCharacter from "./first-edition/pages/CreateCharacter";
import CharacterList from "./shared/pages/CharacterList";
import ErrorBoundary from "./shared/ErrorBoundary";
import Header from "./shared/Header";
import Footer from "./shared/Footer";
import {useState} from "react";
import {UserContext, userDefaults} from './shared/contexts/UserContext';
import CreateCharacter2E from "./second-edition/pages/CreateCharacter2E";
function App(props: {faro?: Faro}) {
const [fetchedUser, setFetchedUser] = useState(userDefaults)
if (fetchedUser.state === "not-started") {
setFetchedUser({...fetchedUser, state: "loading"})
fetch(`${window.location.origin}/api/User/me`)
.then(response => {
if (response.ok) {
return response.text();
}
console.log(response)
})
.then(userNameResponse => {
const userName = userNameResponse && userNameResponse.length > 0 ? userNameResponse : null;
setFetchedUser({
data: userName,
state: "finished"
});
})
.catch(() => {
setFetchedUser({
data: null,
state: "finished"
})
})
}
const router = createBrowserRouter([
{
path: "/",
loader: () => fetchedUser.data == null ? redirect("/2e/karakter") : redirect("/karaktereim")
},
{
path: "/karaktereim",
element: <CharacterList />,
loader: () => fetch(`${window.location.origin}/api/Character`),
ErrorBoundary: ErrorBoundary,
},
{
path: "/1e",
loader: () => redirect("/1e/karakter")
},
{
path: "/1e/karakter",
element: <CreateCharacter faro={props.faro} />,
ErrorBoundary: ErrorBoundary,
},
{
path: "/1e/karakter/:id",
element: <CreateCharacter faro={props.faro}/>,
loader: args => fetch(`${window.location.origin}/api/Character1E/${args.params.id}`),
ErrorBoundary: ErrorBoundary,
},
{
path: "/2e",
loader: () => redirect("/2e/karakter")
},
{
path: "/2e/karakter",
element: <CreateCharacter2E faro={props.faro}/>,
ErrorBoundary: ErrorBoundary,
},
{
path: "/2e/karakter/:id",
element: <CreateCharacter2E faro={props.faro}/>,
loader: args => fetch(`${window.location.origin}/api/Character2E/${args.params.id}`),
ErrorBoundary: ErrorBoundary,
},
]);
return (
<UserContext.Provider value={fetchedUser}>
<Header/>
<div className='container'>
<RouterProvider router={router}/>
</div>
<Footer />
</UserContext.Provider>
);
}
export default App;
@@ -0,0 +1,32 @@
import {KarakterInputs} from "../domain-models/karakter";
import axios from "axios";
export async function StoreNewCharacter(karakter: KarakterInputs, isPublic: boolean = false) {
let response = await axios.post(`${window.location.origin}/api/Character1E/`, karakter, {
withCredentials: true,
params: {
isPublic,
}
})
if (response.status < 300){
return response.data as string
}
else {
throw Error(response.statusText)
}
}
export async function UpdateCharacter(id: string, karakter: KarakterInputs, isPublic: boolean = false) {
let response = await axios.post(`${window.location.origin}/api/Character1E/${id}`, karakter, {
withCredentials: true,
params: {
isPublic,
}
})
if (response.status < 300){
return response.data as string
}
else {
throw Error(response.statusText)
}
}
@@ -0,0 +1,88 @@
import React from "react";
import {KarakterInputs} from "../domain-models/karakter";
import {SignedNumberToText} from "../../shared/components/Helpers";
import {Mentok, MentoTipus, MentoTipusLabel, MentokTotal} from "../domain-models/mentok";
import {BaseHP, HP} from "../domain-models/masodlagos_ertekek";
import {TamadasBonusz} from "../domain-models/tamadas_bonusz";
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<KarakterInputs, 'osztaly' | 'tulajdonsagok' | 'hpRolls'>
szint: number
changeRolledHP: (hp: number) => void,
}) {
const {karakter, szint, changeRolledHP} = props
const regiMentok = MentokTotal({...karakter, szint: szint - 1})
const ujMentok = MentokTotal({...karakter, szint: szint})
const regiTB = TamadasBonusz(karakter.osztaly, szint-1)
const ujTB = TamadasBonusz(karakter.osztaly, 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(karakter.osztaly)}){' '}</label>
<div className='col-sm-2 col'>
<input
className='form-control'
maxLength={2}
type='number'
value={karakter.hpRolls[szint-2] || 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, hpRolls: karakter.hpRolls.slice(0, szint-1)})}</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,43 @@
import React from "react";
import {Faj, FajDescription, FajLabel, FajSpecials} from "../domain-models/faj";
function FajSelector(props: { faj: Faj, changeFaj: (val: Faj) => void }) {
const {faj, changeFaj} = props
return <>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Faj</label>
<select className='col form-select' value={faj} onChange={e => changeFaj(e.target.value as Faj)}>
<option key={Faj.Ember} value={Faj.Ember}>{FajLabel(Faj.Ember)}</option>
<optgroup label='más emberi népek'>
<option key={Faj.Amazon} value={Faj.Amazon}>{FajLabel(Faj.Amazon)}</option>
<option key={Faj.Birodalmi} value={Faj.Birodalmi}>{FajLabel(Faj.Birodalmi)}</option>
<option key={Faj.Eszaki} value={Faj.Eszaki}>{FajLabel(Faj.Eszaki)}</option>
<option key={Faj.Etuniai} value={Faj.Etuniai}>{FajLabel(Faj.Etuniai)}</option>
<option key={Faj.Osember} value={Faj.Osember}>{FajLabel(Faj.Osember)}</option>
</optgroup>
<optgroup label='nem emberi fajok'>
<option key={Faj.Elf} value={Faj.Elf}>{FajLabel(Faj.Elf)}</option>
<option key={Faj.Felelf} value={Faj.Felelf}>{FajLabel(Faj.Felelf)}</option>
<option key={Faj.Felork} value={Faj.Felork}>{FajLabel(Faj.Felork)}</option>
<option key={Faj.Felszerzet}
value={Faj.Felszerzet}>{FajLabel(Faj.Felszerzet)}</option>
<option key={Faj.Gnom} value={Faj.Gnom}>{FajLabel(Faj.Gnom)}</option>
<option key={Faj.Torpe} value={Faj.Torpe}>{FajLabel(Faj.Torpe)}</option>
</optgroup>
</select>
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 form-label'></label>
<div className='col'>
<p className='row pt-2'>
{FajDescription(faj)}
</p>
<ul>
{FajSpecials(faj).map(((special, i) => (<li key={faj + i}>{special}</li>)))}
</ul>
</div>
</div>
</>
}
export default FajSelector
@@ -0,0 +1,78 @@
import React from "react";
import {Fegyver, KarakterFelszereles, PancelTypeLabel} from "../domain-models/felszereles";
import {Osztaly} from '../domain-models/osztaly'
import {AllowedPancelTypes} from '../domain-models/allowed-pancel-types'
import pancelok from '../domain-models/pancel.json'
import pajzsok from '../domain-models/pajzs.json'
import fegyverek from '../domain-models/fegyver.json'
import {arraySetN} from "../../util";
import {AllowedFegyver} from "../domain-models/allowed-fegyver";
export function FegyverLabel(fegyver: Fegyver): string {
return `${fegyver.Name}${fegyver.Ketkezes ? '+' : ''}${fegyver.Egzotikus ? '(E)' : ''} | ${
fegyver.NumberOfDamageDice}d${fegyver.DamageDice} ${
fegyver.CritRangeStart < 20 ? `${fegyver.CritRangeStart}-20` : ''}x${fegyver.CritMultiplier}${
fegyver.Price > 0 ? ` | ár: ${fegyver.Price} at ` : ''}${
fegyver.Range > 0 ? ` | táv: ${fegyver.Range}` : ''}`
}
function Felszereles(props: {felszereles: KarakterFelszereles, changeFelszereles: (felszereles: KarakterFelszereles) => void, osztaly: Osztaly, harcosSpec: Fegyver['Id'][]}) {
const { felszereles, changeFelszereles, osztaly, harcosSpec } = props;
const allowedPancelTypes = AllowedPancelTypes([osztaly])
const valasztottFegyverek = felszereles.fegyverIDk
.map((id, idx) => ({idx, fegyver: fegyverek.data.find(f => f.Id === id)})).filter((x) => x.fegyver != null).map(({idx, fegyver}) => ({idx, fegyver: fegyver as Fegyver}))
const osztalyAllowedFegyverek = AllowedFegyver(osztaly, harcosSpec)
const allowedFegyverek = [...osztalyAllowedFegyverek, ...valasztottFegyverek.filter(({idx, fegyver}) => !osztalyAllowedFegyverek.map(f => f.Id).includes(fegyver.Id)).map(({fegyver}) => fegyver)]
const changeFegyver = (idx: number, newID: string) => {
const found = fegyverek.data.find(x => x.Id === newID)
if (!found) {
console.log('Nem létező fegyver ID: ', newID)
return
}
changeFelszereles({...felszereles, fegyverIDk: arraySetN(felszereles.fegyverIDk, idx, newID)})
}
return <>
<div className='row'>
<h5 className='col align-self-center'>Felszerelés</h5>
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Viselt páncél</label>
<select className='col form-select' value={felszereles.pancelID} onChange={e => changeFelszereles({...felszereles, pancelID: e.target.value === 'nincs' ? undefined : e.target.value})}>
<option key={'nincs'} value={'nincs'}>Nincs</option>
{
allowedPancelTypes.map(pancelType => (
<optgroup key={pancelType} label={PancelTypeLabel(pancelType)}>
{pancelok['pancel'].filter(p => p.Type === pancelType).map(p => <option key={p.Id} value={p.Id}>{`${p.Name} (+${p.VO} VO) - ${p.Price} at`}</option>)}
</optgroup>
))
}
</select>
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Hordott pajzs</label>
<select className='col form-select' value={felszereles.pajzsID} onChange={e => changeFelszereles({...felszereles, pajzsID: e.target.value === 'nincs' ? undefined : e.target.value})}>
<option key={'nincs'} value={'nincs'}>Nincs</option>
{allowedPancelTypes.length > 0 && (pajzsok['pajzs'].map(p => <option key={p.Id} value={p.Id}>{`${p.Name} (+${p.VO} VO) - ${p.Price} at`}</option>))}
</select>
</div>
{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="Távolsági">
{allowedFegyverek.filter(f => f.Type === 'lofegyver').map(f => <option key={f.Id} value={f.Id}>{FegyverLabel(f)}</option>)}
</optgroup>
</select>
<button className='col-md-2 col-sm-3 btn btn-outline-dark btn-lg ms-2' type='button' onClick={() => changeFelszereles({...felszereles, fegyverIDk: [...felszereles.fegyverIDk.slice(0, idx), ...felszereles.fegyverIDk.slice(idx+1)]})}>Elvesz</button>
</div>
))}
<button className='btn btn-outline-dark btn btn-lg ms-2' type='button' onClick={() => changeFelszereles({...felszereles, fegyverIDk: [...felszereles.fegyverIDk, 'okol']})}>Fegyver hozzáadása</button>
</>
}
export default Felszereles
@@ -0,0 +1,38 @@
import React from "react";
import fegyverek from "../domain-models/fegyver.json"
import {defaultFegyverID, GetFegyver} from "../domain-models/felszereles";
function HarcosFegyverSpecializacio(props: {
specialization: string,
existingSpecializations: string[],
changeSpecialization: (specialization: string) => void
szint: number
}) {
const {
specialization,
existingSpecializations,
changeSpecialization,
szint
} = props
const fegyver = GetFegyver(specialization)
const pickableFegyverek = fegyverek.data.filter(f => f.Id === specialization || existingSpecializations.filter(x => x === f.Id).length < (szint < 9 ? 1 : 2))
const kozelharciPickable = pickableFegyverek.filter(f => f.Type === 'kozelharci')
const tavolsagiPickable = pickableFegyverek.filter(f => f.Type === 'lofegyver')
return <>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label' >Fegyver specializáció</label>
<select className='col form-select' value={fegyver?.Id || defaultFegyverID} onChange={e => changeSpecialization(e.target.value)}>
<optgroup label="Közelharci">
{kozelharciPickable.map(f => <option key={`spec-${szint}-${f.Id}`} value={f.Id}>{f.Name}</option>)}
</optgroup>
<optgroup label="Távolsági">
{tavolsagiPickable.map(f => <option key={`spec-${szint}-${f.Id}`} value={f.Id}>{f.Name}</option>)}
</optgroup>
</select>
{fegyver == null && <span className='form-field-error'>Ismeretlen fegyver: {specialization}</span>}
</div>
</>
}
export default HarcosFegyverSpecializacio
@@ -0,0 +1,27 @@
import React from "react";
import {AllJellemIDs, GetJellem, JellemID} from "../domain-models/jellem";
function JellemSelector(props: {selected: JellemID, changeJellem: (val: JellemID) => void}) {
const { selected, changeJellem } = props
const jellem = GetJellem(selected)
return <>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Jellem</label>
<select className='col form-select' value={selected} onChange={e => changeJellem(e.target.value as JellemID)}>
{AllJellemIDs.map((id) => (
<option key={id} value={id}>{GetJellem(id).Label}</option>
))}
</select>
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 form-label'></label>
<div className='col'>
<p className='row pt-2'>
{jellem.Description}
</p>
</div>
</div>
</>
}
export default JellemSelector
@@ -0,0 +1,34 @@
import React from "react";
import {Osztaly} from "../domain-models/osztaly";
import {AllowedFegyver} from "../domain-models/allowed-fegyver";
function KalozKritikus(props: {
kritFegyverId: string,
existingKrits: string[],
changeKrit: (krit: string) => void,
szint: number
}) {
const {
kritFegyverId,
existingKrits,
changeKrit,
szint
} = props
const fegyverek = AllowedFegyver(Osztaly.Kaloz, []).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>
<select className='col form-select' value={kritFegyverId} onChange={e => changeKrit(e.target.value)}>
<optgroup label="Közelharci">
{fegyverek.filter(f => f.Type === 'kozelharci').map(f => <option key={f.Id} value={f.Id}>{f.Name}</option>)}
</optgroup>
<optgroup label="Távolsági">
{fegyverek.filter(f => f.Type === 'lofegyver').map(f => <option key={f.Id} value={f.Id}>{f.Name}</option>)}
</optgroup>
</select>
</div>
</>
}
export default KalozKritikus
@@ -0,0 +1,89 @@
import React from "react";
import KepzettsegSelector from "./KepzettsegSelector";
import {
Kepzettseg,
Kepzettsegek,
KepzettsegId,
TolvajKepzettsegList,
} from "../domain-models/kepzettsegek";
import {arraySetN} from "../../util";
function InternalKepzettsegekSelector(props: {
title: string,
numberOfKepzettseg: number,
getKepzettsegListaN: (n: number) => Kepzettseg[],
kepzettsegek: KepzettsegId[],
changeKepzettsegek: (newKepzettsegek: KepzettsegId[]) => void
}) {
const {title, numberOfKepzettseg, getKepzettsegListaN, kepzettsegek, changeKepzettsegek} = props
const klist = Array.from(new Array(numberOfKepzettseg).keys())
const preCalculated: {
kepzettsegek: Kepzettseg[],
changeKepzettseg: (newKepzettseg: KepzettsegId) => void,
selected: Kepzettseg,
}[] = [];
for (let i = 0; i < numberOfKepzettseg; i++) {
preCalculated.push({
kepzettsegek: getKepzettsegListaN(i),
selected: Kepzettsegek[kepzettsegek[i]],
changeKepzettseg: (newKepzettseg: KepzettsegId) => changeKepzettsegek(arraySetN(kepzettsegek, i, newKepzettseg))
})
}
return <>
<div className='row m-2'>
<label className='col-lg-2 col-md-12 col-form-label'>{title}</label>
<div className='col'>
{klist.map((idx) =>
<KepzettsegSelector
key={title+idx}
kepzettsegek={preCalculated[idx].kepzettsegek}
selected={preCalculated[idx].selected}
changeKepzettseg={preCalculated[idx].changeKepzettseg}
/>)
}
</div>
</div>
</>
}
function KarakterKepzettsegek (props: {
numberOfKepzettsegek: number
kepzettsegek: KepzettsegId[],
changeKepzettsegek: (newKepzettsegek: KepzettsegId[]) => void
tolvajKepzettsegek: KepzettsegId[],
changeTolvajKepzettsegek: (newKepzettsegek: KepzettsegId[]) => void
getKepzettsegListaN: (n: number) => Kepzettseg[]
}) {
const { kepzettsegek, numberOfKepzettsegek, getKepzettsegListaN, changeKepzettsegek, tolvajKepzettsegek, changeTolvajKepzettsegek} = props
// 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 <>
<InternalKepzettsegekSelector
title="Képzettségek"
numberOfKepzettseg={numberOfKepzettsegek}
getKepzettsegListaN={getKepzettsegListaN}
changeKepzettsegek={changeKepzettsegek}
kepzettsegek={kepzettsegek} />
{tolvajKepzettsegek.length > 0 && <InternalKepzettsegekSelector
title="Tolvaj képzettségek"
numberOfKepzettseg={4}
getKepzettsegListaN={getTolvajKepzettsegListaN}
changeKepzettsegek={changeTolvajKepzettsegek}
kepzettsegek={tolvajKepzettsegek} />}
</>
}
export default KarakterKepzettsegek
@@ -0,0 +1,32 @@
import React from "react";
import {Kepzettseg, KepzettsegId} from "../domain-models/kepzettsegek";
import {TulajdonsagLabel} from "../domain-models/tulajdonsag";
import {OsztalyLabel} from "../domain-models/osztaly";
function kepzettsegOptionText(k: Kepzettseg) {
const osztalyok = k.Osztalyok?.reduce((acc, o) => acc + ' / ' + OsztalyLabel(o), '')?.substring(3)
return k.Name + ' (' + k.Tulajdonsag.reduce((acc, k) => (acc === '' ? acc : acc + ' / ') + TulajdonsagLabel(k), '') + ')' + (osztalyok == null ? '' : (' - ' + osztalyok));
}
function KepzettsegSelector (props: {kepzettsegek: Kepzettseg[], changeKepzettseg: (newKepzettseg: KepzettsegId) => void, selected : Kepzettseg}) {
const { kepzettsegek, changeKepzettseg, selected } = props
return <>
<div className='row'>
<select className='form-control'
value={selected.Id} onChange={e => changeKepzettseg(e.target.value as KepzettsegId)}>
{kepzettsegek.map(k =>(
<option key={k.Id} value={k.Id}>
{kepzettsegOptionText(k)}
</option>
))}
</select>
</div>
<div className='row pt-2'>
<p className='col'>{selected.Description}</p>
</div>
</>
}
export default KepzettsegSelector
@@ -0,0 +1,107 @@
import React from "react";
import {KarakterInputs} from "../domain-models/karakter";
import BasicNewLevel from "./BasicNewLevel";
import TulajdonsagNoveles from "./TulajdonsagNoveles";
import {Tulajdonsag, TulajdonsagokTotal} from "../domain-models/tulajdonsag";
import {arraySetN} from "../../util";
import {Osztaly} from "../domain-models/osztaly";
import HarcosFegyverSpecializacio from "./HarcosFegyverSpecializacio";
import KalozKritikus from "./KalozKritikus";
function kalozKritIndex(szint: number) {
return Math.floor(szint/3)-1
}
function LevelUp(props: {
szint: number,
karakter: KarakterInputs,
changeTulajdonsagNoveles: (t: Tulajdonsag) => void,
changeRolledHP: (rolledHP: number) => void,
changeHarcosSpec: (spec: string) => void,
changeKalozKritikus: (krit: string) => void,
}) {
const {
szint,
karakter,
changeTulajdonsagNoveles,
changeRolledHP,
changeHarcosSpec,
changeKalozKritikus,
} = props
const specializationIndex = Math.floor((szint-1) / 2)
const specialization = karakter.harcosSpecializaciok[specializationIndex]
const kritIndex = kalozKritIndex(szint)
const krit = karakter.kalozKritikus[kritIndex]
return <div>
<div className='row mt-3'>
<h5 className='col align-self-center'>{szint}. Szint</h5>
</div>
{szint % 4 === 0 && <TulajdonsagNoveles
tulajdonsagok={karakter.tulajdonsagok}
szint={szint}
tulajdonsagNovelesek={karakter.tulajdonsagNovelesek}
changeTulajdonsagNoveles={changeTulajdonsagNoveles}
/>}
<BasicNewLevel
szint={szint}
karakter={karakter}
changeRolledHP={changeRolledHP}
/>
{karakter.osztaly === Osztaly.Harcos && szint % 2 === 1 &&
<HarcosFegyverSpecializacio
specialization={specialization}
existingSpecializations={karakter.harcosSpecializaciok}
changeSpecialization={changeHarcosSpec}
szint={szint}
/>
}
{karakter.osztaly === Osztaly.Kaloz && szint % 3 === 0 && !!krit &&
<KalozKritikus
kritFegyverId={krit}
existingKrits={karakter.kalozKritikus}
changeKrit={changeKalozKritikus}
szint={szint}
/>
}
</div>
}
export function LevelUps(props: {karakter: KarakterInputs, changeKarakter: (inputs: KarakterInputs) => void}){
const {karakter, changeKarakter} = props;
const tulajdonsagokAtSzint = (szint: number) => TulajdonsagokTotal({...karakter, tulajdonsagNovelesek: karakter.tulajdonsagNovelesek.slice(0, Math.floor(szint / 4))})
const changeRolledHPAtSzint = (szint: number) => (newHProll: number) => {
changeKarakter({...karakter, hpRolls: arraySetN(karakter.hpRolls, szint-2, newHProll)})
console.log(karakter)
}
const changeTulajdonsagNovelesAtSzint = (szint: number) => (t: Tulajdonsag) => { changeKarakter({...karakter, tulajdonsagNovelesek: arraySetN(karakter.tulajdonsagNovelesek, Math.floor((szint / 4)-1), t)}) }
const changeHarcosSpecializacioAtSzint = (szint: number) => (spec: string) => {
changeKarakter({...karakter, harcosSpecializaciok: arraySetN(karakter.harcosSpecializaciok, Math.floor((szint-1) / 2), spec)})
}
const changeKalozKritikusAtSzint = (szint: number) => (krit: string) => {
changeKarakter({...karakter, kalozKritikus: arraySetN(karakter.kalozKritikus, kalozKritIndex(szint), krit)})
}
const levels = karakter.hpRolls.map((_, i) => i+2);
return <>
{levels.map(szint =>
<LevelUp
key={`level-up-${szint}`}
szint={szint}
karakter={{...karakter, tulajdonsagok: tulajdonsagokAtSzint(szint)}}
changeTulajdonsagNoveles={changeTulajdonsagNovelesAtSzint(szint)}
changeRolledHP={changeRolledHPAtSzint(szint)}
changeHarcosSpec={changeHarcosSpecializacioAtSzint(szint)}
changeKalozKritikus={changeKalozKritikusAtSzint(szint)}
/>
)}
</>
}
export default LevelUps
@@ -0,0 +1,61 @@
import React from "react";
import {OverlayTrigger, Tooltip} from "react-bootstrap";
import {SignedNumberToText} from "../../shared/components/Helpers";
import {MasodlagosErtekekView} from "../domain-models/masodlagos_ertekek";
function MasodlagosErtekek(props: MasodlagosErtekekView) {
const { HP, VO, CelzoTB, Mentok, KozelharciTB } = props
const tooltip = (
<Tooltip id="tooltip">
<strong>Hentelési Potenciál</strong>
</Tooltip>
);
return <>
<div className='row'>
<h5 className='col align-self-center'>Másodlagos értékek</h5>
</div>
<div className='row'>
<div className='col-lg-6'>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label'>Életerő{' '}
<OverlayTrigger placement='top' overlay={tooltip} delay={0} defaultShow={false} flip={false}>
<span>(HP)</span>
</OverlayTrigger>
</label>
<p className='col col-form-label'>{HP}</p>
</div>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label' >Védekezési Osztály (VO)</label>
<p className='col col-form-label'>{VO}</p>
</div>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label' >Közelharci támadás</label>
<p className='col col-form-label'>{KozelharciTB.map(SignedNumberToText).join(" / ")}</p>
</div>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label' >Célzó támadás</label>
<p className='col col-form-label'>{CelzoTB.map(SignedNumberToText).join(" / ")}</p>
</div>
</div>
<div className='col-lg-6'>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label' >Kitartás mentő</label>
<p className='col col-form-label'>{SignedNumberToText(Mentok.kitartas)}</p>
</div>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label' >Reflex mentő</label>
<p className='col col-form-label'>{SignedNumberToText(Mentok.reflex)}</p>
</div>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label' >Akaraterő mentő </label>
<p className='col col-form-label'>{SignedNumberToText(Mentok.akaratero)}</p>
</div>
</div>
</div>
</>
}
export default MasodlagosErtekek
@@ -0,0 +1,67 @@
import React from "react";
import {
Osztaly,
OsztalyDescription,
OsztalyLabel,
OsztalyProperties,
OsztalySpecialSkills
} from "../domain-models/osztaly";
import {Faj, SzintKorlatokFajokra} from "../domain-models/faj";
function OsztalySelector(props: {currentFaj: Faj, currentOsztaly: Osztaly, changeOsztaly: (newOsztaly: Osztaly) => void}) {
const {currentFaj, currentOsztaly, changeOsztaly} = props;
return <>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Osztály</label>
<select className="col form-select" value={currentOsztaly} onChange={(e) => changeOsztaly(e.target.value as Osztaly)} >
<optgroup label='Harcos'>
<option value={Osztaly.Harcos}>{OsztalyLabel(Osztaly.Harcos)}</option>
{SzintKorlatokFajokra(currentFaj)[Osztaly.Amazon] > 0 && (
<option value={Osztaly.Amazon}>{OsztalyLabel(Osztaly.Amazon)}</option>) }
<option value={Osztaly.Barbar}>{OsztalyLabel(Osztaly.Barbar)}</option>
<option value={Osztaly.Ijasz}>{OsztalyLabel(Osztaly.Ijasz)}</option>
<option value={Osztaly.Kaloz}>{OsztalyLabel(Osztaly.Kaloz)}</option>
</optgroup>
<option value={Osztaly.Pap}>{OsztalyLabel(Osztaly.Pap)}</option>
{SzintKorlatokFajokra(currentFaj)[Osztaly.Tolvaj] > 0 && (<option value={Osztaly.Tolvaj}>{OsztalyLabel(Osztaly.Tolvaj)}</option>)}
{SzintKorlatokFajokra(currentFaj)[Osztaly.Varazslo] > 0 && (
<optgroup label='Varázsló'>
<option value={Osztaly.Varazslo}>{OsztalyLabel(Osztaly.Varazslo)}</option>
<option value={Osztaly.Illuzionista}>{OsztalyLabel(Osztaly.Illuzionista)}</option>
</optgroup>
)}
</select>
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 form-label'></label>
<div className='col' >
<p className='row pt-2'>
{OsztalyDescription(currentOsztaly)}
</p>
<ul>
{OsztalyProperties(currentOsztaly).map(((special, i) => (<li key={currentOsztaly + i}>{special}</li>)))}
</ul>
<div className='col'>
{OsztalySpecialSkills(currentOsztaly).map(skill =>
<div key={skill.Id} >
<strong className='d-inline'>
{skill.Name}
</strong>
{' '}
{skill.Description}
{skill.ExtendedDescription && (
<ol>
{skill.ExtendedDescription.map((ext, i) =>
(<li key={skill.Id + '-' + i}>{ext}</li>))}
</ol>
)}
</div>
)}
</div>
</div>
</div>
</>
}
export default OsztalySelector;
@@ -0,0 +1,8 @@
import {Tooltip} from "react-bootstrap";
import React from "react";
const SaveOverlayTooltip = <Tooltip id="save-tooltip">
A mentéssel egyedi URL-en elérhető lesz a karakter bárkinek, azonban módosítani már nem lehet, csak újat menteni.
</Tooltip>
export default SaveOverlayTooltip
@@ -0,0 +1,52 @@
import React from "react";
import {Faj, FajLabel} from "../domain-models/faj";
import {Modifier} from "../domain-models/tulajdonsag";
import {SignedNumberToText} from "../../shared/components/Helpers";
function fajiModositoText(faj: Faj, szamolas: (f: Faj) => number) : string {
const mod = szamolas(faj)
return (mod !== 0) ? SignedNumberToText(mod) + ' (' + FajLabel(faj) + ')' : ''
}
function TulajdonsagInput(props: {
tulajdonsag: string,
currentFaj : Faj,
fajiModosito: (faj: Faj) => number,
changeValue: (val: number) => void
currentValue: number
tooLowError: string,
tooHighError: string
}) {
const {
tulajdonsag,
changeValue,
currentFaj,
currentValue,
fajiModosito,
tooLowError,
tooHighError,
} = props;
return (<div className='row m-2'>
<label className='col-lg-1 col-sm-2 col-form-label'>{tulajdonsag}</label>
<div className='col-lg-1 col-sm-2 m-2'>
<input className='form-control' maxLength={2} type='number' value={currentValue} onChange={e => changeValue(Number(e.target.value))} />
</div>
<span className='col-sm-2 m-2'>
{fajiModositoText(currentFaj, fajiModosito)}
</span>
<span className='col-sm-2 m-2'>
Összesen: { (currentValue + (fajiModosito(currentFaj))).toString() }
</span>
<span className='col-sm-2 m-2'>
Módosító: { SignedNumberToText(Modifier(currentValue + fajiModosito(currentFaj))) }
</span>
{currentValue < 3 && (
<span className='form-field-error'>{tooLowError}</span>)}
{currentValue > 18 && (
<span className='form-field-error'>{tooHighError}</span>)}
</div>)
}
export default TulajdonsagInput;
@@ -0,0 +1,38 @@
import React from "react";
import {
KarakterTulajdonsagok,
Modifier,
Tulajdonsag,
TulajdonsagIDs,
TulajdonsagLabel
} from "../domain-models/tulajdonsag";
import {SignedNumberToText} from "../../shared/components/Helpers";
function TulajdonsagNoveles(props: {tulajdonsagok: KarakterTulajdonsagok, szint: number, tulajdonsagNovelesek: Tulajdonsag[], changeTulajdonsagNoveles: (tulajdonsag: Tulajdonsag) => void}) {
const {tulajdonsagok, szint, tulajdonsagNovelesek, changeTulajdonsagNoveles} = props
const tulajdonsagNoveles = tulajdonsagNovelesek[(szint / 4)-1]
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 Tulajdonsag)}>
{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
@@ -0,0 +1,83 @@
import React from "react";
import {
KarakterTulajdonsagok,
RollAllAbilities,
Tulajdonsag,
} from "../domain-models/tulajdonsag";
import TulajdonsagInput from "./TulajdonsagInput";
import {Faj, TulajdonsagModositokFajokra} from "../domain-models/faj";
function Tulajdonsagok(props: {
currentFaj: Faj,
tulajdonsagok: KarakterTulajdonsagok
changeTulajdonsagok: (tulajdonsagok: KarakterTulajdonsagok) => void
}) {
const {currentFaj, tulajdonsagok, changeTulajdonsagok} = props
return <>
<div className='row'>
<h5 className='col-lg-2 col-sm-4 align-self-center'>Tulajdonságok</h5>
<div className='col-sm-2 m-2'>
<button className='btn btn-dark' type='button'
onClick={() => changeTulajdonsagok(RollAllAbilities())}>Dobás
</button>
</div>
</div>
<TulajdonsagInput
tulajdonsag='Erő'
currentValue={tulajdonsagok.t_ero}
currentFaj={currentFaj}
fajiModosito={TulajdonsagModositokFajokra(Tulajdonsag.Ero)}
changeValue={(val: number) => changeTulajdonsagok({...tulajdonsagok, t_ero: val})}
tooLowError='Túl gyenge vagy, nem bírtad felemelni a kezed a jelentkezéshez!'
tooHighError='Szét szakadtak az izmaid!'
/>
<TulajdonsagInput
tulajdonsag='Ügyesség'
currentValue={tulajdonsagok.t_ugy}
currentFaj={currentFaj}
fajiModosito={TulajdonsagModositokFajokra(Tulajdonsag.Ugyesseg)}
changeValue={(val: number) => changeTulajdonsagok({...tulajdonsagok, t_ugy: val})}
tooLowError='Orrabuktál jelentkezés helyett!'
tooHighError='Emberfeletti zsonglörködésedre záptojással válaszoltak a helyiek!'
/>
<TulajdonsagInput
tulajdonsag='Egészség'
currentValue={tulajdonsagok.t_egs}
currentFaj={currentFaj}
fajiModosito={TulajdonsagModositokFajokra(Tulajdonsag.Egeszseg)}
changeValue={(val: number) => changeTulajdonsagok({...tulajdonsagok, t_egs: val})}
tooLowError='Túl beteg vagy ahhoz, hogy kalandozni menj!'
tooHighError='Kicsattanó egészségedet csak a diambroid állíthatja meg, minő véletlen, hogy pont az arcodba robbant...'
/>
<TulajdonsagInput
tulajdonsag='Intelligencia'
currentValue={tulajdonsagok.t_int}
currentFaj={currentFaj}
fajiModosito={TulajdonsagModositokFajokra(Tulajdonsag.Intelligencia)}
changeValue={(val: number) => changeTulajdonsagok({...tulajdonsagok, t_int: val})}
tooLowError='Elfelejtetted, hogy mikor is kéne kalandozni indulni!'
tooHighError='Ilyen tudás és logika birtokában mi szükséged van új információkra? Mindent le tudsz vezetni a már meglévő ismereteidből, és ezt azonnal beláttad.'
/>
<TulajdonsagInput
tulajdonsag='Bölcsesség'
currentValue={tulajdonsagok.t_bol}
currentFaj={currentFaj}
fajiModosito={TulajdonsagModositokFajokra(Tulajdonsag.Bolcsesseg)}
changeValue={(val: number) => changeTulajdonsagok({...tulajdonsagok, t_bol: val})}
tooLowError='Nem bírtál ellenállni a kíváncsiságodnak, hogy közelebbről is megvizsgáld a tőr hegyét. Nagyon közelről.'
tooHighError='Hatalmas bölcsességedben beláttad a kalandozás veszélyeit és inkább más tevékenységbe fogtál.'
/>
<TulajdonsagInput
tulajdonsag='Karizma'
currentValue={tulajdonsagok.t_kar}
currentFaj={currentFaj}
fajiModosito={TulajdonsagModositokFajokra(Tulajdonsag.Karizma)}
changeValue={(val: number) => changeTulajdonsagok({...tulajdonsagok, t_kar: val})}
tooLowError='Mintha taszítanád az embereket, sose sikerült kalandozó csapatot találnod.'
tooHighError='Az ellenkező nem tagjai nem engedték, hogy útnak indulj.'
/>
</>
}
export default Tulajdonsagok
@@ -0,0 +1,24 @@
import {Fegyver, GetFegyver} from "./felszereles";
import fegyverDB from "./fegyver.json";
import {Osztaly} from "./osztaly";
export function AllowedFegyver(o: Osztaly, harcosSpec: string[]): Fegyver[] {
const fegyverek = fegyverDB.data.map(x => x as Fegyver)
const nemEgzotikus = fegyverek.filter(f => !f.Egzotikus)
const varazslo = fegyverek.filter(f => ['okol', 'bot', 'bunko', 'tor', 'parittya', 'dobotu'].includes(f.Id))
const tolvaj = fegyverek.filter(f => ['okol', 'tor', 'parittya', 'rovid_ij', 'dobotu', 'konnyu_szamszerij', 'nehez_szamszerij', 'szablya', 'hosszu_kard', 'rovid_kard' ].includes(f.Id))
switch (o) {
case Osztaly.Harcos:
const harcosSpecFegyverek = harcosSpec.map(GetFegyver).filter(x => x != null && x.Egzotikus).map(x => x as Fegyver)
return [...nemEgzotikus, ...harcosSpecFegyverek]
case Osztaly.Amazon:
case Osztaly.Barbar:
case Osztaly.Kaloz: return nemEgzotikus;
case Osztaly.Ijasz: return [...nemEgzotikus, fegyverDB.data.find(f => f.Id === 'visszacsapo_ij') as Fegyver]
// Note: Isteni fegyver barmi lehet istentol foggoen
case Osztaly.Pap: return fegyverek;
case Osztaly.Tolvaj: return tolvaj;
case Osztaly.Illuzionista:
case Osztaly.Varazslo: return varazslo;
}
}
@@ -0,0 +1,20 @@
import {PancelType} from "./felszereles";
import {Osztaly} from "./osztaly";
export function AllowedPancelTypes(osztalyok: Osztaly[]): PancelType[] {
return [...new Set(osztalyok.flatMap(osztalyAllowedPancelTypes))]
}
function osztalyAllowedPancelTypes(o: Osztaly): PancelType[] {
switch (o) {
case Osztaly.Amazon: return ['konnyu'];
case Osztaly.Barbar: return ['konnyu', 'kozepes', 'nehez'];
case Osztaly.Pap: return ['konnyu', 'kozepes', 'nehez'];
case Osztaly.Illuzionista: return [];
case Osztaly.Varazslo: return [];
case Osztaly.Harcos: return ['konnyu', 'kozepes', 'nehez'];
case Osztaly.Ijasz: return ['konnyu', 'kozepes'];
case Osztaly.Kaloz: return ['konnyu', 'kozepes'];
case Osztaly.Tolvaj: return ['konnyu']
}
}
@@ -0,0 +1,328 @@
import {Osztaly} from "./osztaly";
import {KarakterTulajdonsagok, Tulajdonsag} from "./tulajdonsag";
export enum Faj {
Ember = 'f_ember',
Amazon = 'f_amazon',
Birodalmi = 'f_birodalmi',
Etuniai = 'f_etuniai',
Eszaki = 'f_eszaki',
Osember = 'f_osember',
Elf = 'f_elf',
Felelf = 'f_felelf',
Felork = 'f_felork',
Felszerzet = 'f_felszerzet',
Gnom = 'f_gnom',
Torpe = 'f_torpe',
}
export function FajLabel(faj: Faj) : string {
switch (faj) {
case Faj.Ember: return 'Ember';
case Faj.Amazon: return 'Amazon';
case Faj.Birodalmi: return 'Birodalmi';
case Faj.Etuniai: return 'Etúniai';
case Faj.Eszaki: return 'Északi';
case Faj.Osember: return 'Ősember';
case Faj.Elf: return 'Elf';
case Faj.Felelf: return 'Félelf';
case Faj.Felork: return 'Félork';
case Faj.Felszerzet: return 'Félszerzet';
case Faj.Gnom: return 'Gnóm';
case Faj.Torpe: return 'Törpe';
}
}
export function FajDescription(faj: Faj) : string {
switch (faj) {
case Faj.Ember: return "Az emberi népek sokfélesége és szaporasága, valamint a bennük élő felfedezői hajlam és erkölcsi rugalmasság hatására képviselőik majdnem minden földön megtalálhatók. A legtöbb ember a lenti vonásokkal rendelkezik.";
case Faj.Amazon: return "Bár az amazonok népe igen kis létszámú, harciasságuk miatt mégis félik őket. Az amazonok nyúlánk termetűek, hajuk általában vöröses vagy világosbarna, bőrük világos. Társadalmukban teljes a nőuralom, a férfiakat rabszolgaságban tartják és megtiltják számukra a fegyverviselést. Képzett tengeri rablók, de a szárazföldön is megállják a helyüket. Az amazon nők kivétel nélkül értenek a harchoz és soha, soha nem hagyják megtorlatlanul az őket ért sérelmeket. Technikailag fejletlenek. Kis erejű passzív pszionikus (elmebeli) képességeik vannak.";
case Faj.Birodalmi: return "Bár a Birodalom történelemelőtti kora már évezredek óta elmúlt, a nép maradékai még megtalálhatók. Az irántuk érzett gyűlölet miatt sokan rejtőzködni kényszerülnek közülük, pedig hatalmuk már meg sem közelíti a régit életüket többnyire dekadens élvezetek habzsolásával, kábítószeres mámorban töltik. Bőrük lilás, a tisztavérűek esetén padlizsánszínű, szemük ibolyaszín. A fajtiszta birodalmiak között a beltenyészet és a degeneráció miatt gyakoriak az örökletes betegségek. Városaik számtalan titkot és mesés kincseket rejtenek.";
case Faj.Etuniai: return "Az etúniai lovas nomádok kultúrája mindmáig megőrizte primitívségét. Akár földművelésből, akár lovas portyázásból, akár rablásból és kereskedésből élnek, nem fogékonyak a finomságok iránt. Eszközeik is egyszerűek: fegyvereik főleg hosszú lándzsák, és bár kedvelik a kardokat és láncvérteket, nem jeleskednek azok előállításában (a vas megmunkálását kovácsaik féltett titokként őrzik). Többnyire semleges jelleműek bár azok, akik otthonuktól elszakadva kénytelenek megélni, inkább a gonosz felé hajlanak. Az etúniaiak középtermetűek, bőrük cserzett, hajuk olajos fekete, amit leggyakrabban copfban fognak össze. A férfiak kedvelik a bajusz viseletét.";
case Faj.Eszaki: return "Az északi kultúra lételeme a harc és a hajózás. Még nőik is kiválóan forgatják a harci fejszét vagy a hosszú élű bárdot, s bár rabszolgatartók kegyetlenségnek tartják rabszolgáikat a legelemibb védekező eszközöktől, mint a késtől vagy a rövidkardtól megfosztani vagy fegyvertelenekre támadni. Ettől eltekintve vérszomjasak és általában semleges vagy gonosz jelleműek. Testalkatuk magas és erős, hajuk vöröses, szőke vagy világosbarna. A férfiak szakállat vagy bajuszt, a nők hosszú hajat viselnek. A déli vidékeken civilizálatlanságuk és faragatlanságuk miatt gyakran megvetik őket.";
case Faj.Osember: return "Az Ősemberek vad földek primitív őslakói. Máig merizték görnyedt tartásukat, szőrös küllemüket és loboncos fekete hajukat. A fémmegmunkálást még sokan nem ismerik, s kőeszközöket esetleg rezet használnak. Írásuk egyáltalán nincs. Félisteneént tisztelik hátasaikat, a hatalmas mamutokat.";
case Faj.Elf: return "Az elfek szépségükről, hosszú életükről és fejlett civilizációjukról ismertek, amely ötvözi a városi és természeti élet előnyeit. Termetük nyúlánk, vonásaik az embernél finomabbak, de egyben törékenyebbek. Kiváló íjászok és varázslók. Általában visszahúzódóak, csak ritkán lépnek kapcsolatba a rövidebb életű népekkel legjobban az emberekkel és a félszerzetekkel jönnek ki. Jellemük általában kaotikus jó.";
case Faj.Felelf: return "A félelfek elf és ember szülők gyermekei. Kis számuk miatt nem alkotnak külön közösségeket.";
case Faj.Felork: return "A félorkok többsége külsejében inkább rút emberre hasonlít (bár termetük gyakran görnyedt, arcvonásaik pedig a disznóéra emlékeztetnek), belsőleg azonban számos ork vonással rendelkeznek: számítók, magányosak és bizalmatlanok a szebb fajokkal, de legtöbbször fajtársaikkal is. Különösen sok közöttük az orgyilkos és a közönséges gonosztevő; egyikük-másikuk ezt némi mágikus erővel vegyíti. A legtöbb félork gonosz jellemű.";
case Faj.Felszerzet: return "A félszerzetek általában megelégszenek a nyugodt vidéki élettel. Termetük az emberi fele. Hajlamosak az elhízásra, s lábukat vastag szőr borítja. Általában mezítláb járnak. Mivel lusták és kényelemszeretők, csak ritkán adják fejüket kalandokra ekkor azonban kiváló tolvajokká válhatnak.";
case Faj.Gnom: return "A gnómok különösen ritka, elsősorban emberi városokban megtalálható alacsony termetű faj. A gnómokat alacsony termetükről, vékony végtagjaikról és tömpe, húsos orrukról lehet fölismerni. A gnómok jó kézművesek és van érzékük a mágiához is elsősorban az illuzionisták trükkjeihez vonzódnak.";
case Faj.Torpe: return "A törpék elsősorban hegyek között és dombvidékeken élnek. Termetük kicsi (1,2 m), de testük tömzsi és igen szívós. Bozontos szakállat viselnek, amely a férfiak esetén dús és sörteszerű, a nőknél finomabb és ritkásabb. A törpék kiemelkedőek a mindenféle mesterségekben, különösen a fémmunkákban, de harcban is megállják helyüket. Nagy nemzetségekbe szerveződnek és híresek igazságérzetükről, makacsságukról és kapzsiságukról. A legtöbb törpe törvényes jó vagy törvényes semleges jellemű.";
}
}
export function FajSpecials(faj : Faj) : string[] {
switch (faj) {
case Faj.Ember: return [
"A játék kezdetén egy extra képzettséggel kezdenek",
"Korlátlanul fejlődhetnek minden osztályban",
];
case Faj.Amazon: return [
"Ha két vagy több amazon együttesen harcol, VO-juk 2-vel nő pszionikus képességeik miatt",
"Szintkorlátok: Harcos -, Pap 5, Tolvaj 0, Varázsló 0",
];
case Faj.Birodalmi: return [
"+1 Int, -1 Egs",
"Egyik kezdő képzettségük mindenképpen az Alkímia vagy a Méregkeverés",
"Szintkorlátok: Harcos 7, Pap 5, Tolvaj 5, Varázsló -",
];
case Faj.Etuniai: return [
"+1 Egs, -1 Böl",
"Az egyik kezdő képzettségük mindenképpen a Lovaglás",
"Szintkorlátok: Harcos -, Pap 5, Tolvaj -, Varázsló 4",
];
case Faj.Eszaki: return [
"+1 Erő, -1 Böl",
"Az egyik kezdő képzettségük mindenképpen a Hajózás",
"Szintkorlátok: Harcos -, Pap 7, Tolvaj -, Varázsló 5",
];
case Faj.Osember: return [
"+1 Erő, +1 Egészség, -1 Int, -1 Böl",
"Szintkorlátok: Harcos -, Pap 5, Tolvaj 4, Varázsló 0",
];
case Faj.Elf: return [
"+1 Ügy, -1 Egs",
"Immunisak az altató mágiára, +2 mentődobás bűbáj ellen",
"Automatikusan képzettek az íjak és a hosszúkard használatában akkor is, ha osztályuk ezt nem adná meg nekik",
"Nem támaszthatók föl közönséges mágiával",
"Szintkorlátok: Harcos 9, Pap 7, Tolvaj 6, Varázsló 9",
];
case Faj.Felelf: return [
"Automatikusan képzettek az íjak és a hosszúkard használatában",
"Szintkorlátok: Harcos 9, Pap 5, Tolvaj 9, Varázsló 9",
];
case Faj.Felork: return [
"+1 Erő, +1 Egs, -2 Kar",
"Szintkorlátok: Harcos 9, Pap 5, Tolvaj 9, Varázsló 5",
];
case Faj.Felszerzet: return [
"+1 Ügy, -1 Erő",
"+1 VO kis méretük miatt",
"+1 támadást kapnak dobófegyverekre és parittyákra",
"Szintkorlátok: Harcos 6, Pap 6, Tolvaj 9, Varázsló 4",
];
case Faj.Gnom: return [
"+1 Int, -1 Erő",
"Szintkorlátok: Harcos 6, Pap 6, Tolvaj 8, Varázsló 7",
];
case Faj.Torpe: return [
"+1 Egs, -1 Kar",
"Lassabban mozognak az embereknél (alapsebességük 20)",
"+2 mentődobást kapnak mérgek és varázslatok hatásai ellen",
"Szintkorlátok: Harcos 9, Pap 9, Tolvaj 6, Varázsló 4",
];
}
}
export type Szintkorlatok = Record<Osztaly, number>
export interface FajDetails {
ID: Faj,
Label: string,
Description: string,
Specials: string[],
TulajdonsagModifiers: KarakterTulajdonsagok
Szintkorlatok: Szintkorlatok
}
export function GetFajDetails(faj: Faj): FajDetails {
return {
ID: faj,
Label: FajLabel(faj),
Description: FajDescription(faj),
Specials: FajSpecials(faj),
TulajdonsagModifiers: fajiTulajdonsagModositok(faj),
Szintkorlatok: SzintKorlatokFajokra(faj),
}
}
// noinspection OverlyComplexFunctionJS,FunctionTooLongJS
function tulajdonsagModositokPerFaj(tul: Tulajdonsag, faj: Faj) : number {
switch (faj) {
case Faj.Birodalmi: switch (tul) {
case Tulajdonsag.Egeszseg: return -1;
case Tulajdonsag.Intelligencia: return +1;
default: return 0;
}
case Faj.Eszaki: switch (tul) {
case Tulajdonsag.Ero: return +1;
case Tulajdonsag.Bolcsesseg: return -1;
default: return 0;
}
case Faj.Etuniai: switch (tul) {
case Tulajdonsag.Egeszseg: return +1;
case Tulajdonsag.Bolcsesseg: return -1;
default: return 0
}
case Faj.Osember: switch (tul) {
case Tulajdonsag.Ero: return +1;
case Tulajdonsag.Egeszseg: return +1;
case Tulajdonsag.Intelligencia: return -1;
case Tulajdonsag.Bolcsesseg: return -1;
default: return 0
}
case Faj.Elf: switch (tul) {
case Tulajdonsag.Egeszseg: return -1;
case Tulajdonsag.Ugyesseg: return +1;
default: return 0;
}
case Faj.Felork: switch (tul) {
case Tulajdonsag.Ero: return +1;
case Tulajdonsag.Egeszseg: return +1;
case Tulajdonsag.Karizma: return -2;
default: return 0;
}
case Faj.Felszerzet: switch (tul) {
case Tulajdonsag.Ero: return -1;
case Tulajdonsag.Ugyesseg: return +1;
default: return 0;
}
case Faj.Gnom: switch (tul) {
case Tulajdonsag.Ero: return -1;
case Tulajdonsag.Intelligencia: return +1;
default: return 0;
}
case Faj.Torpe: switch (tul) {
case Tulajdonsag.Egeszseg: return +1;
case Tulajdonsag.Karizma: return -1;
default: return 0;
}
default: return 0;
}
}
export function TulajdonsagModositokFajokra(tul: Tulajdonsag) {
return (faj: Faj) => tulajdonsagModositokPerFaj(tul, faj);
}
export function TulajdonsagokFajjal(tulajdonsagok: KarakterTulajdonsagok, faj: Faj): KarakterTulajdonsagok {
return {
[Tulajdonsag.Ero]: tulajdonsagok.t_ero + tulajdonsagModositokPerFaj(Tulajdonsag.Ero, faj),
[Tulajdonsag.Ugyesseg]: tulajdonsagok.t_ugy + tulajdonsagModositokPerFaj(Tulajdonsag.Ugyesseg, faj),
[Tulajdonsag.Egeszseg]: tulajdonsagok.t_egs + tulajdonsagModositokPerFaj(Tulajdonsag.Egeszseg, faj),
[Tulajdonsag.Intelligencia]: tulajdonsagok.t_int + tulajdonsagModositokPerFaj(Tulajdonsag.Intelligencia, faj),
[Tulajdonsag.Bolcsesseg]: tulajdonsagok.t_bol + tulajdonsagModositokPerFaj(Tulajdonsag.Bolcsesseg, faj),
[Tulajdonsag.Karizma]: tulajdonsagok.t_kar + tulajdonsagModositokPerFaj(Tulajdonsag.Karizma, faj),
}
}
function fajiTulajdonsagModositok(faj: Faj): KarakterTulajdonsagok {
return {
[Tulajdonsag.Ero]: TulajdonsagModositokFajokra(Tulajdonsag.Ero)(faj),
[Tulajdonsag.Ugyesseg]: TulajdonsagModositokFajokra(Tulajdonsag.Ugyesseg)(faj),
[Tulajdonsag.Egeszseg]: TulajdonsagModositokFajokra(Tulajdonsag.Egeszseg)(faj),
[Tulajdonsag.Intelligencia]: TulajdonsagModositokFajokra(Tulajdonsag.Intelligencia)(faj),
[Tulajdonsag.Bolcsesseg]: TulajdonsagModositokFajokra(Tulajdonsag.Bolcsesseg)(faj),
[Tulajdonsag.Karizma]: TulajdonsagModositokFajokra(Tulajdonsag.Karizma)(faj),
}
}
const EmberiSzintKorlatok: Szintkorlatok = {
o_amazon: 0,
o_barbar: 20,
o_harcos: 20,
o_ijasz: 20,
o_illuzionista: 20,
o_kaloz: 20,
o_pap: 20,
o_tolvaj: 20,
o_varazslo: 20
}
// noinspection OverlyComplexFunctionJS,FunctionTooLongJS
export function SzintKorlatokFajokra(faj: Faj) : Szintkorlatok {
let szintkorlatok = {...EmberiSzintKorlatok}
switch (faj) {
case Faj.Ember: break;
case Faj.Amazon:
szintkorlatok.o_pap = 5
szintkorlatok.o_tolvaj = 0
szintkorlatok.o_illuzionista = 0
szintkorlatok.o_varazslo = 0
szintkorlatok.o_amazon = 20
break
case Faj.Birodalmi:
szintkorlatok.o_harcos = 7
szintkorlatok.o_barbar = 7
szintkorlatok.o_ijasz = 7
szintkorlatok.o_kaloz = 7
szintkorlatok.o_pap = 5
szintkorlatok.o_tolvaj = 5
break
case Faj.Eszaki:
case Faj.Etuniai:
szintkorlatok.o_pap = 7
szintkorlatok.o_illuzionista = 5
szintkorlatok.o_varazslo = 5
break
case Faj.Osember:
szintkorlatok.o_pap = 7
szintkorlatok.o_tolvaj = 4
szintkorlatok.o_illuzionista = 0
szintkorlatok.o_varazslo = 0
break
case Faj.Elf:
szintkorlatok.o_pap = 7
szintkorlatok.o_tolvaj = 6
szintkorlatok.o_illuzionista = 9
szintkorlatok.o_varazslo = 9
szintkorlatok.o_harcos = 9
szintkorlatok.o_barbar = 9
szintkorlatok.o_ijasz = 9
szintkorlatok.o_kaloz = 9
break
case Faj.Felelf:
szintkorlatok.o_pap = 5
szintkorlatok.o_tolvaj = 9
szintkorlatok.o_illuzionista = 9
szintkorlatok.o_varazslo = 9
szintkorlatok.o_harcos = 9
szintkorlatok.o_barbar = 9
szintkorlatok.o_ijasz = 9
szintkorlatok.o_kaloz = 9
break
case Faj.Felork:
szintkorlatok.o_pap = 5
szintkorlatok.o_tolvaj = 9
szintkorlatok.o_illuzionista = 5
szintkorlatok.o_varazslo = 5
szintkorlatok.o_harcos = 9
szintkorlatok.o_barbar = 9
szintkorlatok.o_ijasz = 9
szintkorlatok.o_kaloz = 9
break
case Faj.Felszerzet:
szintkorlatok.o_pap = 6
szintkorlatok.o_tolvaj = 9
szintkorlatok.o_illuzionista = 4
szintkorlatok.o_varazslo = 4
szintkorlatok.o_harcos = 6
szintkorlatok.o_barbar = 6
szintkorlatok.o_ijasz = 6
szintkorlatok.o_kaloz = 6
break
case Faj.Gnom:
szintkorlatok.o_pap = 6
szintkorlatok.o_tolvaj = 8
szintkorlatok.o_illuzionista = 7
szintkorlatok.o_varazslo = 7
szintkorlatok.o_harcos = 6
szintkorlatok.o_barbar = 6
szintkorlatok.o_ijasz = 6
szintkorlatok.o_kaloz = 6
break
case Faj.Torpe:
szintkorlatok.o_pap = 9
szintkorlatok.o_tolvaj = 6
szintkorlatok.o_illuzionista = 4
szintkorlatok.o_varazslo = 4
szintkorlatok.o_harcos = 9
szintkorlatok.o_barbar = 9
szintkorlatok.o_ijasz = 9
szintkorlatok.o_kaloz = 9
break
}
return szintkorlatok
}
@@ -0,0 +1,467 @@
{
"data": [
{
"Id": "alabard",
"Name": "Alabárd és más szálfegyverek",
"Type": "kozelharci",
"Ketkezes": true,
"Egzotikus": false,
"Rohamfelfogo": true,
"Price": 10,
"DamageDice": 10,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 3,
"Range": 0
},
{
"Id": "bot",
"Name": "Bot",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 0,
"DamageDice": 6,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 2,
"Range": 0
},
{
"Id": "bunko",
"Name": "Bunkó",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 0,
"DamageDice": 6,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 2,
"Range": 0
},
{
"Id": "buzogany",
"Name": "Buzogány és láncos buzogány",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 8,
"DamageDice": 8,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 2,
"Range": 0
},
{
"Id": "csatabard",
"Name": "Csatabárd",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 10,
"DamageDice": 8,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 3,
"Range": 0
},
{
"Id": "csatabard_2",
"Name": "Kétkezes csatabárd",
"Type": "kozelharci",
"Ketkezes": true,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 20,
"DamageDice": 12,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 3,
"Range": 0
},
{
"Id": "csakany",
"Name": "Csákány",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 5,
"DamageDice": 6,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 4,
"Range": 0
},
{
"Id": "darda",
"Name": "Dárda",
"Type": "kozelharci",
"Ketkezes": true,
"Egzotikus": false,
"Rohamfelfogo": true,
"Price": 10,
"DamageDice": 8,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 3,
"Range": 0
},
{
"Id": "fejsze",
"Name": "Fejsze",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 6,
"DamageDice": 6,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 3,
"Range": 10
},
{
"Id": "torpe_fejsze",
"Name": "Törpe fejsze",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": true,
"Rohamfelfogo": false,
"Price": 30,
"DamageDice": 10,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 3,
"Range": 0
},
{
"Id": "handzsar",
"Name": "Handzsár",
"Type": "kozelharci",
"Ketkezes": true,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 50,
"DamageDice": 4,
"NumberOfDamageDice": 2,
"CritRangeStart": 18,
"CritMultiplier": 2,
"Range": 0
},
{
"Id": "harci_kalapacs",
"Name": "Harci kalapács",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 12,
"DamageDice": 8,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 3,
"Range": 0
},
{
"Id": "harci_kalapacs_2",
"Name": "Kétkezes harci kalapács",
"Type": "kozelharci",
"Ketkezes": true,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 25,
"DamageDice": 10,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 3,
"Range": 0
},
{
"Id": "fattyu_kard",
"Name": "Fattyú kard",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": true,
"Rohamfelfogo": false,
"Price": 35,
"DamageDice": 10,
"NumberOfDamageDice": 1,
"CritRangeStart": 19,
"CritMultiplier": 2,
"Range": 0
},
{
"Id": "rovid_kard",
"Name": "Rövid kard",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 10,
"DamageDice": 6,
"NumberOfDamageDice": 1,
"CritRangeStart": 19,
"CritMultiplier": 2,
"Range": 0
},
{
"Id": "hosszu_kard",
"Name": "Hosszú kard",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 15,
"DamageDice": 8,
"NumberOfDamageDice": 1,
"CritRangeStart": 19,
"CritMultiplier": 2,
"Range": 0
},
{
"Id": "kard_2",
"Name": "Kétkezes kard",
"Type": "kozelharci",
"Ketkezes": true,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 50,
"DamageDice": 6,
"NumberOfDamageDice": 2,
"CritRangeStart": 19,
"CritMultiplier": 2,
"Range": 0
},
{
"Id": "kasza",
"Name": "Kasza",
"Type": "kozelharci",
"Ketkezes": true,
"Egzotikus": true,
"Rohamfelfogo": false,
"Price": 18,
"DamageDice": 4,
"NumberOfDamageDice": 2,
"CritRangeStart": 20,
"CritMultiplier": 4,
"Range": 0
},
{
"Id": "landzsa",
"Name": "Lándzsa",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 5,
"DamageDice": 8,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 2,
"Range": 20
},
{
"Id": "sarlo",
"Name": "Sarló",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 3,
"DamageDice": 6,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 2,
"Range": 0
},
{
"Id": "szablya",
"Name": "Szablya",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 15,
"DamageDice": 6,
"NumberOfDamageDice": 1,
"CritRangeStart": 18,
"CritMultiplier": 2,
"Range": 0
},
{
"Id": "szigony",
"Name": "Háromágú szigony",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 10,
"DamageDice": 8,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 2,
"Range": 10
},
{
"Id": "tor",
"Name": "Tőr",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 2,
"DamageDice": 4,
"NumberOfDamageDice": 1,
"CritRangeStart": 19,
"CritMultiplier": 2,
"Range": 10
},
{
"Id": "ostor",
"Name": "Ostor",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": true,
"Rohamfelfogo": false,
"Price": 1,
"DamageDice": 3,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 2,
"Range": 0
},
{
"Id": "okol",
"Name": "Ököl",
"Type": "kozelharci",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 0,
"DamageDice": 4,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 2,
"Range": 0
},
{
"Id": "dobodarda",
"Name": "Dobódárda",
"Type": "lofegyver",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 0.1,
"DamageDice": 6,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 2,
"Range": 30
},
{
"Id": "dobotu",
"Name": "Dobótű",
"Type": "lofegyver",
"Ketkezes": false,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 0.5,
"DamageDice": 4,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 2,
"Range": 20
},
{
"Id": "rovid_ij",
"Name": "Rövid íj",
"Type": "lofegyver",
"Ketkezes": true,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 30,
"DamageDice": 6,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 3,
"Range": 60
},
{
"Id": "hosszu_ij",
"Name": "Hosszú íj",
"Type": "lofegyver",
"Ketkezes": true,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 75,
"DamageDice": 8,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 3,
"Range": 70
},
{
"Id": "visszacsapo_ij",
"Name": "Visszacsapó íj",
"Type": "lofegyver",
"Ketkezes": true,
"Egzotikus": true,
"Rohamfelfogo": false,
"Price": 100,
"DamageDice": 8,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 3,
"Range": 110
},
{
"Id": "parittya",
"Name": "Parittya",
"Type": "lofegyver",
"Ketkezes": true,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 0,
"DamageDice": 4,
"NumberOfDamageDice": 1,
"CritRangeStart": 20,
"CritMultiplier": 2,
"Range": 50
},
{
"Id": "konnyu_szamszerij",
"Name": "Könnyű számszeríj",
"Type": "lofegyver",
"Ketkezes": true,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 35,
"DamageDice": 8,
"NumberOfDamageDice": 1,
"CritRangeStart": 19,
"CritMultiplier": 2,
"Range": 80
},
{
"Id": "nehez_szamszerij",
"Name": "Nehéz számszeríj",
"Type": "lofegyver",
"Ketkezes": true,
"Egzotikus": false,
"Rohamfelfogo": false,
"Price": 50,
"DamageDice": 10,
"NumberOfDamageDice": 1,
"CritRangeStart": 19,
"CritMultiplier": 2,
"Range": 120
}
]
}
@@ -0,0 +1,419 @@
{
"data": [
{
"Id": "acel_es_kova",
"Name": "Acél és kova",
"Type": "simple",
"PriceInGold": 0.1
},
{
"Id": "aso",
"Name": "Ásó",
"Type": "simple",
"PriceInGold": 1
},
{
"Id": "vas_bogracs",
"Name": "Bogrács, vas",
"Type": "simple",
"PriceInGold": 0.5
},
{
"Id": "borostomlo",
"Name": "Borostömlő",
"Type": "simple",
"PriceInGold": 1
},
{
"Id": "csakany",
"Name": "Csákány",
"Type": "simple",
"PriceInGold": 5
},
{
"Id": "elelem",
"Name": "Élelem, tartós úti, egy napra",
"Type": "simple",
"PriceInGold": 0.5
},
{
"Id": "faklya",
"Name": "Fáklya",
"Type": "simple",
"PriceInGold": 0.01
},
{
"Id": "farud",
"Name": "Farúd, 10",
"Type": "simple",
"PriceInGold": 0.03
},
{
"Id": "feszitovas",
"Name": "Feszítővas",
"Type": "simple",
"PriceInGold": 2
},
{
"Id": "fustulo",
"Name": "Füstölő, rúd",
"Type": "simple",
"PriceInGold": 1
},
{
"Id": "gyertya_viasz",
"Name": "Gyertya, viasz",
"Type": "simple",
"PriceInGold": 0.1
},
{
"Id": "halasz_halo",
"Name": "Háló, halász, 5*5",
"Type": "simple",
"PriceInGold": 4
},
{
"Id": "hatizsak",
"Name": "Hátizsák",
"Type": "simple",
"PriceInGold": 2
},
{
"Id": "hordo_kicsi",
"Name": "Hordó, kicsi",
"Type": "simple",
"PriceInGold": 2
},
{
"Id": "illatszer",
"Name": "Illatszer",
"Type": "simple",
"PriceInGold": 5
},
{
"Id": "kotel_kender_50",
"Name": "Kötél, kender, 50",
"Type": "simple",
"PriceInGold": 1
},
{
"Id": "kotel_selyem_50",
"Name": "Kötél, selyem, 50",
"Type": "simple",
"PriceInGold": 10
},
{
"Id": "kreta",
"Name": "Kréta",
"Type": "simple",
"PriceInGold": 0.01
},
{
"Id": "lada_fa_kicsi",
"Name": "Láda, fa, kicsi",
"Type": "simple",
"PriceInGold": 2
},
{
"Id": "lada_fa_nagy",
"Name": "Láda, fa, nagy",
"Type": "simple",
"PriceInGold": 5
},
{
"Id": "lada_vas_kicsi",
"Name": "Láda, vas, kicsi",
"Type": "simple",
"PriceInGold": 9
},{
"Id": "lada_vas_nagy",
"Name": "Láda, vas, nagy",
"Type": "simple",
"PriceInGold": 28
},
{
"Id": "lakat_egyszeru",
"Name": "Lakat, egyszerű",
"Type": "simple",
"PriceInGold": 20
},
{
"Id": "lakat_kivalo",
"Name": "Lakat, kiváló",
"Type": "simple",
"PriceInGold": 80
},
{
"Id": "olaj_lampa_egyszeru",
"Name": "Lámpa, olaj, egyszerű",
"Type": "simple",
"PriceInGold": 1
},
{
"Id": "olaj_lampa_vedett",
"Name": "Lámpa, olaj, védett",
"Type": "simple",
"PriceInGold": 7
},
{
"Id": "olaj_lampa_nagy",
"Name": "Lámpa, olaj, nagy",
"Type": "simple",
"PriceInGold": 12
},
{
"Id": "lanc_10",
"Name": "Lánc, 10",
"Type": "simple",
"PriceInGold": 10
},
{
"Id": "maszovas",
"Name": "Mászóvas",
"Type": "simple",
"PriceInGold": 1
},
{
"Id": "olaj_palck",
"Name": "Olaj, palack",
"Type": "simple",
"PriceInGold": 1
},
{
"Id": "palack_cserep",
"Name": "Palack, cserép",
"Type": "simple",
"PriceInGold": 0.03
},
{
"Id": "palack_uveg",
"Name": "Palack, üveg",
"Type": "simple",
"PriceInGold": 2
},
{
"Id": "papir",
"Name": "Papír, lap",
"Type": "simple",
"PriceInGold": 0.4
},
{
"Id": "pecsetgyuru",
"Name": "Pecsétgyűrű",
"Type": "simple",
"PriceInGold": 8
},
{
"Id": "pecsetviasz",
"Name": "Pecsétviasz",
"Type": "simple",
"PriceInGold": 1
},
{
"Id": "pergamen",
"Name": "Pergamen, lap",
"Type": "simple",
"PriceInGold": 0.2
},
{
"Id": "sator",
"Name": "Sátor",
"Type": "simple",
"PriceInGold": 10
},
{
"Id": "sav",
"Name": "Sav",
"Type": "simple",
"PriceInGold": 10
},
{
"Id": "szappan",
"Name": "Szappan",
"Type": "simple",
"PriceInGold": 0.5
},
{
"Id": "szentelt_viz",
"Name": "Szenteltvíz",
"Type": "simple",
"PriceInGold": 25
},
{
"Id": "szog",
"Name": "Szög",
"Type": "simple",
"PriceInGold": 0.01
},
{
"Id": "takaro",
"Name": "Takaró",
"Type": "simple",
"PriceInGold": 0.2
},
{
"Id": "tarsoly",
"Name": "Tarsoly",
"Type": "simple",
"PriceInGold": 1
},
{
"Id": "tegez",
"Name": "Tegez",
"Type": "simple",
"PriceInGold": 2
},
{
"Id": "telercstarto",
"Name": "Tekercstartó",
"Type": "simple",
"PriceInGold": 5
},
{
"Id": "tinta",
"Name": "Tinta",
"Type": "simple",
"PriceInGold": 8
},
{
"Id": "tukor",
"Name": "Tükör",
"Type": "simple",
"PriceInGold": 10
},
{
"Id": "uvegcse",
"Name": "Üvegcse",
"Type": "simple",
"PriceInGold": 1
},
{
"Id": "vodor",
"Name": "Vödör",
"Type": "simple",
"PriceInGold": 0.5
},
{
"Id": "zsak",
"Name": "Zsák",
"Type": "simple",
"PriceInGold": 0.1
},
{
"Id": "elokelo_ruha",
"Name": "Előkelő",
"Type": "clothes",
"PriceInGold": 75
},
{
"Id": "elokelo_ruha",
"Name": "Előkelő",
"Type": "clothes",
"PriceInGold": 75
},
{
"Id": "kozrendu_ruha",
"Name": "Közrendű",
"Type": "clothes",
"PriceInGold": 0.1
},
{
"Id": "meleg_ruha",
"Name": "Meleg",
"Type": "clothes",
"PriceInGold": 8
},
{
"Id": "mesterember_ruha",
"Name": "Mesterember",
"Type": "clothes",
"PriceInGold": 1
},
{
"Id": "nagyuri_ruha",
"Name": "Nagyúri",
"Type": "clothes",
"PriceInGold": 200
},
{
"Id": "papi_ruha",
"Name": "Papi",
"Type": "clothes",
"PriceInGold": 5
},
{
"Id": "utazo_ruha",
"Name": "Utazó",
"Type": "clothes",
"PriceInGold": 1
},
{
"Id": "alkimista_labor",
"Name": "Alkimista labor",
"Type": "special",
"PriceInGold": 500
},
{
"Id": "homokora",
"Name": "Homokóra",
"Type": "special",
"PriceInGold": 25
},
{
"Id": "merleg",
"Name": "Mérleg",
"Type": "special",
"PriceInGold": 2
},
{
"Id": "nagyito",
"Name": "Nagyító",
"Type": "special",
"PriceInGold": 100
},
{
"Id": "szent_szimbolum",
"Name": "Szent szimbólum",
"Type": "special",
"PriceInGold": 25
},
{
"Id": "tavcso",
"Name": "Távcső",
"Type": "special",
"PriceInGold": 1000
},
{
"Id": "tolvajszerszamok",
"Name": "Tolvajszerszámok",
"Type": "special",
"PriceInGold": 30
},
{
"Id": "varazskomponens_tarto",
"Name": "Varázskomponenstartó",
"Type": "special",
"PriceInGold": 5
},
{
"Id": "varazskonyv",
"Name": "Varázskönyv, üres",
"Type": "special",
"PriceInGold": 15
},
{
"Id": "vizora",
"Name": "Vízóra",
"Type": "special",
"PriceInGold": 500
},
{
"Id": "zeneszerszam",
"Name": "Zeneszerszám, egyszerű",
"Type": "special",
"PriceInGold": 5
}
]
}
@@ -0,0 +1,101 @@
import pancelok from './pancel.json'
import pajzsok from './pajzs.json'
import fegyverek from './fegyver.json'
export type PancelType = 'konnyu' | 'kozepes' | 'nehez'
export const PancelTypes: PancelType[] = ['konnyu', 'kozepes', 'nehez']
export interface Pancel {
Id: string,
Name: string,
Type: PancelType
VO: number,
Price: number,
}
export interface Pajzs {
Id: string,
Name: string,
Type: 'pajzs'
VO: number,
Price: number,
}
export interface Fegyver {
Id: string,
Name: string,
Type: 'kozelharci' | 'lofegyver'
Ketkezes: boolean,
Egzotikus: boolean,
Rohamfelfogo: boolean,
Price: number,
DamageDice: number,
NumberOfDamageDice: number,
CritRangeStart: number,
CritMultiplier: number,
Range: number
}
export interface KarakterFelszereles {
pancelID?: string
pajzsID?: string
fegyverIDk: string[]
}
export const DefaultFelszereles: KarakterFelszereles = {
fegyverIDk: [],
}
export function PancelTypeLabel(type: PancelType): string {
let result: string
switch(type) {
case 'konnyu':
result = 'Könnyű'
break
case 'kozepes':
result = 'Közepes'
break
case 'nehez':
result = 'Nehéz'
break
default:
result = 'Egyéb páncél'
}
return result
}
export function GetPancel(id?: string): Pancel | undefined {
if (id == null) {
return undefined
}
const filteredPancel = pancelok['pancel'].filter(p => p.Id === id) as Pancel[]
return filteredPancel.length > 0 ? filteredPancel[0] : undefined
}
export function MaxAbilityVO(p: PancelType) {
switch(p){
case 'konnyu': return 3;
case 'kozepes': return 2;
case 'nehez': return 1;
default: return 9999;
}
}
export function GetPajzs(id?: string): Pajzs | undefined {
if (id == null) {
return undefined
}
const filtered = pajzsok['pajzs'].filter(p => p.Id === id) as Pajzs[]
return filtered.length > 0 ? filtered[0] : undefined
}
export function GetFegyver(id: string): Fegyver | undefined {
return fegyverek.data.find(f => f.Id === id) as Fegyver | undefined
}
export function GetFegyverek(ids: string[]): Fegyver[] {
return ids.map(GetFegyver).filter(x => x != null).map(x => x as Fegyver)
}
export const defaultFegyverID = 'okol'
export const defaultFegyver = GetFegyver('okol')!
@@ -0,0 +1,59 @@
export type JellemID = 'TJ' | 'SJ' | 'KJ' | 'TS' | 'S' | 'KS' | 'TG' | 'SG' | 'KG'
export const AllJellemIDs: JellemID[] = ['TJ', 'SJ', 'KJ', 'TS', 'S', 'KS', 'TG', 'SG', 'KG']
export interface Jellem {
ID: JellemID
Label: string
Description: string
}
export function GetJellem(id: JellemID): Jellem {
switch (id) {
case "TJ": return {
ID: "TJ",
Label: "Törvényes Jó",
Description: "A törvényes jó karakterek betartják és fontosnak tartják az igazságos törvényeket, valamint a kiérdemelt tekintély tiszteletét. Viselkedésükben lehetőleg egyenesek, és nem okoznak szükségtelen károkat másnak; továbbá segítenek a kevésbé szerencséseken.",
}
case "SJ": return {
ID: "SJ",
Label: "Semleges Jó",
Description: "A semleges jó karakterek általában jóindulatúak, de nem jellemző rájuk sem a törvényes jók szigora, sem a kaotikus jók kiszámíthatatlansága.",
}
case "KJ": return {
ID: "KJ",
Label: "Kaotikus Jó",
Description: "A kaotikus jó karakterek nagy súlyt fektetnek az egyéni szabadságra és a jótéteményekre akkor is, ha ezek a nézetek és tettek ellentétesek a társadalmi szokásokkal és írott törvényekkel.",
}
case "TS": return {
ID: "TS",
Label: "Törvényes Semleges",
Description: "A törvényes semleges karakterek szigorúan betartják a törvényeket és adott szavukat, de ez a törvénytisztelet nem altruizmusból, hanem elsősorban önzésből, közömbösségből vagy a törvények öncélú tiszteletéből fakad.",
}
case "S": return {
ID: "S",
Label: "Semleges",
Description: "A semleges karakterek vagy nem törődnek a fontos erkölcsi kérdésekkel azaz nem kötelezik el magukat egyes ügyek mellett vagy éppen ellenkezőleg, aktívan a különféle nézetek közötti egyensúlyra és harmóniára törekednek. Az átlagemberek többsége ehhez a jellemhez tartozik.",
}
case "KS": return {
ID: "KS",
Label: "Kaotikus Semleges",
Description: "A kaotikus semleges karakterek kiszámíthatatlanok, saját boldogulásukra törekednek akár mások ellenében is. A kalandozók igen jelentős része kaotikus semleges jellemű"
}
case "TG": return {
ID: "TG",
Label: "Törvényes Gonosz",
Description: "A törvényes gonosz karakterek lételeme a törvényadta lehetőségek kihasználása mások kisemmizésére és önmaguk gyarapítására. Nem csoda, hogy számos zsarnok és kiskirály e jellem képviselője.",
}
case "SG": return {
ID: "SG",
Label: "Semleges Gonosz",
Description: "A semleges gonosz karakterek döntően aljasak és rosszindulatúak, s céljaikat hol a törvények kijátszásával, hol a rájuk való hivatkozással érik el."
}
case "KG": return {
ID: "KG",
Label: "Kaotikus Gonosz",
Description: "A kaotikus gonosz karakterek kíméletlenül, mindenen és mindenkin átgázolva hajszolják a hatalmat vagy a vagyont, s ennek érdekében bármilyen gaztettre képesek."
}
}
}
@@ -0,0 +1,41 @@
import {Faj} from "./faj";
import {Osztaly} from "./osztaly";
import {KepzettsegId} from "./kepzettsegek";
import {JellemID} from "./jellem";
import { DefaultFelszereles, KarakterFelszereles } from './felszereles'
import {KarakterTulajdonsagok, Tulajdonsag, TulajdonsagDefaults} from "./tulajdonsag";
export interface KarakterInputs {
name: string,
nem?: string,
kor: number,
jellem: JellemID,
isten?: string,
faj: Faj,
osztaly: Osztaly,
tulajdonsagok: KarakterTulajdonsagok,
kepzettsegek: KepzettsegId[],
tolvajKepzettsegek?: KepzettsegId[]
szint: number
hpRolls: number[],
tulajdonsagNovelesek: Tulajdonsag[],
harcosSpecializaciok: string[],
kalozKritikus: string[],
felszereles: KarakterFelszereles,
}
export const KarakterDefaults: KarakterInputs = {
name: "Névtelen Kalandozó",
faj: Faj.Ember,
kor: 20,
jellem: 'S',
tulajdonsagok: TulajdonsagDefaults,
osztaly: Osztaly.Kaloz,
kepzettsegek: [],
szint: 1,
hpRolls: [],
tulajdonsagNovelesek: [],
harcosSpecializaciok: [],
kalozKritikus: [],
felszereles: DefaultFelszereles,
}
@@ -0,0 +1,297 @@
import {Osztaly} from "./osztaly";
import {Faj} from "./faj";
import {KarakterInputs} from "./karakter";
import {Modifier, Tulajdonsag} from "./tulajdonsag";
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_jelek_olvasasa' | 'k_koncentracio' | 'k_lovaglas' | 'k_maszas' | 'k_megfigyeles' | 'k_meregkeveres' | 'k_mesterseg' | 'k_nyomkereses' | 'k_osonas' | 'k_rejtozes' | 'k_szabadulomuveszet' | 'k_tudas' | 'k_ugras' | 'k_uszas' | 'k_varazslatismeret' | 'k_zarnyitas' | 'k_zsebmetszes'
export interface Kepzettseg {
Id: KepzettsegId,
Name: string,
Description: string,
Tulajdonsag: Tulajdonsag[],
Osztalyok?: Osztaly[],
}
export const KepzettsegLista: Kepzettseg[] = [
{
Id: 'k_alkimia',
Name: 'Alkímia',
Description: 'Különféle különleges anyagok, pl. savak, görögtűz, stb. kikeverésére szolgál. Használatához gyakran jól felszerelt laboratórium szükséges. Az Alkímia képzettséggel a karakter képes beazonosítani a különféle vegyi anyagokat és mérgeket, bizonyos esetekben varázsitalokat is.',
Tulajdonsag: [Tulajdonsag.Intelligencia],
Osztalyok: [Osztaly.Varazslo, Osztaly.Illuzionista],
},
{
Id: 'k_alcazas',
Name: 'Álcázás',
Description: 'Álöltözetek készítése és effektív használata. Próbadobást akkor kell dobni, ha az álca hitelességét valaki kétségbe vonhatja (általában titkos dobás; az eredményt csak a mesélő ismeri).',
Tulajdonsag: [Tulajdonsag.Karizma],
Osztalyok: [Osztaly.Tolvaj],
},
{
Id: 'k_allatidomitas',
Name: 'Állatidomítás',
Description: 'Állatok megszelídítésére, betanítására szolgál. A járatos karakter próbát tehet arra is, hogy a különféle vadállatok ne tekintsék ellenségüknek vagy prédájuknak',
Tulajdonsag: [Tulajdonsag.Karizma],
},
{
Id: 'k_csapdak',
Name: 'Csapdák állítása',
Description: 'A karakter a rendelkezésre álló idő és alapanyagok függvényében készíthet szimpla vagy komplex csapdákat. Ha egy csapda eltávolítására tett próba sikertelen, az bizonyos eséllyel kisülhet és veszélybe sodorhatja az óvatlan próbálkozót.',
Tulajdonsag: [Tulajdonsag.Ugyesseg],
Osztalyok: [Osztaly.Tolvaj],
},
{
Id: 'k_csillagjoslas',
Name: 'Csillagjóslás',
Description: 'A karakter a képzettséggel egyrészt képes meghatározni a karakterek hozzávetőleges pozícióját a térképen, másrészt egyszerűbb jóslatokat készíthet.',
Tulajdonsag: [Tulajdonsag.Bolcsesseg],
Osztalyok: [Osztaly.Varazslo, Osztaly.Illuzionista],
},
{
Id: 'k_egyensulyozas',
Name: 'Egyensúlyozás',
Description: 'Omlatag talajon vagy keskeny járófelületen való átjutáshoz szükséges képzettség. Ha a próba sikertelen, a karakter lezuhan.',
Tulajdonsag: [Tulajdonsag.Ugyesseg],
Osztalyok: [Osztaly.Tolvaj],
},
{
Id: 'k_eloadas',
Name: 'Előadás',
Description: 'A képzettséggel rendelkező karakter járatos a hangszerek használatában, éneklésben, eposzmondásban, stb.',
Tulajdonsag: [Tulajdonsag.Karizma],
},
{
Id: 'k_ertekbecsles',
Name: 'Értékbecslés',
Description: 'Sikeres próba esetén a karakter hozzávetőlegesen felméri egy tárgy pénzbeli értékét és kiszúrja a hamisítványokat.',
Tulajdonsag: [Tulajdonsag.Intelligencia],
},
{
Id: 'k_gyogyitas',
Name: 'Gyógyítás',
Description: 'A sebek ellátásának, mérgek és betegségek semlegesítésének vagy enyhítésének tudománya. Ha a képzettséget sikeresen alkalmazzák közvetlenül harc után, a bekötözött karakter visszanyer 1d4 elvesztett Hp-t.',
Tulajdonsag: [Tulajdonsag.Bolcsesseg],
},
{
Id: 'k_hajozas',
Name: 'Hajózás',
Description: 'A karakter ismeri a hajózás trükkjeit, tehát elkormányoz kisebb vitorlásokat vagy gályákat, tudja hogyan kell elkerülni a zátonyokat, képes javításokat végezni, stb.',
Tulajdonsag: [Tulajdonsag.Ugyesseg],
},
{
Id: 'k_hallgatozas',
Name: 'Hallgatózás',
Description: 'Alig hallható zajok, beszéd meghallása.',
Tulajdonsag: [Tulajdonsag.Bolcsesseg],
},
{
Id: 'k_hamisitas',
Name: 'Hamisítás',
Description: 'A képzettséggel hamis pénz, iratok, pecsétek és értéktárgyak készíthetők. Próbadobást akkor kell dobni, ha a hamisítvány hitelességét valaki kétségbe vonhatja.',
Tulajdonsag: [Tulajdonsag.Intelligencia],
Osztalyok: [Osztaly.Tolvaj],
},
{
Id: 'k_jelek_olvasasa',
Name: 'Jelek Olvasása',
Description: 'A képzettséggel a karakter el tud olvasni más nyelven írt írásokat és feliratokat.',
Tulajdonsag: [Tulajdonsag.Intelligencia],
Osztalyok: [Osztaly.Tolvaj, Osztaly.Varazslo, Osztaly.Illuzionista],
},
{
Id: 'k_koncentracio',
Name: 'Koncentráció',
Description: 'Ez a képesség arra teszi alkalmassá a varázshasználót, hogy nehéz körülmények között sebesülten, vágtató lovon, hánykolódó hajón, stb. is alkalmazhassa varázslatait.',
Tulajdonsag: [Tulajdonsag.Bolcsesseg],
Osztalyok: [Osztaly.Varazslo, Osztaly.Illuzionista],
},
{
Id: 'k_lovaglas',
Name: 'Lovaglás',
Description: 'A karakter kiemelkedően jól ért a lovagláshoz; harcban és nehéz helyzetekben is csodákra képes sarkallni hátasát.',
Tulajdonsag: [Tulajdonsag.Ugyesseg],
},
{
Id: 'k_maszas',
Name: 'Mászás',
Description: 'A képzettség olyan falakra való felmászásra alkalmas, ami egy átlagembernek komol gondot jelentene. Sikertelen próba esetén a karakter lezuhan, vagy a feladat meghaladja a képességeit.',
Tulajdonsag: [Tulajdonsag.Ero],
},
{
Id: 'k_megfigyeles',
Name: 'Megfigyelés',
Description: 'Ez a képzettség olyan érzékelési képességet jelent, ami meghaladja az átlagemberét. Így olyan részletek vehetők észre, amelye szabad szemmel láthatóak, de csak az avatott megfigyelő számára.',
Tulajdonsag: [Tulajdonsag.Bolcsesseg],
},
{
Id: 'k_meregkeveres',
Name: 'Méregkeverés',
Description: 'A megfelelő eszközök és alapanyagok birtokában különféle mérgek készíthetők. Sikertelen próbadobás esetén a keverék hatástalan, vagy a méregkeverő veszélybe sodorja önmagát.',
Tulajdonsag: [Tulajdonsag.Intelligencia],
Osztalyok: [Osztaly.Tolvaj],
},
{
Id: 'k_mesterseg',
Name: 'Mesterség',
Description: 'A karakter járatos egy kiválasztott mesterségben pl. kovácsolás, gyertyaöntés, adószedés, ötvösség. A kézügyességet igénylő mesterségekhez Ügyesség, a szellemi foglalkozásokhoz Intelligencia szükséges.',
Tulajdonsag: [Tulajdonsag.Intelligencia, Tulajdonsag.Ugyesseg],
},
{
Id: 'k_nyomkereses',
Name: 'Nyomkeresés',
Description: 'A karakter képes a nyomok követésére és hozzávetőleges azonosítására, vagyis megtudhatja, milyen régiek, miféle szerzet hagyta őket, milyen gyorsan haladt a nyomokat hagyó, stb.',
Tulajdonsag: [Tulajdonsag.Bolcsesseg],
},
{
Id: 'k_osonas',
Name: 'Osonás',
Description: 'Csöndes és általában viszonylag gyors mozgás.',
Tulajdonsag: [Tulajdonsag.Ugyesseg],
},
{
Id: 'k_rejtozes',
Name: 'Rejtőzés',
Description: 'A képzettséget ismerő karakter olyan helyeken is elrejtőzhet, ahol más nem volna képes például sötétebb sarkokban, árnyékokban.',
Tulajdonsag: [Tulajdonsag.Ugyesseg],
},
{
Id: 'k_szabadulomuveszet',
Name: 'Szabadulóművészet',
Description: 'A karakter képes kiszabadulni kötelékeiből és láncaiból.',
Tulajdonsag: [Tulajdonsag.Ugyesseg],
Osztalyok: [Osztaly.Tolvaj],
},
{
Id: 'k_tudas',
Name: 'Tudás',
Description: 'A karakter járatos egy bizonyos tudományterületen. Számos példa lehetséges, a leggyakoribbak a Történelemismeret, Országismeret, Mágiaismeret (csak varázshasználók sajátíthatják el), Teológia, Vadonismeret és a Növényismeret.',
Tulajdonsag: [Tulajdonsag.Intelligencia],
},
{
Id: 'k_ugras',
Name: 'Ugrás',
Description: 'A karakter képes átugrani szélesebb vagy magasabb akadályokat is, és tompíthatja az esésből származó sebesüléseket. Ha a próba sikertelen, a karakter leesik.',
Tulajdonsag: [Tulajdonsag.Ero],
},
{
Id: 'k_uszas',
Name: 'Úszás',
Description: 'A karakter jól tud úszni, akár nehéz körülmények között (zajló vízben, megterhelten, stb.) is képes a felszínen maradni.',
Tulajdonsag: [Tulajdonsag.Ero],
},
{
Id: 'k_varazslatismeret',
Name: 'Varázslatismeret',
Description: 'A Mágiaismerettel ellentétben ez a képzettség nem a mágia elméletében, hanem a konkrét varázslatokban való ismereteket méri. Sikeres próbával a karakter azonosíthatja az elmondás alatt álló varázslatokat (NF= a mentődobás nehézsége), bizonyos mágikus effektusokat, csapdákat, stb.',
Tulajdonsag: [Tulajdonsag.Intelligencia],
Osztalyok: [Osztaly.Illuzionista, Osztaly.Varazslo],
},
{
Id: 'k_zarnyitas',
Name: 'Zárnyitás',
Description: 'Tolvajszerszámok segítségével a karakter felnyithatja a különféle mechanikus zárakat. Sikertelen próba esetén nem képes az adott zár felnyitására egészen addig, amíg új tapasztalatokat nem szerez (vagyis a következő szintlépéséig).',
Tulajdonsag: [Tulajdonsag.Ugyesseg],
Osztalyok: [Osztaly.Tolvaj],
},
{
Id: 'k_zsebmetszes',
Name: 'Zsebmetszés',
Description: 'Őrizetlenül hagyott vagy övön, zsebben található kisebb tárgyak észrevétlen eltulajdonítása. Ha a próba sikertelen, a karaktert észreveszik vagy legalábbis gyanút kelt.',
Tulajdonsag: [Tulajdonsag.Ugyesseg],
Osztalyok: [Osztaly.Tolvaj],
},
]
export const TolvajKepzettsegList = KepzettsegLista.filter(x => x.Osztalyok?.includes(Osztaly.Tolvaj))
export const Kepzettsegek: Record<KepzettsegId, Kepzettseg> =
KepzettsegLista.reduce(
(acc: Record<KepzettsegId, Kepzettseg>, k) => { acc[k.Id] = k; return acc; },
{} as Record<KepzettsegId, Kepzettseg>);
export function AvailableKezpettsegList(osztaly: Osztaly): Kepzettseg[] {
return KepzettsegLista.filter(k => k.Osztalyok == null || k.Osztalyok.includes(osztaly))
}
export function GetNumberOfKepzettsegek(t_int: number, faj: Faj, max: number) {
let numberOfKepzettseg = 3 + Modifier(t_int) + (faj === Faj.Ember ? 1 : 0)
if (numberOfKepzettseg < 1) {
numberOfKepzettseg = 1
console.log('Adjusted Number of Kepzetsegek = ', numberOfKepzettseg)
}
if (numberOfKepzettseg > max) {
numberOfKepzettseg = max
console.log('Adjusted Number of Kepzetsegek = ', numberOfKepzettseg)
}
return numberOfKepzettseg;
}
export function SetDefaultTolvajKepzettsegek(karakter: Pick<KarakterInputs, 'osztaly' | 'tolvajKepzettsegek'>, changeTolvajKepzettsegek: (tolvajKepzettsegek?: KepzettsegId[]) => void) {
if (karakter.osztaly !== Osztaly.Tolvaj && karakter.tolvajKepzettsegek != null) {
changeTolvajKepzettsegek(undefined)
}
if (karakter.osztaly === Osztaly.Tolvaj && (karakter.tolvajKepzettsegek == null || karakter.tolvajKepzettsegek.length === 0)) {
const tolvajKepzettsegek = TolvajKepzettsegList.slice(0, 4).map(x => x.Id)
changeTolvajKepzettsegek(tolvajKepzettsegek)
}
}
export function GetKepzettsegListaN(karakter: Pick<KarakterInputs, 'osztaly' | 'faj' | 'kepzettsegek' | 'tolvajKepzettsegek'>) {
const availableKepzettsegList = AvailableKezpettsegList(karakter.osztaly)
return (n: number): Kepzettseg[] => {
let available = [...availableKepzettsegList]
if (n === 0) {
if (karakter.faj === Faj.Eszaki) {
return [Kepzettsegek.k_hajozas]
}
if (karakter.faj === Faj.Etuniai) {
return [Kepzettsegek.k_lovaglas]
}
if (karakter.faj === Faj.Birodalmi && karakter.osztaly !== Osztaly.Tolvaj) {
available = [Kepzettsegek.k_meregkeveres, Kepzettsegek.k_alkimia]
}
}
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))
}
}
export function SetDefaultKepzettsegek(karakter: Pick<KarakterInputs, 'osztaly' | 'faj' | 'tulajdonsagok' | 'kepzettsegek' | 'tolvajKepzettsegek'>, changeKepzettsegek: (k: KepzettsegId[]) => void) {
const availableKepzettsegList = AvailableKezpettsegList(karakter.osztaly)
const numberOfKepzettsegek = GetNumberOfKepzettsegek(karakter.tulajdonsagok.t_int, karakter.faj, availableKepzettsegList.length)
let kepzettsegek = [...karakter.kepzettsegek]
let mustChangeKepzettsegek = false
const restrictedKepzettsegek = GetKepzettsegListaN(karakter)(0)
if (kepzettsegek.length === 0) {
if (restrictedKepzettsegek.length > 0) {
kepzettsegek.push(restrictedKepzettsegek[0].Id)
mustChangeKepzettsegek = true
}
} else {
if (!restrictedKepzettsegek.includes(Kepzettsegek[kepzettsegek[0]])) {
kepzettsegek[0] = restrictedKepzettsegek[0].Id
mustChangeKepzettsegek = true
}
}
if (kepzettsegek.length < numberOfKepzettsegek) {
for (let i = kepzettsegek.length; i < numberOfKepzettsegek; i++) {
const kepzettsegLista = GetKepzettsegListaN({...karakter, kepzettsegek})(i)
if (kepzettsegLista.length > 0) {
kepzettsegek.push(kepzettsegLista[0].Id)
}
}
mustChangeKepzettsegek = true
}
if (kepzettsegek.length > numberOfKepzettsegek){
kepzettsegek = kepzettsegek.slice(0, numberOfKepzettsegek)
mustChangeKepzettsegek = true
}
if (mustChangeKepzettsegek){
changeKepzettsegek(kepzettsegek)
}
}
@@ -0,0 +1,60 @@
import {BaseHP} from "./masodlagos_ertekek";
import {KarakterInputs} from "./karakter";
import {GetFajDetails} from "./faj";
import {Osztaly} from "./osztaly";
import fegyverek from "./fegyver.json"
import {AllowedFegyver} from "./allowed-fegyver";
import {TulajdonsagokTotal} from "./tulajdonsag";
import {dAny} from "../../shared/domain-models/kockak";
import {TulajdonsagIDs} from "./tulajdonsag";
export function PickableSpecializationFilter(existingSpecilizations: string[], szint: number) {
return (fegyver: {Id: string}) => existingSpecilizations.filter(x => x === fegyver.Id).length < (szint < 9 ? 1 : 2)
}
export function LevelUp(karakter: KarakterInputs, changeKarakter: (input: KarakterInputs) => void) {
const tulajdonsagokTotal = TulajdonsagokTotal(karakter)
const szint = karakter.szint + 1
const dice = BaseHP(karakter.osztaly)
const roll = dAny(dice)
const hpRolls = [...karakter.hpRolls, roll]
console.log(`Level ${szint} HP roll on d${dice} is: ${roll}`)
let tulajdonsagNovelesek = karakter.tulajdonsagNovelesek
if (szint % 4 === 0) {
for (const tulajdonsag of TulajdonsagIDs) {
if (tulajdonsagokTotal[tulajdonsag] < 18) {
tulajdonsagNovelesek = [...tulajdonsagNovelesek, tulajdonsag]
break;
}
}
}
let harcosSpecializaciok = karakter.harcosSpecializaciok
if(karakter.osztaly === Osztaly.Harcos && szint % 2 === 1) {
const nextPickableFegyverId = fegyverek.data.filter(PickableSpecializationFilter(karakter.harcosSpecializaciok, karakter.szint))[0].Id
harcosSpecializaciok = [...harcosSpecializaciok, nextPickableFegyverId]
}
let kalozKritikus = karakter.kalozKritikus
if(karakter.osztaly === Osztaly.Kaloz && szint % 3 === 0) {
const nextPickableFegyverId = AllowedFegyver(Osztaly.Kaloz, []).filter(PickableSpecializationFilter(karakter.kalozKritikus, szint))[0].Id
kalozKritikus = [...kalozKritikus, nextPickableFegyverId]
}
changeKarakter({...karakter, szint, hpRolls, tulajdonsagNovelesek, harcosSpecializaciok, kalozKritikus})
}
export function LevelDown(karakter: KarakterInputs, changeKarakter: (input: KarakterInputs) => void) {
let tulajdonsagNovelesek = karakter.tulajdonsagNovelesek
if (karakter.szint % 4 === 0) {
tulajdonsagNovelesek = tulajdonsagNovelesek.slice(0, -1)
}
const szint = karakter.szint - 1
const hpRolls = karakter.hpRolls.slice(0, -1)
let harcosSpecializaciok = karakter.harcosSpecializaciok
if (karakter.osztaly === Osztaly.Harcos && szint % 2 === 0) {
harcosSpecializaciok = harcosSpecializaciok.slice(0, -1)
}
changeKarakter({...karakter, szint, hpRolls, tulajdonsagNovelesek, harcosSpecializaciok})
}
export function CanLevelUp(karakter: Pick<KarakterInputs, 'szint' | 'faj' | 'osztaly'>): boolean {
return karakter.szint < 10 && karakter.szint < GetFajDetails(karakter.faj).Szintkorlatok[karakter.osztaly]
}
@@ -0,0 +1,87 @@
import {Mentok, MentokTotal} from "./mentok";
import {Osztaly} from "./osztaly";
import {KarakterInputs} from "./karakter";
import {CelzoTB, KozelharciTB} from "./tamadas_bonusz";
import {Faj} from "./faj";
import { GetPajzs, GetPancel, MaxAbilityVO } from "./felszereles";
import {Modifier} from "./tulajdonsag";
export interface MasodlagosErtekekView {
HP: number
VO: number
KozelharciTB: number[]
CelzoTB: number[]
Mentok: Mentok
}
export type KarakterpickForMasodlagosErtekek = Pick<KarakterInputs, 'osztaly' | 'hpRolls' | 'tulajdonsagok' | 'szint' | 'faj' | 'felszereles'>
export function BaseHP(osztaly: Osztaly) {
let base = 4;
switch (osztaly){
case Osztaly.Barbar:
base = 12
break;
case Osztaly.Amazon:
case Osztaly.Ijasz:
case Osztaly.Kaloz:
case Osztaly.Harcos:
base = 10;
break
case Osztaly.Pap:
base = 8;
break;
case Osztaly.Tolvaj:
base = 6;
break;
case Osztaly.Varazslo:
case Osztaly.Illuzionista:
base = 4;
break;
}
return base
}
export function HP(karakter: Pick<KarakterInputs, 'osztaly' | 'tulajdonsagok' | 'hpRolls'>): number {
const egeszsegModifier = Modifier(karakter.tulajdonsagok.t_egs)
return BaseHP(karakter.osztaly) + egeszsegModifier + karakter.hpRolls
// https://lfg.hu/forum/topic/15079-kard-es-magia/page/219/#comment-2218333
.map(hp => Math.max(1, hp + egeszsegModifier))
.reduce((sum, val) => sum + val, 0)
}
export function VO(karakter: Pick<KarakterInputs, 'tulajdonsagok' | 'faj' | 'osztaly' | 'szint' | 'felszereles'>): number {
const pancel = GetPancel(karakter.felszereles.pancelID)
const pancelVO = pancel?.VO ?? 0
const pajzsVO = GetPajzs(karakter.felszereles.pajzsID)?.VO ?? 0
let vo = 10 + pajzsVO + pancelVO
let abilityVO = Modifier(karakter.tulajdonsagok.t_ugy)
if (karakter.osztaly === Osztaly.Kaloz) {
vo += Math.floor(karakter.szint / 3)
}
if (karakter.osztaly === Osztaly.Amazon) {
if (karakter.tulajdonsagok.t_kar > karakter.tulajdonsagok.t_ugy) {
abilityVO = Modifier(karakter.tulajdonsagok.t_kar)
}
vo += Math.floor(karakter.szint / 2)
}
if (karakter.faj === Faj.Felszerzet){
vo += 1
}
if (pancel) {
abilityVO = Math.min(MaxAbilityVO(pancel.Type), abilityVO)
}
vo += abilityVO
return vo
}
export function CalculateMasodlagosErtekek(karakter: KarakterpickForMasodlagosErtekek): MasodlagosErtekekView {
return {
HP: HP(karakter),
VO: VO(karakter),
CelzoTB: CelzoTB(karakter),
KozelharciTB: KozelharciTB(karakter),
Mentok: MentokTotal(karakter)
}
}
@@ -0,0 +1,24 @@
{
"varazslatok": [
[3,1],
[4,2],
[4,2,1],
[5,3,2],
[5,3,2,1],
[5,3,2,2],
[6,4,3,2,1],
[6,4,3,3,2],
[6,4,4,3,2,1],
[6,4,4,3,3,2],
[7,5,4,4,3,2],
[7,5,4,4,3,3],
[7,5,5,4,4,3],
[7,5,5,4,4,3],
[7,5,5,5,4,4],
[8,6,5,5,4,4],
[8,6,5,5,5,4],
[8,6,6,5,5,4],
[8,6,6,5,5,5],
[8,6,6,6,5,5]
]
}
@@ -0,0 +1,38 @@
import MemorizaltVarazslatTabla from "./memorizalt_varazslat_tabla.json";
import {KarakterInputs} from "./karakter";
import {Osztaly} from "./osztaly";
import {Modifier} from "./tulajdonsag";
function isSpellcaster(osztaly: Osztaly): boolean {
return osztaly === Osztaly.Pap || osztaly === Osztaly.Illuzionista || osztaly === Osztaly.Varazslo;
}
function getMagicAbilityModifier(karakter: Pick<KarakterInputs, 'osztaly' | 'tulajdonsagok'>) : number {
const magicAbility = karakter.osztaly === Osztaly.Pap ? karakter.tulajdonsagok.t_bol : karakter.tulajdonsagok.t_int
return Modifier(magicAbility)
}
export function NapiVarazslatok(karakter: Pick<KarakterInputs, 'szint' | 'osztaly' | 'tulajdonsagok'>) : number[] {
if (!isSpellcaster(karakter.osztaly)) {
return []
}
const magicAbilityModifier = getMagicAbilityModifier(karakter)
const response = [...MemorizaltVarazslatTabla.varazslatok[karakter.szint-1]]
for (let i = 1; i <= magicAbilityModifier && i < response.length; i++){
response[i] += 1
}
return response
}
export function CalculateVarazslatMentokNF(karakter: Pick<KarakterInputs, 'osztaly' | 'tulajdonsagok'>): number[] {
if (!isSpellcaster(karakter.osztaly)) {
return []
}
const magicAbilityModifier = getMagicAbilityModifier(karakter)
const response = []
for (let spellLevel = 0; spellLevel < 6; spellLevel++){
const NF = 10 + spellLevel + magicAbilityModifier
response.push(NF)
}
return response
}
@@ -0,0 +1,80 @@
import {Osztaly} from "./osztaly";
import {KarakterInputs} from "./karakter";
import {KarakterTulajdonsagok, TulajdonsagModosito} from "./tulajdonsag";
export interface Mentok {
kitartas: number
reflex: number
akaratero: number
}
export type MentoTipus = keyof Mentok
export function MentoTipusLabel(tipus: MentoTipus): string {
switch (tipus) {
case "akaratero":
return "Akaraterő";
case "kitartas":
return "Kitartás";
case "reflex":
return "Reflex";
}
}
export function ElsodlegesMento(szint: number): number {
return Math.min(Math.floor(2 + szint / 2), 10);
}
export function MasodlagosMento(szint: number): number {
return Math.floor(szint / 3)
}
export function ElsodlegesMentok(osztaly: Osztaly) : MentoTipus[] {
let t: MentoTipus[] = []
switch (osztaly){
case Osztaly.Barbar:
case Osztaly.Amazon:
case Osztaly.Ijasz:
case Osztaly.Kaloz:
case Osztaly.Harcos:
t = ['kitartas'];
break;
case Osztaly.Pap:
t = ['kitartas', 'akaratero'];
break;
case Osztaly.Tolvaj:
t = ['reflex'];
break;
case Osztaly.Varazslo:
case Osztaly.Illuzionista:
t = ['akaratero']
break;
}
return t
}
export function MentokAlap(osztaly: Osztaly, szint: number = 1) {
const elsodlegesek = ElsodlegesMentok(osztaly)
return {
kitartas: elsodlegesek.includes('kitartas') ? ElsodlegesMento(szint) : MasodlagosMento(szint),
reflex: elsodlegesek.includes('reflex') ? ElsodlegesMento(szint) : MasodlagosMento(szint),
akaratero: elsodlegesek.includes('akaratero') ? ElsodlegesMento(szint) : MasodlagosMento(szint),
}
}
export function MentoModositok(tulajdonsagModositok: KarakterTulajdonsagok) {
return {
kitartas: tulajdonsagModositok.t_egs,
reflex: tulajdonsagModositok.t_ugy,
akaratero: tulajdonsagModositok.t_bol,
}
}
export function MentokTotal(karakter: Pick<KarakterInputs, 'osztaly' | 'szint' | 'tulajdonsagok'>) {
const alap = MentokAlap(karakter.osztaly, karakter.szint)
const modositok = MentoModositok(TulajdonsagModosito(karakter.tulajdonsagok))
return {
kitartas: alap.kitartas + modositok.kitartas,
reflex: alap.reflex + modositok.reflex,
akaratero: alap.akaratero + modositok.akaratero,
}
}
@@ -0,0 +1,268 @@
import {GetPancel, KarakterFelszereles} from "./felszereles";
import {AllowedPancelTypes} from "./allowed-pancel-types";
export enum Osztaly {
Harcos = 'o_harcos',
Ijasz = 'o_ijasz',
Amazon = 'o_amazon',
Kaloz = 'o_kaloz',
Barbar = 'o_barbar',
Pap = 'o_pap',
Tolvaj = 'o_tolvaj',
Varazslo = 'o_varazslo',
Illuzionista = 'o_illuzionista',
}
export function SetFelszerelesForChangedOsztaly(o: Osztaly, felszereles: KarakterFelszereles, changeFelszereles: (f: KarakterFelszereles) => void) {
const allowedPancelTypes = AllowedPancelTypes([o])
const pancel = GetPancel(felszereles.pancelID)
if (pancel && !allowedPancelTypes.includes(pancel.Type)) {
changeFelszereles({...felszereles, pancelID: undefined})
}
if (allowedPancelTypes.length === 0 && !!felszereles.pajzsID){
changeFelszereles({...felszereles, pajzsID: undefined})
}
}
export function OsztalyLabel(osztaly: Osztaly) : string {
switch (osztaly) {
case Osztaly.Amazon: return 'Amazon';
case Osztaly.Barbar: return 'Barbár';
case Osztaly.Pap: return 'Pap';
case Osztaly.Illuzionista: return 'Illúzionista';
case Osztaly.Varazslo: return 'Varázsló';
case Osztaly.Harcos: return 'Harcos';
case Osztaly.Ijasz: return 'Íjász';
case Osztaly.Kaloz: return 'Tengerész/Kalóz';
case Osztaly.Tolvaj: return 'Tolvaj'
}
}
export function OsztalyDescription(osztaly: Osztaly) : string {
switch (osztaly) {
case Osztaly.Harcos:
case Osztaly.Amazon:
case Osztaly.Barbar:
case Osztaly.Ijasz:
case Osztaly.Kaloz:
return 'A harcosok a fegyverhasználat mesterei. Megállják a helyüket a közelharcban és a távolsági összecsapásokban is, s sokkal jobban viselik a sebesüléseket, mint más osztályok képviselői. Ez az osztály mind közül a leggyakoribb, de egyben a legváltozatosabb is öt alosztállyal rendelkezik.';
case Osztaly.Tolvaj:
return 'A tolvajok a becstelen harc és a csöndes gyilkolás mesterei (közülük sokan alkalmaznak mérgeket őket orgyilkosoknak nevezik). Harci képességeik és életerejük viszonylag gyenge, s nehezebb vértezeteket sem használhatnak. Ezért ha módjukban áll, nem szemtől szembe, hanem lesből támadnak áldozataikra, és ha ez nem sikerülne, gyorsan kereket oldanak. Emellett alvilági pályafutásuk során számos hasznos képzettségben válnak járatossá.';
case Osztaly.Pap:
return 'A papok az istenek militáns varázshasználó szolgái. Hitük parancsait minden körülmények között be kell tartaniuk, s bizonyos rendszerességgel áldozatokat kell bemutatniuk. A papok csak istenükkel azonos vagy attól legfeljebb eggyel különböző jelleműek lehetnek. A papok csak az istenük által kijelölt fegyvereket forgathatják, de szabadon válogathatnak a különféle vértek között.';
case Osztaly.Varazslo:
return 'A varázslók kiemelkedő szellemi fegyelmezettségük segítségével képesek a magasabb fokú mágikus erőket irányító varázsigék memorizálására és fölhasználására. Tudásukat varázskönyekben rögzítik, s mindent elkövetnek egy ismeretlen formula megszerzéséért. Többségüket a magabiztos önhittség és az alacsonyabb szellemi képeségűek megvetése jellemzi. A varázslók nem képesek _bármiféle_ páncélban varázsolni, és csak kevés fegyvertípus forgatásában járatosak.';
case Osztaly.Illuzionista:
return 'A varázslók kiemelkedő szellemi fegyelmezettségük segítségével képesek a magasabb fokú mágikus erőket irányító varázsigék memorizálására és fölhasználására. Tudásukat varázskönyekben rögzítik, s mindent elkövetnek egy ismeretlen formula megszerzéséért. Többségüket a magabiztos önhittség és az alacsonyabb szellemi képeségűek megvetése jellemzi. Az illuzionisták a mágia egy igen hasonló, de több lényeges területen eltérő fajtáját művelik. Lételemük a káprázat, a megtévesztés és mások elméjének befolyásolása többségük hipnotizőr vagy ügyes manipulátor. Az illuzionista varázslatok csekélyebb pusztító erőt képviselnek, ellenben egy élelmes illuzionista kezében szinte bármire képesek beleértve a közönséges varázslók egyes varázslatainak utánzását. Az illuzionisták nem képesek _bármiféle_ páncélban varázsolni, és csak kevés fegyvertípus forgatásában járatosak.';
}
}
export function OsztalyProperties(osztaly: Osztaly) : string[] {
switch (osztaly) {
case Osztaly.Harcos: return [
'Támadásbónusz: Szint * 1',
'Mentődobások: Kitartás az első, Reflex és Akaraterő a második oszlop szerint',
'Fegyverhasználat: az egzotikus fegyvereken kívül bármilyen fegyvertípust használhatnak',
'Páncélhasználat: bármilyen',
'Hp: szintenként 1d10, 9. szint („Nagyúr”) fölött 3/szint',
'Képzettségek: 3',
]
case Osztaly.Ijasz: return [
'Támadásbónusz: Szint x 1',
'Mentődobások: Kitartás az első, Reflex és Akaraterő a második oszlop szerint',
'Fegyverhasználat: az egzotikus fegyvereken kívül bármilyen fegyvertípust használhatnak',
'Páncélhasználat: nehéz vérteken kívül bármilyen',
'Hp: szintenként 1d10, 9. szint („Nagyúr”) fölött 3/szint',
'Képzettségek: 3',
]
case Osztaly.Amazon: return [
'Támadásbónusz: Szint x 1',
'Mentődobások: Kitartás az első, Reflex és Akaraterő a második oszlop szerint',
'Fegyverhasználat: az egzotikus fegyvereken kívül bármilyen fegyvertípust használhatnak',
'Páncélhasználat: láncing vagy könnyebb vértek',
'Hp: szintenként 1d10, 9. szint („Nagyúr”) fölött 3/szint',
'Képzettségek: 3',
]
case Osztaly.Kaloz: return [
'Támadásbónusz: Szint x 1',
'Mentődobások: Kitartás az első, Reflex és Akaraterő a második oszlop szerint',
'Fegyverhasználat: az egzotikus fegyvereken kívül bármilyen fegyvertípust használhatnak',
'Páncélhasználat: nehéz vérteken kívül bármilyen',
'Hp: szintenként 1d10, 9. szint („Nagyúr”) fölött 3/szint',
'Képzettségek: 3',
]
case Osztaly.Barbar: return [
'Támadásbónusz: Szint x 1',
'Mentődobások: Kitartás az első, Reflex és Akaraterő a második oszlop szerint',
'Fegyverhasználat: az egzotikus fegyvereken kívül bármilyen fegyvertípust használhatnak',
'Páncélhasználat: bármilyen',
'Hp: szintenként 1d12, 9. szint („Nagyúr”) fölött 3/szint',
'Képzettségek: 3',
]
case Osztaly.Pap: return [
'Támadásbónusz: Szint x 2/3',
'Mentődobás: Kitartás és Akaraterő az első, Reflex a második oszlop szerint',
'Fegyverhasználat: csak buzogányokat, tőröket és istenük kijelölt fegyvereit használhatják',
'Páncélhasználat: bármilyen',
'Hp: szintenként 1d8, 9. szint („Főpap”) fölött 2/szint',
'Képzettségek: 3',
]
case Osztaly.Tolvaj: return [
'Támadásbónusz: Szint x 2/3',
'Mentődobás: Reflex az első, Kitartás és Akaraterő a második oszlop szerint',
'Fegyverhasználat: tőröket, dobótűket, parittyákat, számszeríjakat , rövidíjakat és egykezes kardokat használhatnak',
'Páncélhasználat: csak könnyű',
'Hp: szintenként 1d6, 9. szint („Mestertolvaj”) fölött 2/szint',
'Képzettségek: 7 (ebből legalább 4 minden képpen a „tolvajképzettségek” listájáról); 9. és ezután minden páratlan szinten újabb, eddig akár tiltott képzettséget tanulhatnak meg.'
]
case Osztaly.Varazslo:
case Osztaly.Illuzionista: return [
'Támadásbónusz: Szint x 1/2',
'Mentődobás: Akaraterő az első, Kitartás és Reflex a második oszlop szerint',
'Fegyverhasználat: tőrt, botot, dobótűket, bunkót és parittyát használhatnak',
'Páncélhasználat: nincs',
'Hp: szintenként 1d4, 9. szint („Varázslónagyúr”) fölött 1/szint',
'Képzettségek: 3'
]
}
}
export interface Skill {
Id: string
Name: string,
Description: string
ExtendedDescription?: string[]
}
export function OsztalySpecialSkills(osztaly: Osztaly) : Skill[] {
switch (osztaly) {
case Osztaly.Harcos: return [{
Id: 's_harcos_harcos',
Name: 'Harcos',
Description: 'Speciális harci tapasztalatuknak köszönhetően minden páratlan szinten +2-t kapnak egy választott fegyvertípus (pl. csatabárd, hosszúkard, stb.) sebzésére. Ez fegyverenként csak egyszer választható, s arra is fölhasználható, hogy a harcos megtanulja egy egzotikus fegyver használatát, amire később specializálódhat is. 9. szinttől elérhető számukra a kettős specializáció: ezzel sebzésmódosítójukat az adott fegyverre +4-re emelhetik.'
}
]
case Osztaly.Ijasz: return [
{
Id: 's_harcos_ijasz',
Name: 'Íjász',
Description: 'A távolsági harc mesterei. Íjak használatakor körönként eggyel többször támadhatnak. Értenek a visszacsapó íjak használatához is.'
},
{
Id: 's_harcos_ijasz_vert',
Name: 'Kötelező mozgékonyság',
Description: 'Az íjászok nem viselhetnek nehéz vérteket.'
}
]
case Osztaly.Amazon: return [
{
Id: 's_harcos_amazon',
Name: 'Amazon',
Description: 'Az amazonok barbár népe szépségéről és a karizmatikus jelenlétre építő védekező harcmodoráról ismert.',
},
{
Id: 's_harcos_amazon_vert',
Name: 'MMO vért',
Description: 'Az amazonok csak láncinget vagy könnyebb vérteket viselhetnek, de VO-juk Szint/2-vel javul, továbbá Karizma- és Ügyességmódosítói közül a kedvezőbbet választhatják.'
},
{
Id: 's_harcos_amazon_csapatmunka',
Name: 'Csapatmunka',
Description: 'Ha kettő vagy több amazon együttesen harcol, VO-juk további 2 ponttal nő pszionikus képességeik miatt.'
},
]
case Osztaly.Kaloz: return [
{
Id: 's_harcos_kaloz',
Name: 'Tengerész/Kalóz',
Description: 'A tengerészek a hajózás mesterei; kereskedők, tengeri rablók és rabszolgavadászok.'
},
{
Id: 's_harcos_kaloz_ugyes_huzas',
Name: 'Ügyes húzás',
Description: 'A kalózok és tengerészek ha magasabb, Ügyességbónuszukat adják hozzá közelharci támadásaikhoz.'
},
{
Id: 's_harcos_kaloz_vasmacska',
Name: 'Vasmacska',
Description: 'A kalózok és tengerészek nem viselhetnek nehéz vértezetet, de VO-juk könnyű vértben Szint/3-al javul.'
},
{
Id: 's_harcos_kaloz_kritikus',
Name: 'Kritikus',
Description: 'Minden harmadik szinten kiválaszthatnak egy fegyvert, amivel könnyebben okoznak kritikus sebesülést, tehát pl. csatabárddal 20 helyett 19-20-on, hosszúkarddal 19-20 helyett 18-20-on, szablyával 18-20 helyett 17-20-on, stb. Ez a képesség 9. szintig fegyverenként csak egyszer választható.'
}]
case Osztaly.Barbar: return [
{
Id: 's_harcos_barbar',
Name: 'Barbár',
Description: 'A barbár törzsek köré sorolhatók a pusztai és sivatagi nomádok, a déli dzsungelek emberevői és az északi tengermelléki rablók nemzetségei egyaránt.'
},
{
Id: 's_harcos_barbar_eletero',
Name: 'Túlélő',
Description: 'A barbárok 1d12-vel dobják a Hp-jüket, -5 Hp-ig öntudatuknál vannak és +1 bónuszt (10. szinttől +2, 15. szinttől +4) kapnak a mentődobásaikra.'
},
{
Id: 's_harcos_barbar_iras',
Name: 'Írástudatlan',
Description: 'Minden barbár írástudatlanul kezdi a játékot, de ha kell, később megtanulhatja a betűvetést.'
}
]
case Osztaly.Pap: return [
{
Id: 's_pap_varazslat',
Name: 'Varázslatok',
Description: 'a papi varázslatok általában gyógyításra, mások megsegítésére és vallási feladatok ellátására szolgálnak. Minél magasabb szintűek, egy nap annál több varázslatot tudnak alkalmazni a következő oldalon található táblának megfelelően. Például egy 4. szintű, 17-es Bölcsességgel rendelkező pap öt nulladik szintű, négy első szintű és három második szintű varázslatot tarthat naponta elméjében.'
},
{
Id: 's_pap_gyogyseb',
Name: 'Gyógyítás vagy sebokozás',
Description: 'a pap az aznap már memorizált (megtanult) varázslatokat gyógyításra vagy sebek okozására is fölhasználhatja. Érintésével a varázslat minden szintjéért 1d8 Hp-t gyógyít vagy sebez így pl. egy Ómen elhasználásával 2d8 Hp-t. Az így konvertált varázslat természetesen elvész. Az, hogy a képesség sebek gyógyítására vagy okozására szolgál, a pap istenétől függ. A békésebb istenek főleg az elsőt, a harciasabbak főleg a másodikat részesítik előnyben, s vannak olyanok is, akik híveikre bízzák, hogy melyik utat választják. Ha azonban a pap már eldöntötte, melyik képességben járatos, döntését többé nem másíthatja meg!'
},
{
Id: 's_pap_elohalott',
Name: 'Élőholtak elűzése vagy irányítása',
Description: 'A papok istenük dogmájának megfelelően képesek lehetnek arra, hogy élőholt szörnyeket (mint például zombikat, lidérceket vagy vámpírokat) szimbólumuk felmutatásával elűzzenek vagy szolgálatukba állítsanak. Ez a képesség naponta csak egyszer (5. szinttől kétszer, 10. szinttől háromszor, 15. szinttől négyszer) használható. A cselekedet a következőképpen működik:',
ExtendedDescription: [
'A pap [1d20+Kar bónusz] próbát tesz. Ennek az értékét összeveti a standard bónusztáblázattal. Az innen kiolvasható módosítót hozzáadja saját szintjéhez (ha negatív, akkor levonja). Az eredmény jelzi, hogy a pap legfeljebb milyen szintű élőholtakra lesz hatással. Ha tehát pl. a próba végeredménye 14 és a pap ötödik szintű, akkor 5+1, azaz 6. szintű a legerősebb élőhalott, amire a kísérlet sikeres. A 20-as dobás itt is automatikus siker, az 1-es automatikus kudarc.',
'A pap [2d6+szint] szintű élőhalottat űzhet el vagy irányíthat. Először a leggyengébb élőholtakra hat, azután a következő leggyengébbekre, stb. Az elűzött élőholtak elmenekülnek a pap szeme elől és addig nem is ártanak neki és társainak, amíg a pap csapata meg nem támadja őket! Ezzel szemben a gonosz papok parancsokat adhatnak az irányított szörnyeknek. Mindkét hatás legfeljebb pár óráig tart, az élőholtak szintjétől és a körülményektől függően.',
'Ha az összes szem előtt lévő élőholtat sikerült elűzni vagy irányítani és még maradt „a szent energiából”, akkor ismét el kell kezdeni a hatást a leggyengébb ellenségektől. Az így elűzött szörnyek megsemmisülnek, illetve irányítás esetén végleg a gonosz pap szolgálatába állnak.',
'A két hatás egymás ellen is fölhasználható, például egy jó pap kiolthatja egy gonosz pap irányítását, de akár két gonosz pap is versenyezhet egy csapat sírlidérc „megtérítésén”.',
]
},
{
Id: 's_pap_invokacio',
Name: 'Invokáció',
Description: 'A pap kérheti istene segítségét (általában nevének kimondásával), ami +1-et, ritkábban +2-t ad hozzá dobásaihoz. A papok hetente annyiszor használhatják az invokációt, amekkora a Bölcsességbónuszuk (de legalább egyszer).'
}
]
case Osztaly.Tolvaj: return [
{
Id: 's_tolvaj_orvtamadas',
Name: 'Orvtámadás',
Description: 'az orvtámadás akkor használható, ha a tolvaj meglepi áldozatát, tehát pl. csöndesen a hátába kerül, vagy beszélgetés közben tőrt ránt és egyetlen mozdulattal gyomron szúrja. Az orvtámadás minden páratlan szintért 1d6 bónusz sebzést okoz (maximuma +5d6). Dobó- és lőfegyverrel is használható, és ha a tolvaj úgy dönt, illetve a használt fegyver lehetővé teszi, az ellenfél elkábítására is alkalmazható.'
},
{
Id: 's_tolvaj_kezdemenyezes',
Name: 'Jobb kezdeményezés',
Description: 'a tolvajok +4-et kapnak kezdeményezési dobásaikra.',
},
{
Id: 's_tolvaj_kepzettsegek',
Name: 'Tolvajképzettségek',
Description: 'minden tolvaj jártas több olyan képzettségben, amely segíti a rejtőzködésben, a jól őrzött értékek eltulajdonításában és hasonló feladatokban. A tolvajok azokat a tolvajképzettségeket is használhatják, amelyekben nem járatosak külön, de ekkor szintjük nem számít bele a próbadobás módosítójába! A tolvajképzettségek listája: Álcázás, Csapdák állítása és eltávolítása, Egyensúlyozás, Értékbecslés, Hallgatózás, Hamisítás, Jelek olvasása, Keresés, Mászás, Megfigyelés, Méregkeverés, Osonás, Rejtőzés, Szabadulóművészet, Zárnyitás és Zsebmetszés.',
},
]
case Osztaly.Varazslo: return [{
Id: 's_varazslo_varazskonyv',
Name: 'Varázskönyv',
Description: 'egy első szintű varázsló varázskönyve 6 nulladik és 3 első szintű varázslatot tartalmaz, harmadik szintű kezdő karakteré ezen felül még 2 első szintűt és 2 második szintűt. Az újabb varázslatokat mások könyveiből, mágikus tekercsekből vagy más módszerek segítségével kell könyvébe másolnia. Az illuzionisták szintén varázskönyveket használnak, de eltérő mágiaformájuk miatt nem képesek a varázslói varázslatok megértésére és viszont.'
}]
case Osztaly.Illuzionista: return [{
Id: 's_illuzionista_varazskonyv',
Name: 'Varázskönyv',
Description: 'egy első szintű varázsló varázskönyve 6 nulladik és 3 első szintű varázslatot tartalmaz, harmadik szintű kezdő karakteré ezen felül még 2 első szintűt és 2 második szintűt. Az újabb varázslatokat mások könyveiből, mágikus tekercsekből vagy más módszerek segítségével kell könyvébe másolnia. Az illuzionisták szintén varázskönyveket használnak, de eltérő mágiaformájuk miatt nem képesek a varázslói varázslatok megértésére és viszont.'
}]
}
}
@@ -0,0 +1,32 @@
{
"pajzs": [
{
"Id": "kis_fa",
"Name": "Kis fapajzs ",
"Type": "pajzs",
"VO": 1,
"Price": 3
},
{
"Id": "kis_fem",
"Name": "Kis fémpajzs ",
"Type": "pajzs",
"VO": 1,
"Price": 9
},
{
"Id": "nagy_fa",
"Name": "Nagy fapajzs ",
"Type": "pajzs",
"VO": 2,
"Price": 7
},
{
"Id": "nagy_fem",
"Name": "Nagy fémpajzs ",
"Type": "pajzs",
"VO": 2,
"Price": 20
}
]
}
@@ -0,0 +1,88 @@
{
"pancel": [
{
"Id": "tomott",
"Name": "Tömött páncél",
"Type": "konnyu",
"VO": 1,
"Price": 4
},
{
"Id": "bor",
"Name": "Bőrvért",
"Type": "konnyu",
"VO": 2,
"Price": 5
},
{
"Id": "kivert_bor",
"Name": "Kivert bőrvért",
"Type": "konnyu",
"VO": 3,
"Price": 15
},
{
"Id": "gyuruvert",
"Name": "Gyűrűvért",
"Type": "konnyu",
"VO": 3,
"Price": 20
},
{
"Id": "lancing",
"Name": "Láncing",
"Type": "konnyu",
"VO": 4,
"Price": 50
},
{
"Id": "pikkelyvert",
"Name": "Pikkelyvért",
"Type": "kozepes",
"VO": 4,
"Price": 30
},
{
"Id": "lancvert",
"Name": "Láncvért",
"Type": "kozepes",
"VO": 5,
"Price": 75
},
{
"Id": "mellvert",
"Name": "Mellvért",
"Type": "kozepes",
"VO": 5,
"Price": 100
},
{
"Id": "racsvert",
"Name": "Rácsvért",
"Type": "nehez",
"VO": 6,
"Price": 200
},
{
"Id": "szalagvert",
"Name": "Szalagvért",
"Type": "nehez",
"VO": 6,
"Price": 250
},
{
"Id": "lemezvert",
"Name": "Lemezvért",
"Type": "nehez",
"VO": 7,
"Price": 400
},
{
"Id": "teljes_lemezvert",
"Name": "Teljes lemezvért",
"Type": "nehez",
"VO": 8,
"Price": 800
}
]
}
@@ -0,0 +1,68 @@
{
"elsodleges": [
[1],
[2],
[3],
[4],
[5],
[6,1],
[7,2],
[8,3],
[9,4],
[10,5],
[10,6,1],
[10,7,2],
[10,8,3],
[10,9,4],
[10,10,5],
[10,10,6],
[10,10,7],
[10,10,8],
[10,10,9],
[10,10,10]
],
"ketharmados": [
[0],
[1],
[2],
[2],
[3],
[4],
[4],
[5],
[6,1],
[6,1],
[7,2],
[8,3],
[8,3],
[9,4],
[10,5],
[10,5],
[10,6],
[10,7],
[10,7],
[10,8]
],
"feles": [
[0],
[1],
[1],
[2],
[2],
[3],
[3],
[4],
[4],
[5],
[5],
[6],
[6],
[7],
[7],
[8],
[8],
[9],
[9],
[10]
]
}
@@ -0,0 +1,136 @@
import {Osztaly} from "./osztaly";
import {CelzoTB, KozelharciTB, TamadasBonusz} from "./tamadas_bonusz";
import {KarakterTulajdonsagok, Modifier} from "./tulajdonsag";
describe('Tamadas Bonusz calculation', () => {
describe('Base Tamadas Bonusz', () => {
test.each([
[Osztaly.Harcos, 1, [1]],
[Osztaly.Amazon, 6, [6,1]],
[Osztaly.Ijasz, 20, [10,10,10]],
[Osztaly.Tolvaj, 1, [0]],
[Osztaly.Tolvaj, 9, [6,1]],
[Osztaly.Pap, 17, [10,6]],
[Osztaly.Varazslo, 1, [0]],
[Osztaly.Varazslo, 12, [6]],
[Osztaly.Illuzionista, 20, [10]],
])('%s has a TB of %i at level %i', (osztaly: Osztaly, szint: number, expectedTB: number[]) => {
const actual = TamadasBonusz(osztaly, szint)
expect(actual).toEqual(expectedTB)
})
});
describe('CelzoTB', () => {
const szint = 10
const tulajdonsagok: KarakterTulajdonsagok = {
t_ugy: 17,
t_kar: 3,
t_bol: 3,
t_egs: 3,
t_ero: 3,
t_int: 3,
}
const modifier = Modifier(tulajdonsagok.t_ugy)
it('should be equal base + ugyesseg modifier', () => {
const osztaly = Osztaly.Tolvaj
const base = TamadasBonusz(osztaly, szint)
const expected = base.map(x => x + modifier)
const actual = CelzoTB({
osztaly,
szint,
tulajdonsagok
})
expect(actual).toEqual(expected)
});
it('should double the first base TB for Íjász', () => {
const osztaly = Osztaly.Ijasz
const base = TamadasBonusz(osztaly, szint)
const ijaszDoubleBase = [base[0], ...base]
const expected = ijaszDoubleBase.map(x => x + modifier)
const actual = CelzoTB({
osztaly,
szint,
tulajdonsagok
})
expect(actual).toEqual(expected)
});
});
describe('KozelharciTB', () => {
const szint = 10
const tulajdonsagok: KarakterTulajdonsagok = {
t_ugy: 3,
t_kar: 3,
t_bol: 3,
t_egs: 3,
t_ero: 3,
t_int: 3,
}
const modifier = Modifier(tulajdonsagok.t_ero)
it('should be equal base + ero modifier', () => {
const osztaly = Osztaly.Tolvaj
const base = TamadasBonusz(osztaly, szint)
const expected = base.map(x => x + modifier)
const actual = KozelharciTB({
osztaly,
szint,
tulajdonsagok
})
expect(actual).toEqual(expected)
});
it('should use t_ugy for Kaloz if t_ugy > t_ero', () => {
const osztaly = Osztaly.Kaloz
const tulajdonsagok: KarakterTulajdonsagok = {
t_ugy: 17,
t_kar: 3,
t_bol: 3,
t_egs: 3,
t_ero: 3,
t_int: 3,
}
const modifier = Modifier(tulajdonsagok.t_ugy)
const base = TamadasBonusz(osztaly, szint)
const expected = base.map(x => x + modifier)
const actual = KozelharciTB({
osztaly,
szint,
tulajdonsagok
})
expect(actual).toEqual(expected)
});
it('should use t_ero for Kaloz if t_ero > t_ugy', () => {
const osztaly = Osztaly.Kaloz
const tulajdonsagok: KarakterTulajdonsagok = {
t_ugy: 3,
t_kar: 3,
t_bol: 3,
t_egs: 3,
t_ero: 13,
t_int: 3,
}
const modifier = Modifier(tulajdonsagok.t_ero)
const base = TamadasBonusz(osztaly, szint)
const expected = base.map(x => x + modifier)
const actual = KozelharciTB({
osztaly,
szint,
tulajdonsagok
})
expect(actual).toEqual(expected)
});
});
});
@@ -0,0 +1,42 @@
import {Osztaly} from "./osztaly";
import TamadasBonuszTabla from "./tamadas_bonus_tabla.json";
import {KarakterInputs} from "./karakter";
import {Modifier} from "./tulajdonsag";
export function TamadasBonusz(osztaly: Osztaly, szint: number = 1) : number[] {
let base = []
switch (osztaly){
case Osztaly.Barbar:
case Osztaly.Amazon:
case Osztaly.Ijasz:
case Osztaly.Kaloz:
case Osztaly.Harcos:
base = TamadasBonuszTabla['elsodleges'];
break;
case Osztaly.Pap:
case Osztaly.Tolvaj:
base = TamadasBonuszTabla['ketharmados'];
break;
case Osztaly.Varazslo:
case Osztaly.Illuzionista:
base = TamadasBonuszTabla['feles'];
break;
}
return [...base[szint-1]]
}
export function KozelharciTB(karakter : Pick<KarakterInputs, 'szint' | 'tulajdonsagok' | 'osztaly'>): number[] {
const base = TamadasBonusz(karakter.osztaly, karakter.szint);
let modifier = Modifier(karakter.tulajdonsagok.t_ero)
if (karakter.osztaly === Osztaly.Kaloz && karakter.tulajdonsagok.t_ugy > karakter.tulajdonsagok.t_ero) {
modifier = Modifier(karakter.tulajdonsagok.t_ugy)
}
return base.map(v => v + modifier)
}
export function CelzoTB(karakter : Pick<KarakterInputs, 'szint' | 'tulajdonsagok' | 'osztaly'>): number[] {
let base = TamadasBonusz(karakter.osztaly, karakter.szint)
if (karakter.osztaly === Osztaly.Ijasz) {
base = [base[0], ...base]
}
return base.map(v => v + Modifier(karakter.tulajdonsagok.t_ugy))
}
@@ -0,0 +1,88 @@
import {TulajdonsagokFajjal} from "./faj";
import {KarakterInputs} from "./karakter";
import {d6} from "../../shared/domain-models/kockak";
export enum Tulajdonsag {
Ero = 't_ero',
Ugyesseg = 't_ugy',
Egeszseg = 't_egs',
Intelligencia = 't_int',
Bolcsesseg = 't_bol',
Karizma = 't_kar',
}
export type KarakterTulajdonsagok = Record<Tulajdonsag, number>
export const TulajdonsagIDs = [
Tulajdonsag.Ero,
Tulajdonsag.Ugyesseg,
Tulajdonsag.Egeszseg,
Tulajdonsag.Intelligencia,
Tulajdonsag.Bolcsesseg,
Tulajdonsag.Karizma
]
export const TulajdonsagDefaults: KarakterTulajdonsagok = {
t_ero: 10,
t_ugy: 10,
t_egs: 10,
t_int: 10,
t_bol: 10,
t_kar: 10,
}
export function TulajdonsagLabel(tul: Tulajdonsag) : string {
switch (tul) {
case Tulajdonsag.Bolcsesseg: return 'Bölcsesség';
case Tulajdonsag.Ero: return 'Erő';
case Tulajdonsag.Ugyesseg: return 'Ügyesség';
case Tulajdonsag.Egeszseg: return 'Egészség';
case Tulajdonsag.Intelligencia: return 'Intelligencia';
case Tulajdonsag.Karizma: return 'Karizma';
}
}
function rollAbility() {
let min = 6;
let sum = 0;
let rolls: number[] = []
for (let i = 0; i < 4; i++) {
const roll = d6();
if (roll < min) {
min = roll;
}
sum += roll;
rolls.push(roll)
}
const result = sum - min;
console.log("Rolled: " + rolls + " got: " + result)
return result;
}
export function RollAllAbilities() : KarakterTulajdonsagok {
const response = {...TulajdonsagDefaults}
for (const key of TulajdonsagIDs) {
response[key] = rollAbility()
}
return response
}
export const Modifier = (val : number) => Math.floor(val / 3) - 3
export function TulajdonsagModosito(tulajdonsagok: KarakterTulajdonsagok): KarakterTulajdonsagok {
let response: KarakterTulajdonsagok = { ...tulajdonsagok }
for (const key of TulajdonsagIDs) {
response[key] = Modifier(tulajdonsagok[key])
}
return response
}
export function TulajdonsagokTotal(karakter: Pick<KarakterInputs, 'tulajdonsagok' | 'faj' | 'tulajdonsagNovelesek'>): KarakterTulajdonsagok {
const response = TulajdonsagokFajjal(karakter.tulajdonsagok, karakter.faj)
for (const tulajdonsag of karakter.tulajdonsagNovelesek) {
// @ts-ignore // Trust me bro!
response[tulajdonsag] += 1
}
return response
}
@@ -0,0 +1 @@
*.css
@@ -0,0 +1,20 @@
.form-field-error {
display: block;
color: #8c0000;
}
form label {
margin-right: 1em;
}
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type=number] {
-moz-appearance: textfield;
}
@@ -0,0 +1,305 @@
import React, {useContext, useState} from 'react';
import './CreateCharacter.css'
import {Faj, TulajdonsagokFajjal} from "../domain-models/faj";
import {Osztaly, SetFelszerelesForChangedOsztaly} from "../domain-models/osztaly"
import FajSelector from "../components/FajSelector";
import Tulajdonsagok from "../components/Tulajdonsagok";
import OsztalySelector from "../components/OsztalySelector";
import KarakterKepzettsegek from "../components/KarakterKepzettsegek";
import MasodlagosErtekek from "../components/MasodlagosErtekek";
import {
AvailableKezpettsegList,
GetKepzettsegListaN,
GetNumberOfKepzettsegek,
KepzettsegId,
SetDefaultKepzettsegek,
SetDefaultTolvajKepzettsegek,
} from "../domain-models/kepzettsegek";
import {CreatePDF} from "../pdf/character.pdf";
import {KarakterDefaults, KarakterInputs} from "../domain-models/karakter";
import {CalculateMasodlagosErtekek} from "../domain-models/masodlagos_ertekek";
import {KarakterInputToPdfView} from "../pdf/karakter_pdf_view";
import JellemSelector from "../components/JellemSelector";
import {CanLevelUp, LevelDown, LevelUp} from "../domain-models/level";
import {Button, Card, InputGroup, Modal, OverlayTrigger, Toast, ToastContainer} from "react-bootstrap";
import Form from "react-bootstrap/Form";
import LevelUps from "../components/LevelUps";
import HarcosFegyverSpecializacio from "../components/HarcosFegyverSpecializacio";
import Felszereles from '../components/Felszereles';
import { KarakterFelszereles } from '../domain-models/felszereles';
import {Faro} from "@grafana/faro-web-sdk";
import {StoreNewCharacter, UpdateCharacter} from "../api/character.api";
import {useLoaderData, useNavigate, useParams} from "react-router-dom";
import saveOverlayTooltip from "../components/SaveOverlayTooltip";
import {UserContext} from "../../shared/contexts/UserContext";
import {KarakterTulajdonsagok} from "../domain-models/tulajdonsag";
function CreateCharacterPage(props: {
faro?: Faro,
}) {
const {faro} = props
const loaderData = useLoaderData() as KarakterInputs & { isPublic: boolean } | undefined;
let initialKarakterInputs = KarakterDefaults;
if (loaderData != null) {
const { isPublic: t1, ...t2 } = loaderData;
initialKarakterInputs = t2
}
const { id } = useParams();
const fetchedUser = useContext(UserContext);
const initialIsPublic = loaderData?.isPublic ?? fetchedUser?.data == null;
const navigate = useNavigate();
let [karakter, changeKarakter] = useState(initialKarakterInputs)
const tulajdonsagokFajjal = TulajdonsagokFajjal(karakter.tulajdonsagok, karakter.faj)
const changeKepzettseg = (k: KepzettsegId[]) => changeKarakter({...karakter, kepzettsegek: k})
const changeTolvajKepzettseg = (tk?: KepzettsegId[]) => changeKarakter({...karakter, tolvajKepzettsegek: tk})
SetDefaultTolvajKepzettsegek(karakter, changeTolvajKepzettseg)
SetDefaultKepzettsegek({...karakter, tulajdonsagok: tulajdonsagokFajjal}, changeKepzettseg)
const availableKepzettsegList = AvailableKezpettsegList(karakter.osztaly)
const setFelszereles = (f: KarakterFelszereles) => changeKarakter({...karakter, felszereles: f})
SetFelszerelesForChangedOsztaly(karakter.osztaly, karakter.felszereles, setFelszereles)
const levelUp = () => LevelUp(karakter, changeKarakter)
const levelDown = () => LevelDown(karakter, changeKarakter)
const canLevelUp = CanLevelUp(karakter)
let [showSaveModal, setShowSaveModal] = useState(false);
let [newId, setNewId] = useState(null as string | null)
let [showSaved, setShowSaved] = useState(false);
let [isPublic, setIsPublic] = useState(initialIsPublic);
const newCharacterUrl = () => `${window.location.origin}/1e/karakter/${newId}`
const handleSaveModalCopyAndClose = async () => {
await navigator.clipboard.writeText(newCharacterUrl());
handleSaveModalClose()
}
const handleSaveModalClose = () => {
setShowSaveModal(false);
navigate(`/1e/karakter/${newId}`)
}
const hideSaved = () => setShowSaved(false);
const onSaveClicked = async () => {
faro?.api.pushEvent('character_stored', {
osztaly: karakter.osztaly,
szint: karakter.szint.toString(),
faj: karakter.faj.toString(),
is_public: isPublic.toString(),
})
if (id == null) {
let recievedId = await StoreNewCharacter(karakter, isPublic);
setNewId(recievedId)
setShowSaveModal(true);
} else {
await UpdateCharacter(id, karakter, isPublic);
setShowSaved(true);
setTimeout(() => {
hideSaved();
}, 5000)
}
}
return (
<div>
<div className='container-fluid p-5 bg-black text-white text-center'>
<h1>Karakter {!!id ? "szerkesztése" : "létrehozása"}</h1>
</div>
<ToastContainer className="position-fixed" position="top-end">
<Toast show={showSaved} onClose={hideSaved} bg="success">
<Toast.Body className="text-light">Karakter mentve!</Toast.Body>
</Toast>
</ToastContainer>
<Modal show={showSaveModal} onHide={handleSaveModalClose}>
<Modal.Header closeButton>
<Modal.Title>Karakter mentve!</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>Karaktered az alábbi {isPublic ? 'publikus' : 'privát'} URLen érhető el.</p>
<p><a href={newCharacterUrl()}>{newCharacterUrl()}</a></p>
<InputGroup className="mb-3">
<Form.Control id="input" value={newCharacterUrl()} />
<Button variant={"outline-dark"} onClick={() => {
let copyText = document.querySelector("#input") as any;
copyText?.select()
document.execCommand("copy");
}}>Másolás</Button>
</InputGroup>
</Modal.Body>
<Modal.Footer>
<Button variant="danger" onClick={handleSaveModalCopyAndClose}>
Másolás és bezárás
</Button>
</Modal.Footer>
</Modal>
<div className='p-3'>
<form onSubmit={async (event) => event.preventDefault()}>
<div className='row'>
<h5 className='col align-self-center'>Származás</h5>
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Név</label>
<input className='col form-control'
value={karakter.name}
onChange={(e) => changeKarakter({...karakter, name: e.target.value})}/>
{!karakter.name && <span className='form-field-error'>A karaktered nem mászkálhat névtelenül a világban!</span>}
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Nem</label>
<input className='col form-control'
value={karakter.nem}
onChange={(e) => changeKarakter({...karakter, nem: e.target.value})}/>
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Kor</label>
<input className='col form-control'
value={karakter.kor}
type={"number"}
onChange={(e) => changeKarakter({...karakter, kor: Number(e.target.value)})}/>
</div>
<JellemSelector selected={karakter.jellem}
changeJellem={(val) => changeKarakter({...karakter, jellem: val})}/>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Választott istenség</label>
<input className='col form-control'
value={karakter.isten}
onChange={(e) => changeKarakter({...karakter, isten: e.target.value})}/>
</div>
<FajSelector
changeFaj={(faj: Faj) => changeKarakter({...karakter, faj: faj})}
faj={karakter.faj}
/>
<hr/>
<Tulajdonsagok
currentFaj={karakter.faj}
tulajdonsagok={karakter.tulajdonsagok}
changeTulajdonsagok={(tul: KarakterTulajdonsagok) => changeKarakter({
...karakter,
tulajdonsagok: tul
})}
/>
<hr/>
<div className='row'>
<h5 className='col align-self-center'>Tanult</h5>
</div>
<OsztalySelector
currentFaj={karakter.faj}
currentOsztaly={karakter.osztaly}
changeOsztaly={(o: Osztaly) =>
changeKarakter({
...karakter,
osztaly: o,
harcosSpecializaciok: (o === Osztaly.Harcos && karakter.harcosSpecializaciok.length === 0) ? ['hosszu_kard'] : karakter.harcosSpecializaciok,
})}
/>
{karakter.osztaly === Osztaly.Harcos &&
<div className='mb-6'>
<HarcosFegyverSpecializacio
specialization={karakter.harcosSpecializaciok[0]}
existingSpecializations={karakter.harcosSpecializaciok}
changeSpecialization={(spec: string) => changeKarakter({
...karakter,
harcosSpecializaciok: [spec, ...karakter.harcosSpecializaciok.slice(1)]
})}
szint={1}
/>
</div>}
<KarakterKepzettsegek
getKepzettsegListaN={GetKepzettsegListaN(karakter)}
numberOfKepzettsegek={GetNumberOfKepzettsegek(tulajdonsagokFajjal.t_int, karakter.faj, availableKepzettsegList.length)}
kepzettsegek={karakter.kepzettsegek}
changeKepzettsegek={changeKepzettseg}
tolvajKepzettsegek={karakter.tolvajKepzettsegek || []}
changeTolvajKepzettsegek={changeTolvajKepzettseg}
/>
<hr/>
<MasodlagosErtekek {...CalculateMasodlagosErtekek({
...karakter,
tulajdonsagok: tulajdonsagokFajjal,
szint: 1,
hpRolls: []
})} />
<hr/>
<LevelUps key={'level-ups'} karakter={karakter} changeKarakter={changeKarakter}/>
{karakter.szint > 1 && <hr/>}
<div className='d-grid gap-2 m-5'>
{canLevelUp &&
<button className='btn btn-dark btn-lg' type='button' onClick={levelUp}>Szintlépés!
</button>}
{!canLevelUp && karakter.szint < 10 &&
<Card className='col text-center p-3'>Elérted a fajod szint limitjét</Card>}
{karakter.szint > 1 &&
<button className='btn btn-dark btn-lg' type='button' onClick={levelDown}>Visszalépés!
</button>}
</div>
<hr/>
<Felszereles osztaly={karakter.osztaly} felszereles={karakter.felszereles}
changeFelszereles={setFelszereles} harcosSpec={karakter.harcosSpecializaciok}/>
<hr/>
<div className='row'>
<h5 className='col align-self-center'>Véglegesítés</h5>
</div>
{fetchedUser.data != null &&
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Publikus karakterlap?</label>
<select className="col form-select" value={isPublic.toString()}
onChange={(event) => setIsPublic(event.target.value === "true")}>
<option value="true">Igen</option>
<option value="false">Nem</option>
</select>
</div>
}
<div className="row">
<div className="col-6">
<div className='d-grid gap-2 m-5'>
{fetchedUser.data == null
? <OverlayTrigger placement='top' overlay={saveOverlayTooltip} delay={0}
defaultShow={false} flip={false}>
<button className='btn btn-danger btn-lg' type='button'
onClick={onSaveClicked}>Mentés
</button>
</OverlayTrigger>
: <button className='btn btn-danger btn-lg' type='button'
onClick={onSaveClicked}>Mentés</button>
}
</div>
</div>
<div className="col-6">
<div className='d-grid gap-2 m-5'>
<button className='btn btn-danger btn-lg' type='button' onClick={async () => {
faro?.api.pushEvent('character_created', {
osztaly: karakter.osztaly,
szint: karakter.szint.toString(),
faj: karakter.faj.toString(),
})
await CreatePDF(KarakterInputToPdfView(karakter))
}}>PDF
</button>
</div>
</div>
</div>
</form>
</div>
</div>
);
}
export default CreateCharacterPage;
@@ -0,0 +1,89 @@
import {KarakterPdfView, KepzettsegPdfView} from "./karakter_pdf_view";
import {PDFDocument, rgb} from "pdf-lib";
import fontkit from '@pdf-lib/fontkit'
import {SignedNumberToText} from "../../shared/components/Helpers";
import download from "downloadjs";
import {DrawTulajdonsagok} from "./tulajdonsagok.pdf";
import {DrawMentok} from "./mentok.pdf";
import {DrawMagic} from "./magic.pdf";
import {DrawMasodlagosErtekek} from './masodlagos_ertekek.pdf'
import {DrawOsztalySpecialsPage} from './osztaly_specials.pdf'
import {DrawFegyverek} from "./fegyverek.pdf";
function drawBaseInfo(karakter: KarakterPdfView, draw: (text: string, x : number ,y: number, scale: number) => void) {
draw(karakter.Name, 60, 710, 1)
draw(karakter.Nem, 454, 710, 1)
draw(karakter.Kor.toString(), 454, 690, 1)
draw(karakter.Isten, 454, 670, 1)
draw(karakter.Osztaly, 60, 672, 1)
draw(karakter.Szint, karakter.Szint.length > 1 ? 252 : 264, 682, 3)
draw(karakter.Faj, 302, 710, 1)
draw(karakter.Jellem, 302, 672, karakter.Jellem.length > 17 ? 11/12 : 1)
}
function DrawKepzettsegek(draw: (text: string, x: number, y: number) => void, kepzettsegek: KepzettsegPdfView[], startFrom: number) {
for (let i = 0; i < kepzettsegek.length; i++) {
let kepzettseg = kepzettsegek[i]
draw(kepzettseg.KepzettsegName, 60, startFrom - (i * 18))
const kepzettsegModifier = SignedNumberToText(kepzettseg.KepzettsegModifier)
draw(kepzettsegModifier, kepzettsegModifier.length > 2 ? 172 : 175, startFrom - (i * 18))
}
}
function DrawFelszereles(draw: (text: string, x: number, y:number) => void, felszereles: string[]) {
const startFrom = 337
for (let i = 0; i < felszereles.length; i++) {
const item = felszereles[i];
draw(item, 390, startFrom - (i * 18))
}
}
export async function CreatePDF(karakter: KarakterPdfView) {
const existingPdfBytes = await fetch('/pdfs/km_karakterlap_hysteria_1.2.pdf').then(res => res.arrayBuffer())
const pdfDoc = await PDFDocument.load(existingPdfBytes)
pdfDoc.registerFontkit(fontkit)
const fontBytes = await fetch('/fonts/Merienda-Regular.ttf').then(res => res.arrayBuffer())
const pdfFont = await pdfDoc.embedFont(fontBytes)
const fontSizeBase = 12
const page = pdfDoc.getPage(0)
function drawText(text: string, x: number, y: number, fontScale = 1, shade = 0) {
page.drawText(text, {
x: x,
y: y,
size: fontSizeBase * fontScale,
font: pdfFont,
color: rgb(shade, shade, shade),
})
}
drawBaseInfo(karakter, drawText);
DrawTulajdonsagok(page, karakter.Tulajdonsagok, fontSizeBase, pdfFont);
DrawMasodlagosErtekek(page, karakter, fontSizeBase, pdfFont);
DrawMentok(page, karakter, fontSizeBase, pdfFont);
DrawKepzettsegek(drawText, karakter.Kepzettsegek, 337);
const nextKepzettsegFrom = 337 - (karakter.Kepzettsegek.length * 18)
DrawKepzettsegek(drawText, karakter.TolvajKepzettsegek, nextKepzettsegFrom)
DrawFelszereles(drawText, karakter.Felszereles)
DrawFegyverek(karakter, pdfFont, fontSizeBase, page)
DrawMagic(page, fontSizeBase, pdfFont, karakter.NapiMemorizalhatoVarazslatok, karakter.VarazslatMentokNF)
const secondPage = pdfDoc.addPage()
const fontBoldBytes = await fetch('/fonts/Merienda-Bold.ttf').then(res => res.arrayBuffer())
const fontBold = await pdfDoc.embedFont(fontBoldBytes)
DrawOsztalySpecialsPage(secondPage, pdfFont, fontBold, karakter)
const pdfBytes = await pdfDoc.save();
download(pdfBytes, karakter.Name + ".pdf", "application/pdf");
}
@@ -0,0 +1,51 @@
import {KarakterPdfView} from "./karakter_pdf_view";
import {PDFFont, PDFPage} from "pdf-lib";
import {SignedNumberToText} from "../../shared/components/Helpers";
function fitTextInWidth(font: PDFFont, text: string, startingSize: number, maxWidth: number): number {
let size = startingSize
while(font.widthOfTextAtSize(text, size) > maxWidth) {
size--
}
return size
}
export function DrawFegyverek(karakter: KarakterPdfView, pdfFont: PDFFont, fontSizeBase: number, page: PDFPage) {
const startFrom = 337
for (let i = 0; i < karakter.Fegyverek.length && i < 3; i++) {
const fegyver = karakter.Fegyverek[i];
const size = fitTextInWidth(pdfFont, fegyver.Name, fontSizeBase, 60)
page.drawText(fegyver.Name, {
x: 200,
y: startFrom - (i * 18) + ((18 - pdfFont.heightAtSize(size)) / 3),
size: size,
font: pdfFont,
})
page.drawText(SignedNumberToText(fegyver.TamadoBonusz), {
x: 265,
y: startFrom - (i * 18),
size: fontSizeBase,
font: pdfFont,
})
const damageSize = fitTextInWidth(pdfFont, fegyver.Damage, fontSizeBase, 24)
page.drawText(fegyver.Damage, {
x: 291,
y: startFrom - (i * 18) + ((18 - pdfFont.heightAtSize(damageSize)) / 3),
size: damageSize,
font: pdfFont,
})
const critSize = fitTextInWidth(pdfFont, fegyver.Crit, fontSizeBase, 35)
page.drawText(fegyver.Crit, {
x: 318,
y: startFrom - (i * 18) + ((18 - pdfFont.heightAtSize(critSize)) / 3),
size: critSize,
font: pdfFont,
})
page.drawText(fegyver.Range, {
x: 360,
y: startFrom - (i * 18),
size: fontSizeBase,
font: pdfFont,
})
}
}
@@ -0,0 +1,157 @@
import {TulajdonsagokTotal} from "../domain-models/tulajdonsag";
import {Mentok, MentokAlap, MentoModositok} from "../domain-models/mentok";
import {Kepzettseg, Kepzettsegek} from "../domain-models/kepzettsegek";
import {CalculateMasodlagosErtekek} from "../domain-models/masodlagos_ertekek";
import {Faj, FajLabel} from "../domain-models/faj";
import {Osztaly, OsztalyLabel, Skill} from "../domain-models/osztaly";
import {CelzoTB, KozelharciTB} from "../domain-models/tamadas_bonusz";
import {KarakterInputs} from "../domain-models/karakter";
import {CalculateVarazslatMentokNF, NapiVarazslatok} from "../domain-models/memorizalt_varazslatok";
import {GetJellem} from "../domain-models/jellem";
import {Fegyver, GetFegyverek, GetPajzs, GetPancel} from '../domain-models/felszereles'
import {SignedNumberToText} from "../../shared/components/Helpers";
import {OsztalySkillsTordelt} from "./osztaly_skills";
import {
KarakterTulajdonsagok,
Modifier,
TulajdonsagModosito
} from "../domain-models/tulajdonsag";
export interface KepzettsegPdfView {
KepzettsegName: string
KepzettsegModifier: number
}
export interface FegyverPdfView {
Name: string
TamadoBonusz: number
Damage: string
Crit: string
Range: string
}
export interface KarakterPdfView {
Name: string
Nem: string
Kor: number
Isten: string
Jellem: string
Faj: string
Osztaly: string
OsztalySkills: Skill[],
Tulajdonsagok: KarakterTulajdonsagok,
TulajdonsagModositok: KarakterTulajdonsagok,
Szint: string
HP: number
VO: number
Mozgas: number
Kezdemenyezes: number
KozelharciTB: number[]
CelzoTB: number[]
MentokAlap: Mentok
MentoModositok: Mentok
MentokModositokkal: Mentok
Kepzettsegek: KepzettsegPdfView[]
TolvajKepzettsegek: KepzettsegPdfView[]
NapiMemorizalhatoVarazslatok: number[]
VarazslatMentokNF : number[]
Felszereles: string[]
PancelVO: number
PajzsVO: number
Fegyverek: FegyverPdfView[]
}
export function KarakterInputToPdfView(karakter: KarakterInputs): KarakterPdfView {
const tulajdonsagok = TulajdonsagokTotal(karakter)
karakter = {...karakter, tulajdonsagok}
const tulajdonsagModositok = TulajdonsagModosito(tulajdonsagok)
const masodlagosErtekek = CalculateMasodlagosErtekek(karakter)
const pancel = GetPancel(karakter.felszereles.pancelID)
const pajzs = GetPajzs(karakter.felszereles.pajzsID)
const mozgas = karakter.faj === Faj.Torpe || pancel?.Type === 'nehez' || pancel?.Type === 'kozepes' ? 20 : 30
const kezdemenyezes = tulajdonsagModositok.t_ugy + (karakter.osztaly === Osztaly.Tolvaj ? 4 : 0)
const mentok = MentokAlap(karakter.osztaly, karakter.szint)
const mentoModositok = MentoModositok(tulajdonsagModositok)
const NapiMemorizalhatoVarazslatok = NapiVarazslatok(karakter)
const VarazslatMentokNF = CalculateVarazslatMentokNF(karakter)
const fegyverek = GetFegyverek(karakter.felszereles.fegyverIDk)
const felszereles = [ pancel?.Name, pajzs?.Name, ...fegyverek.filter(f => f.Id !== 'okol').map(f => f.Name.includes('és') ? f.Name.split(' ')[0] : f.Name) ].filter(x => !!x) as string[]
return {
Faj: FajLabel(karakter.faj),
Isten: karakter.isten || "",
Jellem: GetJellem(karakter.jellem).Label,
Kor: karakter.kor,
Name: karakter.name,
Nem: karakter.nem || "",
Tulajdonsagok: karakter.tulajdonsagok,
Osztaly: OsztalyLabel(karakter.osztaly),
OsztalySkills: OsztalySkillsTordelt(karakter.osztaly),
Szint: karakter.szint.toString(),
Kepzettsegek: karakter.kepzettsegek.map(id => mapKepzettsegToPdfView(Kepzettsegek[id], tulajdonsagok, karakter.szint)),
TolvajKepzettsegek: karakter.tolvajKepzettsegek?.map(id => mapKepzettsegToPdfView(Kepzettsegek[id], tulajdonsagok, karakter.szint)) || [],
TulajdonsagModositok: tulajdonsagModositok,
HP: masodlagosErtekek.HP,
VO: masodlagosErtekek.VO,
Mozgas: mozgas,
Kezdemenyezes: kezdemenyezes,
KozelharciTB: KozelharciTB(karakter),
CelzoTB: CelzoTB(karakter),
MentoModositok: mentoModositok,
MentokAlap: mentok,
MentokModositokkal: masodlagosErtekek.Mentok,
NapiMemorizalhatoVarazslatok,
VarazslatMentokNF,
Felszereles: felszereles,
PajzsVO: pajzs?.VO ?? 0,
PancelVO: pancel?.VO ?? 0,
Fegyverek: fegyverek.map(mapFegyverToPdfView(karakter)),
}
}
function mapKepzettsegToPdfView(kepzettseg: Kepzettseg, tulajdonsagok: KarakterTulajdonsagok, szint: number) {
const tulajdonsagErtek = kepzettseg.Tulajdonsag.reduce((maxTulajdonsagErtek, currentTulajdonsag) =>
tulajdonsagok[currentTulajdonsag] > maxTulajdonsagErtek
? tulajdonsagok[currentTulajdonsag]
: maxTulajdonsagErtek,
tulajdonsagok[kepzettseg.Tulajdonsag[0]])
const modifier = Modifier(tulajdonsagErtek) + szint
return {
KepzettsegName: kepzettseg.Name,
KepzettsegModifier: modifier,
}
}
function mapFegyverToPdfView(karakter: KarakterInputs) {
return (fegyver: Fegyver): FegyverPdfView => {
const simplifiedName = fegyver.Name.includes('és') ? fegyver.Name.split(' ')[0] : fegyver.Name
const tb = fegyver.Type === 'kozelharci' ? KozelharciTB(karakter)[0] : CelzoTB(karakter)[0]
const tulajdonsagModositok = TulajdonsagModosito(TulajdonsagokTotal(karakter))
let damageModifier = fegyver.Type === 'kozelharci' || (fegyver.Id === 'visszacsapo_ij' && tulajdonsagModositok.t_ero > 0) ? tulajdonsagModositok.t_ero : 0
damageModifier += karakter.harcosSpecializaciok.filter(x => x === fegyver.Id).length * 2
if (fegyver.Id === 'visszacsapo_ij' && tulajdonsagModositok.t_ero > 0) { // "hozzaadhato", so only add if positive
damageModifier += tulajdonsagModositok.t_ero
}
if (fegyver.Ketkezes) {
damageModifier += Math.floor(tulajdonsagModositok.t_ero / 2)
}
const damage = `${fegyver.NumberOfDamageDice > 1 ? fegyver.NumberOfDamageDice.toString() : ''}d${fegyver.DamageDice}${damageModifier !== 0 ? SignedNumberToText(damageModifier) : ''}`
const critStart = fegyver.CritRangeStart - karakter.kalozKritikus.filter(x => x === fegyver.Id).length
const crit = `${critStart < 20 ? `${critStart}-20` : '20'} x${fegyver.CritMultiplier}`
const range = fegyver.Range === 0 ? '-' : fegyver.Range.toString()
return {
Name: simplifiedName,
TamadoBonusz: tb,
Damage: damage,
Crit: crit,
Range: range,
}
}
}
@@ -0,0 +1,30 @@
import {PDFFont, PDFPage, rgb} from "pdf-lib";
export function DrawMagic(page: PDFPage, fontSizeBase: number, pdfFont: PDFFont, napiVarazslatok: number[], varazsMentokNF: number[]){
for(let i = 0; i < 6; i++) {
const y = 247 - (i*18)
page.drawText(i.toString(), {
x: 210,
y: y,
size: fontSizeBase,
font: pdfFont,
color: rgb(0, 0, 0),
})
if (i < napiVarazslatok.length) {
page.drawText(napiVarazslatok[i].toString(), {
x: 242,
y: y,
size: fontSizeBase,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(varazsMentokNF[i].toString(), {
x: 269,
y: y,
size: fontSizeBase,
font: pdfFont,
color: rgb(0, 0, 0),
})
}
}
}
@@ -0,0 +1,88 @@
import {PDFFont, PDFPage} from "pdf-lib";
import {KarakterPdfView} from "./karakter_pdf_view";
import {SignedNumberToText} from "../../shared/components/Helpers";
export function DrawMasodlagosErtekek(
page: PDFPage,
karakter: KarakterPdfView,
fontSizeBase: number,
pdfFont: PDFFont)
{
page.drawText(karakter.Mozgas.toString(), {
x: 162,
y: 416,
size: fontSizeBase * 2,
font: pdfFont,
})
page.drawText(SignedNumberToText(karakter.Kezdemenyezes), {
x: 505,
y: 416,
size: fontSizeBase * 2,
font: pdfFont,
})
page.drawText(karakter.HP.toString(), {
x: 318,
y: 517,
size: fontSizeBase * 1.5,
font: pdfFont,
})
page.drawText(karakter.VO.toString(), {
x: 368,
y: 495,
size: fontSizeBase * 3,
font: pdfFont,
})
page.drawText(karakter.PancelVO.toString(), {
x: 512,
y: 515,
size: fontSizeBase * 2,
font: pdfFont,
})
page.drawText(karakter.PajzsVO.toString(), {
x: 512,
y: 482,
size: fontSizeBase * 2,
font: pdfFont,
})
const kozelharciTB = karakter.KozelharciTB.map(SignedNumberToText).join("/")
function getFontSize(length: number): number {
switch (length) {
case 1:
case 2:
return 24;
case 3:
return 18;
case 4:
return 14;
case 5:
return 11;
case 6:
return 9;
case 7:
return 8;
default:
return 6;
}
}
function getYOffset(length: number): number {
return Math.min(length - 2, 6);
}
const kozelHarciFontSize = getFontSize(kozelharciTB.length)
page.drawText(kozelharciTB, {
x: kozelharciTB.length > 4 ? 256 : 258,
y: 383 + getYOffset(kozelharciTB.length),
size: kozelHarciFontSize,
font: pdfFont,
})
const celzoTB = karakter.CelzoTB.map(SignedNumberToText).join("/")
const celzoFontSize = getFontSize(celzoTB.length)
page.drawText(celzoTB, {
x: celzoTB.length > 4 ? 343 : 346,
y: 383 + getYOffset(celzoTB.length),
size: celzoFontSize,
font: pdfFont,
})
}
@@ -0,0 +1,72 @@
import {PDFFont, PDFPage, rgb} from "pdf-lib";
import {KarakterPdfView} from "./karakter_pdf_view";
import {SignedNumberToText} from "../../shared/components/Helpers";
import {Modifier} from "../domain-models/tulajdonsag";
export function DrawMentok(page: PDFPage, karakter: KarakterPdfView, fontSizeBase: number, pdfFont: PDFFont) {
const mentokAlap = karakter.MentokAlap
page.drawText(SignedNumberToText(mentokAlap.kitartas), {
x: 435,
y: 613,
size: fontSizeBase,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(SignedNumberToText(mentokAlap.reflex), {
x: 435,
y: 580,
size: fontSizeBase,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(SignedNumberToText(mentokAlap.akaratero), {
x: 435,
y: 547,
size: fontSizeBase,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(SignedNumberToText(Modifier(karakter.Tulajdonsagok.t_egs)), {
x: 475,
y: 613,
size: fontSizeBase,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(SignedNumberToText(Modifier(karakter.Tulajdonsagok.t_ugy)), {
x: 475,
y: 580,
size: fontSizeBase,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(SignedNumberToText(Modifier(karakter.Tulajdonsagok.t_bol)), {
x: 475,
y: 547,
size: fontSizeBase,
font: pdfFont,
color: rgb(0, 0, 0),
})
const mentok = karakter.MentokModositokkal
page.drawText(SignedNumberToText(mentok.kitartas), {
x: 515,
y: 613,
size: fontSizeBase,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(SignedNumberToText(mentok.reflex), {
x: 515,
y: 580,
size: fontSizeBase,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(SignedNumberToText(mentok.akaratero), {
x: 515,
y: 547,
size: fontSizeBase,
font: pdfFont,
color: rgb(0, 0, 0),
})
}
@@ -0,0 +1,166 @@
import {Osztaly, Skill} from "../domain-models/osztaly";
export function OsztalySkillsTordelt(osztaly: Osztaly) : Skill[] {
switch (osztaly) {
case Osztaly.Harcos: return [{
Id: 's_harcos_harcos',
Name: 'Harcos',
Description: 'Speciális harci tapasztalatuknak köszönhetően minden páratlan szinten +2-t kapnak egy\n' +
'választott fegyvertípus (pl. csatabárd, hosszúkard, stb.) sebzésére. Ez fegyverenként csak\n' +
'egyszer választható, s arra is fölhasználható, hogy a harcos megtanulja egy egzotikus\n' +
'fegyver használatát, amire később specializálódhat is. 9. szinttől elérhető számukra a\n' +
'kettős specializáció: ezzel sebzésmódosítójukat az adott fegyverre +4-re emelhetik.'
}]
case Osztaly.Ijasz: return [
{
Id: 's_harcos_ijasz',
Name: 'Íjász',
Description: 'Íjak használatakor körönként eggyel többször támadhatnak.\n' +
'Értenek a visszacsapó íjak használatához is.'
},
{
Id: 's_harcos_ijasz_vert',
Name: 'Kötelező mozgékonyság',
Description: 'Az íjászok nem viselhetnek nehéz vérteket.'
}
]
case Osztaly.Amazon: return [
{
Id: 's_harcos_amazon_vert',
Name: 'MMO vért',
Description:
'Az amazonok csak láncinget vagy könnyebb vérteket viselhetnek, de VO-juk Szint/2-vel\n' +
'javul, továbbá Karizma- és Ügyességmódosítói közül a kedvezőbbet választhatják.'
},
{
Id: 's_harcos_amazon_csapatmunka',
Name: 'Csapatmunka',
Description: 'Ha kettő vagy több amazon együttesen harcol, VO-juk további 2 ponttal nő pszionikus\n' +
'képességeik miatt.'
},
]
case Osztaly.Kaloz: return [
{
Id: 's_harcos_kaloz_ugyes_huzas',
Name: 'Ügyes húzás',
Description: 'A kalózok és tengerészek ha magasabb, Ügyességbónuszukat adják hozzá közelharci\n' +
'támadásaikhoz.'
},
{
Id: 's_harcos_kaloz_vasmacska',
Name: 'Vasmacska',
Description: 'A kalózok és tengerészek nem viselhetnek nehéz vértezetet, de VO-juk könnyű vértben\n' +
'Szint/3-al javul.'
},
{
Id: 's_harcos_kaloz_kritikus',
Name: 'Kritikus',
Description: 'Minden harmadik szinten kiválaszthatnak egy fegyvert, amivel könnyebben okoznak\n' +
'kritikus sebesülést, tehát pl. csatabárddal 20 helyett 19-20-on, hosszúkarddal 19-20\n' +
'helyett 18-20-on, szablyával 18-20 helyett 17-20-on, stb. Ez a képesség 9. szintig\n' +
'fegyverenként csak egyszer választható.'
}]
case Osztaly.Barbar: return [
{
Id: 's_harcos_barbar_eletero',
Name: 'Túlélő',
Description: 'A barbárok 1d12-vel dobják a Hp-jüket, -5 Hp-ig öntudatuknál vannak és\n' +
'+1 bónuszt (10. szinttől +2, 15. szinttől +4) kapnak a mentődobásaikra.'
},
{
Id: 's_harcos_barbar_iras',
Name: 'Írástudatlan',
Description: 'Minden barbár írástudatlanul kezdi a játékot, de ha kell,\n' +
'később megtanulhatja a betűvetést.'
}
]
case Osztaly.Pap: return [
{
Id: 's_pap_gyogyseb',
Name: 'Gyógyítás vagy sebokozás',
Description:
'A pap az aznap már memorizált (megtanult) varázslatokat gyógyításra vagy sebek\n' +
'okozására is fölhasználhatja. Érintésével a varázslat minden szintjéért 1d8 Hp-t gyógyít\n' +
'vagy sebez így pl. egy Ómen elhasználásával 2d8 Hp-t. Az így konvertált varázslat\n' +
'természetesen elvész. Az, hogy a képesség sebek gyógyítására vagy okozására szolgál, a\n' +
'pap istenétől függ. A békésebb istenek főleg az elsőt, a harciasabbak főleg a másodikat\n' +
'részesítik előnyben, s vannak olyanok is, akik híveikre bízzák, hogy melyik utat választják.\n' +
'Ha azonban a pap már eldöntötte, melyik képességben járatos, döntését többé nem\n' +
'másíthatja meg!'
},
{
Id: 's_pap_elohalott',
Name: 'Élőholtak elűzése vagy irányítása',
Description:
'A papok istenük dogmájának megfelelően képesek lehetnek arra, hogy élőholt szörnyeket\n' +
'(mint például zombikat, lidérceket vagy vámpírokat) szimbólumuk felmutatásával\n' +
'elűzzenek vagy szolgálatukba állítsanak. Ez a képesség naponta csak egyszer (5. szinttől\n' +
'kétszer, 10. szinttől háromszor, 15. szinttől négyszer) használható. A cselekedet a\n' +
'következő módon működik:',
ExtendedDescription: [
'A pap [1d20+Kar bónusz] próbát tesz. Ennek az értékét összeveti a standard bónusztáblázattal. Az innen kiolvasható módosítót hozzáadja saját szintjéhez (ha negatív, akkor levonja). Az eredmény jelzi, hogy a pap legfeljebb milyen szintű élőholtakra lesz hatással. Ha tehát pl. a próba végeredménye 14 és a pap ötödik szintű, akkor 5+1, azaz 6. szintű a legerősebb élőhalott, amire a kísérlet sikeres. A 20-as dobás itt is automatikus siker, az 1-es automatikus kudarc.',
'A pap [2d6+szint] szintű élőhalottat űzhet el vagy irányíthat. Először a leggyengébb élőholtakra hat, azután a következő leggyengébbekre, stb. Az elűzött élőholtak elmenekülnek a pap szeme elől és addig nem is ártanak neki és társainak, amíg a pap csapata meg nem támadja őket! Ezzel szemben a gonosz papok parancsokat adhatnak az irányított szörnyeknek. Mindkét hatás legfeljebb pár óráig tart, az élőholtak szintjétől és a körülményektől függően.',
'Ha az összes szem előtt lévő élőholtat sikerült elűzni vagy irányítani és még maradt „a szent energiából”, akkor ismét el kell kezdeni a hatást a leggyengébb ellenségektől. Az így elűzött szörnyek megsemmisülnek, illetve irányítás esetén végleg a gonosz pap szolgálatába állnak.',
'A két hatás egymás ellen is fölhasználható, például egy jó pap kiolthatja egy gonosz pap irányítását, de akár két gonosz pap is versenyezhet egy csapat sírlidérc „megtérítésén”.',
]
},
{
Id: 's_pap_invokacio',
Name: 'Invokáció',
Description:
'A pap kérheti istene segítségét (általában nevének kimondásával), ami +1-et, ritkábban\n' +
' +2-t ad hozzá dobásaihoz. A papok hetente annyiszor használhatják az invokációt,\n' +
' amekkora a Bölcsességbónuszuk (de legalább egyszer).'
}
]
case Osztaly.Tolvaj: return [
{
Id: 's_tolvaj_orvtamadas',
Name: 'Orvtámadás',
Description:
'az orvtámadás akkor használható, ha a tolvaj meglepi áldozatát, tehát pl. csöndesen a\n' +
'hátába kerül, vagy beszélgetés közben tőrt ránt és egyetlen mozdulattal gyomron szúrja.\n' +
'Az orvtámadás minden páratlan szintért 1d6 bónusz sebzést okoz (maximuma +5d6).\n' +
'Dobó- és lőfegyverrel is használható, és ha a tolvaj úgy dönt, illetve a használt fegyver\n' +
'lehetővé teszi, az ellenfél elkábítására is alkalmazható.'
},
{
Id: 's_tolvaj_kezdemenyezes',
Name: 'Jobb kezdeményezés',
Description: 'a tolvajok +4-et kapnak kezdeményezési dobásaikra.',
},
{
Id: 's_tolvaj_kepzettsegek',
Name: 'Tolvajképzettségek',
Description:
'Minden tolvaj jártas több olyan képzettségben, amely segíti a rejtőzködésben, a jól őrzött\n' +
'értékek eltulajdonításában és hasonló feladatokban. A tolvajok azokat a tolvajképzettség-\n' +
'eket is használhatják, amelyekben nem járatosak külön, de ekkor szintjük nem számít bele\n' +
'a próbadobás módosítójába! A tolvajképzettségek listája:\n' +
'Álcázás, Csapdák állítása és eltávolítása, Egyensúlyozás, Értékbecslés, Hallgatózás,\n' +
'Hamisítás, Jelek olvasása, Keresés, Mászás, Megfigyelés, Méregkeverés, Osonás, Rejtőzés,\n' +
'Szabadulóművészet, Zárnyitás és Zsebmetszés.',
},
]
case Osztaly.Varazslo: return [{
Id: 's_varazslo_varazskonyv',
Name: 'Varázskönyv',
Description:
'Egy első szintű varázsló varázskönyve 6 nulladik és 3 első szintű varázslatot tartalmaz,\n' +
'harmadik szintű kezdő karakteré ezen felül még 2 első szintűt és 2 második szintűt.\n' +
'Az újabb varázslatokat mások könyveiből, mágikus tekercsekből vagy más módszerek\n' +
'segítségével kell könyvébe másolnia. Az illuzionisták szintén varázskönyveket használnak\n' +
'de eltérő mágiaformájuk miatt nem képesek a varázslói varázslatok megértésére és viszont.'
}]
case Osztaly.Illuzionista: return [{
Id: 's_illuzionista_varazskonyv',
Name: 'Varázskönyv',
Description:
'Egy első szintű illuzionista varázskönyve 6 nulladik és 3 első szintű varázslatot tartalmaz,\n' +
'harmadik szintű kezdő karakteré ezen felül még 2 első szintűt és 2 második szintűt. Az\n' +
'újabb varázslatokat mások könyveiből, mágikus tekercsekből vagy más módszerek\n' +
'segítségével kell könyvébe másolnia. Az varázslók szintén varázskönyveket használnak, de\n' +
'eltérő mágiaformájuk miatt nem képesek a varázslói varázslatok megértésére és viszont.'
}]
}
}
@@ -0,0 +1,69 @@
import {PDFFont, PDFPage, rgb, breakTextIntoLines} from "pdf-lib";
import {KarakterPdfView} from "./karakter_pdf_view";
import {Skill} from "../domain-models/osztaly";
const maxWidth = 540
function drawExtendedSkillDescription(skill: Skill, font: PDFFont, baseFontSize: number, page: PDFPage, rows: number) {
const indent = 20
for (let j = 0; j < (skill.ExtendedDescription?.length || 0); j++) {
const paragraph = `${j + 1}. ${skill.ExtendedDescription![j]}`
const lines = breakTextIntoLines(paragraph, [' ', '-'], maxWidth - indent, (s) => font.widthOfTextAtSize(s, baseFontSize))
page.drawText(paragraph, {
x: 30 + indent,
y: 700 - rows,
size: baseFontSize,
font: font,
lineHeight: 16,
color: rgb(0, 0, 0),
maxWidth: maxWidth - indent,
wordBreaks: [' ', '-']
})
rows += lines.length * font.heightAtSize(baseFontSize)
}
return rows;
}
export function DrawOsztalySpecialsPage(page: PDFPage, font: PDFFont, fontBold: PDFFont, karakter: KarakterPdfView): number {
const baseFontSize = 12
const titleFontSize = baseFontSize * 3
const pageCenterX = page.getWidth()/2
const titleWidth = font.widthOfTextAtSize(karakter.Osztaly, titleFontSize)
const titleX = pageCenterX - (titleWidth / 2)
page.drawText(karakter.Osztaly, {
x: titleX,
y: 770,
size: titleFontSize,
font: font,
color: rgb(0, 0, 0),
})
let rows = 0
for (let i = 0; i < karakter.OsztalySkills.length; i++) {
const skill = karakter.OsztalySkills[i];
page.drawText(skill.Name, {
x: 30,
y: 720 - rows,
size: baseFontSize,
font: fontBold,
color: rgb(0, 0, 0),
})
const lines = breakTextIntoLines(skill.Description, [' ', '-'], maxWidth, (s) => font.widthOfTextAtSize(s, baseFontSize))
page.drawText(skill.Description, {
x: 30,
y: 700 - rows,
size: baseFontSize,
font: font,
lineHeight: 16,
color: rgb(0, 0, 0),
maxWidth: maxWidth,
wordBreaks: [' ', '-']
})
// rows += Math.ceil(font.widthOfTextAtSize(skill.Description, baseFontSize)/textWidth)*20 + 30
// rows += skill.Description.split('\n').length*16 + 30
rows += lines.length * font.heightAtSize(baseFontSize)
rows = drawExtendedSkillDescription(skill, font, baseFontSize, page, rows);
rows += 24 // for title and spacing
}
return rows
}
@@ -0,0 +1,90 @@
import {PDFFont, PDFPage, rgb} from "pdf-lib";
import {SignedNumberToText} from "../../shared/components/Helpers";
import {KarakterTulajdonsagok, Modifier} from "../domain-models/tulajdonsag";
export function DrawTulajdonsagok(page: PDFPage, tulajdonsagok: KarakterTulajdonsagok, fontSizeBase: number, pdfFont: PDFFont) {
page.drawText(tulajdonsagok.t_ero.toString(), {
x: 130,
y: 613,
size: fontSizeBase * 2,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(SignedNumberToText(Modifier(tulajdonsagok.t_ero)), {
x: 176,
y: 613,
size: fontSizeBase * 2,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(tulajdonsagok.t_ugy.toString(), {
x: 130,
y: 580,
size: fontSizeBase * 2,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(SignedNumberToText(Modifier(tulajdonsagok.t_ugy)), {
x: 176,
y: 580,
size: fontSizeBase * 2,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(tulajdonsagok.t_egs.toString(), {
x: 130,
y: 547,
size: fontSizeBase * 2,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(SignedNumberToText(Modifier(tulajdonsagok.t_egs)), {
x: 176,
y: 547,
size: fontSizeBase * 2,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(tulajdonsagok.t_int.toString(), {
x: 130,
y: 515,
size: fontSizeBase * 2,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(SignedNumberToText(Modifier(tulajdonsagok.t_int)), {
x: 176,
y: 515,
size: fontSizeBase * 2,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(tulajdonsagok.t_bol.toString(), {
x: 130,
y: 482,
size: fontSizeBase * 2,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(SignedNumberToText(Modifier(tulajdonsagok.t_bol)), {
x: 176,
y: 482,
size: fontSizeBase * 2,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(tulajdonsagok.t_kar.toString(), {
x: 130,
y: 449,
size: fontSizeBase * 2,
font: pdfFont,
color: rgb(0, 0, 0),
})
page.drawText(SignedNumberToText(Modifier(tulajdonsagok.t_kar)), {
x: 176,
y: 449,
size: fontSizeBase * 2,
font: pdfFont,
color: rgb(0, 0, 0),
})
}
+8
View File
@@ -0,0 +1,8 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
+32
View File
@@ -0,0 +1,32 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import 'bootstrap/dist/css/bootstrap.css';
import './index.css';
import App from './App';
import { getWebInstrumentations, initializeFaro } from '@grafana/faro-web-sdk';
const faro = window.location.hostname === 'localhost' ? undefined : initializeFaro({
url: 'https://faro-collector-prod-eu-west-2.grafana.net/collect/88adcc2543da5af525c451166286f2cc',
app: {
name: 'kemkas',
version: '1.0.0',
environment: 'production'
},
instrumentations: [
// Mandatory, overwriting the instrumentations array would cause the default instrumentations to be omitted
...getWebInstrumentations(),
// Initialization of the tracing package.
// This packages is optional because it increases the bundle size noticeably. Only add it if you want tracing data.
// new TracingInstrumentation(),
],
});
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App faro={faro} />
</React.StrictMode>
);
+1
View File
@@ -0,0 +1 @@
/// <reference types="react-scripts" />
+15
View File
@@ -0,0 +1,15 @@
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;
@@ -0,0 +1,32 @@
import axios from "axios";
import {Karakter2E} from "../domain-models/karakter2E";
export async function StoreNewCharacter2E(karakter: Karakter2E, isPublic: boolean = false) {
let response = await axios.post(`${window.location.origin}/api/Character2E/`, karakter, {
withCredentials: true,
params: {
isPublic,
}
})
if (response.status < 300){
return response.data as string
}
else {
throw Error(response.statusText)
}
}
export async function UpdateCharacter2E(id: string, karakter: Karakter2E, isPublic: boolean = false) {
let response = await axios.post(`${window.location.origin}/api/Character2E/${id}`, karakter, {
withCredentials: true,
params: {
isPublic,
}
})
if (response.status < 300){
return response.data as string
}
else {
throw Error(response.statusText)
}
}
@@ -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,43 @@
import React from "react";
import {Faj2E, FajDescription, FajLabel, FajSpecials} from "../domain-models/faj2E";
function FajSelector2E(props: { faj: Faj2E, changeFaj: (val: Faj2E) => void }) {
const {faj, changeFaj} = props
return <>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Faj</label>
<select className='col form-select' value={faj} onChange={e => changeFaj(e.target.value as Faj2E)}>
<option key={Faj2E.Ember} value={Faj2E.Ember}>{FajLabel(Faj2E.Ember)}</option>
<optgroup label='más emberi népek'>
<option key={Faj2E.Amazon} value={Faj2E.Amazon}>{FajLabel(Faj2E.Amazon)}</option>
<option key={Faj2E.Birodalmi} value={Faj2E.Birodalmi}>{FajLabel(Faj2E.Birodalmi)}</option>
<option key={Faj2E.Eszaki} value={Faj2E.Eszaki}>{FajLabel(Faj2E.Eszaki)}</option>
<option key={Faj2E.Nomad} value={Faj2E.Nomad}>{FajLabel(Faj2E.Nomad)}</option>
<option key={Faj2E.Osember} value={Faj2E.Osember}>{FajLabel(Faj2E.Osember)}</option>
</optgroup>
<optgroup label='nem emberi fajok'>
<option key={Faj2E.Elf} value={Faj2E.Elf}>{FajLabel(Faj2E.Elf)}</option>
<option key={Faj2E.Felelf} value={Faj2E.Felelf}>{FajLabel(Faj2E.Felelf)}</option>
<option key={Faj2E.Felork} value={Faj2E.Felork}>{FajLabel(Faj2E.Felork)}</option>
<option key={Faj2E.Felszerzet}
value={Faj2E.Felszerzet}>{FajLabel(Faj2E.Felszerzet)}</option>
<option key={Faj2E.Gnom} value={Faj2E.Gnom}>{FajLabel(Faj2E.Gnom)}</option>
<option key={Faj2E.Torpe} value={Faj2E.Torpe}>{FajLabel(Faj2E.Torpe)}</option>
</optgroup>
</select>
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 form-label'></label>
<div className='col'>
<p className='row pt-2'>
{FajDescription(faj)}
</p>
<ul>
{FajSpecials(faj).map(((special, i) => (<li key={faj + i}>{special}</li>)))}
</ul>
</div>
</div>
</>
}
export default FajSelector2E
@@ -0,0 +1,87 @@
import React from "react";
import {Fegyver} from "../domain-models/felszereles";
export function FegyverFlags(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")
}
return flags.join(' ');
}
export function FegyverLabel(fegyver: Fegyver): string {
const serializedFlags = FegyverFlags(fegyver) + (fegyver.Megjegyzes != null ? " " + fegyver.Megjegyzes : "");
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
@@ -0,0 +1,73 @@
import React from "react";
import {Fegyver, KarakterFelszereles, PancelTypeLabel} from "../domain-models/felszereles";
import {Osztaly2E} from '../domain-models/osztaly2E'
import {AllowedPajzsTypes, AllowedPancelTypes} from '../domain-models/allowed-pancel-types'
import fegyverek from '../domain-models/fegyver.json'
import {arraySetN} from "../../util";
import {AllowedFegyver} from "../domain-models/allowed-fegyver";
import FegyverSelector from "./FegyverSelector";
function Felszereles(props: {felszereles: KarakterFelszereles, changeFelszereles: (felszereles: KarakterFelszereles) => void, osztaly: Osztaly2E}) {
const { felszereles, changeFelszereles, osztaly } = props;
const allowedPancel = AllowedPancelTypes([osztaly])
const allowedPajzs = AllowedPajzsTypes([osztaly])
const valasztottFegyverek = felszereles.fegyverIDk
.map((id, idx) => ({idx, fegyver: fegyverek.data.find(f => f.Id === id)})).filter((x) => x.fegyver != null).map(({idx, fegyver}) => ({idx, fegyver: fegyver as Fegyver}))
const osztalyAllowedFegyverek = AllowedFegyver(osztaly)
const allowedFegyverek = [...osztalyAllowedFegyverek, ...valasztottFegyverek.filter(({idx, fegyver}) => !osztalyAllowedFegyverek.map(f => f.Id).includes(fegyver.Id)).map(({fegyver}) => fegyver)]
const changeFegyver = (idx: number, newID: string) => {
const found = fegyverek.data.find(x => x.Id === newID)
if (!found) {
console.log('Nem létező fegyver ID: ', newID)
return
}
changeFelszereles({...felszereles, fegyverIDk: arraySetN(felszereles.fegyverIDk, idx, newID)})
}
return <>
<div className='row'>
<h5 className='col align-self-center'>Felszerelés</h5>
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Viselt páncél</label>
<select className='col form-select' value={felszereles.pancelID} onChange={e => changeFelszereles({...felszereles, pancelID: e.target.value === 'nincs' ? undefined : e.target.value})}>
<option key={'nincs'} value={'nincs'}>Nincs</option>
{
<optgroup label={PancelTypeLabel("konnyu")}>
{allowedPancel.filter(p => p.Type === "konnyu").map(p =>
<option key={p.Id} value={p.Id}>{`${p.Name}${p.Size > 1 ? ` [${p.Size}]` : ''} (+${p.VO} VO) - ${p.Price} at`}</option>)}
</optgroup>
}
{
allowedPancel.some(p => p.Type === "nehez") &&
<optgroup label={PancelTypeLabel("nehez")}>
{allowedPancel.filter(p => p.Type === "nehez").map(p =>
<option key={p.Id} value={p.Id}>{`${p.Name} (+${p.VO} VO) - ${p.Price} at`}</option>)}
</optgroup>
}
</select>
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Hordott pajzs</label>
<select className='col form-select' value={felszereles.pajzsID}
onChange={e => changeFelszereles({...felszereles, pajzsID: e.target.value === 'nincs' ? undefined : e.target.value})}>
<option key={'nincs'} value={'nincs'}>Nincs</option>
{allowedPajzs.map(p => <option key={p.Id} value={p.Id}>{`${p.Name} (+${p.VO} VO) - ${p.Price} at`}</option>)}
</select>
</div>
{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>
<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,
fegyverIDk: [...felszereles.fegyverIDk.slice(0, idx), ...felszereles.fegyverIDk.slice(idx+1)]})}>Elvesz</button>
</div>
))}
<button className='btn btn-outline-dark btn btn-lg ms-2' type='button' onClick={() => changeFelszereles({...felszereles, fegyverIDk: [...felszereles.fegyverIDk, 'bot']})}>Fegyver hozzáadása</button>
</>
}
export default 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,38 @@
// import React from "react";
// import fegyverek from "../domain-models/fegyver.json"
// import {defaultFegyverID, GetFegyver} from "../domain-models/felszereles";
//
// function HarcosFegyverSpecializacio(props: {
// specialization: string,
// existingSpecializations: string[],
// changeSpecialization: (specialization: string) => void
// szint: number
// }) {
// const {
// specialization,
// existingSpecializations,
// changeSpecialization,
// szint
// } = props
// const fegyver = GetFegyver(specialization)
// const pickableFegyverek = fegyverek.data.filter(f => f.Id === specialization || existingSpecializations.filter(x => x === f.Id).length < (szint < 9 ? 1 : 2))
// const kozelharciPickable = pickableFegyverek.filter(f => f.Type === 'kozelharci')
// const tavolsagiPickable = pickableFegyverek.filter(f => f.Type === 'lofegyver')
//
// return <>
// <div className='row m-2'>
// <label className='col-md-2 col-sm-3 col-form-label' >Fegyver specializáció</label>
// <select className='col form-select' value={fegyver?.Id || defaultFegyverID} onChange={e => changeSpecialization(e.target.value)}>
// <optgroup label="Közelharci">
// {kozelharciPickable.map(f => <option key={`spec-${szint}-${f.Id}`} value={f.Id}>{f.Name}</option>)}
// </optgroup>
// <optgroup label="Távolsági">
// {tavolsagiPickable.map(f => <option key={`spec-${szint}-${f.Id}`} value={f.Id}>{f.Name}</option>)}
// </optgroup>
// </select>
// {fegyver == null && <span className='form-field-error'>Ismeretlen fegyver: {specialization}</span>}
// </div>
// </>
// }
//
// export default HarcosFegyverSpecializacio
@@ -0,0 +1,27 @@
import React from "react";
import {AllJellemIDs, GetJellem, JellemID} from "../domain-models/jellem";
function JellemSelector(props: {selected: JellemID, changeJellem: (val: JellemID) => void}) {
const { selected, changeJellem } = props
const jellem = GetJellem(selected)
return <>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Jellem</label>
<select className='col form-select' value={selected} onChange={e => changeJellem(e.target.value as JellemID)}>
{AllJellemIDs.map((id) => (
<option key={id} value={id}>{GetJellem(id).Label}</option>
))}
</select>
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 form-label'></label>
<div className='col'>
<p className='row pt-2'>
{jellem.Description}
</p>
</div>
</div>
</>
}
export default JellemSelector
@@ -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,89 @@
import React from "react";
import KepzettsegSelector from "./KepzettsegSelector2E";
import {
Kepzettseg,
Kepzettsegek,
KepzettsegId,
TolvajKepzettsegList,
} from "../domain-models/kepzettsegek2E";
import {arraySetN} from "../../util";
function InternalKepzettsegekSelector(props: {
title: string,
numberOfKepzettseg: number,
getKepzettsegListaN: (n: number) => Kepzettseg[],
kepzettsegek: KepzettsegId[],
changeKepzettsegek: (newKepzettsegek: KepzettsegId[]) => void
}) {
const {title, numberOfKepzettseg, getKepzettsegListaN, kepzettsegek, changeKepzettsegek} = props
const klist = Array.from(new Array(numberOfKepzettseg).keys())
const preCalculated: {
kepzettsegek: Kepzettseg[],
changeKepzettseg: (newKepzettseg: KepzettsegId) => void,
selected: Kepzettseg,
}[] = [];
for (let i = 0; i < numberOfKepzettseg; i++) {
preCalculated.push({
kepzettsegek: getKepzettsegListaN(i),
selected: Kepzettsegek[kepzettsegek[i]],
changeKepzettseg: (newKepzettseg: KepzettsegId) => changeKepzettsegek(arraySetN(kepzettsegek, i, newKepzettseg))
})
}
return <>
<div className='row m-2'>
<label className='col-lg-2 col-md-12 col-form-label'>{title}</label>
<div className='col'>
{klist.map((idx) =>
<KepzettsegSelector
key={title+idx}
kepzettsegek={preCalculated[idx].kepzettsegek}
selected={preCalculated[idx].selected}
changeKepzettseg={preCalculated[idx].changeKepzettseg}
/>)
}
</div>
</div>
</>
}
function KarakterKepzettsegek (props: {
numberOfKepzettsegek: number
kepzettsegek: KepzettsegId[],
changeKepzettsegek: (newKepzettsegek: KepzettsegId[]) => void
tolvajKepzettsegek: KepzettsegId[],
changeTolvajKepzettsegek: (newKepzettsegek: KepzettsegId[]) => void
getKepzettsegListaN: (n: number) => Kepzettseg[]
}) {
const { kepzettsegek, numberOfKepzettsegek, getKepzettsegListaN, changeKepzettsegek, tolvajKepzettsegek, changeTolvajKepzettsegek} = props
// 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 <>
<InternalKepzettsegekSelector
title="Képzettségek"
numberOfKepzettseg={numberOfKepzettsegek}
getKepzettsegListaN={getKepzettsegListaN}
changeKepzettsegek={changeKepzettsegek}
kepzettsegek={kepzettsegek} />
{tolvajKepzettsegek.length > 0 && <InternalKepzettsegekSelector
title="Tolvaj képzettségek"
numberOfKepzettseg={4}
getKepzettsegListaN={getTolvajKepzettsegListaN}
changeKepzettsegek={changeTolvajKepzettsegek}
kepzettsegek={tolvajKepzettsegek} />}
</>
}
export default KarakterKepzettsegek
@@ -0,0 +1,32 @@
import React from "react";
import {Kepzettseg, KepzettsegId} from "../domain-models/kepzettsegek2E";
import {TulajdonsagLabel} from "../domain-models/tulajdonsag2E";
import {OsztalyLabel} from "../domain-models/osztaly2E";
function kepzettsegOptionText(k: Kepzettseg) {
const osztalyok = k.Osztalyok?.reduce((acc, o) => acc + ' / ' + OsztalyLabel(o), '')?.substring(3)
return k.Name + ' (' + k.Tulajdonsag.reduce((acc, k) => (acc === '' ? acc : acc + ' / ') + TulajdonsagLabel(k), '') + ')' + (osztalyok == null ? '' : (' - ' + osztalyok));
}
function KepzettsegSelector (props: {kepzettsegek: Kepzettseg[], changeKepzettseg: (newKepzettseg: KepzettsegId) => void, selected : Kepzettseg}) {
const { kepzettsegek, changeKepzettseg, selected } = props
return <>
<div className='row'>
<select className='form-control'
value={selected.Id} onChange={e => changeKepzettseg(e.target.value as KepzettsegId)}>
{kepzettsegek.map(k =>(
<option key={k.Id} value={k.Id}>
{kepzettsegOptionText(k)}
</option>
))}
</select>
</div>
<div className='row pt-2'>
<p className='col'>{selected.Description}</p>
</div>
</>
}
export default KepzettsegSelector
@@ -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,61 @@
import React from "react";
import {OverlayTrigger, Tooltip} from "react-bootstrap";
import {SignedNumberToText} from "../../shared/components/Helpers";
import {MasodlagosErtekekView} from "../domain-models/masodlagos_ertekek";
function MasodlagosErtekek(props: MasodlagosErtekekView) {
const { HP, VO, CelzoTB, Mentok, KozelharciTB } = props
const tooltip = (
<Tooltip id="tooltip">
<strong>Hentelési Potenciál</strong>
</Tooltip>
);
return <>
<div className='row'>
<h5 className='col align-self-center'>Másodlagos értékek</h5>
</div>
<div className='row'>
<div className='col-lg-6'>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label'>Életerő{' '}
<OverlayTrigger placement='top' overlay={tooltip} delay={0} defaultShow={false} flip={false}>
<span>(HP)</span>
</OverlayTrigger>
</label>
<p className='col col-form-label'>{HP}</p>
</div>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label' >Védekezési Osztály (VO)</label>
<p className='col col-form-label'>{VO}</p>
</div>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label' >Közelharci támadás</label>
<p className='col col-form-label'>{KozelharciTB.map(SignedNumberToText).join(" / ")}</p>
</div>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label' >Célzó támadás</label>
<p className='col col-form-label'>{CelzoTB.map(SignedNumberToText).join(" / ")}</p>
</div>
</div>
<div className='col-lg-6'>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label' >Kitartás mentő</label>
<p className='col col-form-label'>{SignedNumberToText(Mentok.kitartas)}</p>
</div>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label' >Reflex mentő</label>
<p className='col col-form-label'>{SignedNumberToText(Mentok.reflex)}</p>
</div>
<div className='row m-2'>
<label className='col-sm-6 col-8 col-form-label' >Akaraterő mentő </label>
<p className='col col-form-label'>{SignedNumberToText(Mentok.akaratero)}</p>
</div>
</div>
</div>
</>
}
export default MasodlagosErtekek
@@ -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,75 @@
import React from "react";
import {
Osztaly2E,
OsztalyDescription,
OsztalyLabel,
OsztalyProperties,
OsztalySpecialSkills
} from "../domain-models/osztaly2E";
import {Faj2E, SzintKorlatokFajokra} from "../domain-models/faj2E";
function OsztalySelector2E(props: {
currentFaj: Faj2E,
currentOsztaly: Osztaly2E,
changeOsztaly: (newOsztaly: Osztaly2E) => void
}) {
const {currentFaj, currentOsztaly, changeOsztaly} = props;
return <>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Osztály</label>
<select className="col form-select" value={currentOsztaly}
onChange={(e) => changeOsztaly(e.target.value as Osztaly2E)}>
<optgroup label='Harcos'>
<option value={Osztaly2E.Harcos}>{OsztalyLabel(Osztaly2E.Harcos)}</option>
{SzintKorlatokFajokra(currentFaj)[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>
<option value={Osztaly2E.Druida}>{OsztalyLabel(Osztaly2E.Druida)}</option>
<option value={Osztaly2E.Vandor}>{OsztalyLabel(Osztaly2E.Vandor)}</option>
</optgroup>
</select>
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 form-label'></label>
<div className='col'>
<p className='row pt-2'>
{OsztalyDescription(currentOsztaly)}
</p>
<ul>
{OsztalyProperties(currentOsztaly).map(((special, i) => (<li key={currentOsztaly + i}>{special}</li>)))}
</ul>
<div className='col'>
{OsztalySpecialSkills(currentOsztaly).map(skill =>
<div key={skill.Id} >
<strong className='d-inline'>
{skill.Name}
</strong>
{' '}
{skill.Description}
{skill.ExtendedDescription && (
<ol>
{skill.ExtendedDescription.map((ext, i) =>
(<li key={skill.Id + '-' + i}>{ext}</li>))}
</ol>
)}
</div>
)}
</div>
</div>
</div>
</>
}
export default OsztalySelector2E;
@@ -0,0 +1,52 @@
import React from "react";
import {Faj2E, FajLabel} from "../domain-models/faj2E";
import {Modifier} from "../domain-models/tulajdonsag2E";
import {SignedNumberToText} from "../../shared/components/Helpers";
function fajiModositoText(faj: Faj2E, szamolas: (f: Faj2E) => number) : string {
const mod = szamolas(faj)
return (mod !== 0) ? SignedNumberToText(mod) + ' (' + FajLabel(faj) + ')' : ''
}
function TulajdonsagInput(props: {
tulajdonsag: string,
currentFaj : Faj2E,
fajiModosito: (faj: Faj2E) => number,
changeValue: (val: number) => void
currentValue: number
tooLowError: string,
tooHighError: string
}) {
const {
tulajdonsag,
changeValue,
currentFaj,
currentValue,
fajiModosito,
tooLowError,
tooHighError,
} = props;
return (<div className='row m-2'>
<label className='col-lg-1 col-sm-2 col-form-label'>{tulajdonsag}</label>
<div className='col-lg-1 col-sm-2 m-2'>
<input className='form-control' maxLength={2} type='number' value={currentValue} onChange={e => changeValue(Number(e.target.value))} />
</div>
<span className='col-sm-2 m-2'>
{fajiModositoText(currentFaj, fajiModosito)}
</span>
<span className='col-sm-2 m-2'>
Összesen: { (currentValue + (fajiModosito(currentFaj))).toString() }
</span>
<span className='col-sm-2 m-2'>
Módosító: { SignedNumberToText(Modifier(currentValue + fajiModosito(currentFaj))) }
</span>
{currentValue < 3 && (
<span className='form-field-error'>{tooLowError}</span>)}
{currentValue > 18 && (
<span className='form-field-error'>{tooHighError}</span>)}
</div>)
}
export default TulajdonsagInput;
@@ -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
@@ -0,0 +1,83 @@
import React from "react";
import {
KarakterTulajdonsagok,
RollAllAbilities,
Tulajdonsag2E,
} from "../domain-models/tulajdonsag2E";
import TulajdonsagInput from "./TulajdonsagInput2E";
import {Faj2E, TulajdonsagModositokFajokra} from "../domain-models/faj2E";
function Tulajdonsagok(props: {
currentFaj: Faj2E,
tulajdonsagok: KarakterTulajdonsagok
changeTulajdonsagok: (tulajdonsagok: KarakterTulajdonsagok) => void
}) {
const {currentFaj, tulajdonsagok, changeTulajdonsagok} = props
return <>
<div className='row'>
<h5 className='col-lg-2 col-sm-4 align-self-center'>Tulajdonságok</h5>
<div className='col-sm-2 m-2'>
<button className='btn btn-dark' type='button'
onClick={() => changeTulajdonsagok(RollAllAbilities())}>Dobás
</button>
</div>
</div>
<TulajdonsagInput
tulajdonsag='Erő'
currentValue={tulajdonsagok.t_ero}
currentFaj={currentFaj}
fajiModosito={TulajdonsagModositokFajokra(Tulajdonsag2E.Ero)}
changeValue={(val: number) => changeTulajdonsagok({...tulajdonsagok, t_ero: val})}
tooLowError='Túl gyenge vagy, nem bírtad felemelni a kezed a jelentkezéshez!'
tooHighError='Szét szakadtak az izmaid!'
/>
<TulajdonsagInput
tulajdonsag='Ügyesség'
currentValue={tulajdonsagok.t_ugy}
currentFaj={currentFaj}
fajiModosito={TulajdonsagModositokFajokra(Tulajdonsag2E.Ugyesseg)}
changeValue={(val: number) => changeTulajdonsagok({...tulajdonsagok, t_ugy: val})}
tooLowError='Orrabuktál jelentkezés helyett!'
tooHighError='Emberfeletti zsonglörködésedre záptojással válaszoltak a helyiek!'
/>
<TulajdonsagInput
tulajdonsag='Egészség'
currentValue={tulajdonsagok.t_egs}
currentFaj={currentFaj}
fajiModosito={TulajdonsagModositokFajokra(Tulajdonsag2E.Egeszseg)}
changeValue={(val: number) => changeTulajdonsagok({...tulajdonsagok, t_egs: val})}
tooLowError='Túl beteg vagy ahhoz, hogy kalandozni menj!'
tooHighError='Kicsattanó egészségedet csak a diambroid állíthatja meg, minő véletlen, hogy pont az arcodba robbant...'
/>
<TulajdonsagInput
tulajdonsag='Intelligencia'
currentValue={tulajdonsagok.t_int}
currentFaj={currentFaj}
fajiModosito={TulajdonsagModositokFajokra(Tulajdonsag2E.Intelligencia)}
changeValue={(val: number) => changeTulajdonsagok({...tulajdonsagok, t_int: val})}
tooLowError='Elfelejtetted, hogy mikor is kéne kalandozni indulni!'
tooHighError='Ilyen tudás és logika birtokában mi szükséged van új információkra? Mindent le tudsz vezetni a már meglévő ismereteidből, és ezt azonnal beláttad.'
/>
<TulajdonsagInput
tulajdonsag='Bölcsesség'
currentValue={tulajdonsagok.t_bol}
currentFaj={currentFaj}
fajiModosito={TulajdonsagModositokFajokra(Tulajdonsag2E.Bolcsesseg)}
changeValue={(val: number) => changeTulajdonsagok({...tulajdonsagok, t_bol: val})}
tooLowError='Nem bírtál ellenállni a kíváncsiságodnak, hogy közelebbről is megvizsgáld a tőr hegyét. Nagyon közelről.'
tooHighError='Hatalmas bölcsességedben beláttad a kalandozás veszélyeit és inkább más tevékenységbe fogtál.'
/>
<TulajdonsagInput
tulajdonsag='Karizma'
currentValue={tulajdonsagok.t_kar}
currentFaj={currentFaj}
fajiModosito={TulajdonsagModositokFajokra(Tulajdonsag2E.Karizma)}
changeValue={(val: number) => changeTulajdonsagok({...tulajdonsagok, t_kar: val})}
tooLowError='Mintha taszítanád az embereket, sose sikerült kalandozó csapatot találnod.'
tooHighError='Az ellenkező nem tagjai nem engedték, hogy útnak indulj.'
/>
</>
}
export default Tulajdonsagok
@@ -0,0 +1,29 @@
import {Fegyver} from "./felszereles";
import fegyverDB from "./fegyver.json";
import {Osztaly2E} from "./osztaly2E";
export function AllowedFegyver(o: Osztaly2E): Fegyver[] {
const fegyverek = fegyverDB.data.map(x => x as Fegyver)
const varazslo = fegyverek.filter(f => ['okol', 'bot', 'bot_vasalt' , 'bunko', 'tor', 'parittya', 'dobotu'].includes(f.Id))
const tolvaj = fegyverek.filter(f => !f.Ketkezes || !f.Masfelkezes || ['ij_rovid', 'szamszerij_konnyu', 'szamszerij_nehez' ].includes(f.Id))
const dalnok = fegyverek.filter(f => !f.Ketkezes || !f.Masfelkezes || ['okol', 'ij_rovid', 'szamszerij_konnyu' ].includes(f.Id))
const druida = fegyverek.filter(f => ['okol', 'bot', 'bunko', 'tor', 'dobotu', 'kard_szablya', 'sarlo', 'landzsa', 'parittya'].includes(f.Id))
const nemHarcos = fegyverek.filter(f => !f.Harcos)
const harcos = fegyverek.filter(f => !f.CsakNemHarcos)
switch (o) {
case Osztaly2E.Harcos:
case Osztaly2E.Amazon:
case Osztaly2E.Barbar:
case Osztaly2E.Ijasz:
case Osztaly2E.Tengeresz:
case Osztaly2E.Vandor:
return harcos;
// Note: Isteni fegyver barmi lehet istentol foggoen
case Osztaly2E.Pap: return nemHarcos;
case Osztaly2E.Tolvaj: return tolvaj;
case Osztaly2E.Illuzionista:
case Osztaly2E.Varazslo: return varazslo;
case Osztaly2E.Dalnok: return dalnok;
case Osztaly2E.Druida: return druida;
}
}
@@ -0,0 +1,50 @@
import {Pajzs, Pancel} from "./felszereles";
import {Osztaly2E} from "./osztaly2E";
import pancelDB from "./pancel.json";
import pajzsDB from "./pajzs.json"
export function AllowedPajzsTypes(osztalyok: Osztaly2E[]): Pajzs[] {
return [...new Set(osztalyok.flatMap(osztalyAllowedPajzs))]
}
function osztalyAllowedPajzs(o: Osztaly2E): Pajzs[] {
const pajzsok = pajzsDB.pajzs.map(x => x as Pajzs)
switch (o) {
case Osztaly2E.Harcos: return pajzsok;
case Osztaly2E.Amazon: return pajzsok;
case Osztaly2E.Barbar: return pajzsok;
case Osztaly2E.Tengeresz: return pajzsok;
case Osztaly2E.Ijasz: return pajzsok;
case Osztaly2E.Tolvaj: return [];
case Osztaly2E.Pap: return pajzsok;
case Osztaly2E.Illuzionista: return [];
case Osztaly2E.Varazslo: return [];
case Osztaly2E.Dalnok: return pajzsok;
case Osztaly2E.Druida: return pajzsok.filter(f => f.Id === "pajzs_fa");
case Osztaly2E.Vandor: return pajzsok;
}
}
export function AllowedPancelTypes(osztalyok: Osztaly2E[]): Pancel[] {
return [...new Set(osztalyok.flatMap(osztalyAllowedPancel))]
}
function osztalyAllowedPancel(o: Osztaly2E): Pancel[] {
const pancelok = pancelDB.pancel.map(x => x as Pancel)
const konnyu = pancelok.filter(x => x.Type === "konnyu")
switch (o) {
case Osztaly2E.Harcos: return pancelok;
case Osztaly2E.Amazon: return konnyu;
case Osztaly2E.Barbar: return pancelok;
case Osztaly2E.Ijasz: return konnyu;
case Osztaly2E.Tengeresz: return konnyu;
case Osztaly2E.Pap: return pancelok;
case Osztaly2E.Illuzionista: return [];
case Osztaly2E.Varazslo: return [];
case Osztaly2E.Tolvaj: return konnyu;
case Osztaly2E.Dalnok: return konnyu;
case Osztaly2E.Druida: return pancelok.filter(f => ['csuha', 'bor', 'kivert_bor', 'prem'].includes(f.Id));
case Osztaly2E.Vandor: return konnyu;
}
}
@@ -0,0 +1,337 @@
import {KarakterTulajdonsagok, Tulajdonsag2E} from "./tulajdonsag2E";
export enum Faj2E {
Ember = 'f_2e_ember',
Amazon = 'f_2e_amazon',
Birodalmi = 'f_2e_birodalmi',
Nomad = 'f_2e_nomad',
Eszaki = 'f_2e_eszaki',
Osember = 'f_2e_osember',
Elf = 'f_2e_elf',
Felelf = 'f_2e_felelf',
Felork = 'f_2e_felork',
Felszerzet = 'f_2e_felszerzet',
Gnom = 'f_2e_gnom',
Torpe = 'f_2e_torpe',
}
export function FajLabel(faj: Faj2E) : string {
switch (faj) {
case Faj2E.Ember: return 'Ember';
case Faj2E.Amazon: return 'Amazon';
case Faj2E.Birodalmi: return 'Birodalmi';
case Faj2E.Nomad: return 'Nomád';
case Faj2E.Eszaki: return 'Északi';
case Faj2E.Osember: return 'Ősember';
case Faj2E.Elf: return 'Elf';
case Faj2E.Felelf: return 'Félelf';
case Faj2E.Felork: return 'Félork';
case Faj2E.Felszerzet: return 'Félszerzet';
case Faj2E.Gnom: return 'Gnóm';
case Faj2E.Torpe: return 'Törpe';
}
}
export function FajDescription(faj: Faj2E) : string {
switch (faj) {
case Faj2E.Ember: return "Az emberi népek sokfélesége és szaporasága, valamint a bennük élő felfedezői hajlam és erkölcsi rugalmasság hatására képviselőik majdnem minden földön megtalálhatók. A legtöbb ember a lenti vonásokkal rendelkezik.";
case Faj2E.Amazon: return "Bár az amazonok barbár népe igen kis létszámú, harciasságuk miatt mégis félik őket. Az amazonok nyúlánk termetűek, hajuk általában vöröses vagy világosbarna, bőrük világos. Társadalmukban teljes a nőuralom; a férfiakat rabszolgaságban tartják és megtiltják számukra a fegyverek viselését. Képzett tengeri rablók, de a szárazföldön is megállják a helyüket. Az amazon nők kivétel nélkül értenek a harchoz és soha, soha nem hagyják megtorlatlanul az őket ért sérelmeket. Technikailag fejletlenek. Kis erejű passzív pszionikus (elmebeli) képességeik vannak.";
case Faj2E.Birodalmi: return "Dekadens, hanyatló birodalmak előkelő lakói. Életüket többnyire dekadens élvezetek habzsolásával, kábítószeres mámorban töltik; soraikban a beltenyészet és a degeneráció miatt gyakoriak az örökletes betegségek. Városaik számtalan titkot és mesés kincseket rejtenek.";
case Faj2E.Eszaki: return "Az északi kultúra lételeme a harc és a hajózás. Még nőik is kiválóan forgatják a harci fejszét vagy a hosszú nyelű bárdot, s bár rabszolgatartók kegyetlenségnek tartják rabszolgáikat a legelemibb védekező eszközöktől, mint a késtől vagy a rövidkardtól megfosztani, vagy fegyvertelenekre támadni. Ettől eltekintve vérszomjasak, és általában semleges vagy gonosz jelleműek. Testalkatuk magas és erős, hajuk vöröses, szőke vagy világosbarna. A férfiak szakállat vagy bajuszt, a nők hosszú hajat viselnek. A déli vidékeken civilizálatlanságuk és faragatlanságuk miatt gyakran megvetik őket.";
case Faj2E.Nomad: return "A lovas nomádok kultúrája mindmáig megőrizte primitívségét. Az alkalomnak megfelelően állattartásból, lovas portyázásból, kereskedésből vagy ezek kombinációjából élnek. Eszközeik is egyszerűek: fegyvereik főleg hosszú lándzsák és íjak, és bár kedvelik a kardokat és láncvérteket, nem jeleskednek azok előállításában (a vas megmunkálását kovácsaik féltett titokként őrzik). Többnyire semleges jelleműek, de otthonuktól elszakadva mindenhol feltalálják magukat, és hajlamosak a gonoszra. A nomádok középtermetűek, bőrük cserzett, hajuk olajos fekete, amit leggyakrabban copfban fognak össze. A férfiak kedvelik a bajusz viseletét." ;
case Faj2E.Osember: return "Vad földek primitív őslakói. Máig megőrizték görnyedt tartásukat, szőrös küllemüket és loboncos fekete hajukat. A fémmegmunkálást még sokan nem ismerik, s kőeszközöket esetleg rezet használnak. Írásuk egyáltalán nincs. Félistenekként tisztelik hátasaikat, a hatalmas mamutokat. Sokan értelem nélküli vagy gyermeki vadaknak hiszik őket, nagy kárukra: az ősemberekben gyakran lakozik bestiális ravaszság, és a civilizált ember eszközeihez jutva rendkívül veszedelmesek.";
case Faj2E.Elf: return "Az elfek szépségükről, 150-200 éves élettartamukról és fejlett civilizációjukról ismertek, amely ötvözi a városi és természeti élet előnyeit. Termetük nyúlánk, vonásaik az embernél finomabbak, de egyben törékenyebbek. Kiváló íjászok és varázslók. Általában visszahúzódók, csak ritkán lépnek kapcsolatba a rövidebb életű népekkel legjobban az emberekkel és a félszerzetekkel jönnek ki. Jellemük általában kaotikus jó.";
case Faj2E.Felelf: return "A félelfek elf és ember szülők gyermekei, akik nem tartoznak egyik közösséghez sem jellemvonásaik általában az elfekhez húznak, de élettartamuk emberi. Kis számuk miatt nem alkotnak külön nemzetségeket, többségük magányos utazó.";
case Faj2E.Felork: return "A félorkok többsége külsejében inkább rút emberre hasonlít (bár termetük gyakran görnyedt, arcvonásaik pedig disznószerűek), belsőleg azonban számos ork vonással rendelkeznek: számítók, magányosak és bizalmatlanok a szebb fajokkal, de legtöbbször fajtársaikkal is. Különösen sok közöttük az orgyilkos és a közönséges gonosztevő; egyikük-másikuk ezt némi mágikus erővel vegyíti. Nem alkotnak különálló közösségeket, életük általában rövid és erőszakos. A legtöbb félork gonosz jellemű.";
case Faj2E.Felszerzet: return "A félszerzetek általában megelégszenek a nyugodt vidéki élettel. Termetük az emberi fele, élettartamuk a másfélszerese. Hajlamosak az elhízásra, s lábukat vastag szőr borítja. Általában mezítláb járnak. Mivel lusták és kényelemszeretők, csak ritkán adják fejüket kalandokra ekkor azonban kiváló tolvajokká válhatnak.";
case Faj2E.Gnom: return "A gnómok különösen ritka, elsősorban az embernéppel együtt élő Faj2E. A gnómokat alacsony termetükről, vékony végtagjaikról és tömpe, húsos orrukról lehet fölismerni. Élettartamuk az emberi másfélszerese. A gnómok jó kézművesek és van érzékük a mágiához is elsősorban az illuzionisták trükkjeihez vonzódnak. A legtöbb gnóm semleges jó vagy semleges jellemű.";
case Faj2E.Torpe: return "A törpék elsősorban hegyek között és dombvidékeken élnek. Termetük kicsi, de testük tömzsi és igen szívós. Bozontos szakállat viselnek, amely a férfiak esetén dús és sörteszerű, a nőknél finomabb és ritkásabb. A törpék kiemelkedőek a mindenféle mesterségekben, különösen a fémmunkákban, de harcban is megállják helyüket. Nagy nemzetségekbe szerveződnek és híresek igazságérzetükről, makacsságukról és kapzsiságukról. A legtöbb törpe törvényes jó vagy törvényes semleges jellemű..";
}
}
export function FajSpecials(faj : Faj2E) : string[] {
switch (faj) {
case Faj2E.Ember: return [
"A játék kezdetén egy extra képzettséggel kezdenek",
"Korlátlanul fejlődhetnek minden osztályban",
];
case Faj2E.Amazon: return [
"+1-et kapnak kezdeményezésükre",
"Ha két vagy több amazon együttesen harcol, VO-juk 2-vel nő pszionikus képességeik miatt",
"Szintkorlátok: Harcos 9, Pap 5, Tolvaj 3, Varázsló 3",
];
case Faj2E.Birodalmi: return [
"+1 Intelligencia, -1 Egészség",
"Egyik kezdő képzettségük mindenképpen az Alkímia, a Méregkeverés vagy az Okkultizmus.",
"Szintkorlátok: Harcos 7, Pap 5, Tolvaj 5, Varázsló 9",
];
case Faj2E.Eszaki: return [
"+1 Erő, -1 Bölcsesség",
"Az egyik kezdő képzettségük mindenképpen a Hajózás",
"-1-et kapnak minden Karizmapróbájukra (népük tagjai között is)",
"Szintkorlátok: Harcos 9, Pap 7, Tolvaj 7, Varázsló 3",
];
case Faj2E.Nomad: return [
"+1 Egészség, -1 Bölcsesség",
"Az egyik kezdő képzettségük mindenképpen a Lovaglás",
"Szintkorlátok: Harcos 9, Pap 5, Tolvaj 7, Varázsló 3",
];
case Faj2E.Osember: return [
"+1 Erő, +1 Egészség, -1 Intelligencia, -1 Bölcsesség",
"Szintkorlátok: Harcos -, Pap 5, Tolvaj 4, Varázsló 0",
];
case Faj2E.Elf: return [
"+1 Ügyesség, -1 Egészség",
"Immunisak az altató mágiára, +2 mentődobás bűbáj ellen",
"Képzettek a hosszúíjak és a hosszúkard használatában akkor is, ha osztályuk ezt nem adná meg nekik",
"Az egyik képzettségük mindenképpen a Vadonjárás",
"Nem támaszthatók föl",
"Szintkorlátok: Harcos 7, Pap 3, Tolvaj 5, Varázsló 7",
];
case Faj2E.Felelf: return [
"Képzettek a hosszúíjak és a hosszúkard használatában akkor is, ha osztályuk ezt nem adná meg nekik",
"Az egyik képzettségük mindenképpen a Vadonjárás",
"Szintkorlátok: Harcos 9, Pap 5, Tolvaj 9, Varázsló 9",
];
case Faj2E.Felork: return [
"+1 Erő, +1 Egészség, -2 Karizma",
"A fenti levonáson túl -1-et kapnak minden Karizmapróbájukra (más félorkokkal szemben is)",
"Szintkorlátok: Harcos 7, Pap 5, Tolvaj 7, Varázsló 5",
];
case Faj2E.Felszerzet: return [
"+1 Ügyesség, -1 Erő",
"+1 VO-t kapnak kis méretük miatt",
"+1 támadást kapnak dobófegyverekre és parittyákra",
"Kétkezes közelharci fegyvereket -4 levonással, másfélkezes fegyvereket két kézzel forgathatnak",
"Kétszer annyi élelmet kell enniük, mint másoknak",
"Szintkorlátok: Harcos 5, Pap 3, Tolvaj 7, Varázsló 3",
];
case Faj2E.Gnom: return [
"+1 Intelligencia, -1 Erő",
"+1 VO-t kapnak kis méretük miatt",
"+1-et kapnak a mágia elleni mentődobásaikra",
"Kétkezes közelharci fegyvereket -4 levonással, másfélkezes fegyvereket két kézzel forgathatnak",
"Szintkorlátok: Harcos 3, Pap 3, Tolvaj 5, Varázsló 7",
];
case Faj2E.Torpe: return [
"+1 Egészség, -1 Karizma",
"Lassabban mozognak az embereknél, kezdeményezésükre -2-t kapnak",
"+2 mentődobást kapnak mérgek, betegségek és varázslatok hatásai ellen",
"Az egyik képzettségük mindenképpen az Értékbecslés",
"Szintkorlátok: Harcos 7, Pap 5, Tolvaj 5, Varázsló 3",
];
}
}
export type Szintkorlatok = Record<string, number> // TODO!! use Osztaly enum, rule fix!
export interface FajDetails {
ID: Faj2E,
Label: string,
Description: string,
Specials: string[],
TulajdonsagModifiers: KarakterTulajdonsagok
Szintkorlatok: Szintkorlatok
}
export function GetFajDetails(faj: Faj2E): FajDetails {
return {
ID: faj,
Label: FajLabel(faj),
Description: FajDescription(faj),
Specials: FajSpecials(faj),
TulajdonsagModifiers: fajiTulajdonsagModositok(faj),
Szintkorlatok: SzintKorlatokFajokra(faj),
}
}
// noinspection OverlyComplexFunctionJS,FunctionTooLongJS
function tulajdonsagModositokPerFaj(tul: Tulajdonsag2E, faj: Faj2E) : number {
switch (faj) {
case Faj2E.Birodalmi: switch (tul) {
case Tulajdonsag2E.Egeszseg: return -1;
case Tulajdonsag2E.Intelligencia: return +1;
default: return 0;
}
case Faj2E.Eszaki: switch (tul) {
case Tulajdonsag2E.Ero: return +1;
case Tulajdonsag2E.Bolcsesseg: return -1;
default: return 0;
}
case Faj2E.Nomad: switch (tul) {
case Tulajdonsag2E.Egeszseg: return +1;
case Tulajdonsag2E.Bolcsesseg: return -1;
default: return 0
}
case Faj2E.Osember: switch (tul) {
case Tulajdonsag2E.Ero: return +1;
case Tulajdonsag2E.Egeszseg: return +1;
case Tulajdonsag2E.Intelligencia: return -1;
case Tulajdonsag2E.Bolcsesseg: return -1;
default: return 0
}
case Faj2E.Elf: switch (tul) {
case Tulajdonsag2E.Egeszseg: return -1;
case Tulajdonsag2E.Ugyesseg: return +1;
default: return 0;
}
case Faj2E.Felork: switch (tul) {
case Tulajdonsag2E.Ero: return +1;
case Tulajdonsag2E.Egeszseg: return +1;
case Tulajdonsag2E.Karizma: return -2;
default: return 0;
}
case Faj2E.Felszerzet: switch (tul) {
case Tulajdonsag2E.Ero: return -1;
case Tulajdonsag2E.Ugyesseg: return +1;
default: return 0;
}
case Faj2E.Gnom: switch (tul) {
case Tulajdonsag2E.Ero: return -1;
case Tulajdonsag2E.Intelligencia: return +1;
default: return 0;
}
case Faj2E.Torpe: switch (tul) {
case Tulajdonsag2E.Egeszseg: return +1;
case Tulajdonsag2E.Karizma: return -1;
default: return 0;
}
default: return 0;
}
}
export function TulajdonsagModositokFajokra(tul: Tulajdonsag2E) {
return (faj: Faj2E) => tulajdonsagModositokPerFaj(tul, faj);
}
export function TulajdonsagokFajjal(tulajdonsagok: KarakterTulajdonsagok, faj: Faj2E): KarakterTulajdonsagok {
return {
[Tulajdonsag2E.Ero]: tulajdonsagok.t_ero + tulajdonsagModositokPerFaj(Tulajdonsag2E.Ero, faj),
[Tulajdonsag2E.Ugyesseg]: tulajdonsagok.t_ugy + tulajdonsagModositokPerFaj(Tulajdonsag2E.Ugyesseg, faj),
[Tulajdonsag2E.Egeszseg]: tulajdonsagok.t_egs + tulajdonsagModositokPerFaj(Tulajdonsag2E.Egeszseg, faj),
[Tulajdonsag2E.Intelligencia]: tulajdonsagok.t_int + tulajdonsagModositokPerFaj(Tulajdonsag2E.Intelligencia, faj),
[Tulajdonsag2E.Bolcsesseg]: tulajdonsagok.t_bol + tulajdonsagModositokPerFaj(Tulajdonsag2E.Bolcsesseg, faj),
[Tulajdonsag2E.Karizma]: tulajdonsagok.t_kar + tulajdonsagModositokPerFaj(Tulajdonsag2E.Karizma, faj),
}
}
function fajiTulajdonsagModositok(faj: Faj2E): KarakterTulajdonsagok {
return {
[Tulajdonsag2E.Ero]: TulajdonsagModositokFajokra(Tulajdonsag2E.Ero)(faj),
[Tulajdonsag2E.Ugyesseg]: TulajdonsagModositokFajokra(Tulajdonsag2E.Ugyesseg)(faj),
[Tulajdonsag2E.Egeszseg]: TulajdonsagModositokFajokra(Tulajdonsag2E.Egeszseg)(faj),
[Tulajdonsag2E.Intelligencia]: TulajdonsagModositokFajokra(Tulajdonsag2E.Intelligencia)(faj),
[Tulajdonsag2E.Bolcsesseg]: TulajdonsagModositokFajokra(Tulajdonsag2E.Bolcsesseg)(faj),
[Tulajdonsag2E.Karizma]: TulajdonsagModositokFajokra(Tulajdonsag2E.Karizma)(faj),
}
}
const EmberiSzintKorlatok: Szintkorlatok = {
o_amazon: 0,
o_barbar: 12,
o_harcos: 12,
o_ijasz: 12,
o_illuzionista: 12,
o_kaloz: 12,
o_pap: 12,
o_tolvaj: 12,
o_varazslo: 12
}
// noinspection OverlyComplexFunctionJS,FunctionTooLongJS
export function SzintKorlatokFajokra(faj: Faj2E) : Szintkorlatok {
let szintkorlatok = {...EmberiSzintKorlatok}
switch (faj) {
case Faj2E.Ember: break;
case Faj2E.Amazon:
szintkorlatok.o_pap = 5
szintkorlatok.o_tolvaj = 0
szintkorlatok.o_illuzionista = 0
szintkorlatok.o_varazslo = 0
szintkorlatok.o_amazon = 20
break
case Faj2E.Birodalmi:
szintkorlatok.o_harcos = 7
szintkorlatok.o_barbar = 7
szintkorlatok.o_ijasz = 7
szintkorlatok.o_kaloz = 7
szintkorlatok.o_pap = 5
szintkorlatok.o_tolvaj = 5
break
case Faj2E.Eszaki:
case Faj2E.Nomad:
szintkorlatok.o_pap = 7
szintkorlatok.o_illuzionista = 5
szintkorlatok.o_varazslo = 5
break
case Faj2E.Osember:
szintkorlatok.o_pap = 7
szintkorlatok.o_tolvaj = 4
szintkorlatok.o_illuzionista = 0
szintkorlatok.o_varazslo = 0
break
case Faj2E.Elf:
szintkorlatok.o_pap = 7
szintkorlatok.o_tolvaj = 6
szintkorlatok.o_illuzionista = 9
szintkorlatok.o_varazslo = 9
szintkorlatok.o_harcos = 9
szintkorlatok.o_barbar = 9
szintkorlatok.o_ijasz = 9
szintkorlatok.o_kaloz = 9
break
case Faj2E.Felelf:
szintkorlatok.o_pap = 5
szintkorlatok.o_tolvaj = 9
szintkorlatok.o_illuzionista = 9
szintkorlatok.o_varazslo = 9
szintkorlatok.o_harcos = 9
szintkorlatok.o_barbar = 9
szintkorlatok.o_ijasz = 9
szintkorlatok.o_kaloz = 9
break
case Faj2E.Felork:
szintkorlatok.o_pap = 5
szintkorlatok.o_tolvaj = 9
szintkorlatok.o_illuzionista = 5
szintkorlatok.o_varazslo = 5
szintkorlatok.o_harcos = 9
szintkorlatok.o_barbar = 9
szintkorlatok.o_ijasz = 9
szintkorlatok.o_kaloz = 9
break
case Faj2E.Felszerzet:
szintkorlatok.o_pap = 6
szintkorlatok.o_tolvaj = 9
szintkorlatok.o_illuzionista = 4
szintkorlatok.o_varazslo = 4
szintkorlatok.o_harcos = 6
szintkorlatok.o_barbar = 6
szintkorlatok.o_ijasz = 6
szintkorlatok.o_kaloz = 6
break
case Faj2E.Gnom:
szintkorlatok.o_pap = 6
szintkorlatok.o_tolvaj = 8
szintkorlatok.o_illuzionista = 7
szintkorlatok.o_varazslo = 7
szintkorlatok.o_harcos = 6
szintkorlatok.o_barbar = 6
szintkorlatok.o_ijasz = 6
szintkorlatok.o_kaloz = 6
break
case Faj2E.Torpe:
szintkorlatok.o_pap = 9
szintkorlatok.o_tolvaj = 6
szintkorlatok.o_illuzionista = 4
szintkorlatok.o_varazslo = 4
szintkorlatok.o_harcos = 9
szintkorlatok.o_barbar = 9
szintkorlatok.o_ijasz = 9
szintkorlatok.o_kaloz = 9
break
}
return szintkorlatok
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,115 @@
import pancelok from './pancel.json'
import pajzsok from './pajzs.json'
import fegyverek from './fegyver.json'
export type PancelType = 'konnyu' | 'nehez'
export const PancelTypes: PancelType[] = ['konnyu', 'nehez']
export interface Pancel {
Id: string,
Name: string,
Type: PancelType
VO: number,
Price: number,
Size: number
}
export interface Pajzs {
Id: string,
Name: string,
Type: 'pajzs'
VO: number,
Price: number,
Size: number
}
export interface Fegyver {
Id: string,
Name: string,
Type: 'kozelharci' | 'dobhato' | 'lofegyver',
Harcos: boolean,
CsakNemHarcos: boolean,
Masfelkezes: boolean,
Ketkezes: boolean,
KetkezesBonusz: boolean,
HarciManover: boolean,
Alakzat: boolean,
Hosszu: boolean,
Kabito: boolean,
LovasRoham: boolean,
PancelToro: boolean,
PajzsZuzo: boolean,
Rohamtoro: boolean,
EroBonusz: boolean,
Megterheltseg: boolean,
Megjegyzes: string | undefined | null,
Price: number,
DamageMultiplier: number,
DamageDice: number,
NumberOfDamageDice: number,
DamageBonus: number,
CritRangeStart: number,
CritMultiplier: number,
Range: number,
Size: number
}
export interface KarakterFelszereles {
pancelID?: string
pajzsID?: string
fegyverIDk: string[]
}
export const DefaultFelszereles: KarakterFelszereles = {
fegyverIDk: [],
}
export function PancelTypeLabel(type: PancelType): string {
let result: string
switch(type) {
case 'konnyu':
result = 'Könnyű'
break
case 'nehez':
result = 'Nehéz'
break
default:
result = 'Egyéb páncél'
}
return result
}
export function GetPancel(id?: string): Pancel | undefined {
if (id == null) {
return undefined
}
const filteredPancel = pancelok['pancel'].filter(p => p.Id === id) as Pancel[]
return filteredPancel.length > 0 ? filteredPancel[0] : undefined
}
export function MaxAbilityVO(p: PancelType) {
switch(p){
case 'konnyu': return 9999;
case 'nehez': return 1;
default: return 9999;
}
}
export function GetPajzs(id?: string): Pajzs | undefined {
if (id == null) {
return undefined
}
const filtered = pajzsok['pajzs'].filter(p => p.Id === id) as Pajzs[]
return filtered.length > 0 ? filtered[0] : undefined
}
export function GetFegyver(id: string): Fegyver | undefined {
return fegyverek.data.find(f => f.Id === id) as Fegyver | undefined
}
export function GetFegyverek(ids: string[]): Fegyver[] {
return ids.map(GetFegyver).filter(x => x != null).map(x => x as Fegyver)
}
export const defaultFegyverID = 'okol'
export const defaultFegyver = GetFegyver('okol')!
@@ -0,0 +1,59 @@
export type JellemID = 'TJ' | 'SJ' | 'KJ' | 'TS' | 'S' | 'KS' | 'TG' | 'SG' | 'KG'
export const AllJellemIDs: JellemID[] = ['TJ', 'SJ', 'KJ', 'TS', 'S', 'KS', 'TG', 'SG', 'KG']
export interface Jellem {
ID: JellemID
Label: string
Description: string
}
export function GetJellem(id: JellemID): Jellem {
switch (id) {
case "TJ": return {
ID: "TJ",
Label: "Törvényes Jó",
Description: "A törvényes jó karakterek betartják és fontosnak tartják az igazságos törvényeket, valamint a kiérdemelt tekintély tiszteletét. Viselkedésükben lehetőleg egyenesek, és nem okoznak szükségtelen károkat másnak; továbbá segítenek a kevésbé szerencséseken.",
}
case "SJ": return {
ID: "SJ",
Label: "Semleges Jó",
Description: "A semleges jó karakterek általában jóindulatúak, de nem jellemző rájuk sem a törvényes jók szigora, sem a kaotikus jók kiszámíthatatlansága.",
}
case "KJ": return {
ID: "KJ",
Label: "Kaotikus Jó",
Description: "A kaotikus jó karakterek nagy súlyt fektetnek az egyéni szabadságra és a jótéteményekre akkor is, ha ezek a nézetek és tettek ellentétesek a társadalmi szokásokkal és írott törvényekkel.",
}
case "TS": return {
ID: "TS",
Label: "Törvényes Semleges",
Description: "A törvényes semleges karakterek szigorúan betartják a törvényeket és adott szavukat, de ez a törvénytisztelet nem altruizmusból, hanem elsősorban önzésből, közömbösségből vagy a törvények öncélú tiszteletéből fakad.",
}
case "S": return {
ID: "S",
Label: "Semleges",
Description: "A semleges karakterek vagy nem törődnek a fontos erkölcsi kérdésekkel azaz nem kötelezik el magukat egyes ügyek mellett vagy éppen ellenkezőleg, aktívan a különféle nézetek közötti egyensúlyra és harmóniára törekednek. Az átlagemberek többsége ehhez a jellemhez tartozik.",
}
case "KS": return {
ID: "KS",
Label: "Kaotikus Semleges",
Description: "A kaotikus semleges karakterek kiszámíthatatlanok, saját boldogulásukra törekednek akár mások ellenében is. A kalandozók igen jelentős része kaotikus semleges jellemű"
}
case "TG": return {
ID: "TG",
Label: "Törvényes Gonosz",
Description: "A törvényes gonosz karakterek lételeme a törvényadta lehetőségek kihasználása mások kisemmizésére és önmaguk gyarapítására. Nem csoda, hogy számos zsarnok és kiskirály e jellem képviselője.",
}
case "SG": return {
ID: "SG",
Label: "Semleges Gonosz",
Description: "A semleges gonosz karakterek döntően aljasak és rosszindulatúak, s céljaikat hol a törvények kijátszásával, hol a rájuk való hivatkozással érik el."
}
case "KG": return {
ID: "KG",
Label: "Kaotikus Gonosz",
Description: "A kaotikus gonosz karakterek kíméletlenül, mindenen és mindenkin átgázolva hajszolják a hatalmat vagy a vagyont, s ennek érdekében bármilyen gaztettre képesek."
}
}
}
@@ -0,0 +1,132 @@
import {KarakterTulajdonsagok, TulajdonsagDefaults} from "./tulajdonsag2E";
import {Faj2E} from "./faj2E";
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 {
nev: string,
nem?: string,
kor: number,
isten?: string,
jellem: JellemID,
tulajdonsagok: KarakterTulajdonsagok
faj: Faj2E
kepzettsegek: KepzettsegId[]
tolvajKepzettsegek?: KepzettsegId[]
szint: number,
felszereles: KarakterFelszereles,
szintlepesek: Szintlepes[]
}
export const DefaultKarakter: Karakter2E = {
nev: 'Névtelen Kalandozó',
kor: 20,
jellem: 'S',
tulajdonsagok: TulajdonsagDefaults,
faj: Faj2E.Ember,
kepzettsegek: [],
tolvajKepzettsegek: [],
szint: 1,
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], HProll: BaseHP(osztaly) , osztaly}, ...karakter.szintlepesek.slice(1)]
}
}
export function ChangeOsztalyAtSzint(karakter: Karakter2E, osztaly: Osztaly2E, szint: number): Karakter2E {
return {
...karakter,
szintlepesek: setOsztalyForSzintLepes(karakter.szintlepesek, osztaly, szint)
}
}
@@ -0,0 +1,344 @@
import {Osztaly2E} from "./osztaly2E";
import {Faj2E} from "./faj2E";
import {Karakter2E} from "./karakter2E";
import {Modifier, Tulajdonsag2E} 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'
export interface Kepzettseg {
Id: KepzettsegId,
Name: string,
Description: string,
Tulajdonsag: Tulajdonsag2E[],
Osztalyok?: Osztaly2E[],
}
export const KepzettsegLista: Kepzettseg[] = [
{
Id: 'k_alkimia',
Name: 'Alkímia',
Description: 'Különféle különleges anyagok, pl. savak, görögtűz,és különböző varázskomponensek kikeverésére szolgál. Használatához gyakran jól felszerelt laboratórium szükséges. A képzettséggel a karakter képes beazonosítani a különféle vegyi anyagokat és mérgeket, Nehéz próbával a varázsitalokat is',
Tulajdonsag: [Tulajdonsag2E.Intelligencia],
Osztalyok: [Osztaly2E.Druida, Osztaly2E.Illuzionista, Osztaly2E.Pap, Osztaly2E.Varazslo, Osztaly2E.Vandor]
},
{
Id: 'k_alcazas',
Name: 'Álcázás',
Description: 'Álöltözetek készítése és használata. Próbadobást akkor kell dobni, ha az álca hitelességét valaki kétségbe vonhatja (általában ellenpróbaként; az eredményt gyakran csak a mesélő ismerheti)',
Tulajdonsag: [Tulajdonsag2E.Karizma],
Osztalyok: [Osztaly2E.Tolvaj],
},
{
Id: 'k_allatidomitas',
Name: 'Állatidomítás',
Description: 'Állatok megszelídítésére és betanítására szolgál. A járatos karakter próbát tehet arra is, hogy a különféle vadállatok ne tekintsék ellenségüknek vagy prédájuknak',
Tulajdonsag: [Tulajdonsag2E.Karizma],
},
{
Id: 'k_csapdak',
Name: 'Csapdák',
Description: 'A karakter a rendelkezésre álló idő és alapanyagok függvényében szimpla vagy komplex csapdákat készíthet. A képzettség arra is jó, hogy hatástalanítsa, tönkretegye, vagy elkerülje ezeket a szerkezeteket. Ha egy csapda eltávolítására tett próba sikertelen, az kisülhet és veszélybe sodorhatja az óvatlan próbálkozót.',
Tulajdonsag: [Tulajdonsag2E.Ugyesseg],
Osztalyok: [Osztaly2E.Tolvaj],
},
{
Id: 'k_csillagjoslas',
Name: 'Csillagjóslás',
Description: 'A karakter a képzettséggel egyrészt képes meghatározni a csapat hozzávetőleges pozícióját a térképen (hexek vagy tájegység szerint), másrészt egyszerűbb jóslatokat készíthet. A jóslat sikertelen próbánál értelmezhetetlen, Közepes nehézségnél homályos, Nehéz próbánál többértelmű, Hősies próbánál pedig világos és jól értelmezhető. Egyetlen jóslat sem tárja fel a pontos jövőt, csak segítséget vagy jelet ad annak értelmezéséhez. A képzettséget csak tiszta, csillagos égbolt alatt vagy ritka asztrológiai térképek birtokában lehet használni (ezek ára 2000 at vagy több, és így is -2 jár a próbára).',
Tulajdonsag: [Tulajdonsag2E.Bolcsesseg],
Osztalyok: [Osztaly2E.Druida, Osztaly2E.Illuzionista, Osztaly2E.Pap, Osztaly2E.Varazslo, Osztaly2E.Vandor]
},
{
Id: 'k_egyensulyozas',
Name: 'Egyensúlyozás',
Description: 'Omlatag talajon, kötélen vagy keskeny párkányon való átjutáshoz szükséges képzettség. Ha a próba sikertelen, a karakter lezuhan.',
Tulajdonsag: [Tulajdonsag2E.Ugyesseg],
Osztalyok: [Osztaly2E.Tolvaj],
},
{
Id: 'k_eloadas',
Name: 'Előadás',
Description: 'A képzettséggel rendelkező karakter járatos a hangszerek használatában, éneklésben, eposzmondásban, stb.',
Tulajdonsag: [Tulajdonsag2E.Karizma],
},
{
Id: 'k_ertekbecsles',
Name: 'Értékbecslés',
Description: 'Sikeres próba esetén a karakter hozzávetőlegesen felméri egy tárgy pénzbeli értékét, és Nehéz vagy jobb próbán kiszúrja a hamisítványokat.',
Tulajdonsag: [Tulajdonsag2E.Intelligencia],
},
{
Id: 'k_gyogyitas',
Name: 'Gyógyítás',
Description: 'A sebek bekötözésének, mérgek és betegségek semlegesítésének vagy enyhítésének tudománya. Harc vagy más sérülés után alkalmazva a bekötözött karakter siker esetén visszanyer 1d4 elvesztett Hp-t, Nehéz próba megdobása esetén 1d4+2-t, Hősies esetén pedig 1d4+4-et. 1-es dobáson 1d4 pontot veszít. A képzettség hatékony alkalmazásához egy adag gyógyfű vagy egy orvosló készlet szükséges; nélkülük legfeljebb 1d4 Hp adható vissza.',
Tulajdonsag: [Tulajdonsag2E.Bolcsesseg],
},
{
Id: 'k_hajozas',
Name: 'Hajózás',
Description: 'A karakter ismeri a hajózás fogásait, tehát elkormányoz kisebb vitorlásokat vagy gályákat, tudja hogyan kell elkerülni a zátonyokat és épen kikerülni egy viharból, képes javításokat végezni stb.',
Tulajdonsag: [Tulajdonsag2E.Ugyesseg],
},
{
Id: 'k_hallgatozas',
Name: 'Hallgatózás',
Description: 'Nehezen hallható zajok, beszéd meghallása és kihallgatása.',
Tulajdonsag: [Tulajdonsag2E.Bolcsesseg],
},
{
Id: 'k_hamisitas',
Name: 'Hamisítás',
Description: 'A képzettséggel néhány óra vagy nap munkájával hamis pénz, iratok, pecsétek és értéktárgyak készíthetők. Ellenpróbát akkor kell dobni, ha a hamisítvány hitelességét valaki kétségbe vonná.',
Tulajdonsag: [Tulajdonsag2E.Intelligencia],
Osztalyok: [Osztaly2E.Tolvaj],
},
{
Id: 'k_herbalizmus',
Name: 'Herbalizmus',
Description: 'A képzettség sikeres alkalmazásával (Közepes, Nehéz és Hősies eredménnyel) 1d3 / 1d3+1 / 1d3+2 adag, a Gyógyítás vagy Méregkeverés képzettséghez hasznos fű gyűjthető össze, az utóbbinál -2, terméketlen vidékeken -3 módosítóval. Utazás közben naponta egy, táborozva két próba dobható.',
Tulajdonsag: [Tulajdonsag2E.Bolcsesseg],
},
{
Id: 'k_historia',
Name: 'História',
Description: 'A karakter ismeri a történetírók munkáit vagy a szóhagyományt. Az ismeretek mértékét és pontosságát a képzettségpróba eredménye mutatja. Természetesen gyakran vannak olyan helyezetek, amikor a képzettség nem ad használható ismeretet.',
Tulajdonsag: [Tulajdonsag2E.Intelligencia],
},
{
Id: 'k_jelek_olvasasa',
Name: 'Jelek Olvasása',
Description: 'A képzettséggel a karakter el tud olvasni más nyelven írt írásokat és feliratokat.',
Tulajdonsag: [Tulajdonsag2E.Intelligencia],
Osztalyok: [Osztaly2E.Druida, Osztaly2E.Illuzionista, Osztaly2E.Pap, Osztaly2E.Varazslo, Osztaly2E.Vandor, Osztaly2E.Tolvaj]
},
{
Id: 'k_lovaglas',
Name: 'Lovaglás',
Description: 'A karakter kiemelkedően jól ért a lovagláshoz; harcban és nehéz helyzetekben is csodákra képes sarkallni hátasát.',
Tulajdonsag: [Tulajdonsag2E.Ugyesseg],
},
{
Id: 'k_maszas',
Name: 'Mászás',
Description: 'A képzettség olyan falakra való felmászásra alkalmas, ami egy átlagembernek komol gondot jelentene. Sikertelen próba esetén a karakter lezuhan, vagy a feladat meghaladja a képességeit.',
Tulajdonsag: [Tulajdonsag2E.Ero],
},
{
Id: 'k_megfigyeles',
Name: 'Megfigyelés',
Description: 'A karakter észreveszi a szabad szemmel látható veszélyforrásokat, a rá leselkedő támadókat, és a csak az avatott megfigyelő számára látható részleteket. (A szobák átkutatásához vagy pl. titkosajtók megtalálásához azonban nem szükséges ez a képzettség.)',
Tulajdonsag: [Tulajdonsag2E.Bolcsesseg],
},
{
Id: 'k_meregkeveres',
Name: 'Méregkeverés',
Description: 'A megfelelő eszközök és alapanyagok birtokában különféle mérgek készíthetők. Sikertelen próbadobás esetén a keverék hatástalan, és a méregkeverő 1:6 eséllyel veszélybe sodorja önmagát.',
Tulajdonsag: [Tulajdonsag2E.Intelligencia],
Osztalyok: [Osztaly2E.Tolvaj],
},
{
Id: 'k_mesterseg',
Name: 'Mesterség',
Description: 'A karakter járatos egy kiválasztott mesterségben pl. kovácsolás, adószedés vagy építészet. A kézügyességet igénylő mesterségekhez Ügyesség, a szellemi foglalkozásokhoz Intelligencia szükséges.',
Tulajdonsag: [Tulajdonsag2E.Intelligencia, Tulajdonsag2E.Ugyesseg],
},
{
Id: 'k_nyomkereses',
Name: 'Nyomkeresés',
Description: 'A karakter képes a nyomok követésére és hozzávetőleges azonosítására, vagyis megtudhatja, milyen régiek, miféle szerzet hagyta őket, milyen gyorsan haladt a nyomokat hagyó stb.',
Tulajdonsag: [Tulajdonsag2E.Bolcsesseg],
},
{
Id: 'k_okkultizmus',
Name: 'Okkultizmus',
Description: 'A képzettség a mágikus tanok elméleti és gyakorlati ismeretét jelenti. Számos hasznán belül a karakter Közepes próbával azonosíthatja az elmondás alatt álló varázslatokat, Nehéz próbával pedig a mágikus effektusokat és csapdákat.',
Tulajdonsag: [Tulajdonsag2E.Intelligencia],
Osztalyok: [Osztaly2E.Druida, Osztaly2E.Illuzionista, Osztaly2E.Pap, Osztaly2E.Varazslo, Osztaly2E.Vandor]
},
{
Id: 'k_osonas',
Name: 'Osonás',
Description: 'Csöndes és gyors mozgást tesz lehetővé, s azt, hogy a karakter elrejtőzzön a figyelmes tekintetek elől nagy szakértelemmel akár sötét sarkokban vagy árnyékokban is. Nehéz próba megdobásával az aktívan nem figyelő ellenfelek nem is dobhatnak ellenpróbát a felfedésére, Hősies próba esetén a karakter gyakorlatilag „mintha ott sem lenne”.',
Tulajdonsag: [Tulajdonsag2E.Ugyesseg],
},
{
Id: 'k_szabadulomuveszet',
Name: 'Szabadulóművészet',
Description: 'A karakter képes kiszabadulni kötelékeiből és láncaiból, és ami még fontosabb, ellenségei gyűrűjéből (lásd harcrendszer).',
Tulajdonsag: [Tulajdonsag2E.Ugyesseg],
Osztalyok: [Osztaly2E.Tolvaj],
},
{
Id: 'k_ugras',
Name: 'Ugrás',
Description: 'A karakter képes átugrani szélesebb vagy magasabb akadályokat is, és tompíthatja az esésből származó sebesüléseket (sikeres próbával egy kocka, Nehéz próbával kettő, Hősiessel három). Ha a próba sikertelen, a karakter lezuhan.',
Tulajdonsag: [Tulajdonsag2E.Ero],
},
{
Id: 'k_uszas',
Name: 'Úszás',
Description: 'A karakter jól tud úszni, akár nehéz körülmények között (zajló vízben, megterhelten stb.) is képes a felszínen maradni.',
Tulajdonsag: [Tulajdonsag2E.Ero],
},
{
Id: 'k_vadonjaras',
Name: 'Vadonjárás',
Description: 'A képzettség segít a vadonbeli megélhetésben, tájékozódásban és túlélésben. Sikeres képzettségpróbával (Közepes, Nehéz és Hősies nehézség megdobása esetén) 1d3 / 1d3+2 / 1d3+4 adag élelmet eredményez, terméketlen vidékeken az utóbbi dobásra -3 módosítóval. Utazás közben naponta egy, táborozva két próba dobható. A képzettség segít abban is, hogy a csapat ne tévedjen el a sűrű erdőkben, kietlen sivatagokban vagy zegzugos szurdokvölgyekben. Végül a képzettségpróba sikere befolyásolja, hogy milyen táborhelyet sikerül találni a vadonban: ha a mesélő máshogy nem határoz, sikeres próbával kettőre, Nehéz próbával egyre csökkenti, Hősies próbával teljesen elkerüli az éjszakai találkozási esélyeket.',
Tulajdonsag: [Tulajdonsag2E.Bolcsesseg],
},
{
Id: 'k_zarnyitas',
Name: 'Zárnyitás',
Description: 'Tolvajszerszámok segítségével a karakter felnyithatja a különféle mechanikus zárakat. Sikertelen próba esetén nem képes az adott zár felnyitására egészen addig, amíg új tapasztalatokat nem szerez (vagyis a következő szintlépéséig).',
Tulajdonsag: [Tulajdonsag2E.Ugyesseg],
Osztalyok: [Osztaly2E.Tolvaj],
},
{
Id: 'k_zsebmetszes',
Name: 'Zsebmetszés',
Description: 'Őrizetlenül hagyott vagy övön, zsebben található kisebb tárgyak észrevétlen eltulajdonítása. Ha a próba sikertelen, a karaktert észreveszik vagy legalábbis gyanút kelt. Másféle, kézügyességet igénylő fogásokra is alkalmas.',
Tulajdonsag: [Tulajdonsag2E.Ugyesseg],
Osztalyok: [Osztaly2E.Tolvaj],
},
]
export const TolvajKepzettsegList = KepzettsegLista.filter(x => x.Osztalyok?.includes(Osztaly2E.Tolvaj))
export const Kepzettsegek: Record<KepzettsegId, Kepzettseg> =
KepzettsegLista.reduce(
(acc: Record<KepzettsegId, Kepzettseg>, k) => { acc[k.Id] = k; return acc; },
{} as Record<KepzettsegId, Kepzettseg>);
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 + 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) {
numberOfKepzettseg = 1
console.log('Adjusted Number of Kepzetsegek = ', numberOfKepzettseg)
}
if (numberOfKepzettseg > max) {
numberOfKepzettseg = max
console.log('Adjusted Number of Kepzetsegek = ', numberOfKepzettseg)
}
return numberOfKepzettseg;
}
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 (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, '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) {
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))
}
}
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]
let mustChangeKepzettsegek = false
const restrictedKepzettsegek = GetKepzettsegListaN(karakter)(0)
if (kepzettsegek.length === 0) {
if (restrictedKepzettsegek.length > 0) {
kepzettsegek.push(restrictedKepzettsegek[0].Id)
mustChangeKepzettsegek = true
}
} else {
if (!restrictedKepzettsegek.includes(Kepzettsegek[kepzettsegek[0]])) {
kepzettsegek[0] = restrictedKepzettsegek[0].Id
mustChangeKepzettsegek = true
}
}
// // TODO: really fix this sometimes.
// const uglyDoubleRestrictedKepzettsegek = GetKepzettsegListaN(karakter)(1)
// if (kepzettsegek.length === 1) {
// if (uglyDoubleRestrictedKepzettsegek.length > 0) {
// kepzettsegek.push(uglyDoubleRestrictedKepzettsegek[0].Id)
// mustChangeKepzettsegek = true
// }
// } else {
// if (!uglyDoubleRestrictedKepzettsegek.includes(Kepzettsegek[kepzettsegek[1]])) {
// kepzettsegek[1] = uglyDoubleRestrictedKepzettsegek[0].Id
// mustChangeKepzettsegek = true
// }
// }
if (kepzettsegek.length < numberOfKepzettsegek) {
for (let i = kepzettsegek.length; i < numberOfKepzettsegek; i++) {
const kepzettsegLista = GetKepzettsegListaN({...karakter, kepzettsegek})(i)
if (kepzettsegLista.length > 0) {
kepzettsegek.push(kepzettsegLista[0].Id)
}
}
mustChangeKepzettsegek = true
}
if (kepzettsegek.length > numberOfKepzettsegek){
kepzettsegek = kepzettsegek.slice(0, numberOfKepzettsegek)
mustChangeKepzettsegek = true
}
if (mustChangeKepzettsegek){
changeKepzettsegek(kepzettsegek)
}
}
@@ -0,0 +1,103 @@
import {Mentok, MentokTotal} from "./mentok";
import {Osztaly2E} from "./osztaly2E";
import {Karakter2E} from "./karakter2E";
import {CelzoTB, KozelharciTB} from "./tamadas_bonusz";
import {Faj2E} from "./faj2E";
import {Modifier} from "./tulajdonsag2E";
import {GetPajzs, GetPancel, MaxAbilityVO} from "./felszereles";
export interface MasodlagosErtekekView {
HP: number
VO: number
KozelharciTB: number[]
CelzoTB: number[]
Mentok: Mentok
}
export type KarakterpickForMasodlagosErtekek = Pick<Karakter2E, 'tulajdonsagok' | 'szint' | 'szintlepesek' | 'faj' | 'felszereles'>
export function BaseHP(osztaly: Osztaly2E) {
let base = 4;
switch (osztaly){
case Osztaly2E.Barbar:
base = 12
break;
case Osztaly2E.Amazon:
case Osztaly2E.Ijasz:
case Osztaly2E.Tengeresz:
case Osztaly2E.Harcos:
case Osztaly2E.Vandor:
base = 10;
break
case Osztaly2E.Pap:
case Osztaly2E.Druida:
base = 8;
break;
case Osztaly2E.Tolvaj:
case Osztaly2E.Dalnok:
base = 6;
break;
case Osztaly2E.Varazslo:
case Osztaly2E.Illuzionista:
base = 4;
break;
}
return base
}
export function HP(karakter: Pick<Karakter2E, 'tulajdonsagok' | 'szintlepesek'>): number {
const egeszsegModifier = Modifier(karakter.tulajdonsagok.t_egs)
return karakter.szintlepesek
// https://lfg.hu/forum/topic/15079-kard-es-magia/page/219/#comment-2218333
.map(szint => Math.max(1, szint.HProll + egeszsegModifier))
.reduce((sum, val) => sum + val, 0)
}
export function VOegyeb(karakter: Pick<Karakter2E, 'faj'| 'szintlepesek'>): number {
const osztalyok = new Set(karakter.szintlepesek.map(x => x.osztaly))
let vo = 0;
if (osztalyok.has(Osztaly2E.Tengeresz)) {
const kalozSzintek = karakter.szintlepesek.filter(x => x.osztaly === Osztaly2E.Tengeresz).length
vo += Math.floor(kalozSzintek / 3)
}
if (osztalyok.has(Osztaly2E.Amazon)) {
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
}
return vo;
}
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
let vo = 10 + pajzsVO + pancelVO
let abilityVO = Modifier(karakter.tulajdonsagok.t_ugy)
if (pancel) {
abilityVO = Math.min(MaxAbilityVO(pancel.Type), abilityVO)
}
if (osztalyok.has(Osztaly2E.Amazon)){
if (karakter.tulajdonsagok.t_kar > karakter.tulajdonsagok.t_ugy) {
abilityVO = Modifier(karakter.tulajdonsagok.t_kar)
}
}
vo += VOegyeb(karakter)
vo += abilityVO
return vo
}
export function CalculateMasodlagosErtekek(karakter: KarakterpickForMasodlagosErtekek): MasodlagosErtekekView {
return {
HP: HP(karakter),
VO: VO(karakter),
CelzoTB: CelzoTB(karakter),
KozelharciTB: KozelharciTB(karakter),
Mentok: MentokTotal(karakter)
}
}
@@ -0,0 +1,103 @@
import {Osztaly2E} from "./osztaly2E";
import {Karakter2E} from "./karakter2E";
import {KarakterTulajdonsagok, TulajdonsagModosito} from "./tulajdonsag2E";
import {Szintlepes} from "./szintlepes";
export interface Mentok {
kitartas: number
reflex: number
akaratero: number
}
export type MentoTipus = keyof Mentok
export function MentoTipusLabel(tipus: MentoTipus): string {
switch (tipus) {
case "akaratero":
return "Akaraterő";
case "kitartas":
return "Kitartás";
case "reflex":
return "Reflex";
}
}
export function ElsodlegesMento(szint: number): number {
return 2 + Math.floor(szint / 2);
}
export function MasodlagosMento(szint: number): number {
return Math.floor(szint / 3)
}
export function ElsodlegesMentok(osztaly: Osztaly2E) : MentoTipus[] {
let t: MentoTipus[] = []
switch (osztaly){
case Osztaly2E.Barbar:
case Osztaly2E.Amazon:
case Osztaly2E.Ijasz:
case Osztaly2E.Tengeresz:
case Osztaly2E.Harcos:
case Osztaly2E.Vandor:
t = ['kitartas'];
break;
case Osztaly2E.Druida:
case Osztaly2E.Pap:
t = ['kitartas', 'akaratero'];
break;
case Osztaly2E.Tolvaj:
case Osztaly2E.Dalnok:
t = ['reflex'];
break;
case Osztaly2E.Varazslo:
case Osztaly2E.Illuzionista:
t = ['akaratero']
break;
}
return t;
}
export function MentokAlap(osztaly: Osztaly2E, szint: number = 1) {
const elsodlegesek = ElsodlegesMentok(osztaly)
return {
kitartas: elsodlegesek.includes('kitartas') ? ElsodlegesMento(szint) : MasodlagosMento(szint),
reflex: elsodlegesek.includes('reflex') ? ElsodlegesMento(szint) : MasodlagosMento(szint),
akaratero: elsodlegesek.includes('akaratero') ? ElsodlegesMento(szint) : MasodlagosMento(szint),
}
}
export function MentoModositok(tulajdonsagModositok: KarakterTulajdonsagok) {
return {
kitartas: tulajdonsagModositok.t_egs,
reflex: tulajdonsagModositok.t_ugy,
akaratero: tulajdonsagModositok.t_bol,
}
}
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,
reflex: alap.reflex + modositok.reflex,
akaratero: alap.akaratero + modositok.akaratero,
}
}
@@ -0,0 +1,376 @@
export enum Osztaly2E {
Harcos = 'o_2e_harcos',
Ijasz = 'o_2e_ijasz',
Amazon = 'o_2e_amazon',
Tengeresz = 'o_2e_tengeresz',
Barbar = 'o_2e_barbar',
Pap = 'o_2e_pap',
Tolvaj = 'o_2e_tolvaj',
Varazslo = 'o_2e_varazslo',
Illuzionista = 'o_2e_illuzionista',
Dalnok = 'o_2e_dalnok',
Druida = 'o_2e_druida',
Vandor = 'o_2e_vandor',
}
export function OsztalyLabel(osztaly: Osztaly2E) : string {
switch (osztaly) {
case Osztaly2E.Amazon: return 'Amazon';
case Osztaly2E.Barbar: return 'Barbár';
case Osztaly2E.Pap: return 'Pap';
case Osztaly2E.Illuzionista: return 'Illúzionista';
case Osztaly2E.Varazslo: return 'Varázsló';
case Osztaly2E.Harcos: return 'Harcos';
case Osztaly2E.Ijasz: return 'Íjász';
case Osztaly2E.Tengeresz: return 'Tengerész';
case Osztaly2E.Tolvaj: return 'Tolvaj';
case Osztaly2E.Dalnok: return 'Dalnok';
case Osztaly2E.Druida: return 'Druida';
case Osztaly2E.Vandor: return 'Vándor';
}
}
export function OsztalyDescription(osztaly: Osztaly2E) : string {
switch (osztaly) {
case Osztaly2E.Harcos:
case Osztaly2E.Amazon:
case Osztaly2E.Barbar:
case Osztaly2E.Ijasz:
case Osztaly2E.Tengeresz:
return 'A harcosok a fegyverhasználat mesterei. Megállják a helyüket a közelharcban és a távolsági összecsapásokban is, és sokkal jobban viselik a sebesüléseket, mint más osztályok képviselői. Ez az osztály mind közül a leggyakoribb, de egyben a legváltozatosabb is öt alosztállyal rendelkezik.';
case Osztaly2E.Tolvaj:
return 'A tolvajok a becstelen harcban és a csöndes gyilkolásban járatosak (közülük sokan alkalmaznak mérgeket őket orgyilkosoknak nevezik). Harci képességeik és életerejük viszonylag gyenge, s nehezebb vértezeteket sem használhatnak. Ezért ha módjukban áll, nem szemtől szembe, hanem lesből támadnak áldozataikra, és ha ez nem sikerülne, gyorsan kereket oldanak. Emellett alvilági pályafutásuk során számos hasznos képzettségben válnak járatossá.';
case Osztaly2E.Pap:
return 'A papok az istenek militáns varázshasználó szolgái. Hitük parancsait minden körülmények között be kell tartaniuk, s bizonyos rendszerességgel áldozatokat kell bemutatniuk. A papok csak istenükkel azonos vagy attól legfeljebb eggyel különböző jelleműek lehetnek. A papok csak az istenük által kijelölt fegyvereket forgathatják, de szabadon válogathatnak a különféle vértek között.';
case Osztaly2E.Varazslo:
return 'A varázslók kiemelkedő szellemi fegyelmezettségük segítségével képesek a magasabb fokú mágikus erőket irányító varázsigék memorizálására és fölhasználására. Tudásukat varázskönyvekben rögzítik, s mindent elkövetnek egy ismeretlen formula megszerzéséért. Többségüket a magabiztos önhittség és az alacsonyabb szellemi képességűek megvetése jellemzi. Sem a varázslók, sem az illuzionisták nem képesek bármiféle páncélban varázsolni, és csak kevés fegyvertípus forgatásában járatosak.';
case Osztaly2E.Illuzionista:
return 'A varázslók kiemelkedő szellemi fegyelmezettségük segítségével képesek a magasabb fokú mágikus erőket irányító varázsigék memorizálására és fölhasználására. Tudásukat varázskönyvekben rögzítik, s mindent elkövetnek egy ismeretlen formula megszerzéséért. Többségüket a magabiztos önhittség és az alacsonyabb szellemi képességűek megvetése jellemzi. Az illuzionisták a varázslók alosztályaként a mágia egy hasonló, de több lényeges területen eltérő fajtáját művelik. Lételemük a káprázat, a megtévesztés és mások elméjének befolyásolása többségük hipnotizőr vagy ügyes manipulátor. Az illuzionista varázslatok csekélyebb pusztító erőt képviselnek, ellenben egy élelmes illuzionista kezében nagyon sokrétűek. Sem a varázslók, sem az illuzionisták nem képesek bármiféle páncélban varázsolni, és csak kevés fegyvertípus forgatásában járatosak.';
case Osztaly2E.Dalnok:
return 'A dalnokok a legendák és hősi énekek ismerői, a tolvaj osztályhoz közeli, de tőlük eltérő képességekkel. A legtöbbjük állandóan az úton jár, s csak rövid időre marad egy helyben, hogy újabb dalokkal és legendákkal gyarapítsa repertoárját, és újabb meg újabb közönségek előtt próbálja ki tudását. A dalnok opcionális osztály, és mesélői engedéllyel indítható.';
case Osztaly2E.Druida:
return 'A druidák a természet erőit szolgáló aszkétikus misztikusok, akiknek rendje az emberi kor elejére néz vissza. Céljaikat titkok köde övezi, amelyet ők maguk sem igyekeznek eloszlatni (s elárulóit ők maguk vadásszák le). Vannak, akik szerint az Egyensúly ügyét képviselik, mások szerint az emberiség visszavadításán és a civilizáció lerombolásán munkálkodnak, vagy emberáldozatoknak hódolnak. Az biztos, munkájuk valamilyen formában a kőkörökhöz, faragott kövekhez, és az ősi vérvonalak fenntartásához fűzi őket. Hitük része a szent eskü, melynek keretén belül megfogadják, hogy csak az ősi druida rend tradicionális fegyvereit használják, és nem viselnek olyan vértet, amely fémet tartalmaz. A druidáknak, szintén fogadalmuk révén, legalább részben Semleges jelleműnek kell lenniük, és nem lehetnek többosztályúak. A druida opcionális osztály, és mesélői engedéllyel indítható.'
case Osztaly2E.Vandor:
return 'A vándorok a vadonbeli túlélés mestereiként ismert harcosok. Ezen kívül sokat foglalkoznak ősi legendák, főleg régi civilizációk, romok feltárásával. Távoli kapcsolatban állnak a druidák rendjével, és sokan az ő ügynökeiknek tekintik őket, de a kapcsolat sokkal lazább, és gyakran csak a kölcsönös tiszteletig terjed. A vándor opcionális osztály, és mesélői engedéllyel indítható.'
}
}
export function OsztalyProperties(osztaly: Osztaly2E) : string[] {
switch (osztaly) {
case Osztaly2E.Harcos: return [
'Támadásbónusz: Szint x 1',
'Mentődobások: Kitartás az első, Reflex és Akaraterő a második oszlop szerint',
'Fegyverhasználat: bármilyen',
'Páncélhasználat: bármilyen',
'Hp: szintenként 1d10, 9. szint („Nagyúr”) fölött 3/szint',
'Képzettségek: 3',
]
case Osztaly2E.Ijasz: return [
'Támadásbónusz: Szint x 1',
'Mentődobások: Kitartás az első, Reflex és Akaraterő a második oszlop szerint',
'Fegyverhasználat: bármilyen',
'Páncélhasználat: nehéz vérteken kívül bármilyen',
'Hp: szintenként 1d10, 9. szint („Nagyúr”) fölött 3/szint',
'Képzettségek: 3',
]
case Osztaly2E.Amazon: return [
'Támadásbónusz: Szint x 1',
'Mentődobások: Kitartás az első, Reflex és Akaraterő a második oszlop szerint',
'Fegyverhasználat: bármilyen',
'Páncélhasználat: csak könnyű vértek',
'Hp: szintenként 1d10, 9. szint („Nagyúr”) fölött 3/szint',
'Képzettségek: 3',
]
case Osztaly2E.Tengeresz: return [
'Támadásbónusz: Szint x 1',
'Mentődobások: Kitartás az első, Reflex és Akaraterő a második oszlop szerint',
'Fegyverhasználat: bármilyen',
'Páncélhasználat: nehéz vérteken kívül bármilyen',
'Hp: szintenként 1d10, 9. szint („Nagyúr”) fölött 3/szint',
'Képzettségek: 3',
]
case Osztaly2E.Barbar: return [
'Támadásbónusz: Szint x 1',
'Mentődobások: Kitartás az első, Reflex és Akaraterő a második oszlop szerint',
'Fegyverhasználat: bármilyen',
'Páncélhasználat: bármilyen',
'Hp: szintenként 1d12, 9. szint („Nagyúr”) fölött 3/szint',
'Képzettségek: 3',
]
case Osztaly2E.Pap: return [
'Támadásbónusz: Szint x 2/3',
'Mentődobás: Kitartás és Akaraterő az első, Reflex a második oszlop szerint',
'Fegyverhasználat: csak istenük kijelölt fegyvereit használhatják (általában buzogányokat és tőröket)',
'Páncélhasználat: bármilyen',
'Hp: szintenként 1d8, 9. szint („Főpap”) fölött 2/szint',
'Képzettségek: 3',
]
case Osztaly2E.Tolvaj: return [
'Támadásbónusz: Szint x 2/3',
'Mentődobás: Reflex az első, Kitartás és Akaraterő a második oszlop szerint',
'Fegyverhasználat: egykezes fegyvereket, rövidíjat és számszeríjakat használhatnak',
'Páncélhasználat: csak könnyű, pajzs nélkül',
'Hp: szintenként 1d6, 9. szint („Mestertolvaj”) fölött 2/szint',
'Képzettségek: 7 (ebből legalább 4 minden képpen a „tolvajképzettségek” listájáról); Sokoldalúságuknak köszönhetően 5. és 9. szinten kapnak egy új képzettséget'
]
case Osztaly2E.Varazslo:
case Osztaly2E.Illuzionista: return [
'Támadásbónusz: Szint x 1/2',
'Mentődobás: Akaraterő az első, Kitartás és Reflex a második oszlop szerint',
'Fegyverhasználat: tőrt, botot, dobótűket, bunkót és parittyát használhatnak',
'Páncélhasználat: nincs',
'Hp: szintenként 1d4, 9. szint („Varázslónagyúr”) fölött 1/szint',
'Képzettségek: 3',
];
case Osztaly2E.Dalnok: return [
'Támadásbónusz: Szint x 2/3',
'Mentődobások: Reflex az első, Kitartás és Akaraterő a második oszlop szerint',
'Fegyverhasználat: egykezes fegyverek, könnyű számszeríj, rövidíj',
'Páncélhasználat: könnyű és pajzsok',
'Hp: szintenként 1d6, 9. szint („Bárd”) fölött 2/szint',
'Képzettségek: 3, az egyik képzettségük mindenképpen az Előadás',
];
case Osztaly2E.Druida: return [
'Támadásbónusz: Szint x 2/3',
'Mentődobás: Kitartás és Akaraterő az első, Reflex a második oszlop szerint',
'Fegyverhasználat: bot, bunkósbot, tőr, dobótű, szablya, sarlókard, lándzsa, parittya.',
'Páncélhasználat: bőrvértet, szőrméket viselhetnek és fapajzsot hordhatnak.',
'Hp: szintenként 1d8, 9. szint („Fődruida”) fölött 2/szint.',
'Képzettségek: 3, az egyik képzettségük mindenképpen a Vadonjárás',
];
case Osztaly2E.Vandor: return [
'Támadásbónusz: Szint x 1',
'Mentődobások: Kitartás az első, Reflex és Akaraterő a második oszlop szerint',
'Fegyverhasználat: tetszőleges',
'Páncélhasználat: könnyű és pajzsok',
'Hp: szintenként 1d10, 9. szint („a Vadak Mestere”) fölött 3/szint',
'Képzettségek: 3, az egyik képzettségük mindenképpen a Vadonjárás',
]
}
}
export interface Skill {
Id: string
Name: string,
Description: string
ExtendedDescription?: string[]
}
export function OsztalySpecialSkills(osztaly: Osztaly2E) : Skill[] {
switch (osztaly) {
case Osztaly2E.Harcos: return [
{
Id: 's_2e_harcos_harcos',
Name: 'Harcos',
Description: 'Speciális harci tapasztalatuknak köszönhetően minden páratlan szinten +2-t kapnak egy választott fegyvertípus (pl. kardok, buzogányok, fejszék, dobófegyverek, íjak stb.) sebzésére. 5. szinttől kétszer ugyanarra a típusra választva a sebzésbónusz +4-re emelhető'
}
]
case Osztaly2E.Amazon: return [
{
Id: 's_2e_harcos_amazon',
Name: 'Amazon',
Description: 'Ezek a barbár harcosnők szépségükről és karizmatikus jelenlétükre építő védekező harcmodorukról ismertek',
},
{
Id: 's_2e_harcos_amazon_vert',
Name: 'MMO vért',
Description: 'Az amazonok csak könnyű vérteket viselhetnek, de VO-juk szint/2-vel javul, továbbá Karizma- és Ügyességmódosítóik közül a kedvezőbbet választhatják a VO számításánál'
},
]
case Osztaly2E.Barbar: return [
{
Id: 's_2e_harcos_barbar',
Name: 'Barbár',
Description: 'Pusztaságok, sarkvidéki jégmezők és fülledt őserdők lakói, akiket kegyetlen környezetük nevelt túlélővé'
},
{
Id: 's_2e_harcos_barbar_tulelo',
Name: 'Túlélő',
Description: 'A barbárok 1d12-vel dobják a Hp-jüket, és +1 bónuszt (9. szinttől +2-t) kapnak a mentődobásaikra.'
},
{
Id: 's_2e_harcos_barbar_szivos',
Name: 'Szívós',
Description: 'Szintenként -1 Hp-ig öntudatuknál vannak, bár -5 Hp-n halálos sebet kapnak, ami 1d3*10 perc alatt akkor is halálos, ha egyébként győznek a csetepatéban.'
},
{
Id: 's_2e_harcos_barbar_iras',
Name: 'Írástudatlan',
Description: 'Minden barbár írástudatlanul kezdi a játékot, de ha kell, később megtanulhatja a betűvetést.'
}
]
case Osztaly2E.Ijasz: return [
{
Id: 's_2e_harcos_ijasz',
Name: 'Íjász',
Description: 'A távolsági harc mesterei. Dobó- és lőfegyverrel (a számszeríjakat kivéve) körönként kapnak egy teljesértékű extra támadást, vagy számszeríj esetén +2-t a célzásra'
},
{
Id: 's_2e_harcos_ijasz_vert',
Name: 'Kötelező mozgékonyság',
Description: 'Az íjászok nem viselhetnek nehéz vérteket'
},
{
Id: 's_2e_harcos_ijasz_lovaglas',
Name: 'Vágta',
Description: 'Lovaglás próbával akár vágtató lóról is tudnak lőni'
}
]
case Osztaly2E.Tengeresz: return [
{
Id: 's_2e_harcos_kaloz',
Name: 'Tengerész/Kalóz',
Description: 'A tengerészek a hajózás mesterei; kereskedők, tengeri rablók és rabszolgavadászok. Egyik képzettségük mindenképpen a Hajózás.'
},
{
Id: 's_2e_harcos_kaloz_vasmacska',
Name: 'Vasmacska',
Description: 'A kalózok és tengerészek nem viselhetnek nehéz vértezetet, de VO-juk Szint/3-al javul.'
},
{
Id: 's_2e_harcos_kaloz_kritikus',
Name: 'Kritikus',
Description: 'Minden harmadik szinten kiválaszthatnak egy fegyvertípust, amellyel könnyebben okoznak kritikus sebesülést, tehát pl. fejszékkel 20 helyett 19-20-on, hosszú kardokkal 19-20 helyett 18-20-on, görbe kardokkal 18-20 helyett 17-20-on stb. 9. szinttől fegyverenként kétszer is választható.'
},
{
Id: 's_2e_harcos_kaloz_trukkos',
Name: 'Trükkös',
Description: '+2-t kapnak harci manővereikre (HM)'
},
]
case Osztaly2E.Tolvaj: return [
{
Id: 's_2e_tolvaj_orvtamadas',
Name: 'Orvtámadás',
Description: 'akkor használható, ha a tolvaj meglepi áldozatát, tehát pl. osonva a hátába kerül, vagy beszélgetés közben tőrt ránt és egyetlen mozdulattal gyomron szúrja. Ekkor a támadásra +4 jár, az orvtámadás pedig minden páratlan szintért +1d6 Hp sebzést okoz (maximuma +5d6). Dobó- és lőfegyverrel is használható, és ha a tolvaj úgy dönt, illetve a használt fegyver lehetővé teszi, az ellenfél elkábítására is alkalmazható.'
},
{
Id: 's_2e_tolvaj_kezdemenyezes',
Name: 'Jobb kezdeményezés',
Description: 'a tolvajok +4-et kapnak kezdeményezési dobásaikra.',
},
{
Id: 's_2e_tolvaj_kepzettsegek',
Name: 'Tolvajképzettségek',
Description: 'minden tolvaj jártas több olyan képzettségben, amely segíti a rejtőzködésben, a jól őrzött értékek eltulajdonításában és hasonló feladatokban.',
},
]
case Osztaly2E.Pap: return [
{
Id: 's_2e_pap_varazslat',
Name: 'Varázslatok',
Description: 'a papi varázslatok általában gyógyításra, mások megsegítésére és vallási feladatok ellátására szolgálnak. Minél magasabb szintűek, egy nap annál több varázslatot tudnak alkalmazni a következő oldalon található táblának megfelelően. Például egy 4. szintű, 17-es Bölcsességgel rendelkező pap öt nulladik szintű, négy első szintű és három második szintű varázslatot tarthat naponta elméjében.'
},
{
Id: 's_2e_pap_bonusz_varazslat',
Name: 'Bónusz varázslatok',
Description: 'a magas Bölcsességű papok minden nap további varázslatokat memorizálhatnak, ha erre szintjük eleve képessé tenné őket. Ez a kegy azonban csak azoknak a papoknak jár, akik jó megítélésben állnak istenüknél (a kalandok elején ez a helyzet).'
},
{
Id: 's_2e_pap_gyogyseb',
Name: 'Gyógyítás vagy sebokozás',
Description: 'a pap az aznap már memorizált (megtanult) varázslatokat gyógyításra vagy sebek okozására is fölhasználhatja. Érintésével a varázslat minden szintjéért 1d8 Hp-t gyógyít vagy sebez így pl. egy Ómen elhasználásával 2d8 Hp-t. Az így konvertált varázslat természetesen elvész. Az, hogy a képesség sebek gyógyítására vagy okozására szolgál, a pap istenétől függ. A békésebb istenek főleg az elsőt, a harciasabbak főleg a másodikat részesítik előnyben, s vannak olyanok is, akik híveikre bízzák, hogy melyik utat választják. Ha azonban a pap már eldöntötte, melyik képességben járatos, döntését többé nem másíthatja meg!'
},
{
Id: 's_2e_pap_elohalott',
Name: 'Élőholtak elűzése vagy irányítása',
Description: 'a papok képesek elűzni vagy uralmuk alá vonni az élőholt szörnyeket, például zombikat, lidérceket és vámpírokat. Ez a képesség naponta egyszer (5. szinttől kétszer, 9. szinttől háromszor) használható. A cselekedet a következőképpen működik:',
ExtendedDescription: [
'A pap [1d20 + Karizma bónusz] próbát tesz. Ennek az értékét összeveti a standard bónusztáblázattal (3-5 = -2, 6-8 = -1, 9-11 = +0, 12-14 = +1, 15-17 = +2, 18 = +3). Az innen kiolvasható módosítót hozzáadja saját szintjéhez (ha negatív, akkor levonja). Az eredmény jelzi, hogy a pap legfeljebb milyen szintű élőholtakra lesz hatással. Ha tehát pl. a próba végeredménye 14 és a pap ötödik szintű, akkor 5+1, azaz 6. szintű a legerősebb élőhalott, amire a kísérlet sikeres. A 20-as dobás itt is automatikus siker, az 1-es automatikus kudarc.',
'A pap [2d6+szint] szintű élőhalottat űzhet el vagy irányíthat. Először a leggyengébb élőholtakra hat, azután a következő leggyengébbekre stb. Az elűzött élőholtak elmenekülnek a pap szeme elől, és addig nem is ártanak neki és társainak, amíg a pap csapata meg nem támadja őket! Ezzel szemben a gonosz papok parancsokat adhatnak az irányított szörnyeknek. Mindkét hatás néhány óráig tart, az élőholtak szintjétől és a körülményektől függően.',
'Ha az összes szem előtt lévő élőholtat sikerült elűzni vagy irányítani és még maradt „a szent energiából”, akkor ismét el kell kezdeni a hatást a leggyengébb ellenségektől. Az így elűzött szörnyek megsemmisülnek, illetve irányítás esetén végleg a gonosz pap szolgálatába állnak.',
'A két hatás egymás ellen is fölhasználható, például egy jó pap kiolthatja egy gonosz pap irányítását, de akár két gonosz pap is versenyezhet egy csapat sírlidérc „megtérítésén”.',
]
},
{
Id: 's_2e_pap_invokacio',
Name: 'Invokáció',
Description: 'az istenség nevének kimondásával kérhetik annak segítségét, ami +2-t ad egy kiválasztott próbadobáshoz. Hetente a Bölcsességbónuszukkal azonos alkalommal (de legalább egyszer) használhatják a képességet.'
}
]
case Osztaly2E.Varazslo: return [
{
Id: 's_2e_varazslo_varazslatok',
Name: 'Varázslatok',
Description: 'a varázslók mágiája mind között a legszerteágazóbb. A papokkal ellentétben azonban minden varázslatot külön kell megszerezniük és könyvükben rögzíteniük. A már ismert varázslatokat reggelenként könyvük tanulmányozásával memorizálják a szabálykönyvben leírt táblázat szerinti számban, s ha felhasználják őket, azok eltűnnek elméjükből és aznap már nem használhatók. Így például egy harmadik szintű varázsló négy nulladik, két első és egy második szintű varázslatot tud egyszerre elméjében tárolni. Egy varázslat kétszeres memorizálása két helyet foglal. Az illuzionisták nem képesek a varázslói varázslatok megértésére és viszont.'
},
{
Id: 's_2e_varazslo_varazskonyv',
Name: 'Varázskönyv',
Description: 'minden varázsló megtanulja és könyvében rögzíti az összes nulladik szintű varázslatot. Ezen kívül harmadik szinten (vagyis a kalandok kezdetén) még 1d3+3 első szintű és 1d3 második szintű varázslatot ismernek. A kezdeti varázslatokat véletlenszerűen, kockadobással kell meghatározni; azonos eredmény esetén nincs lehetőség új dobásra. A további varázslatok mások könyveiből, mágikus tekercsekből vagy más módszerek segítségével sajátíthatók el (lásd a IV. fejezetet).'
},
]
case Osztaly2E.Illuzionista: return [
{
Id: 's_2e_illuzionista_varazslatok',
Name: 'Varázslatok',
Description: 'a varázslók mágiája mind között a legszerteágazóbb. A papokkal ellentétben azonban minden varázslatot külön kell megszerezniük és könyvükben rögzíteniük. A már ismert varázslatokat reggelenként könyvük tanulmányozásával memorizálják a szabálykönyvben leírt táblázat szerinti számban, s ha felhasználják őket, azok eltűnnek elméjükből és aznap már nem használhatók. Így például egy harmadik szintű varázsló négy nulladik, két első és egy második szintű varázslatot tud egyszerre elméjében tárolni. Egy varázslat kétszeres memorizálása két helyet foglal. Az illuzionisták nem képesek a varázslói varázslatok megértésére és viszont.'
},
{
Id: 's_2e_illuzionista_varazskonyv',
Name: 'Varázskönyv',
Description: 'minden varázsló megtanulja és könyvében rögzíti az összes nulladik szintű varázslatot. Ezen kívül harmadik szinten (vagyis a kalandok kezdetén) még 1d3+3 első szintű és 1d3 második szintű varázslatot ismernek. A kezdeti varázslatokat véletlenszerűen, kockadobással kell meghatározni; azonos eredmény esetén nincs lehetőség új dobásra. A további varázslatok mások könyveiből, mágikus tekercsekből vagy más módszerek segítségével sajátíthatók el (lásd a IV. fejezetet).'
}
];
case Osztaly2E.Dalnok: return [
{
Id: 's_2e_dalnok_zene',
Name: 'Zene',
Description: 'zenéjük napi egy alkalommal inspiráló hatással van a szövetségeseikre és demoralizáló hatással ellenfeleikre: az előbbiek +1-et, az utóbbiak -1-et kapnak mindenféle dobásukra. Ezt a képzettséget 5. szinttől kétszer, 9. szinttől háromszor használhatják.'
},
{
Id: 's_2e_dalnok_legendak',
Name: 'Legendák (Böl)',
Description: 'ezzel a speciális képzettséggel a karakter megtudhat valamit a kiválasztott hely, család, szervezet stb. hátteréről ha ez a legendákból vagy hősi énekekből megtudható volna. Közepes próba esetén általános, Nehéz próba esetén specifikus, Hősies próba esetén titkos ismereteket is adhat. Olyan dolgokra alkalmazható, amelyek szerepelhetnének egy közszájon forgó legendában, és az ismeretek is ezt fogják tükrözni. A köznapi, vagy éppenséggel nagyon jól elrejtett dolgok kapcsán nem nyújt segítséget.'
},
{
Id: 's_2e_dalnok_varazslatok',
Name: 'Varázslatok',
Description: 'szintenként 2 db varázslatot tudnak megtanulni a dalnok varázslatlistából, s azt verses rigmusként, dalként vagy zeneműként előadni. További dalok rejtőznek régi könyvekben és más dalnokok ajkán. A dalnokok nem kapnak bónusz varázslatokat.\n A dalnok varázslatok egy része folyamatos, megszakítatlan éneklést igényel (ezek mellett csak mozgásra és egyszerűbb cselekedetekre nyílik lehetőség, de cserébe az énekszó abbamaradtáig hatnak), mások pedig egy rövid dallam után a varázslatleírásokban jelzett határidő lejártáig érvényesek. A dalnok szintenként fél óráig tudja fenntartani énekét.'
},
];
case Osztaly2E.Druida: return [
{
Id: 's_2e_druida_varazslatok',
Name: 'Varázslatok',
Description: 'a druida varázslatok általában a természet erőinek kisebb-nagyobb megnyilvánulásai. A naponta memorizálható varázslatok száma a papokéval egyező, és szintén a Bölcsességük alapján kapnak bónusz varázslatokat.'
},
{
Id: 's_2e_druida_termeszetjaras',
Name: 'Természetjárás',
Description: 'szabadon mozoghatnak bármiféle természetes aljnövényzetben (mint például bozótosban, sűrűn benőtt területen, pókhálókban, de nem mágikus kötelékekben). Nem hagynak nyomot természetes környezetben, így hacsak nem akarják követni sem lehet őket.'
},
{
Id: 's_2e_druida_alakvaltas',
Name: 'Alakváltás',
Description: 'a tapasztalt druidák 6. szinttől naponta egyszer, 9. szinttől kétszer képesek felvenni a közönséges, helyben honos állatok alakját. A fölvett forma összes fizikai jellemzőjét megkapják (fizikai tulajdonságok, támadási forma, sebzés, speciális képességek stb.), de megtartják Hp-jüket, alap támadásbónuszukat, és szellemi képességeiket. Állati alakban nem képesek varázsolni és eszközöket használni. Minden átalakulás 2/szint Hp-t gyógyít a druidán.'
}
];
case Osztaly2E.Vandor: return [
{
Id: 's_2e_vandor_kepzettsegek',
Name: 'Vándorképzettségek',
Description: 'olyan képzettségeket is szabadon tanulhatnak, amelyeket csak varázshasználók ismernének.'
},
{
Id: 's_2e_vandor_termeszetjaras',
Name: 'Természetjárás',
Description: 'szabadon mozoghatnak bármiféle természetes aljnövényzetben (mint például bozótosban, sűrűn benőtt területen, pókhálókban, de nem mágikus kötelékekben). Nem hagynak nyomot természetes környezetben, így hacsak nem akarják követni sem lehet őket.'
},
{
Id: 's_2e_vandor_vadon_sugallata',
Name: 'A vadon sugallata',
Description: 'ha számukra új, vagy rég nem látott vadonbéli területre érkeznek, játékalkalmanként egyszer sikeres Vadonjárás próbával értelmezhetik a környezetükben elrejtett jeleket: ez lehet figyelmeztetés a környék veszélyeiről („valami nincs rendjén a hely vizeivel”), rejtélyeiről („régen emberek éltek itt de ez már a múlt”), az ott történtek lenyomata („itt hadak jártak!”), vagy más praktikus, de csak utalásszerű információ, amit a mesélő gondol.'
},
{
Id: 's_2e_vandor_varazslatok',
Name: 'Varázslatok',
Description: 'első szinten 2 + Bölcsességmódosítónyi 0. szintű varázslatot ismernek, majd szintenként további egy varázslatot tanulhatnak a druida listából. További varázslatok titokzatos kőkörökből és a druida hit más helyszínein ismerhetők meg. A vándorok nem kapnak bónusz varázslatokat.'
},
];
}
}
@@ -0,0 +1,20 @@
{
"pajzs": [
{
"Id": "pajzs_fa",
"Name": "Pajzs, fa",
"Type": "pajzs",
"VO": 2,
"Price": 8,
"Size": 1
},
{
"Id": "pajzs_fem",
"Name": "Pajzs, fém",
"Type": "pajzs",
"VO": 2,
"Price": 20,
"Size": 1
}
]
}
@@ -0,0 +1,92 @@
{
"pancel": [
{
"Id": "csuha",
"Name": "Vastag ruha, csuha, stb",
"Type": "konnyu",
"VO": 1,
"Price": 1,
"Size": 1
},
{
"Id": "bor",
"Name": "Bőrvért, bőrruha, fegyverkabát",
"Type": "konnyu",
"VO": 2,
"Price": 5,
"Size": 1
},
{
"Id": "kivert_bor",
"Name": "Kivert bőrvért, brigantin",
"Type": "konnyu",
"VO": 3,
"Price": 15,
"Size": 1
},
{
"Id": "prem",
"Name": "Prémruha, bunda",
"Type": "konnyu",
"VO": 3,
"Price": 12,
"Size": 2
},
{
"Id": "len",
"Name": "Linothorax, ragasztott lenpáncél",
"Type": "konnyu",
"VO": 4,
"Price": 20,
"Size": 2
},
{
"Id": "lancing",
"Name": "Láncing",
"Type": "konnyu",
"VO": 4,
"Price": 50,
"Size": 1
},
{
"Id": "pikkelyvert",
"Name": "Pikkelyvért",
"Type": "nehez",
"VO": 5,
"Price": 30,
"Size": 3
},
{
"Id": "lancvert",
"Name": "Láncvért",
"Type": "nehez",
"VO": 5,
"Price": 75,
"Size": 2
},
{
"Id": "mellvert",
"Name": "Mellvért, lorica musculata",
"Type": "nehez",
"VO": 5,
"Price": 100,
"Size": 2
},
{
"Id": "szalagvert",
"Name": "Szalagvért, lorica segmentata",
"Type": "nehez",
"VO": 6,
"Price": 250,
"Size": 2
},
{
"Id": "lemezvert",
"Name": "Lemezvért",
"Type": "nehez",
"VO": 7,
"Price": 400,
"Size": 3
}
]
}
@@ -0,0 +1,102 @@
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;
// only at level 5 and 9
tolvajExtraKepzettseg?: KepzettsegId;
// every odd level
harcosFegyver?: string;
// every 3rd level
kalozKritikus?: string;
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})
}
@@ -0,0 +1,83 @@
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[] {
if (attack < 6) {
return [attack]
}
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){
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 ExtraAttack(base)
}
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, 'szintlepesek' | 'tulajdonsagok'>): number[] {
let base = MultiClassTamadasBonusz(karakter.szintlepesek)
return base.map(v => v + Modifier(karakter.tulajdonsagok.t_ugy))
}
@@ -0,0 +1,90 @@
import {d6} from "../../shared/domain-models/kockak";
import {Karakter2E} from "./karakter2E";
import {TulajdonsagokFajjal} from "./faj2E";
export enum Tulajdonsag2E {
Ero = 't_ero',
Ugyesseg = 't_ugy',
Egeszseg = 't_egs',
Intelligencia = 't_int',
Bolcsesseg = 't_bol',
Karizma = 't_kar',
}
export type KarakterTulajdonsagok = Record<Tulajdonsag2E, number>
export const TulajdonsagIDs = [
Tulajdonsag2E.Ero,
Tulajdonsag2E.Ugyesseg,
Tulajdonsag2E.Egeszseg,
Tulajdonsag2E.Intelligencia,
Tulajdonsag2E.Bolcsesseg,
Tulajdonsag2E.Karizma
]
export const TulajdonsagDefaults: KarakterTulajdonsagok = {
t_ero: 10,
t_ugy: 10,
t_egs: 10,
t_int: 10,
t_bol: 10,
t_kar: 10,
}
export function TulajdonsagLabel(tul: Tulajdonsag2E) : string {
switch (tul) {
case Tulajdonsag2E.Bolcsesseg: return 'Bölcsesség';
case Tulajdonsag2E.Ero: return 'Erő';
case Tulajdonsag2E.Ugyesseg: return 'Ügyesség';
case Tulajdonsag2E.Egeszseg: return 'Egészség';
case Tulajdonsag2E.Intelligencia: return 'Intelligencia';
case Tulajdonsag2E.Karizma: return 'Karizma';
}
}
function rollAbility() {
let min = 6;
let sum = 0;
let rolls: number[] = []
for (let i = 0; i < 4; i++) {
const roll = d6();
if (roll < min) {
min = roll;
}
sum += roll;
rolls.push(roll)
}
const result = sum - min;
console.log("Rolled: " + rolls + " got: " + result)
return result;
}
export function RollAllAbilities() : KarakterTulajdonsagok {
const response = {...TulajdonsagDefaults}
for (const key of TulajdonsagIDs) {
response[key] = rollAbility()
}
return response
}
export const Modifier = (val : number) => Math.floor(val / 3) - 3
export function TulajdonsagModosito(tulajdonsagok: KarakterTulajdonsagok): KarakterTulajdonsagok {
let response: KarakterTulajdonsagok = { ...tulajdonsagok }
for (const key of TulajdonsagIDs) {
response[key] = Modifier(tulajdonsagok[key])
}
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,
}
}
@@ -0,0 +1,294 @@
import React, {useContext, useState} from 'react'
import {useLoaderData, useNavigate, useParams} from "react-router-dom";
import {Button, InputGroup, Modal, OverlayTrigger, Toast, ToastContainer} from "react-bootstrap";
import {ChangeLvl1Osztaly, DefaultKarakter, Karakter2E} from "../domain-models/karakter2E";
import {KarakterTulajdonsagok} from "../domain-models/tulajdonsag2E";
import FajSelector2E from "../components/FajSelector2E";
import Tulajdonsagok2E from "../components/Tulajdonsagok2E";
import OsztalySelector2E from "../components/OsztalySelector2E";
import {Osztaly2E} from "../domain-models/osztaly2E";
import {
GetKepzettsegListaN,
GetNumberOfKepzettsegek,
KepzettsegId, SetDefaultKepzettsegek,
SetDefaultTolvajKepzettsegek
} from "../domain-models/kepzettsegek2E";
import KarakterKepzettsegek from "../components/KarakterKepzettsegek2E";
import {TulajdonsagokFajjal} from "../domain-models/faj2E";
import JellemSelector from "../components/JellemSelector";
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";
import saveOverlayTooltip from "../../first-edition/components/SaveOverlayTooltip";
import {UserContext} from "../../shared/contexts/UserContext";
import {
StoreNewCharacter2E,
UpdateCharacter2E
} from "../api/character.api";
import {Faro} from "@grafana/faro-web-sdk";
import Form from "react-bootstrap/Form";
import {CreatePDF} from "../pdf/character.pdf";
import {KarakterInputToPdfView} from "../pdf/karakter_pdf_view";
function CreateCharacter2E(props: {
faro?: Faro
}) {
const {faro} = props
const { id } = useParams();
const fetchedUser = useContext(UserContext);
const loaderData = useLoaderData() as Karakter2E & { isPublic: boolean } | undefined;
let initialKarakterInputs = DefaultKarakter;
if (loaderData != null) {
const { isPublic: t1, ...t2 } = loaderData;
initialKarakterInputs = t2
}
const initialIsPublic = loaderData?.isPublic ?? fetchedUser?.data == null;
const navigate = useNavigate();
const [karakter, setKarakter] = useState(initialKarakterInputs)
const tulajdonsagokFajjal = TulajdonsagokFajjal(karakter.tulajdonsagok, karakter.faj)
const changeKepzettseg = (k: KepzettsegId[]) => setKarakter({...karakter, kepzettsegek: k})
const changeTolvajKepzettseg = (tk?: KepzettsegId[]) => setKarakter({...karakter, tolvajKepzettsegek: tk})
SetDefaultTolvajKepzettsegek(karakter, changeTolvajKepzettseg)
SetDefaultKepzettsegek({...karakter, tulajdonsagok: tulajdonsagokFajjal}, changeKepzettseg)
const changeFelszereles = (f: KarakterFelszereles) => setKarakter({...karakter, felszereles: f})
let [showSaveModal, setShowSaveModal] = useState(false);
let [newId, setNewId] = useState(null as string | null)
let [showSaved, setShowSaved] = useState(false);
let [isPublic, setIsPublic] = useState(initialIsPublic);
const newCharacterUrl = () => `${window.location.origin}/2e/karakter/${newId}`
const handleSaveModalCopyAndClose = async () => {
await navigator.clipboard.writeText(newCharacterUrl());
handleSaveModalClose()
}
const handleSaveModalClose = () => {
setShowSaveModal(false);
navigate(`/2e/karakter/${newId}`)
}
const hideSaved = () => setShowSaved(false);
const onSaveClicked = async () => {
faro?.api.pushEvent('character_stored', {
osztaly: karakter.szintlepesek[0].osztaly.toString(),
szint: karakter.szint.toString(),
faj: karakter.faj.toString(),
is_public: isPublic.toString(),
edition: "2e",
})
if (id == null) {
let recievedId = await StoreNewCharacter2E(karakter, isPublic);
setNewId(recievedId)
setShowSaveModal(true);
} else {
await UpdateCharacter2E(id, karakter, isPublic);
setShowSaved(true);
setTimeout(() => {
hideSaved();
}, 5000)
}
}
return <div>
<div className='container-fluid p-5 bg-black text-white text-center'>
<h1>Karakter {!!id ? "szerkesztése" : "létrehozása"}</h1>
</div>
<ToastContainer className="position-fixed" position="top-end">
<Toast show={showSaved} onClose={hideSaved} bg="success">
<Toast.Header><strong>Karakter mentve!</strong></Toast.Header>
</Toast>
</ToastContainer>
<Modal show={showSaveModal} onHide={handleSaveModalClose}>
<Modal.Header closeButton>
<Modal.Title>Karakter mentve!</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>Karaktered az alábbi {isPublic ? 'publikus' : 'privát'} URLen érhető el.</p>
<p><a href={newCharacterUrl()}>{newCharacterUrl()}</a></p>
<InputGroup className="mb-3">
<Form.Control id="input" value={newCharacterUrl()} />
<Button variant={"outline-dark"} onClick={() => {
let copyText = document.querySelector("#input") as any;
copyText?.select()
document.execCommand("copy");
}}>Másolás</Button>
</InputGroup>
</Modal.Body>
<Modal.Footer>
<Button variant="danger" onClick={handleSaveModalCopyAndClose}>
Másolás és bezárás
</Button>
</Modal.Footer>
</Modal>
<div className='p-3'>
<form onSubmit={async (event) => event.preventDefault()}>
<div className='row'>
<h5 className='col align-self-center'>Származás</h5>
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Név</label>
<input className='col form-control'
value={karakter.nev}
onChange={(e) => setKarakter({...karakter, nev: e.target.value})}/>
{!karakter.nev &&
<span className='form-field-error'>A karaktered nem mászkálhat névtelenül a világban!</span>}
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Nem</label>
<input className='col form-control'
value={karakter.nem}
onChange={(e) => setKarakter({...karakter, nem: e.target.value})}/>
</div>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Kor</label>
<input className='col form-control'
value={karakter.kor}
type={"number"}
onChange={(e) => setKarakter({...karakter, kor: Number(e.target.value)})}/>
</div>
<JellemSelector selected={karakter.jellem}
changeJellem={(val) => setKarakter({...karakter, jellem: val})}/>
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Választott istenség</label>
<input className='col form-control'
value={karakter.isten}
onChange={(e) => setKarakter({...karakter, isten: e.target.value})}/>
</div>
<FajSelector2E
changeFaj={faj => setKarakter({...karakter, faj})}
faj={karakter.faj}
/>
<hr/>
<Tulajdonsagok2E
currentFaj={karakter.faj}
tulajdonsagok={karakter.tulajdonsagok}
changeTulajdonsagok={(tul: KarakterTulajdonsagok) => setKarakter({
...karakter,
tulajdonsagok: tul
})}
/>
<hr/>
<div className='row'>
<h5 className='col align-self-center'>Tanult</h5>
</div>
<OsztalySelector2E
currentFaj={karakter.faj}
currentOsztaly={karakter.szintlepesek[0].osztaly}
changeOsztaly={o => setKarakter(ChangeLvl1Osztaly(karakter, o))}
/>
{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)}
kepzettsegek={karakter.kepzettsegek}
changeKepzettsegek={changeKepzettseg}
tolvajKepzettsegek={karakter.tolvajKepzettsegek || []}
changeTolvajKepzettsegek={changeTolvajKepzettseg}
/>
<hr/>
{/* 1. szinten */}
<MasodlagosErtekek {...CalculateMasodlagosErtekek({
...karakter,
tulajdonsagok: tulajdonsagokFajjal,
szint: 1,
szintlepesek: karakter.szintlepesek.slice(0, 1)
})} />
<hr/>
<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/>
<div className='row'>
<h5 className='col align-self-center'>Véglegesítés</h5>
</div>
{fetchedUser.data != null &&
<div className='row m-2'>
<label className='col-md-2 col-sm-3 col-form-label'>Publikus karakterlap?</label>
<select className="col form-select" value={isPublic.toString()}
onChange={(event) => setIsPublic(event.target.value === "true")}>
<option value="true">Igen</option>
<option value="false">Nem</option>
</select>
</div>
}
<div className="row">
<div className="col-6">
<div className='d-grid gap-2 m-5'>
{fetchedUser.data == null
? <OverlayTrigger
placement='top'
overlay={saveOverlayTooltip}
delay={0}
defaultShow={false}
flip={false}
>
<button
className='btn btn-danger btn-lg'
type='button'
onClick={onSaveClicked}
>
Mentés
</button>
</OverlayTrigger>
: <button
className='btn btn-danger btn-lg'
type='button'
onClick={onSaveClicked}
>
Mentés
</button>
}
</div>
</div>
<div className="col-6">
<div className='d-grid gap-2 m-5'>
<button className='btn btn-danger btn-lg' type='button' onClick={async () => {
faro?.api.pushEvent('character_created', {
osztaly: karakter.szintlepesek[0].osztaly,
szint: karakter.szint.toString(),
faj: karakter.faj.toString(),
})
await CreatePDF(KarakterInputToPdfView(karakter))
}}>PDF</button>
</div>
</div>
</div>
</form>
</div>
</div>
}
export default CreateCharacter2E;
@@ -0,0 +1,90 @@
import {KarakterPdfView} from "./karakter_pdf_view";
import {PDFDocument} from "pdf-lib";
import fontkit from "@pdf-lib/fontkit";
import download from "downloadjs";
import {SignedNumberToText} from "../../shared/components/Helpers";
export async function CreatePDF(karakter: KarakterPdfView) {
const existingPdfBytes = await fetch('/pdfs/kem2karakterlap_form.pdf').then(res => res.arrayBuffer())
const pdfDoc = await PDFDocument.load(existingPdfBytes)
pdfDoc.registerFontkit(fontkit)
const fontBytes = await fetch('/fonts/Milonga-Regular.ttf').then(res => res.arrayBuffer())
const pdfFont = await pdfDoc.embedFont(fontBytes)
const form = pdfDoc.getForm()
const osztalyok = karakter.Osztaly.length > 1 ? karakter.Osztaly.map(x => `${x.osztaly} (${x.szint})`).join(", ") : karakter.Osztaly[0].osztaly
form.getTextField("nev_faj_osztaly").setText(`${karakter.Nev}, ${karakter.Faj} ${osztalyok}`)
form.getTextField("szint").setText((karakter.SzintTotal.length > 1 ? " " : " ") + karakter.SzintTotal)
form.getTextField("jellem").setText(karakter.Jellem)
form.getTextField("istenseg").setText(karakter.Isten)
form.getTextField("vo").setText(karakter.VO.toString())
form.getTextField("pancel").setText(" " + karakter.PancelVO)
form.getTextField("pajzs").setText(" " + karakter.PajzsVO)
form.getTextField("egyeb_vo").setText(" " + karakter.EgyebVO)
form.getTextField("tul_ero").setText(` ${karakter.Tulajdonsagok.t_ero}`)
form.getTextField("tul_ero_mod").setText(SignedNumberToText(karakter.TulajdonsagModositok.t_ero))
form.getTextField("tul_ugy").setText(` ${karakter.Tulajdonsagok.t_ugy}`)
form.getTextField("tul_ugy_mod").setText(SignedNumberToText(karakter.TulajdonsagModositok.t_ugy))
form.getTextField("tul_egs").setText(` ${karakter.Tulajdonsagok.t_egs}`)
form.getTextField("tul_egs_mod").setText(SignedNumberToText(karakter.TulajdonsagModositok.t_egs))
form.getTextField("tul_int").setText(` ${karakter.Tulajdonsagok.t_int}`)
form.getTextField("tul_int_mod").setText(SignedNumberToText(karakter.TulajdonsagModositok.t_int))
form.getTextField("tul_bol").setText(` ${karakter.Tulajdonsagok.t_bol}`)
form.getTextField("tul_bol_mod").setText(SignedNumberToText(karakter.TulajdonsagModositok.t_bol))
form.getTextField("tul_kar").setText(` ${karakter.Tulajdonsagok.t_kar}`)
form.getTextField("tul_kar_mod").setText(SignedNumberToText(karakter.TulajdonsagModositok.t_kar))
form.getTextField("hp_rolls").setText(karakter.HPszintenkent.join(", "))
form.getTextField("hp_aktualis").setText(karakter.HP.toString())
form.getTextField("kitartas_alap").setText(" " + karakter.MentokAlap.kitartas.toString())
form.getTextField("kitartas_mod").setText(" " + SignedNumberToText(karakter.MentoModositok.kitartas))
form.getTextField("kitartas_sum").setText(" " + karakter.MentokModositokkal.kitartas.toString())
form.getTextField("reflex_alap").setText(" " + karakter.MentokAlap.reflex.toString())
form.getTextField("reflex_mod").setText(" " + SignedNumberToText(karakter.MentoModositok.reflex))
form.getTextField("reflex_sum").setText(" " + karakter.MentokModositokkal.reflex.toString())
form.getTextField("akaratero_alap").setText(" " + karakter.MentokAlap.akaratero.toString())
form.getTextField("akaratero_mod").setText(" " + SignedNumberToText(karakter.MentoModositok.akaratero))
form.getTextField("akaratero_sum").setText(" " + karakter.MentokModositokkal.akaratero.toString())
form.getTextField("kezdemenyezes").setText(" " + SignedNumberToText(karakter.Kezdemenyezes))
form.getTextField("tamadas_bonusz_alap").setText(karakter.TamadasBonuszAlap.join("/"))
form.getTextField("tamadas_bonusz_ero").setText(" " + SignedNumberToText(karakter.TulajdonsagModositok.t_ero))
form.getTextField("tamadas_bonusz_kozelharc").setText(karakter.KozelharciTB.join("/"))
form.getTextField("tamadas_bonusz_ugy").setText(" " + SignedNumberToText(karakter.TulajdonsagModositok.t_ugy))
form.getTextField("tamadas_bonusz_tavolsagi").setText(karakter.CelzoTB.join("/"))
for (let i = 0; i < karakter.Kepzettsegek.length; i++) {
const kepzettseg = karakter.Kepzettsegek[i]
form.getTextField(`kepzettseg_${i}_nev`).setText(kepzettseg.nev)
form.getTextField(`kepzettseg_${i}_alap`).setText(kepzettseg.alap)
form.getTextField(`kepzettseg_${i}_bonusz`).setText(kepzettseg.bonusz)
form.getTextField(`kepzettseg_${i}_osszes`).setText(kepzettseg.osszes)
}
for (let i = 0; i < karakter.Fegyverek.length; i++) {
form.getTextField(`fegyver_${i+1}`).setText(karakter.Fegyverek[i])
}
for (let i = 0; i < karakter.FelszerelesViselt.length && i < 8; i++) {
form.getTextField(`viselt_${i}`).setText(karakter.FelszerelesViselt[i])
}
for (let i = 0; i < karakter.FelszerelesCipelt.length && i < 8; i++) {
form.getTextField(`cipelt_${i}`).setText(karakter.FelszerelesCipelt[i])
}
for (let i = 0; i < karakter.FelszerelesAprosagok.length && i < 8; i++) {
form.getTextField(`aprosagok_${i}`).setText(karakter.FelszerelesAprosagok[i])
}
form.updateFieldAppearances(pdfFont)
const pdfBytes = await pdfDoc.save();
download(pdfBytes, `${karakter.Nev}_${karakter.SzintTotal}.pdf`, "application/pdf");
}
@@ -0,0 +1,176 @@
import {Modifier, TulajdonsagokTotal} from "../domain-models/tulajdonsag2E";
import {Mentok, MentoModositok, MultiClassMentokAlap} from "../domain-models/mentok";
import {CalculateMasodlagosErtekek, VOegyeb} from "../domain-models/masodlagos_ertekek";
import {FajLabel} from "../domain-models/faj2E";
import {Osztaly2E, OsztalyLabel} from "../domain-models/osztaly2E";
import {CelzoTB, KozelharciTB, MultiClassTamadasBonusz} from "../domain-models/tamadas_bonusz";
import {Karakter2E} from "../domain-models/karakter2E";
import {GetJellem} from "../domain-models/jellem";
import {GetFegyver, GetPajzs, GetPancel} from '../domain-models/felszereles'
import {
KarakterTulajdonsagok,
TulajdonsagModosito
} from "../domain-models/tulajdonsag2E";
import {SignedNumberToText} from "../../shared/components/Helpers";
import {Kepzettseg, Kepzettsegek} from "../domain-models/kepzettsegek2E";
import {getClassLevels} from "../domain-models/szintlepes";
import {FegyverFlags} from "../components/FegyverSelector";
interface KepzettsegPdfView {
nev: string,
alap: string,
bonusz: string,
osszes: string
}
export interface KarakterPdfView {
Nev: string
Isten: string
Jellem: string
Faj: string
Osztaly: {osztaly: string, szint: number}[]
Tulajdonsagok: KarakterTulajdonsagok,
TulajdonsagModositok: KarakterTulajdonsagok,
SzintTotal: string
HP: number
HPszintenkent: number[]
VO: number
PancelVO: string
PajzsVO: string
EgyebVO: string
Kezdemenyezes: number
TamadasBonuszAlap: number[]
KozelharciTB: number[]
CelzoTB: number[]
MentokAlap: Mentok
MentoModositok: Mentok
MentokModositokkal: Mentok,
Kepzettsegek: KepzettsegPdfView[],
Fegyverek: string[],
FelszerelesViselt: string[],
FelszerelesCipelt: string[],
FelszerelesAprosagok: string[],
}
export function KarakterInputToPdfView(karakter: Karakter2E): KarakterPdfView {
const tulajdonsagok = TulajdonsagokTotal(karakter)
karakter = {...karakter, tulajdonsagok}
const tulajdonsagModositok = TulajdonsagModosito(tulajdonsagok)
const masodlagosErtekek = CalculateMasodlagosErtekek(karakter)
const pancel = GetPancel(karakter.felszereles.pancelID)
const pajzs = GetPajzs(karakter.felszereles.pajzsID)
const kezdemenyezes = tulajdonsagModositok.t_ugy + (karakter.szintlepesek.some(x => x.osztaly === Osztaly2E.Tolvaj) ? 4 : 0)
const mentok = MultiClassMentokAlap(karakter.szintlepesek)
const mentoModositok = MentoModositok(tulajdonsagModositok)
const osztalyok = new Array(...(new Set(karakter.szintlepesek.map(x => x.osztaly))))
const osztaly = osztalyok.map(x => ({
osztaly: OsztalyLabel(x),
szint: karakter.szintlepesek.filter(sz => sz.osztaly === x).length
}))
const classLevels = getClassLevels(karakter.szintlepesek)
let felszereles = [] as string[]
if(pancel != null){
felszereles = felszereles.concat(FelszerelesToPdfView(pancel))
}
if(pajzs != null){
felszereles = felszereles.concat(FelszerelesToPdfView(pajzs))
}
felszereles = felszereles.concat(karakter.felszereles.fegyverIDk.map(x => {
const fegyver = GetFegyver(x)
if (fegyver == null) {
console.error("unknown fegyver: " + x)
return []
}
return FelszerelesToPdfView(fegyver)
}).reduce((acc, item) => acc.concat(item), [] as string[]))
return {
Faj: FajLabel(karakter.faj).toLowerCase(),
Isten: karakter.isten || "",
Jellem: GetJellem(karakter.jellem).Label,
Nev: karakter.nev,
Tulajdonsagok: karakter.tulajdonsagok,
Osztaly: osztaly,
SzintTotal: karakter.szint.toString(),
TulajdonsagModositok: tulajdonsagModositok,
HP: masodlagosErtekek.HP,
HPszintenkent: karakter.szintlepesek.map(x => x.HProll),
Kezdemenyezes: kezdemenyezes,
TamadasBonuszAlap: MultiClassTamadasBonusz(karakter.szintlepesek),
KozelharciTB: KozelharciTB(karakter),
CelzoTB: CelzoTB(karakter),
MentoModositok: mentoModositok,
MentokAlap: mentok,
MentokModositokkal: masodlagosErtekek.Mentok,
VO: masodlagosErtekek.VO,
PajzsVO: SignedNumberToText(pajzs?.VO),
PancelVO: SignedNumberToText(pancel?.VO),
EgyebVO: SignedNumberToText(VOegyeb(karakter)),
Kepzettsegek:
karakter.kepzettsegek.map(k =>
mapKepzettsegToPdfView(Kepzettsegek[k], tulajdonsagok, karakter.szint))
.concat(karakter.tolvajKepzettsegek?.map(k =>
mapKepzettsegToPdfView(Kepzettsegek[k], tulajdonsagok, classLevels[Osztaly2E.Tolvaj])) || []),
Fegyverek: karakter.felszereles.fegyverIDk.slice(0, 4).map(fid => {
const fegyver = GetFegyver(fid)
if (fegyver == null) {
console.error("unknown fegyver: " + fid)
return ""
}
const serializedFlags = FegyverFlags(fegyver);
const critStart = fegyver.CritRangeStart - karakter.szintlepesek.filter(x => x.kalozKritikus === fegyver.Id).length
const damageBonus = fegyver.DamageBonus +
(fegyver.KetkezesBonusz ? 1 : 0) +
(fegyver.Type !== "lofegyver" || fegyver.EroBonusz ? tulajdonsagModositok.t_ero : 0) +
(karakter.szintlepesek.filter(x => x.harcosFegyver === fegyver.Id).length * 2)
return `${fegyver.Name} ${serializedFlags.length > 0 ? serializedFlags + " " : ''} ${
fegyver.DamageMultiplier > 1 ? `${fegyver.DamageMultiplier}*` : ''}${
fegyver.NumberOfDamageDice}d${fegyver.DamageDice}${damageBonus !== 0 ? `${
SignedNumberToText(damageBonus)}` : ''} ${
critStart < 20 ? `${critStart}-20` : '20'}x${fegyver.CritMultiplier}`
}),
FelszerelesViselt: felszereles,
FelszerelesCipelt: [],
FelszerelesAprosagok: []
}
}
interface FelszerelesPdf {
Name: string,
Size: number,
}
function FelszerelesToPdfView(item: FelszerelesPdf): string[] {
const ans = [item.Name]
for (let i = 1; i < item.Size; i ++) {
ans.push("+")
}
return ans
}
function mapKepzettsegToPdfView(kepzettseg: Kepzettseg, tulajdonsagok: KarakterTulajdonsagok, szint: number): KepzettsegPdfView {
const tulajdonsagErtek = kepzettseg.Tulajdonsag.reduce((maxTulajdonsagErtek, currentTulajdonsag) =>
tulajdonsagok[currentTulajdonsag] > maxTulajdonsagErtek
? tulajdonsagok[currentTulajdonsag]
: maxTulajdonsagErtek,
tulajdonsagok[kepzettseg.Tulajdonsag[0]])
return {
nev: kepzettseg.Name,
alap: szint.toString(),
bonusz: SignedNumberToText(Modifier(tulajdonsagErtek)),
osszes: (szint + Modifier(tulajdonsagErtek)).toString(),
}
}
+16
View File
@@ -0,0 +1,16 @@
import * as React from "react";
function ErrorBoundary(props: {}) {
return <div>
<div className="container-fluid p-5 bg-danger text-white text-center">
<h1>Valami hiba történt :(</h1>
</div>
<div className="p-5 text-center">
<p>Töltsd újra az oldalt, és ha az sem segít lépj kapcsolatba a fejlesztővel a <a href="mailto:developer@kemkas.hu">developer@kemkas.hu</a> email címen!</p>
<p>Kérlek vedd figyelembe, hogy ez egy hobby projekt limitált erőforrásokkal.</p>
</div>
</div>
}
export default ErrorBoundary;
+15
View File
@@ -0,0 +1,15 @@
import * as React from "react";
function Footer() {
return <footer>
<div className="text-center p-4" style={{backgroundColor: "rgba(0, 0, 0, 0.05)"}}>
A Kard és Mágiát Lux Gábor alkotta. A szerzői jogok őt illetik. Ez az oldal egy "rajongói
hozzájárulás", célja a Kard és Mágia népszerűsítése. Ha többet akarsz tudni a Kard és Mágiáról
látogass el a <a href="https://fomalhaut.lfg.hu/">hivatalos honlapjára</a>. A Kard és Mágia az Open
Gaming Licence 1.0a alatt lett kiadva, melynek szövege <a href="ogl.html">itt található</a>
</div>
</footer>
}
export default Footer;
+51
View File
@@ -0,0 +1,51 @@
import * as React from "react";
import {useContext} from "react";
import axios from "axios";
import {Container, Nav, Navbar} from "react-bootstrap";
import { UserContext } from "./contexts/UserContext";
async function logout() {
await axios.post("/Identity/Account/Logout")
document.location.reload()
}
function Header(props: {}) {
const fetchedUser = useContext(UserContext);
return <header>
<Navbar expand="sm" className="navbar-light bg-white border-bottom box-shadow mb-3">
<Container className="ps-4 pe-4">
<Navbar.Brand href="/">Kemkas</Navbar.Brand>
{fetchedUser.data != null && <Nav.Link className="text-dark" href="/karaktereim">Karaktereim</Nav.Link>}
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav" className="d-sm-inline-flex flex-sm-row-reverse">
<Nav>
{fetchedUser.state !== "finished" ? "Loading..." :
fetchedUser.data == null
? <>
<li className="nav-item">
<a className="nav-link text-dark" href="/Identity/Account/Register">Register</a>
</li>
<li className="nav-item">
<a className="nav-link text-dark" href="/Identity/Account/Login">Login</a>
</li>
</>
: <>
<li className="nav-item">
<a className="nav-link text-dark"
href="/Identity/Account/Manage">Hello {fetchedUser.data}!</a>
</li>
<li className="nav-item">
<button className="nav-link nav text-dark" onClick={logout}>Logout</button>
</li>
</>
}
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
</header>
}
export default Header;
@@ -0,0 +1,19 @@
import * as React from "react";
import {CharacterListItemViewModel} from "../pages/CharacterList";
function CharacterListItem(props: {
character: CharacterListItemViewModel,
}) {
const {character:c} = props
return <>
<td><a href={`/${c.edition}/karakter/${c.id}`}>{c.name}</a></td>
<td>{c.szint}. szintű</td>
<td>{c.faj}</td>
<td>{c.osztaly}</td>
<td>{c.edition}</td>
</>
}
export default CharacterListItem;
@@ -0,0 +1,3 @@
export function SignedNumberToText(val?: number) : string {
return (val === null || val === undefined) ? "" : (val === 0 ? " 0" : (val > 0 ? '+'+val : val.toString()))
}
@@ -0,0 +1,10 @@
import { Fetched } from "../models/Fetched";
import React from "react";
export const userDefaults: Fetched<string | null> = {
state: "not-started",
data: null,
}
export const UserContext = React.createContext(userDefaults)
UserContext.displayName = "UserEmailContext";
@@ -0,0 +1,8 @@
export function dAny(sides: number) {
return Math.floor(Math.random() * sides) + 1;
}
export function d6() {
return dAny(6)
}

Some files were not shown because too many files have changed in this diff Show More