Skip to content

Commit f159335

Browse files
authored
Implement character generator (#4)
* Start implementing character generator. * Finish character generation.
1 parent 62a9e61 commit f159335

File tree

11 files changed

+238
-37
lines changed

11 files changed

+238
-37
lines changed

lang/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"DIS.AbilityBody": "Body",
33
"DIS.Add": "Add",
4+
"DIS.GenerateCharacter": "Generate Character",
45
"DIS.NickName": "Nickname",
56
"DIS.OwnerName": "Owner Signature",
67
"DIS.PlayerName": "Player Name",

module/actor/actor.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ export class DISActor extends Actor {
1414
actorLink: true,
1515
disposition: 1, // friendly
1616
vision: true,
17-
dimSight: 60,
18-
brightSight: 10,
1917
};
2018
} else if (data.type === "npc") {
2119
defaults = {
@@ -28,8 +26,6 @@ export class DISActor extends Actor {
2826
actorLink: true,
2927
disposition: 0, // neutral
3028
vision: true,
31-
dimSight: 60,
32-
brightSight: 10,
3329
};
3430
}
3531
mergeObject(data.token, defaults, { overwrite: false });

module/deathinspace.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { DISCharacterSheet } from "./actor/sheet/character-sheet.js";
77
import { DISHubSheet } from "./actor/sheet/hub-sheet.js";
88
import { DISNpcSheet } from "./actor/sheet/npc-sheet.js";
99
import { DIS } from "./config.js";
10+
import { generateCharacter } from "./generator.js";
1011
import { DISItem } from "./item/item.js";
1112
import { DISItemSheet } from "./item/sheet/item-sheet.js";
1213

@@ -23,6 +24,8 @@ Hooks.once("init", async function() {
2324
CONFIG.Actor.documentClass = DISActor;
2425
CONFIG.Item.documentClass = DISItem;
2526

27+
game.gen = generateCharacter; // DEBUGGING
28+
2629
CONFIG.DIS = DIS;
2730
Actors.unregisterSheet("core", ActorSheet);
2831
Actors.registerSheet("deathinspace", DISCharacterSheet, {
@@ -44,6 +47,29 @@ Hooks.once("init", async function() {
4447
Items.registerSheet("deathinspace", DISItemSheet, { makeDefault: true });
4548
});
4649

50+
Hooks.on("renderActorDirectory", (app, html) => {
51+
if (game.user.can("ACTOR_CREATE")) {
52+
// only show the Create Scvm button to users who can create actors
53+
const section = document.createElement("header");
54+
section.classList.add("generate-character");
55+
section.classList.add("directory-header");
56+
// Add menu before directory header
57+
const dirHeader = html[0].querySelector(".directory-header");
58+
dirHeader.parentNode.insertBefore(section, dirHeader);
59+
section.insertAdjacentHTML(
60+
"afterbegin",
61+
`
62+
<div class="header-actions action-buttons flexrow">
63+
<button class="generate-character-button"><i class="fas fa-skull"></i>${game.i18n.localize('DIS.GenerateCharacter')}</button>
64+
</div>
65+
`
66+
);
67+
section
68+
.querySelector(".generate-character-button")
69+
.addEventListener("click", () => { generateCharacter() });
70+
}
71+
});
72+
4773
// TODO: can we just use Foundry's "eq" helper? verify.
4874
Handlebars.registerHelper('ifEq', function(arg1, arg2, options) {
4975
// TODO: verify whether we want == or === for this equality check

module/generator.js

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import { DISActor } from "./actor/actor.js";
2+
3+
export const generateCharacter = async () => {
4+
const creationPack = "deathinspace.character-creation";
5+
6+
// 1. abilities
7+
const body = generateAbilityValue();
8+
const dexterity = generateAbilityValue();
9+
const savvy = generateAbilityValue();
10+
const tech = generateAbilityValue();
11+
12+
const defenseRating = 12 + dexterity;
13+
14+
// 2. origin
15+
const origin = await drawDocument(creationPack, "Origins");
16+
const originBenefit = await pickOriginBenefit(origin);
17+
18+
// 3. character details
19+
const background = await drawText(creationPack, "Backgrounds");
20+
const trait = await drawText(creationPack, "Traits");
21+
const drive = await drawText(creationPack, "Drives");
22+
const looks = await drawText(creationPack, "Looks");
23+
24+
// 4. past allegiance
25+
const pastAllegiance = await drawText(creationPack, "Past Allegiances");
26+
27+
// 5. hit points and defense rating
28+
const hitPoints = rollTotal("1d8");
29+
30+
// 6. starting gear and starting bonus
31+
const holos = rollTotal("3d10");
32+
// TODO: need to handle multiple results/docs drawn
33+
//const startingKit = await drawResult(creationPack, "Starting Kits");
34+
// TODO: extract entities
35+
36+
const sumOfAbilityScores = body + dexterity + savvy + tech;
37+
let startingBonus = null;
38+
if (sumOfAbilityScores < 0) {
39+
// startingBonus = drawResult("deathinspace.character-creation", "Starting Bonuses");
40+
}
41+
const personalTrinket = await drawDocument(creationPack, "Personal Trinkets");
42+
43+
const firstName = await drawText(creationPack, "First Names");
44+
const lastName = await drawText(creationPack, "Last Names");
45+
46+
const actorData = {
47+
name: `${firstName} ${lastName}`,
48+
data: {
49+
abilities: {
50+
body: { value: body },
51+
dexterity: { value: dexterity },
52+
savvy: { value: savvy },
53+
tech: { value: tech },
54+
},
55+
background,
56+
defenseRating,
57+
drive,
58+
hitPoints: {
59+
max: hitPoints,
60+
value: hitPoints,
61+
},
62+
holos,
63+
looks,
64+
pastAllegiance,
65+
trait,
66+
},
67+
type: "character",
68+
};
69+
const actor = await DISActor.create(actorData);
70+
71+
// TODO: apply starting bonus to character
72+
73+
// TODO: originBenefit
74+
await actor.createEmbeddedDocuments("Item", [origin.data, originBenefit.data, personalTrinket.data])
75+
actor.sheet.render(true);
76+
77+
return actor;
78+
}
79+
80+
const rollTotal = (formula) => {
81+
const roll = new Roll(formula).evaluate({
82+
async: false,
83+
});
84+
return roll.result;
85+
};
86+
87+
const generateAbilityValue = () => {
88+
return rollTotal("1d4") - rollTotal("1d4");
89+
};
90+
91+
const pickOriginBenefit = async (origin) => {
92+
console.log(origin);
93+
if (origin.data.data.benefitNames) {
94+
const names = origin.data.data.benefitNames.split(",");
95+
if (names.length) {
96+
const randName = names[Math.floor(Math.random() * names.length)];
97+
const pack = game.packs.get("deathinspace.origin-benefits");
98+
const docs = await pack.getDocuments();
99+
const benefit = docs.find(
100+
(i) => i.name === randName
101+
);
102+
return benefit;
103+
}
104+
}
105+
};
106+
107+
const drawFromTable = async (packName, tableName) => {
108+
const creationPack = game.packs.get(packName);
109+
const creationDocs = await creationPack.getDocuments();
110+
const table = creationDocs.find(
111+
(i) => i.name === tableName
112+
);
113+
const tableDraw = await table.draw({ displayChat: false });
114+
// TODO: decide if/how we want to handle multiple results
115+
return tableDraw;
116+
}
117+
118+
const drawText = async (packName, tableName) => {
119+
const draw = await drawFromTable(packName, tableName);
120+
return draw.results[0].data.text;
121+
};
122+
123+
const drawDocument = async (packName, tableName) => {
124+
const draw = await drawFromTable(packName, tableName);
125+
const doc = await documentFromDraw(draw);
126+
return doc;
127+
};
128+
129+
const drawDocuments = async (packName, tableName) => {
130+
const draw = await drawFromTable(packName, tableName);
131+
const docs = await documentsFromDraw(draw);
132+
return docs;
133+
};
134+
135+
const documentsFromDraw = async (draw) => {
136+
return Promise.all(draw.results.map(r => documentFromResult(r)));
137+
};
138+
139+
const documentFromDraw = async (draw) => {
140+
const doc = await documentFromResult(draw.results[0]);
141+
return doc;
142+
};
143+
144+
const documentFromResult = async (result) => {
145+
const collectionName = result.data.type === 2
146+
? "Compendium." + result.data.collection
147+
: result.data.collection;
148+
const uuid = `${collectionName}.${result.data.resultId}`;
149+
const doc = await fromUuid(uuid);
150+
return doc;
151+
};
152+
153+
const generateSpacecraft = async () => {
154+
155+
};
156+
157+
const generateStation = async () => {
158+
159+
};

module/item/item.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@
22
* @extends {Item}
33
*/
44
export class DISItem extends Item {
5-
65
}

module/item/sheet/item-sheet.js

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,27 @@ import { DIS } from "../../config.js";
44
* @extends {ItemSheet}
55
*/
66
export class DISItemSheet extends ItemSheet {
7+
/** @override */
8+
static get defaultOptions() {
9+
return mergeObject(super.defaultOptions, {
10+
classes: ["deathinspace", "sheet", "item"],
11+
width: 730,
12+
height: 680,
13+
tabs: [{navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "notes"}],
14+
dragDrop: [{dragSelector: ".item-list .item", dropSelector: null}]
15+
});
16+
}
717

8-
/** @override */
9-
static get defaultOptions() {
10-
return mergeObject(super.defaultOptions, {
11-
classes: ["deathinspace", "sheet", "item"],
12-
width: 730,
13-
height: 680,
14-
tabs: [{navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "data"}],
15-
dragDrop: [{dragSelector: ".item-list .item", dropSelector: null}]
16-
});
17-
}
18-
19-
/** @override */
20-
get template() {
21-
const path = "systems/deathinspace/templates/item";
22-
// specific item-type sheet
23-
return `${path}/${this.item.data.type}-sheet.html`;
24-
}
18+
/** @override */
19+
get template() {
20+
const path = "systems/deathinspace/templates/item";
21+
// specific item-type sheet
22+
return `${path}/${this.item.data.type}-sheet.html`;
23+
}
2524

26-
/** @override */
27-
activateListeners(html) {
28-
super.activateListeners(html);
29-
}
25+
/** @override */
26+
activateListeners(html) {
27+
super.activateListeners(html);
3028
}
29+
}
3130

0 commit comments

Comments
 (0)