Skip to content

Commit

Permalink
porting smooth-combat to TS
Browse files Browse the repository at this point in the history
  • Loading branch information
danielrab committed Oct 30, 2020
1 parent cf1805f commit 41d33d2
Show file tree
Hide file tree
Showing 37 changed files with 4,328 additions and 112 deletions.
13 changes: 13 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "build",
"group": {"kind": "build", "isDefault": true},
"problemMatcher": [],
"label": "npm: build",
"detail": "gulp build"
}
]
}
60 changes: 22 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,22 @@
# Changelog
*Please for the love of all that you hold dear, do everyone a favor and include a changelog here rather than making people guess at the capabilities of your module since last release*

# Description
This is a typescript template to get you started. This is not intended for beginners.

Please use the javascript template as necessary for your stuff.


## Manifest Plus
Adds the following fields to the manifest for package browsers to pick up and show information better:

```
- includes: [] # list of files to include in the zip
- icon: "" # link to icon img
- cover: "" #link to cover img
- screenshots: [] #links to screenshot images
- video: ""
- authors: [
{
"name": "name",
"email": "email",
"discord": "discord"
}
]
```


## Versioned Releases

The Github Actions script will automatically create a Latest release which will always have a module.json that points to the latest release, and a versioned release whenever you update the version in your module.json.

This allows people who depend on a specific version of your module to just install that and be version locked. The versioned releases will *not* auto update.


# License
MIT License. Do what you will. PRs welcome.
manifest url: https://raw.githubusercontent.com/danielrab/smooth-combat/master/module.json <br><br>
This module was inspired primarily by MidiQOL and tries to provide automation of combat for dnd5e. <br>
Currently it is on a very early stage of development, so it only dealt with weapon attacks (not spells and feats). <br>
My main goal when developing this module is first and foremost smooth user experience. It means that if a click or a keypress is avoidable, I will try to avoid it. It also means that on every step, the information must be presented in the cleanest form, and the user should have full control over every step of the process. <br>
For now, what the module does is: <br>
1) When an item is dragged to the macro panel, the script that is created is replaced with one that allows to roll the attack even if no token is selected.
2) When a weapon is used, the following will happen: <br>
2.1) You will be switched to target selection. <br>
2.2) All targets will be cleared. <br>
2.3) You will be asked to select a target and given an option to abort the execution or to select another amount of targets. <br>
2.4) Selection of multiple targets will be inverted (no need to hold shift to select multiple). <br>
2.5) Once you selected the target(s), an attack will be rolled for each target and damage will be rolled for each attack that hits showing each damage formula and their damage types separately (in case the enemy has resistance to some but not all of the damage dealt) <br>
2.6) After all of this you will be returned to the tool and layer you were at, and the target selection will return to normal. <br>
2.7) Damage will be applied using resistances, immunities, and vulnerabilities of the hit creatures (the weapons are assumed to be nonmagical until i find a way to differentiate between magical and nonmagical weapons). <br><br>
when using an item, alt will use advantage, ctrl will use disadvantage, shift will use versitile. <br><br>
planned in the near future:<br>
1) sttings.
2) a damage card for the DM to know how much damage was actually dealt after the resistances, as well as have an option to reverse it.
3) ability to define a secondary use with multiple formulas. (currently versitle replaces only the first one).
4) ability to define a weapon as magical for purposes of bypassing resistances.
5) automatic disadvantage for ranged weapons depending on range.
6) ability to define a token as in partial cover.
60 changes: 22 additions & 38 deletions dist/README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,22 @@
# Changelog
*Please for the love of all that you hold dear, do everyone a favor and include a changelog here rather than making people guess at the capabilities of your module since last release*

# Description
This is a typescript template to get you started. This is not intended for beginners.

Please use the javascript template as necessary for your stuff.


## Manifest Plus
Adds the following fields to the manifest for package browsers to pick up and show information better:

```
- includes: [] # list of files to include in the zip
- icon: "" # link to icon img
- cover: "" #link to cover img
- screenshots: [] #links to screenshot images
- video: ""
- authors: [
{
"name": "name",
"email": "email",
"discord": "discord"
}
]
```


## Versioned Releases

The Github Actions script will automatically create a Latest release which will always have a module.json that points to the latest release, and a versioned release whenever you update the version in your module.json.

This allows people who depend on a specific version of your module to just install that and be version locked. The versioned releases will *not* auto update.


