Skip to content

Commit

Permalink
Initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
BadIdeasBureau authored and BadIdeasBureau committed Jan 2, 2022
1 parent e63a7a5 commit 4ac1fd7
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 87 deletions.
72 changes: 6 additions & 66 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,71 +1,11 @@
![](https://img.shields.io/badge/Foundry-v0.8.6-informational)
<!--- Downloads @ Latest Badge -->
<!--- replace <user>/<repo> with your username/repository -->
<!--- ![Latest Release Download Count](https://img.shields.io/github/downloads/<user>/<repo>/latest/module.zip) -->
# Memento Mori
A module for Foundry VTT.

<!--- Forge Bazaar Install % Badge -->
<!--- replace <your-module-name> with the `name` in your manifest -->
<!--- ![Forge Installs](https://img.shields.io/badge/dynamic/json?label=Forge%20Installs&query=package.installs&suffix=%25&url=https%3A%2F%2Fforge-vtt.com%2Fapi%2Fbazaar%2Fpackage%2F<your-module-name>&colorB=4aa94a) -->
When installed, this module will automatically mark tokens with a configurable status effect when they run out of health. The status effect name and icon can be set by the user, and a different effect can be used for linked or unlinked tokens (e.g. dead vs dying).

The module is set up out-of-the-box for dnd5e and systems which use the same path for an actor's HP (`data.data.attributes.hp.value`), which should include PF1 and PF2. There are settings to configure which will allow the attribute path being monitored to be changed, as well as the check (so e.g. if your system requires marking a token dead/dying when their `wounds` are greater than their `maxWounds`, that can be done). More complex setups (e.g. "a token is dead if each of its 6 stats is marked") are not supported.

# How to use this Template to create a versioned Release

1. Open your repository's releases page.
Icons from game-icons.net

![Where to click to open repository releases.](https://user-images.githubusercontent.com/7644614/93409301-9fd25080-f864-11ea-9e0c-bdd09e4418e4.png)

2. Click "Draft a new release"

![Draft a new release button.](https://user-images.githubusercontent.com/7644614/93409364-c1333c80-f864-11ea-89f1-abfcb18a8d9f.png)

3. Fill out the release version as the tag name.

If you want to add details at this stage you can, or you can always come back later and edit them.

![Release Creation Form](https://user-images.githubusercontent.com/7644614/93409543-225b1000-f865-11ea-9a19-f1906a724421.png)

4. Hit submit.

5. Wait a few minutes.

A Github Action will run to populate the `module.json` and `module.zip` with the correct urls that you can then use to distribute this release. You can check on its status in the "Actions" tab.

![Actions Tab](https://user-images.githubusercontent.com/7644614/93409820-c1800780-f865-11ea-8c6b-c3792e35e0c8.png)

6. Grab the module.json url from the release's details page.

![image](https://user-images.githubusercontent.com/7644614/93409960-10c63800-f866-11ea-83f6-270cc5d10b71.png)

This `module.json` will only ever point at this release's `module.zip`, making it useful for sharing a specific version for compatibility purposes.

7. You can use the url `https://github.com/<user>/<repo>/releases/latest/download/module.json` to refer to the manifest.

This is the url you want to use to install the module typically, as it will get updated automatically.

# How to List Your Releases on Package Admin

To request a package listing for your first release, go to the [Package Submission Form](https://foundryvtt.com/packages/submit) (accessible via a link at the bottom of the "[Systems and Modules](https://foundryvtt.com/packages/)" page on the Foundry website).

Fill in the form. "Package Name" must match the name in the module manifest. Package Title will be the display name for the package. Package URL should be your repo URL.
![image](https://user-images.githubusercontent.com/36359784/120664263-b49e5500-c482-11eb-9126-af7006389903.png)


One of the Foundry staff will typically get back to you with an approval or any further questions within a few days, and give you access to the package admin pages.

Once you have access to the [module admin page](https://foundryvtt.com/admin/packages/package/), you can release a new version by going into the page for your module, scrolling to the bottom, and filling in a new Package Version.

When listing a new version, Version should be the version number you set above, and the Manifest URL should be the manifest __for that specific version__ (do not use /latest/ here).
![image](https://user-images.githubusercontent.com/36359784/120664346-c4b63480-c482-11eb-9d8b-731b50d70939.png)

> ### :warning: Important :warning:
>
> It is very important that you use the specific release manifest url, and not the `/latest` url here. For more details about why this is important and how Foundry Installs/Updates packages, read [this wiki article](https://foundryvtt.wiki/en/development/guides/releases-and-history).
Clicking "Save" in the bottom right will save the new version, which means that anyone installing your module from within Foundry will get that version, and a post will be generated in the #release-announcements channel on the official Foundry VTT Discord.


# FoundryVTT Module

Does something, probably

## Changelog
Bleeding Wound and Pirate grave icon by [Lorc](https://lorcblog.blogspot.com/) under [CC BY 3.0](http://creativecommons.org/licenses/by/3.0/), from http://game-icons.net
1 change: 1 addition & 0 deletions icons/bleeding-wound.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions icons/pirate-grave.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 18 additions & 1 deletion languages/en.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
{
"MODULE.hello": "hello"
"MEMENTO_MORI.Settings.HitPath.Name": "Path to changing value (A)",
"MEMENTO_MORI.Settings.HitPath.Hint": "The property path to the value which will change with each update, e.g. current HP or wounds. This must be relative to actor.data, e.g. for the dnd5e system, where the HP value is stored at actor.data.data.attributes.hp.value, this setting should be data.attributes.hp.value",
"MEMENTO_MORI.Settings.comparison.Name": "Comparison Type",
"MEMENTO_MORI.Settings.comparison.Hint": "The type of comparison to perform, of the form A (comparison) B. 'Less than or equal to' is suitable for systems where HP counts down to 0, 'greater than or equal to' is suitable for systems where a number of wounds counts up to a maximum number of wounds.",
"MEMENTO_MORI.Settings.comparison.LT": "Less than or equal to (<=)",
"MEMENTO_MORI.Settings.comparison.GT": "Greater than or equal to (>=)",
"MEMENTO_MORI.Settings.CompareTo.Name": "Static value (B)",
"MEMENTO_MORI.Settings.CompareTo.Hint": "A number or property path for the value to compare to. For systems where HP counts down, this should generally be 0. For systems where wounds count up, this should be the maximum number of wounds, or a path to that property (relative to actor.data).",
"MEMENTO_MORI.Settings.UnlinkedStatusName.Name": "Status Name (unlinked token)",
"MEMENTO_MORI.Settings.UnlinkedStatusName.Hint": "The name for the status to be applied to an unlinked token. Leave blank if you don't want statuses to be created for unlinked tokens.",
"MEMENTO_MORI.Settings.UnlinkedStatusIcon.Name": "Status Icon (unlinked token)",
"MEMENTO_MORI.Settings.UnlinkedStatusIcon.Hint": "The icon to be used for the status on an unlinked token",
"MEMENTO_MORI.Settings.LinkedStatusName.Name": "Status Name (linked token)",
"MEMENTO_MORI.Settings.LinkedStatusName.Hint": "The name for the status to be applied to a linked token. Leave blank if you don't want statuses to be created for linked tokens.",
"MEMENTO_MORI.Settings.LinkedStatusIcon.Name": "Status Icon (unlinked token)",
"MEMENTO_MORI.Settings.LinkedStatusIcon.Hint": "The icon to be used for the status on an unlinked token",
"MEMENTO_MORI.Settings.Overlay.Name": "Overlay",
"MEMENTO_MORI.Settings.Overlay.Hint": "If true, the status will display as an overlay on the whole token. If false, it will display as a small icon in the corner of the token."
}
25 changes: 7 additions & 18 deletions module.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "module",
"title": "New Module",
"name": "memento-mori",
"title": "Memento Mori - Automatically Mark Tokens Dead",
"description": "",
"version": "This is auto replaced",
"library": "false",
Expand All @@ -9,9 +9,8 @@
"compatibleCoreVersion": "9",
"authors": [
{
"name": "The League of Extraordinary FVTT Developers",
"url": "https://github.com/League-of-Foundry-Developers",
"discord": "discordID#0001"
"name": "BadIdeasBureau",
"discord": "BadIdeasBureau#7024"
}
],
"dependencies": [
Expand All @@ -23,12 +22,7 @@
"esmodules": [
"/scripts/module.js"
],
"scripts": [
"/scripts/lib/lib.js"
],
"styles": [
"/styles/module.css"
],

"languages": [
{
"lang": "en",
Expand All @@ -38,11 +32,6 @@
],
"url": "This is auto replaced",
"manifest": "This is auto replaced",
"download": "This is auto replaced",
"media": [
{
"type": "icon",
"url": "https://avatars2.githubusercontent.com/u/71292812?s=400&u=ccdb4eeb7abf551ca8f314e5a9bfd0479a4d3d41&v=4"
}
]
"download": "This is auto replaced"

}
Empty file removed scripts/lib/lib.js
Empty file.
157 changes: 155 additions & 2 deletions scripts/module.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,160 @@
Hooks.once('init', async function() {
//Dev mode integration
const MODULE_ID = "memento-mori";

Hooks.once('devModeReady', ({ registerPackageDebugFlag }) => {
registerPackageDebugFlag(MODULE_ID);
});

Hooks.once('ready', async function() {
function log(...args) {
try {
const isDebugging = game.modules.get('_dev-mode')?.api?.getPackageDebugValue(MODULE_ID);

if (isDebugging) {
console.log(MODULE_ID, '|', ...args);
}
} catch (e) {}
}

//settings
function getSetting(key){
return game.settings.get(MODULE_ID, key)
}

Hooks.once('init', async function() {

game.settings.register(MODULE_ID, "unlinkedStatusName",{
name: "MEMENTO_MORI.Settings.UnlinkedStatusName.Name",
hint: "MEMENTO_MORI.Settings.UnlinkedStatusName.Hint",
scope: 'world', // "world" = sync to db, "client" = local storage
config: true, // false if you dont want it to show in module config
type: String, // Number, Boolean, String,
default: "Dead"
})

game.settings.register(MODULE_ID, "unlinkedStatusIcon",{
name: "MEMENTO_MORI.Settings.UnlinkedStatusIcon.Name",
hint: "MEMENTO_MORI.Settings.UnlinkedStatusIcon.Hint",
scope: 'world', // "world" = sync to db, "client" = local storage
config: true, // false if you dont want it to show in module config
type: String, // Number, Boolean, String,
filePicker: "image",
default: `modules/${MODULE_ID}/icons/pirate-grave.svg`
})

game.settings.register(MODULE_ID, "linkedStatusName",{
name: "MEMENTO_MORI.Settings.LinkedStatusName.Name",
hint: "MEMENTO_MORI.Settings.LinkedStatusName.Hint",
scope: 'world', // "world" = sync to db, "client" = local storage
config: true, // false if you dont want it to show in module config
type: String, // Number, Boolean, String,
default: "Dying"
})

game.settings.register(MODULE_ID, "linkedStatusIcon",{
name: "MEMENTO_MORI.Settings.LinkedStatusIcon.Name",
hint: "MEMENTO_MORI.Settings.LinkedStatusIcon.Hint",
scope: 'world', // "world" = sync to db, "client" = local storage
config: true, // false if you dont want it to show in module config
type: String, // Number, Boolean, String,
filePicker: "image",
default: `modules/${MODULE_ID}/icons/bleeding-wound.svg`
})

game.settings.register(MODULE_ID, "overlay",{
name: "MEMENTO_MORI.Settings.Overlay.Name",
hint: "MEMENTO_MORI.Settings.Overlay.Hint",
scope: 'world', // "world" = sync to db, "client" = local storage
config: true, // false if you dont want it to show in module config
type: Boolean, // Number, Boolean, String,
default: true
})

game.settings.register(MODULE_ID, "hitPath",{
name: "MEMENTO_MORI.Settings.HitPath.Name",
hint: "MEMENTO_MORI.Settings.HitPath.Hint",
scope: 'world', // "world" = sync to db, "client" = local storage
config: true, // false if you dont want it to show in module config
type: String, // Number, Boolean, String,
default: "data.attributes.hp.value"
})

game.settings.register(MODULE_ID, "comparison",{
name: "MEMENTO_MORI.Settings.comparison.Name",
hint: "MEMENTO_MORI.Settings.comparison.Hint",
scope: 'world', // "world" = sync to db, "client" = local storage
config: true, // false if you dont want it to show in module config
type: String, // Number, Boolean, String,
default: "lt",
choices: {
"lt": game.i18n.localize("MEMENTO_MORI.Settings.comparison.LT"), //Less than or equal to
"gt": game.i18n.localize("MEMENTO_MORI.Settings.comparison.GT") //greater than or equal to
}
})

game.settings.register(MODULE_ID, "compareTo",{
name: "MEMENTO_MORI.Settings.CompareTo.Name",
hint: "MEMENTO_MORI.Settings.CompareTo.Hint",
scope: 'world', // "world" = sync to db, "client" = local storage
config: true, // false if you dont want it to show in module config
type: String, // Number, Boolean, String,
default: "0"
})
});

async function addEffect(actor){
log("Adding Effect to " + actor.name)
if(actor.effects.find(e => e.data?.flags?.core?.statusId === MODULE_ID)) return //no new effect if one is already present
let linked = actor.isToken ? "unlinked" : "linked"
let label = getSetting(`${linked}StatusName`);
let icon = getSetting(`${linked}StatusIcon`);
let effect = {
label,
icon,
flags: {
core:{
statusId: MODULE_ID,
overlay: getSetting("overlay")
}
}
}
await actor.createEmbeddedDocuments("ActiveEffect", [effect])
}

async function removeEffects(actor){
log("Removing Effect From" + actor.name)
let effects = actor.effects.filter(e => e.data?.flags?.core?.statusId === MODULE_ID)
if (effects.length===0) return
for (let effect of effects) {
await effect.delete()
}
}

async function updateActor(actor, update){
if(!game.user === game.users.find(u => u.isGM && u.active)) return //first GM only
let hp = getProperty(update, getSetting("hitPath"))
if(hp === undefined) {
console.warn(`${MODULE_ID} | The setting ${game.i18n.localize("MEMENTO_MORI.Settings.HitPath.Name")} is not a valid property of actor.data or that property is undefined`)
return
}
let compareTo = isNaN(parseInt(getSetting("compareTo"))) ? actor.data.getProperty(getSetting("compareTo")) : parseInt(getSetting("compareTo"))
if (compareTo === undefined) {
console.warn(`${MODULE_ID} | The setting ${game.i18n.localize("MEMENTO_MORI.Settings.compareTo.Name")} is not a number or a valid property of actor.data or that property is undefined`)
return
}
let comparison = getSetting("comparison")
let dead = false
switch (comparison) {
case "lt":
dead = hp <= compareTo;
break
case "gt":
dead = hp >= compareTo;
break
}
if(dead) await addEffect(actor)
if(!dead && hp) await removeEffects(actor) //only remove if not dead and if hp exists, to avoid false removal
}
Hooks.on("ready", () => {
if (game.user.isGM) Hooks.on("updateActor", updateActor)
log("Ready Hook Fired")
})
Empty file removed styles/module.css
Empty file.

0 comments on commit 4ac1fd7

Please sign in to comment.