Skip to content

Commit

Permalink
add RollTable as result
Browse files Browse the repository at this point in the history
  • Loading branch information
AngryBeaver committed Oct 14, 2022
1 parent feba4ac commit a05e6de
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 103 deletions.
37 changes: 27 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
![Download Count](https://img.shields.io/github/downloads/AngryBeaver/beavers-crafting/total?color=bright-green)

## Work in progress
! Carefull structure will probably change until i finalize this module,

! Carefully structure will probably change until i finalize this module with version 1.0.0 !,
so do not start creating tones of recipes already ! The future upgrades might break them.

! breaking change release: 0.1.x -> 0.2.x !

## Features
### Loot subtype Recipe
![img.png](pictures/newItem.png)
Expand All @@ -17,11 +20,21 @@ Default is "Create New Item" obviously you need to adapt if you have a different
### Configure subtype Recipe
![img.png](pictures/configure.png)

- cost: you may add costs to the crafting process
- ingredients: you add items that will get consumed while crafting
- skill: you may add a skill that is required in the crafting process
- your may check that all costs are payed in the crafting process no matter of success or fail.
- results: you may add items as a result of a successfull crafting process of this recipe.
#### cost:
you may add costs to the crafting process
#### Ingredients:


You may add Items via drag and drop as Ingredients.
#### skill:
you may add a skill that is required in the crafting process.
you can enable that costs and ingredients are also payed when the check failed.
#### results:
![img.png](pictures/rollTable.png)

You may add Items or RollTable via drag and drop as result.
The result is the outcome of a successfull crafting process.
If you add a RollTable you will get quantity amount of rolls on that table not one roll quantity of times.

### Crafting
![img.png](pictures/crafting.png)
Expand All @@ -41,11 +54,16 @@ or throu recipe compendium

You will see a chat message with your result

## latest features:
### 0.2.x feature add rollTable result
you now can produce a random Potion.
breaking change 0.1.x -> 0.2.x

## Upcoming Changes
### "any" of ingredient
I want to create recipes with "any" weapon or mushroom therefor i may need some new fields in recipe or a new subtype ingredient
### results should include rollTables
I want to create a random potion with random ingredients.
I want to create recipes with "any" xxx e.g. (weapon,mushroom,etc) therefor i may need some new fields in recipe or a new subtype ingredient
### results should include rollTables (done 0.2.x)
I want to create a random potion.
### macro
I want to be able to add macros to recipes.
giving them more flexibility e.g. get damage on certain recipes where you failed your check.
Expand All @@ -69,6 +87,5 @@ Actor Items will get merged to stacks in the crafting process.
(only those that match ingredients or results)



## Credits
Copy organizational structur from midi-qol (gulpfile,package.json,tsconcig.json)
7 changes: 6 additions & 1 deletion lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@
},
"crafting-app": {
"title": "Beaver's Recipe Compendium",
"button": "Craft"
"button": "Craft",
"errors": {
"tableNotFound": "RollTable not found for : ",
"tableNotValid": "RollTable return invalid Object : ",
"tableItemNotFound": "Item not found in RollTable for :"
}
},
"settings": {
"craftById": {
Expand Down
2 changes: 1 addition & 1 deletion module.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"title": "Beaver's Crafting System",
"description": "A Crafting Module for DnD",
"id": "beavers-crafting",
"version": "0.1.0",
"version": "0.2.0",
"authors": [
{
"name": "angryBeaver",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "beavers-crafting",
"title": "Beaver's Crafting",
"version": "0.1.0",
"version": "0.2.0",
"description": "Crafting",
"devDir": "C:\\Users\\User\\AppData\\Local\\FoundryVTT\\Data\\modules",
"main": "src/main.js",
Expand Down
Binary file added pictures/rollTable.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
129 changes: 73 additions & 56 deletions src/Crafting.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {Recipe} from "./Recipe.js";
import {DefaultComponent, Recipe} from "./Recipe.js";
import {Exchange} from "./Exchange.js";
import {Settings} from "./Settings.js";
import {DefaultResult, RecipeCompendium} from "./RecipeCompendium.js";
import {rollTableToComponents} from "./helpers/RollTableToComponent.js";

export class Crafting {
recipe: Recipe;
Expand All @@ -14,57 +15,46 @@ export class Crafting {
this.actor = actor;
this.item = item;
}
static fromOwned(item):Crafting{

static fromOwned(item): Crafting {
return new Crafting(item.parent, item);
}

static async from(actorId, itemId):Promise<Crafting> {
static async from(actorId, itemId): Promise<Crafting> {
const actor = await fromUuid("Actor." + actorId)
const item = await fromUuid("Item." + itemId);
return new Crafting(actor, item);
}

async craft():Promise<Result> {
async craft(): Promise<Result> {
const result = await this.checkSkill();
RecipeCompendium.validateRecipeToItemList(this.recipe,this.actor.items,result);
RecipeCompendium.validateRecipeToItemList(this.recipe, this.actor.items, result);
this.checkCurrency(result);
this.addResults(result);
await this.addResults(result);
await this.updateActor(result);
await this._sendToChat(result);
console.log(result);
return result;
}

//if you have to comment it, its not clean code !
addResults(result?:Result):Result {
async checkSkill(result?: Result): Promise<Result> {
if (!result) result = new DefaultResult();
if (result.hasErrors) return result;
for (const [k, component] of Object.entries(this.recipe.results)) {
const itemChange = RecipeCompendium.findComponentInList(this.actor.items,component);
if (itemChange.toUpdate["system.quantity"] == 0) { // actor does not have item
result.changes.items.toCreate.push(component); //add that item
} else { // actor does have item
const updates = result.changes.items.toUpdate
.filter(x => x._id === itemChange.toUpdate._id);
if (updates.length > 0) { //crafting already updated that item
updates.forEach(x => x["system.quantity"] = x["system.quantity"] + component.quantity) //reupdate it
} else { //crafting does not update that item
if (result.changes.items.toDelete.includes(itemChange.toUpdate._id)) { //crafting deleted that item
result.changes.items.toCreate.push(component); //add that item // now i delete it then create it again.
} else { //on actor but not yet touched
itemChange.toUpdate["system.quantity"] = itemChange.toUpdate["system.quantity"] + component.quantity
result.changes.items.toUpdate.push(itemChange.toUpdate);
result.changes.items.toDelete.push(...itemChange.toDelete);

}
}
if (this.recipe.skill) {
this.roll = await this.actor.rollSkill(this.recipe.skill.name, {"chatMessage": false});
result.skill = {
name: this.recipe.skill.name,
difference: this.roll.total - this.recipe.skill.dc,
total: this.roll.total
}
if (this.roll.total < this.recipe.skill.dc) {
result.hasErrors = true;
}
}
return result;
}

//simple stupid functional but not performant (yagni)
checkCurrency(result?:Result):Result {
checkCurrency(result?: Result): Result {
if (!result) result = new DefaultResult();
result.changes.currencies = this.actor.system.currency;
if (this.recipe.currency) {
Expand All @@ -78,40 +68,22 @@ export class Crafting {
return result;
}

async checkSkill(result?:Result):Promise<Result> {
async addResults(result?: Result): Promise<Result> {
if (!result) result = new DefaultResult();
if (result.hasErrors) return result;
if (this.recipe.skill) {
this.roll = await this.actor.rollSkill(this.recipe.skill.name, {"chatMessage": false});
result.skill = {
name:this.recipe.skill.name,
difference:this.roll.total-this.recipe.skill.dc,
total:this.roll.total
}
if (this.roll.total < this.recipe.skill.dc) {
result.hasErrors = true;
}
const components = await this._getResultComponents(result);
for (const component of components) {
this._addComponentToResult(result, component);
}
return result;
}

async _sendToChat(result:Result) {
let content = await renderTemplate(`modules/${Settings.NAMESPACE}/templates/crafting-chat.hbs`,
{recipe: this.recipe, result: result, roll: this.roll})
content = await TextEditor.enrichHTML(content);
ChatMessage.create({
content: content,
speaker: {actor: this.actor.id},
})
}


async updateActor(result:Result) {
async updateActor(result: Result) {
if (!result) result = new DefaultResult();
if (result.hasErrors && (!this.recipe.skill?.consume || !result.skill)) return;
if (result.hasException || (result.hasErrors && (!this.recipe.skill?.consume || !result.skill))) return;
await this.actor.updateEmbeddedDocuments("Item", result.changes.items.toUpdate);
await this.actor.deleteEmbeddedDocuments("Item", result.changes.items.toDelete);
const createItems:any[] = [];
const createItems: any[] = [];
for (const component of result.changes.items.toCreate) {
if (component.uuid) {
const item = await fromUuid(component.uuid);
Expand All @@ -132,8 +104,53 @@ export class Crafting {
return result;
}

processId() {
return foundry.utils.randomID();
async _sendToChat(result: Result) {
let content = await renderTemplate(`modules/${Settings.NAMESPACE}/templates/crafting-chat.hbs`,
{recipe: this.recipe, result: result, roll: this.roll})
content = TextEditor.enrichHTML(content);
await ChatMessage.create({
content: content,
speaker: {actor: this.actor.id},
})
}

//if you have to comment it, its not clean code !
_addComponentToResult(result: Result, component: Component) {
const itemChange = RecipeCompendium.findComponentInList(this.actor.items, component);
if(result.results[component.uuid]){
DefaultComponent.inc(result.results[component.uuid])
}else{
result.results[component.uuid] = component;
}
if (itemChange.toUpdate["system.quantity"] == 0) { // actor does not have item
result.changes.items.toCreate.push(component); //add that item
} else { // actor does have item
const updates = result.changes.items.toUpdate
.filter(x => x._id === itemChange.toUpdate._id);
if (updates.length > 0) { //crafting already updated that item
updates.forEach(x => x["system.quantity"] = x["system.quantity"] + component.quantity) //reupdate it
} else { //crafting does not update that item
if (result.changes.items.toDelete.includes(itemChange.toUpdate._id)) { //crafting deleted that item
result.changes.items.toCreate.push(component); //add that item // now i delete it then create it again.
} else { //on actor but not yet touched
itemChange.toUpdate["system.quantity"] = itemChange.toUpdate["system.quantity"] + component.quantity
result.changes.items.toUpdate.push(itemChange.toUpdate);
result.changes.items.toDelete.push(...itemChange.toDelete);

}
}
}
}

async _getResultComponents(result: Result): Promise<Component[]> {
const items = Object.values(this.recipe.results).filter(component => component.type === "Item");
const tables = Object.values(this.recipe.results).filter(component => component.type === "RollTable");
for (const component of tables) {
items.push(...await rollTableToComponents(component, result));
if (result.hasErrors) return [];
}
return items;
}


}
34 changes: 18 additions & 16 deletions src/Recipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,27 +56,27 @@ export class Recipe implements RecipeStoreData{
return {...this.results,...this._trash.results}
}

addIngredient(entity,uuid) {
if(!this.ingredients[entity.id]){
this.ingredients[entity.id] = new DefaultComponent(entity,uuid);
addIngredient(entity,uuid,type) {
if(!this.ingredients[uuid]){
this.ingredients[uuid] = new DefaultComponent(entity,uuid,type);
}else{
DefaultComponent.inc(this.ingredients[entity.id])
DefaultComponent.inc(this.ingredients[uuid])
}
}
removeIngredient(id){
delete this.ingredients[id];
this._trash.ingredients["-="+id] = null;
removeIngredient(uuid){
delete this.ingredients[uuid];
this._trash.ingredients["-="+uuid] = null;
}
addResult(entity,uuid) {
if(!this.results[entity.id]){
this.results[entity.id] = new DefaultComponent(entity,uuid);
addResult(entity,uuid,type) {
if(!this.results[uuid]){
this.results[uuid] = new DefaultComponent(entity,uuid,type);
}else{
DefaultComponent.inc(this.results[entity.id])
DefaultComponent.inc(this.results[uuid])
}
}
removeResults(id) {
delete this.results[id];
this._trash.results["-=" + id] = null;
removeResults(uuid) {
delete this.results[uuid];
this._trash.results["-=" + uuid] = null;
}
addSkill() {
this.skill = new DefaultSkill();
Expand All @@ -103,13 +103,15 @@ export class DefaultComponent implements Component {
quantity: number;
sourceId: string;
uuid: string;
type: string;

constructor(entity,uuid) {
constructor(entity, uuid, type) {
this.id = entity.id;
this.uuid = uuid;
this.type = type;
this.name = entity.name;
this.img = entity.img;
this.quantity = entity.system.quantity;
this.quantity = entity.system?.quantity || 1;
this.sourceId = entity.flags.core?.sourceId;
}

Expand Down
2 changes: 2 additions & 0 deletions src/RecipeCompendium.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ export class DefaultResult implements Result {
},
currencies: {},
};
results = {};
hasErrors = false;
hasException:false;
isAvailable = true;
}
Loading

0 comments on commit a05e6de

Please sign in to comment.