Ranged attacks
This commit is contained in:
@@ -556,7 +556,16 @@ export class CrucibleActor extends Actor {
|
||||
rollData.forceRollDisadvantage = this.isForcedRollDisadvantage()
|
||||
rollData.noAdvantage = this.isNoAdvantage()
|
||||
if ( rollData.defenderTokenId) {
|
||||
let defender = game.canvas.tokens.get(rollData.defenderTokenId).actor
|
||||
let defenderToken = game.canvas.tokens.get(rollData.defenderTokenId)
|
||||
let defender = defenderToken.actor
|
||||
|
||||
// Distance management
|
||||
if ( this.token) {
|
||||
const ray = new Ray(this.token.object.center, defenderToken.center)
|
||||
rollData.tokensDistance = canvas.grid.measureDistances([{ray}], {gridSpaces:false})[0] / canvas.grid.grid.options.dimensions.distance
|
||||
} else {
|
||||
ui.notifications.info("No token connected to this actor, unable to compute distance.")
|
||||
}
|
||||
if (defender ) {
|
||||
rollData.forceAdvantage = defender.isAttackerAdvantage()
|
||||
rollData.advantageFromTarget = true
|
||||
@@ -625,6 +634,10 @@ export class CrucibleActor extends Actor {
|
||||
if ( !rollData.forceDisadvantage) { // This is an attack, check if disadvantaged
|
||||
rollData.forceDisadvantage = this.isAttackDisadvantage()
|
||||
}
|
||||
if (rollData.weapon.system.isranged && rollData.tokensDistance > CrucibleUtility.getWeaponMaxRange(rollData.weapon) ) {
|
||||
ui.notifications.warn(`Your target is out of range of your weapon (max: ${CrucibleUtility.getWeaponMaxRange(rollData.weapon)} - current : ${rollData.tokensDistance})` )
|
||||
return
|
||||
}
|
||||
this.startRoll(rollData)
|
||||
} else {
|
||||
ui.notifications.warn("Unable to find the relevant skill for weapon " + weapon.name)
|
||||
@@ -663,6 +676,23 @@ export class CrucibleActor extends Actor {
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
rollDefenseRanged(attackRollData) {
|
||||
let rollData = this.getCommonRollData()
|
||||
rollData.defenderTokenId = undefined // Cleanup
|
||||
rollData.mode = "rangeddefense"
|
||||
rollData.attackRollData = duplicate(attackRollData)
|
||||
rollData.sizeDice = CrucibleUtility.getSizeDice( this.system.biodata.size )
|
||||
rollData.effectiveRange = CrucibleUtility.getWeaponRange(attackRollData.weapon)
|
||||
rollData.tokensDistance = attackRollData.tokensDistance // QoL copy
|
||||
rollData.distanceBonusDice = Math.max(0, Math.floor((rollData.tokensDistance - rollData.effectiveRange) + 0.5))
|
||||
rollData.hasCover = "none"
|
||||
rollData.situational = "none"
|
||||
rollData.useshield = false
|
||||
rollData.shield = this.getEquippedShield()
|
||||
this.startRoll(rollData)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
rollShieldDie() {
|
||||
let shield = this.getEquippedShield()
|
||||
|
@@ -70,6 +70,12 @@ export class CrucibleRollDialog extends Dialog {
|
||||
html.find('#useshield').change((event) => {
|
||||
this.rollData.useshield = event.currentTarget.checked
|
||||
})
|
||||
html.find('#hasCover').change((event) => {
|
||||
this.rollData.hasCover = event.currentTarget.value
|
||||
})
|
||||
html.find('#situational').change((event) => {
|
||||
this.rollData.situational = event.currentTarget.value
|
||||
})
|
||||
|
||||
}
|
||||
}
|
@@ -10,6 +10,7 @@ const __color2RollTable = {
|
||||
blue: "Blue Armor Die", black: "Black Armor Die", green: "Green Armor Die", purple: "Purple Armor Die",
|
||||
white: "White Armor Die", red: "Red Armor Die", blackgreen: "Black & Green Armor Dice"
|
||||
}
|
||||
const __size2Dice = [ { nb: 0, dice: "d0" }, { nb: 5, dice: "d8" }, { nb: 3, dice: "d8" }, { nb: 2, dice: "d8" }, { nb: 1, dice: "d8" }, { nb: 1, dice: "d6" }, { nb: 1, noAddFirst: true, dice: "d6" }]
|
||||
|
||||
/* -------------------------------------------- */
|
||||
export class CrucibleUtility {
|
||||
@@ -56,7 +57,7 @@ export class CrucibleUtility {
|
||||
if (typeof text !== 'string') return text
|
||||
return text.charAt(0).toUpperCase() + text.slice(1)
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------- */
|
||||
static getSkills() {
|
||||
return duplicate(this.skills)
|
||||
@@ -150,6 +151,20 @@ export class CrucibleUtility {
|
||||
}
|
||||
return false
|
||||
}
|
||||
static getWeaponRange(weapon) {
|
||||
if (weapon && weapon.system.isranged) {
|
||||
let rangeValue = weapon.system.range.replace(/[^0-9]/g, '')
|
||||
return Number(rangeValue)
|
||||
}
|
||||
return false
|
||||
}
|
||||
static getWeaponMaxRange(weapon) {
|
||||
if (weapon && weapon.system.isranged) {
|
||||
let rangeValue = weapon.system.maxrange.replace(/[^0-9]/g, '')
|
||||
return Number(rangeValue)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async getRollTableFromDiceColor(diceColor, displayChat = true) {
|
||||
@@ -163,6 +178,10 @@ export class CrucibleUtility {
|
||||
return draw.results.length > 0 ? draw.results[0] : undefined
|
||||
}
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
static getSizeDice(sizeValue) {
|
||||
return __size2Dice[sizeValue]
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async getCritical(level, weapon) {
|
||||
@@ -191,6 +210,15 @@ export class CrucibleUtility {
|
||||
actor.rollDefenseMelee(rollData)
|
||||
}
|
||||
})
|
||||
html.on("click", '.roll-defense-ranged', event => {
|
||||
let rollId = $(event.currentTarget).data("roll-id")
|
||||
let rollData = CrucibleUtility.getRollData(rollId)
|
||||
let defender = game.canvas.tokens.get(rollData.defenderTokenId).actor
|
||||
if (defender && (game.user.isGM || defender.isOwner)) {
|
||||
defender.rollDefenseRanged(rollData)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
@@ -290,12 +318,12 @@ export class CrucibleUtility {
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async displayDefenseMessage(rollData) {
|
||||
if (rollData.mode == "weapon" && rollData.defenderTokenId) {
|
||||
if (rollData.mode == "weapon" && rollData.defenderTokenId) {
|
||||
let defender = game.canvas.tokens.get(rollData.defenderTokenId).actor
|
||||
if (game.user.isGM || (game.user.character && game.user.character.id == defender.id)) {
|
||||
rollData.defender = defender
|
||||
rollData.defenderWeapons = defender.getEquippedWeapons()
|
||||
rollData.isRollTarget = rollData.weapon?.system.isranged
|
||||
rollData.isRangedAttack = rollData.weapon?.system.isranged
|
||||
this.createChatWithRollMode(defender.name, {
|
||||
name: defender.name,
|
||||
alias: defender.name,
|
||||
@@ -373,7 +401,7 @@ export class CrucibleUtility {
|
||||
}
|
||||
if (result.critical_1 || result.critical_2) {
|
||||
let isDeadly = CrucibleUtility.isWeaponDeadly(rollData.attackRollData.weapon)
|
||||
result.critical = await this.getCritical((result.critical_1) ? "I" : "II", rollData.attackRollData.weapon )
|
||||
result.critical = await this.getCritical((result.critical_1) ? "I" : "II", rollData.attackRollData.weapon)
|
||||
result.criticalText = result.critical.text
|
||||
}
|
||||
this.createChatWithRollMode(rollData.alias, {
|
||||
@@ -480,12 +508,26 @@ export class CrucibleUtility {
|
||||
skill.system.skilldice = __skillLevel2Dice[skill.system.level]
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static getDiceFromCover(cover) {
|
||||
if (cover == "cover50") return 1
|
||||
return 0
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
static getDiceFromSituational(cover) {
|
||||
if (cover == "prone") return 1
|
||||
if (cover == "dodge") return 1
|
||||
if (cover == "moving") return 1
|
||||
if (cover == "engaged") return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static async rollCrucible(rollData) {
|
||||
|
||||
let actor = game.actors.get(rollData.actorId)
|
||||
|
||||
// ability/save => 0
|
||||
// ability/save/size => 0
|
||||
let diceFormula
|
||||
let startFormula = "0d6cs>=5"
|
||||
if (rollData.ability) {
|
||||
@@ -494,6 +536,10 @@ export class CrucibleUtility {
|
||||
if (rollData.save) {
|
||||
startFormula = String(rollData.save.value) + "d6cs>=5"
|
||||
}
|
||||
if (rollData.sizeDice) {
|
||||
let nb = rollData.sizeDice.nb + rollData.distanceBonusDice + this.getDiceFromCover(rollData.hasCover) + this.getDiceFromSituational(rollData.situational)
|
||||
startFormula = String(nb) + String(rollData.sizeDice.dice) + "cs>=5"
|
||||
}
|
||||
diceFormula = startFormula
|
||||
|
||||
// skill => 2
|
||||
@@ -582,16 +628,16 @@ export class CrucibleUtility {
|
||||
rollData.roll = myRoll
|
||||
rollData.nbSuccess = myRoll.total
|
||||
|
||||
if ( rollData.rollAdvantage == "none" && rollData.forceRollAdvantage) {
|
||||
if (rollData.rollAdvantage == "none" && rollData.forceRollAdvantage) {
|
||||
rollData.rollAdvantage = "roll-advantage"
|
||||
}
|
||||
if ( rollData.rollAdvantage == "none" && rollData.forceRollDisadvantage) {
|
||||
}
|
||||
if (rollData.rollAdvantage == "none" && rollData.forceRollDisadvantage) {
|
||||
rollData.rollAdvantage = "roll-disadvantage"
|
||||
}
|
||||
if (rollData.rollAdvantage != "none" ) {
|
||||
}
|
||||
if (rollData.rollAdvantage != "none") {
|
||||
|
||||
rollData.rollOrder = 1
|
||||
rollData.rollType = (rollData.rollAdvantage == "roll-advantage") ? "Advantage": "Disadvantage"
|
||||
rollData.rollType = (rollData.rollAdvantage == "roll-advantage") ? "Advantage" : "Disadvantage"
|
||||
this.createChatWithRollMode(rollData.alias, {
|
||||
content: await renderTemplate(`systems/fvtt-crucible-rpg/templates/chat-generic-result.html`, rollData)
|
||||
})
|
||||
@@ -599,7 +645,7 @@ export class CrucibleUtility {
|
||||
rollData.rollOrder = 2
|
||||
let myRoll2 = new Roll(diceFormula).roll({ async: false })
|
||||
await this.showDiceSoNice(myRoll2, game.settings.get("core", "rollMode"))
|
||||
|
||||
|
||||
rollData.roll = myRoll2 // Tmp switch to display the proper results
|
||||
rollData.nbSuccess = myRoll2.total
|
||||
this.createChatWithRollMode(rollData.alias, {
|
||||
|
Reference in New Issue
Block a user