mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-08-31 04:02:29 +00:00
RolemasterUnified Official: Pre-Treasure Law Fixes
- Finalise handling of Professions with selectable Base lists
- Alchemists are the first users
- Half shield bonus skill for partial block - not the full one (all my
players)
- Change 'maneuver_penalty' -> status_penalty
- Update everywhere except inventory; it was a collision of names before.
- Try and squash the super long list of errors when you first update stateud
- Compendium data: Move a long bow to Bow, Long for the table. (@nef)
- Equipment:
- Perception Penalties are now applied
- Ranged Penalties are now applied.
- Take a terrible guess if it's a ranged or melee weapon when adding attacks
- Creatures: Creatures can now roll skills.
- Creatures: Add hx for Hand Crossbows, and a few other attacks (@nef)
- Creatures now have framework for type of attack (melee, ranged, directed)
- Does not (yet) change attack
- Add test to validate attack types
This commit is contained in:
@@ -2426,8 +2426,8 @@ Lists Selected <input type='number' name='comp_cmbaselistcount'> / 6
|
||||
</div>
|
||||
|
||||
<div class='prop_pair'>
|
||||
<span>Maneuver Penalty:</span>
|
||||
<span name='attr_maneuver_penalty'></span>
|
||||
<span>Status Penalty:</span>
|
||||
<span name='attr_status_penalty'></span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2459,12 +2459,14 @@ Lists Selected <input type='number' name='comp_cmbaselistcount'> / 6
|
||||
<div class='creatureskills'>
|
||||
<span class='creatureheading'>Skills</span>
|
||||
<fieldset class='repeating_creatureskill'>
|
||||
<span name='attr_name'></span>
|
||||
<button type='action' class='editbutton rollbutton'
|
||||
name='act_creatureskill'>
|
||||
<span name='attr_bonus'></span>
|
||||
<span style="font-family:dicefontd10;">j</span>
|
||||
name='act_creatureskillroll'>
|
||||
<span name='attr_name'></span>
|
||||
</button>
|
||||
<button type='action' class='editbutton rollbutton'
|
||||
name='act_creatureskillrollmod'>
|
||||
<span name='attr_bonus'></span>
|
||||
</button>,
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
@@ -2720,8 +2722,8 @@ Name <input name="attr_character_name">
|
||||
</div>
|
||||
|
||||
<div class='prop_pair'>
|
||||
<span>Maneuver Penalty:</span>
|
||||
<span name='attr_maneuver_penalty'></span>
|
||||
<span>Status Penalty:</span>
|
||||
<span name='attr_status_penalty'></span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3150,6 +3152,12 @@ Name <input name="attr_character_name">
|
||||
</section>
|
||||
<section id='skills' class='tab-panel'>
|
||||
<div class="skillspane">
|
||||
|
||||
<!-- Inventory/Encumberance/Armor based one -->
|
||||
<div style='float: right'>
|
||||
Manuever Penalty: <span name='attr_maneuver_penalty'></span>
|
||||
</div>
|
||||
|
||||
<h2>Skills</h2>
|
||||
|
||||
<div class='skilllist'>
|
||||
@@ -5111,6 +5119,9 @@ Name <input name="attr_character_name">
|
||||
<span>
|
||||
<button type='action' class='fancybuttoninline fancybutton' name='act_itemcarrytopack'>Pack</button>
|
||||
<button type='action' class='fancybuttoninline fancybutton' name='act_itemcarrytostore'>Store</button>
|
||||
|
||||
<button type='action' class='fancybuttoninline fancybutton' name='act_edititem'
|
||||
><span style="font-family:Pictos;">p</span></button>
|
||||
<button type='action' class='fancybuttoninline fancybutton' name='act_deleteitem'
|
||||
><span style="font-family:Pictos;">#</span>
|
||||
</button>
|
||||
@@ -5120,11 +5131,16 @@ Name <input name="attr_character_name">
|
||||
</fieldset>
|
||||
|
||||
<p>
|
||||
Total Weight: <span name='attr_total_weight'></span><br>
|
||||
Weight Allowance: <span name='attr_weightallowance'></span><br>
|
||||
Maneuver Penalty: <span name='attr_encumberance_penalty'></span><br>
|
||||
Spell Casting Armor Enc% <span name='attr_armorenc'></span><br>
|
||||
Spell Casting Metal Armor Enc% <span name='attr_armorencmetal'></span><br>
|
||||
<div>Total Weight: <span name='attr_total_weight'></span></div>
|
||||
<div>Weight Allowance: <span name='attr_weightallowance'></span></div>
|
||||
<input type='hidden' name='attr_encumberance_penalty' class='hidenext'>
|
||||
<div>Encumberance Penalty: <span name='attr_encumberance_penalty'></span></div>
|
||||
<input type='hidden' name='attr_perception_penalty' class='hidenext'>
|
||||
<div>Perception Penalty: <span name='attr_perception_penalty'></span></div>
|
||||
<input type='hidden' name='attr_ranged_penalty' class='hidenext'>
|
||||
<div>Ranged Penalty: <span name='attr_ranged_penalty'></span></div>
|
||||
<div>Spell Casting Armor Enc% <span name='attr_armorenc'></span></div>
|
||||
<div>Spell Casting Metal Armor Enc% <span name='attr_armorencmetal'></span></div>
|
||||
</p>
|
||||
|
||||
<h3>Backpack</h3>
|
||||
@@ -5358,7 +5374,7 @@ Sheet Version: <span name='attr_version'></span>
|
||||
|
||||
<br>
|
||||
|
||||
Revision 0e32e4dbcd10426972eb1499037242f2c1e9fae3
|
||||
Revision 2ba06ffcab4ce3ab15b4d974809598b37447caa4
|
||||
|
||||
|
||||
<hr>
|
||||
@@ -5378,6 +5394,13 @@ function rmuerror(message) {
|
||||
}
|
||||
}
|
||||
|
||||
function rmuwarn(message) {
|
||||
console.log("** Warning: ")
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
console.log('>', arguments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Something character related is wrong
|
||||
// Should surface to the user.
|
||||
function cherror(message) {
|
||||
@@ -6607,7 +6630,7 @@ function checkPending() {
|
||||
}
|
||||
asyncTimeout = setTimeout(checkPending, 500, item);
|
||||
|
||||
console.log(`Calling pending ${item.name}`, item);
|
||||
console.log(`>> Calling pending ${item.name}`, item);
|
||||
try {
|
||||
item.cb();
|
||||
} catch (e) {
|
||||
@@ -6996,6 +7019,15 @@ let RMUSkills = {};
|
||||
}
|
||||
}
|
||||
|
||||
RMUSkills.updateSingleCategory = function(catname) {
|
||||
const cat = getCatByDisplayName(catname)
|
||||
if (!cat) {
|
||||
rmuerror("Unable to find category", catname);
|
||||
return;
|
||||
}
|
||||
updateCategory(cat, statCache_);
|
||||
}
|
||||
|
||||
function updateSkill(skill, catbonus, catlog) {
|
||||
const aname = skill.aname;
|
||||
getAttrs([aname + "_ranks_misc", aname + "_pbonus", aname + "_misc", aname + "_ranks"], (attrs) => {
|
||||
@@ -7271,7 +7303,7 @@ RMUSkills.updateFavoriteSkillSpecialisation = function(skillname, attrname) {
|
||||
RMUSkills.startSkillRoll = function(prefix, usemod = false) {
|
||||
getAttrs([
|
||||
prefix + '_name', prefix + '_favskill', prefix + '_bonus',
|
||||
'character_name', 'maneuver_penalty',
|
||||
'character_name', 'statuspenalty',
|
||||
'statuseffect'],
|
||||
(cdata) => {
|
||||
const simplified = {};
|
||||
@@ -7284,7 +7316,7 @@ RMUSkills.startSkillRoll = function(prefix, usemod = false) {
|
||||
}
|
||||
}
|
||||
if (!simplified.name) {
|
||||
// Look it up by anamee -> use the prefix
|
||||
// Look it up by aname -> use the prefix
|
||||
const skill = RMUSkills.getSkillByName(prefix);
|
||||
if (skill) {
|
||||
simplified.name = skill.fulldisplay ? skill.fulldisplay : skill.display;
|
||||
@@ -7311,9 +7343,9 @@ RMUSkills.handleSkillRoll = function(cdata, usemod) {
|
||||
let mlog = `${mod} [Skill Bonus]`
|
||||
|
||||
{
|
||||
const manp = parseIntDefault(cdata.maneuver_penalty, 0);
|
||||
const manp = parseIntDefault(cdata.statuspenalty, 0);
|
||||
if (manp < 0) {
|
||||
mlog += ` ${manp} [Maneuver Penalty]`;
|
||||
mlog += ` ${manp} [Status Penalty]`;
|
||||
mod += manp;
|
||||
}
|
||||
}
|
||||
@@ -7445,7 +7477,6 @@ onCheck("clicked:repeating_favorite:rollskillmod", (ev) => {
|
||||
RMUSkills.startSkillRoll(basename, true);
|
||||
});
|
||||
|
||||
|
||||
onCheck(`clicked:repeating_specializationanimalhandling:rollskilldirect`, (ev) => {
|
||||
const basename = ev.sourceAttribute.replace(/_rollskilldirect\d*$/, "");
|
||||
RMUSkills.startSkillRoll(basename);
|
||||
@@ -7597,6 +7628,19 @@ RMUSkills.updateFavorites = function() {
|
||||
});
|
||||
}
|
||||
|
||||
onCheck("clicked:repeating_creatureskill:creatureskillroll", (ev) => {
|
||||
const basename = ev.sourceAttribute.replace(/_creatureskillroll$/, "");
|
||||
RMUSkills.startSkillRoll(basename, false);
|
||||
});
|
||||
|
||||
onCheck("clicked:repeating_creatureskill:creatureskillrollmod", (ev) => {
|
||||
const basename = ev.sourceAttribute.replace(/_creatureskillrollmod$/, "");
|
||||
RMUSkills.startSkillRoll(basename, true);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7614,27 +7658,57 @@ function _spellDoQuery(prevranks, listtype, filter, subtarget, cost, listranks)
|
||||
return;
|
||||
}
|
||||
|
||||
// Looping though the lists we got.
|
||||
for (let list of lists) {
|
||||
// Filter is currently on ListType
|
||||
// Filter is currently on ListType - this filtesr out healer/layhealer
|
||||
if (filter && filter.length > 0) {
|
||||
if (list.data.ListType == filter) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
addRepeatingSectionPending(`cmspelllistspell${subtarget}`, 'spelllist',
|
||||
`${subtarget}spelllist`,
|
||||
(sectionname) => {
|
||||
cmPopulateListOptions(`${sectionname}_cmranks`, costarray,
|
||||
prevranks[list.name] || costarray[0]);
|
||||
setAttrsPending({[`${sectionname}_listname`]: list.name,
|
||||
[`${sectionname}_listranks`]: listranks[list.name]?.ranks || '-'
|
||||
});
|
||||
addRepeatingSectionPending(`cmspelllistspell${subtarget}`, 'spelllist', `${subtarget}spelllist`,
|
||||
(sectionname) => {
|
||||
cmPopulateListOptions(`${sectionname}_cmranks`, costarray,
|
||||
prevranks[list.name] || costarray[0]);
|
||||
setAttrsPending({[`${sectionname}_listname`]: list.name,
|
||||
[`${sectionname}_listranks`]: listranks[list.name]?.ranks || '-'});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Base lists are different to other lists. No matter what we only show 6.
|
||||
// So do the query to get the base lists. Then we get the characters selected
|
||||
// base lists and display that.
|
||||
function _spellDoBaseListQuery(selectedbase, profession, cost, previousranks) {
|
||||
const costarray = generateCostArray(cost);
|
||||
var data = getCharmancerData();
|
||||
|
||||
if (selectedbase.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let name in selectedbase) {
|
||||
let details = selectedbase[name];
|
||||
if (!details.prefix.match(/^repeating_spelllistspellownbase_/)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
addRepeatingSectionPending("cmspelllistspellownbase", 'spelllist', "ownbasespelllist",
|
||||
(sectionname) => {
|
||||
cmPopulateListOptions(`${sectionname}_cmranks`, costarray,
|
||||
previousranks[name] || costarray[0]);
|
||||
setAttrsPending({[`${sectionname}_listname`]: name,
|
||||
[`${sectionname}_listranks`]: details.ranks || '-'});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Initialise the spell lists in the Charactermancer
|
||||
// listranks is a dictionary of ranks indexed by the spell list name
|
||||
function initSpellListsCM(savedlists, profession, realm, costs, listranks) {
|
||||
const cmdata = getCharmancerData();
|
||||
@@ -7663,17 +7737,17 @@ function initSpellListsCM(savedlists, profession, realm, costs, listranks) {
|
||||
|
||||
console.log("Pensing for base");
|
||||
addPendingFunction("Base lists",()=> {
|
||||
_spellDoQuery(previousranks, `ListType:*${profession} Base*`, '', 'ownbase', costs['Base'], listranks);
|
||||
_spellDoBaseListQuery(listranks, profession, costs.Base, previousranks);
|
||||
});
|
||||
|
||||
if (realm.indexOf('/') > -1) {
|
||||
// Hybrid: Two realms
|
||||
realms = realm.split('/');
|
||||
addPendingFunction("Hybrid realm 1 open lists",()=> {
|
||||
_spellDoQuery(previousranks, `ListType:*${realms[0]} Open*`, '', 'open', costs['Open'], listranks );
|
||||
_spellDoQuery(previousranks, `ListType:*${realms[0]} Open*`, '', 'open', costs.Open, listranks );
|
||||
});
|
||||
addPendingFunction("Hybrid realm 2 open lists",()=> {
|
||||
_spellDoQuery(previousranks, `ListType:*${realms[1]} Open*`, '', 'open', costs['Open'], listranks );
|
||||
_spellDoQuery(previousranks, `ListType:*${realms[1]} Open*`, '', 'open', costs.Open, listranks );
|
||||
});
|
||||
addPendingFunction("Hybid realm 1 closed lists",()=> {
|
||||
_spellDoQuery(previousranks, `ListType:*${realms[0]} Closed*`, '', 'closed',
|
||||
@@ -8058,7 +8132,7 @@ onCheck(spellcastevents.join(" "), (ev) => {
|
||||
// AP
|
||||
'castinguseap', 'aptrack_spell',
|
||||
// Maneuver Penalty
|
||||
'maneuver_penalty', 'statuseffect',
|
||||
'statuspenalty', 'statuseffect',
|
||||
],
|
||||
(spelldata) => {
|
||||
console.log("Casting a spell", spelldata);
|
||||
@@ -8127,9 +8201,9 @@ onCheck(spellcastevents.join(" "), (ev) => {
|
||||
}
|
||||
|
||||
{
|
||||
const manp = parseIntDefault(spelldata.maneuver_penalty, 0);
|
||||
const manp = parseIntDefault(spelldata.statuspenalty, 0);
|
||||
if (manp < 0) {
|
||||
mlog += ` ${manp} [Maneuver Penalty]`;
|
||||
mlog += ` ${manp} [Status Penalty]`;
|
||||
mod += manp;
|
||||
}
|
||||
}
|
||||
@@ -8760,40 +8834,9 @@ onCheck("mancerchange:cmskilllocksranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskilloperationranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillrepairconstructionranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskilltrapsranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillbookkeeperranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskilllibrarianranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillofficerranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillmanagerranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillquartermasterranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillseneschalranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillbodyguardranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillbonesetterranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillcasinodealercroupierranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillguardsmanranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillguideranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillinnkeeperranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillpurserranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillresearcherranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillshamanranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillteacherranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillvaletranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillblacksmithranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillcarpenterranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillfarmerranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillfenceranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillgamblerranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillherdsmanranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillhunterranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillmerchantranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillminerranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillpirateranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillsailorranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillscriberanks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillsoldierranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillthiefranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskilltinkerranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskilltrapperranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:cmskillweaverranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:repeating_cmskill_administration_specializations_cmskillranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:repeating_cmskill_service_specializations_cmskillranks", changeSkillPurchase);
|
||||
onCheck("mancerchange:repeating_cmskill_trade_specializations_cmskillranks", changeSkillPurchase);
|
||||
|
||||
|
||||
|
||||
@@ -9186,7 +9229,7 @@ function levelUpStart(_) {
|
||||
});
|
||||
|
||||
|
||||
// FIXME: THis is duplicated below. I hsould make it function
|
||||
// FIXME: This is duplicated below. I hsould make it function
|
||||
const listRanks = {};
|
||||
{
|
||||
const spellRanks = {}
|
||||
@@ -9378,7 +9421,7 @@ onCheck("mancerfinish:levelup", (ev) => {
|
||||
{
|
||||
const spellRanks = {}
|
||||
const listIdsToGet = [];
|
||||
addPendingFunction('Levelup: Get Ranks for spells', () => {
|
||||
addPendingFunction('LevelupFinish: Get Ranks for spells', () => {
|
||||
// So two pieces here. Get all the lists with ranks. Put in an array indexed
|
||||
// by spell name.
|
||||
// FIXME: This is in RMUSkills.lua, should sync
|
||||
@@ -10902,26 +10945,26 @@ let RMUCultures = {}
|
||||
|
||||
// FIXME: This needs to die
|
||||
RMUCultures.cultures = {
|
||||
reaver : { skills : {
|
||||
survival: '1', regionneighbour: '1', rangedweapons: '1', riding: '1', craftingvocation: '3', jumping: '1', perception: '1', bodydevelopment: '2', lores: '3', animalhandling: '1', maneuveringinarmor: '2', unarmed: '2', tracking: '1', running: '2', medicine: '1', stalking: '1', meleeweapons: '1', climbing: '1', regionown: '5', languages: '11', } },
|
||||
urban : { skills : {
|
||||
lores: '7', animalhandling: '1', unarmed: '1', trading: '2', running: '1', influence: '2', craftingvocation: '7', socialawareness: '2', compositionperf: '3', religionphilosophy: '1', perception: '2', bodydevelopment: '1', regionneighbour: '3', regionown: '5', languages: '17', } },
|
||||
highland : { skills : {
|
||||
survival: '2', craftingvocation: '4', jumping: '1', compositionperf: '1', perception: '1', bodydevelopment: '2', lores: '4', animalhandling: '1', maneuveringinarmor: '1', unarmed: '1', tracking: '1', running: '2', medicine: '1', stalking: '1', socialawareness: '1', swimming: '1', meleeweapons: '1', climbing: '1', regionown: '5', languages: '13', } },
|
||||
cosmopolitan : { skills : {
|
||||
lores: '7', languages: '19', unarmed: '1', trading: '1', influence: '2', craftingvocation: '6', socialawareness: '3', compositionperf: '4', running: '1', perception: '2', bodydevelopment: '1', religionphilosophy: '2', regionneighbour: '2', regionown: '5', } },
|
||||
nomad : { skills : {
|
||||
survival: '1', regionneighbour: '2', riding: '3', craftingvocation: '4', navigation: '1', compositionperf: '1', perception: '1', bodydevelopment: '1', lores: '4', animalhandling: '4', herbalism: '1', unarmed: '1', tracking: '1', running: '2', medicine: '1', trading: '1', socialawareness: '1', regionown: '5', languages: '14', } },
|
||||
harsh : { skills : {
|
||||
survival: '3', languages: '8', maneuveringinarmor: '1', unarmed: '1', tracking: '1', stalking: '2', concealment: '1', craftingvocation: '4', navigation: '1', running: '2', medicine: '2', perception: '2', bodydevelopment: '3', lores: '2', regionown: '5', meleeweapons: '1', } },
|
||||
languages: '19', perception: '2', trading: '1', regionown: '5', bodydevelopment: '1', influence: '2', regionneighbour: '2', socialawareness: '3', lores: '7', running: '1', unarmed: '1', religionphilosophy: '2', craftingvocation: '6', compositionperf: '4', } },
|
||||
sylvan : { skills : {
|
||||
survival: '2', rangedweapons: '1', craftingvocation: '4', socialawareness: '1', perception: '1', bodydevelopment: '1', lores: '4', animalhandling: '2', herbalism: '1', unarmed: '1', tracking: '1', running: '2', medicine: '1', stalking: '2', concealment: '1', climbing: '2', regionown: '5', languages: '13', } },
|
||||
underground : { skills : {
|
||||
survival: '2', languages: '13', stalking: '3', unarmed: '1', concealment: '1', socialawareness: '1', running: '1', weighttraining: '1', medicine: '2', climbing: '1', lores: '4', perception: '2', bodydevelopment: '2', regionown: '5', craftingvocation: '5', meleeweapons: '1', } },
|
||||
rural : { skills : {
|
||||
swimming: '1', influence: '1', regionneighbour: '1', riding: '2', weighttraining: '1', socialawareness: '1', compositionperf: '1', perception: '1', bodydevelopment: '1', lores: '4', animalhandling: '3', herbalism: '1', trading: '1', running: '1', medicine: '1', climbing: '1', regionown: '5', languages: '13', craftingvocation: '4', piloting: '1', } },
|
||||
tracking: '1', perception: '1', regionown: '5', herbalism: '1', animalhandling: '2', unarmed: '1', medicine: '1', concealment: '1', climbing: '2', bodydevelopment: '1', running: '2', stalking: '2', socialawareness: '1', lores: '4', languages: '13', survival: '2', craftingvocation: '4', rangedweapons: '1', } },
|
||||
mariner : { skills : {
|
||||
survival: '1', influence: '1', regionneighbour: '2', weighttraining: '1', navigation: '1', compositionperf: '1', perception: '2', bodydevelopment: '1', lores: '5', languages: '15', unarmed: '1', trading: '1', running: '1', medicine: '1', socialawareness: '1', swimming: '2', acrobatics: '1', climbing: '1', regionown: '5', jumping: '1', craftingvocation: '4', } },
|
||||
languages: '15', perception: '2', regionown: '5', navigation: '1', regionneighbour: '2', socialawareness: '1', lores: '5', unarmed: '1', medicine: '1', weighttraining: '1', climbing: '1', bodydevelopment: '1', swimming: '2', running: '1', trading: '1', acrobatics: '1', influence: '1', jumping: '1', survival: '1', craftingvocation: '4', compositionperf: '1', } },
|
||||
urban : { skills : {
|
||||
languages: '17', perception: '2', trading: '2', running: '1', regionown: '5', bodydevelopment: '1', influence: '2', regionneighbour: '3', socialawareness: '2', animalhandling: '1', lores: '7', unarmed: '1', religionphilosophy: '1', craftingvocation: '7', compositionperf: '3', } },
|
||||
underground : { skills : {
|
||||
stalking: '3', languages: '13', perception: '2', meleeweapons: '1', climbing: '1', regionown: '5', bodydevelopment: '2', concealment: '1', running: '1', socialawareness: '1', lores: '4', medicine: '2', unarmed: '1', survival: '2', craftingvocation: '5', weighttraining: '1', } },
|
||||
reaver : { skills : {
|
||||
tracking: '1', perception: '1', regionown: '5', regionneighbour: '1', animalhandling: '1', unarmed: '2', medicine: '1', riding: '1', meleeweapons: '1', climbing: '1', bodydevelopment: '2', running: '2', maneuveringinarmor: '2', stalking: '1', lores: '3', languages: '11', jumping: '1', survival: '1', craftingvocation: '3', rangedweapons: '1', } },
|
||||
rural : { skills : {
|
||||
languages: '13', perception: '1', regionown: '5', regionneighbour: '1', herbalism: '1', animalhandling: '3', medicine: '1', weighttraining: '1', riding: '2', piloting: '1', climbing: '1', bodydevelopment: '1', swimming: '1', running: '1', trading: '1', socialawareness: '1', influence: '1', lores: '4', craftingvocation: '4', compositionperf: '1', } },
|
||||
nomad : { skills : {
|
||||
tracking: '1', perception: '1', regionown: '5', navigation: '1', regionneighbour: '2', herbalism: '1', animalhandling: '4', unarmed: '1', medicine: '1', riding: '3', bodydevelopment: '1', running: '2', trading: '1', socialawareness: '1', lores: '4', languages: '14', survival: '1', craftingvocation: '4', compositionperf: '1', } },
|
||||
harsh : { skills : {
|
||||
running: '2', tracking: '1', perception: '2', meleeweapons: '1', concealment: '1', regionown: '5', bodydevelopment: '3', navigation: '1', maneuveringinarmor: '1', stalking: '2', lores: '2', medicine: '2', unarmed: '1', survival: '3', languages: '8', craftingvocation: '4', } },
|
||||
highland : { skills : {
|
||||
tracking: '1', perception: '1', regionown: '5', socialawareness: '1', animalhandling: '1', unarmed: '1', medicine: '1', meleeweapons: '1', climbing: '1', running: '2', bodydevelopment: '2', swimming: '1', maneuveringinarmor: '1', stalking: '1', lores: '4', languages: '13', jumping: '1', survival: '2', craftingvocation: '4', compositionperf: '1', } },
|
||||
|
||||
};
|
||||
|
||||
@@ -11483,12 +11526,13 @@ frontpage.updateDB = function () {
|
||||
const shield_bonus = ev.shield_bonus;
|
||||
{
|
||||
const sb2 = Math.floor(shield_bonus / 2);
|
||||
updates.db_partialblock = basedb + shielddb + runningranks + Math.max(0, shield_bonus) + passivedodgebonus;
|
||||
updates.db_partialblock = basedb + shielddb + runningranks +
|
||||
Math.round(Math.max(0, sb2) / 2) + passivedodgebonus;
|
||||
updates.db_partialblock_info = "Partial Block\n" +
|
||||
`${F(basedb)} Base DB\n` +
|
||||
`${F(runningranks)} Passive dodge\n` +
|
||||
`${F(shielddb)} Shield DB\n` +
|
||||
`${F(shield_bonus)} Shield bonus x ½ (min 0)\n` +
|
||||
`${F(sb2)} Shield bonus x ½ (min 0)\n` +
|
||||
`${F(passivedodgebonus)} Passive Dodge`;
|
||||
}
|
||||
|
||||
@@ -11844,7 +11888,9 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
|
||||
*/
|
||||
RMUInventory.addSpecialEquipment = function() {
|
||||
getSectionIDsPending(`repeating_stuff`, ids => {
|
||||
let toget = ['base_at_misc']
|
||||
let percept = 0;
|
||||
let rangedpenalty = 0;
|
||||
let toget = ['base_at_misc', 'perception_misc']
|
||||
ids.forEach(id => {
|
||||
toget.push(`repeating_stuff_${id}_itemname`);
|
||||
toget.push(`repeating_stuff_${id}_itemequipped`);
|
||||
@@ -11916,10 +11962,22 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
|
||||
if (json['Attack Table']) {
|
||||
RMUInventory.updateWeapon(name, json);
|
||||
}
|
||||
|
||||
percept += parseIntDefault(json['Perception Penalty'], 0);
|
||||
rangedpenalty += parseIntDefault(json['Ranged Penalty'], 0);
|
||||
maneuver_penalty += parseIntDefault(json['Maneuver Penalty'], 0);
|
||||
});
|
||||
|
||||
// Adjust due to perception penalty
|
||||
updates.perception_misc = miscBonusSet(updates.perception_misc,
|
||||
"Armor Perception Penalty", percept);
|
||||
updates.perception_penalty = percept;
|
||||
updates.ranged_penalty = rangedpenalty;
|
||||
updates.maneuver_penalty = maneuver_penalty;
|
||||
|
||||
console.log("equipment update", updates);
|
||||
setAttrsPending(updates);
|
||||
setAttrsPending(updates, () => {
|
||||
RMUSkills.updateSingleCategory("Awareness");
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -12017,13 +12075,17 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
|
||||
const range = json['RI'] || json['short'] || '-';
|
||||
|
||||
getCompendiumPagePending("AttackTable:" + json['Attack Table'], (cdata) => {
|
||||
console.log("Attack data for", json['Attack Table'], cdata);
|
||||
if (!cdata.data['data-attack']) {
|
||||
rmuerror("Data attack missing in ", json['Attack Table']);
|
||||
return;
|
||||
}
|
||||
let min = cdata.data.min.match(/\d+/)[0] || 0;
|
||||
let max = cdata.data.max.match(/\d+/)[0] || 0;
|
||||
let attack_table = cdata.data['data-attack'] || "";
|
||||
let attacktype = 'melee';
|
||||
if (cdata.RI && parseIntDefault(cdata.RI, 0) > 0) {
|
||||
attacktype = 'ranged';
|
||||
}
|
||||
setAttrsPending({
|
||||
[`${prefix}_attackname`]: name,
|
||||
[`${prefix}_attacksize`]: size,
|
||||
@@ -12192,10 +12254,20 @@ RMUInventory.itemfields = [ "itemname", "itemweight", "itemcount", "itemsystem",
|
||||
console.log(fullnames);
|
||||
setAttrs(fullnames);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
onCheck("clicked:repeating_stuff:edititem", (ev) => {
|
||||
console.log(ev);
|
||||
const basename = ev.sourceAttribute.replace("_edititem", "");
|
||||
console.log(basename);
|
||||
// Don't need count - you can edit that directly
|
||||
getAttrs([`${basename}_name`, `${basename}_itemweight`,
|
||||
`${basename}_itemsystem`, `${basename}_itemnotes`,
|
||||
`${basename}_itemmaterial`], (item) => {
|
||||
console.log(item);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
} // End 'cheap-module' scope
|
||||
|
||||
@@ -12917,7 +12989,7 @@ J E + C + B
|
||||
// - cdata.targetcrit = values.target_crit || '';
|
||||
// - cdata.attackmod = The numeric attack modifer - all mods
|
||||
// - cdata.attackmoddescription = Description of the above
|
||||
// - cdata.maneuver_penalty = values.maneuver_penalty || 0;
|
||||
// - cdata.statuspenalty = values.statuspenalty || 0;
|
||||
|
||||
//
|
||||
// Results are:
|
||||
@@ -13113,9 +13185,9 @@ J E + C + B
|
||||
attackmod += cdata.attackbonus;
|
||||
attackmoddescription = ` +${cdata.attackbonus} [Attack Bonus]`;
|
||||
|
||||
if (cdata.maneuver_penalty < 0) {
|
||||
attackmod += cdata.maneuver_penalty;
|
||||
attackmoddescription += ` ${cdata.maneuver_penalty} [Maneuver Penalty]`;
|
||||
if (cdata.statuspenalty < 0) {
|
||||
attackmod += cdata.statuspenalty;
|
||||
attackmoddescription += ` ${cdata.statuspenalty} [Status Penalty]`;
|
||||
}
|
||||
|
||||
if (cdata.targetsize < cdata.attackersize) {
|
||||
@@ -13172,6 +13244,11 @@ J E + C + B
|
||||
attackmod -= cdata.parrymod;
|
||||
}
|
||||
|
||||
if (cdata.ranged_penalty) {
|
||||
attackmoddescription += ` -${cdata.ranged_penalty} [Armor Ranged Penalty]`;
|
||||
attackmod -= cdata.ranged_penalty;
|
||||
}
|
||||
|
||||
attackmod += cdata.othermod;
|
||||
if (cdata.othermod > 0) {
|
||||
attackmoddescription += ` +${cdata.othermod} [Other]`;
|
||||
@@ -13193,7 +13270,7 @@ J E + C + B
|
||||
`{\{target=${cdata.targetname}}} ` +
|
||||
`{\{targetescaped=${cdata.targetescaped}}} ` +
|
||||
`{\{weapon=${cdata.attackname}}} ` +
|
||||
`{\{maneuverpenalty=${cdata.maneuver_penalty}}} ` +
|
||||
`{\{statuspenalty=${cdata.statu_penalty}}} ` +
|
||||
`{\{attackmoddescription=[[0]]}} ` +
|
||||
`{\{targetname=[[0]]}} ` +
|
||||
"{\{hitlocation=[[1d7]] }} " +
|
||||
@@ -13223,10 +13300,12 @@ J E + C + B
|
||||
`${basename}_attackfumble`,
|
||||
`${basename}_attacktype`,
|
||||
'size', `${basename}_attacksize`,
|
||||
'maneuver_penalty', 'statuseffect',
|
||||
'statuspenalty', 'statuseffect',
|
||||
'target_name', 'target_db', 'target_at',
|
||||
'target_size', 'target_crit',
|
||||
'parrymod', 'othermod',
|
||||
// Ranged Penalty from armor
|
||||
'ranged_penalty',
|
||||
// Used depending on type: So get melee, missile and spell
|
||||
'attackuseap', 'aptrack_melee', 'aptrack_missile', 'aptrack_spell',
|
||||
'token_id'
|
||||
@@ -13255,16 +13334,19 @@ J E + C + B
|
||||
cdata.statuseffect = parseIntDefault(values.statuseffect, 0);
|
||||
cdata.parrymod = parseIntDefault(values.parrymod, 0);
|
||||
cdata.othermod = parseIntDefault(values.othermod, 0);
|
||||
cdata.maneuver_penalty = parseIntDefault(values.maneuver_penalty, 0);
|
||||
cdata.statuspenalty = parseIntDefault(values.statuspenalty, 0);
|
||||
cdata.useap = (values.attackuseap == 'on') ? true : false;
|
||||
// Use the attack type to get the right AP
|
||||
cdata.attacktype = values[`${basename}_attacktype`];
|
||||
if (cdata.attacktype == 'melee') {
|
||||
cdata.apused = parseIntDefault(values.aptrack_melee, 0);
|
||||
cdata.ranged_penalty = 0;
|
||||
} else if (cdata.attacktype == 'ranged') {
|
||||
cdata.apused = parseIntDefault(values.aptrack_missile, 0);
|
||||
cdata.ranged_penalty = parseIntDefault(values.ranged_penalty, 0);
|
||||
} else if (cdata.attacktype == 'directed') {
|
||||
cdata.apused = parseIntDefault(values.aptrack_spell, 0);
|
||||
cdata.ranged_penalty = 0;
|
||||
} else {
|
||||
cdata.apused = 0;
|
||||
}
|
||||
@@ -13534,6 +13616,10 @@ actions = [
|
||||
const actionevents = actions.map(name => `change:aptrack_${name}`).join(" ");
|
||||
|
||||
onCheck(actionevents, (ev) => {
|
||||
if (ev.newValue == null) {
|
||||
rmuwarn("Action new value is null/undefined - ignoring");
|
||||
return;
|
||||
}
|
||||
var select = ev.sourceAttribute.match(/aptrack_(.*)/)[1];
|
||||
toclear = {}
|
||||
for (var action of actions) {
|
||||
@@ -14069,7 +14155,7 @@ function updatePenalties() {
|
||||
let total = parseIntDefault(ev.fatigue, 0) +
|
||||
parseIntDefault(ev.hppenalty, 0) +
|
||||
parseIntDefault(ev.injurypenalty, 0);
|
||||
setAttrs({maneuver_penalty: total, bar3_value: total});
|
||||
setAttrs({statuspenalty: total, bar3_value: total});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14108,7 +14194,7 @@ onCheck("clicked:statusrest", (_) => {
|
||||
|
||||
onCheck("clicked:rollinitiative", () => {
|
||||
// roll20 treats floor as round to negative infinity, so ceil since they should be neagtive
|
||||
startRoll('&{template:rmuinit} [[ [[ 2d10 ]] + @{initiative} + [[ceil(@{maneuver_penalty} / 10)]] &{tracker}]] ' +
|
||||
startRoll('&{template:rmuinit} [[ [[ 2d10 ]] + @{initiative} + [[ceil(@{statuspenalty} / 10)]] &{tracker}]] ' +
|
||||
'{\{actor=@{character_name} }} ' +
|
||||
'{\{bonus=@{initiative} }} ' +
|
||||
'{\{total=$[[2]]}} {\{roll=$[[0]]}} ' +
|
||||
@@ -14145,35 +14231,37 @@ creature.getLongSize = function(shortname) {
|
||||
// Weapon (we) and ranged weapon are special (rw)
|
||||
// Require table and size for each.
|
||||
creature.weaponcodes = {
|
||||
be: { table: "Beak", size: 0 },
|
||||
bi: { table: "Bite", size: 0 },
|
||||
cl: { table: "Claw", size: 0 },
|
||||
cr: { table: "Crush", size: 0 },
|
||||
gr: { table: "Grapple", size: 0 },
|
||||
ho: { table: "Horn", size: 0 },
|
||||
ra: { table: "Ram", size: 0 },
|
||||
sw: { table: "Unarmed Sweeps", size: 0 },
|
||||
un: { table: "Unarmed Strike", size: 0 },
|
||||
si: { table: "Stinger", size: 0 },
|
||||
tr: { table: "Trample", size: 0 },
|
||||
be: { table: "Beak", size: 0, type: "melee" },
|
||||
bi: { table: "Bite", size: 0, type: "melee" },
|
||||
cl: { table: "Claw", size: 0, type: "melee" },
|
||||
cr: { table: "Crush", size: 0, type: "melee" },
|
||||
gr: { table: "Grapple", size: 0, type: "melee" },
|
||||
ho: { table: "Horn", size: 0, type: "melee" },
|
||||
ra: { table: "Ram", size: 0, type: "melee" },
|
||||
sw: { table: "Unarmed Sweeps", size: 0, type: "melee" },
|
||||
un: { table: "Unarmed Strike", size: 0, type: "melee" },
|
||||
si: { table: "Stinger", size: 0, type: "melee" },
|
||||
tr: { table: "Trample", size: 0, type: "melee" },
|
||||
// FXIME: Hacky
|
||||
we: { table: "Arming Sword", size: 0 },
|
||||
da: { table: "Dagger", size: 0 },
|
||||
rp: { table: "Rapier", size: 0 },
|
||||
as: { table: "Arming Sword", size: 0 },
|
||||
bs: { table: "Broadsword", size: 0 },
|
||||
sc: { table: "Scimitar", size: 0 },
|
||||
fa: { table: "Falchion", size: 0 },
|
||||
ma: { table: "Mace", size: 0 },
|
||||
cu: { table: "Club", size: 0 },
|
||||
lc: { table: "Club", size: 1 },
|
||||
sb: { table: "Bow, Short", size: 0 },
|
||||
lb: { table: "Bow, Long", size: 0 },
|
||||
ss: { table: "Arming Sword", size: -1 },
|
||||
ls: { table: "Arming Sword", size: 1 },
|
||||
fa: { table: "Falchion", size: 0 },
|
||||
|
||||
we: { table: "Arming Sword", size: 0, type: "melee" },
|
||||
da: { table: "Dagger", size: 0, type: "melee" },
|
||||
rp: { table: "Rapier", size: 0, type: "melee" },
|
||||
as: { table: "Arming Sword", size: 0, type: "melee" },
|
||||
bs: { table: "Broadsword", size: 0, type: "melee" },
|
||||
sc: { table: "Scimitar", size: 0, type: "melee" },
|
||||
fa: { table: "Falchion", size: 0, type: "melee" },
|
||||
ma: { table: "Mace", size: 0, type: "melee" },
|
||||
cu: { table: "Club", size: 0, type: "melee" },
|
||||
lc: { table: "Club", size: 1, type: "melee" },
|
||||
sb: { table: "Bow, Short", size: 0, type: "ranged" },
|
||||
lb: { table: "Bow, Long", size: 0, type: "ranged", },
|
||||
ss: { table: "Arming Sword", size: -1, type: "melee" },
|
||||
ls: { table: "Arming Sword", size: 1, type: "melee" },
|
||||
fa: { table: "Falchion", size: 0, type: "melee" },
|
||||
|
||||
lc: { table: "Crossbow", size: 0, type: "ranged" },
|
||||
hc: { table: "Crossbow", size: 1, type: "ranged" },
|
||||
hx: { table: "Crossbow", size: -1, type: "ranged" },
|
||||
|
||||
/*
|
||||
Two-handed Sword ts
|
||||
@@ -14192,8 +14280,6 @@ Spear sp
|
||||
Halberd ha
|
||||
Pole Axe px
|
||||
Sling sl
|
||||
Light Crossbow lc
|
||||
Heavy Crossbow hc
|
||||
Bola bo
|
||||
Javelin ja
|
||||
Dagger (Thrown) td
|
||||
@@ -14289,7 +14375,7 @@ creature.newCreature = function(name, jdata) {
|
||||
updates.injurypenalty = 0;
|
||||
updates.endurance = cdata.Fatigue;
|
||||
updates.fatigue = 0;
|
||||
updates.maneuver_penalty = 0;
|
||||
updates.status_penalty = 0;
|
||||
|
||||
updates.rr_channeling_bonus = cdata.Ch;
|
||||
updates.rr_essence_bonus = cdata.Ess;
|
||||
@@ -14421,7 +14507,7 @@ onCheck('clicked:repeating_creatureattack:creaturemakeattack', (ev) => {
|
||||
'target_at',
|
||||
'target_size',
|
||||
'target_crit',
|
||||
'maneuver_penalty',
|
||||
'status_penalty',
|
||||
'statuseffect',
|
||||
];
|
||||
getAttrs(toget, (values) => {
|
||||
@@ -14443,7 +14529,7 @@ onCheck('clicked:repeating_creatureattack:creaturemakeattack', (ev) => {
|
||||
cdata.targetsize = parseIntDefault(values.target_size, 5);
|
||||
cdata.targetcrit = parseIntDefault(values.target_crit, 0);
|
||||
cdata.statuseffect = parseIntDefault(values.statuseffect, 0);
|
||||
cdata.maneuver_penalty = parseIntDefault(values.maneuver_penalty, 0);
|
||||
cdata.status_penalty = parseIntDefault(values.status_penalty, 0);
|
||||
// FIXME:
|
||||
cdata.parrymod = 0;
|
||||
cdata.othermod = 0;
|
||||
@@ -14940,6 +15026,14 @@ function doVersionCheck(version) {
|
||||
version = 7;
|
||||
}
|
||||
|
||||
if (version == 7) {
|
||||
// updatePenalties in in status; old value will be updateed by equipment update
|
||||
addPendingFunction("Upgrade to 8: Manuever_penalty -> statuspenalty", updatePenalties);
|
||||
addPendingFunction("Upgrade to 8: Update Equipement", RMUInventory.updateEquiopement);
|
||||
version = 8;
|
||||
}
|
||||
|
||||
|
||||
if (oldversion != version) {
|
||||
addPendingFunction(`Version update to ${version}`, () => {
|
||||
setAttrsPending({version: version});
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"legacy": false,
|
||||
"printable": true,
|
||||
"compendium": "RMU",
|
||||
"version": "1733207371"
|
||||
"version": "1733815042"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
# 2024-12-10
|
||||
|
||||
- Finalise handling of Professions with selectable Base lists
|
||||
- Alchemists are the first users
|
||||
- Creatures: Add hx for Hand Crossbows, and a few other attacks
|
||||
- Half shield bonus skill for partial block - not the full one.
|
||||
- Try and squash the super long list of errors when you first update stateud
|
||||
- Compendium data: Move a long bow to Bow, Long for the table.
|
||||
- Creatures: Creatures can now roll skills.
|
||||
- Equipment:
|
||||
- Perception Penalties are now applied
|
||||
- Ranged Penalties are now applied.
|
||||
- Take a terrible guess if it's a ranged or melee weapon when adding attacks
|
||||
- Creatures now have framework for type of attack (melee, ranged, directed)
|
||||
- Does not (yet) change attack
|
||||
- Add test to validate attack types
|
||||
- Change 'maneuver_penalty' -> status_penalty
|
||||
- Update everywhere except inventory; it was a collision of names before.
|
||||
|
||||
# 2024-12-3
|
||||
|
||||
- Initiative modified by maneuver penalty
|
||||
@@ -5,6 +24,7 @@
|
||||
- Add tests and validations for creatures.
|
||||
- All creature weapons now have a size (usually 0)
|
||||
- Display a message when an injury takes a character to negative hits.
|
||||
- Fix a stupid misplaced class for RRs
|
||||
|
||||
# 2024-11-26
|
||||
|
||||
|
||||
Reference in New Issue
Block a user