Skip to content

Commit

Permalink
feat: add custom effect manager
Browse files Browse the repository at this point in the history
This allows a GM user to customize their FX.

Features:

- May define macros for items by "name" or "Lancer ID"
- Macros overwrite module defaults
- Extensive drag-drop support
- Folder system
- "Execute Macro" preview button
- "Export/Import" buttons to transfer state between worlds
- Effects Manger respects system theme choice
  • Loading branch information
cirrahn committed Jul 24, 2024
1 parent 15f9927 commit 8f8cda7
Show file tree
Hide file tree
Showing 20 changed files with 1,590 additions and 36 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
run: |
npm ci
npm run build
zip -r ./module.zip module.json icons/ lang/ packs/ scripts/ sprites/ soundfx/ video/
zip -r ./module.zip module.json icons/ lang/ packs/ scripts/ sprites/ soundfx/ video/ templates/ css/ tours/
# Create a release for this specific version
- name: Update Release with Files
Expand Down
87 changes: 87 additions & 0 deletions css/effects-manager.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
.lwfx-effects-manager {
color: var(--dark-text);
background: var(--background-color);
}

/* -------------------------------------------- */

.lwfx-effects-manager__vr {
height: 100%;
border: 1px solid var(--primary-color, fuschia);
}

.lwfx-effects-manager__hr {
border-color: var(--primary-color, fuschia);
}

/* -------------------------------------------- */

.lwfx-effects-manager__row {
margin-top: calc(0.5 * var(--lwfx-spacer));
margin-bottom: calc(0.5 * var(--lwfx-spacer));
}

/* -------------------------------------------- */

.lwfx-effects-manager__folder,
.lwfx-effects-manager__uncategorized {
border: 2px solid var(--primary-color);
border-top: 0;
}

.lancer.app input.lwfx-effects-manager__ipt-folder-name[type="text"] {
height: calc(2.25rem + 2px);
color: var(--light-text, fuschia);
font-size: 15px;
font-weight: 700;
text-transform: uppercase;
border: 0;
}

.lancer.app input.lwfx-effects-manager__ipt-folder-name[type="text"]::placeholder {
color: color-mix(in srgb, var(--light-text), var(--dark-text) 27%);
font-style: italic;
font-weight: initial;
}

/* -------------------------------------------- */

.lwfx-effects-manager__effects-uncategorized-empty {
/* Pad to a comfortable height for drag-drop */
min-height: 100px;
}

/* -------------------------------------------- */

.lwfx-effects-manager__effect {
padding: var(--lwfx-spacer) var(--lwfx-spacer) var(--lwfx-spacer) 0;
margin-left: var(--lwfx-spacer);
margin-right: var(--lwfx-spacer);
}

.lwfx-effects-manager__grip {
cursor: move;
width: calc(2 * var(--lwfx-spacer));
}

.lwfx-effects-manager__grip-handle {
display: none;
}

.lwfx-effects-manager__effect:hover .lwfx-effects-manager__grip-handle {
display: block;
}

.lwfx-effects-manager__effect-ipt[type="text"],
.lwfx-effects-manager__effect-sel {
width: calc(100% - calc(4 * var(--lwfx-spacer)));
margin: calc(0.5 * var(--lwfx-spacer)) calc(2 * var(--lwfx-spacer));
background-color: var(--tooltip-background);
color: var(--dark-text);
height: var(--form-field-height);
}

.lwfx-effects-manager__icon-duplicate {
color: var(--error-color);
filter: contrast(2);
}
165 changes: 165 additions & 0 deletions css/helpers.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/* TODO(v12) remove some of these in favor of v2 styling */

:root {
--lwfx-spacer: 0.5rem;
--lwfx-top-dogear-path: polygon(var(--lwfx-spacer) 0, 100% 0, 100% 100%, 0 100%, 0 var(--lwfx-spacer));
--lwfx-double-dogear-path: polygon(var(--lwfx-spacer) 0, 100% 0, 100% calc(100% - var(--lwfx-spacer)), calc(100% - var(--lwfx-spacer)) 100%, 0 100%, 0 var(--lwfx-spacer));
}

/* -------------------------------------------- */

.lwfx__darken-1 {
background-color: var(--darken-1);
}

.lwfx__darken-2 {
background-color: var(--darken-2);
}

.lwfx__clipped-top {
clip-path: var(--lwfx-top-dogear-path);
}