# License
MIT License. Do what you will. PRs welcome.
manifest url: https://raw.githubusercontent.com/danielrab/smooth-combat/master/module.json <br><br>
This module was inspired primarily by MidiQOL and tries to provide automation of combat for dnd5e. <br>
Currently it is on a very early stage of development, so it only dealt with weapon attacks (not spells and feats). <br>
My main goal when developing this module is first and foremost smooth user experience. It means that if a click or a keypress is avoidable, I will try to avoid it. It also means that on every step, the information must be presented in the cleanest form, and the user should have full control over every step of the process. <br>
For now, what the module does is: <br>
1) When an item is dragged to the macro panel, the script that is created is replaced with one that allows to roll the attack even if no token is selected.
2) When a weapon is used, the following will happen: <br>
2.1) You will be switched to target selection. <br>
2.2) All targets will be cleared. <br>
2.3) You will be asked to select a target and given an option to abort the execution or to select another amount of targets. <br>
2.4) Selection of multiple targets will be inverted (no need to hold shift to select multiple). <br>
2.5) Once you selected the target(s), an attack will be rolled for each target and damage will be rolled for each attack that hits showing each damage formula and their damage types separately (in case the enemy has resistance to some but not all of the damage dealt) <br>
2.6) After all of this you will be returned to the tool and layer you were at, and the target selection will return to normal. <br>
2.7) Damage will be applied using resistances, immunities, and vulnerabilities of the hit creatures (the weapons are assumed to be nonmagical until i find a way to differentiate between magical and nonmagical weapons). <br><br>
when using an item, alt will use advantage, ctrl will use disadvantage, shift will use versitile. <br><br>
planned in the near future:<br>
1) sttings.
2) a damage card for the DM to know how much damage was actually dealt after the resistances, as well as have an option to reverse it.
3) ability to define a secondary use with multiple formulas. (currently versitle replaces only the first one).
4) ability to define a weapon as magical for purposes of bypassing resistances.
5) automatic disadvantage for ranged weapons depending on range.
6) ability to define a token as in partial cover.
29 changes: 11 additions & 18 deletions dist/module.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
{
"name": "foundry-typescript-template",
"title": "Typescript Template",
"description": "",
"name": "smooth-combat",
"title": "Smooth Combat",
"description": "combat enhancements for 5e",
"authors": [
{
"name": "spacemandev",
"discord": "spacemandev#6256"
"name": "danielrab",
"discord": "danielrab#7070"
}
],
"version": "0.0.1",
"minimumCoreVersion": "0.6.0",
"compatibleCoreVersion": "0.7.2",
"version": "0.0.3-alpha",
"minimumCoreVersion": "0.7.5",
"compatibleCoreVersion": "0.7.5",
"url": "",
"manifest": "",
"download": "",
"type": "module",
"includes": [
"./assets/**",
"./lang/**",
"./scripts/**",
"./styles/**",
"./templates/**",
Expand All @@ -39,16 +37,11 @@
}
],
"esmodules": [
"scripts/module.js"
"scripts/module.js",
"scripts/handlebars-setup.js",
"scripts/monkey-patches.js"
],
"styles": [
"styles/module.css"
],
"languages": [
{
"lang": "en",
"name": "English",
"path": "lang/en.json"
}
]
}
39 changes: 39 additions & 0 deletions dist/scripts/AttackRoll.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
class AttackRollHandler {
constructor(roll, target) {
this.roll = roll;
this.target = target;
}
get hits() {
return this.total >= this.target.actor.ac && !this.fumble;
}
get die() {
return this.roll.terms[0];
}
get total() {
return this.roll.total;
}
get formula() {
return this.roll._formula;
}
get critical() {
return this.die.results[0].result >= this.die.options.critical;
}
get fumble() {
return this.die.results[0].result <= this.die.options.fumble;
}
}
export default function attackRoll(item, target, options) {
return __awaiter(this, void 0, void 0, function* () {
const roll = yield item.rollAttack(Object.assign(Object.assign({}, options), { fastForward: true, chatMessage: false }));
return new AttackRollHandler(roll, target);
});
}
33 changes: 33 additions & 0 deletions dist/scripts/DamageRoll.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { DamageRollPart } from './DamageRollPart.js';
import settings from './settings.js';
class DamageRollHandler {
constructor(parts) {
this.parts = parts;
}
apply(target) {
const damage = this.parts.map((part) => part.getDamage(target)).reduce((a, b) => a + b);
if (settings.applyDamage)
target.actor.applyDamage(damage);
return damage;
}
}
export default function damageRoll(item, versatile, critical) {
return __awaiter(this, void 0, void 0, function* () {
const damageParts = item.data.data.damage.parts;
const rollParts = yield Promise.all(damageParts.map((part) => __awaiter(this, void 0, void 0, function* () { return DamageRollPart(item, part, critical); })));
if (versatile && item.data.data.damage.versatile) {
const versatileRollPart = yield DamageRollPart(item, [item.data.data.damage.versatile, damageParts[0][1]], critical);
rollParts[0] = versatileRollPart;
}
return new DamageRollHandler(rollParts);
});
}
36 changes: 36 additions & 0 deletions dist/scripts/DamageRollPart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
export class DamageRollPartHandler {
constructor(roll, type) {
this.roll = roll;
this.type = type;
}
get total() {
return this.roll.total;
}
get formula() {
return this.roll._formula;
}
getDamage(target) {
return Math.floor(target.actor.damageMultiplier(this.type) * this.total);
}
}
export function DamageRollPart(item, [formula, type], critical) {
return __awaiter(this, void 0, void 0, function* () {
const roll = yield game.dnd5e.dice.damageRoll({
parts: [formula],
data: item.getRollData(),
critical,
fastForward: true,
chatMessage: false,
});
return new DamageRollPartHandler(roll, type);
});
}
38 changes: 38 additions & 0 deletions dist/scripts/handlebars-setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* eslint-disable func-names */
Handlebars.registerHelper('ifObject', function (item, options) {
if (typeof item === 'object') {
return options.fn(this);
}
return options.inverse(this);
});
Handlebars.registerHelper('when', function (operand1, operator, operand2, options) {
const operators = {
eq(l, r) {
return l === r;
},
noteq(l, r) {
return l !== r;
},
gt(l, r) {
return Number(l) > Number(r);
},
or(l, r) {
return l || r;
},
and(l, r) {
return l && r;
},
'%': function (l, r) {
return (l % r) === 0;
},
};
const result = operators[operator](operand1, operand2);
if (result)
return options.fn(this);
return options.inverse(this);
});
Hooks.on('init', () => loadTemplates([
'./modules/smooth-combat/templates/rollDetails.hbs',
'./modules/smooth-combat/templates/damageRoll.hbs',
'./modules/smooth-combat/templates/roll.hbs',
]));
14 changes: 14 additions & 0 deletions dist/scripts/html-generation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
export default function attackResultHTML(result) {
return __awaiter(this, void 0, void 0, function* () {
return renderTemplate('./modules/smooth-combat/templates/attackResult.hbs', result);
});
}
Loading

0 comments on commit 41d33d2

Please # to comment.