diff --git a/images/icons/icon_vice.webp b/images/icons/icon_vice.webp new file mode 100644 index 0000000..c16b213 Binary files /dev/null and b/images/icons/icon_vice.webp differ diff --git a/images/icons/icon_virtue.webp b/images/icons/icon_virtue.webp new file mode 100644 index 0000000..9e0b006 Binary files /dev/null and b/images/icons/icon_virtue.webp differ diff --git a/lang/en.json b/lang/en.json index 077404a..fdf14cd 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1,3 +1,25 @@ { - + "ITEM": { + "TypeRace": "Race", + "TypeRole": "Role", + "TypeAbility": "Ability", + "TypeSpecialisation": "Specialisation", + "TypePerk": "Perk", + "TypePower": "Power", + "TypeArmor": "Armor", + "TypeShield": "Shield", + "TypeEquipment": "Equipment", + "TypeWeapon": "Weapon", + "TypeEffect": "Effect", + "TypeMoney": "Money", + "TypeVirtue": "Virtue", + "TypeVice": "Vice", + "TypeVehiclehull": "Vehicule Hull", + "TypePowercoremodule": "Power Core Module", + "TypeMobilitymodule": "Mobility Module", + "TypeCombatmodule": "Combat Module", + "TypeVehiclemodule": "Vehicle Module", + "TypeVehicleweaponmodule" : "Vehicle Weapon Module", + "TypePropulsionmodule": "Propulsion module" + } } \ No newline at end of file diff --git a/modules/pegasus-actor-sheet.js b/modules/pegasus-actor-sheet.js index 2c9e865..f1e6b44 100644 --- a/modules/pegasus-actor-sheet.js +++ b/modules/pegasus-actor-sheet.js @@ -3,8 +3,8 @@ * @extends {ActorSheet} */ -import { PegasusUtility } from "./pegasus-utility.js"; -import { PegasusRollDialog } from "./pegasus-roll-dialog.js"; +import { PegasusUtility } from "./pegasus-utility.js" +import { PegasusRollDialog } from "./pegasus-roll-dialog.js" /* -------------------------------------------- */ export class PegasusActorSheet extends ActorSheet { @@ -78,15 +78,8 @@ export class PegasusActorSheet extends ActorSheet { /* -------------------------------------------- */ async openGenericRoll() { - let rollData = PegasusUtility.getBasicRollData() - rollData.alias = "Dice Pool Roll", - rollData.mode = "generic" - rollData.title = `Dice Pool Roll` - rollData.img = "icons/dice/d12black.svg" - rollData.isGeneric = true + let rollData = PegasusUtility.initGenericRoll() rollData.traumaState = this.actor.getTraumaState() - rollData.diceList = PegasusUtility.getDiceList() - rollData.dicePool = [] let rollDialog = await PegasusRollDialog.create( this.actor, rollData); rollDialog.render( true ); diff --git a/modules/pegasus-actor.js b/modules/pegasus-actor.js index 8345301..7d3698f 100644 --- a/modules/pegasus-actor.js +++ b/modules/pegasus-actor.js @@ -464,6 +464,9 @@ export class PegasusActor extends Actor { ui.notifications.warn("Stun level cannot go below 0") } let stunAbove = combat.stunlevel - combat.stunthreshold + if (stunAbove > 0) { + ChatMessage.create( { content: `${this.name} Stun threshold has been exceeded.`}) + } if (incDec > 0 && stunAbove > 0) { let delirium = duplicate(this.data.data.secondary.delirium) delirium.value -= incDec @@ -561,6 +564,27 @@ export class PegasusActor extends Actor { /* -------------------------------------------- */ async preprocessItem(event, item, onDrop = false) { + + // Pre-filter effects + if (item.data.type == 'effect' ) { + if ( this.checkMentalDisruption() && item.data.data.type == "mental" && item.data.data.genre == "positive") { + ChatMessage.create( { content: "Effects of this type cannot be applied while Disruption is applied, Use a Soft Action to remove Disruption"}) + return + } + if ( this.checkPhysicalDisruption() && item.data.data.type == "physical" && item.data.data.genre == "positive") { + ChatMessage.create( { content: "Effects of this type cannot be applied while Disruption is applied, Use a Soft Action to remove Disruption"}) + return + } + if ( this.checkMentalImmunity() && item.data.data.type == "mental" && item.data.data.genre == "negative") { + ChatMessage.create( { content: "Effects of this type cannot be applied while Immunity is applied"}) + return + } + if ( this.checkPhysicalImmunity() && item.data.data.type == "physical" && item.data.data.genre == "negative") { + ChatMessage.create( { content: "Effects of this type cannot be applied while Immunity is applied"}) + return + } + } + if (item.data.type == 'race') { this.applyRace(item.data) } else if (item.data.type == 'role') { @@ -833,6 +857,10 @@ export class PegasusActor extends Actor { if (item) { if (item.data.data.status == status) return;// Ensure we are really changing the status + if (this.checkNoPerksAllowed() ) { + ChatMessage( {content: "No perks activation allowed due to effect !"}) + return + } // Severe Trauma management if (this.getTraumaState() == "severetrauma") { @@ -977,7 +1005,79 @@ export class PegasusActor extends Actor { ChatMessage.create({ content: `${this.name} has used a Hero Level to reroll !` }) return biodata.currentlevelremaining } - + + /* -------------------------------------------- */ + checkIgnoreHealth() { + for (let effect of this.data.items) { + if (effect.type == "effect" && effect.data.data.ignorehealthpenalty) { + return true + } + } + return false + } + /* -------------------------------------------- */ + checkMentalDisruption() { + for (let effect of this.data.items) { + if (effect.type == "effect" && effect.data.data.mentaldisruption) { + return true + } + } + return false + } + /* -------------------------------------------- */ + checkPhysicalDisruption() { + for (let effect of this.data.items) { + if (effect.type == "effect" && effect.data.data.physicaldisruption) { + return true + } + } + return false + } + /* -------------------------------------------- */ + checkMentalImmunity() { + for (let effect of this.data.items) { + if (effect.type == "effect" && effect.data.data.mentalimmunity) { + return true + } + } + return false + } + /* -------------------------------------------- */ + checkPhysicalImmunity() { + for (let effect of this.data.items) { + if (effect.type == "effect" && effect.data.data.physicalimmunity) { + return true + } + } + return false + } + /* -------------------------------------------- */ + checkNoBonusDice() { + for (let effect of this.data.items) { + if (effect.type == "effect" && effect.data.data.nobonusdice) { + return true + } + } + return false + } + /* -------------------------------------------- */ + checkNoPerksAllowed() { + for (let effect of this.data.items) { + if (effect.type == "effect" && effect.data.data.noperksallowed) { + return true + } + } + return false + } + /* -------------------------------------------- */ + checkIfPossible() { + for (let effect of this.data.items) { + if (effect.type == "effect" && effect.data.data.isthispossible.length > 0) { + ChatMessage.create( { content: effect.data.data.isthispossible} ) + } + } + } + /* -------------------------------------------- */ async computeNRGHealth() { if (this.isOwner || game.user.isGM) { @@ -1021,9 +1121,10 @@ export class PegasusActor extends Actor { updates['data.nrg.value'] = nrgValue } - nrgValue = PegasusUtility.getDiceValue(this.data.data.statistics.mnd.value) + this.data.data.statistics.mnd.mod; - if (nrgValue != this.data.data.combat.stunthreshold) { - updates['data.combat.stunthreshold'] = nrgValue + let stunth = PegasusUtility.getDiceValue(this.data.data.statistics.phy.value) + PegasusUtility.getDiceValue(this.data.data.statistics.mnd.value) + PegasusUtility.getDiceValue(this.data.data.statistics.foc.value) + + this.data.data.statistics.mnd.mod + this.data.data.statistics.phy.mod + this.data.data.statistics.foc.mod + if (stunth != this.data.data.combat.stunthreshold) { + updates['data.combat.stunthreshold'] = stunth } let momentum = this.data.data.statistics.foc.value + this.data.data.statistics.foc.mod @@ -1038,7 +1139,6 @@ export class PegasusActor extends Actor { updates['data.mr.value'] = mrLevel } - let moralitythreshold = - (Number(PegasusUtility.getDiceValue(this.data.data.statistics.foc.value)) + Number(this.data.data.statistics.foc.mod)) if (moralitythreshold != this.data.data.biodata.moralitythreshold) { updates['data.biodata.moralitythreshold'] = moralitythreshold @@ -1063,20 +1163,20 @@ export class PegasusActor extends Actor { } if (Object.entries(updates).length > 0) { await this.update(updates) - this.computeThreatLevel() } - + this.computeThreatLevel() } if (this.isOwner || game.user.isGM) { // Update current hindrance level let hindrance = this.data.data.combat.hindrancedice - if (this.data.data.secondary.health.value < 0) { - if (this.data.data.secondary.health.value < -Math.floor((this.data.data.secondary.health.max + 1) / 2)) { // Severe wounded - hindrance += 3 - } else { - hindrance += 1 - + if ( !this.checkIgnoreHealth() ) { + if (this.data.data.secondary.health.value < 0) { + if (this.data.data.secondary.health.value < -Math.floor((this.data.data.secondary.health.max + 1) / 2)) { // Severe wounded + hindrance += 3 + } else { + hindrance += 1 + } } } this.data.data.combat.hindrancedice = hindrance @@ -1347,6 +1447,7 @@ export class PegasusActor extends Actor { rollData.levelRemaining = this.getLevelRemaining() rollData.activePerks = duplicate(this.getActivePerks()) rollData.diceList = PegasusUtility.getDiceList() + rollData.noBonusDice = this.checkNoBonusDice() rollData.dicePool = [] if (statKey) { diff --git a/modules/pegasus-item.js b/modules/pegasus-item.js index c6e6b98..c1a16bc 100644 --- a/modules/pegasus-item.js +++ b/modules/pegasus-item.js @@ -15,6 +15,8 @@ export const defaultItemImg = { weapon: "systems/fvtt-pegasus-rpg/images/icons/icon_meleeweapon.webp", shield: "systems/fvtt-pegasus-rpg/images/icons/icon_shield.webp", money: "systems/fvtt-pegasus-rpg/images/icons/icon_money.webp", + vice: "systems/fvtt-pegasus-rpg/images/icons/icon_vice.webp", + virtue: "systems/fvtt-pegasus-rpg/images/icons/icon_virtue.webp", } /** diff --git a/modules/pegasus-roll-dialog.js b/modules/pegasus-roll-dialog.js index f7a5b12..a609326 100644 --- a/modules/pegasus-roll-dialog.js +++ b/modules/pegasus-roll-dialog.js @@ -33,8 +33,8 @@ export class PegasusRollDialog extends Dialog { super(conf, options); - this.actor = actor; - this.rollData = rollData; + this.actor = actor + this.rollData = rollData } /* -------------------------------------------- */ @@ -106,6 +106,7 @@ export class PegasusRollDialog extends Dialog { manageWeapons(weaponIdx, toggled) { let weapon = this.rollData.weaponsList[weaponIdx] if (weapon) { + this.rollData.weapon = duplicate(weapon) if (toggled) { this.rollData.weaponName = weapon.weapon.name } else { diff --git a/modules/pegasus-utility.js b/modules/pegasus-utility.js index 63eab47..a898609 100644 --- a/modules/pegasus-utility.js +++ b/modules/pegasus-utility.js @@ -2,6 +2,7 @@ import { PegasusCombat } from "./pegasus-combat.js"; import { PegasusCommands } from "./pegasus-commands.js"; import { PegasusActorCreate } from "./pegasus-create-char.js"; +import { PegasusRollDialog } from "./pegasus-roll-dialog.js"; /* -------------------------------------------- */ const __level2Dice = ["d0", "d4", "d6", "d8", "d10", "d12"] @@ -14,7 +15,10 @@ export class PegasusUtility { /* -------------------------------------------- */ static async init() { - Hooks.on('renderChatLog', (log, html, data) => PegasusUtility.chatListeners(html)); + Hooks.on('renderChatLog', (log, html, data) => PegasusUtility.chatListeners(html)) + Hooks.on('targetToken', (user, token, flag) => PegasusUtility.targetToken(user, token, flag)) + Hooks.on('renderSidebarTab', (app, html, data) => PegasusUtility.addDiceRollButton(app, html, data)) + Hooks.on("getCombatTrackerEntryContext", (html, options) => { PegasusUtility.pushInitiativeOptions(html, options); }); @@ -54,6 +58,41 @@ export class PegasusUtility { }) } + /* -------------------------------------------- */ + static initGenericRoll() { + let rollData = PegasusUtility.getBasicRollData() + rollData.alias = "Dice Pool Roll", + rollData.mode = "generic" + rollData.title = `Dice Pool Roll` + rollData.img = "icons/dice/d12black.svg" + rollData.isGeneric = true + rollData.diceList = PegasusUtility.getDiceList() + rollData.dicePool = [] + rollData.traumaState = "none" + return rollData + } + + /* -------------------------------------------- */ + static async addDiceRollButton(app, html, data) { + if (app.tabName !== 'chat') return + let $chat_form = html.find('#chat-form') + const template = 'systems/fvtt-pegasus-rpg/templates/chat-roll-button.html' + renderTemplate(template, {}).then(c => { + if (c.length > 0) { + let $content = $(c) + $chat_form.before($content) + $content.find('#pegasus-chat-roll-button').on('click', async event => { + event.preventDefault() + let rollData = PegasusUtility.initGenericRoll() + rollData.isChatRoll = true + let rollDialog = await PegasusRollDialog.create( undefined, rollData) + rollDialog.render( true ) + }) + + } + }) + } + /* -------------------------------------------- */ static pushInitiativeOptions(html, options) { options.push({ name: "Apply -10", condition: true, icon: '', callback: target => { PegasusCombat.decInitBy10(target.data('combatant-id'), -10); } }) @@ -345,6 +384,7 @@ export class PegasusUtility { ui.notifications.warn(`No character linked to the player : reroll not allowed.`) return } + console.log("Going to reroll", character, rollId) let rollData = this.getRollData(rollId) if (character.getLevelRemaining() > 0) { rollData.rerollHero = true @@ -361,6 +401,13 @@ export class PegasusUtility { game.socket.emit("system.fvtt-pegasus-rpg", { name: "msg_reroll_hero", data: { userId: userId, rollId: rollId } }) } + /* -------------------------------------------- */ + static targetToken( user, token, flag) { + if (flag) { + token.actor.checkIfPossible() + } + } + /* -------------------------------------------- */ static async chatListeners(html) { @@ -509,7 +556,7 @@ export class PegasusUtility { /* -------------------------------------------- */ static getTarget() { - if (game.user.targets && game.user.targets.size == 1) { + if (game.user.targets) { for (let target of game.user.targets) { return target } @@ -546,11 +593,12 @@ export class PegasusUtility { let id = rollData.rollId let oldRollData = this.rollDataStore[id] || {} let newRollData = mergeObject(oldRollData, rollData) + console.log("Rolldata saved", id) this.rollDataStore[id] = newRollData } /* -------------------------------------------- */ static saveRollData(rollData) { - game.socket.emit("system.pegasus-rpg", { + game.socket.emit("system.fvtt-pegasus-rpg", { name: "msg_update_roll", data: rollData }); // Notify all other clients of the roll this.updateRollData(rollData) @@ -581,6 +629,7 @@ export class PegasusUtility { this.addItemDropToActor(actor, item) } if (msg.name == "msg_reroll_hero") { + console.log("Reroll requested") this.rerollHeroRemaining(msg.data.userId, msg.data.rollId) } if (msg.name == "msg_gm_remove_effect") { @@ -681,7 +730,7 @@ export class PegasusUtility { if (game.user.isGM) { this.removeForeignEffect(effect) } else { - game.socket.emit("system.fvtt-pegasus-rgp", { msg: "msg_gm_remove_effect", data: effect }) + game.socket.emit("system.fvtt-pegasus-rpg", { msg: "msg_gm_remove_effect", data: effect }) } } else { toRem.push(effect.effect._id) @@ -863,7 +912,7 @@ export class PegasusUtility { chatGM.whisper = this.getUsers(user => user.isGM); chatGM.content = "Blinde message of " + game.user.name + "
" + chatOptions.content; console.log("blindMessageToGM", chatGM); - game.socket.emit("system.fvtt-pegasus-rgp", { msg: "msg_gm_chat_message", data: chatGM }); + game.socket.emit("system.fvtt-pegasus-rpg", { msg: "msg_gm_chat_message", data: chatGM }); } @@ -950,7 +999,7 @@ export class PegasusUtility { console.log("TARGET ", target) let defenderActor = target.actor rollData.defenderTokenId = target.id - rollData.attackerId = this.id + //rollData.attackerId = this.id console.log("DEFENDER", defenderActor) defenderActor.addHindrancesList(rollData.effectsList) } diff --git a/styles/simple.css b/styles/simple.css index f23384b..9221f64 100644 --- a/styles/simple.css +++ b/styles/simple.css @@ -1159,6 +1159,10 @@ ul, li { position:relative; top:1px; } +.pegasus-chat-roll-button { + height: 38px; + max-height: 38px; +} .plus-minus-button { box-shadow: inset 0px 1px 0px 0px #a6827e; @@ -1282,7 +1286,7 @@ Focus FOC: #ff0084 background: black; } .color-class-agi, -.color-class-range { +.color-class-ranged-attack { background-color: #02a41d; background: #02a41d; } @@ -1318,7 +1322,7 @@ Focus FOC: #ff0084 background-color: #505050; } .color-class-per, -.color-class-ranged { +.color-class-ranged-damage { background-color: #f9c801; } .color-class-foc { diff --git a/system.json b/system.json index e401dc1..5160da4 100644 --- a/system.json +++ b/system.json @@ -180,9 +180,9 @@ "styles": [ "styles/simple.css" ], - "templateVersion": 99, + "templateVersion": 100, "title": "Pegasus RPG", "url": "https://www.uberwald.me/data/files/fvtt-pegasus-rpg", - "version": "0.6.8", + "version": "0.6.10", "background" : "./images/ui/pegasus_welcome_page.webp" } diff --git a/template.json b/template.json index 86ecf23..d32d063 100644 --- a/template.json +++ b/template.json @@ -361,6 +361,14 @@ "effectstatlevel": false, "effectstat": "", "oneuse": false, + "ignorehealthpenalty": false, + "isthispossible": "", + "mentaldisruption": false, + "physicaldisruption": false, + "mentalimmunity": false, + "physicalimmunity": false, + "nobonusdice": false, + "noperksallowed": false, "description": "" }, "race": { @@ -443,6 +451,8 @@ "effectsgained": [], "category": "general", "upgradable": false, + "activatedtext": "", + "deactivatedtext": "", "features": { "nrgcost": { "label": "NRG cost to use", diff --git a/templates/actor-sheet.html b/templates/actor-sheet.html index c94f6cd..385c3b1 100644 --- a/templates/actor-sheet.html +++ b/templates/actor-sheet.html @@ -110,16 +110,16 @@
diff --git a/templates/chat-perk-activated.html b/templates/chat-perk-activated.html index 765275d..d9dca5d 100644 --- a/templates/chat-perk-activated.html +++ b/templates/chat-perk-activated.html @@ -4,4 +4,9 @@ {{/if}}
{{name}} has just activated the Perk: {{perk.name}}.
+ + {{#if (count perk.data.activatedtext)}} +
{{perk.data.activatedtext}}
+ {{/if}} + diff --git a/templates/chat-perk-ready.html b/templates/chat-perk-ready.html index 432481f..5e44619 100644 --- a/templates/chat-perk-ready.html +++ b/templates/chat-perk-ready.html @@ -4,4 +4,9 @@ {{/if}}
{{name}} has just deactivated the Perk: {{perk.name}}, make sure to manually delete all Effects provided by this Perk from Targets.
+ + {{#if (count perk.data.deactivatedtext)}} +
{{perk.data.deactivatedtext}}
+ {{/if}} + diff --git a/templates/chat-roll-button.html b/templates/chat-roll-button.html new file mode 100644 index 0000000..06a405c --- /dev/null +++ b/templates/chat-roll-button.html @@ -0,0 +1,5 @@ +
+
+ +
+
diff --git a/templates/item-effect-sheet.html b/templates/item-effect-sheet.html index 186a2ae..6c232f9 100644 --- a/templates/item-effect-sheet.html +++ b/templates/item-effect-sheet.html @@ -37,6 +37,30 @@
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • diff --git a/templates/item-perk-sheet.html b/templates/item-perk-sheet.html index 1c4f585..9ffd5ab 100644 --- a/templates/item-perk-sheet.html +++ b/templates/item-perk-sheet.html @@ -77,6 +77,13 @@
  • +
  • + +
  • +
  • + +
  • + {{#each data.features as |feature key|}} {{#if feature.isvalid}}
  • diff --git a/templates/partial-actor-status.html b/templates/partial-actor-status.html index f587a2c..171c3ed 100644 --- a/templates/partial-actor-status.html +++ b/templates/partial-actor-status.html @@ -39,8 +39,8 @@ - + -  - + + +  -