Added very basic testing, got attribute calculations working
This commit is contained in:
@@ -54,3 +54,5 @@ dynamic-import@0.4.0
|
||||
ddp-rate-limiter@1.0.7
|
||||
rate-limit@1.0.9
|
||||
iron:router
|
||||
meteortesting:mocha
|
||||
mdg:validated-method
|
||||
|
||||
@@ -65,13 +65,19 @@ lai:collection-extensions@0.2.1_1
|
||||
launch-screen@1.1.1
|
||||
less@2.7.12
|
||||
livedata@1.0.18
|
||||
lmieulet:meteor-coverage@1.1.4
|
||||
localstorage@1.2.0
|
||||
logging@1.1.20
|
||||
matb33:collection-hooks@0.8.4
|
||||
mdg:validated-method@1.1.0
|
||||
mdg:validation-error@0.5.1
|
||||
meteor@1.9.2
|
||||
meteor-base@1.4.0
|
||||
meteorhacks:picker@1.0.3
|
||||
meteorhacks:subs-manager@1.6.4
|
||||
meteortesting:browser-tests@1.0.0
|
||||
meteortesting:mocha@1.0.1
|
||||
meteortesting:mocha-core@1.0.1
|
||||
minifier-css@1.3.1
|
||||
minifier-js@2.3.5
|
||||
minimongo@1.4.4
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// TODO make sure all attributes can only have lowercase, stripped, no spaced names
|
||||
// TODO make sure proficiencies are indexed by type
|
||||
// TODO skills rely on an ability's modifier
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
|
||||
const recomputeCharacter = new ValidatedMethod({
|
||||
|
||||
"Characters.methods.recomputeCharacter", // DDP method name
|
||||
name: "Characters.methods.recomputeCharacter", // DDP method name
|
||||
|
||||
validate: new SimpleSchema({
|
||||
charId: { type: String }
|
||||
@@ -12,6 +14,7 @@ const recomputeCharacter = new ValidatedMethod({
|
||||
applyOptions: {
|
||||
noRetry: true,
|
||||
},
|
||||
|
||||
run({ charId }) {
|
||||
// `this` is the same method invocation object you normally get inside
|
||||
// Meteor.methods
|
||||
@@ -23,7 +26,7 @@ const recomputeCharacter = new ValidatedMethod({
|
||||
|
||||
computeCharacterById(charId);
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
@@ -76,6 +79,8 @@ const buildCharacter = function (charId){
|
||||
atts: {},
|
||||
skills: {},
|
||||
dms: {},
|
||||
classes: {},
|
||||
level: 0,
|
||||
};
|
||||
// Fetch the attributes of the character and add them to an object for quick lookup
|
||||
Attributes.find({charId}).forEach(attribute => {
|
||||
@@ -83,12 +88,12 @@ const buildCharacter = function (charId){
|
||||
char.atts[attribute.name] = {
|
||||
computed: false,
|
||||
busyComputing: false,
|
||||
type: "attribute";
|
||||
type: "attribute",
|
||||
result: 0,
|
||||
mod: 0, // The resulting modifier if this is an ability
|
||||
base: 0,
|
||||
add: 0,
|
||||
mul: 0,
|
||||
mul: 1,
|
||||
min: Number.NEGATIVE_INFINITY,
|
||||
max: Number.POSITIVE_INFINITY,
|
||||
effects: [],
|
||||
@@ -102,11 +107,12 @@ const buildCharacter = function (charId){
|
||||
char.skills[skill.name] = {
|
||||
computed: false,
|
||||
busyComputing: false,
|
||||
type: "skill";
|
||||
type: "skill",
|
||||
ability: skill.ability,
|
||||
result: 0, // For skills the result is the skillMod
|
||||
proficiency: 0,
|
||||
add: 0,
|
||||
mul: 0,
|
||||
mul: 1,
|
||||
min: Number.NEGATIVE_INFINITY,
|
||||
max: Number.POSITIVE_INFINITY,
|
||||
advantage: 0,
|
||||
@@ -126,7 +132,7 @@ const buildCharacter = function (charId){
|
||||
char.dms[damageMultiplier.name] = {
|
||||
computed: false,
|
||||
busyComputing: false,
|
||||
type: "damageMultiplier";
|
||||
type: "damageMultiplier",
|
||||
result: 0,
|
||||
immunityCount: 0,
|
||||
ressistanceCount: 0,
|
||||
@@ -137,12 +143,11 @@ const buildCharacter = function (charId){
|
||||
});
|
||||
|
||||
// Fetch the class levels and store them
|
||||
char.level = 0;
|
||||
char.classes = {};
|
||||
Classes.find({charId}).forEach(class => {
|
||||
if (!char.classes[class.name]){
|
||||
char.classes[class.name] = {level: class.level};
|
||||
char.level += class.level;
|
||||
// don't use the word "class" it's reserved
|
||||
Classes.find({charId}).forEach(cls => {
|
||||
if (!char.classes[cls.name]){
|
||||
char.classes[cls.name] = {level: cls.level};
|
||||
char.level += cls.level;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -151,14 +156,19 @@ const buildCharacter = function (charId){
|
||||
charId: charId,
|
||||
enabled: true,
|
||||
}).forEach(effect => {
|
||||
effect.computed = false;
|
||||
effect.result = 0;
|
||||
let storedEffect = {
|
||||
computed: false,
|
||||
result: 0,
|
||||
operation: effect.operation,
|
||||
value: effect.value,
|
||||
calculation: effect.calculation,
|
||||
}
|
||||
if (char.atts[effect.stat]) {
|
||||
char.atts[effect.stat].effects.push(effect);
|
||||
char.atts[effect.stat].effects.push(storedEffect);
|
||||
} else if (char.skills[effect.stat]) {
|
||||
char.skills[effect.stat].effects.push(effect);
|
||||
char.skills[effect.stat].effects.push(storedEffect);
|
||||
} else if (char.dms[effect.stat]) {
|
||||
char.dms[effect.stat].effects.push(effect);
|
||||
char.dms[effect.stat].effects.push(storedEffect);
|
||||
} else {
|
||||
// ignore effects that don't apply to an actual stat
|
||||
}
|
||||
@@ -174,20 +184,24 @@ const buildCharacter = function (charId){
|
||||
char.skills[proficiency.name].proficiencies.push(effect);
|
||||
}
|
||||
});
|
||||
return char;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the character's stats in-place, returns the same char object
|
||||
*/
|
||||
const computeCharacter = function (char){
|
||||
export const computeCharacter = function (char){
|
||||
// Iterate over each stat in order and compute it
|
||||
for (stat in char.atts){
|
||||
for (statName in char.atts){
|
||||
let stat = char.atts[statName]
|
||||
computeStat (stat, char);
|
||||
}
|
||||
for (stat in char.skills){
|
||||
for (statName in char.skills){
|
||||
let stat = char.skills[statName]
|
||||
computeStat (stat, char);
|
||||
}
|
||||
for (stat in char.dms){
|
||||
for (statName in char.dms){
|
||||
let stat = char.dms[statName]
|
||||
computeStat (stat, char);
|
||||
}
|
||||
return char;
|
||||
@@ -211,10 +225,10 @@ const computeStat = function(stat, char){
|
||||
}
|
||||
|
||||
// Iterate over each effect which applies to the stat
|
||||
for (effect in stat.effects){
|
||||
computeEffect(effect, char);
|
||||
for (i in stat.effects){
|
||||
computeEffect(stat.effects[i], char);
|
||||
// apply the effect to the stat
|
||||
applyEffect(effect, stat);
|
||||
applyEffect(stat.effects[i], stat);
|
||||
}
|
||||
|
||||
// Conglomerate all the effects to compute the final stat values
|
||||
@@ -229,15 +243,18 @@ const computeStat = function(stat, char){
|
||||
* Compute a single effect on a character
|
||||
*/
|
||||
const computeEffect = function(effect, char){
|
||||
if (effect.computed) return;
|
||||
if (_.isFinite(effect.value)){
|
||||
effect.result = effect.value;
|
||||
} else if(effect.operation === "conditional"){
|
||||
effect.result = effect.calculation;
|
||||
} else if(_.contains(["advantage", "disadvantage", "fail"], effect.operation){
|
||||
} else if(_.contains(["advantage", "disadvantage", "fail"], effect.operation)){
|
||||
effect.result = 1;
|
||||
} else if (_.isString(effect.calculation)){
|
||||
effect.result = evaluateCalculation(charId, effect.calculation);
|
||||
effect.result = evaluateCalculation(effect.calculation, char);
|
||||
}
|
||||
effect.computed = true;
|
||||
console.log({effect});
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -320,20 +337,37 @@ const combineAttribute = function(stat, char){
|
||||
if (stat.result < stat.min) stat.result = stat.min;
|
||||
if (stat.result > stat.max) stat.result = stat.max;
|
||||
// Round everything that isn't the carry multiplier
|
||||
if (stat.name !== "carryMultiplier") stat.result = Math.floor(stat.result);
|
||||
stat.mod = Math.floor((stat.result - 10) / 2);
|
||||
if (!stat.decimal) stat.result = Math.floor(stat.result);
|
||||
if (stat.attributeType === "ability") {
|
||||
stat.mod = Math.floor((stat.result - 10) / 2);
|
||||
}
|
||||
console.log({statResult: stat.result})
|
||||
}
|
||||
|
||||
const combineSkill = function(stat, char){
|
||||
for (prof in stat.proficiencies){
|
||||
for (i in stat.proficiencies){
|
||||
let prof = stat.proficiencies[i];
|
||||
if (prof.value > stat.proficiency) stat.proficiency = prof.value;
|
||||
}
|
||||
if (!char.atts.proficiencyBonus.computed){
|
||||
computeStat(char.atts.proficiencyBonus, char);
|
||||
let profBonus;
|
||||
if (char.atts.proificiencyBonus){
|
||||
if (!char.atts.proficiencyBonus.computed){
|
||||
computeStat(char.atts.proficiencyBonus, char);
|
||||
}
|
||||
profBonus = char.atts.proficiencyBonus.result;
|
||||
} else {
|
||||
profBonus = Math.floor(char.level / 4 + 1.75);
|
||||
}
|
||||
const profBonus = char.atts.proficiencyBonus.result;
|
||||
const base = profBonus * stat.proficiency;
|
||||
stat.result = (base + stat.add) * stat.mul;
|
||||
profBonus *= stat.proficiency;
|
||||
// Skills are based on some ability Modifier
|
||||
let abilityMod = 0;
|
||||
if (stat.ability && char.atts[stat.ability]){
|
||||
if (!char.atts[stat.ability].computed){
|
||||
computeStat(char.atts[stat.ability], char);
|
||||
}
|
||||
abilityMod = char.atts[stat.ability].mod;
|
||||
}
|
||||
stat.result = (abilityMod + profBonus + stat.add) * stat.mul;
|
||||
if (stat.result < stat.min) stat.result = stat.min;
|
||||
if (stat.result > stat.max) stat.result = stat.max;
|
||||
stat.result = Math.floor(stat.result);
|
||||
@@ -341,9 +375,9 @@ const combineSkill = function(stat, char){
|
||||
|
||||
const combineDamageMultiplier = function(stat, char){
|
||||
if (stat.immunityCount) return 0;
|
||||
if (ressistanceCount && !vulnerabilityCount){
|
||||
if (stat.ressistanceCount && !stat.vulnerabilityCount){
|
||||
stat.result = 0.5;
|
||||
} else if (!ressistanceCount && vulnerabilityCount){
|
||||
} else if (!stat.ressistanceCount && stat.vulnerabilityCount){
|
||||
stat.result = 2;
|
||||
} else {
|
||||
stat.result = 1;
|
||||
|
||||
97
app/Model/Character/CharacterComputation.test.js
Normal file
97
app/Model/Character/CharacterComputation.test.js
Normal file
@@ -0,0 +1,97 @@
|
||||
import {computeCharacter} from "./CharacterComputation.js";
|
||||
import assert from "assert";
|
||||
|
||||
const makeEffect = function(operation, value){
|
||||
let effect = {computed: false, result: 0, operation}
|
||||
if (_.isFinite(value)){
|
||||
effect.value = +value;
|
||||
} else {
|
||||
effect.calculation = value;
|
||||
}
|
||||
return effect;
|
||||
}
|
||||
|
||||
describe('computeCharacter', function () {
|
||||
it('computes an aritrary character', function () {
|
||||
let char = {
|
||||
atts: {
|
||||
attribute1: {
|
||||
computed: false,
|
||||
busyComputing: false,
|
||||
type: "attribute",
|
||||
attributeType: "ability",
|
||||
result: 0,
|
||||
mod: 0, // The resulting modifier if this is an ability
|
||||
base: 0,
|
||||
add: 0,
|
||||
mul: 1,
|
||||
min: Number.NEGATIVE_INFINITY,
|
||||
max: Number.POSITIVE_INFINITY,
|
||||
effects: [
|
||||
makeEffect("base", 10),
|
||||
makeEffect("add", 5),
|
||||
makeEffect("mul", 2),
|
||||
],
|
||||
},
|
||||
attribute2: {
|
||||
computed: false,
|
||||
busyComputing: false,
|
||||
type: "attribute",
|
||||
result: 0,
|
||||
mod: 0, // The resulting modifier if this is an ability
|
||||
base: 0,
|
||||
add: 0,
|
||||
mul: 1,
|
||||
min: Number.NEGATIVE_INFINITY,
|
||||
max: Number.POSITIVE_INFINITY,
|
||||
effects: [
|
||||
makeEffect("base", "attribute1"),
|
||||
makeEffect("max", 2),
|
||||
],
|
||||
},
|
||||
},
|
||||
skills: {
|
||||
skill1: {
|
||||
computed: false,
|
||||
busyComputing: false,
|
||||
type: "skill",
|
||||
ability: "attribute1",
|
||||
result: 0,
|
||||
proficiency: 0,
|
||||
add: 0,
|
||||
mul: 1,
|
||||
min: Number.NEGATIVE_INFINITY,
|
||||
max: Number.POSITIVE_INFINITY,
|
||||
advantage: 0,
|
||||
disadvantage: 0,
|
||||
passiveAdd: 0,
|
||||
fail: 0,
|
||||
conditional: 0,
|
||||
effects: [],
|
||||
proficiencies: [],
|
||||
},
|
||||
},
|
||||
dms: {
|
||||
dm1: {
|
||||
computed: false,
|
||||
busyComputing: false,
|
||||
type: "damageMultiplier",
|
||||
result: 0,
|
||||
immunityCount: 0,
|
||||
ressistanceCount: 0,
|
||||
vulnerabilityCount: 0,
|
||||
effects: [],
|
||||
}
|
||||
},
|
||||
classes: {
|
||||
Barbarian: {
|
||||
level: 5,
|
||||
},
|
||||
},
|
||||
level: 5,
|
||||
};
|
||||
char = computeCharacter(char);
|
||||
console.log(char);
|
||||
assert(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user