diff --git a/module/actor-sheet.js b/module/actor-sheet.js index 557f3c0..30e89cf 100644 --- a/module/actor-sheet.js +++ b/module/actor-sheet.js @@ -98,6 +98,11 @@ export class SoSActorSheet extends ActorSheet { const skill = this.actor.getOwnedItem(li.data("item-id")); this.actor.rollSkill(skill); }); + html.find('.weapon-label a').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const weapon = this.actor.getOwnedItem(li.data("item-id")); + this.actor.rollWeapon(weapon); + }); html.find('.skill-value').change((event) => { let skillName = event.currentTarget.attributes.skillname.value; //console.log("Competence changed :", skillName); diff --git a/module/actor.js b/module/actor.js index 5b3c258..6c2fd52 100644 --- a/module/actor.js +++ b/module/actor.js @@ -78,11 +78,13 @@ export class SoSActor extends Actor { this.cardDeck.drawEdge( 1 ); this.saveDeck(); } - /* -------------------------------------------- */ - resetDeck() { - - } + /* -------------------------------------------- */ + resetDeck( ) { + this.cardDeck.resetDeck(); + this.saveDeck(); + } + /* -------------------------------------------- */ saveDeck( ) { let deck = { deck: duplicate(this.cardDeck.data.deck), @@ -210,7 +212,6 @@ export class SoSActor extends Actor { } let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/dialog-flip.html', flipData); new SoSFlipDialog(flipData, html).render(true); - } /* -------------------------------------------- */ @@ -218,6 +219,7 @@ export class SoSActor extends Actor { let flipData = { mode: 'skill', statList: duplicate(this.data.data.stats), + selectedStat: 'strength', consequencesList: duplicate( this.getApplicableConsequences() ), skill: duplicate(skill), actor: this, @@ -229,4 +231,40 @@ export class SoSActor extends Actor { let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/dialog-flip.html', flipData); new SoSFlipDialog(flipData, html).render(true); } + + /* -------------------------------------------- */ + async rollWeapon( weapon ) { + let target = SoSUtility.getTarget(); + let skill, selectedStatName; + if ( weapon.data.data.category == 'ballistic' || weapon.data.data.category == 'laser' ) { + skill = this.data.items.find( item => item.name == 'Guns'); + selectedStatName = 'dexterity'; + } else if ( weapon.data.data.category == 'melee' ) { + skill = this.data.items.find( item => item.name == 'Melee'); + selectedStatName = 'dexterity'; + } else if ( weapon.data.data.category == 'grenade' ) { + skill = this.data.items.find( item => item.name == 'Athletics'); + selectedStatName = 'dexterity'; + } + + let flipData = { + mode: 'weapon', + weapon: duplicate(weapon.data), + statList: duplicate(this.data.data.stats), + target: target, + selectedStat: selectedStatName, + consequencesList: duplicate( this.getApplicableConsequences() ), + skill: duplicate(skill), + actor: this, + modifierList: SoSUtility.fillRange(-10, +10), + tnList: SoSUtility.fillRange(6, 20), + malusConsequence: 0 + } + + console.log(flipData); + + flipData.statList['nostat'] = { label: "No stat (ie defaulting skills)", value: 0, cardsuit: "none" } + let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/dialog-flip.html', flipData); + new SoSFlipDialog(flipData, html).render(true); + } } diff --git a/module/sos-card-deck.js b/module/sos-card-deck.js index f1fca53..e33ebfb 100644 --- a/module/sos-card-deck.js +++ b/module/sos-card-deck.js @@ -1,3 +1,4 @@ +import { SoSUtility } from "./sos-utility.js"; /* -------------------------------------------- */ const NB_POKER_CARD = 54; @@ -165,7 +166,7 @@ export class SoSCardDeck { this.setJoker( flipData ); } else { - console.log("First card : ", flipData.cardSlot[0].card1); + //console.log("First card : ", flipData.cardSlot[0].card1); // Face check for first card flipData.cardSlot[0].value1 = this.getCardValue(flipData.cardSlot[0].card1.cardName); flipData.cardSlot[0].isFace1 = this.isCardFace(flipData.cardSlot[0].card1.cardName); @@ -212,14 +213,21 @@ export class SoSCardDeck { // Card Total flipData.cardTotal = flipData.cardSlot[0].total; + flipData.cardSlotIndex = 0; if ( flipData.fullTrump ) { flipData.cardTotal = flipData.cardSlot[0].total + flipData.cardSlot[1].total; } else if (flipData.isTrump) { - flipData.cardTotal = (flipData.cardSlot[0].total > flipData.cardSlot[1].total) ? flipData.cardSlot[0].total : flipData.cardSlot[1].total; + if (flipData.cardSlot[0].total > flipData.cardSlot[1].total ) { + flipData.cardSlotIndex = 0; + flipData.cardTotal = flipData.cardSlot[0].total; + } else { + flipData.cardSlotIndex = 0; + flipData.cardTotal = flipData.cardSlot[1].total; + } } // Compute final result and compare - if ( flipData.mode == 'stat' ) { + if ( flipData.mode == 'stat' || flipData.mode == 'weapon' ) { flipData.baseScore = flipData.stat.value + flipData.malusConsequence; } else if (flipData.mode == 'skill') { flipData.baseScore = Math.floor(flipData.stat.value/2) + flipData.skill.data.value + flipData.malusConsequence @@ -228,13 +236,43 @@ export class SoSCardDeck { flipData.magnitude = flipData.finalScore - flipData.tn; flipData.result = (flipData.magnitude >= 0) ? "Success": "Failure"; - console.log(flipData); + //console.log(flipData); this.data.actor.saveDeck(); flipData.alias = this.data.actor.name; let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/chat-flip.html', flipData); ChatMessage.create( { content: html }); + + if ( flipData.mode == 'weapon' && flipData.magnitude >= 0 && !flipData.isJoker) { // Success + this.processWeapon( flipData ); + } + + if (flipData.isJoker) { // Critical mismatch ! + // TODO + } } + /* -------------------------------------------- */ + async processWeapon( flipData ) { + flipData.damageCardsuit = flipData.cardSlot[flipData.cardSlotIndex].cardsuit; + let damageKey = 'damage_'+ flipData.damageCardsuit; + flipData.damageString = flipData.weapon.data[damageKey]; + let damageRegexp = flipData.damageString.match( /(\d*)([LMSC])/i ); + flipData.damageValue = damageRegexp[1]; + flipData.damageSeverity = damageRegexp[2]; + + // Now process damage + if ( flipData.target) { + if ( game.user.isGM ) { // Direct access + SoSUtility.applyDamage( flipData ); + } else { + game.socket.emit("system.foundryvtt-shadows-over-sol", { + msg: "msg_defense", data: flipData } ); + } + } else { + let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/chat-damage-only.html', flipData ); + ChatMessage.create( { content: html }); + } + } /* -------------------------------------------- */ getDeckHTML( ) { diff --git a/module/sos-combat.js b/module/sos-combat.js index de1d05d..27389c1 100644 --- a/module/sos-combat.js +++ b/module/sos-combat.js @@ -116,6 +116,18 @@ export class SoSCombat extends Combat { return 0; } + /* -------------------------------------------- */ + getAPFromActor( actorId ) { + for( let combatant of this.combatants) { + //console.log(combatant); + if ( combatant.actor.data._id == actorId ) { + let phase = this.phaseSetup[combatant._id]; + return phase.remainingAP; + } + } + return 0; + } + /* -------------------------------------------- */ async setupActorActions(actionConf) { console.log("Setting combat for phase : ", actionConf); @@ -124,7 +136,7 @@ export class SoSCombat extends Combat { // Keep track this.phaseSetup[actionConf.combatantId] = actionConf; - console.log( this.combatants); + console.log( this.combatants ); //let combatant = this.combatants.find( comb => comb._id == actionConf.combatantId); await this.setInitiative( actionConf.combatantId, this.getPhaseRank( actionConf ) ); diff --git a/module/sos-flip-dialog.js b/module/sos-flip-dialog.js index 63f6d04..4110fe8 100644 --- a/module/sos-flip-dialog.js +++ b/module/sos-flip-dialog.js @@ -1,5 +1,7 @@ +/* -------------------------------------------- */ import { SoSUtility } from "./sos-utility.js"; +/* -------------------------------------------- */ export class SoSFlipDialog extends Dialog { /* -------------------------------------------- */ @@ -25,7 +27,7 @@ export class SoSFlipDialog extends Dialog { /* -------------------------------------------- */ updateScoreBase( ) { let scoreBase = 0; - if ( this.flipData.mode == 'skill') { + if ( this.flipData.mode == 'skill' || this.flipData.mode == 'weapon' ) { let statKey = $('#statSelect').val(); this.flipData.stat = duplicate( this.flipData.statList[ statKey ] ); scoreBase = Math.floor(this.flipData.statList[ statKey ].value / 2) + this.flipData.skill.data.value; @@ -49,14 +51,15 @@ export class SoSFlipDialog extends Dialog { this.updateScoreBase(); $('.edge-card').click((event) => { - let flipData = duplicate(this.flipData); + let flipData = this.flipData; flipData.modifier = $('#modifier').val(); - flipData.tn = $('#tn').val(); + flipData.tn = (flipData.target) ? flipData.target.actor.data.data.scores.defense.value : $('#tn').val(); flipData.edgeName = event.currentTarget.attributes['data-edge-card'].value; flipData.cardOrigin = "Edge"; - if ( flipData.mode == 'skill') { + if ( flipData.mode == 'skill' || flipData.mode == 'weapon') { flipData.stat = duplicate( flipData.statList[ $('#statSelect').val() ] ); } + console.log("CLICK:", flipData); this.flipData.actor.cardDeck.doFlipFromDeckOrEdge(flipData); this.onFlipClose(); }); @@ -105,15 +108,14 @@ export class SoSFlipDialog extends Dialog { } ); html.find('.class-view-deck').click((event) => { - let flipData = duplicate(this.flipData); + let flipData = this.flipData; flipData.modifier = html.find('#modifier').val(); - flipData.tn = html.find('#tn').val(); - if ( flipData.mode == 'skill') { + if ( flipData.mode == 'skill' || flipData.mode == 'weapon') { let statKey = $('#statSelect').val(); flipData.stat = duplicate( flipData.statList[ statKey ] ); } flipData.cardOrigin = "Deck"; - flipData.tn = html.find('#tn').val(); + flipData.tn = (flipData.target) ? flipData.target.actor.data.data.scores.defense.value : $('#tn').val(); dialog.flipData.actor.cardDeck.doFlipFromDeckOrEdge(flipData); dialog.onFlipClose(); }); diff --git a/module/sos-utility.js b/module/sos-utility.js index 02bb582..5a4bc08 100644 --- a/module/sos-utility.js +++ b/module/sos-utility.js @@ -1,4 +1,5 @@ /* -------------------------------------------- */ +import { SoSCombat } from "./sos-combat.js"; import { SoSDialogCombatActions } from "./sos-dialog-combat-actions.js"; /* -------------------------------------------- */ @@ -129,4 +130,102 @@ export class SoSUtility { }); } + /* -------------------------------------------- */ + static getTarget() { + if (game.user.targets && game.user.targets.size == 1) { + for (let target of game.user.targets) { + return target; + } + } + return undefined; + } + + /* -------------------------------------------- */ + static increaseSeverity( severity ) { + if ( severity == 'L') return 'M'; + if ( severity == 'M') return 'S'; + if ( severity == 'S') return 'C'; + if ( severity == 'C') return 'F'; + } + + /* -------------------------------------------- */ + static decreaseSeverity( severity ) { + if ( severity == 'C') return 'S'; + if ( severity == 'S') return 'M'; + if ( severity == 'M') return 'L'; + if ( severity == 'L') return 'N'; + } + + /* -------------------------------------------- */ + static getSeverityLevel( severity) { + if ( severity == 'C') return 4; + if ( severity == 'S') return 3; + if ( severity == 'M') return 2; + if ( severity == 'L') return 1; + return 0; + } + + /* -------------------------------------------- */ + static async applyDamage( flipData ) { + let dr = flipData.target.actor.data.data.scores.dr.value; + let shock = flipData.target.actor.data.data.scores.shock.value; + let defenseCritical = flipData.target.actor.data.data.scores.defense.critical; + flipData.damageStatus = 'apply_damage'; + + flipData.targetShock = shock; + flipData.targetDR = dr; + flipData.targetCritical = defenseCritical; + // DR management + if ( flipData.damageValue < dr) { + if (flipData.damageValue < dr / 2) { + flipData.damageStatus = "no_damage"; + // TODO : No damage ! + } else { + flipData.damageSeverity = this.decreaseSeverity(flipData.damageSeverity ); + if ( flipData.damageSeverity == 'N') { + flipData.damageStatus = "no_damage"; + } + } + } + + // Shock management + flipData.woundsList = []; + if ( flipData.damageValue >= shock) { + let incSeverity = Math.floor(flipData.damageValue / shock); + for (let i=0; i= defenseCritical); + + let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/chat-damage-target.html', flipData ); + ChatMessage.create( { content: html }); + + // Is target able to dodge ?? + let defender = game.actors.get( flipData.target.actor._id); + flipData.coverConsequence = defender.data.items.find( item => item.type == 'consequence' && item.name == 'Cover'); + flipData.APavailable = game.combat.getAPFromActor( defender.data._id ); + console.log("FLIPDATE : ", flipData); + if ( flipData.APavailable > 0) { + if ( (flipData.weapon.data.category == 'melee' ) || ( (flipData.weapon.data.category == 'laser' || flipData.weapon.data.category == 'ballistic') && + flipData.coverConsequence.data.severity != 'none') ) { + flipData.coverSeverityLevel = this.getSeverityLevel( flipData.coverConsequence.data.severity ) * 2; + flipData.coverSeverityFlag = (flipData.coverSeverityLevel > 0); + flipData.isMelee = (flipData.weapon.data.category == 'melee' ); + let melee = defender.data.items.find( item => item.type == 'skill' && item.name == 'Melee'); + flipData.defenderMelee = melee.data.value; + let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/chat-damage-request-dodge.html', flipData ); + ChatMessage.create( { content: html, whisper: [ChatMessage.getWhisperRecipients(flipData.target.actor.name), ChatMessage.getWhisperRecipients("GM") ] } ); + } + } + } } \ No newline at end of file diff --git a/system.json b/system.json index ed367d9..70d75e7 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "name": "foundryvtt-shadows-over-sol", "title": "Shadows over Sol", "description": "Shadows over Sol for FoundryVTT", - "version": "0.0.20", + "version": "0.0.21", "manifestPlusVersion": "1.0.0", "minimumCoreVersion": "0.7.5", "compatibleCoreVersion": "0.7.9", diff --git a/templates/actor-sheet.html b/templates/actor-sheet.html index f3695d9..9ec47f0 100644 --- a/templates/actor-sheet.html +++ b/templates/actor-sheet.html @@ -192,7 +192,7 @@ {{#each data.weapons as |weapon key|}}
  • - {{weapon.name}} + {{weapon.name}}
    {{#if weapon.data.equiped}}{{else}}{{/if}} @@ -206,7 +206,7 @@ {{#each data.armors as |armor key|}}
  • - {{armor.name}} + {{armor.name}}
    {{#if armor.data.worn}}{{else}}{{/if}} @@ -220,7 +220,7 @@ {{#each data.gears as |gear key|}}
  • - {{gear.name}} + {{gear.name}}
    {{#if armor.data.worn}}{{else}}{{/if}} diff --git a/templates/chat-damage-only.html b/templates/chat-damage-only.html new file mode 100644 index 0000000..ccf02c7 --- /dev/null +++ b/templates/chat-damage-only.html @@ -0,0 +1,8 @@ +

    {{alias}} has made damages with its weapon {{weapon.name}} !

    +
    + + + + +
    diff --git a/templates/chat-damage-request-dodge.html b/templates/chat-damage-request-dodge.html new file mode 100644 index 0000000..0fa1c84 --- /dev/null +++ b/templates/chat-damage-request-dodge.html @@ -0,0 +1,10 @@ +

    {{target.actor.name}} has {{remainingAP}} AP and can react to the attack from {{actor.name}} !

    +
    + {{#if coverSeverityFlag}} + + {{/if}} + {{#if isMelee}} + + {{/if}} + +
    diff --git a/templates/chat-damage-target.html b/templates/chat-damage-target.html new file mode 100644 index 0000000..50fa415 --- /dev/null +++ b/templates/chat-damage-target.html @@ -0,0 +1,17 @@ +

    {{alias}} has made damages with its weapon {{weapon.name}} against + {{target.actor.name}} !

    +
    + + + + + {{#if isCritical}} + + {{/if}} + +
      + {{#each woundsList as |severity key|}} +
    • 1 Wound of severity {{severity}}
    • + {{/each}} +
    +
    diff --git a/templates/dialog-flip.html b/templates/dialog-flip.html index 6aa5875..7cc34f0 100644 --- a/templates/dialog-flip.html +++ b/templates/dialog-flip.html @@ -18,7 +18,7 @@ @@ -43,10 +43,19 @@
    + {{#if target}} +
    +

    Target : {{target.actor.name}} - Defense : {{target.actor.data.data.scores.defense.value}}/{{target.actor.data.data.scores.defense.critical}}

    +
    + {{/if}} +
    - + + {{#if target}} + + {{else}} + {{/if}}
    - +