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:
Brett Nash
2024-12-09 23:17:33 -08:00
parent 1fd218fedb
commit 43900cb34c
3 changed files with 253 additions and 139 deletions
+232 -138
View File
@@ -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}&nbsp;[Skill&nbsp;Bonus]`
{
const manp = parseIntDefault(cdata.maneuver_penalty, 0);
const manp = parseIntDefault(cdata.statuspenalty, 0);
if (manp < 0) {
mlog += ` ${manp}&nbsp;[Maneuver&nbsp;Penalty]`;
mlog += ` ${manp}&nbsp;[Status&nbsp;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}&nbsp;[Maneuver&nbsp;Penalty]`;
mlog += ` ${manp}&nbsp;[Status&nbsp;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}&nbsp;[Attack&nbsp;Bonus]`;
if (cdata.maneuver_penalty < 0) {
attackmod += cdata.maneuver_penalty;
attackmoddescription += ` ${cdata.maneuver_penalty}&nbsp;[Maneuver&nbsp;Penalty]`;
if (cdata.statuspenalty < 0) {
attackmod += cdata.statuspenalty;
attackmoddescription += ` ${cdata.statuspenalty}&nbsp;[Status&nbsp;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}&nbsp;[Armor Ranged Penalty]`;
attackmod -= cdata.ranged_penalty;
}
attackmod += cdata.othermod;
if (cdata.othermod > 0) {
attackmoddescription += ` +${cdata.othermod}&nbsp;[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});
+1 -1
View File
@@ -8,5 +8,5 @@
"legacy": false,
"printable": true,
"compendium": "RMU",
"version": "1733207371"
"version": "1733815042"
}
+20
View File
@@ -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