Skip to content

Commit

Permalink
fix: add untrained skill
Browse files Browse the repository at this point in the history
untrained skills are currently handled in a
special way. This change makes it so that they
are handeled like other skills by creating an
untrained skill on every actor.
  • Loading branch information
xdy committed Jan 14, 2021
1 parent ec8cebc commit d7135bb
Show file tree
Hide file tree
Showing 20 changed files with 286 additions and 85 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ dist/
#This project
foundryconfig.json
foundry.js
foundry
sample_data
59 changes: 59 additions & 0 deletions src/module/entities/TwodsixActor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@ import {calcModFor, getKeyByValue} from "../utils/sheetUtils";
import {TWODSIX} from "../config";
import {TwodsixRollSettings} from "../utils/TwodsixRollSettings";
import {TwodsixDiceRoll} from "../utils/TwodsixDiceRoll";
import TwodsixItem from "./TwodsixItem";

export default class TwodsixActor extends Actor {
public static async create(data:Record<string, unknown>, options?:Record<string, unknown>):Promise<Entity> {
const actor = await super.create(data, options) as TwodsixActor;
if (actor.data.type == "traveller") {
await actor.createUntrainedSkill();
}
return actor;
}

/**
* Augment the basic actor data with additional dynamic data.
*/
Expand Down Expand Up @@ -120,4 +129,54 @@ export default class TwodsixActor extends Actor {
console.log("DEBUG CHARACTERISTICS ROLL:", diceRoll);
return diceRoll;
}

public getUntrainedSkill():TwodsixItem {
return this.getOwnedItem(this.data.data.untrainedSkill) as TwodsixItem;
}

private async createUntrainedSkill() {
const untrainedSkill = await this.buildUntrainedSkill();
await this.update({"data.untrainedSkill": untrainedSkill._id});
}

public async buildUntrainedSkill():Promise<TwodsixItem> {
if (this.data.data.untrainedSkill) {
return;
}
const data = {
"name": game.i18n.localize("TWODSIX.Actor.Skills.Untrained"),
"type": "skills",
"flags": {'twodsix.untrainedSkill': true}
};
return await this.createOwnedItem(data) as TwodsixItem;
}

private static _applyToAllActorItems(func: (actor:TwodsixActor, item:TwodsixItem) => void):void {
TwodsixActor.collection.forEach(actor => {
actor.data.items.forEach((itemData:Record<string,any>) => {
const item = actor.getOwnedItem(itemData._id);
func(actor, item);
});
});
}

public static resetUntrainedSkill():void {
TwodsixActor._applyToAllActorItems((actor:TwodsixActor, item:TwodsixItem) => {
if (item.type === "skills") {
return;
}
const skill = actor.getOwnedItem(item.data.data.skill);
if (skill && skill.getFlag("twodsix", "untrainedSkill")) {
item.update({ "data.skill": "" }, {});
}
});
}

public static setUntrainedSkillForWeapons():void {
TwodsixActor._applyToAllActorItems((actor:TwodsixActor, item:TwodsixItem) => {
if (item.type === "weapon" && !item.data.data.skill) {
item.update({"data.skill": actor.getUntrainedSkill().id}, {});
}
});
}
}
12 changes: 12 additions & 0 deletions src/module/entities/TwodsixItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,19 @@ export default class TwodsixItem extends Item {
*/
prepareData():void {
super.prepareData();
if (this.getFlag("twodsix", "untrainedSkill")) {
this.data.name = game.i18n.localize("TWODSIX.Actor.Skills.Untrained");
}
}

public async performAttack(attackType:string, showThrowDialog:boolean, rateOfFireCE:number = null, showInChat = true):Promise<void> {
if (this.type !== "weapon") {
return;
}
if (!this.data.data.skill) {
ui.notifications.error(game.i18n.localize("TWODSIX.Errors.NoSkillForSkillRoll"));
return;
}

let numberOfAttacks = 1;
let bonusDamage = "0";
Expand Down Expand Up @@ -78,6 +85,11 @@ export default class TwodsixItem extends Item {
item = this;
}

if (!skill) {
ui.notifications.error(game.i18n.localize("TWODSIX.Errors.NoSkillForSkillRoll"));
return;
}

//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) {
tmpSettings = await TwodsixRollSettings.create(showThrowDialog, tmpSettings, skill, item);
Expand Down
7 changes: 6 additions & 1 deletion src/module/handlebars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,13 @@ export default function registerHandlebarsHelpers():void {
return TwodsixItem.burstBonusDamage(number);
});

Handlebars.registerHelper('twodsix_filterSkills', (skill) => {
return skill!=null && !skill.flags?.twodsix?.untrainedSkill && skill.type === "skills";
});

// Handy for debugging
Handlebars.registerHelper('json', function(context) {
Handlebars.registerHelper('debug', function(context) {
console.log(context);
return JSON.stringify(context);
});

Expand Down
24 changes: 24 additions & 0 deletions src/module/hooks/renderSettingsConfig.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
import {TWODSIX} from "../config";

function createWarningDialog(event:Event, message:string) {
event.preventDefault();
event.stopPropagation();
Dialog.confirm({
title: game.i18n.localize("TWODSIX.Settings.hideUntrainedSkills.warning"),
content: message,
yes: async () => event.currentTarget["checked"] = !event.currentTarget["checked"],
no: null,
defaultYes: true
});
}

Hooks.on('renderSettingsConfig', async (app, html) => {
html.find('[name="twodsix.ruleset"]').on('change', ev => {
const ruleset = ev.target.value;
Expand All @@ -16,4 +28,16 @@ Hooks.on('renderSettingsConfig', async (app, html) => {
}
});
});

html.find('[name="twodsix.hideUntrainedSkills"]').on('click', async (event:Event) => {
const continueText = game.i18n.localize("TWODSIX.Settings.hideUntrainedSkills.continue");

if (game.settings.get('twodsix', 'hideUntrainedSkills') && !event.currentTarget["checked"]) {
const warningResetText = game.i18n.localize("TWODSIX.Settings.hideUntrainedSkills.warningReset");
createWarningDialog(event, `${warningResetText}<br><br>${continueText}<br><br>`);
} else if (!game.settings.get('twodsix', 'hideUntrainedSkills') && event.currentTarget["checked"]) {
const warningUpdateWeaponText = game.i18n.localize("TWODSIX.Settings.hideUntrainedSkills.warningUpdateWeapon");
createWarningDialog(event, `${warningUpdateWeaponText}<br><br>${continueText}<br><br>`);
}
});
});
42 changes: 31 additions & 11 deletions src/module/migration.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {TwodsixItemData, UpdateData} from "../types/twodsix";
import TwodsixActor from "./entities/TwodsixActor";
import TwodsixItem from "./entities/TwodsixItem";

/**
* New style migrations should look at the object instead of the version to figure out what needs to be done.
Expand All @@ -7,29 +9,47 @@ import {TwodsixItemData, UpdateData} from "../types/twodsix";
*/
export class Migration {

private static async migrateActorData(actor:Actor):Promise<UpdateData> {
private static async migrateActorData(actor:TwodsixActor):Promise<UpdateData> {
const updateData:UpdateData = <UpdateData>{};
const actorData = actor.data;
await this.migrateActorItems(actorData, actor);

let untrainedSkill = actor.getUntrainedSkill();
if (!untrainedSkill) {
untrainedSkill = await actor.buildUntrainedSkill();
updateData['data.untrainedSkill'] = untrainedSkill.id;
}

//TODO Get rid of the untrainedSkill passing
await this.migrateActorItems(actor, untrainedSkill);

return updateData;
}

private static async migrateActorItems(actorData:ActorData, actor:Actor) {
private static async migrateActorItems(actor:TwodsixActor, untrainedSkill:TwodsixItem=null) {
//Handle any items that are on the actor
const actorItems = actorData["items"];
const actorItems = actor.data["items"];
const toUpdate = [];
for (const i of actorItems) {
toUpdate.push(mergeObject(i, this.migrateItemData(i)));
toUpdate.push(mergeObject(i, this.migrateItemData(i, actor, untrainedSkill)));
}
await actor.updateEmbeddedEntity("OwnedItem", toUpdate);
}

private static migrateItemData(item:TwodsixItemData):UpdateData {
private static migrateItemData(item:TwodsixItemData, actor:TwodsixActor = null, untrainedSkill:TwodsixItem=null):UpdateData {
const updateData:UpdateData = <UpdateData>{};

if (item.type === 'skills') {
if (item.type === 'skills') { //0.6.82
updateData['data.rolltype'] = item.data.rolltype || 'Normal';
if (typeof item.data.value === "string") { // 0.7.8
updateData['data.value'] = parseInt(item.data.value, 10);
}
}

if (actor) {
if (item.type !== 'skills') {
if (!item.data.skill) { //0.6.84
updateData['data.skill'] = untrainedSkill.id;
}
}
}

return updateData;
Expand All @@ -45,7 +65,7 @@ export class Migration {
t.actorId = null;
t.actorData = {};
} else if (!t.actorLink) {
const updateData = Migration.migrateActorData(token.actor);
const updateData = Migration.migrateActorData(<TwodsixActor>token.actor);
t.actorData = mergeObject(token.data.actorData, updateData);
}
return t;
Expand Down Expand Up @@ -73,7 +93,7 @@ export class Migration {
let updateData = null;
switch (entity) {
case 'Item':
updateData = Migration.migrateItemData(actor.data);
updateData = Migration.migrateItemData(actor.data, actor);
break;
case 'Actor':
updateData = Migration.migrateActorData(actor);
Expand Down Expand Up @@ -133,7 +153,7 @@ export class Migration {

const actorMigrations = game.actors.entities.map(async actor => {
try {
const updateData = await Migration.migrateActorData(actor);
const updateData = await Migration.migrateActorData(<TwodsixActor>actor);
if (!isObjectEmpty(updateData)) {
console.log(`Migrating Actor ${actor.name}`);
await actor.update(updateData, {enforceTypes: false});
Expand Down
12 changes: 10 additions & 2 deletions src/module/settings.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {TWODSIX} from "./config";
import TwodsixActor from "./entities/TwodsixActor";

export const registerSettings = function ():void {

Expand Down Expand Up @@ -36,8 +37,7 @@ export const registerSettings = function ():void {

_booleanSetting('ExperimentalFeatures', false);

_booleanSetting('hideUntrainedSkills', false);
_numberSetting('untrainedSkillValue', -3, 'client');
_booleanSetting('hideUntrainedSkills', false, "world", _onHideUntrainedSkillsChange);

_stringChoiceSetting('autofireRulesUsed', TWODSIX.RULESETS.CE.key, TWODSIX.VARIANTS);

Expand Down Expand Up @@ -103,4 +103,12 @@ export const registerSettings = function ():void {
onChange: onChange
});
}

function _onHideUntrainedSkillsChange (setting:boolean) {
if (!setting) {
TwodsixActor.resetUntrainedSkill();
} else {
TwodsixActor.setUntrainedSkillForWeapons();
}
}
};
13 changes: 9 additions & 4 deletions src/module/sheets/AbstractTwodsixActorSheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export abstract class AbstractTwodsixActorSheet extends ActorSheet {
// Get the type of item to create.
const {type} = header.dataset;
// Grab any data associated with this control.
const data = duplicate(header.dataset);
const data = duplicate(header.dataset) as Record<string, any>;
// Initialize a default name, handle bad naming of 'skills' item type, which should be singular.
const itemType = (type === "skills" ? "skill" : type);
data.name = game.i18n.localize("TWODSIX.Items.Items.New") + " " + game.i18n.localize("TWODSIX.itemTypes." + itemType);
Expand All @@ -104,11 +104,16 @@ export abstract class AbstractTwodsixActorSheet extends ActorSheet {

if (itemData.type === 'skills') {
if (!game.settings.get('twodsix', 'hideUntrainedSkills')) {
itemData.data.value = game.settings.get('twodsix', 'untrainedSkillValue');
itemData.data.value = game.system.template.Item.skills.value;
} else {
itemData.data.value = String(0);
itemData.data.value = 0;
}
}

if (game.settings.get('twodsix', 'hideUntrainedSkills') && type === "weapon") {
itemData.data.skill = this.actor.getUntrainedSkill().id;
}

// Finally, create the item!
return this.actor.createOwnedItem(itemData);
}
Expand Down Expand Up @@ -182,7 +187,7 @@ export abstract class AbstractTwodsixActorSheet extends ActorSheet {
}

if (!game.settings.get('twodsix', 'hideUntrainedSkills')) {
itemData.data.value = game.settings.get('twodsix', 'untrainedSkillValue');
itemData.data.value = game.system.template.Item.skills.value;
} else {
itemData.data.value = 0;
}
Expand Down
49 changes: 48 additions & 1 deletion src/module/sheets/TwodsixActorSheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ export class TwodsixActorSheet extends AbstractTwodsixActorSheet {
// Prepare items.
if (this.actor.data.type == 'traveller') {
TwodsixActorSheet._prepareItemContainers(data);
const untrainedSkill = this.actor.getUntrainedSkill();
if (untrainedSkill) {
data.untrainedSkill = this.actor.getUntrainedSkill();
data.jackOfAllTrades = TwodsixActorSheet.untrainedToJoat(data.untrainedSkill.data.data.value);
}
}

// Add relevant data from system settings
data.data.settings = {
ShowRangeBandAndHideRange: game.settings.get('twodsix', 'ShowRangeBandAndHideRange'),
ExperimentalFeatures: game.settings.get('twodsix', 'ExperimentalFeatures'),
untrainedSkillValue: game.settings.get('twodsix', 'untrainedSkillValue'),
autofireRulesUsed: game.settings.get('twodsix', 'autofireRulesUsed')
};
data.config = TWODSIX;
Expand Down Expand Up @@ -59,6 +63,11 @@ export class TwodsixActorSheet extends AbstractTwodsixActorSheet {

html.find('.roll-damage').on('click', (this._onRollDamage.bind(this)));

html.find('#joat-skill-input').on('input', (this._updateJoatSkill.bind(this)));
html.find('#joat-skill-input').on('blur', (this._onJoatSkillBlur.bind(this)));
html.find('#joat-skill-input').on('click', (event) => {
$(event.currentTarget).trigger("select");
});
}


Expand All @@ -79,6 +88,36 @@ export class TwodsixActorSheet extends AbstractTwodsixActorSheet {
};
}


/**
* Handle when the joat skill is changed.
* @param {Event} event The originating click event
* @private
*/
private async _updateJoatSkill(event:Event):Promise<void> {
const joatValue = parseInt(event.currentTarget["value"], 10);
const skillValue = TwodsixActorSheet.joatToUndrained(joatValue);

if (!isNaN(joatValue) && joatValue >= 0 && skillValue <= 0) {
const untrainedSkill = this.actor.getUntrainedSkill();
untrainedSkill.update({"data.value": skillValue});
} else if (event.currentTarget["value"] !== "") {
event.currentTarget["value"] = "";
}
}

/**
* Handle when user tabs out and leaves blank value.
* @param {Event} event The originating click event
* @private
*/
private async _onJoatSkillBlur(event:Event):Promise<void> {
if (isNaN(parseInt(event.currentTarget["value"], 10))) {
const skillValue = this.actor.getUntrainedSkill().data.data.value;
event.currentTarget["value"] = TwodsixActorSheet.untrainedToJoat(skillValue);
}
}

/**
* Handle clickable weapon attacks.
* @param {Event} event The originating click event
Expand Down Expand Up @@ -134,4 +173,12 @@ export class TwodsixActorSheet extends AbstractTwodsixActorSheet {

rollDamage.call(this);
}

private static untrainedToJoat(skillValue:number):number {
return skillValue - game.system.template.Item.skills.value;
}

private static joatToUndrained(joatValue:number):number {
return joatValue + game.system.template.Item.skills.value;
}
}
Loading

0 comments on commit d7135bb

Please sign in to comment.