Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(species): allow species to have talents #1416

Merged
merged 2 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions modules/importer/data-importer.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ export default class DataImporter extends FormApplication {
let isSpecialization = false;
let isVehicle = false;
let isCareer = false;
let isSpecies = false;

let skillsFileName;
try {
Expand Down Expand Up @@ -190,25 +191,29 @@ export default class DataImporter extends FormApplication {
promises.push(OggDude.Import.Armor(xmlDoc, zip));
promises.push(OggDude.Import.Talents(xmlDoc, zip));
promises.push(OggDude.Import.ForcePowers(xmlDoc, zip));
//promises.push(OggDude.Import.SignatureAbilities(xmlDoc, zip));
promises.push(OggDude.Import.ItemAttachments(xmlDoc));
} else {
if (file.file.includes("/Specializations/")) {
isSpecialization = true;
}
if (file.file.includes("/Careers/")) {
// delay career processing so specializations can be done first
isCareer = true;
}
if (file.file.includes("/Species/")) {
promises.push(OggDude.Import.Species(zip));
// delay species processing so talents are done first
isSpecies = true;
}
if (file.file.includes("/Vehicles/")) {
isVehicle = true;
}
}
});

await Promise.all(promises);

if (isSpecies) {
await OggDude.Import.Species(zip);
}
if (isSpecialization) {
await OggDude.Import.Specializations(zip);
}
Expand All @@ -219,7 +224,7 @@ export default class DataImporter extends FormApplication {
await OggDude.Import.Vehicles(zip);
}

