Merge branch 'master' into 012_equipped_flag
This commit is contained in:
commit
c2c0b63b9c
35 changed files with 1110 additions and 125 deletions
|
@ -1,26 +1,65 @@
|
|||
export interface DS4ActorDataType {
|
||||
attributes: DS4ActorDataAttributes;
|
||||
traits: DS4ActorDataTraits;
|
||||
combatValues: DS4ActorDataCombatValues;
|
||||
baseInfo: DS4ActorDataBaseInfo;
|
||||
progression: DS4ActorDataProgression;
|
||||
}
|
||||
|
||||
interface DS4ActorDataAttributes {
|
||||
body: BodyAttribute;
|
||||
mobility: ExtensibleData<number>;
|
||||
mind: ExtensibleData<number>;
|
||||
mobility: ModifiableData<number>;
|
||||
mind: ModifiableData<number>;
|
||||
}
|
||||
|
||||
interface ExtensibleData<T> {
|
||||
initial: T;
|
||||
export interface ModifiableData<T> {
|
||||
base: T;
|
||||
mod: T;
|
||||
total?: T;
|
||||
}
|
||||
|
||||
interface UsableResource<T> {
|
||||
total: T;
|
||||
used: T;
|
||||
}
|
||||
|
||||
interface CurrentData<T> extends ModifiableData<T> {
|
||||
current: T;
|
||||
}
|
||||
|
||||
// Blueprint in case we need more detailed differentiation
|
||||
type BodyAttribute = ExtensibleData<number>;
|
||||
type BodyAttribute = ModifiableData<number>;
|
||||
|
||||
interface DS4ActorDataTraits {
|
||||
strength: ExtensibleData<number>;
|
||||
constitution: ExtensibleData<number>;
|
||||
agility: ExtensibleData<number>;
|
||||
dexterity: ExtensibleData<number>;
|
||||
intellect: ExtensibleData<number>;
|
||||
aura: ExtensibleData<number>;
|
||||
strength: ModifiableData<number>;
|
||||
constitution: ModifiableData<number>;
|
||||
agility: ModifiableData<number>;
|
||||
dexterity: ModifiableData<number>;
|
||||
intellect: ModifiableData<number>;
|
||||
aura: ModifiableData<number>;
|
||||
}
|
||||
|
||||
interface DS4ActorDataCombatValues {
|
||||
hitPoints: CurrentData<number>;
|
||||
defense: ModifiableData<number>;
|
||||
initiative: ModifiableData<number>;
|
||||
movement: ModifiableData<number>;
|
||||
meleeAttack: ModifiableData<number>;
|
||||
rangedAttack: ModifiableData<number>;
|
||||
spellcasting: ModifiableData<number>;
|
||||
targetedSpellcasting: ModifiableData<number>;
|
||||
}
|
||||
|
||||
interface DS4ActorDataBaseInfo {
|
||||
race: string;
|
||||
class: string;
|
||||
heroClass: string;
|
||||
racialAbilities: string;
|
||||
}
|
||||
|
||||
interface DS4ActorDataProgression {
|
||||
level: number;
|
||||
experiencePoints: number;
|
||||
talentPoints: UsableResource<number>;
|
||||
progressPoints: UsableResource<number>;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ export class DS4ActorSheet extends ActorSheet<DS4ActorDataType, DS4Actor, DS4Ite
|
|||
return mergeObject(super.defaultOptions, {
|
||||
classes: ["ds4", "sheet", "actor"],
|
||||
template: "systems/ds4/templates/actor/actor-sheet.hbs",
|
||||
width: 600,
|
||||
width: 725,
|
||||
height: 600,
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }],
|
||||
});
|
||||
|
|
|
@ -1,43 +1,22 @@
|
|||
import { DS4Item } from "../item/item";
|
||||
import { DS4ItemDataType } from "../item/item-data";
|
||||
import { DS4ActorDataType } from "./actor-data";
|
||||
import { DS4ActorDataType, ModifiableData } from "./actor-data";
|
||||
|
||||
/**
|
||||
* Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system.
|
||||
* @extends {Actor}
|
||||
*/
|
||||
export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, DS4Item> {
|
||||
/** @override */
|
||||
prepareDerivedData(): void {
|
||||
const data = this.data;
|
||||
this._prepareCombatValues(data);
|
||||
}
|
||||
|
||||
private _prepareCombatValues(data: ActorData<DS4ActorDataType>): void {
|
||||
const hitPointsModifier = getProperty(data, "data.combatValues.hitPoints.modifier") || 0;
|
||||
const actorData = data.data;
|
||||
setProperty(
|
||||
data,
|
||||
"data.combatValues.hitPoints.max",
|
||||
actorData.attributes.body.initial + actorData.traits.constitution.initial + 10 + hitPointsModifier,
|
||||
const attributes = data.data.attributes;
|
||||
Object.values(attributes).forEach(
|
||||
(attribute: ModifiableData<number>) => (attribute.total = attribute.base + attribute.mod),
|
||||
);
|
||||
|
||||
const defenseModifier = getProperty(data, "data.combatValues.defense.modifier") || 0;
|
||||
setProperty(
|
||||
data,
|
||||
"data.combatValues.defense.value",
|
||||
actorData.attributes.body.initial +
|
||||
actorData.traits.constitution.initial +
|
||||
this._getArmorValue() +
|
||||
defenseModifier,
|
||||
);
|
||||
}
|
||||
const traits = data.data.traits;
|
||||
Object.values(traits).forEach((trait: ModifiableData<number>) => (trait.total = trait.base + trait.mod));
|
||||
|
||||
private _getArmorValue(): number {
|
||||
return this.data["items"]
|
||||
.filter((item) => ["armor", "shield"].includes(item.type))
|
||||
.filter((item) => item.data.equipped)
|
||||
.map((item) => item.data.armorValue)
|
||||
.reduce((a, b) => a + b, 0);
|
||||
const combatValues = data.data.combatValues;
|
||||
Object.values(combatValues).forEach(
|
||||
(combatValue: ModifiableData<number>) => (combatValue.total = combatValue.base + combatValue.mod),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ export const DS4 = {
|
|||
|
||||
/**
|
||||
* Define the set of acttack types that can be performed with weapon items
|
||||
* @type {Object}
|
||||
*/
|
||||
attackTypes: {
|
||||
melee: "DS4.AttackTypeMelee",
|
||||
|
@ -19,8 +18,7 @@ export const DS4 = {
|
|||
},
|
||||
|
||||
/**
|
||||
* * Define the file paths to icon images
|
||||
* @type {Object}
|
||||
* Define the file paths to icon images
|
||||
*/
|
||||
attackTypesIcons: {
|
||||
melee: "systems/ds4/assets/DS4-MAT.png",
|
||||
|
@ -30,7 +28,6 @@ export const DS4 = {
|
|||
|
||||
/**
|
||||
* Define the set of item availabilties
|
||||
* @type {Object}
|
||||
*/
|
||||
itemAvailabilities: {
|
||||
unset: "DS4.ItemAvailabilityUnset",
|
||||
|
@ -43,8 +40,7 @@ export const DS4 = {
|
|||
},
|
||||
|
||||
/**
|
||||
* * Define the set of item types
|
||||
* @type {Object}
|
||||
* Define the set of item types
|
||||
*/
|
||||
itemTypes: {
|
||||
weapon: "DS4.ItemTypeWeapon",
|
||||
|
@ -55,8 +51,7 @@ export const DS4 = {
|
|||
},
|
||||
|
||||
/**
|
||||
* * Define the set of armor types, a character may only wear one item of each at any given time
|
||||
* @type {Object}
|
||||
* Define the set of armor types, a character may only wear one item of each at any given time
|
||||
*/
|
||||
armorTypes: {
|
||||
body: "DS4.ArmorTypeBody",
|
||||
|
@ -67,8 +62,7 @@ export const DS4 = {
|
|||
},
|
||||
|
||||
/**
|
||||
* * Define abbreviations for the armor types
|
||||
* @type {Object}
|
||||
* Define abbreviations for the armor types
|
||||
*/
|
||||
armorTypesAbbr: {
|
||||
body: "DS4.ArmorTypeBodyAbbr",
|
||||
|
@ -79,8 +73,7 @@ export const DS4 = {
|
|||
},
|
||||
|
||||
/**
|
||||
* * Define the set of armor materials, used to determine if a characer may wear the armor without additional penalties
|
||||
* @type {Object}
|
||||
* Define the set of armor materials, used to determine if a characer may wear the armor without additional penalties
|
||||
*/
|
||||
armorMaterialTypes: {
|
||||
cloth: "DS4.ArmorMaterialTypeCloth",
|
||||
|
@ -90,8 +83,7 @@ export const DS4 = {
|
|||
},
|
||||
|
||||
/**
|
||||
* * Define the abbreviations of armor materials
|
||||
* @type {Object}
|
||||
* Define the abbreviations of armor materials
|
||||
*/
|
||||
armorMaterialTypesAbbr: {
|
||||
cloth: "DS4.ArmorMaterialTypeClothAbbr",
|
||||
|
@ -99,4 +91,59 @@ export const DS4 = {
|
|||
chain: "DS4.ArmorMaterialTypeChainAbbr",
|
||||
plate: "DS4.ArmorMaterialTypePlateAbbr",
|
||||
},
|
||||
|
||||
/**
|
||||
* Define the set of attributes a character has
|
||||
*/
|
||||
attributes: {
|
||||
body: "DS4.AttributeBody",
|
||||
mobility: "DS4.AttributeMobility",
|
||||
mind: "DS4.AttributeMind",
|
||||
},
|
||||
|
||||
/**
|
||||
* Define the set of traits a character has
|
||||
*/
|
||||
traits: {
|
||||
strength: "DS4.TraitStrength",
|
||||
constitution: "DS4.TraitConstitution",
|
||||
agility: "DS4.TraitAgility",
|
||||
dexterity: "DS4.TraitDexterity",
|
||||
intellect: "DS4.TraitIntellect",
|
||||
aura: "DS4.TraitAura",
|
||||
},
|
||||
|
||||
/**
|
||||
* Define the set of combat values a character has
|
||||
*/
|
||||
combatValues: {
|
||||
hitPoints: "DS4.CombatValuesHitPoints",
|
||||
defense: "DS4.CombatValuesDefense",
|
||||
initiative: "DS4.CombatValuesInitiative",
|
||||
movement: "DS4.CombatValuesMovement",
|
||||
meleeAttack: "DS4.CombatValuesMeleeAttack",
|
||||
rangedAttack: "DS4.CombatValuesRangedAttack",
|
||||
spellcasting: "DS4.CombatValuesSpellcasting",
|
||||
targetedSpellcasting: "DS4.CombatValuesTargetedSpellcasting",
|
||||
},
|
||||
|
||||
/**
|
||||
* Define the base info of a character
|
||||
*/
|
||||
baseInfo: {
|
||||
race: "DS4.BaseInfoRace",
|
||||
class: "DS4.BaseInfoClass",
|
||||
heroClass: "DS4.BaseInfoHeroClass",
|
||||
racialAbilities: "DS4.BaseInfoRacialAbilities",
|
||||
},
|
||||
|
||||
/**
|
||||
* Definme the progression info of a character
|
||||
*/
|
||||
progression: {
|
||||
level: "DS4.ProgressionLevel",
|
||||
experiencePoints: "DS4.ProgressionExperiencePoints",
|
||||
talentPoints: "DS4.ProgressionTalentPoints",
|
||||
progressPoints: "DS4.ProgressionProgressPoints",
|
||||
},
|
||||
};
|
||||
|
|
|
@ -58,6 +58,12 @@ Hooks.once("setup", function () {
|
|||
"armorTypesAbbr",
|
||||
"armorMaterialTypes",
|
||||
"armorMaterialTypesAbbr",
|
||||
"armorMaterialTypes",
|
||||
"attributes",
|
||||
"traits",
|
||||
"combatValues",
|
||||
"baseInfo",
|
||||
"progression",
|
||||
];
|
||||
|
||||
// Exclude some from sorting where the default order matters
|
||||
|
|
99
src/module/rolls/roll-executor.ts
Normal file
99
src/module/rolls/roll-executor.ts
Normal file
|
@ -0,0 +1,99 @@
|
|||
import { DS4RollProvider, RollProvider } from "./roll-provider";
|
||||
|
||||
export function ds4test(testValue: number, rollOptions: RollOptions = new RollOptions()): RollResult {
|
||||
const finalRollValue = testValue;
|
||||
if (finalRollValue <= 20) {
|
||||
return rollCheckSingleDie(finalRollValue, rollOptions);
|
||||
} else {
|
||||
return rollCheckMultipleDice(finalRollValue, rollOptions);
|
||||
}
|
||||
}
|
||||
|
||||
export function rollCheckSingleDie(
|
||||
testValue: number,
|
||||
rollOptions: RollOptions,
|
||||
provider: RollProvider = new DS4RollProvider(),
|
||||
): RollResult {
|
||||
const roll = provider.getNextRoll();
|
||||
const dice = [roll];
|
||||
|
||||
if (roll <= rollOptions.maxCritSucc) {
|
||||
return new RollResult(testValue, RollResultStatus.CRITICAL_SUCCESS, dice);
|
||||
} else if (roll >= rollOptions.minCritFail) {
|
||||
return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, dice);
|
||||
} else {
|
||||
if (roll <= testValue) {
|
||||
return new RollResult(roll, RollResultStatus.SUCCESS, dice);
|
||||
} else {
|
||||
return new RollResult(0, RollResultStatus.FAILURE, dice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function rollCheckMultipleDice(
|
||||
testValue: number,
|
||||
rollOptions: RollOptions,
|
||||
provider: RollProvider = new DS4RollProvider(),
|
||||
): RollResult {
|
||||
const finalCheck = testValue % 20;
|
||||
const numberOfDice = Math.ceil(testValue / 20);
|
||||
|
||||
const dice = provider.getNextRolls(numberOfDice);
|
||||
|
||||
const firstResult = dice[0];
|
||||
|
||||
if (firstResult >= rollOptions.minCritFail) {
|
||||
return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, dice);
|
||||
}
|
||||
|
||||
const partitionCallback = (prev: [Array<number>, Array<number>], cur: number) => {
|
||||
if (cur <= rollOptions.maxCritSucc) {
|
||||
prev[0].push(cur);
|
||||
} else {
|
||||
prev[1].push(cur);
|
||||
}
|
||||
return prev;
|
||||
};
|
||||
|
||||
const [critSuccesses, otherRolls] = dice
|
||||
.reduce(partitionCallback, [[], []])
|
||||
.map((a) => a.sort((r1, r2) => r2 - r1));
|
||||
|
||||
const sortedRollResults: Array<number> = critSuccesses.concat(otherRolls);
|
||||
|
||||
const evaluationResult = sortedRollResults
|
||||
.map((value, index) => {
|
||||
if (index == numberOfDice - 1) {
|
||||
if (value == 1) {
|
||||
return finalCheck;
|
||||
} else {
|
||||
return value <= finalCheck ? value : 0;
|
||||
}
|
||||
} else {
|
||||
if (value <= rollOptions.maxCritSucc) {
|
||||
return 20;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
})
|
||||
.reduce((a, b) => a + b);
|
||||
|
||||
return new RollResult(evaluationResult, RollResultStatus.SUCCESS, sortedRollResults);
|
||||
}
|
||||
|
||||
export class RollOptions {
|
||||
public maxCritSucc = 1;
|
||||
public minCritFail = 20;
|
||||
}
|
||||
|
||||
export class RollResult {
|
||||
constructor(public value: number, public status: RollResultStatus, public dice: Array<number>) {}
|
||||
}
|
||||
|
||||
export enum RollResultStatus {
|
||||
FAILURE,
|
||||
SUCCESS,
|
||||
CRITICAL_FAILURE,
|
||||
CRITICAL_SUCCESS,
|
||||
}
|
16
src/module/rolls/roll-provider.ts
Normal file
16
src/module/rolls/roll-provider.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
export class DS4RollProvider {
|
||||
getNextRoll(): number {
|
||||
return new Roll("1d20").roll().total;
|
||||
}
|
||||
|
||||
getNextRolls(amount: number): Array<number> {
|
||||
return Array(amount)
|
||||
.fill(0)
|
||||
.map(() => this.getNextRoll());
|
||||
}
|
||||
}
|
||||
|
||||
export interface RollProvider {
|
||||
getNextRoll(): number;
|
||||
getNextRolls(number: number): Array<number>;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue