/** * The Star Wars 5th Edition game system for Foundry Virtual Tabletop * Author: Kakeman89 * Software License: GNU GPLv3 * Content License: https://media.wizards.com/2016/downloads/SW5ERD-OGL_V5.1.pdf * Repository: https://gitlab.com/foundrynet/sw5e * Issue Tracker: https://gitlab.com/foundrynet/sw5e/issues */ // Import Modules import { SW5E } from "./module/config.js"; import { registerSystemSettings } from "./module/settings.js"; import { preloadHandlebarsTemplates } from "./module/templates.js"; import { _getInitiativeFormula } from "./module/combat.js"; import { measureDistance, getBarAttribute } from "./module/canvas.js"; import { Actor5e } from "./module/actor/entity.js"; import { ActorSheet5eCharacter } from "./module/actor/sheets/character.js"; import { Item5e } from "./module/item/entity.js"; import { ItemSheet5e } from "./module/item/sheet.js"; import { ActorSheet5eNPC } from "./module/actor/sheets/npc.js"; import { Dice5e } from "./module/dice.js"; import * as chat from "./module/chat.js"; import * as migrations from "./module/migration.js"; /* -------------------------------------------- */ /* Foundry VTT Initialization */ /* -------------------------------------------- */ Hooks.once("init", function() { console.log(`SW5e | Initializing Star Wars 5th Edition System\n${SW5E.ASCII}`); // Create a SW5E namespace within the game global game.sw5e = { Actor5e, Dice5e, Item5e, migrations, rollItemMacro }; // Record Configuration Values CONFIG.SW5E = SW5E; CONFIG.Actor.entityClass = Actor5e; CONFIG.Item.entityClass = Item5e; // Register System Settings registerSystemSettings(); // Patch Core Functions Combat.prototype._getInitiativeFormula = _getInitiativeFormula; // Register sheet application classes Actors.unregisterSheet("core", ActorSheet); Actors.registerSheet("sw5e", ActorSheet5eCharacter, { types: ["character"], makeDefault: true }); Actors.registerSheet("sw5e", ActorSheet5eNPC, { types: ["npc"], makeDefault: true }); Items.unregisterSheet("core", ItemSheet); <<<<<<< Updated upstream Items.registerSheet("sw5e", ItemSheet5e, {makeDefault: true}); ======= Items.registerSheet("sw5e", ItemSheet5e, { types: ['weapon', 'equipment', 'consumable', 'tool', 'loot', 'class', 'power', 'feat', 'species', 'backpack', 'archetype', 'classfeature', 'background'], makeDefault: true, label: "SW5E.SheetClassItem" }); >>>>>>> Stashed changes // Preload Handlebars Templates preloadHandlebarsTemplates(); }); /* -------------------------------------------- */ /* Foundry VTT Setup */ /* -------------------------------------------- */ /** * This function runs after game data has been requested and loaded from the servers, so entities exist */ Hooks.once("setup", function() { // Localize CONFIG objects once up-front const toLocalize = [ "abilities", "alignments", "conditionTypes", "consumableTypes", "currencies", "damageTypes", "distanceUnits", "equipmentTypes", "healingTypes", "itemActionTypes", "limitedUsePeriods", "senses", "skills", "powerComponents", "powerLevels", "powerPreparationModes", "powerSchools", "powerScalingModes", "targetTypes", "timePeriods", "weaponProperties", "weaponTypes", "languages", "polymorphSettings", "armorProficiencies", "weaponProficiencies", "toolProficiencies", "abilityActivationTypes", "actorSizes", "proficiencyLevels", "armorpropertiesTypes" ]; for ( let o of toLocalize ) { CONFIG.SW5E[o] = Object.entries(CONFIG.SW5E[o]).reduce((obj, e) => { obj[e[0]] = game.i18n.localize(e[1]); return obj; }, {}); } }); /* -------------------------------------------- */ /** * Once the entire VTT framework is initialized, check to see if we should perform a data migration */ Hooks.once("ready", function() { // Determine whether a system migration is required and feasible const currentVersion = game.settings.get("sw5e", "systemMigrationVersion"); const NEEDS_MIGRATION_VERSION = 0.84; const COMPATIBLE_MIGRATION_VERSION = 0.80; let needMigration = (currentVersion < NEEDS_MIGRATION_VERSION) || (currentVersion === null); const canMigrate = currentVersion >= COMPATIBLE_MIGRATION_VERSION; // Perform the migration if ( needMigration && game.user.isGM ) { if ( currentVersion && (currentVersion < COMPATIBLE_MIGRATION_VERSION) ) { ui.notifications.error(`Your SW5E system data is from too old a Foundry version and cannot be reliably migrated to the latest version. The process will be attempted, but errors may occur.`, {permanent: true}); } migrations.migrateWorld(); } // Wait to register hotbar drop hook on ready so that modules could register earlier if they want to Hooks.on("hotbarDrop", (bar, data, slot) => create5eMacro(data, slot)); }); /* -------------------------------------------- */ /* Canvas Initialization */ /* -------------------------------------------- */ Hooks.on("canvasInit", function() { // Extend Diagonal Measurement canvas.grid.diagonalRule = game.settings.get("sw5e", "diagonalMovement"); SquareGrid.prototype.measureDistance = measureDistance; // Extend Token Resource Bars Token.prototype.getBarAttribute = getBarAttribute; }); /* -------------------------------------------- */ /* Other Hooks */ /* -------------------------------------------- */ Hooks.on("renderChatMessage", (app, html, data) => { // Display action buttons chat.displayChatActionButtons(app, html, data); // Highlight critical success or failure die chat.highlightCriticalSuccessFailure(app, html, data); // Optionally collapse the content if (game.settings.get("sw5e", "autoCollapseItemCards")) html.find(".card-content").hide(); }); Hooks.on("getChatLogEntryContext", chat.addChatMessageContextOptions); Hooks.on("renderChatLog", (app, html, data) => Item5e.chatListeners(html)); Hooks.on('getActorDirectoryEntryContext', Actor5e.addDirectoryContextOptions); /* -------------------------------------------- */ /* Hotbar Macros */ /* -------------------------------------------- */ /** * Create a Macro from an Item drop. * Get an existing item macro if one exists, otherwise create a new one. * @param {Object} data The dropped data * @param {number} slot The hotbar slot to use * @returns {Promise} */ async function create5eMacro(data, slot) { if ( data.type !== "Item" ) return; if (!( "data" in data ) ) return ui.notifications.warn("You can only create macro buttons for owned Items"); const item = data.data; // Create the macro command const command = `game.sw5e.rollItemMacro("${item.name}");`; let macro = game.macros.entities.find(m => (m.name === item.name) && (m.command === command)); if ( !macro ) { macro = await Macro.create({ name: item.name, type: "script", img: item.img, command: command, flags: {"sw5e.itemMacro": true} }); } game.user.assignHotbarMacro(macro, slot); return false; } /* -------------------------------------------- */ /** * Create a Macro from an Item drop. * Get an existing item macro if one exists, otherwise create a new one. * @param {string} itemName * @return {Promise} */ function rollItemMacro(itemName) { const speaker = ChatMessage.getSpeaker(); let actor; if ( speaker.token ) actor = game.actors.tokens[speaker.token]; if ( !actor ) actor = game.actors.get(speaker.actor); // Get matching items const items = actor ? actor.items.filter(i => i.name === itemName) : []; if ( items.length > 1 ) { ui.notifications.warn(`Your controlled Actor ${actor.name} has more than one Item with name ${itemName}. The first matched item will be chosen.`); } else if ( items.length === 0 ) { return ui.notifications.warn(`Your controlled Actor does not have an item named ${itemName}`); } const item = items[0]; // Trigger the item roll if ( item.data.type === "power" ) return actor.usePower(item); return item.roll(); }