diff --git a/src/migrations/2023_09_16_08_232_fix_bad_Equipped_state.ts b/src/migrations/2023_09_16_08_232_fix_bad_Equipped_state.ts index 5e1854d63..0c380d0ce 100644 --- a/src/migrations/2023_09_16_08_232_fix_bad_Equipped_state.ts +++ b/src/migrations/2023_09_16_08_232_fix_bad_Equipped_state.ts @@ -12,7 +12,7 @@ async function checkEquippedState (actor: TwodsixActor): Promise { } async function checkItemEquippedState (item:TwodsixItem): Promise { - if (!["skills", "trait", "spell", "component", "ship_position"].includes(item.type)) { + if (!["skills", "trait", "spell", "component", "ship_position", "psiAbility"].includes(item.type)) { if (typeof item.system.equipped !== "string") { await item.update({"system.equipped": "backpack"}); console.log("Migrated " + item.name + (item.actor ? " on " + item.actor.name : "")); diff --git a/src/module/config.ts b/src/module/config.ts index abefbecd7..ec33fe492 100644 --- a/src/module/config.ts +++ b/src/module/config.ts @@ -94,7 +94,8 @@ const RULESETS = Object.freeze({ addEffectToDamage: false, addEffectToManualDamage: false, weightModifierForWornArmor: "0", - chainBonus: "0, 0, 0, 0, 0, 0" + chainBonus: "0, 0, 0, 0, 0, 0", + psiTalentsRequireRoll: false } }, CE: { @@ -146,7 +147,8 @@ const RULESETS = Object.freeze({ armorDamageFormula: "@damage - @effectiveArmor", addEffectToDamage: true, weightModifierForWornArmor: "1", - chainBonus: "-2, -1, -1, 1, 1, 2" + chainBonus: "-2, -1, -1, 1, 1, 2", + psiTalentsRequireRoll: true } }, CEL: { @@ -192,7 +194,8 @@ const RULESETS = Object.freeze({ armorDamageFormula: "@damage - @effectiveArmor", addEffectToDamage: true, weightModifierForWornArmor: "1", - chainBonus: "0, 0, 0, 0, 0, 0" + chainBonus: "0, 0, 0, 0, 0, 0", + psiTalentsRequireRoll: false } }, CEFTL: { @@ -237,7 +240,8 @@ const RULESETS = Object.freeze({ armorDamageFormula: "@damage - @effectiveArmor", addEffectToDamage: true, weightModifierForWornArmor: "1", - chainBonus: "0, 0, 0, 0, 0, 0" + chainBonus: "0, 0, 0, 0, 0, 0", + psiTalentsRequireRoll: false }, }, CEATOM: { @@ -282,7 +286,8 @@ const RULESETS = Object.freeze({ armorDamageFormula: "@damage - @effectiveArmor", addEffectToDamage: true, weightModifierForWornArmor: "1", - chainBonus: "0, 0, 0, 0, 0, 0" + chainBonus: "0, 0, 0, 0, 0, 0", + psiTalentsRequireRoll: false } }, BARBARIC: { @@ -326,7 +331,8 @@ const RULESETS = Object.freeze({ armorDamageFormula: "@damage - @effectiveArmor", addEffectToDamage: true, weightModifierForWornArmor: "1", - chainBonus: "0, 0, 0, 0, 0, 0" + chainBonus: "0, 0, 0, 0, 0, 0", + psiTalentsRequireRoll: false }, }, CEQ: { @@ -371,7 +377,8 @@ const RULESETS = Object.freeze({ targetDMList: "", armorDamageFormula: "@damage - @effectiveArmor", addEffectToDamage: true, - chainBonus: "0, 0, 0, 0, 0, 0" + chainBonus: "0, 0, 0, 0, 0, 0", + psiTalentsRequireRoll: false } }, CD: { @@ -425,7 +432,8 @@ const RULESETS = Object.freeze({ addEffectToDamage: true, weightModifierForWornArmor: "1", chainBonus: "0, 0, 0, 1, 1, 1", - reverseHealingOrder: true + reverseHealingOrder: true, + psiTalentsRequireRoll: false } }, CDEE: { @@ -479,7 +487,8 @@ const RULESETS = Object.freeze({ addEffectToDamage: true, weightModifierForWornArmor: "1", chainBonus: "0, 0, 0, 1, 1, 1", - reverseHealingOrder: true + reverseHealingOrder: true, + psiTalentsRequireRoll: false } }, CLU: { @@ -533,7 +542,8 @@ const RULESETS = Object.freeze({ addEffectToDamage: true, weightModifierForWornArmor: "1", chainBonus: "0, 0, 0, 1, 1, 1", - reverseHealingOrder: true + reverseHealingOrder: true, + psiTalentsRequireRoll: false } }, SOC: { @@ -632,7 +642,8 @@ const RULESETS = Object.freeze({ armorDamageFormula: "@damage - @effectiveArmor", addEffectToDamage: true, weightModifierForWornArmor: "1", - chainBonus: "0, 0, 0, 0, 0, 0" + chainBonus: "0, 0, 0, 0, 0, 0", + psiTalentsRequireRoll: false } }, CU: { @@ -684,7 +695,8 @@ const RULESETS = Object.freeze({ armorDamageFormula: "@damage - @effectiveArmor", addEffectToDamage: true, weightModifierForWornArmor: "1", - chainBonus: "-2, -1, -1, 1, 1, 2" + chainBonus: "-2, -1, -1, 1, 1, 2", + psiTalentsRequireRoll: false } }, OTHER: { @@ -706,6 +718,8 @@ const ROLLTYPES = Object.freeze({ Disadvantage: {key: 'Disadvantage', formula: "3d6kl2"} }); +const WeightlessItems = ["skills", "trait", "spell", "psiAbility"]; + const CONSUMABLES = Object.freeze({ air: "TWODSIX.Items.Consumable.Types.air", drugs: "TWODSIX.Items.Consumable.Types.drugs", @@ -1152,15 +1166,17 @@ export const AUG_LOCATIONS = { }; export const ITEM_TYPE_SELECT = { - augment: "TWODSIX.Items.Items.AssignAugment", - weapon: "TWODSIX.Items.Items.AssignWeapon", armor: "TWODSIX.Items.Items.AssignArmor", - equipment: "TWODSIX.Items.Items.AssignEquipment", - consumable: "TWODSIX.Items.Items.AssignConsumable", + augment: "TWODSIX.Items.Items.AssignAugment", computer: "TWODSIX.Items.Items.AssignComputer", - tool: "TWODSIX.Items.Items.AssignTool", + consumable: "TWODSIX.Items.Items.AssignConsumable", + equipment: "TWODSIX.Items.Items.AssignEquipment", junk: "TWODSIX.Items.Items.AssignJunk", - storage: "TWODSIX.Items.Items.MoveStorage" + psiAbility: "TWODSIX.Items.Items.AssignPsiAbility", + storage: "TWODSIX.Items.Items.MoveStorage", + spell: "TWODSIX.Items.Items.AssignSpell", + tool: "TWODSIX.Items.Items.AssignTool", + weapon: "TWODSIX.Items.Items.AssignWeapon" }; export const CU_DAMAGE_TYPES = { @@ -1174,7 +1190,8 @@ export const CU_DAMAGE_TYPES = { melee: "TWODSIX.DamageType.Melee", plasma: "TWODSIX.DamageType.Plasma", poison: "TWODSIX.DamageType.Poison", - stun: "TWODSIX.DamageType.Stun" + stun: "TWODSIX.DamageType.Stun", + psionic: "TWODSIX.DamageType.Psionic" }; export const DAMAGECOLORS = Object.freeze({ @@ -1231,7 +1248,8 @@ export type TWODSIX = { ITEM_TYPE_SELECT: typeof ITEM_TYPE_SELECT, CU_DAMAGE_TYPES: typeof CU_DAMAGE_TYPES, effectType: typeof effectType, - DAMAGECOLORS: typeof DAMAGECOLORS + DAMAGECOLORS: typeof DAMAGECOLORS, + WeightlessItems: typeof WeightlessItems }; export const TWODSIX = { @@ -1274,5 +1292,6 @@ export const TWODSIX = { ITEM_TYPE_SELECT: ITEM_TYPE_SELECT, CU_DAMAGE_TYPES: CU_DAMAGE_TYPES, effectType: effectType, - DAMAGECOLORS: DAMAGECOLORS + DAMAGECOLORS: DAMAGECOLORS, + WeightlessItems: WeightlessItems }; diff --git a/src/module/data/item.ts b/src/module/data/item.ts index caae81661..ca386de6c 100644 --- a/src/module/data/item.ts +++ b/src/module/data/item.ts @@ -129,6 +129,24 @@ export class SpellData extends TraitData { } } +export class PsiAbilityData extends TraitData { + static defineSchema() { + const schema = super.defineSchema(); + schema.target = makeTargetTemplate(); + schema.duration = new fields.StringField({...requiredBlankString}); + schema.associatedSkillName = new fields.StringField({...requiredBlankString}); + schema.skill = new fields.StringField({...requiredBlankString}); + schema.range = new fields.StringField({...requiredBlankString}); + schema.rangeBand = new fields.StringField({required: true, blank: false, initial: "none" }); + schema.damage = new fields.StringField({...requiredBlankString}); + schema.damageType = new fields.StringField({required: true, blank: false, initial: "psionic" }); + schema.psiCost = new fields.NumberField({...requiredInteger, initial: 0}); + schema.difficulty = new fields.StringField({required: true, blank: false, initial: "Average" }); + schema.skillModifier = new fields.NumberField({...requiredInteger, initial: 0}); + return schema; + } +} + export class ConsumableData extends GearData { static defineSchema() { const schema = super.defineSchema(); diff --git a/src/module/entities/TwodsixActor.ts b/src/module/entities/TwodsixActor.ts index cd2c514e2..9846f44bb 100644 --- a/src/module/entities/TwodsixActor.ts +++ b/src/module/entities/TwodsixActor.ts @@ -466,7 +466,7 @@ export default class TwodsixActor extends Actor { getActorEncumbrance():number { let encumbrance = 0; - const actorItems = this.items.filter( i => !["skills", "trait", "ship_position", "storage", "spell"].includes(i.type)); + const actorItems = this.items.filter( i => ![...TWODSIX.WeightlessItems, "ship_position", "storage"].includes(i.type)); for (const item of actorItems) { encumbrance += getEquipmentWeight(item); } @@ -1048,7 +1048,7 @@ export default class TwodsixActor extends Actor { //Create Item const addedItem = (await this.createEmbeddedDocuments("Item", [transferData]))[0]; - if (game.settings.get('twodsix', 'useEncumbranceStatusIndicators') && this.type === 'traveller' && !["skills", "trait", "spell"].includes(addedItem.type)) { + if (game.settings.get('twodsix', 'useEncumbranceStatusIndicators') && this.type === 'traveller' && !TWODSIX.WeightlessItems.includes(addedItem.type)) { await applyEncumberedEffect(this); } console.log(`Twodsix | Added Item ${itemData.name} to character`); @@ -1083,7 +1083,7 @@ export default class TwodsixActor extends Actor { } break; case 'ship': - if (!["skills", "trait", "spell"].includes(itemData.type)) { + if (!TWODSIX.WeightlessItems.includes(itemData.type)) { return await this._addDroppedEquipment(itemData); } break; @@ -1267,6 +1267,20 @@ export default class TwodsixActor extends Actor { return 0; } } + + /** + * Removes (damages) psionic characteristic and spreads excess to regular damage + * @param {number} psiCost The number of psi points to remove + */ + public async removePsiPoints(psiCost: number): Promise { + if (psiCost > 0) { + const netPoints = Math.min(this.system.characteristics.psionicStrength.current, psiCost); + await this.update({'system.characteristics.psionicStrength.damage': this.system.characteristics.psionicStrength.damage + netPoints}); + if (netPoints < psiCost) { + await this.damageActor({damageValue: psiCost - netPoints, armorPiercingValue: 9999, damageType: "psionic", damageLabel: game.i18n.localize("TWODSIX.DamageType.Psionic"), canBeParried: false}, false); + } + } + } } /** @@ -1346,7 +1360,7 @@ async function deleteIdFromShipPositions(actorId: string) { * @function */ function getEquipmentWeight(item:TwodsixItem):number { - if (!["skills", "spell", "trait"].includes(item.type)) { + if (!TWODSIX.WeightlessItems.includes(item.type)) { if (["backpack", "equipped"].includes(item.system.equipped)) { let q = item.system.quantity || 0; const w = item.system.weight || 0; diff --git a/src/module/entities/TwodsixItem.ts b/src/module/entities/TwodsixItem.ts index d10ce0314..12395437a 100644 --- a/src/module/entities/TwodsixItem.ts +++ b/src/module/entities/TwodsixItem.ts @@ -5,7 +5,7 @@ import {TwodsixDiceRoll} from "../utils/TwodsixDiceRoll"; import {TwodsixRollSettings} from "../utils/TwodsixRollSettings"; import TwodsixActor from "./TwodsixActor"; import {DICE_ROLL_MODES} from "@league-of-foundry-developers/foundry-vtt-types/src/foundry/common/constants.mjs"; -import {Component, Consumable, Gear, Skills, UsesConsumables, Weapon} from "../../types/template"; +import {Consumable, Gear, Skills, UsesConsumables, Weapon} from "../../types/template"; import { confirmRollFormula } from "../utils/sheetUtils"; import { getCharacteristicFromDisplayLabel } from "../utils/utils"; import ItemTemplate from "../utils/ItemTemplate"; @@ -36,7 +36,7 @@ export default class TwodsixItem extends Item { if (this.type === 'weapon') { Object.assign(updates, {img: 'systems/twodsix/assets/icons/default_weapon.png'}); } else if (this.type === "spell") { - const defaultSkill = await game.settings.get("twodsix", "sorcerySkill") ?? ""; + const defaultSkill = game.settings.get("twodsix", "sorcerySkill") ?? ""; Object.assign(updates, { img: 'systems/twodsix/assets/icons/spell-book.svg', 'system.associatedSkillName': defaultSkill @@ -45,6 +45,8 @@ export default class TwodsixItem extends Item { Object.assign(updates, {img: 'systems/twodsix/assets/icons/components/other.svg'}); } else if (this.type === 'computer') { Object.assign(updates, {img: 'systems/twodsix/assets/icons/components/computer.svg'}); + } else if (this.type === 'psiAbility') { + Object.assign(updates, {img: 'systems/twodsix/assets/icons/extra-lucid.svg'}); } } @@ -91,7 +93,7 @@ export default class TwodsixItem extends Item { if (game.user?.id === userId) { const owningActor: TwodsixActor = this.actor; if (game.settings.get('twodsix', 'useEncumbranceStatusIndicators') && owningActor?.type === 'traveller' && !options.dontSync) { - if (!["skills", "trait", "spell"].includes(this.type) && changed.system) { + if (!TWODSIX.WeightlessItems.includes(this.type) && changed.system) { if ((Object.hasOwn(changed.system, "weight") || Object.hasOwn(changed.system, "quantity") || (Object.hasOwn(changed.system, "equipped")) && this.system.weight > 0)) { await applyEncumberedEffect(owningActor); } @@ -107,7 +109,7 @@ export default class TwodsixItem extends Item { //Update item tab list if TL Changed if (game.settings.get('twodsix', 'showTLonItemsTab')) { - if(["skills", "trait", "spell", "ship_position"].includes(this.type)) { + if([...TWODSIX.WeightlessItems, "ship_position"].includes(this.type)) { return; } else if (this.isEmbedded || this.compendium) { return; @@ -205,19 +207,7 @@ export default class TwodsixItem extends Item { } /*Apply measured template if valid AOE*/ - const magazine:TwodsixItem | undefined = weapon.useConsumableForAttack ? this.actor?.items.get(weapon.useConsumableForAttack) : undefined; - const itemForAOE:TwodsixItem = (magazine?.system.target.type !== "none" && magazine) ? magazine : this; - if ( itemForAOE.system.target?.type !== "none" ) { - try { - await (ItemTemplate.fromItem(itemForAOE))?.drawPreview(); - //const templates = await (ItemTemplate.fromItem(this))?.drawPreview(); - //if (templates?.length > 0) { - // ItemTemplate.targetTokensInTemplate(templates[0]); - //} - } catch(err) { - ui.notifications.error(game.i18n.localize("TWODSIX.Errors.CantPlaceTemplate")); - } - } + await this.drawItemTemplate(); // Initialize settings const tmpSettings = { @@ -303,6 +293,7 @@ export default class TwodsixItem extends Item { } // Update consumables for use + const magazine:TwodsixItem | undefined = weapon.useConsumableForAttack ? this.actor?.items.get(weapon.useConsumableForAttack) : undefined; if (magazine) { try { await magazine.consume(usedAmmo); @@ -366,10 +357,10 @@ export default class TwodsixItem extends Item { * A method to get the weapon fire mode parameters. * @param {number} rateOfFireCE The rate of fire used * @param {string} attackType The type of attack (e.g. burst, double-tap, etc.) - * @param {any} tmpSettings the temporary settings object for the roll - * @returns {any} { weaponType, isAutoFull, usedAmmo, numberOfAttacks } + * @param {object} tmpSettings the temporary settings object for the roll + * @returns {object} { weaponType, isAutoFull, usedAmmo, numberOfAttacks } */ - private getFireModeParams( rateOfFireCE: number, attackType: string, tmpSettings: any): any { + private getFireModeParams( rateOfFireCE: number, attackType: string, tmpSettings: object): object { const ruleSet = game.settings.get('twodsix', 'ruleset'); const weapon:Weapon = this.system; let numberOfAttacks = 1; @@ -439,9 +430,9 @@ export default class TwodsixItem extends Item { * Valid for Classic Traveller and Cepheus Engine band types as well as other rule sets with range values. * @param {number} range The measured distance to the target * @param {string} weaponBand The type of weapon used - as key string - * @returns {any} {rangeModifier: rangeModifier, rollType: rollType} + * @returns {object} {rangeModifier: rangeModifier, rollType: rollType} */ - public getRangeModifier(range:number, weaponBand: string, isAutoFull:boolean): any { + public getRangeModifier(range:number, weaponBand: string, isAutoFull:boolean): object { let rangeModifier = 0; let rollType = 'Normal'; const rangeModifierType = game.settings.get('twodsix', 'rangeModifierType'); @@ -506,7 +497,7 @@ export default class TwodsixItem extends Item { /** * A method to return ammo range modifier. * @param {string} rangeModifierType The type of range modifier (setting) - * @returns {any} {dodgeParry: dodgeParryModifier, dodgeParryLabel: skillName} + * @returns {number} range modifier DM */ public getAmmoRangeModifier(rangeModifierType:string):number { let returnValue = 1; @@ -526,7 +517,7 @@ export default class TwodsixItem extends Item { /** * A method to get the dodge / parry modifer based on the target's corresponding skill used for attack. * @param {Token} target The target token - * @returns {any} {dodgeParry: dodgeParryModifier, dodgeParryLabel: skillName} + * @returns {object} {dodgeParry: dodgeParryModifier, dodgeParryLabel: skillName} */ public getDodgeParryValues(target: Token): object { let dodgeParryModifier = 0; @@ -575,11 +566,12 @@ export default class TwodsixItem extends Item { /** * Perform a skill roll / check based on input settings. - * @param showThrowDialog {boolean} Whether to show roll/through dialog - * @param tmpSettings {TwodsixRollSettings|undefined} Roll settings to use - * @param showInChat Whehter to show attack in chat + * @param {boolean} showThrowDialog Whether to show roll/through dialog + * @param {TwodsixRollSettings|undefined} tmpSettings Roll settings to use + * @param {boolean} showInChat Whether to show attack in chat + * @returns {TwodsixDiceRoll | void} Results of dice roll, if made */ - public async skillRoll(showThrowDialog:boolean, tmpSettings?:TwodsixRollSettings, showInChat = true):Promise { + public async skillRoll(showThrowDialog:boolean, tmpSettings?:TwodsixRollSettings, showInChat: boolean = true):Promise { let skill:TwodsixItem | null = null; let item:TwodsixItem | undefined; let workingActor:TwodsixActor = this.actor; @@ -615,26 +607,23 @@ export default class TwodsixItem extends Item { //TODO Refactor. This is an ugly fix for weapon attacks, when settings are first created, then skill rolls are made, creating new settings, so multiplying bonuses. if (!tmpSettings) { + const workingSettings = {}; if(this.type === "spell") { // Spells under SOC and Barbaric have a sequential difficulty class based on spell level. Create an override to system difficulties. - const workingSettings = {"difficulties": {}, "difficulty": ""}; + Object.assign(workingSettings, {difficulties: {}}); for (let i = 1; i <= game.settings.get("twodsix", "maxSpellLevel"); i++) { const levelKey = game.i18n.localize("TWODSIX.Items.Spells.Level") + " " + i; - workingSettings.difficulties[levelKey] = {mod: -i, target: i+6}; + Object.assign(workingSettings.difficulties, {[levelKey]: {mod: -i, target: i+6}}); } const level = game.i18n.localize("TWODSIX.Items.Spells.Level") + " " + (this.system.value > Object.keys(workingSettings.difficulties).length ? Object.keys(workingSettings.difficulties).length : this.system.value); - workingSettings.difficulty = workingSettings.difficulties[level]; - if ( this.system.target?.type !== "none" ) { - try { - await (ItemTemplate.fromItem(this))?.drawPreview(); - } catch(err) { - ui.notifications.error(game.i18n.localize("TWODSIX.Errors.CantPlaceTemplate")); - } + Object.assign(workingSettings, {difficulty: workingSettings.difficulties[level]}); + await this.drawItemTemplate(); + } else if (this.type === "psiAbility") { + if (this.system.difficulty) { + Object.assign(workingSettings, {difficulty: TWODSIX.DIFFICULTIES[game.settings.get('twodsix', 'difficultyListUsed')][this.system.difficulty]}); } - tmpSettings = await TwodsixRollSettings.create(showThrowDialog, workingSettings, skill, item, workingActor); - } else { - tmpSettings = await TwodsixRollSettings.create(showThrowDialog, tmpSettings, skill, item, workingActor); } + tmpSettings = await TwodsixRollSettings.create(showThrowDialog, workingSettings, skill, item, workingActor); if (!tmpSettings.shouldRoll) { return; } @@ -666,18 +655,150 @@ export default class TwodsixItem extends Item { if (showInChat) { await diceRoll.sendToChat(tmpSettings.difficulties); } + return diceRoll; } - public async rollDamage(rollMode:DICE_ROLL_MODES, bonusDamage = "", showInChat = true, confirmFormula = false):Promise { - const weapon = this.system; + /** + * Perform post skill roll actions for using psiAbility (damage and use of psi points). + * @param {TwodsixDiceRoll} diceRollEffect Results effect of psionic skill check + * @param {DICE_ROLL_MODES} rollMode The rollMode (who roll is shared with) + * @param {boolean} showThrowDiag whether or not to show throw dialog for damage + * @returns {number} The number of psi points used + */ + async processPsiAction(diceRollEffect:number, rollMode:DICE_ROLL_MODES, showThrowDiag:boolean): Promise { + if(diceRollEffect < 0) { + await (this.actor).removePsiPoints(1); + return 1; + } else { + let psiCost:number; + try { + psiCost = await foundry.applications.api.DialogV2.prompt({ + window: { title: "TWODSIX.Items.Psionics.PsiCost" }, + content: ``, + ok: { + label: "TWODSIX.Items.Psionics.UsePoints", + callback: (event, button /*, dialog*/) => button.form.elements.psiCost.valueAsNumber + } + }); + } catch { + console.log("No psionic points selected"); + return 0; + } + + if(isNaN(psiCost)) { + return; + } else if (this.system.damage !== "" && this.system.damage !== "0" && game.settings.get("twodsix", "automateDamageRollOnHit")) { + const damagePayload = await this.rollDamage(rollMode || game.settings.get('core', 'rollMode'), ` ${diceRollEffect}`, true, showThrowDiag); + if (damagePayload?.damageValue > 0) { + const targetTokens = Array.from(game.user.targets); + if (targetTokens.length > 0) { + await (targetTokens[0].actor).handleDamageData(damagePayload, !game.settings.get('twodsix', 'autoDamageTarget')); + } + } + } + await (this.actor).removePsiPoints(psiCost); + return psiCost; + } + } + + /** + * Send item description to chat. + */ + public sendDescriptionToChat():Promise { + const picture = this.img; + const capType = game.i18n.localize(`TYPES.Item.${this.type}`).capitalize(); + const msg = `
${capType}: ${this.name}
${this.system["description"]}
`; + ChatMessage.create({ content: msg, speaker: ChatMessage.getSpeaker({ actor: this.actor }) }); + } + + /** + * Send message to chat when using a psionic action. + * @param {number} pointsUsed The number of psi points used for action + * @param {DICE_ROLL_MODES} rollMode The roll mode used, if any + * @param {number} rollEffect the effect of the skill roll,used for damage calcs if necessary + * @private + */ + private sendPsiUseToChat(pointsUsed:number, rollMode:DICE_ROLL_MODES , rollEffect:number=0):Promise { + const picture = this.img; + const capType = game.i18n.localize(`TYPES.Item.${this.type}`).capitalize(); + let msg = `
` + + `${game.i18n.localize('TWODSIX.Items.Psionics.Used')} ${capType}: ${this.name}, ${pointsUsed} ${game.i18n.localize('TWODSIX.Items.Psionics.Pts')}
`; + if (!game.settings.get("twodsix", "automateDamageRollOnHit") && this.system.damage !== "" && this.system.damage !== "0" && pointsUsed > 0 && rollEffect >= 0) { + msg += `
`; + } + + const flags = { + "core.canPopout": true, + "twodsix.itemUUID": this.uuid ?? "", + "twodsix.tokenUUID": this.actor?.token?.uuid ?? "", + "twodsix.actorUUID": this.actor?.uuid ?? "", + "twodsix.bonusDamage": "", + "twodsix.effect": rollEffect + }; + + const messageContent = { + content: msg, + flags: flags, + speaker: ChatMessage.getSpeaker({ actor: this.actor }) + }; + + if (rollMode !== 'publicroll') { + const showToUsers = game.users.filter((user)=> user.isGM || (game.userId === user.id)); + Object.assign(messageContent, {whisper: showToUsers}); + } + + ChatMessage.create(messageContent); + } + + /** + * Handle skill and talent rolls. + * @param {boolean} showTrowDiag Whether to show the throw dialog or not + * @private + */ + public async doSkillTalentRoll(showThrowDiag: boolean): Promise { + if (this.type === "psiAbility") { + this.doPsiAction(showThrowDiag); + } else { + await this.skillRoll(showThrowDiag); + } + } + + /** + * Handle psionic ability / talent. + * @param {boolean} showTrowDiag Whether to show the throw dialog or not + * @private + */ + private async doPsiAction(showThrowDiag: boolean): Promise { + let psiCost = 0; + let rollEffect = 0; + let rollMode = "gmroll"; + if ((this.actor).system.characteristics.psionicStrength.current <= 0) { + ui.notifications.warn(game.i18n.localize("TWODSIX.Warnings.NoPsiPoints")); + } else { + await this.drawItemTemplate(); + if (!game.settings.get('twodsix', 'psiTalentsRequireRoll')) { + psiCost = await this.processPsiAction(0, rollMode, showThrowDiag); + } else { + const diceRoll = await this.skillRoll(showThrowDiag); + if (diceRoll) { + rollMode = diceRoll.rollSettings.rollMode; + psiCost = await this.processPsiAction(diceRoll.effect, rollMode, showThrowDiag); + rollEffect = diceRoll.effect; + } + } + await this.sendPsiUseToChat(psiCost, rollMode, rollEffect); + } + } + + public async rollDamage(rollMode:DICE_ROLL_MODES, bonusDamage = "", showInChat = true, confirmFormula = false):Promise { const consumableDamage = this.getConsumableBonusDamage(); - if (!weapon.damage && !consumableDamage) { + if (!this.system.damage && !consumableDamage) { ui.notifications.warn(game.i18n.localize("TWODSIX.Warnings.NoDamageForWeapon")); return; } else { //Calc regular damage - let rollFormula = weapon.damage + ((bonusDamage !== "0" && bonusDamage !== "") ? " + " + bonusDamage : "") + (consumableDamage != "" ? " + " + consumableDamage : ""); + let rollFormula = this.system.damage + ((bonusDamage !== "0" && bonusDamage !== "") ? " + " + bonusDamage : "") + (consumableDamage != "" ? " + " + consumableDamage : ""); //console.log(rollFormula); if (confirmFormula) { rollFormula = await confirmRollFormula(rollFormula, game.i18n.localize("TWODSIX.Damage.DamageFormula")); @@ -685,7 +806,7 @@ export default class TwodsixItem extends Item { rollFormula = rollFormula.replace(/dd/ig, "d6*10"); //Parse for a destructive damage roll DD = d6*10 //rollFormula = simplifyRollFormula(rollFormula, { preserveFlavor: true }); let damage = {}; - let apValue = weapon.armorPiercing ?? 0; + let apValue = this.system.armorPiercing ?? 0; if (Roll.validate(rollFormula)) { damage = new Roll(rollFormula, this.actor?.getRollData()); @@ -710,7 +831,7 @@ export default class TwodsixItem extends Item { //Deterime Damage type let damageType:string = this.getConsumableDamageType(); if (damageType === '' || damageType === "NONE") { - damageType = weapon.damageType ?? "NONE"; //component doesn't have a specified damage type + damageType = this.system.damageType ?? "NONE"; //component doesn't have a specified damage type } const damageLabels = getDamageTypes(true); const contentData = {}; @@ -981,7 +1102,12 @@ export default class TwodsixItem extends Item { return {armorModifier: armorModifier, armorLabel: armorLabel }; } - private getCustomArmorMod(isAuto:boolean):any { + /** + * A method for returning an object of custom armor values for a weapon in CT + * @param {boolean} isAuto Is full automatic fire + * @returns {object} Object of protection values versus different armor types + */ + private getCustomArmorMod(isAuto:boolean):object { return { nothing: parseCustomCTValue(this.system.customCT.armor.nothing, isAuto), jack: parseCustomCTValue(this.system.customCT.armor.jack, isAuto), @@ -1034,14 +1160,19 @@ export default class TwodsixItem extends Item { console.log("Not a valid weapon range band type"); break; } - } catch(err) { + } catch /*(err)*/ { ui.notifications.error(game.i18n.localize("TWODSIX.Errors.InvalidRangeBand")); } } return returnVal; } - private getCustomRangeMod(isAuto:boolean):any { + /** + * A method for returning an object of custom range modifiers for a weapon in CT + * @param {boolean} isAuto Is full automatic fire + * @returns {object} Object of range modifiers versus different range bands + */ + private getCustomRangeMod(isAuto:boolean):object { return { close: parseCustomCTValue(this.system.customCT.range.close, isAuto), short: parseCustomCTValue(this.system.customCT.range.short, isAuto), @@ -1050,6 +1181,25 @@ export default class TwodsixItem extends Item { veryLong: parseCustomCTValue(this.system.customCT.range.veryLong, isAuto) }; } + /** + * A method for drawing a measured template for an item action - accounting for consumables + * having attachements with AOE's + */ + private async drawItemTemplate():Promise { + const magazine:TwodsixItem | undefined = this.system.useConsumableForAttack ? this.actor?.items.get(this.system.useConsumableForAttack) : undefined; + const itemForAOE:TwodsixItem = (magazine?.system.target.type !== "none" && magazine) ? magazine : this; + if ( itemForAOE.system.target?.type !== "none" ) { + try { + await (ItemTemplate.fromItem(itemForAOE))?.drawPreview(); + //const templates = await (ItemTemplate.fromItem(this))?.drawPreview(); + //if (templates?.length > 0) { + // ItemTemplate.targetTokensInTemplate(templates[0]); + //} + } catch /*(err)*/ { + ui.notifications.error(game.i18n.localize("TWODSIX.Errors.CantPlaceTemplate")); + } + } + } } /** @@ -1098,7 +1248,7 @@ export async function onRollDamage(event:Event):Promise { const useInvertedShiftClick:boolean = (game.settings.get('twodsix', 'invertSkillRollShiftClick')); const showFormulaDialog = useInvertedShiftClick ? event["shiftKey"] : !event["shiftKey"]; - await item.rollDamage((game.settings.get('core', 'rollMode')), bonusDamageFormula, true, showFormulaDialog); + await item.rollDamage(item.type === 'psiAbility' ? "gmroll" : game.settings.get('core', 'rollMode'), bonusDamageFormula, true, showFormulaDialog); } /** @@ -1108,7 +1258,7 @@ export async function onRollDamage(event:Event):Promise { * @returns {object[]} The resulting simplified dice terms. */ export function getDiceResults(inputRoll:Roll) { - const returnValue:any[] = []; + const returnValue:object[] = []; for (const die of inputRoll.dice) { returnValue.push(die.results); } diff --git a/src/module/handlebars.ts b/src/module/handlebars.ts index 588a079df..5a3568347 100644 --- a/src/module/handlebars.ts +++ b/src/module/handlebars.ts @@ -485,11 +485,11 @@ export default function registerHandlebarsHelpers(): void { }); Handlebars.registerHelper('twodsix_canBeEquipped', (item: TwodsixItem) => { - return !["skills", "trait", "spell", "component", "ship_position"].includes(item.type); + return ![...TWODSIX.WeightlessItems, "ship_position", "component"].includes(item.type); }); Handlebars.registerHelper('twodsix_getTLString', (itemData: any) => { - if(["skills", "trait", "spell", "ship_position"].includes(itemData.type)) { + if([...TWODSIX.WeightlessItems, "ship_position"].includes(itemData.type)) { return ""; } else if (itemData.system?.techLevel !== null && itemData.system?.techLevel !== undefined) { if(isNaN(itemData.system.techLevel)) { diff --git a/src/module/hooks/itemPilesIntegration.ts b/src/module/hooks/itemPilesIntegration.ts index 578519463..95bb7e690 100644 --- a/src/module/hooks/itemPilesIntegration.ts +++ b/src/module/hooks/itemPilesIntegration.ts @@ -18,7 +18,7 @@ Hooks.once("item-piles-ready", async function() { "ITEM_FILTERS": [ { "path": "type", - "filters": "skills,trait,spell,ship_position" + "filters": "skills,trait,spell,ship_position,psiAbility" }, { "path": "name", diff --git a/src/module/settings/RulsetSettings.ts b/src/module/settings/RulsetSettings.ts index bd2d2f2b6..eb7fa8eb7 100644 --- a/src/module/settings/RulsetSettings.ts +++ b/src/module/settings/RulsetSettings.ts @@ -114,6 +114,7 @@ export default class RulesetSettings extends AdvancedSettings { settings.general.push(numberSetting("meleeRange", 2, false, "world")); settings.roll.push(largeStringSetting('targetDMList', "", false, "world", generateTargetDMObject)); settings.roll.push(stringSetting('chainBonus', "", false, 'world')); + settings.roll.push(booleanSetting("psiTalentsRequireRoll", false)); return settings; } } diff --git a/src/module/sheets/AbstractTwodsixActorSheet.ts b/src/module/sheets/AbstractTwodsixActorSheet.ts index 344e60f8d..cf912a9d8 100644 --- a/src/module/sheets/AbstractTwodsixActorSheet.ts +++ b/src/module/sheets/AbstractTwodsixActorSheet.ts @@ -10,6 +10,7 @@ import { getRollTypeSelectObject } from "../utils/sheetUtils"; import { openPDFReference, deletePDFReference } from "../utils/sheetUtils"; import { sortObj } from "../utils/utils"; import { TwodsixActiveEffect } from "../entities/TwodsixActiveEffect"; +import { TWODSIX } from "../config"; export abstract class AbstractTwodsixActorSheet extends ActorSheet { @@ -112,7 +113,7 @@ export abstract class AbstractTwodsixActorSheet extends ActorSheet { // Handle click for attack roll html.find('.perform-attack').on('click', this._onRollWrapper(this._onPerformAttack)); if (this.actor.type != "vehicle") { //Vehcile has a special skill roll - html.find('.rollable').on('click', this._onRollWrapper(this._onSkillRoll)); + html.find('.rollable').on('click', this._onRollWrapper(this._onSkillTalentRoll)); } html.find('.rollable-characteristic').on('click', this._onRollWrapper(this._onRollChar)); if (this.actor.type != "space-object") { //Space Object has a non-item damage roll @@ -125,7 +126,12 @@ export abstract class AbstractTwodsixActorSheet extends ActorSheet { }); //display trait item to chat - html.find(".showChat").on("click", this._onSendToChat.bind(this)); + html.find(".showChat").on("click", (event:Event) => { + const item = this.getItem(event); + if (item) { + item.sendDescriptionToChat(); + } + }); //Roll initiative from traveller sheet html.find(".roll-initiative").on("click", this._onRollInitiative.bind(this)); @@ -220,6 +226,11 @@ export abstract class AbstractTwodsixActorSheet extends ActorSheet { itemData.system.max = 1; } break; + case "psiAbility": + if (!itemData.img) { + itemData.img = 'systems/twodsix/assets/icons/extra-lucid.svg'; + } + break; } } @@ -338,7 +349,7 @@ export abstract class AbstractTwodsixActorSheet extends ActorSheet { // Iterate through items, calculating derived data items.forEach((item:TwodsixItem) => { // item.img = item.img || CONST.DEFAULT_TOKEN; // apparent item.img is read-only.. - if (!["ship_position", "spell", "skills", "trait"].includes(item.type)) { + if (![...TWODSIX.WeightlessItems, "ship_position"].includes(item.type)) { item.prepareConsumable(); } if (["traveller", "animal", "robot"].includes(actor.type)) { @@ -405,7 +416,7 @@ export abstract class AbstractTwodsixActorSheet extends ActorSheet { } else if (actor.type === "ship" || actor.type === "vehicle" ) { sheetData.componentObject = sortObj(component); sheetData.summaryStatus = sortObj(summaryStatus); - sheetData.storage = items.filter(i => !["ship_position", "spell", "skills", "trait", "component"].includes(i.type)); + sheetData.storage = items.filter(i => ![...TWODSIX.WeightlessItems, "ship_position", "component"].includes(i.type)); sheetData.container.nonCargo = actor.itemTypes.component.filter( i => i.system.subtype !== "cargo"); } sheetData.effects = Array.from(actor.allApplicableEffects()); @@ -515,14 +526,16 @@ export abstract class AbstractTwodsixActorSheet extends ActorSheet { } /** - * Handle clickable skill rolls. + * Handle clickable skill and talent rolls. * @param {Event} event The originating click event * @param {boolean} showTrowDiag Whether to show the throw dialog or not * @private */ - protected async _onSkillRoll(event, showThrowDiag: boolean): Promise { - const item = this.getItem(event); - await item.skillRoll(showThrowDiag ); + protected async _onSkillTalentRoll(event:Event, showThrowDiag: boolean): Promise { + const item:TwodsixItem = this.getItem(event); + if (item) { + item.doSkillTalentRoll(showThrowDiag); + } } /** @@ -531,7 +544,7 @@ export abstract class AbstractTwodsixActorSheet extends ActorSheet { * @param {boolean} showThrowDiag Whether to show the throw dialog or not * @private */ - protected async _onRollChar(event, showThrowDiag: boolean): Promise { + protected async _onRollChar(event:Event, showThrowDiag: boolean): Promise { const shortChar = $(event.currentTarget).data("label"); //const fullCharLabel = getKeyByValue(TWODSIX.CHARACTERISTICS, shortChar); //const displayShortChar = (this.actor).system["characteristics"][fullCharLabel].displayShortLabel; @@ -557,21 +570,6 @@ export abstract class AbstractTwodsixActorSheet extends ActorSheet { } } - /** - * Handle send to chat. - * @param {Event} event The originating click event - * @private - */ - protected async _onSendToChat(event): Promise { - const item = this.getItem(event); - const picture = item.img; - const capType = item.type.capitalize(); - if (item.type === "trait" || item.type === "spell") { - const msg = `
${capType}: ${item.name}

${item.system["description"]}`; - ChatMessage.create({ content: msg, speaker: ChatMessage.getSpeaker({ actor: this.actor }) }); - } - } - /** * Handle when the clicking on status icon. * @param {Event} event The originating click event diff --git a/src/module/sheets/TwodsixItemSheet.ts b/src/module/sheets/TwodsixItemSheet.ts index e5432b2f2..885894ebe 100644 --- a/src/module/sheets/TwodsixItemSheet.ts +++ b/src/module/sheets/TwodsixItemSheet.ts @@ -71,7 +71,8 @@ export class TwodsixItemSheet extends AbstractTwodsixItemSheet { rollTypes: getRollTypeSelectObject(), augLocations: TWODSIX.AUG_LOCATIONS, consumableOptions: getConsumableOptions(this.item), - itemTypes: TWODSIX.ITEM_TYPE_SELECT + itemTypes: TWODSIX.ITEM_TYPE_SELECT, + psiTalentsRequireRoll: game.settings.get('twodsix', 'psiTalentsRequireRoll') }; if (this.item.type === 'skills') { @@ -188,6 +189,12 @@ export class TwodsixItemSheet extends AbstractTwodsixItemSheet { const duplicateItem = this.item.toJSON(); const newType = event.currentTarget.value; duplicateItem.system.priorType = this.item.type; + //Remove Sorcery As Associated Skill if Spell + if (duplicateItem.system.priorType === 'spell' && duplicateItem.system.associatedSkillName === game.settings.get("twodsix", "sorcerySkill")) { + duplicateItem.system.associatedSkillName = ""; + } else if (newType === 'spell' && duplicateItem.system.associatedSkillName === "") { + duplicateItem.system.associatedSkillName = game.settings.get("twodsix", "sorcerySkill"); + } duplicateItem.system.type = newType; duplicateItem.type = newType; const options = {renderSheet: true}; @@ -443,7 +450,7 @@ export class TwodsixItemSheet extends AbstractTwodsixItemSheet { } else if (dropData.type === 'Item'){ //This part handles just comsumables TwodsixItemSheet.check(!this.item.isOwned, "OnlyOwnedItems"); - TwodsixItemSheet.check(["skills", "trait", "spell"].includes(this.item.type), "TraitsandSkillsNoConsumables"); + TwodsixItemSheet.check(TWODSIX.WeightlessItems.includes(this.item.type), "TraitsandSkillsNoConsumables"); TwodsixItemSheet.check(dropData.type !== "Item", "OnlyDropItems"); diff --git a/src/module/utils/TwodsixDiceRoll.ts b/src/module/utils/TwodsixDiceRoll.ts index 53732ed6c..9d68e9aba 100644 --- a/src/module/utils/TwodsixDiceRoll.ts +++ b/src/module/utils/TwodsixDiceRoll.ts @@ -307,7 +307,7 @@ export class TwodsixDiceRoll { switch (modifierName) { case "item": flavorText += (this.rollSettings.skillRoll ? ` &` : ` ${usingString}`) + ` ${this.rollSettings.itemName}`; - flavorTable += `${game.i18n.localize("TWODSIX.Chat.Roll.ItemModifier")}${this.rollSettings.itemName}`; + flavorTable += `${game.i18n.localize("TWODSIX.Chat.Roll." + (this.item?.type === 'psiAbility' ? "AbilityModifier" : "ItemModifier"))}${this.rollSettings.itemName}`; break; case "chain": flavorText += ` ${game.i18n.localize("TWODSIX.Chat.Roll.WithChainBonus")}`; diff --git a/src/module/utils/TwodsixRollSettings.ts b/src/module/utils/TwodsixRollSettings.ts index 292fb66f8..dde17d2e8 100644 --- a/src/module/utils/TwodsixRollSettings.ts +++ b/src/module/utils/TwodsixRollSettings.ts @@ -97,7 +97,11 @@ export class TwodsixRollSettings { rollClass = "ShipWeapon"; } else { rollClass = "ShipAction"; - } + } ////NEED TO EXPAND TYPES HERE to INCLUDE SP + } else if (anItem.type === "spell") { + rollClass = "Spell"; + } else if (anItem.type === "psiAbility") { + rollClass = "PsionicAbility"; } else { rollClass = "Item"; } @@ -115,6 +119,7 @@ export class TwodsixRollSettings { this.rollMode = settings?.rollMode ?? game.settings.get('core', 'rollMode'); this.skillRoll = !!(settings?.skillRoll ?? aSkill); this.itemRoll = !!(anItem); + this.isPsionicAbility = this.itemRoll ? anItem.type === "psiAbility" : false; this.itemName = settings?.itemName ?? itemName; this.showRangeModifier = (game.settings.get('twodsix', 'rangeModifierType') !== 'none' && anItem?.type === "weapon" && settings?.rollModifiers?.rangeLabel) ?? false; this.showTargetModifier = Object.keys(TWODSIX.TARGET_DM).length > 1; @@ -144,7 +149,7 @@ export class TwodsixRollSettings { selectedSkill: aSkill?.uuid, skillLevelMax: settings?.rollModifiers?.skillLevelMax ?? undefined, armorModifier: settings?.rollModifiers?.armorModifier ?? 0, - armorLabel: settings?.rollModifiers?.armorLabel ?? "", + armorLabel: settings?.rollModifiers?.armorLabel ?? "" }; this.flags = { rollClass: rollClass, @@ -229,6 +234,7 @@ export class TwodsixRollSettings { showConditions: (game.settings.get('twodsix', 'useWoundedStatusIndicators') || game.settings.get('twodsix', 'useEncumbranceStatusIndicators')), showWounds: game.settings.get('twodsix', 'useWoundedStatusIndicators'), showEncumbered: game.settings.get('twodsix', 'useEncumbranceStatusIndicators'), + isPsionicAbility: this.isPsionicAbility }; const buttons = { diff --git a/src/module/utils/TwodsixShipActions.ts b/src/module/utils/TwodsixShipActions.ts index 2b16b2493..d6f497d51 100644 --- a/src/module/utils/TwodsixShipActions.ts +++ b/src/module/utils/TwodsixShipActions.ts @@ -87,9 +87,9 @@ export class TwodsixShipActions { } if (extra.component) { - return extra.component.skillRoll(false, options); + return await extra.component.skillRoll(false, options); } else { - return skill.skillRoll(false, options); + return await skill.skillRoll(false, options); } } } else { diff --git a/src/module/utils/actorDamage.ts b/src/module/utils/actorDamage.ts index 7839ed3b6..b8b2e50ee 100644 --- a/src/module/utils/actorDamage.ts +++ b/src/module/utils/actorDamage.ts @@ -75,7 +75,7 @@ export class Stats { this.damageLabel = damageLabels[damageType] || damageLabel; this.armorPiercingValue = armorPiercingValue; if (actor.type !== "ship") { - this.primaryArmor = (actor.system).primaryArmor.value; + this.primaryArmor = this.damageType === 'psionic' ? 0 : (actor.system).primaryArmor.value; //primary armor does not stop psionic damage this.secondaryArmor = actor.getSecondaryProtectionValue(damageType); this.parryArmor = parryArmor; this.canOnlyBeBlocked = canOnlyBeBlocked; diff --git a/src/twodsix.ts b/src/twodsix.ts index aa2c351c1..e3d20435a 100644 --- a/src/twodsix.ts +++ b/src/twodsix.ts @@ -31,7 +31,7 @@ import { TwodsixRollSettings } from "./module/utils/TwodsixRollSettings"; import { addCustomEnrichers } from "./module/utils/enrichers"; import {TravellerData, AnimalData, RobotData} from "./module/data/characters"; import { ShipData, SpaceObjectData, VehicleData } from "./module/data/vehicles"; -import { ArmorData, AugmentData, ComponentData, ComputerData, ConsumableData, JunkStorageData, ShipPositionData, SkillData, SpellData, TraitData, WeaponData } from "./module/data/item"; +import { ArmorData, AugmentData, ComponentData, ComputerData, ConsumableData, JunkStorageData, ShipPositionData, SkillData, SpellData, TraitData, WeaponData, PsiAbilityData } from "./module/data/item"; import { GearData } from "./module/data/item-base"; import { TwodsixActiveEffect } from "./module/entities/TwodsixActiveEffect"; import { TwodsixBattleSheet } from "./module/sheets/TwodsixBattleSheet"; @@ -147,7 +147,8 @@ Hooks.once('init', async function () { "consumable": ConsumableData, "component": ComponentData, "ship_position": ShipPositionData, - "computer": ComputerData + "computer": ComputerData, + "psiAbility": PsiAbilityData }); //Extend ActiveEffects class with custom overrides diff --git a/src/types/template.d.ts b/src/types/template.d.ts index 1e4a5b54c..e51d5e163 100644 --- a/src/types/template.d.ts +++ b/src/types/template.d.ts @@ -1,6 +1,5 @@ //Everything except the namespace is generated from template.json using https://app.quicktype.io/?l=ts - // Actors export interface TravellerDataSource { type:'traveller'; @@ -78,6 +77,11 @@ export interface SpellDataSource { system:Spell; } +export interface PsiAbilityDataSource { + type:'psiAbility'; + system:PsiAbility; +} + export interface ConsumableDataSource { type:'consumable'; system:Consumable; @@ -114,6 +118,7 @@ export type ItemTwodsixDataSource = ArmorDataSource | StorageItemDataSource | TraitDataSource | SpellDataSource + | PsiAbilityDataSource | WeaponDataSource | ShipPositionDataSource | ComputerDataSource @@ -532,6 +537,7 @@ export interface Item { skills:Skills; trait:Trait; spell:Spell; + psiAbility:PsiAbility; consumable:Consumable; component:Component; computer:Computer; @@ -703,6 +709,20 @@ export interface Spell extends LinkTemplate, TargetTemplate { damage:string; } +export interface PsiAbility extends LinkTemplate, TargetTemplate { + templates:string[]; + description:string; + duration:string; + shortdescr:string; + subtype:string; + associatedSkillName:string; + damage:string; + rangeBand:string; + range:string; + psiCost:integer; + damageType:string; +} + export interface Weapon extends GearTemplate, LinkTemplate, TargetTemplate { templates:string[]; range:string; diff --git a/static/lang/en.json b/static/lang/en.json index 53be759fd..745bb99e2 100644 --- a/static/lang/en.json +++ b/static/lang/en.json @@ -60,6 +60,7 @@ "Melee": "Melee", "Plasma": "Plasma", "Poison": "Poison", + "Psionic": "Psionic", "Stun": "Stun" }, "Dialogs": { @@ -182,7 +183,8 @@ "Value": "Value", "WEAPONS": "WEAPONS", "Weapons": "Weapons", - "Weight": "Wt" + "Weight": "Wt", + "PSIONIC_ABILITIES": "PSIONIC ABILITIES" }, "Notes": { "NOTES": "CHARACTER NOTES", @@ -325,6 +327,7 @@ "RollModifiers": "Roll Modifiers", "SkillModifier": "Skill Level", "ItemModifier": "Item Bonus", + "AbilityModifier": "Ability Level", "Rof": "Rate-of-Fire", "Wounds": "Wounds", "Encumbered": "Encumbered", @@ -623,7 +626,9 @@ "MoveStorage": "Move Storage", "New": "New", "Storage": "Storage", - "Junk": "Junk" + "Junk": "Junk", + "AssignPsiAbility": "Assign Psionic Ability", + "AssignSpell": "Assign Spell" }, "Skills": { "DEX": "DEX", @@ -735,7 +740,15 @@ }, "Computer": { "ProcessingPower": "Processing Power" + }, + "Psionics": { + "PsiCost": "Psionic Points Cost", + "UsePoints": "Use Psionic Points", + "Used": "Used", + "Pts": "Pts.", + "Level": "Level" } + }, "Target": { "AreaEffect": "Area of Effect", @@ -1260,7 +1273,7 @@ }, "allowDropOnIcon": { "hint": "Items can be dropped and added to actors directly on top of icon without opening actor sheet. Duplicate items increase inventory quantity. Chat damage items will apply damage.", - "name": "Allow Items to be droped onto icons" + "name": "Allow Items to be dropped onto icons" }, "defaultMovement": { "hint": "The default movement/walk for new travellers (typically a minor action), e.g. 10 for 10m", @@ -1287,7 +1300,7 @@ "name": "Unarmed Damage Formula" }, "showTimeframe": { - "hint": "Add timeframe options to skill / characteristic rolls/", + "hint": "Add timeframe options to skill / characteristic rolls.", "name": "Show Timeframe in Roll Dialog" }, "showStatusIcons": { @@ -1571,6 +1584,10 @@ "componentsIgnored": { "hint": "A list of non-cargo components to omit from 'Damage' section of Battle Sheet", "name": "Components Ignored for Damage Tracking" + }, + "psiTalentsRequireRoll": { + "hint": "Using a psionic talent requires a sucessful skill roll", + "name": "Psionics Require Roll" } }, "CloseAndCreateNew": "Close and create new", @@ -1593,7 +1610,8 @@ "component": "component", "computer": "computer", "unknown": "unknown", - "ship_position": "position" + "ship_position": "position", + "psiAbility": "psionic ability" }, "speeds": { "walk": "walk", @@ -1665,7 +1683,8 @@ "NoJournalFound": "Can't find linked Journal Entry", "CantFindMeleeSkill": "Can't find Melee Combat skill", "CantFindMeleeWeapon": "Can't find an equipped melee weapon to parry", - "CantFindShield": "Can't find an equipped shield to block" + "CantFindShield": "Can't find an equipped shield to block", + "NoPsiPoints": "Must have at least one psionic point to use an ability" } }, "TYPES": { @@ -1686,6 +1705,7 @@ "junk": "Junk", "skills": "Skill", "trait": "Trait", + "psiAbility": "Psionic Ability", "ship_position": "Ship Position", "backpackPl": "Backpacks", "containerPl": "Containers", @@ -1703,7 +1723,8 @@ "junkPl": "Junk", "skillsPl": "Skills", "traitPl": "Traits", - "ship_positionPl": "Ship Positions" + "ship_positionPl": "Ship Positions", + "psiAbilityPl": "Psionic Abilities" }, "Actor": { "traveller": "Traveller", diff --git a/static/packs/cepheus-deluxe-items/000024.ldb b/static/packs/cepheus-deluxe-items/000024.ldb deleted file mode 100644 index 6f998bd6d..000000000 Binary files a/static/packs/cepheus-deluxe-items/000024.ldb and /dev/null differ diff --git a/static/packs/cepheus-deluxe-items/000038.ldb b/static/packs/cepheus-deluxe-items/000038.ldb new file mode 100644 index 000000000..50b45f475 Binary files /dev/null and b/static/packs/cepheus-deluxe-items/000038.ldb differ diff --git a/static/packs/cepheus-deluxe-items/000022.log b/static/packs/cepheus-deluxe-items/000041.log similarity index 100% rename from static/packs/cepheus-deluxe-items/000022.log rename to static/packs/cepheus-deluxe-items/000041.log diff --git a/static/packs/cepheus-deluxe-items/CURRENT b/static/packs/cepheus-deluxe-items/CURRENT index 5b5401076..150cae32f 100644 --- a/static/packs/cepheus-deluxe-items/CURRENT +++ b/static/packs/cepheus-deluxe-items/CURRENT @@ -1 +1 @@ -MANIFEST-000011 +MANIFEST-000039 diff --git a/static/packs/cepheus-deluxe-items/LOG b/static/packs/cepheus-deluxe-items/LOG index 3271cc064..b6e1da42d 100644 --- a/static/packs/cepheus-deluxe-items/LOG +++ b/static/packs/cepheus-deluxe-items/LOG @@ -1,17 +1,8 @@ -2023/07/23-13:11:50.856625 7000055d3000 Recovering log #8 -2023/07/23-13:11:50.856818 7000055d3000 Recovering log #20 -2023/07/23-13:11:50.858147 7000055d3000 Delete type=3 #6 -2023/07/23-13:11:50.858300 7000055d3000 Delete type=0 #8 -2023/07/23-13:11:50.858442 7000055d3000 Delete type=0 #20 -2023/07/24-12:46:13.174151 70000665c000 Level-0 table #23: started -2023/07/24-12:46:13.175214 70000665c000 Level-0 table #23: 29358 bytes OK -2023/07/24-12:46:13.175607 70000665c000 Delete type=0 #21 -2023/07/24-12:46:13.176807 70000665c000 Manual compaction at level-0 from '!folders!3tvDUg0aKiHbJ8Du' @ 72057594037927935 : 1 .. '!items.effects!eqI5bWQKPyaApqu8.CfPLmZMvrEE9uEKD' @ 0 : 0; will stop at (end) -2023/07/24-12:46:13.176879 70000665c000 Manual compaction at level-1 from '!folders!3tvDUg0aKiHbJ8Du' @ 72057594037927935 : 1 .. '!items.effects!eqI5bWQKPyaApqu8.CfPLmZMvrEE9uEKD' @ 0 : 0; will stop at '!items!vYz6QCX5AIFZI4B6' @ 744 : 1 -2023/07/24-12:46:13.176892 70000665c000 Compacting 1@1 + 1@2 files -2023/07/24-12:46:13.182395 70000665c000 Generated table #24@1: 339 keys, 212221 bytes -2023/07/24-12:46:13.182438 70000665c000 Compacted 1@1 + 1@2 files => 212221 bytes -2023/07/24-12:46:13.182640 70000665c000 compacted to: files[ 0 0 1 0 0 0 0 ] -2023/07/24-12:46:13.182805 70000665c000 Delete type=2 #10 -2023/07/24-12:46:13.183130 70000665c000 Delete type=2 #23 -2023/07/24-12:46:13.188257 70000665c000 Manual compaction at level-1 from '!items!vYz6QCX5AIFZI4B6' @ 744 : 1 .. '!items.effects!eqI5bWQKPyaApqu8.CfPLmZMvrEE9uEKD' @ 0 : 0; will stop at (end) +2024/11/02-12:56:04.880411 70000c325000 Recovering log #36 +2024/11/02-12:56:04.881515 70000c325000 Delete type=3 #34 +2024/11/02-12:56:04.881690 70000c325000 Delete type=0 #36 +2024/11/02-12:57:00.127526 70000d3ae000 Level-0 table #42: started +2024/11/02-12:57:00.127580 70000d3ae000 Level-0 table #42: 0 bytes OK +2024/11/02-12:57:00.128251 70000d3ae000 Delete type=0 #40 +2024/11/02-12:57:00.137042 70000d3ae000 Manual compaction at level-0 from '!folders!3tvDUg0aKiHbJ8Du' @ 72057594037927935 : 1 .. '!items.effects!eqI5bWQKPyaApqu8.CfPLmZMvrEE9uEKD' @ 0 : 0; will stop at (end) +2024/11/02-12:57:00.140168 70000d3ae000 Manual compaction at level-1 from '!folders!3tvDUg0aKiHbJ8Du' @ 72057594037927935 : 1 .. '!items.effects!eqI5bWQKPyaApqu8.CfPLmZMvrEE9uEKD' @ 0 : 0; will stop at (end) diff --git a/static/packs/cepheus-deluxe-items/LOG.old b/static/packs/cepheus-deluxe-items/LOG.old index 91a50ba4b..0d3ca0db6 100644 --- a/static/packs/cepheus-deluxe-items/LOG.old +++ b/static/packs/cepheus-deluxe-items/LOG.old @@ -1,15 +1,15 @@ -2023/04/07-08:19:13.134112 70000f2dc000 Recovering log #4 -2023/04/07-08:19:13.135599 70000f2dc000 Delete type=0 #4 -2023/04/07-08:19:13.135783 70000f2dc000 Delete type=3 #2 -2023/04/07-13:21:26.419341 700010b68000 Level-0 table #9: started -2023/04/07-13:21:26.423750 700010b68000 Level-0 table #9: 208979 bytes OK -2023/04/07-13:21:26.424222 700010b68000 Delete type=0 #7 -2023/04/07-13:21:26.427872 700010b68000 Manual compaction at level-0 from '!folders!3tvDUg0aKiHbJ8Du' @ 72057594037927935 : 1 .. '!items.effects!eqI5bWQKPyaApqu8.CfPLmZMvrEE9uEKD' @ 0 : 0; will stop at (end) -2023/04/07-13:21:26.427966 700010b68000 Manual compaction at level-1 from '!folders!3tvDUg0aKiHbJ8Du' @ 72057594037927935 : 1 .. '!items.effects!eqI5bWQKPyaApqu8.CfPLmZMvrEE9uEKD' @ 0 : 0; will stop at '!items!zmsJljbsb3GXCYjW' @ 365 : 1 -2023/04/07-13:21:26.427977 700010b68000 Compacting 1@1 + 1@2 files -2023/04/07-13:21:26.433296 700010b68000 Generated table #10@1: 334 keys, 204553 bytes -2023/04/07-13:21:26.433328 700010b68000 Compacted 1@1 + 1@2 files => 204553 bytes -2023/04/07-13:21:26.433489 700010b68000 compacted to: files[ 0 0 1 0 0 0 0 ] -2023/04/07-13:21:26.433594 700010b68000 Delete type=2 #5 -2023/04/07-13:21:26.433929 700010b68000 Delete type=2 #9 -2023/04/07-13:21:26.441411 700010b68000 Manual compaction at level-1 from '!items!zmsJljbsb3GXCYjW' @ 365 : 1 .. '!items.effects!eqI5bWQKPyaApqu8.CfPLmZMvrEE9uEKD' @ 0 : 0; will stop at (end) +2024/11/02-12:48:55.082090 70000d9d5000 Recovering log #30 +2024/11/02-12:48:55.083187 70000d9d5000 Delete type=3 #27 +2024/11/02-12:48:55.083315 70000d9d5000 Delete type=0 #30 +2024/11/02-12:54:13.681305 70000ea5e000 Level-0 table #37: started +2024/11/02-12:54:13.682765 70000ea5e000 Level-0 table #37: 3181 bytes OK +2024/11/02-12:54:13.683541 70000ea5e000 Delete type=0 #35 +2024/11/02-12:54:13.698470 70000ea5e000 Manual compaction at level-0 from '!folders!3tvDUg0aKiHbJ8Du' @ 72057594037927935 : 1 .. '!items.effects!eqI5bWQKPyaApqu8.CfPLmZMvrEE9uEKD' @ 0 : 0; will stop at (end) +2024/11/02-12:54:13.698576 70000ea5e000 Manual compaction at level-1 from '!folders!3tvDUg0aKiHbJ8Du' @ 72057594037927935 : 1 .. '!items.effects!eqI5bWQKPyaApqu8.CfPLmZMvrEE9uEKD' @ 0 : 0; will stop at '!items!tZ2ePxOU5UlZGWnR' @ 1220 : 1 +2024/11/02-12:54:13.698589 70000ea5e000 Compacting 1@1 + 1@2 files +2024/11/02-12:54:13.704782 70000ea5e000 Generated table #38@1: 340 keys, 225181 bytes +2024/11/02-12:54:13.704855 70000ea5e000 Compacted 1@1 + 1@2 files => 225181 bytes +2024/11/02-12:54:13.705087 70000ea5e000 compacted to: files[ 0 0 1 0 0 0 0 ] +2024/11/02-12:54:13.705225 70000ea5e000 Delete type=2 #37 +2024/11/02-12:54:13.706665 70000ea5e000 Delete type=2 #33 +2024/11/02-12:54:13.720492 70000ea5e000 Manual compaction at level-1 from '!items!tZ2ePxOU5UlZGWnR' @ 1220 : 1 .. '!items.effects!eqI5bWQKPyaApqu8.CfPLmZMvrEE9uEKD' @ 0 : 0; will stop at (end) diff --git a/static/packs/cepheus-deluxe-items/MANIFEST-000011 b/static/packs/cepheus-deluxe-items/MANIFEST-000011 deleted file mode 100644 index 2ccd95f50..000000000 Binary files a/static/packs/cepheus-deluxe-items/MANIFEST-000011 and /dev/null differ diff --git a/static/packs/cepheus-deluxe-items/MANIFEST-000017 b/static/packs/cepheus-deluxe-items/MANIFEST-000017 deleted file mode 100644 index d4feccd73..000000000 Binary files a/static/packs/cepheus-deluxe-items/MANIFEST-000017 and /dev/null differ diff --git a/static/packs/cepheus-deluxe-items/MANIFEST-000039 b/static/packs/cepheus-deluxe-items/MANIFEST-000039 new file mode 100644 index 000000000..8055f23f0 Binary files /dev/null and b/static/packs/cepheus-deluxe-items/MANIFEST-000039 differ diff --git a/static/packs/cepheus-light-items/000024.ldb b/static/packs/cepheus-light-items/000024.ldb deleted file mode 100644 index f55c5677c..000000000 Binary files a/static/packs/cepheus-light-items/000024.ldb and /dev/null differ diff --git a/static/packs/cepheus-light-items/000022.log b/static/packs/cepheus-light-items/000036.log similarity index 100% rename from static/packs/cepheus-light-items/000022.log rename to static/packs/cepheus-light-items/000036.log diff --git a/static/packs/cepheus-light-items/000038.ldb b/static/packs/cepheus-light-items/000038.ldb new file mode 100644 index 000000000..761c9d67d Binary files /dev/null and b/static/packs/cepheus-light-items/000038.ldb differ diff --git a/static/packs/cepheus-light-items/CURRENT b/static/packs/cepheus-light-items/CURRENT index 5b5401076..eea9b0f2d 100644 --- a/static/packs/cepheus-light-items/CURRENT +++ b/static/packs/cepheus-light-items/CURRENT @@ -1 +1 @@ -MANIFEST-000011 +MANIFEST-000034 diff --git a/static/packs/cepheus-light-items/LOG b/static/packs/cepheus-light-items/LOG index a45b065ce..bdb7e34a6 100644 --- a/static/packs/cepheus-light-items/LOG +++ b/static/packs/cepheus-light-items/LOG @@ -1,17 +1,15 @@ -2023/07/23-13:11:50.816694 700005dd6000 Recovering log #8 -2023/07/23-13:11:50.816915 700005dd6000 Recovering log #20 -2023/07/23-13:11:50.818103 700005dd6000 Delete type=3 #6 -2023/07/23-13:11:50.818307 700005dd6000 Delete type=0 #8 -2023/07/23-13:11:50.818512 700005dd6000 Delete type=0 #20 -2023/07/24-12:46:13.147861 70000665c000 Level-0 table #23: started -2023/07/24-12:46:13.148400 70000665c000 Level-0 table #23: 8228 bytes OK -2023/07/24-12:46:13.148655 70000665c000 Delete type=0 #21 -2023/07/24-12:46:13.149918 70000665c000 Manual compaction at level-0 from '!folders!3JNYaeiiyV2bfLVW' @ 72057594037927935 : 1 .. '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 0 : 0; will stop at (end) -2023/07/24-12:46:13.149982 70000665c000 Manual compaction at level-1 from '!folders!3JNYaeiiyV2bfLVW' @ 72057594037927935 : 1 .. '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 0 : 0; will stop at '!items!zVqWalHXjv9pPEH8' @ 453 : 1 -2023/07/24-12:46:13.149989 70000665c000 Compacting 1@1 + 1@2 files -2023/07/24-12:46:13.152175 70000665c000 Generated table #24@1: 202 keys, 107488 bytes -2023/07/24-12:46:13.152213 70000665c000 Compacted 1@1 + 1@2 files => 107488 bytes -2023/07/24-12:46:13.152378 70000665c000 compacted to: files[ 0 0 1 0 0 0 0 ] -2023/07/24-12:46:13.152525 70000665c000 Delete type=2 #10 -2023/07/24-12:46:13.153006 70000665c000 Delete type=2 #23 -2023/07/24-12:46:13.154054 70000665c000 Manual compaction at level-1 from '!items!zVqWalHXjv9pPEH8' @ 453 : 1 .. '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 0 : 0; will stop at (end) +2024/11/02-12:48:54.980134 70000d9d5000 Recovering log #30 +2024/11/02-12:48:54.981363 70000d9d5000 Delete type=3 #27 +2024/11/02-12:48:54.981517 70000d9d5000 Delete type=0 #30 +2024/11/02-12:54:13.585203 70000ea5e000 Level-0 table #37: started +2024/11/02-12:54:13.585975 70000ea5e000 Level-0 table #37: 4104 bytes OK +2024/11/02-12:54:13.586407 70000ea5e000 Delete type=0 #35 +2024/11/02-12:54:13.591962 70000ea5e000 Manual compaction at level-0 from '!folders!3JNYaeiiyV2bfLVW' @ 72057594037927935 : 1 .. '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 0 : 0; will stop at (end) +2024/11/02-12:54:13.592032 70000ea5e000 Manual compaction at level-1 from '!folders!3JNYaeiiyV2bfLVW' @ 72057594037927935 : 1 .. '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 0 : 0; will stop at '!items!tZ2ePxOU5UlZGWnR' @ 704 : 1 +2024/11/02-12:54:13.592040 70000ea5e000 Compacting 1@1 + 1@2 files +2024/11/02-12:54:13.594515 70000ea5e000 Generated table #38@1: 208 keys, 124694 bytes +2024/11/02-12:54:13.594539 70000ea5e000 Compacted 1@1 + 1@2 files => 124694 bytes +2024/11/02-12:54:13.594648 70000ea5e000 compacted to: files[ 0 0 1 0 0 0 0 ] +2024/11/02-12:54:13.594719 70000ea5e000 Delete type=2 #37 +2024/11/02-12:54:13.594953 70000ea5e000 Delete type=2 #33 +2024/11/02-12:54:13.601028 70000ea5e000 Manual compaction at level-1 from '!items!tZ2ePxOU5UlZGWnR' @ 704 : 1 .. '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 0 : 0; will stop at (end) diff --git a/static/packs/cepheus-light-items/LOG.old b/static/packs/cepheus-light-items/LOG.old index 74698cbb2..80a8b8f72 100644 --- a/static/packs/cepheus-light-items/LOG.old +++ b/static/packs/cepheus-light-items/LOG.old @@ -1,15 +1,23 @@ -2023/04/07-08:19:13.091550 700010ae5000 Recovering log #4 -2023/04/07-08:19:13.093278 700010ae5000 Delete type=0 #4 -2023/04/07-08:19:13.093482 700010ae5000 Delete type=3 #2 -2023/04/07-13:21:26.347127 700010b68000 Level-0 table #9: started -2023/04/07-13:21:26.349243 700010b68000 Level-0 table #9: 115869 bytes OK -2023/04/07-13:21:26.349558 700010b68000 Delete type=0 #7 -2023/04/07-13:21:26.351376 700010b68000 Manual compaction at level-0 from '!folders!3JNYaeiiyV2bfLVW' @ 72057594037927935 : 1 .. '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 0 : 0; will stop at (end) -2023/04/07-13:21:26.355612 700010b68000 Manual compaction at level-1 from '!folders!3JNYaeiiyV2bfLVW' @ 72057594037927935 : 1 .. '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 0 : 0; will stop at '!items!znchdhlB8zBvUKE7' @ 309 : 1 -2023/04/07-13:21:26.355624 700010b68000 Compacting 1@1 + 1@2 files -2023/04/07-13:21:26.357641 700010b68000 Generated table #10@1: 202 keys, 106985 bytes -2023/04/07-13:21:26.357665 700010b68000 Compacted 1@1 + 1@2 files => 106985 bytes -2023/04/07-13:21:26.357756 700010b68000 compacted to: files[ 0 0 1 0 0 0 0 ] -2023/04/07-13:21:26.357818 700010b68000 Delete type=2 #5 -2023/04/07-13:21:26.358042 700010b68000 Delete type=2 #9 -2023/04/07-13:21:26.358276 700010b68000 Manual compaction at level-1 from '!items!znchdhlB8zBvUKE7' @ 309 : 1 .. '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 0 : 0; will stop at (end) +2024/11/02-12:45:46.529491 700008733000 Recovering log #26 +2024/11/02-12:45:46.530503 700008733000 Level-0 table #28: started +2024/11/02-12:45:46.534130 700008733000 Level-0 table #28: 136723 bytes OK +2024/11/02-12:45:46.535876 700008733000 Delete type=3 #25 +2024/11/02-12:45:46.536089 700008733000 Delete type=0 #26 +2024/11/02-12:46:35.795871 7000087b6000 Level-0 table #31: started +2024/11/02-12:46:35.795901 7000087b6000 Level-0 table #31: 0 bytes OK +2024/11/02-12:46:35.796337 7000087b6000 Delete type=0 #29 +2024/11/02-12:46:35.800494 7000087b6000 Manual compaction at level-0 from '!folders!3JNYaeiiyV2bfLVW' @ 72057594037927935 : 1 .. '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 0 : 0; will stop at '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 623 : 1 +2024/11/02-12:46:35.800511 7000087b6000 Compacting 1@0 + 0@1 files +2024/11/02-12:46:35.802914 7000087b6000 Generated table #32@0: 222 keys, 123948 bytes +2024/11/02-12:46:35.802934 7000087b6000 Compacted 1@0 + 0@1 files => 123948 bytes +2024/11/02-12:46:35.803043 7000087b6000 compacted to: files[ 0 1 1 0 0 0 0 ] +2024/11/02-12:46:35.803116 7000087b6000 Delete type=2 #28 +2024/11/02-12:46:35.805031 7000087b6000 Manual compaction at level-0 from '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 623 : 1 .. '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 0 : 0; will stop at (end) +2024/11/02-12:46:35.810267 7000087b6000 Manual compaction at level-1 from '!folders!3JNYaeiiyV2bfLVW' @ 72057594037927935 : 1 .. '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 0 : 0; will stop at '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 623 : 1 +2024/11/02-12:46:35.810282 7000087b6000 Compacting 1@1 + 1@2 files +2024/11/02-12:46:35.813333 7000087b6000 Generated table #33@1: 208 keys, 124656 bytes +2024/11/02-12:46:35.813355 7000087b6000 Compacted 1@1 + 1@2 files => 124656 bytes +2024/11/02-12:46:35.813462 7000087b6000 compacted to: files[ 0 0 1 0 0 0 0 ] +2024/11/02-12:46:35.813530 7000087b6000 Delete type=2 #32 +2024/11/02-12:46:35.813718 7000087b6000 Delete type=2 #24 +2024/11/02-12:46:35.816019 7000087b6000 Manual compaction at level-1 from '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 623 : 1 .. '!items.effects!sARNM9jBfsjzkXvr.yh20LV8aj6p0tI1C' @ 0 : 0; will stop at (end) diff --git a/static/packs/cepheus-light-items/MANIFEST-000011 b/static/packs/cepheus-light-items/MANIFEST-000011 deleted file mode 100644 index 7a978b0dd..000000000 Binary files a/static/packs/cepheus-light-items/MANIFEST-000011 and /dev/null differ diff --git a/static/packs/cepheus-light-items/MANIFEST-000017 b/static/packs/cepheus-light-items/MANIFEST-000017 deleted file mode 100644 index 0b840d1ad..000000000 Binary files a/static/packs/cepheus-light-items/MANIFEST-000017 and /dev/null differ diff --git a/static/packs/cepheus-light-items/MANIFEST-000034 b/static/packs/cepheus-light-items/MANIFEST-000034 new file mode 100644 index 000000000..6f4b524fc Binary files /dev/null and b/static/packs/cepheus-light-items/MANIFEST-000034 differ diff --git a/static/styles/twodsix.css b/static/styles/twodsix.css index 51c0536ae..29420c4be 100644 --- a/static/styles/twodsix.css +++ b/static/styles/twodsix.css @@ -1675,6 +1675,14 @@ a.actor-notes-tab:hover { word-wrap: break-word; } +.items-psiAbilities { + display: grid; + grid-template-columns: 30% 10% 8% 12% 30% 10%; + grid-template-areas: + '. . . . . .'; + word-wrap: break-word; +} + .items-consumable { display: grid; grid-template-columns: 5% 40% 15% 10% 15% 15%; diff --git a/static/styles/twodsix_basic.css b/static/styles/twodsix_basic.css index 60645625b..7aace0bf3 100644 --- a/static/styles/twodsix_basic.css +++ b/static/styles/twodsix_basic.css @@ -894,7 +894,6 @@ input.stat-name-table { width: 1.5em; text-align: center; border: none; - border: none; -moz-appearance: textfield; appearance: textfield; } @@ -1193,6 +1192,15 @@ a.skill-tab.active, a.item-tab.active, a.finances-tab.active, a.info-tab.active, width: 98%; } +.items-psiAbilities { + display: grid; + grid-template-columns: 30% 10% 8% 12% 30% 10%; + grid-template-areas: + '. . . . . .'; + word-wrap: break-word; + width: 98%; +} + .items-consumable { display: grid; grid-template-columns: 5% 40% 15% 10% 15% 15%; diff --git a/static/template.json b/static/template.json index 18809ddff..54bb13b5d 100644 --- a/static/template.json +++ b/static/template.json @@ -10,7 +10,7 @@ "space-object": {} }, "Item": { - "types": ["equipment", "weapon", "armor", "augment", "storage", "tool", "junk", "skills", "spell", "trait", "consumable", "component", "ship_position", "computer"], + "types": ["equipment", "weapon", "armor", "augment", "storage", "tool", "junk", "skills", "spell", "trait", "consumable", "component", "ship_position", "computer", "psiAbility"], "equipment": {}, "tool": {}, "weapon": {}, diff --git a/static/templates/actors/parts/actor/actor-info.html b/static/templates/actors/parts/actor/actor-info.html index 68cee0529..d4c2cdfe9 100644 --- a/static/templates/actors/parts/actor/actor-info.html +++ b/static/templates/actors/parts/actor/actor-info.html @@ -39,6 +39,42 @@ + +
+ {{localize "TWODSIX.Actor.Items.PSIONIC_ABILITIES"}} +
+ {{localize "TWODSIX.Actor.Items.Name"}} + {{localize "TWODSIX.Actor.Skills.Level"}} + {{localize "TWODSIX.Items.Psionics.Pts"}} + {{localize "TWODSIX.Actor.Items.Damage"}} + {{localize "TWODSIX.Actor.Items.ShortDescr"}} + +
+
+ {{#each_sort_item container.psiAbility as |item id|}} +
+
    +
  1. + + {{item.name}} + {{item.system.skillModifier}} + {{item.system.psiCost}} + {{twodsix_limitLength item.system.damage 8}} + {{item.system.shortdescr}} + + + + + +
  2. +
+
+ {{/each_sort_item}} +
+
+ {{#if settings.showSpells}}
diff --git a/static/templates/chat/throw-dialog.html b/static/templates/chat/throw-dialog.html index faee92257..7fec2a03d 100644 --- a/static/templates/chat/throw-dialog.html +++ b/static/templates/chat/throw-dialog.html @@ -49,7 +49,7 @@ {{/if}} {{#if itemRoll}}
-
+ {{> "systems/twodsix/templates/items/parts/item-type.html"}}