// import signature abilities
// import signature abilities (delayed so careers are done first)
ImportHelpers.clearCache();
const promisesPhase2 = [];
await this.asyncForEach(importFiles, async (file) => {
Expand Down
29 changes: 26 additions & 3 deletions modules/importer/import-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,14 @@ export default class ImportHelpers {
* @param {string} id - Entity Id
* @returns {object} - Entity Object Data
*/
static async findCompendiumEntityByImportId(type, id, packId, itemType) {
static async findCompendiumEntityByImportId(type, id, packId, itemType, skipCache) {
if (skipCache) {
const pack = game.packs.get(packId);
const contents = await pack.getDocuments();
return contents.find((item) => {
return item.flags.starwarsffg.ffgimportid === id;
});
}
const cachePack = async (packid) => {
if (!CONFIG.temporary[packid]) {
const pack = await game.packs.get(packid);
Expand Down Expand Up @@ -2252,12 +2259,28 @@ export default class ImportHelpers {

if (updateData?.data?.attributes) {
// Remove and repopulate all modifiers
if (entry.data?.attributes) {
for (let k of Object.keys(entry.data.attributes)) {
if (entry.system?.attributes) {
for (let k of Object.keys(entry.system.attributes)) {
if (!updateData.data.attributes.hasOwnProperty(k)) updateData.data.attributes[`-=${k}`] = null;
}
}
}
if (updateData?.data?.specializations) {
// Remove and repopulate all specializations
if (entry.system?.specializations) {
for (let k of Object.keys(entry.system.specializations)) {
if (!updateData.data.specializations.hasOwnProperty(k)) updateData.data.specializations[`-=${k}`] = null;
}
}
}
if (updateData?.data?.talents) {
// Remove and repopulate all talents
if (entry.system?.talents) {
for (let k of Object.keys(entry.system.talents)) {
if (!updateData.data.talents.hasOwnProperty(k)) updateData.data.talents[`-=${k}`] = null;
}
}
}

CONFIG.logger.debug(`Updating ${type} ${dataType} ${data.name} : ${JSON.stringify(updateData)}`);
try {
Expand Down
17 changes: 17 additions & 0 deletions modules/importer/oggdude/importers/species.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default class Species {
data.data = {
attributes: {},
description: item.Description,
talents: {},
};

// populate starting characteristics
Expand Down Expand Up @@ -79,6 +80,22 @@ export default class Species {
});
}

// populate talents
if (item?.TalentModifiers?.TalentModifier) {
for (const talentData of Object.values(item.TalentModifiers)) {
const talentKey = talentData.Key;
let talent = await ImportHelpers.findCompendiumEntityByImportId("Item", talentKey, "world.oggdudetalents", "talent", true);
if (!talent) {
continue;
}
data.data.talents[talent._id] = {
name: talent.name,
source: talent.uuid,
id: talent._id,
}
}
}

if (item?.OptionChoices?.OptionChoice) {
if (!Array.isArray(item.OptionChoices.OptionChoice)) {
item.OptionChoices.OptionChoice = [item.OptionChoices.OptionChoice];
Expand Down
59 changes: 59 additions & 0 deletions modules/items/item-sheet-ffg.js
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,40 @@ export class ItemSheetFFG extends ItemSheet {
}
new Item(item).sheet.render(true);
});
} else if (this.object.type === "species") {
try {
const dragDrop = new DragDrop({
dragSelector: ".item",
dropSelector: ".tab.talents",
permissions: { dragstart: this._canDragStart.bind(this), drop: this._canDragDrop.bind(this) },
callbacks: { drop: this.onDropTalentToSpecies.bind(this) },
});

dragDrop.bind($(`form.editable.item-sheet-${this.object.type}`)[0]);

// handle click events for talents on species
html.find(".item-delete").on("click", async (event) => {
event.stopPropagation();
const itemId = $(event.target).data("talent-id");
const itemType = $(event.target).data("item-type");
if (itemType === "talent") {
const updateData = this.object.system.talents;
delete updateData[itemId];
updateData[`-=${itemId}`] = null;
await this.object.update({system: {talents: updateData}})
}
});
// handle click events for specialization and signature ability on careers
html.find(".item-pill2").on("click", async (event) => {
event.stopPropagation();
const itemId = $(event.target).data("talent-id");
const itemType = $(event.target).data("item-type");
let item = await fromUuid(this.object.system.talents[itemId].source);
new Item(item).sheet.render(true);
});
} catch (err) {
CONFIG.logger.debug(err);
}
}

// hidden here instead of css to prevent non-editable display of edit button
Expand Down Expand Up @@ -1262,5 +1296,30 @@ export class ItemSheetFFG extends ItemSheet {
}
}

/**
* Handles dragging talents to the species sheet.
* @param event
* @returns {Promise<boolean>}
*/
async onDropTalentToSpecies(event) {
let data;

try {
data = JSON.parse(event.dataTransfer.getData("text/plain"));
if (data.type !== "Item") return;
} catch (err) {
return false;
}

// as of v10, "id" is not passed in - instead, "uuid" is. Let's use the Foundry API to get the item Document from the uuid.
const itemObject = await fromUuid(data.uuid);

if (!itemObject) return;

if (itemObject.type === "talent") {
await this.object.update({system: {talents: {[itemObject.id]: {name: itemObject.name, source: itemObject.uuid, id: itemObject.id}}}});
}
}

async _onDragItemStart(event) {}
}
39 changes: 39 additions & 0 deletions modules/swffg-main.js
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,45 @@ Hooks.once("ready", async () => {
Hooks.call(`closeAssociatedTalent_${item.object._id}`, item);
});

Hooks.on("createItem", (item, options, userId) => {
// add talents from species to character
if (item.isEmbedded && item.parent.documentName === "Actor") {
const actor = item.actor
if (item.type === "species" && actor.type === "character") {
const toAdd = [];
for(const talentId of Object.keys(item.system.talents)) {
const talentUuid = item.system.talents[talentId].source;
const talent = fromUuidSync(talentUuid);
if (talent) {
toAdd.push(talent);
}
}
if (toAdd.length > 0) {
actor.createEmbeddedDocuments("Item", toAdd);
}
}
}
});
Hooks.on("deleteItem", (item, options, userId) => {
// remove talents added by species
if (item.isEmbedded && item.parent.documentName === "Actor") {
const actor = item.actor
if (item.type === "species" && actor.type === "character") {
const toDelete = [];
for(const talentId of Object.keys(item.system.talents)) {
const speciesTalent = item.system.talents[talentId];
const actorTalent = actor.items.find(i => i.name === speciesTalent.name && i.type === "talent");
if (actorTalent) {
toDelete.push(actorTalent.id);
}
}
if (toDelete.length > 0) {
actor.deleteEmbeddedDocuments("Item", toDelete);
}
}
}
});

// Display Destiny Pool
let destinyPool = { light: game.settings.get("starwarsffg", "dPoolLight"), dark: game.settings.get("starwarsffg", "dPoolDark") };

Expand Down
4 changes: 2 additions & 2 deletions styles/mandar.css
Original file line number Diff line number Diff line change
Expand Up @@ -4032,7 +4032,7 @@ img {
max-width: 24px;
max-height: 24px;
}
.starwarsffg.sheet.item .item-sheet-career .signatureability-pill.item-pill2, .starwarsffg.sheet.item .item-sheet-career .specialization-pill.item-pill2 {
.starwarsffg.sheet.item .item-sheet-career .signatureability-pill.item-pill2, .starwarsffg.sheet.item .item-sheet-career .specialization-pill.item-pill2, .starwarsffg.sheet.item .item-sheet-species .talent-pill.item-pill2 {
padding: 0.1rem 0.5rem;
background-color: rgba(255, 255, 255, 0.25);
border: 1px solid rgba(255, 255, 255, 0.5);
Expand All @@ -4041,7 +4041,7 @@ img {
width: fit-content;
}

.starwarsffg.sheet.item .item-sheet-career .signatureability-pill.item-pill2 .item-delete, .starwarsffg.sheet.item .item-sheet-career .specialization-pill.item-pill2 .item-delete {
.starwarsffg.sheet.item .item-sheet-career .signatureability-pill.item-pill2 .item-delete, .starwarsffg.sheet.item .item-sheet-career .specialization-pill.item-pill2 .item-delete, .starwarsffg.sheet.item .item-sheet-species .talent-pill.item-pill2 .item-delete {
cursor: pointer;
}

Expand Down
3 changes: 2 additions & 1 deletion template.json
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,8 @@
"label": "Ship Attachment"
},
"species": {
"templates": ["core"]
"templates": ["core"],
"talents": {}
},
"criticalinjury": {
"templates": ["core"],
Expand Down
11 changes: 10 additions & 1 deletion templates/items/ffg-species-sheet.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ <h1 class="charname"><input name="name" type="text" value="{{item.name}}" placeh
</div>
</header>

{{!-- Sheet Tab Navigation --}} {{> "systems/starwarsffg/templates/parts/shared/ffg-tabs.html" displayLimited=true limited=limited items=(array (object tab="description" label="SWFFG.TabDescription" icon="far fa-file-alt" cls=classType) (object tab="attributes" label="SWFFG.TabModifiers" icon="fas fa-cog" cls=classType) )}} {{!-- Sheet Body --}}
{{!-- Sheet Tab Navigation --}} {{> "systems/starwarsffg/templates/parts/shared/ffg-tabs.html" displayLimited=true limited=limited items=(array (object tab="description" label="SWFFG.TabDescription" icon="far fa-file-alt" cls=classType) (object tab="attributes" label="SWFFG.TabModifiers" icon="fas fa-cog" cls=classType) (object tab="talents" label="SWFFG.TabTalents" icon="fas fa-cog" cls=classType))}} {{!-- Sheet Body --}}
<section class="sheet-body small species">
{{!-- Description Tab --}}
<div class="tab" data-group="primary" data-tab="description">
Expand All @@ -54,5 +54,14 @@ <h1 class="charname"><input name="name" type="text" value="{{item.name}}" placeh
<div class="tab attributes" data-group="primary" data-tab="attributes">
{{> "systems/starwarsffg/templates/parts/shared/ffg-modifiers.html"}}
</div>

{{!-- Talents Tab --}}
<div class="tab talents" data-group="primary" data-tab="talents">
{{#each data.talents as |talent id|}}
<div class="talent-pill item-pill2" data-item-type="talent" data-talent-id="{{id}}">{{talent.name}}
<i class="fas fa-times item-delete" data-item-type="talent" data-talent-id="{{id}}"></i>
</div>
{{/each}}
</div>
</section>
</form>