.lwfx__clipped {
clip-path: var(--lwfx-double-dogear-path);
}

.lwfx__clipped > .lancer-header,
.lwfx__clipped-top > .lancer-header {
padding-left: var(--lwfx-spacer);
padding-right: var(--lwfx-spacer);
}

/* -------------------------------------------- */

.lwfx__fa {
padding-right: 0 !important;
padding-left: 0 !important;
flex: unset !important;
}

.lwfx__fa-fw {
margin-right: 0 !important;
}

/* -------------------------------------------- */

.lwfx__flexcol {
display: flex !important;
flex-direction: column !important;
}

.lwfx__flexrow {
display: flex !important;
}

.lwfx__flexrow-v-center {
display: flex !important;
align-items: center !important;
}

.lwfx__flexrow-vh-center {
display: flex !important;
align-items: center !important;
justify-content: center !important;
}

.lwfx__hidden {
display: none !important;
}

/* -------------------------------------------- */

.lwfx__flexshrink-0 {
flex-shrink: 0 !important;
}

/* -------------------------------------------- */

.lwfx__w-initial {
width: initial !important;
}

.lwfx__w-100 {
width: 100% !important;
}

.lwfx__min-w-initial {
min-width: initial !important;
}

.lwfx__min-w-0 {
min-width: 0 !important;
}

.lwfx__max-w-100px {
max-width: 100px !important;
}

.lwfx__h-100 {
height: 100% !important;
}

.lwfx__min-h-0 {
min-height: 0 !important;
}

/* -------------------------------------------- */

.lwfx__scrollable {
margin-right: -0.75rem !important;
padding-right: 0.75rem !important;
overflow: hidden auto !important;
}

/* -------------------------------------------- */

.lwfx__ws-nowrap {
white-space: nowrap !important;
}

/* -------------------------------------------- */

.lwfx__m-0 {
margin: 0 !important;
}

.lwfx__mx-1 {
margin-left: calc(0.5 * var(--lwfx-spacer)) !important;
margin-right: calc(0.5 * var(--lwfx-spacer)) !important;
}

.lwfx__my-1 {
margin-top: calc(0.5 * var(--lwfx-spacer)) !important;
margin-bottom: calc(0.5 * var(--lwfx-spacer)) !important;
}

.lwfx__mr-1 {
margin-right: calc(0.5 * var(--lwfx-spacer)) !important;
}

.lwfx__mr-2 {
margin-right: var(--lwfx-spacer) !important;
}

.lwfx__mb-1 {
margin-bottom: calc(0.5 * var(--lwfx-spacer)) !important;
}

.lwfx__ml-1 {
margin-left: calc(0.5 * var(--lwfx-spacer)) !important;
}

.lwfx__pr-2 {
padding-right: var(--lwfx-spacer) !important;
}

.lwfx__px-2 {
padding-left: var(--lwfx-spacer) !important;
padding-right: var(--lwfx-spacer) !important;
}

.lwfx__py-1 {
padding-top: calc(0.5 * var(--lwfx-spacer)) !important;
padding-bottom: calc(0.5 * var(--lwfx-spacer)) !important;
}
49 changes: 48 additions & 1 deletion lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,53 @@
"Sound Volume Hint": "Set a base volume level for all sound effects. Note that for each user, this value is combined with the user's \"Global: Interface\" volume setting (see the \"Playlists\" sidebar tab) to produce their final volume level.",
"Use Weapon Heuristic": "Use Weapon Heuristic",
"Use Weapon Heuristic Hint": "Use a heuristic to determine a suitable effect to play when using an unknown (homebrew/custom) weapon.",
"Debug: Play Miss Animations by Default": "Debug: Play Miss Animations by Default"
"Debug: Play Miss Animations by Default": "Debug: Play Miss Animations by Default",

"effectManager": {
"settings": {
"Effects Manager": "Effects Manager",
"Open Effects Manager": "Open Effects Manager"
},

"app": {
"Effects Manager": "Effects Manager",
"Name": "Name",
"Lancer ID": "Lancer ID",
"Import Custom Effects": "Import Custom Effects",
"Import Custom Effects Hint 1": "You may import custom effects from an exported JSON file.",
"Import Custom Effects Hint 2": "This operation will update your custom effects and cannot be un-done.",
"Import": "Import",
"Create Effect": "Create Effect",
"Create Folder": "Create Folder",
"Export Data": "Export Data",
"Import Data": "Import Data",
"Uncategorized": "Uncategorized",
"Uncategorized Hint 1": "Drop an effect here to remove it from its folder.",
"Expand Folder": "Expand Folder",
"Collapse Folder": "Collapse Folder",
"Folder Name": "Folder Name",
"SHIFT to Also Delete Contents": "SHIFT to Also Delete Contents",
"Mode Hint": "Use \"Lancer ID\" mode for items from LCPs. Use \"Name\" mode for other items.",
"Mode": "Mode",
"Item Name": "Item Name",
"Macro": "Macro",
"Macro UUID": "Macro UUID",
"Execute Macro": "Execute Macro",
"Remove Folder": "Remove Folder",
"Delete Selected": "Delete Selected",
"Select All": "Select All",
"Delete Selected Effects": "Delete Selected Effects",
"Delete Selected Effects Hint": "{count} effect(s) will be deleted. This cannot be un-done.",
"Multiple Effects Item Name Hint": "Multiple effects exist with this item name!",
"Multiple Effects Lancer ID Hint": "Multiple effects exist with this Lancer ID!",
"Start Tour": "Start Tour"
},

"fields": {
"macroUuid": {
"label": "Macro"
}
}
}
}
}
4 changes: 4 additions & 0 deletions module.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@
"esmodules": [
"./scripts/WeaponFX.js"
],
"styles": [
"./css/effects-manager.css",
"./css/helpers.css"
],
"packs": [
{
"name": "weaponfx",
Expand Down
2 changes: 2 additions & 0 deletions scripts/WeaponFX.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { bindHooks as bindApiHooks } from "./api.js";
import { bindHooks as bindSettingsHooks } from "./settings.js";
import { bindHooks as bindModuleCheckHooks } from "./moduleCheck.js";
import { bindHooks as bindFlowListenerHooks } from "./flow/flowListener.js";
import { bindHooks as bindManagerHooks } from "./effectManager/effectManager.js";

bindSettingsHooks();
bindApiHooks();
bindModuleCheckHooks();
bindFlowListenerHooks();
bindManagerHooks();
28 changes: 3 additions & 25 deletions scripts/api.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { euclideanDistance } from "./utils.js";
import { euclideanDistance, getMacroVariables } from "./utils.js";
import LloydsAlgorithm from "./lloydsAlgorithm.js";
import { SETTING_DEBUG_IS_DEFAULT_MISS, SETTING_VOLUME } from "./settings.js";
import { SETTING_VOLUME } from "./settings.js";
import { MODULE_ID } from "./consts.js";

/**
Expand All @@ -11,29 +11,6 @@ class ModuleApi {
return volume * game.settings.get(MODULE_ID, SETTING_VOLUME);
}

static getMacroVariables(macro) {
const sourceTokenFallback = canvas.tokens.controlled[0] ?? game.combat?.current?.tokenId;
const targetsFallback = [...game.user.targets];
const flowInfo = macro?.flags?.[MODULE_ID]?.flowInfo;

if (!flowInfo) {
return {
sourceToken: sourceTokenFallback,
targetTokens: targetsFallback,
targetsMissed: game.settings.get(MODULE_ID, SETTING_DEBUG_IS_DEFAULT_MISS)
? new Set(targetsFallback.map(target => target.id))
: new Set(),
};
}

const { sourceToken, targetTokens, targetsMissed } = flowInfo;
return {
sourceToken: sourceToken || sourceTokenFallback,
targetTokens: targetTokens || targetsFallback,
targetsMissed,
};
}

static getTargetLocationsFromTokenGroup(targetTokens, numGroups) {
const targetPoints = targetTokens.map(token => {
return { x: token.center.x, y: token.center.y };
Expand All @@ -42,6 +19,7 @@ class ModuleApi {
return LloydsAlgorithm.getCentroids(targetPoints, numGroups);
}

static getMacroVariables = getMacroVariables;
static euclideanDistance = euclideanDistance;
}

Expand Down
2 changes: 2 additions & 0 deletions scripts/consts.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export const MODULE_ID = "lancer-weapon-fx";

export const PACK_ID_WEAPONFX = `${MODULE_ID}.weaponfx`;
Loading

0 comments on commit 8f8cda7

Please sign in to comment.