diff --git a/README.md b/README.md index f6c34f8..a8347b6 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,49 @@ - Tom Bloom Creator of Cain - My sunday DND group +## v1.1.X Update + +Version 1.1.X update is a rework of the item sheet, blasphemies, agendas, sin-marks, and afflictions. All of these items have now been itemized, and we are able to further update better automation into the system moving forward. + +Features are included but not limited too: + +- Re-work of sheet aesthetics to better fit accessibility needs. +- Re-coloring and re-designing the look of blasphmies, as well as letting you customize your own +- Kit has been re-styled and icons have been updated. +- Homebrew page for Agenda and blasphemies for easy to create home-brew content. +- Afflictions are now items, and afflictions can now be added onto sheet by dragging them on. +- Sin-mark re-haul, automatic sin-mark rolling, and improved sin-mark roll system. +- Agendas are now semi-automated. You drag an agenda onto sheet, it will automate the process of adding in agenda items. +- Blasphmies are now semi-automated, You drag blasphmies onto your sheet and it will automate the process of adding in abilities etc. +- XP managment has been vastly improved with automated xp advancment, xp maximum increase and decrease, and end of session XP bonuses. +- Added in some settings for player managment to be more accessible. + + +## Migrating to v1.1.X + +If you are coming from an older version of the system here is what you have to know: +We have completley re-worked items, and they are **Mandatory** for the new system work. Details are below when importing the new items, and remember **KEEP DOCUMENT IDS** +When updating, I highly, highly suggest deleting all of your old items and adding the new ones. +In addition to this, you may have to wipe some parts of actor-sheets. +I would take screenshots of what you had before and replicate them back to the now standard items that are needed. + +Things that will most definitley be wiped off your old character sheets are: +------------------ +1. Agendas +2. Blasphemies +3. Sin Marks +4. Afflictions + + +Overall, the rest of your game should be un-changed on the game-master side. + +As a side note, it will definitley be in a 1.2.X update where we re-haul sins and improve functionality. This may not be for some time as we perfect the player portion of the game. +If you have any questions, message in the foundry vtt channel. Thank you! + + ## Overview -Cain is an open-source project designed to streamline CAIN RPG system management within FoundryVTT. Cain enhances user experiences through customizable actors, items, and effects. The projects core functionalities, encapsulated in the `cain` module, facilitate dynamic talisman management, item roll logic, and interactive character sheet customization. +Cain is an open-source project designed to streamline CAIN RPG player, gm and system management within FoundryVTT. Cain enhances user experiences through customizable actors, items, and effects. The projects core functionalities, encapsulated in the `cain` module, facilitate dynamic talisman management, item roll logic, and interactive character sheet customization. --- @@ -81,7 +121,7 @@ Then, proceed to copy the link at the top. https://github.com/user-attachments/assets/035fbb43-5e0c-4ed6-970d-b3dd6f9d6909 -* **Creating a World** +### **Creating a World** 1. Click the "Game Worlds" button and then click "Create World". 2. Fill in info. 3. Click Create World @@ -91,15 +131,18 @@ https://github.com/user-attachments/assets/035fbb43-5e0c-4ed6-970d-b3dd6f9d6909 https://github.com/user-attachments/assets/88cb39a3-a235-4c6b-9f56-ebfbcff463fa -* **Importing Compendium Information** +### **Importing Compendium Information** * Go to Compendium tab (Book with world looking icon). -* Right click on each pack and click import content, then click yes. -* Items are in items tab, Rollable tables are in Rollable tables tab. +* Right click on each pack and click import content, **CLICK KEEP DOCUMENT IDS** then click yes. + + +**YOU MUST KEEP DOCUMENT IDS, THIS IS CRITICAL TO THE SYSTEM WORKING AS INTENDED.** + +https://github.com/user-attachments/assets/464240a1-7d6c-49f0-b68e-6415b091d353 -https://github.com/user-attachments/assets/f8bb07fc-d305-42a3-97b3-d1dedd0cbfb2 ## Investigations diff --git a/assets/CAT/CAT0.png b/assets/CAT/CAT0.png new file mode 100644 index 0000000..317cbd7 Binary files /dev/null and b/assets/CAT/CAT0.png differ diff --git a/assets/KIT/KIT-empty.png b/assets/KIT/KIT-empty.png new file mode 100644 index 0000000..8843441 Binary files /dev/null and b/assets/KIT/KIT-empty.png differ diff --git a/assets/KIT/KIT-filled.png b/assets/KIT/KIT-filled.png new file mode 100644 index 0000000..8f468b3 Binary files /dev/null and b/assets/KIT/KIT-filled.png differ diff --git a/assets/homebrew.png b/assets/homebrew.png new file mode 100644 index 0000000..fb693b0 Binary files /dev/null and b/assets/homebrew.png differ diff --git a/assets/sheet/CAIN-eye-0-0.png b/assets/sheet/CAIN-eye-0-0.png new file mode 100644 index 0000000..0cca08f Binary files /dev/null and b/assets/sheet/CAIN-eye-0-0.png differ diff --git a/assets/sheet/CAIN-eye-0-1.png b/assets/sheet/CAIN-eye-0-1.png new file mode 100644 index 0000000..1a8c3ce Binary files /dev/null and b/assets/sheet/CAIN-eye-0-1.png differ diff --git a/assets/sheet/CAIN-eye-1-0.png b/assets/sheet/CAIN-eye-1-0.png new file mode 100644 index 0000000..8031bc0 Binary files /dev/null and b/assets/sheet/CAIN-eye-1-0.png differ diff --git a/assets/sheet/CAIN-eye-1-1.png b/assets/sheet/CAIN-eye-1-1.png new file mode 100644 index 0000000..891a12d Binary files /dev/null and b/assets/sheet/CAIN-eye-1-1.png differ diff --git a/assets/sheet/CAIN-eye-2-0.png b/assets/sheet/CAIN-eye-2-0.png new file mode 100644 index 0000000..33cb873 Binary files /dev/null and b/assets/sheet/CAIN-eye-2-0.png differ diff --git a/assets/sheet/CAIN-eye-2-1.png b/assets/sheet/CAIN-eye-2-1.png new file mode 100644 index 0000000..bcbb8c6 Binary files /dev/null and b/assets/sheet/CAIN-eye-2-1.png differ diff --git a/assets/sheet/CAIN-eye-3-0.png b/assets/sheet/CAIN-eye-3-0.png new file mode 100644 index 0000000..361cf6e Binary files /dev/null and b/assets/sheet/CAIN-eye-3-0.png differ diff --git a/assets/sheet/CAIN-eye-3-1.png b/assets/sheet/CAIN-eye-3-1.png new file mode 100644 index 0000000..8ccfc7e Binary files /dev/null and b/assets/sheet/CAIN-eye-3-1.png differ diff --git a/assets/sheet/CAIN-eye-4-0.png b/assets/sheet/CAIN-eye-4-0.png new file mode 100644 index 0000000..b6f008f Binary files /dev/null and b/assets/sheet/CAIN-eye-4-0.png differ diff --git a/assets/sheet/CAIN-eye-4-1.png b/assets/sheet/CAIN-eye-4-1.png new file mode 100644 index 0000000..b1a2551 Binary files /dev/null and b/assets/sheet/CAIN-eye-4-1.png differ diff --git a/assets/sheet/CAIN-eye-5-0.png b/assets/sheet/CAIN-eye-5-0.png new file mode 100644 index 0000000..4084dd5 Binary files /dev/null and b/assets/sheet/CAIN-eye-5-0.png differ diff --git a/assets/sheet/CAIN-eye-5-1.png b/assets/sheet/CAIN-eye-5-1.png new file mode 100644 index 0000000..96617ca Binary files /dev/null and b/assets/sheet/CAIN-eye-5-1.png differ diff --git a/assets/sheet/CAIN-eye-6-0.png b/assets/sheet/CAIN-eye-6-0.png new file mode 100644 index 0000000..d349564 Binary files /dev/null and b/assets/sheet/CAIN-eye-6-0.png differ diff --git a/assets/sheet/CAIN-eye-6-1.png b/assets/sheet/CAIN-eye-6-1.png new file mode 100644 index 0000000..7caf827 Binary files /dev/null and b/assets/sheet/CAIN-eye-6-1.png differ diff --git a/assets/sheet/CAIN-eye-closed.png b/assets/sheet/CAIN-eye-closed.png new file mode 100644 index 0000000..81b0dfe Binary files /dev/null and b/assets/sheet/CAIN-eye-closed.png differ diff --git a/assets/sheet/CAIN-eye-old.png b/assets/sheet/CAIN-eye-old.png new file mode 100644 index 0000000..ad3e6d0 Binary files /dev/null and b/assets/sheet/CAIN-eye-old.png differ diff --git a/assets/sheet/CAIN-eye-small-0.png b/assets/sheet/CAIN-eye-small-0.png new file mode 100644 index 0000000..8b3426b Binary files /dev/null and b/assets/sheet/CAIN-eye-small-0.png differ diff --git a/assets/sheet/CAIN-eye-small-1.png b/assets/sheet/CAIN-eye-small-1.png new file mode 100644 index 0000000..8b5766a Binary files /dev/null and b/assets/sheet/CAIN-eye-small-1.png differ diff --git a/assets/sheet/CAIN-eye-small-2.png b/assets/sheet/CAIN-eye-small-2.png new file mode 100644 index 0000000..8680691 Binary files /dev/null and b/assets/sheet/CAIN-eye-small-2.png differ diff --git a/assets/sheet/CAIN-eye-small-3.png b/assets/sheet/CAIN-eye-small-3.png new file mode 100644 index 0000000..b9a9bfe Binary files /dev/null and b/assets/sheet/CAIN-eye-small-3.png differ diff --git a/assets/sheet/CAIN-eye-small-4.png b/assets/sheet/CAIN-eye-small-4.png new file mode 100644 index 0000000..143c49f Binary files /dev/null and b/assets/sheet/CAIN-eye-small-4.png differ diff --git a/assets/sheet/CAIN-eye-small-5.png b/assets/sheet/CAIN-eye-small-5.png new file mode 100644 index 0000000..3467728 Binary files /dev/null and b/assets/sheet/CAIN-eye-small-5.png differ diff --git a/assets/sheet/CAIN-eye-small-6.png b/assets/sheet/CAIN-eye-small-6.png new file mode 100644 index 0000000..86f05da Binary files /dev/null and b/assets/sheet/CAIN-eye-small-6.png differ diff --git a/assets/sheet/CAIN-eye-small-closed.png b/assets/sheet/CAIN-eye-small-closed.png new file mode 100644 index 0000000..a833bd5 Binary files /dev/null and b/assets/sheet/CAIN-eye-small-closed.png differ diff --git a/assets/sheet/CAIN-sin-no.png b/assets/sheet/CAIN-sin-no.png new file mode 100644 index 0000000..8c88e14 Binary files /dev/null and b/assets/sheet/CAIN-sin-no.png differ diff --git a/assets/sin-marks/arm.png b/assets/sin-marks/arm.png new file mode 100644 index 0000000..a2a0203 Binary files /dev/null and b/assets/sin-marks/arm.png differ diff --git a/assets/sin-marks/back.png b/assets/sin-marks/back.png new file mode 100644 index 0000000..c9a1ea1 Binary files /dev/null and b/assets/sin-marks/back.png differ diff --git a/assets/sin-marks/chest.png b/assets/sin-marks/chest.png new file mode 100644 index 0000000..78c84e6 Binary files /dev/null and b/assets/sin-marks/chest.png differ diff --git a/assets/sin-marks/eye.png b/assets/sin-marks/eye.png new file mode 100644 index 0000000..6bfd0a3 Binary files /dev/null and b/assets/sin-marks/eye.png differ diff --git a/assets/sin-marks/hair.png b/assets/sin-marks/hair.png new file mode 100644 index 0000000..37aa4e1 Binary files /dev/null and b/assets/sin-marks/hair.png differ diff --git a/assets/sin-marks/hand.png b/assets/sin-marks/hand.png new file mode 100644 index 0000000..6254a2f Binary files /dev/null and b/assets/sin-marks/hand.png differ diff --git a/assets/sin-marks/jaw.png b/assets/sin-marks/jaw.png new file mode 100644 index 0000000..6d3b76f Binary files /dev/null and b/assets/sin-marks/jaw.png differ diff --git a/assets/sin-marks/leg.png b/assets/sin-marks/leg.png new file mode 100644 index 0000000..88eda2c Binary files /dev/null and b/assets/sin-marks/leg.png differ diff --git a/assets/sin-marks/skin.png b/assets/sin-marks/skin.png new file mode 100644 index 0000000..93fe59b Binary files /dev/null and b/assets/sin-marks/skin.png differ diff --git a/css/cain.css b/css/cain.css index fff7f5c..77b2d33 100644 --- a/css/cain.css +++ b/css/cain.css @@ -3,6 +3,9 @@ @import url('https://fonts.googleapis.com/css2?family=UnifrakturMaguntia&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Lacquer&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Tiny5&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&display=swap'); +@import url('./eye-anim.css'); +@import url('./glitch-effect.css'); /* .unifrakturmaguntia-regular { @@ -13,7 +16,9 @@ */ - +img { + border: none; +} /* Global styles */ .window-app { @@ -388,8 +393,11 @@ } /* New styles for checkbox group */ -.cain .checkbox-group_cat { +.cain .group_cat { + align-items: center; + justify-content: center; display: flex; + flex-wrap: wrap; flex-direction: row; gap: 10px; /* Adjust the gap between items as needed */ } @@ -401,13 +409,32 @@ text-align: center; } -.cain .checkbox-item img { - max-width: 45px; /* Adjust the image size as needed */ +.cain .checkbox-item_cat label { + margin-bottom: 5px; /* Space between label and checkbox */ +} + +.CAT-selector { + width: 45px; + height: 45px; + overflow: hidden; + cursor: pointer; margin-bottom: 5px; /* Space between image and label */ + padding: 1px; } -.cain .checkbox-item_cat label { - margin-bottom: 5px; /* Space between label and checkbox */ +.CAT-selector img { + width: 100%; + border: none !important; +} + +.CAT-selected{ + border: #ff3333 solid 1px; + padding: unset !important; +} + +.CAT-session-text{ + color: white; + width:100%; } .extra-dice-section { @@ -533,10 +560,132 @@ .agenda-background { background-color: #1a1a1a; - box-shadow: 10px 10px 0 rgba(0, 0, 0, 0.5), -10px -10px 0 rgba(0, 0, 0, 0.5), 10px -10px 0 rgba(0, 0, 0, 0.5), -10px 10px 0 rgba(0, 0, 0, 0.5);} + box-shadow: 10px 10px 0 rgba(0, 0, 0, 0.5), -10px -10px 0 rgba(0, 0, 0, 0.5), 10px -10px 0 rgba(0, 0, 0, 0.5), -10px 10px 0 rgba(0, 0, 0, 0.5); +} /* end agenda styles */ +/* homebrew styles */ +.homebrew-size { + font-size: 1.2em; + font-weight: bold; + margin: 0; + padding: 0; + height: 400px; +} + +.homebrew-name { + font-family: "Metal Mania", system-ui; + font-weight: 400; + font-style: normal; + font-weight: bold; + margin: 0; + padding: 0; +} + +/* homebrew Header */ +.homebrew-header { + background-color: #2b2b2b; + border-bottom: 2px solid #444; + padding: 1em; + display: flex; + align-items: center; +} + +.homebrew-header .profile-img { + width: 50px; + height: 50px; + border-radius: 50%; + margin-right: 1em; + border: 2px solid #555; +} + +.homebrew-header .header-fields { + flex-grow: 1; +} + +.homebrew-header .charname input { + font-size: 1.5em; + width: 100%; + background-color: #333; + color: #e0e0e0; + border: 1px solid #555; + padding: 0.5em; + border-radius: 4px; +} + +/* homebrew Tabs */ +.homebrew-tabs { + background-color: #2b2b2b; + border-bottom: 2px solid #444; + display: flex; + justify-content: space-around; + padding: 0.5em 0; +} + +.homebrew-tabs .item { + color: #e0e0e0; + padding: 0.5em 1em; + text-decoration: none; + border-bottom: 2px solid transparent; + transition: border-bottom 0.3s; +} + +.homebrew-tabs .item:hover, +.homebrew-tabs .item.active { + border-bottom: 2px solid #e0e0e0; +} + +/* homebrew Body */ +.homebrew-body { + background-color: #1a1a1a; + padding: 1em; +} + +.homebrew-body .tab { + display: none; +} + +.homebrew-body .tab.active { + display: block; +} + +.homebrew-body .form-group { + margin-bottom: 1em; +} + +.homebrew-body .form-group label { + display: block; + margin-bottom: 0.5em; + font-weight: bold; + color: #e0e0e0; +} + +.homebrew-body .form-group textarea { + width: 100%; + padding: 0.5em; + background-color: #333; + color: #e0e0e0; + border: 1px solid #555; + border-radius: 4px; + min-height: 100px; +} + +.homebrew-text-color { + color: #e0e0e0; +} + +.homebrew-background { + background-color: #1a1a1a; + box-shadow: 10px 10px 0 rgba(0, 0, 0, 0.5), -10px -10px 0 rgba(0, 0, 0, 0.5), 10px -10px 0 rgba(0, 0, 0, 0.5), -10px 10px 0 rgba(0, 0, 0, 0.5); +} + +.homebrew-toggle-bold { + width: 20%; +} +/* end homebrew styles */ + + /* Character Sheet */ .mob-psycho-theme { @@ -615,10 +764,8 @@ } .mob-psycho-header-fields { - background-color: #262626; padding: 10px; border-radius: 5px; - border: 1px dashed #00bfff; /* Dashed border for a report look */ } .mob-psycho-charname input { @@ -631,7 +778,6 @@ .mob-psycho-grid { color: #ffffff; gap: 10px; - border: 1px solid #00bfff; /* Jujutsu Kaisen inspired accent color */ padding: 10px; margin: 10px 0; } @@ -640,7 +786,6 @@ background-color: #333333; padding: 5px; border-radius: 5px; - border: 1px dashed #00bfff; /* Dashed border for a report look */ } .mob-psycho-label { @@ -697,8 +842,6 @@ .mob-psycho-tab { background-color: #0d0d0d; - padding: 10px; - border: 1px solid #00bfff; /* Jujutsu Kaisen inspired accent color */ } .mob-psycho-sidebar { @@ -719,7 +862,7 @@ background-color: #262626; padding: 5px; border-radius: 5px; - border: 1px dashed #00bfff; /* Dashed border for a report look */ + margin: 2px; /* Dashed border for a report look */ } .mob-psycho-extra-dice { @@ -730,17 +873,15 @@ } .mob-psycho-affliction { - background-color: #262626; padding: 5px; border-radius: 5px; - border: 1px dashed #00bfff; /* Dashed border for a report look */ } .mob-psycho-main { background-color: #1a1a1a; padding: 10px; border-radius: 5px; - border: 2px solid #00bfff; /* Jujutsu Kaisen inspired accent color */ + border: 1px dashed #00bfff; /* Jujutsu Kaisen inspired accent color */ } .mob-psycho-editor { @@ -825,6 +966,14 @@ color: #ff4d4d; /* Red color for delete icon */ } +.no-border{ + border: none !important; +} + +.no-padding{ + padding: unset !important; +} + .flavor-text { color: #ffffff; /* White text */ font-style: italic; @@ -956,20 +1105,31 @@ } +.sinOverflow-icon{ + border: none !important; +} + +/* sin-marks.css */ + /* sin-marks.css */ .sin-marks { - background-color: #2b2b2b; - border: 1px solid #444; - padding: 20px; - border-radius: 8px; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); + background-color: #1a1a1a; + border: 2px solid #660066; + padding: 30px; + border-radius: 12px; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.8); + font-family: 'Courier-New', serif; + color: #e0b3ff; + transition: transform 0.3s, box-shadow 0.3s; } .sin-marks h3 { - color: #ff6666; - font-family: 'Courier New', Courier, monospace; - text-shadow: 2px 2px 4px #000; + color: #cc33ff; + font-family: 'Courier-New', serif; + text-shadow: 3px 3px 6px #000; + font-size: 2em; + margin-bottom: 20px; } .sin-marks ul { @@ -977,108 +1137,142 @@ padding: 0; } -.sin-marks li { - background-color: #333; - border: 1px solid #555; - padding: 10px; - margin: 5px 0; - border-radius: 4px; - transition: background-color 0.3s; +.sin-mark-body-part { + background-color: #2b2b2b; + border: 2px solid #660066; + padding: 15px; + margin: 10px 0; + border-radius: 8px; + transition: transform 0.3s, box-shadow 0.3s; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); } .sin-marks li:hover { - background-color: #444; + box-shadow: 0 0 20px rgba(128, 0, 128, 0.8); +} + +.sin-marks li:active { + background-color: #1a1a1a; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.8); } .button-row { display: flex; justify-content: space-between; - margin-top: 10px; + margin-top: 20px; } .sin-marks button { - background-color: #ff6666; color: #fff; - border: none; - padding: 10px 20px; - margin: 5px; - border-radius: 4px; + border: 2px solid #660066;; + margin: 0px; cursor: pointer; - transition: background-color 0.3s; + font-family: 'Courier-New', serif; + transition: background-color 0.3s, transform 0.3s, box-shadow 0.3s; } .sin-marks button:hover { - background-color: #ff3333; + background-color: #b300b3; + box-shadow: 0 0 20px rgba(128, 0, 128, 0.8); } .sin-marks button:active { - background-color: #cc0000; + background-color: #990099; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.8); } .no-sin-marks { - color: #e0e0e0; - background-color: #333; - padding: 10px; - border-radius: 4px; - margin: 10px 0; - font-style: italic; -} -.sin-overflow { + color: #e0b3ff; background-color: #2b2b2b; - border: 1px solid #444; - padding: 20px; + padding: 15px; border-radius: 8px; + margin: 20px 0; + font-style: italic; + border: 2px solid #660066; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); - margin-bottom: 10px; + transition: transform 0.3s, box-shadow 0.3s; +} + +.no-sin-marks:hover { + transform: scale(1.05); + box-shadow: 0 0 20px rgba(128, 0, 128, 0.8); +} + +.sin-overflow { + background-color: #1a1a1a; + border: 2px solid #660066; + padding: 30px; + border-radius: 12px; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.8); + font-family: 'Courier-New', serif; + color: #e0b3ff; + transition: transform 0.3s, box-shadow 0.3s; + margin-bottom: 20px; } .sin-overflow h3 { - color: #ff6666; - font-family: 'Courier New', Courier, monospace; - text-shadow: 2px 2px 4px #000; + color: #cc33ff; + font-family: 'Courier-New', serif; + text-shadow: 3px 3px 6px #000; + font-size: 2em; + margin-bottom: 20px; } .sin-overflow-controls { display: flex; flex-direction: column; - gap: 10px; + gap: 15px; } .sin-overflow-controls label { - color: #e0e0e0; + color: #e0b3ff; + font-family: 'Courier-New', serif; } .sin-overflow-controls input { + background-color: #2b2b2b; + color: #e0b3ff; + border: 2px solid #660066; + padding: 10px; + border-radius: 8px; + font-family: 'Courier-New', serif; + transition: background-color 0.3s, box-shadow 0.3s; +} + +.sin-overflow-controls input:hover { background-color: #333; - color: #fff; - border: 1px solid #555; - padding: 5px; - border-radius: 4px; + box-shadow: 0 0 10px rgba(128, 0, 128, 0.8); } .sin-overflow-buttons { display: flex; justify-content: space-between; - margin-top: 10px; + margin-top: 20px; } .sin-overflow-buttons button { - background-color: #ff6666; + background-color: #cc33ff; color: #fff; - border: none; - padding: 10px 20px; + border: 2px solid #660066; + padding: 12px 24px; margin: 5px; - border-radius: 4px; + border-radius: 8px; cursor: pointer; - transition: background-color 0.3s; + font-family: 'Courier-New', serif; + transition: background-color 0.3s, transform 0.3s, box-shadow 0.3s; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); } .sin-overflow-buttons button:hover { - background-color: #ff3333; + background-color: #b300b3; + transform: scale(1.1); + box-shadow: 0 0 20px rgba(128, 0, 128, 0.8); } .sin-overflow-buttons button:active { - background-color: #cc0000; + background-color: #990099; + transform: scale(0.95); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.8); } /* Media query for responsive design */ @@ -1272,3 +1466,145 @@ filter: brightness(0.8); /* Adjust the brightness to indicate the button has been clicked */ } +.form-blasphemy-power { + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + height: 100%; + border-radius: 15px; + padding: 25px; + gap: 25px; + box-shadow: 0 0 15px var(--accent-color, #ff00ff), 0 0 30px var(--accent-color, #ff00ff); + font-family: 'Lacquer', sans-serif; + background: linear-gradient(135deg, var(--primary-color, #2a2a2a), var(--secondary-color, #555)); + color: var(--text-color, #ffffff); + margin: 0; + border: 3px solid var(--secondary-color, #555); + overflow-y: auto; + position: relative; + width: 100%; +} + +.form-blasphemy-power label { + font-weight: bold; + color: var(--text-color, #ffffff); + text-decoration: underline; +} + +.blasphemy-power-header { + display: flex; + align-items: flex-start; + gap: 20px; + width: 100%; + border-bottom: 2px solid var(--secondary-color, #555); + padding-bottom: 10px; + overflow: visible; /* Override the parent overflow */ + background: linear-gradient(135deg, var(--accent-color, #ff00ff), var(--primary-color, #2a2a2a)); + padding: 10px; + border-radius: 10px; + width: 100%; +} + +.form-blasphemy-power .header-fields { + display: flex; + flex-direction: column; + width: 100%; +} + +.form-blasphemy-power .form-group { + display: flex; + flex-direction: column; + gap: 5px; + width: 100%; +} + +.form-blasphemy-power .charname { + font-size: 24px; + font-weight: bold; +} + +.form-blasphemy-power .profile-img { + border: 3px solid var(--accent-color, #ff00ff); + border-radius: 50%; + width: 80px; + height: 80px; + object-fit: cover; + box-shadow: 0 0 10px var(--accent-color, #ff00ff), 0 0 20px var(--accent-color, #ff00ff); + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.form-blasphemy-power .profile-img:hover { + transform: scale(1.1); + box-shadow: 0 0 20px var(--accent-color, #ff00ff), 0 0 40px var(--accent-color, #ff00ff); +} + +.form-blasphemy-power input[type="text"], +.form-blasphemy-power input[type="checkbox"] { + background-color: var(--primary-color, #2a2a2a); + color: var(--text-color, #f0f0f0); + border: 2px solid var(--secondary-color, #333); + border-radius: 5px; + padding: 5px; + font-family: 'Lacquer', sans-serif; + font-size: 16px; + font-weight: 400; +} + +.form-blasphemy-power input[type="checkbox"] { + width: auto; + height: auto; +} + +.form-blasphemy-power .blasphemy-power-body { + flex-grow: 1; + overflow-y: auto; + width: 100%; + max-height: 600px; + text-align: center; /* Center text in the sheet body */ + background: linear-gradient(135deg, var(--primary-color, #2a2a2a), var(--secondary-color, #555)); + padding: 20px; + border-radius: 10px; +} + +.form-blasphemy-power .blasphemy-power-body h2 { + text-align: center; /* Center the Description heading */ + color: var(--accent-color, #ff00ff); + text-shadow: 0 0 10px var(--accent-color, #ff00ff); +} + +.form-blasphemy-power .blasphemy-power-body p { + text-align: center; + padding: 10px; + border: 2px solid var(--secondary-color, #555); + border-radius: 10px; + background-color: var(--primary-color, #2a2a2a); + font-family: 'Courier New', Courier, monospace; + color: var(--text-color, #ffffff); + box-shadow: 0 0 10px var(--accent-color, #ff00ff); +} + +.form-blasphemy-power .blasphemy-power-body img { + max-width: 100%; + border-radius: 10px; + box-shadow: 0 0 10px var(--accent-color, #ff00ff); +} + +.form-blasphemy-power .er h1 { + font-size: 24px; + font-weight: bold; + color: var(--accent-color, #ff00ff); +} + +.form-blasphemy-power .blasphemy-power-header h2 { + font-size: 20px; + font-weight: bold; + color: var(--accent-color, #ff00ff); +} + +.form-blasphemy-power .power-label { + margin-left: 10px; + font-size: 18px; + font-weight: bold; + color: var(--text-color, #ff00ff); +} diff --git a/css/eye-anim.css b/css/eye-anim.css new file mode 100644 index 0000000..b4a490c --- /dev/null +++ b/css/eye-anim.css @@ -0,0 +1,83 @@ +.sinOverflow-icon-anim{ + width: 45px; + height: 45px; + border: none !important; +} + +.sin-icon-anim-0{ + animation: eyeAnim-0 5s infinite; +} + +.sin-icon-anim-1{ + animation: eyeAnim-1 5.5s infinite; +} + +.sin-icon-anim-2{ + animation: eyeAnim-2 4.7s infinite; +} + +.sin-icon-anim-3{ + animation: eyeAnim-3 4.5s infinite; +} + +.sin-icon-anim-4{ + animation: eyeAnim-4 6s infinite; +} + +.sin-icon-anim-5{ + animation: eyeAnim-5 5.4s infinite; +} + +.sin-icon-anim-6{ + animation: eyeAnim-6 4.3s infinite; +} + +@keyframes eyeAnim-0 { + 0%, 7%, 100% {content:url("../assets/sheet/CAIN-eye-0-0.png");} + 1% {content:url("../assets/sheet/CAIN-eye-0-1.png");} + 3% {content:url("../assets/sheet/CAIN-eye-closed.png");} + 5% {content:url("../assets/sheet/CAIN-eye-0-1.png");} +} + +@keyframes eyeAnim-1 { + 0%, 20%, 27%, 100% {content:url("../assets/sheet/CAIN-eye-1-0.png");} + 21% {content:url("../assets/sheet/CAIN-eye-1-1.png");} + 23% {content:url("../assets/sheet/CAIN-eye-closed.png");} + 25% {content:url("../assets/sheet/CAIN-eye-1-1.png");} +} + +@keyframes eyeAnim-2 { + 0%, 71%, 78%, 100% {content:url("../assets/sheet/CAIN-eye-2-0.png");} + 72% {content:url("../assets/sheet/CAIN-eye-2-1.png");} + 74% {content:url("../assets/sheet/CAIN-eye-closed.png");} + 76% {content:url("../assets/sheet/CAIN-eye-2-1.png");} +} + +@keyframes eyeAnim-3 { + 0%, 90%, 97%, 100% {content:url("../assets/sheet/CAIN-eye-3-0.png");} + 91% {content:url("../assets/sheet/CAIN-eye-3-1.png");} + 93% {content:url("../assets/sheet/CAIN-eye-closed.png");} + 95% {content:url("../assets/sheet/CAIN-eye-3-1.png");} +} + +@keyframes eyeAnim-4 { + 0%, 9%, 16%, 100% {content:url("../assets/sheet/CAIN-eye-4-0.png");} + 10% {content:url("../assets/sheet/CAIN-eye-4-1.png");} + 12% {content:url("../assets/sheet/CAIN-eye-closed.png");} + 14% {content:url("../assets/sheet/CAIN-eye-4-1.png");} +} + +@keyframes eyeAnim-5 { + 0%, 50%, 57%, 100% {content:url("../assets/sheet/CAIN-eye-5-0.png");} + 51% {content:url("../assets/sheet/CAIN-eye-5-1.png");} + 53% {content:url("../assets/sheet/CAIN-eye-closed.png");} + 55% {content:url("../assets/sheet/CAIN-eye-5-1.png");} +} + +@keyframes eyeAnim-6 { + 0%, 33%, 40%, 100% {content:url("../assets/sheet/CAIN-eye-6-0.png");} + 34% {content:url("../assets/sheet/CAIN-eye-6-1.png");} + 36% {content:url("../assets/sheet/CAIN-eye-closed.png");} + 38% {content:url("../assets/sheet/CAIN-eye-6-1.png");} +} + diff --git a/css/glitch-effect.css b/css/glitch-effect.css new file mode 100644 index 0000000..302f89e --- /dev/null +++ b/css/glitch-effect.css @@ -0,0 +1,161 @@ +.glitch { + position: relative; + margin: 0 auto; + background-color: inherit; + } + .glitch::before, .glitch::after { + animation-iteration-count: infinite; + animation-timing-function: linear; + animation-direction: alternate-reverse; + overflow: hidden; + position: absolute; + top: 2; + clip: rect(0, 900px, 0, 0); + content: attr(data-text); + } + .glitch::after { + animation-name: glitch-animation; + animation-duration: 4s; + left: 0px; + text-shadow: -1px 0 #ffa800; + background-color: inherit; + } + .glitch::before { + animation-name: glitch-animation-2; + animation-duration: 6s; + left: -4px; + text-shadow: 1px 0 #00d8ff; + background-color: inherit; + } + /* Expanded Animations */ + @keyframes glitch-animation { + 0% { + clip: rect(42px, 9999px, 44px, 0); + } + 5% { + clip: rect(12px, 9999px, 59px, 0); + } + 10% { + clip: rect(48px, 9999px, 29px, 0); + } + 15% { + clip: rect(42px, 9999px, 73px, 0); + } + 20% { + clip: rect(63px, 9999px, 27px, 0); + } + 25% { + clip: rect(34px, 9999px, 55px, 0); + } + 30% { + clip: rect(86px, 9999px, 73px, 0); + } + 35% { + clip: rect(20px, 9999px, 20px, 0); + } + 40% { + clip: rect(26px, 9999px, 60px, 0); + } + 45% { + clip: rect(25px, 9999px, 66px, 0); + } + 50% { + clip: rect(57px, 9999px, 98px, 0); + } + 55% { + clip: rect(5px, 9999px, 46px, 0); + } + 60% { + clip: rect(82px, 9999px, 31px, 0); + } + 65% { + clip: rect(54px, 9999px, 27px, 0); + } + 70% { + clip: rect(28px, 9999px, 99px, 0); + } + 75% { + clip: rect(45px, 9999px, 69px, 0); + } + 80% { + clip: rect(23px, 9999px, 85px, 0); + } + 85% { + clip: rect(54px, 9999px, 84px, 0); + } + 90% { + clip: rect(45px, 9999px, 47px, 0); + } + 95% { + clip: rect(37px, 9999px, 20px, 0); + } + 100% { + clip: rect(4px, 9999px, 91px, 0); + } + } + @keyframes glitch-animation-2 { + 0% { + clip: rect(65px, 9999px, 100px, 0); + } + 5% { + clip: rect(52px, 9999px, 74px, 0); + } + 10% { + clip: rect(79px, 9999px, 85px, 0); + } + 15% { + clip: rect(75px, 9999px, 5px, 0); + } + 20% { + clip: rect(67px, 9999px, 61px, 0); + } + 25% { + clip: rect(14px, 9999px, 79px, 0); + } + 30% { + clip: rect(1px, 9999px, 66px, 0); + } + 35% { + clip: rect(86px, 9999px, 30px, 0); + } + 40% { + clip: rect(23px, 9999px, 98px, 0); + } + 45% { + clip: rect(85px, 9999px, 72px, 0); + } + 50% { + clip: rect(71px, 9999px, 75px, 0); + } + 55% { + clip: rect(2px, 9999px, 48px, 0); + } + 60% { + clip: rect(30px, 9999px, 16px, 0); + } + 65% { + clip: rect(59px, 9999px, 50px, 0); + } + 70% { + clip: rect(41px, 9999px, 62px, 0); + } + 75% { + clip: rect(2px, 9999px, 82px, 0); + } + 80% { + clip: rect(47px, 9999px, 73px, 0); + } + 85% { + clip: rect(3px, 9999px, 27px, 0); + } + 90% { + clip: rect(26px, 9999px, 55px, 0); + } + 95% { + clip: rect(42px, 9999px, 97px, 0); + } + 100% { + clip: rect(38px, 9999px, 49px, 0); + } + } + \ No newline at end of file diff --git a/lang/en.json b/lang/en.json index 0467456..95cce67 100644 --- a/lang/en.json +++ b/lang/en.json @@ -48,7 +48,26 @@ "Item": { "item": "Item", "agenda": "Agenda", - "blasphemy": "Blasphemy" + "blasphemy": "Blasphemy", + "blasphemyPower": "Blasphemy Power", + "agendaTask": "Agenda Task", + "agendaAbility": "Agenda Ability", + "sinMark": "Sin Mark", + "sinMarkAbility": "Sin Mark Ability", + "affliction": "Affliction" + } + }, + + "SHEET": { + "SIN": { + "ROLLSIN": "Roll SIN (1D3)", + "ROLLRESIST": "Resist Roll (1D6 + SIN MARKS)", + "ADJUSTMAX": "Adjust maximum" + }, + "TOOLTIPS": { + "SURVIVED": "Missions Survived", + "CATSELECT": "Right click to set CAT 0", + "SINMAXADJUST": "Change maximum SIN capacity" } } } diff --git a/module/cain.mjs b/module/cain.mjs index 8994dc1..ca5dbe1 100644 --- a/module/cain.mjs +++ b/module/cain.mjs @@ -2,6 +2,7 @@ import { CainActor } from './documents/actor.mjs'; import { CainItem } from './documents/item.mjs'; import { TalismanWindow } from './documents/talisman-window.mjs'; +import { HomebrewWindow } from './documents/homebrew-window.mjs'; // Import sheet classes. import { CainActorSheet } from './sheets/actor-sheet.mjs'; import { CainItemSheet } from './sheets/item-sheet.mjs'; @@ -17,7 +18,7 @@ import * as models from './data/_module.mjs'; /* Init Hook */ /* -------------------------------------------- */ -Hooks.once('init', function () { +Hooks.once('init', async function () { // Add utility classes to the global game object so that they're more easily // accessible in global contexts. game.cain = { @@ -54,8 +55,14 @@ Hooks.once('init', function () { item: models.CainItem, agenda: models.CainAgenda, blasphemy: models.CainBlasphemy, + blasphemyPower: models.CainBlasphemyPower, + agendaTask: models.CainAgendaTask, + agendaAbility: models.CainAgendaAbility, + sinMark: models.CainSinMark, + sinMarkAbility: models.CainSinMarkAbility, + affliction: models.CainAffliction, } - + console.log('CAIN | Initializing Cain system'); console.log(CONFIG) // Active Effects are never copied to the Actor, @@ -104,6 +111,26 @@ Hooks.once('init', function () { } }); + game.settings.register('cain', 'developerMode', { + name: 'Enable Developer Mode', + hint: 'Shows a lot of ugly debug information that allows direct modification of values.', + scope: 'world', + config: true, + type: Boolean, + default: false, + onChange: value => { + ui.players.render(); + } + }); + + const blasphemyPowerTemplate = await getTemplate("systems/cain/templates/item/parts/item-blasphemy-power-sheet.hbs"); + const blasphemyPowerPartialTemplate = await getTemplate("systems/cain/templates/item/parts/item-blasphemy-power-partial.hbs"); + const sinMarkAbilityTemplate = await getTemplate("systems/cain/templates/item/parts/item-sin-mark-partial.hbs"); + + Handlebars.registerPartial("sinMarkAbility", sinMarkAbilityTemplate); + Handlebars.registerPartial("blasphemyPower", blasphemyPowerTemplate); + Handlebars.registerPartial("blasphemyPowerPartial", blasphemyPowerPartialTemplate); + // Preload Handlebars templates. return preloadHandlebarsTemplates(); }); @@ -149,6 +176,146 @@ Handlebars.registerHelper('times', function(n, block) { return accum; }); +Handlebars.registerHelper('formatted', function(text, category) { + // console.log(category); + const categoryTable = [ + { + 'CAT': 0, + 'people': 'one', + 'size': 'human', + 'area': 'personal', + 'range': 'touch', + 'speed': 'average human', + 'magnitude': 'small' + }, + { + 'CAT': 1, + 'people': 'a few', + 'size': 'heavy furniture', + 'area': 'a few people', + 'range': 'same room', + 'speed': 'fast human', + 'magnitude': 'Noticable' + }, + { + 'CAT': 2, + 'people': 'small group', + 'size': 'large animal', + 'area': 'entire room', + 'range': 'accross the street', + 'speed': 'fast animal', + 'magnitude': 'large' + }, + { + 'CAT': 3, + 'people': 'large group', + 'size': 'vehicle', + 'area': 'few rooms', + 'range': 'down the block', + 'speed': 'car', + 'magnitude': 'very large' + }, + { + 'CAT': 4, + 'people': 'a crowd', + 'size': 'large vehicle', + 'area': 'whole building', + 'range': 'a few blocks away', + 'speed': 'train', + 'magnitude': 'massive' + }, + { + 'CAT': 5, + 'people': 'a huge crowd', + 'size': 'building', + 'area': 'city block', + 'range': 'across town', + 'speed': 'maglev', + 'magnitude': 'destructive' + }, + { + 'CAT': 6, + 'people': 'thousands', + 'size': 'large building', + 'area': 'whole neighborhood', + 'range': 'visual range', + 'speed': 'airliner', + 'magnitude': 'overwhelming' + }, + { + 'CAT': 7, + 'people': 'many thousands', + 'size': 'skyscraper', + 'area': 'whole town', + 'range': 'over the horizon', + 'speed': 'jet fighter', + 'magnitude': 'cataclysmic' + } + ] + // Check if the text is defined and is a string + let parse_cat_values = (inputString => { + const regex = /\{\s+(\S+)\s+(\S+)\}/g; + + const matches = [...inputString.matchAll(regex)]; + + return matches.map(match => ({ + string: Handlebars.escapeExpression(match[0]), + type: match[1], + modifier: match[2] + })); + }); + + + + if (typeof text === 'string') { + const CatFormattingData = parse_cat_values(text); + //TODO: fix hardcoded category limits - it'd be nice to have the option to expand Category beyond 0-7 + let updatedText = Handlebars.escapeExpression(text); + if (isNaN(category) || Number(category) < 0 || Number(category) > 7) { + CatFormattingData.forEach(catData => { + const replacementString = ` CAT${(catData.modifier <= 0 ? '' : '+') + (catData.modifier == 0 ? '' : catData.modifier)}`; + updatedText = updatedText.replace(catData.string, replacementString) + }) + } else { + CatFormattingData.forEach(catData => { + const catIndex = Math.max(Math.min(Number(category) + Number(catData.modifier), 7), 0); + const replacementString = ` ${categoryTable[catIndex][catData.type]} `; + console.log(replacementString); + updatedText = updatedText.replace(catData.string, replacementString) + }) + } + + //Allow user text to have bolds and italics because they won't pose security issues. + updatedText = updatedText.split(Handlebars.escapeExpression("")).join(""); + updatedText = updatedText.split(Handlebars.escapeExpression("")).join(""); + updatedText = updatedText.split(Handlebars.escapeExpression("")).join(""); + updatedText = updatedText.split(Handlebars.escapeExpression("")).join(""); + + // Replace all newlines with
tags + return new Handlebars.SafeString(updatedText.replace(/\n/g, '
')); + } else { + return text; // Return the text as is if it's not a string + } +}); + +Handlebars.registerHelper('mod', function(value, modval, options){ + if(value===undefined || modval===undefined || parseInt(value) === NaN || !parseInt(modval) === NaN){ + throw new Error(`Mod helper did not receive a number: val=${value}, modval=${modval}`); + } + return parseInt(value) % parseInt(modval) +}); + + +Handlebars.registerHelper('json', function(context) { + return JSON.stringify(context); +}); + +Handlebars.registerHelper('CainOffset', function(value, offset, options) { + if(value===undefined || offset===undefined || parseInt(value)===NaN || !parseInt(offset) === NaN){ + throw new Error(`offset helper did not receive a number: val=${value}, offset=${offset}`); + } + return parseInt(value) + parseInt(offset); +}); /* -------------------------------------------- */ /* Ready Hook */ @@ -201,6 +368,30 @@ Hooks.once('ready', function () { } } + function addHomebrewButton() { + // Create the button element with the talisman icon + if (!game.user.isGM) return; + const button = $(''); + + // Add click event to open the TalismanWindow + button.on('click', () => { + new HomebrewWindow().render(true); + }); + + // Create an aside element and append the button to it + const aside = $('').append(button); + + // Insert the aside element into the action bar + const actionBar = $('#action-bar'); + if (actionBar.length) { + actionBar.append(aside); + console.log('Homebrew button inserted successfully.'); + } else { + console.error('Action bar not found.'); + } + } + + // Function to create and insert the Risk Roll button function addRiskRollButton() { if (!game.user.isGM) return; @@ -241,12 +432,14 @@ Hooks.once('ready', function () { // Add the Risk Roll and Fate Roll buttons when the action bar is first ready addRiskRollButton(); addFateRollButton(); + addHomebrewButton(); // Ensure the buttons are added every time the action bar is rendered Hooks.on('renderHotbar', () => { addTalismanButton(); addRiskRollButton(); addFateRollButton(); + addHomebrewButton(); }); // Register hotbar drop hook diff --git a/module/data/_module.mjs b/module/data/_module.mjs index 0d18481..78c9ea0 100644 --- a/module/data/_module.mjs +++ b/module/data/_module.mjs @@ -10,6 +10,11 @@ export {default as CainItem} from "./item-item.mjs"; export {default as CainFeature} from "./item-feature.mjs"; export {default as CainSpell} from "./item-spell.mjs"; export {default as CainSins} from "./item-sins.mjs"; -export {default as CainAgenda} from "./item-agenda.mjs"; -export {default as CainBlasphemy} from "./item-blasphemy.mjs"; - +export {default as CainAgenda} from "./agenda/item-agenda.mjs"; +export {default as CainAgendaTask} from "./agenda/item-agenda-task.mjs"; +export {default as CainAgendaAbility} from "./agenda/item-agenda-ability.mjs"; +export {default as CainBlasphemy} from "./blasphemy/item-blasphemy.mjs"; +export {default as CainBlasphemyPower} from "./blasphemy/item-blasphemy-power.mjs"; +export {default as CainSinMark} from "./sins/item-sin-mark.mjs"; +export {default as CainSinMarkAbility} from "./sins/item-sin-mark-ability.mjs"; +export {default as CainAffliction} from "./afflictions/item-affliction.mjs"; diff --git a/module/data/actor-character.mjs b/module/data/actor-character.mjs index 97ba95d..0449fb2 100644 --- a/module/data/actor-character.mjs +++ b/module/data/actor-character.mjs @@ -34,8 +34,8 @@ export default class CainCharacter extends CainActorBase { // Add XP and advancements fields schema.xp = new fields.SchemaField({ - value: new fields.NumberField({ required: true, initial: 0, min: 0, max: 4 }), - max: new fields.NumberField({ required: true, initial: 4, min: 4, max: 4 }), + value: new fields.NumberField({ required: true, initial: 0, min: 0, max: 10 }), + max: new fields.NumberField({ required: true, initial: 4, min: 4, max: 10 }), }); schema.advancements = new fields.SchemaField({ @@ -46,16 +46,10 @@ export default class CainCharacter extends CainActorBase { schema.scrip = new fields.NumberField({ required: true, initial: 0, min: 0, max: 1000 }); schema.CATLEVEL = new fields.SchemaField({ - value: new fields.NumberField({ required: true, initial: 0, min: 0, max: 7 }), + value: new fields.NumberField({ required: true, initial: 1, min: 0, max: 7 }), }); - schema.afflictions = new fields.SchemaField({ - affliction1: new fields.StringField({ required: true, blank: true }), - affliction2: new fields.StringField({ required: true, blank: true }), - affliction3: new fields.StringField({ required: true, blank: true }), - affliction4: new fields.StringField({ required: true, blank: true }), - affliction5: new fields.StringField({ required: true, blank: true }), - }); + schema.afflictions = new fields.ArrayField(new fields.StringField({ required: true, initial: " " }), { required: true, initial: [] }); schema.divineAgony = new fields.SchemaField({ value: new fields.NumberField({ required: true, initial: 0, min: 0, max: 3 }), @@ -95,7 +89,7 @@ export default class CainCharacter extends CainActorBase { schema.kitPoints = new fields.SchemaField({ value: new fields.NumberField({ required: true, initial: 5, min: 0, max: 10 }), - max: new fields.NumberField({ required: true, initial: 10, min: 0, max: 10 }), + max: new fields.NumberField({ required: true, initial: 5, min: 0, max: 10 }), }); // Add psyche field @@ -108,15 +102,16 @@ export default class CainCharacter extends CainActorBase { schema.restDiceModifier = new fields.NumberField({ required: true, initial: 0, min: -3, max: 3 }); - schema.currentAgendaItems = new fields.ArrayField(new fields.SchemaField({ - text: new fields.StringField({ required: true, initial: " " }), - isBold: new fields.BooleanField({ required: true, initial: false }) - }), { required: true, initial: [] }); - - schema.currentAgendaAbilities = new fields.ArrayField(new fields.SchemaField({ - text: new fields.StringField({ required: true, initial: " " }), - isBold: new fields.BooleanField({ required: true, initial: false }) - }), { required: true, initial: [] }); + schema.currentBoldedAgendaTasks = new fields.ArrayField(new fields.StringField(), { required: true, initial: [] }); + schema.currentUnboldedAgendaTasks = new fields.ArrayField(new fields.StringField(), { required: true, initial: [] }); + schema.currentAgendaAbilities = new fields.ArrayField(new fields.StringField(), { required: true, initial: [] }); + schema.currentAgenda = new fields.StringField({required: true, nullable: false, initial: "INVALID"}); + + schema.currentBlasphemies = new fields.ArrayField(new fields.StringField(), { required: true, initial: [] }); + schema.currentBlasphemyPowers = new fields.ArrayField(new fields.StringField(), { required: true, initial: [] }); + + schema.sinMarks = new fields.ArrayField(new fields.StringField(), { required: true, initial: [] }); + schema.sinMarkAbilities = new fields.ArrayField(new fields.StringField(), { required: true, initial: [] }); return schema; } diff --git a/module/data/afflictions/item-affliction.mjs b/module/data/afflictions/item-affliction.mjs new file mode 100644 index 0000000..361a16f --- /dev/null +++ b/module/data/afflictions/item-affliction.mjs @@ -0,0 +1,18 @@ +import CainItemBase from "../base-item.mjs"; + +export default class CainAffliction extends CainItemBase { + static defineSchema() { + const fields = foundry.data.fields; + const requiredInteger = { required: true, nullable: false, integer: true }; + const schema = super.defineSchema(); + + + schema.afflictionName = new fields.StringField({ required: true, nullable: false, initial: "New Affliction" }); + schema.afflictionDescription = new fields.StringField({required: true, nullable: false, initial: "Affliction Description"}); + + + schema.formula = new fields.StringField({ blank: true }); + + return schema; + } +} diff --git a/module/data/agenda/item-agenda-ability.mjs b/module/data/agenda/item-agenda-ability.mjs new file mode 100644 index 0000000..1f514ba --- /dev/null +++ b/module/data/agenda/item-agenda-ability.mjs @@ -0,0 +1,18 @@ +import CainItemBase from "../base-item.mjs"; + +export default class CainAgendaAbility extends CainItemBase { + static defineSchema() { + const fields = foundry.data.fields; + const requiredInteger = { required: true, nullable: false, integer: true }; + const schema = super.defineSchema(); + + + schema.abilityName = new fields.StringField({ required: true, nullable: false, initial: "New Ability" }); + schema.abilityDescription = new fields.StringField({required: true, nullable: false, initial: "Ability Description"}); + + + schema.formula = new fields.StringField({ blank: true }); + + return schema; + } +} diff --git a/module/data/agenda/item-agenda-task.mjs b/module/data/agenda/item-agenda-task.mjs new file mode 100644 index 0000000..afd1058 --- /dev/null +++ b/module/data/agenda/item-agenda-task.mjs @@ -0,0 +1,21 @@ +import CainItemBase from "../base-item.mjs"; + +export default class CainAgendaTask extends CainItemBase { + static defineSchema() { + const fields = foundry.data.fields; + const requiredInteger = { required: true, nullable: false, integer: true }; + const schema = super.defineSchema(); + + + schema.task = new fields.StringField({ required: true, nullable: false, initial: "New Task" }); + schema.isBold = new fields.BooleanField({ required: true, nullable: false, initial: false}); + + + schema.formula = new fields.StringField({ blank: true }); + return schema; + } + + get template() { + return `systems/cain/templates/item/agendas/item-agenda-sheet.hbs`; + } +} diff --git a/module/data/item-agenda.mjs b/module/data/agenda/item-agenda.mjs similarity index 58% rename from module/data/item-agenda.mjs rename to module/data/agenda/item-agenda.mjs index cfcfae2..e466970 100644 --- a/module/data/item-agenda.mjs +++ b/module/data/agenda/item-agenda.mjs @@ -1,20 +1,18 @@ -import CainItemBase from "./base-item.mjs"; +import CainItemBase from "../base-item.mjs"; +import CainAgendaTask from "./item-agenda-task.mjs"; export default class CainAgenda extends CainItemBase { - static defineSchema() { const fields = foundry.data.fields; const requiredInteger = { required: true, nullable: false, integer: true }; const schema = super.defineSchema(); - schema.agendaName = new fields.StringField({ required: true, nullable: false, initial: "New Agenda" }); - schema.agendaItemChecklist = new fields.StringField({ required: true, nullable: false, initial: "New Agenda" }); - schema.abilityChecklist = new fields.StringField({ required: true, nullable: false, initial: "New Agenda" }); - schema.formula = new fields.StringField({ blank: true }); - + schema.unboldedTasks = new fields.ArrayField(new fields.StringField()); + schema.boldedTasks = new fields.ArrayField(new fields.StringField()); + schema.abilities = new fields.ArrayField(new fields.StringField()); + return schema; } - } \ No newline at end of file diff --git a/module/data/base-item.mjs b/module/data/base-item.mjs index 2aac4bd..b240cb8 100644 --- a/module/data/base-item.mjs +++ b/module/data/base-item.mjs @@ -8,6 +8,12 @@ export default class CainItemBase extends CainDataModel { schema.description = new fields.StringField({ required: true, blank: true }); + // Set default color scheme to a dark theme + schema.primaryColor = new fields.StringField({ required: false, nullable: false, initial: "#2a2a2a" }); // Dark Gray + schema.accentColor = new fields.StringField({ required: false, nullable: false, initial: "#ff00ff" }); // Magenta + schema.secondaryColor = new fields.StringField({ required: false, nullable: false, initial: "#555555" }); // Medium Gray + schema.textColor = new fields.StringField({ required: false, nullable: false, initial: "#ffffff" }); // White + return schema; } diff --git a/module/data/blasphemy/item-blasphemy-power.mjs b/module/data/blasphemy/item-blasphemy-power.mjs new file mode 100644 index 0000000..2827214 --- /dev/null +++ b/module/data/blasphemy/item-blasphemy-power.mjs @@ -0,0 +1,18 @@ +import CainItemBase from "../base-item.mjs"; + +export default class CainBlasphemyPower extends CainItemBase { + static defineSchema() { + const fields = foundry.data.fields; + const requiredInteger = { required: true, nullable: false, integer: true }; + const schema = super.defineSchema(); + + + schema.blasphemyType = new fields.StringField({ required: true, nullable: false, initial: "New Blasphemy" }); + schema.powerName = new fields.StringField({ required: true, nullable: false, initial: "Blasphemy Power Name" }); + schema.isPassive = new fields.BooleanField({ required: true, nullable: false, initial: false }); + schema.keywords = new fields.ArrayField(new fields.StringField(), { required: true, nullable: false, initial: [] }); + schema.powerDescription = new fields.StringField({ required: true, nullable: false, initial: "default" }); + + return schema; + } +} \ No newline at end of file diff --git a/module/data/blasphemy/item-blasphemy.mjs b/module/data/blasphemy/item-blasphemy.mjs new file mode 100644 index 0000000..567a33a --- /dev/null +++ b/module/data/blasphemy/item-blasphemy.mjs @@ -0,0 +1,14 @@ +import CainItemBase from "../base-item.mjs"; + +export default class CainBlasphemy extends CainItemBase { + static defineSchema() { + const fields = foundry.data.fields; + const requiredInteger = { required: true, nullable: false, integer: true }; + const schema = super.defineSchema(); + + schema.blasphemyName = new fields.StringField({ required: true, nullable: false, initial: "default" }); + schema.powers = new fields.ArrayField(new fields.StringField(), { required: true, nullable: false, initial: [] }); + + return schema; + } +} \ No newline at end of file diff --git a/module/data/item-blasphemy.mjs b/module/data/item-blasphemy.mjs deleted file mode 100644 index e01f3c3..0000000 --- a/module/data/item-blasphemy.mjs +++ /dev/null @@ -1,20 +0,0 @@ -import CainItemBase from "./base-item.mjs"; - -export default class CainBlasphemy extends CainItemBase { - static defineSchema() { - const fields = foundry.data.fields; - const requiredInteger = { required: true, nullable: false, integer: true }; - const schema = super.defineSchema(); - - - schema.classType = new fields.StringField({ required: true, nullable: false, initial: "default" }); - schema.isPassive = new fields.BooleanField({ required: true, nullable: false, initial: false }); - schema.keywords = new fields.StringField({ required: true, nullable: false, initial: "" }); - - - return schema; - } - - prepareDerivedData() { - } -} \ No newline at end of file diff --git a/module/data/sins/item-sin-mark-ability.mjs b/module/data/sins/item-sin-mark-ability.mjs new file mode 100644 index 0000000..8f153b6 --- /dev/null +++ b/module/data/sins/item-sin-mark-ability.mjs @@ -0,0 +1,23 @@ +import CainItemBase from "../base-item.mjs"; + +export default class CainSinMarkAbility extends CainItemBase { + static defineSchema() { + const fields = foundry.data.fields; + const requiredInteger = { required: true, nullable: false, integer: true }; + const schema = super.defineSchema(); + + + schema.abilityName = new fields.StringField({ required: true, nullable: false, initial: "New Ability" }); + schema.abilityDescription = new fields.StringField({required: true, nullable: false, initial: "Ability Description"}); + schema.bodyPartName = new fields.StringField({ required: true, nullable: false, initial: "Body Part Name" }); + + /* + item name and description are inherited by CainItemBase + and Item from foundry.data.Item + */ + + schema.formula = new fields.StringField({ blank: true }); + + return schema; + } +} \ No newline at end of file diff --git a/module/data/sins/item-sin-mark.mjs b/module/data/sins/item-sin-mark.mjs new file mode 100644 index 0000000..149c0a9 --- /dev/null +++ b/module/data/sins/item-sin-mark.mjs @@ -0,0 +1,22 @@ +import CainItemBase from "../base-item.mjs"; + +export default class CainSinMark extends CainItemBase { + + static defineSchema() { + const fields = foundry.data.fields; + const requiredInteger = { required: true, nullable: false, integer: true }; + const schema = super.defineSchema(); + + schema.bodyPartName = new fields.StringField({ required: true, nullable: false, initial: "Body Part Name" }); + schema.markAmount = new fields.NumberField({ required: true, nullable: false, initial: 0, min: 0, max: 10 }); + schema.abilities = new fields.ArrayField(new fields.StringField(), { required: true, nullable: false, initial: [] }); + + /* + description is inherited by CainItemBase + schema.description = new fields.StringField({ required: true, nullable: false, initial: "default" }); + */ + + return schema; + } + +} \ No newline at end of file diff --git a/module/documents/homebrew-window.mjs b/module/documents/homebrew-window.mjs new file mode 100644 index 0000000..cd706a6 --- /dev/null +++ b/module/documents/homebrew-window.mjs @@ -0,0 +1,427 @@ +import { CainAgenda, CainAgendaTask, CainAgendaAbility } from "../data/_module.mjs"; +import { CainBlasphemy, CainBlasphemyPower } from "../data/_module.mjs"; + +export class HomebrewWindow extends Application { + agendaOptions = { + name: "New Agenda", + tasks: [{ + task: "Primary Task", + isBold: false + }, + { + task: "Secondary Task", + isBold: true + }], + abilities: [{ + name: "Ability 1", + abilityDescription: "Ability Description" + }, + { + name: "Ability 2", + abilityDescription: "Ability Description" + }, + { + name: "Ability 3", + abilityDescription: "Ability Description" + }, + { + name: "Ability 4", + abilityDescription: "Ability Description" + }, + { + name: "Ability 5", + abilityDescription: "Ability Description" + }, + { + name: "Ability 6", + abilityDescription: "Ability Description" + }, + ] + } + + blasphemyOptions = { + name: "New Blasphemy", + powers: [{ + name: "Power 1", + isPassive: true, + keywords: "default", + powerDescription: "Power Description" + }, + { + name: "Power 2", + isPassive: false, + keywords: "default", + powerDescription: "Power Description" + }, + { + name: "Power 3", + isPassive: false, + keywords: "default", + powerDescription: "Power Description" + }, + { + name: "Power 4", + isPassive: false, + keywords: "default", + powerDescription: "Power Description" + }, + { + name: "Power 5", + isPassive: false, + keywords: "default", + powerDescription: "Power Description" + }, + { + name: "Power 6", + isPassive: false, + keywords: "default", + powerDescription: "Power Description" + }, + { + name: "Power 7", + isPassive: false, + keywords: "default", + powerDescription: "Power Description" + }] + } + + static get defaultOptions() { + return mergeObject(super.defaultOptions, { + id: 'homebrew-window', + title: 'Create Homebrew', + template: 'systems/cain/templates/homebrew-window.hbs', + width: 1000, + height: 1000, + resizable: true, + tabs: [ + { + navSelector: '.sheet-tabs', + contentSelector: '.sheet-body', + initial: 'description', + }, + ], + }); + } + + getData() { + return { + agendaOptions: this.agendaOptions, + blasphemyOptions: this.blasphemyOptions + } + } + + activateListeners(html) { + super.activateListeners(html); + html.find('.homebrew-agenda-name-input').change(this._onChangeAgendaName.bind(this)); + html.find('.homebrew-toggle-bold').click(this._onToggleBold.bind(this)); + html.find('.homebrew-new-task').click(this._onCreateNewTask.bind(this)); + html.find('.homebrew-task-input').change(this._onChangeTaskName.bind(this)); + html.find('.homebrew-remove-task').click(this._onRemoveTask.bind(this)); + html.find('.homebrew-new-ability').click(this._onCreateNewAbility.bind(this)); + html.find('.homebrew-ability-name-input').change(this._onChangeAbilityName.bind(this)); + html.find('.homebrew-ability-input').change(this._onChangeAbilityDescription.bind(this)); + html.find('.homebrew-remove-ability').click(this._onRemoveAbility.bind(this)); + html.find('.homebrew-submit-agenda').click(this._onSubmitAgenda.bind(this)); + + + html.find('.homebrew-blasphemy-name-input').change(this._onChangeBlasphemyName.bind(this)); + html.find('.homebrew-new-power').click(this._onCreateNewPower.bind(this)); + html.find('.homebrew-power-name-input').change(this._onChangePowerName.bind(this)); + html.find('.homebrew-power-passive-input').change(this._onChangePowerPassive.bind(this)); + html.find('.homebrew-power-tags-input').change(this._onChangePowerTags.bind(this)); + html.find('.homebrew-power-input').change(this._onChangePowerDescription.bind(this)); + html.find('.homebrew-remove-power').click(this._onRemovePower.bind(this)); + html.find('.homebrew-submit-blasphemy').click(this._onSubmitBlasphemy.bind(this)); + + + } + + + _onCreateNewPower(event) { + event.preventDefault(); + this.blasphemyOptions.powers.push({ + name: "New Power", + isPassive: false, + keywords: "default", + power: "Ability Description" + }); + this.render(true); + } + + _onRemovePower(event) { + event.preventDefault(); + const powerIndex = event.currentTarget.getAttribute('data-power-index'); + const newPowers = this.blasphemyOptions.powers.slice(0, powerIndex).concat(this.blasphemyOptions.powers.slice(Number(powerIndex)+1)); + this.blasphemyOptions.powers = newPowers; + this.render(true); + } + + _onChangeBlasphemyName(event) { + event.preventDefault(); + this.blasphemyOptions.name = event.currentTarget.value; + this.render(true); + } + + _onChangePowerName(event) { + event.preventDefault(); + const powerIndex = event.currentTarget.getAttribute('data-power-index'); + this.blasphemyOptions.powers[powerIndex].name = event.currentTarget.value; + this.render(true); + } + + _onChangePowerDescription(event) { + event.preventDefault(); + const powerIndex = event.currentTarget.getAttribute('data-power-index'); + this.blasphemyOptions.powers[powerIndex].powerDescription = event.currentTarget.value; + this.render(true); + } + + _onChangePowerPassive(event) { + event.preventDefault(); + const powerIndex = event.currentTarget.getAttribute('data-power-index'); + this.blasphemyOptions.powers[powerIndex].isPassive = event.currentTarget.checked; + this.render(true); + } + + _onChangePowerTags(event) { + event.preventDefault(); + const powerIndex = event.currentTarget.getAttribute('data-power-index'); + this.blasphemyOptions.powers[powerIndex].keywords = event.currentTarget.value; + this.render(true); + } + + + async _onSubmitBlasphemy(event) { + event.preventDefault(); + let blasphemyFolderFolder = game.folders.find(f => f.name === "Blasphemies" && f.type === "Item"); + if (!blasphemyFolderFolder) { + blasphemyFolderFolder = await Folder.create({ + name: "Blasphemies", + type: "Item", + folder: null, // Set a parent folder ID if nesting is desired + sorting: "m", // 'm' for manual sorting, 'a' for alphabetical + }); + } + + let blasphemyFolder = game.folders.find(f => f.name === this.blasphemyOptions.name && f.type === "Item"); + if (!blasphemyFolder) { + blasphemyFolder = await Folder.create({ + name: this.blasphemyOptions.name, + type: "Item", + folder: blasphemyFolderFolder.id, // Set a parent folder ID if nesting is desired + sorting: "m", // 'm' for manual sorting, 'a' for alphabetical + }); + } + + let blasphemyPowerFolder = game.folders.find(f => f.name === (this.blasphemyOptions.name + " Powers") && f.type === "Item"); + if (!blasphemyPowerFolder) { + blasphemyPowerFolder = await Folder.create({ + name: (this.blasphemyOptions.name + " Powers"), + type: "Item", + folder: blasphemyFolder.id, // Set a parent folder ID if nesting is desired + sorting: "m", // 'm' for manual sorting, 'a' for alphabetical + }); + } + + const powerList = []; + for (const index in this.blasphemyOptions.powers) { + const power = this.blasphemyOptions.powers[index]; + const createdPowerData = { + name: power.name, + type: "blasphemyPower", // Ensure this matches the item type defined in your game system + img: "icons/svg/item-bag.svg", + folder: blasphemyPowerFolder.id, // Assign the item to the folder + system: { + blasphemyType: this.blasphemyOptions.name, + powerName: power.name, + isPassive: power.isPassive, + keywords: power.keywords, + powerDescription: power.powerDescription + } + }; + const createdPower = await Item.create(createdPowerData); + powerList.push(createdPower.id); + console.log(createdPower); + } + const createdBlasphemyData = { + name: this.blasphemyOptions.name, + type: "blasphemy", + img: "icons/svg/item-bag.svg", + folder: blasphemyFolder.id, // Assign the item to the folder + system: { + blasphemyName: this.blasphemyOptions.name, + powers: powerList, + } + }; + console.log(createdBlasphemyData); + const createdBlasphemy = await Item.create(createdBlasphemyData); + console.log(createdBlasphemy); + } + + _onCreateNewTask(event) { + event.preventDefault(); + this.agendaOptions.tasks.push({ + task: "New Task", + isBold: true + }); + this.render(true); + } + + _onCreateNewAbility(event) { + event.preventDefault(); + this.agendaOptions.abilities.push({ + name: "New Ability", + ability: "Ability Description" + }); + this.render(true); + } + + _onRemoveTask(event) { + event.preventDefault(); + const taskIndex = event.currentTarget.getAttribute('data-task-index'); + const newTasks = this.agendaOptions.tasks.slice(0, taskIndex).concat(this.agendaOptions.tasks.slice(Number(taskIndex)+1)); + this.agendaOptions.tasks = newTasks; + this.render(true); + } + + _onRemoveAbility(event) { + event.preventDefault(); + const abilityIndex = event.currentTarget.getAttribute('data-ability-index'); + const newAbilities = this.agendaOptions.abilities.slice(0, abilityIndex).concat(this.agendaOptions.abilities.slice(Number(abilityIndex)+1)); + this.agendaOptions.abilities = newAbilities; + this.render(true); + } + + _onChangeAgendaName(event) { + event.preventDefault(); + this.agendaOptions.name = event.currentTarget.value; + this.render(true); + } + + + _onChangeTaskName(event) { + event.preventDefault(); + const taskIndex = event.currentTarget.getAttribute('data-task-index'); + this.agendaOptions.tasks[taskIndex].task = event.currentTarget.value; + this.render(true); + } + + _onChangeAbilityName(event) { + event.preventDefault(); + const abilityIndex = event.currentTarget.getAttribute('data-ability-index'); + this.agendaOptions.abilities[abilityIndex].name = event.currentTarget.value; + this.render(true); + } + + _onChangeAbilityDescription(event) { + event.preventDefault(); + const abilityIndex = event.currentTarget.getAttribute('data-ability-index'); + this.agendaOptions.abilities[abilityIndex].abilityDescription = event.currentTarget.value; + this.render(true); + } + + _onToggleBold(event) { + event.preventDefault(); + const taskIndex = event.currentTarget.getAttribute('data-task-index'); + this.agendaOptions.tasks[taskIndex].isBold = !this.agendaOptions.tasks[taskIndex].isBold; + this.render(true); + } + + async _onSubmitAgenda(event) { + event.preventDefault(); + let agendaFolderFolder = game.folders.find(f => f.name === "Agendas" && f.type === "Item"); + if (!agendaFolderFolder) { + agendaFolderFolder = await Folder.create({ + name: "Agendas", + type: "Item", + folder: null, // Set a parent folder ID if nesting is desired + sorting: "m", // 'm' for manual sorting, 'a' for alphabetical + }); + } + + let agendaFolder = game.folders.find(f => f.name === this.agendaOptions.name && f.type === "Item"); + if (!agendaFolder) { + agendaFolder = await Folder.create({ + name: this.agendaOptions.name, + type: "Item", + folder: agendaFolderFolder.id, // Set a parent folder ID if nesting is desired + sorting: "m", // 'm' for manual sorting, 'a' for alphabetical + }); + } + + let agendaTaskFolder = game.folders.find(f => f.name === (this.agendaOptions.name + " Tasks") && f.type === "Item"); + if (!agendaTaskFolder) { + agendaTaskFolder = await Folder.create({ + name: (this.agendaOptions.name + " Tasks"), + type: "Item", + folder: agendaFolder.id, // Set a parent folder ID if nesting is desired + sorting: "m", // 'm' for manual sorting, 'a' for alphabetical + }); + } + + let agendaAbilityFolder = game.folders.find(f => f.name === (this.agendaOptions.name + " Abilities") && f.type === "Item"); + if (!agendaAbilityFolder) { + agendaAbilityFolder = await Folder.create({ + name: this.agendaOptions.name + " Abilities", + type: "Item", + folder: agendaFolder.id, // Set a parent folder ID if nesting is desired + sorting: "m", // 'm' for manual sorting, 'a' for alphabetical + }); + } + + const unboldedTaskList = []; + const boldedTaskList = []; + const abilityList = []; + for (const index in this.agendaOptions.tasks) { + const task = this.agendaOptions.tasks[index]; + const createdTaskData = { + name: task.task, + type: "agendaTask", // Ensure this matches the item type defined in your game system + img: "icons/svg/item-bag.svg", + folder: agendaTaskFolder.id, // Assign the item to the folder + system: { + task: task.task, + isBold: task.isBold + } + }; + const createdTask = await Item.create(createdTaskData); + if (createdTask.system.isBold) { + boldedTaskList.push(createdTask.id); + } else { + unboldedTaskList.push(createdTask.id); + } + } + for (const index in this.agendaOptions.abilities) { + const ability = this.agendaOptions.abilities[index]; + const createdAbilityData = { + name: ability.name, + type: "agendaAbility", // Ensure this matches the item type defined in your game system + img: "icons/svg/item-bag.svg", + folder: agendaAbilityFolder.id, // Assign the item to the folder + system: { + abilityName: ability.name, + abilityDescription: ability.abilityDescription + } + }; + const createdAbility = await Item.create(createdAbilityData); + abilityList.push(createdAbility.id); + console.log(createdAbility); + } + const createdAgendaData = { + name: this.agendaOptions.name, + type: "agenda", + img: "icons/svg/item-bag.svg", + folder: agendaFolder.id, // Assign the item to the folder + system: { + agendaName: this.agendaOptions.name, + formula: "", + unboldedTasks: unboldedTaskList, + boldedTasks: boldedTaskList, + abilities: abilityList + } + }; + console.log(createdAgendaData); + const createdAgenda = await Item.create(createdAgendaData); + console.log(createdAgenda); + } +} diff --git a/module/documents/player-overview.mjs b/module/documents/player-overview.mjs index f3beb75..fd4d3af 100644 --- a/module/documents/player-overview.mjs +++ b/module/documents/player-overview.mjs @@ -19,6 +19,10 @@ export class PlayerOverview extends Application { return { name: player.name, actor: actor ? actor : null, + agendaTasks: actor ? this._getItemsFromIDs(actor.system.currentUnboldedAgendaTasks.concat(actor.system.currentBoldedAgendaTasks)) : [], + agendaAbilities: actor ? this._getItemsFromIDs(actor.system.currentAgendaAbilities) : [], + blasphemies: actor ? this._getItemsFromIDs(actor.system.currentBlasphemies) : [], + blasphemyPowers: actor ? this._getItemsFromIDs(actor.system.currentBlasphemyPowers) : [], message: actor ? null : "No actor assigned. Please assign an actor for player overview." }; }); @@ -26,6 +30,10 @@ export class PlayerOverview extends Application { return { playerData }; } + _getItemsFromIDs(ids) { + return ids.map(id => game.items.get(id)); + } + activateListeners(html) { super.activateListeners(html); // Add any event listeners if needed diff --git a/module/documents/session-end-advancement.mjs b/module/documents/session-end-advancement.mjs new file mode 100644 index 0000000..eb05951 --- /dev/null +++ b/module/documents/session-end-advancement.mjs @@ -0,0 +1,66 @@ +export class SessionEndAdvancement extends FormApplication { + constructor(actor) { + super(); + this.actor = actor; + } + static get defaultOptions() { + return mergeObject(super.defaultOptions, { + classes: ['form'], + popOut: true, + template: `systems/cain/templates/session-end-advancement.hbs`, + id: 'session-end-advancement', + title: 'End of Session Advancement', + }); + } + + /** @override */ + getData() { + const currentUnboldedAgendaTasks = []; + const currentBoldedAgendaTasks = []; + for (const agendaTaskID in this.actor.system.currentUnboldedAgendaTasks) { + currentUnboldedAgendaTasks.push(game.items.get(this.actor.system.currentUnboldedAgendaTasks[agendaTaskID])); + } + for (const agendaTaskID in this.actor.system.currentBoldedAgendaTasks) { + currentBoldedAgendaTasks.push(game.items.get(this.actor.system.currentBoldedAgendaTasks[agendaTaskID])); + } + console.log(currentBoldedAgendaTasks); + + return { + actor: this.actor, + boldedAgendaTasks: currentBoldedAgendaTasks, + unboldedAgendaTasks: currentUnboldedAgendaTasks, + }; + } + + activateListeners(html) { + super.activateListeners(html); + } + + async _updateObject(event, formData) { + var survival = 0; + var firstAgenda = 0; + var boldAgenda = 0; + var injuries = 0; + for(const [key, value] of Object.entries(formData)){ + console.log(key + " : " + value); + if (key == "survival" && value) survival = 1; + if (key.substring(0,11) == "firstAgenda" && value) firstAgenda++; + if (key.substring(0,10) == "boldAgenda" && value) boldAgenda++; + if (key == "injuries" && value) injuries = 1; + } + if (firstAgenda > 1) firstAgenda = 1; + if (boldAgenda > 2) boldAgenda = 2; + const totalGain = survival + firstAgenda + boldAgenda + injuries; + const oldXPValue = this.actor.system.xp.value; + const newXPValue = oldXPValue + totalGain; + if (newXPValue >= this.actor.system.xp.max) { + this.actor.update({ 'system.xp.value': newXPValue % this.actor.system.xp.max}); + const newAdvanceValue = this.actor.system.advancements.value + Math.floor(newXPValue/this.actor.system.xp.max); + this.actor.update({ 'system.advancements.value': newAdvanceValue}); + } else { + this.actor.update({ 'system.xp.value': newXPValue}); + console.log("Updated xp from " + oldXPValue + " to " + newXPValue ); + } + + } +} \ No newline at end of file diff --git a/module/helpers/index-offset.mjs b/module/helpers/index-offset.mjs new file mode 100644 index 0000000..cc0e1fb --- /dev/null +++ b/module/helpers/index-offset.mjs @@ -0,0 +1,3 @@ +var Handlebars = require('handlebars'); + +// the index offset handlebar shortcut should go here once we figured importing out \ No newline at end of file diff --git a/module/helpers/standard_event_assignment_shortcuts.mjs b/module/helpers/standard_event_assignment_shortcuts.mjs new file mode 100644 index 0000000..d1c8bc0 --- /dev/null +++ b/module/helpers/standard_event_assignment_shortcuts.mjs @@ -0,0 +1,24 @@ + +export class HTMLShortcut{ + constructor(setObj){ + this.htmlObj = setObj + } + + setLeftClick(selector, func){ + this.htmlObj.find(selector).click(func); + } + + setChange(selector, func){ + this.htmlObj.find(selector).change(func); + } + + setRightClick(selector, func){ + this.htmlObj.find(selector).on('contextmenu', func); + } + + setLeftAndRightClick(selector, funcLeft, funcRight){ + let obj = this.htmlObj.find(selector); + obj.click(funcLeft); + obj.on('contextmenu', funcRight); + } +} diff --git a/module/helpers/templates.mjs b/module/helpers/templates.mjs index 4de7adc..93899cd 100644 --- a/module/helpers/templates.mjs +++ b/module/helpers/templates.mjs @@ -26,6 +26,8 @@ export const preloadHandlebarsTemplates = async function () { 'systems/cain/templates/actor/npc-parts/actor-domains.hbs', // Item partials 'systems/cain/templates/item/parts/item-effects.hbs', - + // Sin mark partials + 'systems/cain/templates/item/item-sinMark-sheet.hbs', + 'systems/cain/templates/item/item-sinMarkAbility-sheet.hbs', ]); }; diff --git a/module/sheets/actor-sheet.mjs b/module/sheets/actor-sheet.mjs index c24c718..d84f4eb 100644 --- a/module/sheets/actor-sheet.mjs +++ b/module/sheets/actor-sheet.mjs @@ -3,6 +3,11 @@ import { prepareActiveEffectCategories, } from '../helpers/effects.mjs'; +import { + HTMLShortcut +} from '../helpers/standard_event_assignment_shortcuts.mjs' + +import { SessionEndAdvancement} from '../documents/session-end-advancement.mjs' import { CAIN } from '../helpers/config.mjs'; /** @@ -10,6 +15,11 @@ import { CAIN } from '../helpers/config.mjs'; * @extends {ActorSheet} */ export class CainActorSheet extends ActorSheet { + sheetConstants = { + "CATSessionNumbers": ["0", "2", "3", "5", "7", "X", "X"], + "SINVisualOffset": Math.round( Math.random() * 8) //a random offset so the EYES in the sin section don't always look exactly the same + }; + /** @override */ static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { @@ -83,38 +93,120 @@ export class CainActorSheet extends ActorSheet { ); } + if (this.actor.system.severeAttack || this.actor.system.attack) { + context.enrichedDescription = await TextEditor.enrichHTML( + this.actor.system.severeAttack.description, + { + secrets: this.document.isOwner, + async: true, + rollData: this.actor.getRollData(), + relativeTo: this.actor, + } + ); + } + context.effects = prepareActiveEffectCategories( this.actor.allApplicableEffects() ); this._calculateRanges(context); + context.sheetConstants = this.sheetConstants return context; } _prepareCharacterData(context) { // Character-specific data preparation + context.agendas = []; + context.blasphemies = []; + + const agendaID = context.system.currentAgenda; + context.currentAgenda = agendaID !== "INVALID" ? game.items.get(agendaID) : null; + context.currentUnboldedAgendaTasks = this._getItemsFromIDs(context.system.currentUnboldedAgendaTasks || []); + context.currentBoldedAgendaTasks = this._getItemsFromIDs(context.system.currentBoldedAgendaTasks || []); + context.currentAgendaAbilities = this._getItemsFromIDs(context.system.currentAgendaAbilities || []); + context.currentBlasphemies = this._getItemsFromIDs(context.system.currentBlasphemies || []); + context.currentBlasphemyPowers = this._getItemsFromIDs(context.system.currentBlasphemyPowers || []); + context.currentSinMarks = this._getItemsFromIDs(context.system.sinMarks || []); + context.currentSinMarkAbilities = this._getItemsFromIDs(context.system.sinMarkAbilities || []); + context.currentAfflictions = this._getItemsFromIDs(context.system.afflictions || []); + + // Calculate currentUnlinkedBlasphemyPowers + context.currentUnlinkedBlasphemyPowers = this._getItemsFromIDs( + (context.system.currentBlasphemyPowers || []).filter(blasphemyPowerID => { + return (context.currentBlasphemies || []).map(blasphemy => { + return !blasphemy.system.powers.includes(blasphemyPowerID); + }).reduce((a, b) => a && b, true); + }) + ); + + context.currentSinMarkData = (context.currentSinMarks || []).map(sinMark => { + const sinMarkAbilities = sinMark.system.abilities || []; + const currentSinMarkAbilities = (context.currentSinMarkAbilities || []).map(item => item.id); + + console.log("CURRENT SIN MARK ABILITIES:", currentSinMarkAbilities); + console.log("CONTEXT SIN MARK ABILITIES:", sinMarkAbilities); + + const abilities = this._getItemsFromIDs(sinMarkAbilities.filter(abilityID => currentSinMarkAbilities.includes(abilityID))); + + console.log("Sin Mark Abilities:", abilities); + return { + sinMark: sinMark, + id: sinMark.id, + abilities: abilities.map(ability => ({ + name: ability.system.abilityName, + id: ability.id, + description: ability.system.abilityDescription, + bodyPart: ability.system.bodyPartName, + formula: ability.system.formula + })) + }; + }); + + // Prepare blasphemy data + context.blasphemyData = (context.currentBlasphemies || []).map(blasphemy => { + const blasphemyPowers = blasphemy.system.powers || []; + const currentBlasphemyPowers = context.system.currentBlasphemyPowers || []; + + return { + blasphemy: blasphemy, + passives: this._getItemsFromIDs(blasphemyPowers.filter(powerID => currentBlasphemyPowers.includes(powerID))) + .filter(power => power.system.isPassive), + powers: this._getItemsFromIDs(blasphemyPowers.filter(powerID => currentBlasphemyPowers.includes(powerID))) + .filter(power => !power.system.isPassive), + availablePowers: this._getItemsFromIDs(blasphemyPowers.filter(powerID => !currentBlasphemyPowers.includes(powerID))) + }; + }); + + + // Prepare currentAgendaAvailableAbilities + if (context.currentAgenda) { + const validAbilities = (context.currentAgenda.system.abilities || []).filter(item => + !(context.system.currentAgendaAbilities || []).includes(item) + ); + context.currentAgendaAvailableAbilities = this._getItemsFromIDs(validAbilities); + } else { + context.currentAgendaAvailableAbilities = []; + } + } + + _getItemsFromIDs(ids) { + return ids.map(id => game.items.get(id)); + } + + _prepareItems(context) { const gear = []; - const agendas = []; - const blasphemies = []; - for (let i of context.items) { i.img = i.img || Item.DEFAULT_ICON; if (i.type === 'item') { gear.push(i); - } else if (i.type === 'agenda') { - agendas.push(i); - } else if (i.type === 'blasphemy') { - blasphemies.push(i); } } context.gear = gear; - context.agendas = agendas; - context.blasphemies = blasphemies; } _calculateRanges(context) { @@ -196,15 +288,19 @@ export class CainActorSheet extends ActorSheet { this.actor.update({ [`system.${field}.value`]: value }); }); + let scHtml = new HTMLShortcut(html); // Character sheet specific listeners html.find('.item-description').click(this._onItemDescription.bind(this)); - html.find('.sinOverflow-checkbox').change(this._onOverflowChange.bind(this)); html.find('.psyche-roll-button').click(this._onRollPsyche.bind(this)); html.find('.psyche-burst-checkbox').change(this._onPsycheBurstChange.bind(this)); - html.find('.kit-points-checkbox').change(this._onKitPointsChange.bind(this)); html.find('.clear-sin-marks').click(this._clearSinMarks.bind(this)); html.find('#increment-xp-value').click(this._increaseXPValue.bind(this)); + html.find('#decrement-xp-value').click(this._decreaseXPValue.bind(this)); + html.find('#increment-max-xp-value').click(this._increaseMaxXPValue.bind(this)); + html.find('#decrement-max-xp-value').click(this._decreaseMaxXPValue.bind(this)); + html.find('#session-end-xp-value').click(this._openEndSessionModal.bind(this)); html.find('.delete-sin-mark').click(this._deleteSinMark.bind(this)); + html.find('.evolve-mark-button').click(this._evolveSinMark.bind(this)); html.find('.roll-sin-mark').click(this._rollSinMark.bind(this)); html.find('.talisman-name').change(this._onInputChange.bind(this)); html.find('.talisman-curr-mark-amount').change(this._onInputChange.bind(this)); @@ -222,16 +318,117 @@ export class CainActorSheet extends ActorSheet { html.find('.talisman-max-mark').change(this._onMaxMarkAmountChange.bind(this)); html.find('.roll-rest-dice').click(this._RollRestDice.bind(this)); html.find('#add-agenda-item-button').on('click', this._addAgendaItemButton.bind(this)); - html.find('#add-agenda-ability-button').on('click', this._addAgendaAbilityButton.bind(this)); - html.find('#editable-agenda-items').on('click', '.remove-item-button', this._removeItemButton.bind(this)); - html.find('#editable-agenda-abilities').on('click', '.remove-ability-button', this._removeAbilityButton.bind(this)); + html.find('#editable-agenda-abilities').on('click', '.remove-ability-button', this._removeAgendaAbilityButton.bind(this)); + html.find('.remove-sin-mark-ability-button').click(this._removeSinMarkAbilityButton.bind(this)); html.find('#editable-agenda-items').on('change', '.editable-item-input', this._updateAgendaItem.bind(this)); html.find('#editable-agenda-abilities').on('change', '.editable-ability-input', this._updateAgendaAbility.bind(this)); - // Bind the bolding functions - html.find('.bold-item-button').click(this._boldAgendaItem.bind(this)); - html.find('.bold-ability-button').click(this._boldAgendaAbility.bind(this)); + html.find('.blasphemy-power-to-chat').on('click', this._sendBlasphemyPowerMessage.bind(this)); + html.find('.remove-blasphemy-power-button').on('click', this._removeBlasphemyPowerButton.bind(this)); + html.find('.remove-blasphemy-button').on('click', this._removeBlasphemyButton.bind(this)); + html.find('.remove-blasphemy-button').on('click', this._removeBlasphemyButton.bind(this)); + html.find('.remove-affliction-button').on('click', this._removeAfflictionButton.bind(this)); + scHtml.setLeftClick('.add-task-button', this._addNewTask.bind(this)); + + html.find('.add-affliction-button').on('click', async event => {await this._addAffliction(event)}); + + scHtml.setLeftAndRightClick( + '.CAT-selector', + this._onCATSelect.bind(this, true), + this._onCATSelect.bind(this, false) + ); + + html.find('#add-agenda-ability-button').on('click', this._addAgendaAbility.bind(this)); + html.find('.add-blasphemy-power-button').on('click', this._addBlasphemyPower.bind(this)); + + scHtml.setLeftAndRightClick( + '.kit-points-selection', + this._onKitPointsChange.bind(this), + this._clearKitPoints.bind(this) + ); + + scHtml.setLeftAndRightClick( + '.sinOverflow-icon', + this._sinChange.bind(this), + this._clearSin.bind(this) + ); + + + // New event listeners for agenda tasks and abilities + html.find('.agenda-task').on('click', (event) => { + const itemId = this.actor.system.currentAgenda; // Get the current agenda ID + this._openAgendaItemSheet(itemId); + }); + + html.find('.agenda-ability').on('click', (event) => { + const itemId = this.actor.system.currentAgenda; // Get the current agenda ID + this._openAgendaItemSheet(itemId); + }); + + // Event delegation for blasphemy-passive + html.on('click', '.blasphemy-passive', (event) => { + const card = event.target.parentElement.parentElement.querySelector('.power-description-card'); + const disableAnimations = document.getElementById('toggle-animation').checked; + if (!disableAnimations) { + const randomRotation = Math.random() * 6 - 3; // Random rotation between -3 and 3 degrees + card.style.transform = `scale(0.95) rotate(${randomRotation}deg)`; + } else { + card.style.transform = 'none'; + } + card.classList.toggle('visible'); + }); + + // Event delegation for blasphemy-power + html.on('click', '.blasphemy-power', (event) => { + const card = event.target.parentElement.parentElement.querySelector('.power-description-card'); + const disableAnimations = document.getElementById('toggle-animation').checked; + if (!disableAnimations) { + const randomRotation = Math.random() * 6 - 3; // Random rotation between -3 and 3 degrees + card.style.transform = `scale(0.95) rotate(${randomRotation}deg)`; + } else { + card.style.transform = 'none'; + } + card.classList.toggle('visible'); + }); + + html.find('.character-drop-target').on('drop', async event => { + event.preventDefault(); + const data = JSON.parse(event.originalEvent.dataTransfer.getData('text/plain')); + const itemDrop = await Item.fromDropData(data); + switch(itemDrop.type) { + case "agenda": + this._onDropAgenda(event, itemDrop); + break; + case "agendaTask": + this._onDropAgendaTask(event, itemDrop); + break; + case "agendaAbility": + this._onDropAgendaAbility(event, itemDrop); + break; + case "blasphemy": + this._onDropBlasphemy(event, itemDrop); + break; + case "blasphemyPower": + this._onDropBlasphemyPower(event, itemDrop); + break; + case "sinMark": + this._onDropSinMark(event, itemDrop); + break; + case "sinMarkAbility": + this._onDropSinMarkAbility(event, itemDrop); + break; + case "affliction": + this._onDropAffliction(event, itemDrop); + break; + default: + ui.notifications.error("Invalid drop type on ability page: " + itemDrop.type); + console.warn("Invalid drop type on ability page: " + itemDrop.type); + } +}); + + + html.find('.remove-task-button').click(this._removeAgendaTask.bind(this)); // Bind the send to chat functions - html.find('.agenda-item-to-chat').click(this._sendAgendaItemMessage.bind(this)); + html.find('.agenda-task-to-chat').click(this._sendAgendaTaskMessage.bind(this)); html.find('.agenda-ability-to-chat').click(this._sendAgendaAbilityMessage.bind(this)); /* NPC sheet specific listeners */ html.find('.attack-button').click(this._onNpcAttack.bind(this)); @@ -244,8 +441,621 @@ export class CainActorSheet extends ActorSheet { this._onSinTypeSelect(sinType); }); + //Initialize the power descriptions + Array.from(html.find('.selectedPower')).forEach(selectedPowerElement => {this._onPowerSelect({ target: selectedPowerElement });}); + + // Call _onAbilitySelect when the sheet is loaded + const selectedAbilityElement = html.find('#selectedAgenda')[0]; + if (selectedAbilityElement) { + this._onAbilitySelect({ target: selectedAbilityElement }); + } + + html.find('.selectedPower').on('change', this._onPowerSelect.bind(this)); + + // Event listener for selectedAgenda + html.find('#selectedAgenda').change(this._onAbilitySelect.bind(this)); + + html.find('.rollable[data-roll="1d3"]').click(async () => { + const actor = this.actor; + await this._rollSinOverflow(actor); + }); + } + async _rollSinOverflow(actor) { + // Roll 1d3 + const roll = await new Roll('1d3').roll(); + const rolledValue = roll.total; + + // Get current sinOverflow value and max + const currentSinOverflow = actor.system.sinOverflow.value; + const maxSinOverflow = actor.system.sinOverflow.max; + + // Calculate new sinOverflow value + let newSinOverflow = currentSinOverflow + rolledValue; + + // Check if new value exceeds max + if (newSinOverflow >= maxSinOverflow) { + newSinOverflow = maxSinOverflow; + ui.notifications.error(`😈 You are now at risk of sin overflow! Current value: ${newSinOverflow} / ${maxSinOverflow} 😈`); + } else { + ui.notifications.info(`😈 Current sin overflow value: ${newSinOverflow} / ${maxSinOverflow} 😈`); + } + + // Update actor's sinOverflow value + await actor.update({'system.sinOverflow.value': newSinOverflow}); + } + + _onAbilitySelect(event) { + const selectElement = event.target; + if (selectElement.options.length === 0) { + document.getElementById('abilityDescription').innerText = 'No more available Abilites'; + return; + } + + const selectedOption = selectElement.options[selectElement.selectedIndex]; + const description = selectedOption.getAttribute('data-description'); + document.getElementById('abilityDescription').innerText = description; + } + + + _onPowerSelect(event) { + const selectElement = event.target; + if (selectElement.options.length === 0) { + selectElement.parentElement.parentElement.querySelector('.powerDescription').innerText = 'There are no more selectable powers.'; + selectElement.parentElement.parentElement.querySelector('.powerKeywords').innerText = 'None'; + return; + } + + const selectedOption = selectElement.options[selectElement.selectedIndex]; + const description = selectedOption.getAttribute('data-description'); + const keywords = selectedOption.getAttribute('data-keywords'); + selectElement.parentElement.parentElement.querySelector('.powerDescription').innerText = description; + selectElement.parentElement.parentElement.querySelector('.powerKeywords').innerText = keywords ? keywords.split(',').join(', ') : ''; + } + + _openAgendaItemSheet(itemId) { + // Logic to open the agenda item sheet + + /** We should add more complicated logic in event listenrr + * to handle different types of items + * AKA agenda, agendaTask, agendaAbility, blasphemy, blasphemyPower + * etc. + * + * Right now this function only opens the whole Agenda you have assigned. + */ + console.log(`Opening agenda item sheet for item ID: ${itemId}`); + const item = Item.get(itemId); + console.log(item); + if (item) { + item.sheet.render(true); + } + } + + _openBlasphemyItemSheet(itemId) { + // Logic to open the blasphemy item sheet + console.log(`Opening blasphemy item sheet for item ID: ${itemId}`); + const item = Item.get(itemId); + console.log(item); + if (item) { + item.sheet.render(true); + } + } + + _onDropAgenda(event, agenda) { + // Ensure this.actor and this.actor.system are defined + if (!this.actor || !this.actor.system) { + console.error("Actor or actor system is undefined."); + ui.notifications.error("Actor or actor system is undefined. Please check your setup."); + return; + } + console.log("Actor and actor system are defined."); + + const boldedTasks = this.actor.system.currentBoldedAgendaTasks || []; + console.log("Current Bolded Agenda Tasks:", boldedTasks); + + // Ensure agenda and agenda.system are defined + if (!agenda || !agenda.system) { + console.error("Agenda or agenda system is undefined."); + ui.notifications.error("Agenda or agenda system is undefined. Please check your setup."); + return; + } + console.log("Agenda and agenda system are defined."); + + const newBoldedTasks = boldedTasks.concat( + (agenda.system.boldedTasks || []).filter(boldedTask => { + const isIncluded = !this.actor.system.currentBoldedAgendaTasks.includes(boldedTask); + console.log("Is bolded task included?", isIncluded); + return isIncluded; + }) + ); + console.log("New Bolded Tasks:", newBoldedTasks); + + this.actor.update({ + 'system.agenda': agenda.system.agendaName, + 'system.currentAgenda': agenda.id, + 'system.currentUnboldedAgendaTasks': agenda.system.unboldedTasks || [], + 'system.currentBoldedAgendaTasks': newBoldedTasks + }).then(() => { + console.log("Actor updated successfully."); + }).catch(err => { + console.error("Error updating actor:", err); + ui.notifications.error("Error updating actor. Please check the console for more details."); + }); + } + + _onDropAgendaTask(event, agendaTask) { + // Ensure this.actor and this.actor.system are defined + if (!this.actor || !this.actor.system) { + console.error("Actor or actor system is undefined."); + ui.notifications.error("Actor or actor system is undefined. Please check your setup."); + return; + } + console.log("Actor and actor system are defined."); + + // Ensure agendaTask and agendaTask.system are defined + if (!agendaTask || !agendaTask.system) { + console.error("Agenda task or agenda task system is undefined."); + ui.notifications.error("Agenda task or agenda task system is undefined. Please check your setup."); + return; + } + console.log("Agenda task and agenda task system are defined."); + + const isBold = agendaTask.system.isBold; + const taskList = isBold ? this.actor.system.currentBoldedAgendaTasks || [] : this.actor.system.currentUnboldedAgendaTasks || []; + console.log("Current Task List:", taskList); + + taskList.push(agendaTask.id); + console.log("Updated Task List:", taskList); + + this.actor.update({ + 'system.currentUnboldedAgendaTasks': isBold ? this.actor.system.currentUnboldedAgendaTasks || [] : taskList, + 'system.currentBoldedAgendaTasks': isBold ? taskList : this.actor.system.currentBoldedAgendaTasks || [] + }).then(() => { + console.log("Actor updated successfully."); + }).catch(err => { + console.error("Error updating actor:", err); + ui.notifications.error("Error updating actor. Please check the console for more details."); + }); + } + + _onDropAgendaAbility(event, agendaAbility) { + // Ensure this.actor and this.actor.system are defined + if (!this.actor || !this.actor.system) { + console.error("Actor or actor system is undefined."); + ui.notifications.error("Actor or actor system is undefined. Please check your setup."); + return; + } + console.log("Actor and actor system are defined."); + + // Ensure agendaAbility and agendaAbility.system are defined + if (!agendaAbility || !agendaAbility.system) { + console.error("Agenda ability or agenda ability system is undefined."); + ui.notifications.error("Agenda ability or agenda ability system is undefined. Please check your setup."); + return; + } + console.log("Agenda ability and agenda ability system are defined."); + + const abilityList = this.actor.system.currentAgendaAbilities || []; + console.log("Current Ability List:", abilityList); + + abilityList.push(agendaAbility.id); + console.log("Updated Ability List:", abilityList); + + this.actor.update({ + 'system.currentAgendaAbilities': abilityList + }).then(() => { + console.log("Actor updated successfully."); + }).catch(err => { + console.error("Error updating actor:", err); + ui.notifications.error("Error updating actor. Please check the console for more details."); + }); + } + + _onDropBlasphemy(event, blasphemy) { + // Ensure this.actor and this.actor.system are defined + if (!this.actor || !this.actor.system) { + console.error("Actor or actor system is undefined."); + ui.notifications.error("Actor or actor system is undefined. Please check your setup."); + return; + } + console.log("Actor and actor system are defined."); + + const blasphemyList = this.actor.system.currentBlasphemies || []; + console.log("Current Blasphemies:", blasphemyList); + + // Check if the blasphemy is already in the list + if (blasphemyList.includes(blasphemy.id)) { + console.log("Blasphemy already exists:", blasphemy.id); + return; + } + + // Add the new blasphemy to the list + blasphemyList.push(blasphemy.id); + console.log("Updated Blasphemies:", blasphemyList); + + // Get the current list of blasphemy powers + const blasphemyPowersList = this.actor.system.currentBlasphemyPowers || []; + console.log("Current Blasphemy Powers:", blasphemyPowersList); + + console.log(blasphemy.system); + + // Get the new blasphemy powers that are passive + const newBlasphemyPowers = this._getItemsFromIDs(blasphemy.system.powers || []) + .filter(power => { + console.log("Inspecting power:", power); + if (!power || !power.system) { + console.error("Power or power system is undefined:", power); + ui.notifications.error("Some powers are undefined. Did you import the compendium to keep document IDs?"); + return false; + } + const isPassive = power.system.isPassive; + console.log("Is power passive?", isPassive); + return isPassive; + }) + .map(power => { + console.log("Mapping power to ID:", power.id); + return power.id; + }); + + console.log("New Blasphemy Powers:", newBlasphemyPowers); + + // Combine the current and new blasphemy powers + const newBlasphemyPowersList = blasphemyPowersList.concat(newBlasphemyPowers); + console.log("Updated Blasphemy Powers:", newBlasphemyPowersList); + + //Check if this raises the number of blasphemies higher than 1, if so, add one to the XP max + let XPmax = this.actor.system.xp.max; + if (blasphemyList.length > 1) XPmax += 1; + const newXPMax = XPmax; + // Update the actor with the new lists + this.actor.update({ + 'system.currentBlasphemies': blasphemyList, + 'system.currentBlasphemyPowers': newBlasphemyPowersList, + 'system.xp.max': newXPMax + }).then(() => { + console.log("Actor updated successfully."); + console.log(this.actor); + }).catch(err => { + console.error("Error updating actor:", err); + ui.notifications.error("Error updating actor. Please check the console for more details."); + }); + } + + _onDropBlasphemyPower(event, blasphemyPower) { + // Ensure this.actor and this.actor.system are defined + if (!this.actor || !this.actor.system) { + console.error("Actor or actor system is undefined."); + ui.notifications.error("Actor or actor system is undefined. Please check your setup."); + return; + } + console.log("Actor and actor system are defined."); + + // Ensure blasphemyPower and blasphemyPower.system are defined + if (!blasphemyPower || !blasphemyPower.system) { + console.error("Blasphemy power or blasphemy power system is undefined."); + ui.notifications.error("Blasphemy power or blasphemy power system is undefined. Please check your setup."); + return; + } + console.log("Blasphemy power and blasphemy power system are defined."); + + const blasphemyPowersList = this.actor.system.currentBlasphemyPowers || []; + console.log("Current Blasphemy Powers List:", blasphemyPowersList); + + // Check if the blasphemy power is already in the list + if (blasphemyPowersList.includes(blasphemyPower.id)) { + console.log("Blasphemy power already exists:", blasphemyPower.id); + return; + } + + // Add the new blasphemy power to the list + blasphemyPowersList.push(blasphemyPower.id); + console.log("Updated Blasphemy Powers List:", blasphemyPowersList); + + // Update the actor with the new list + this.actor.update({ + 'system.currentBlasphemyPowers': blasphemyPowersList + }).then(() => { + console.log("Actor updated successfully."); + }).catch(err => { + console.error("Error updating actor:", err); + ui.notifications.error("Error updating actor. Please check the console for more details."); + }); + } + + _onDropSinMark(event, sinMark) { + // Ensure this.actor and this.actor.system are defined + if (!this.actor || !this.actor.system) { + console.error("Actor or actor system is undefined."); + ui.notifications.error("Actor or actor system is undefined. Please check your setup."); + return; + } + console.log("Actor and actor system are defined."); + + const sinMarkList = this.actor.system.sinMarks || []; + console.log("Current Sin Marks:", sinMarkList); + + // Check if the sin mark is already in the list + if (sinMarkList.includes(sinMark.id)) { + console.log("Sin mark already exists:", sinMark.id); + return; + } + + // Add the new sin mark to the list + sinMarkList.push(sinMark.id); + console.log("Updated Sin Marks:", sinMarkList); + + // Get the current list of sin mark abilities + const sinMarkAbilitiesList = this.actor.system.sinMarkAbilities || []; + console.log("Current Sin Mark Abilities:", sinMarkAbilitiesList); + + // Get the new sin mark abilities that are passive + const newSinMarkAbilities = this._getItemsFromIDs(sinMark.system.abilities || []) + .filter(ability => { + console.log("Inspecting ability:", ability); + if (!ability || !ability.system) { + console.error("Ability or ability system is undefined:", ability); + ui.notifications.error("Some abilities are undefined. Did you import the compendium to keep document IDs?"); + return false; + } + const isPassive = ability.system.isPassive; + console.log("Is ability passive?", isPassive); + return isPassive; + }) + .map(ability => { + console.log("Mapping ability to ID:", ability.id); + return ability.id; + }); + + console.log("New Sin Mark Abilities:", newSinMarkAbilities); + + // Combine the current and new sin mark abilities + const newSinMarkAbilitiesList = sinMarkAbilitiesList.concat(newSinMarkAbilities); + console.log("Updated Sin Mark Abilities:", newSinMarkAbilitiesList); + + // Update the actor with the new lists + this.actor.update({ + 'system.sinMarks': sinMarkList, + 'system.sinMarkAbilities': newSinMarkAbilitiesList + }).then(() => { + console.log("Actor updated successfully."); + }).catch(err => { + console.error("Error updating actor:", err); + ui.notifications.error("Error updating actor. Please check the console for more details."); + } + ); + } + + _onDropSinMarkAbility(event, sinMarkAbility) { + // Ensure this.actor and this.actor.system are defined + if (!this.actor || !this.actor.system) { + console.error("Actor or actor system is undefined."); + ui.notifications.error("Actor or actor system is undefined. Please check your setup."); + return; + } + console.log("Actor and actor system are defined."); + + // Ensure sinMarkAbility and sinMarkAbility.system are defined + if (!sinMarkAbility || !sinMarkAbility.system) { + console.error("Sin mark ability or sin mark ability system is undefined."); + ui.notifications.error("Sin mark ability or sin mark ability system is undefined. Please check your setup."); + return; + } + + console.log("Sin mark ability and sin mark ability system are defined."); + + const sinMarkAbilitiesList = this.actor.system.sinMarkAbilities || []; + console.log("Current Sin Mark Abilities List:", sinMarkAbilitiesList); + + // Check if the sin mark ability is already in the list + if (sinMarkAbilitiesList.includes(sinMarkAbility.id)) { + console.log("Sin mark ability already exists:", sinMarkAbility.id); + return; + } + + // Add the new sin mark ability to the list + sinMarkAbilitiesList.push(sinMarkAbility.id); + console.log("Updated Sin Mark Abilities List:", sinMarkAbilitiesList); + + // Update the actor with the new list + this.actor.update({ + 'system.sinMarkAbilities': sinMarkAbilitiesList + }).then(() => { + console.log("Actor updated successfully."); + }).catch(err => { + console.error("Error updating actor:", err); + ui.notifications.error("Error updating actor. Please check the console for more details."); + }); + } + + _onDropAffliction(event, affliction) { + // Ensure this.actor and this.actor.system are defined + if (!this.actor || !this.actor.system) { + console.error("Actor or actor system is undefined."); + ui.notifications.error("Actor or actor system is undefined. Please check your setup."); + return; + } + console.log("Actor and actor system are defined."); + + // Ensure agendaTask and agendaTask.system are defined + if (!affliction || !affliction.system) { + console.error("Affliction or affliction system is undefined."); + ui.notifications.error("Affliction or affliction system is undefined. Please check your setup."); + return; + } + console.log("Affliction and affliction system system are defined."); + + const afflictionList = this.actor.system.afflictions || []; + console.log("Current Task List:", afflictionList); + + afflictionList.push(affliction.id); + console.log("Updated Task List:", afflictionList); + + this.actor.update({ + 'system.afflictions': afflictionList, + }).then(() => { + console.log("Actor updated successfully."); + }).catch(err => { + console.error("Error updating actor:", err); + ui.notifications.error("Error updating actor. Please check the console for more details."); + }); + } + + _addAgendaAbility(event) { + event.preventDefault(); + const abilityID = event.currentTarget.parentElement.querySelector('#selectedAgenda').value; + const currentAbilities = this.actor.system.currentAgendaAbilities; + if (currentAbilities.includes(abilityID)) return; + currentAbilities.push(abilityID); + this.actor.update({'system.currentAgendaAbilities': currentAbilities}); + this.actor.render(true); + } + + _addBlasphemyPower(event) { + event.preventDefault(); + const powerID = event.currentTarget.parentElement.querySelector('.selectedPower').value; + const currentPowers = this.actor.system.currentBlasphemyPowers; + if (currentPowers.includes(powerID)) return; + currentPowers.push(powerID); + this.actor.update({'system.currentBlasphemyPowers': currentPowers}); + this.actor.render(true); + } + + async _addAffliction(event) { + event.preventDefault(); + console.log("adding affliction"); + const dialogResult = await Dialog.wait({ + title: "Add Affliction", + content: `

This lets you create a new affliction. If your Admin has an existing one in mind, they should add it from the player overview section or by dragging it to you sheet.

+
+
+ +
`, + buttons: { + submit: { label: "Submit", callback: (html) => { + const formElement = html[0].querySelector('form'); + const formData = new FormDataExtended(formElement); + const formDataObject = formData.object; + return formDataObject; + }}, + cancel: { label: "Cancel" }, + } + }, {height: 500}); + if (dialogResult === 'cancel') return; + let afflictionFolderFolder = game.folders.find(f => f.name === "Afflictions" && f.type === "Item"); + if (!afflictionFolderFolder) { + afflictionFolderFolder = await Folder.create({ + name: "Afflictions", + type: "Item", + folder: null, // Set a parent folder ID if nesting is desired + sorting: "m", // 'm' for manual sorting, 'a' for alphabetical + }); + } + + let afflictionFolder = game.folders.find(f => f.name === "Misc Afflictions" && f.type === "Item"); + if (!afflictionFolder) { + afflictionFolder = await Folder.create({ + name: "Misc Afflictions", + type: "Item", + folder: afflictionFolderFolder.id, // Set a parent folder ID if nesting is desired + sorting: "m", // 'm' for manual sorting, 'a' for alphabetical + }); + } + const createdAfflictionData = { + name: dialogResult.afflictionName, + type: "affliction", // Ensure this matches the item type defined in your game system + img: "icons/svg/item-bag.svg", + folder: afflictionFolder.id, // Assign the item to the folder + system: { + afflictionName: dialogResult.afflictionName, + afflictionDescription: dialogResult.afflictionDescription + } + }; + const createdAffliction = await Item.create(createdAfflictionData); + const afflictionList = this.actor.system.afflictions; + afflictionList.push(createdAffliction.id); + this.actor.update({'system.afflictions': afflictionList}); + } + + + async _addNewTask(event) { + event.preventDefault(); + console.log("adding task"); + const dialogResult = await Dialog.wait({ + title: "Add Task", + content: `

This lets you create a new task. If you or your Admin has an existing one in mind, they should add by dragging it to you sheet.

+
+
+ +
`, + buttons: { + submit: { label: "Submit", callback: (html) => { + const formElement = html[0].querySelector('form'); + const formData = new FormDataExtended(formElement); + const formDataObject = formData.object; + return formDataObject; + }}, + cancel: { label: "Cancel" }, + } + }, {height: 500}); + if (dialogResult === 'cancel') return; + let agendaFolderFolder = game.folders.find(f => f.name === "Agendas/Tasks" && f.type === "Item"); + if (!agendaFolderFolder) { + agendaFolderFolder = await Folder.create({ + name: "Agendas/Tasks", + type: "Item", + folder: null, // Set a parent folder ID if nesting is desired + sorting: "m", // 'm' for manual sorting, 'a' for alphabetical + }); + } + + let agendaFolder = game.folders.find(f => f.name === "Misc Tasks" && f.type === "Item"); + if (!agendaFolder) { + agendaFolder = await Folder.create({ + name: "Misc Tasks", + type: "Item", + folder: agendaFolderFolder.id, // Set a parent folder ID if nesting is desired + sorting: "m", // 'm' for manual sorting, 'a' for alphabetical + }); + } + const createdTaskData = { + name: dialogResult.task, + type: "agendaTask", // Ensure this matches the item type defined in your game system + img: "icons/svg/item-bag.svg", + folder: agendaFolder.id, // Assign the item to the folder + system: { + task: dialogResult.task, + isBold: dialogResult.isBold + } + }; + const createdTask = await Item.create(createdTaskData); + const boldTaskList = this.actor.system.currentBoldedAgendaTasks; + const unboldTaskList = this.actor.system.currentUnboldedAgendaTasks; + if (dialogResult.isBold) { + boldTaskList.push(createdTask.id); + } else { + unboldTaskList.push(createdTask.id); + } + this.actor.update({ + 'system.currentBoldedAgendaTasks': boldTaskList, + 'system.currentUnboldedAgendaTasks': unboldTaskList, + }); + } + + _removeAgendaTask(event) { + event.preventDefault(); + const index = event.currentTarget.getAttribute('data-index'); + if (event.currentTarget.hasAttribute('data-bold')) { + const agendaBoldedTasks = this.actor.system.currentBoldedAgendaTasks; + const newAgendaBoldedTasks = agendaBoldedTasks.slice(0, index).concat(agendaBoldedTasks.slice(Number(index)+1)); + this.actor.update({'system.currentBoldedAgendaTasks': newAgendaBoldedTasks}); + } else { + const agendaUnboldedTasks = this.actor.system.currentUnboldedAgendaTasks; + const newAgendaUnboldedTasks = agendaUnboldedTasks.slice(0, index).concat(agendaUnboldedTasks.slice(Number(index)+1)); + this.actor.update({'system.currentUnboldedAgendaTasks': newAgendaUnboldedTasks}); + } + } + _addQuestion(event) { event.preventDefault(); const questions = this.actor.system.severeAbilityQuestions || []; @@ -278,43 +1088,66 @@ export class CainActorSheet extends ActorSheet { console.log("Updated xp from " + oldXPValue + " to " + newXPValue ); } } + + _decreaseXPValue(event) { + event.preventDefault(); + const oldXPValue = this.actor.system.xp.value; + const newXPValue = Math.max(oldXPValue - 1, 0); + //No need to check for advancement when decreasing + this.actor.update({ 'system.xp.value': newXPValue}); + console.log("Updated xp from " + oldXPValue + " to " + newXPValue ); + } - _boldAgendaItem(event) { + _increaseMaxXPValue(event) { event.preventDefault(); - const index = event.currentTarget.getAttribute('data-index'); - const textarea = document.querySelector(`.editable-item-input[data-index="${index}"]`); - const list = this.actor.system.currentAgendaItems; - - list[index].isBold = !list[index].isBold; - textarea.style.fontWeight = list[index].isBold ? 'bold' : 'normal'; - event.currentTarget.querySelector('i').classList.toggle('active', list[index].isBold); - - console.log(`Bolding item at index ${index}: ${list[index].isBold}`); - - this.actor.update({ 'system.currentAgendaItems': list }); + const oldXPValue = this.actor.system.xp.max; + const newXPValue = oldXPValue + 1; + this.actor.update({ 'system.xp.max': newXPValue}); + console.log("Updated max xp from " + oldXPValue + " to " + newXPValue ); + } + + _decreaseMaxXPValue(event) { + event.preventDefault(); + const oldXPValue = this.actor.system.xp.max; + const newXPValue = Math.max(oldXPValue - 1, 1); + //No need to check for advancement when decreasing + this.actor.update({ 'system.xp.max': newXPValue}); + console.log("Updated max xp from " + oldXPValue + " to " + newXPValue ); } - _boldAgendaAbility(event) { + + _openEndSessionModal(event) { event.preventDefault(); - const index = event.currentTarget.getAttribute('data-index'); - const textarea = document.querySelector(`.editable-ability-input[data-index="${index}"]`); - const list = this.actor.system.currentAgendaAbilities; - - list[index].isBold = !list[index].isBold; - textarea.style.fontWeight = list[index].isBold ? 'bold' : 'normal'; - event.currentTarget.querySelector('i').classList.toggle('active', list[index].isBold); - - console.log(`Bolding ability at index ${index}: ${list[index].isBold}`); - - this.actor.update({ 'system.currentAgendaAbilities': list }); + console.log(this.actor); + new SessionEndAdvancement(this.actor).render(true); } - _sendAgendaItemMessage(event) { + _sendAgendaTaskMessage(event) { event.preventDefault(); const index = event.currentTarget.getAttribute('data-index'); - const textarea = document.querySelector(`.editable-item-input[data-index="${index}"]`); - const itemText = textarea.value; - const message = `

${itemText}

`; + let agendaTaskList = []; + if (event.currentTarget.hasAttribute('data-bold')) { + agendaTaskList = this.actor.system.currentBoldedAgendaTasks; + } else { + agendaTaskList = this.actor.system.currentUnboldedAgendaTasks; + } + console.log(index); + console.log(agendaTaskList); + const agendaTask = game.items.get(agendaTaskList[index]); + console.log(agendaTask); + const message = `

${this.actor.name}

${(agendaTask.system.isBold ? '' : '')}${agendaTask.system.task}${(agendaTask.system.isBold ? '' : '')}

`; + ChatMessage.create({ + content: message, + speaker: ChatMessage.getSpeaker({ actor: this.actor }), + }); + } + + _sendBlasphemyPowerMessage(event) { + event.preventDefault(); + const id = event.currentTarget.getAttribute('data-id'); + const blasphemyPower = game.items.get(id); + const formattedDescription = blasphemyPower.system.powerDescription.replace(/\n/g, '
'); + const message = `

${blasphemyPower.system.powerName}

${formattedDescription}

`; ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: this.actor }), @@ -324,9 +1157,9 @@ export class CainActorSheet extends ActorSheet { _sendAgendaAbilityMessage(event) { event.preventDefault(); const index = event.currentTarget.getAttribute('data-index'); - const textarea = document.querySelector(`.editable-ability-input[data-index="${index}"]`); - const abilityText = textarea.value; - const message = `

${abilityText}

`; + const agendaAbility = game.items.get(this.actor.system.currentAgendaAbilities[index]); + const formattedDescription = agendaAbility.system.abilityDescription.replace(/\n/g, '
'); + const message = `

${agendaAbility.system.abilityName}

${formattedDescription}

`; ChatMessage.create({ content: message, speaker: ChatMessage.getSpeaker({ actor: this.actor }), @@ -351,26 +1184,90 @@ export class CainActorSheet extends ActorSheet { }); } - _removeItemButton(event) { + _removeAgendaAbilityButton(event) { event.preventDefault(); const index = event.currentTarget.dataset.index; - const agendaItems = this.actor.system.currentAgendaItems || []; - agendaItems.splice(index, 1); - this.actor.update({ 'system.currentAgendaItems': agendaItems }).then(() => { + const agendaAbilities = this.actor.system.currentAgendaAbilities || []; + const newAgendaAbilities = agendaAbilities.slice(0, Number(index)).concat(agendaAbilities.slice(Number(index)+1)) + this.actor.update({ 'system.currentAgendaAbilities': newAgendaAbilities }).then(() => { this.render(false); // Re-render the sheet to reflect changes }); } - _removeAbilityButton(event) { + _removeAfflictionButton(event) { event.preventDefault(); const index = event.currentTarget.dataset.index; - const agendaAbilities = this.actor.system.currentAgendaAbilities || []; - agendaAbilities.splice(index, 1); - this.actor.update({ 'system.currentAgendaAbilities': agendaAbilities }).then(() => { + const afflictions = this.actor.system.afflictions || []; + const newAfflictions = afflictions.slice(0, Number(index)).concat(afflictions.slice(Number(index)+1)) + console.log(newAfflictions); + this.actor.update({ 'system.afflictions': newAfflictions }).then(() => { + this.render(false); // Re-render the sheet to reflect changes + }); + } + + _removeBlasphemyPowerButton(event) { + event.preventDefault(); + const powerID = event.currentTarget.dataset.id; + const blasphemyPowers = this.actor.system.currentBlasphemyPowers || []; + const index = blasphemyPowers.indexOf(powerID); + const newblasphemyPowers = blasphemyPowers.slice(0, Number(index)).concat(blasphemyPowers.slice(Number(index)+1)) + this.actor.update({ 'system.currentBlasphemyPowers': newblasphemyPowers }).then(() => { + this.render(false); // Re-render the sheet to reflect changes + }); + } + + _removeBlasphemyButton(event) { + event.preventDefault(); + const blasphemyID = event.currentTarget.dataset.id; + console.log(blasphemyID); + const blasphemy = game.items.get(blasphemyID); + console.log(blasphemy); + const blasphemies = this.actor.system.currentBlasphemies || []; + if (!blasphemies.includes(blasphemyID)) {console.error("Tried to remove Blasphemy ID: " + blasphemyID + " but did not find it in list: " + blasphemies); return}; //Break out if we're trying to remove a non-existant blasphemy. + + //Handle reducing XP max if removing a 2nd blasphemy. + let XPmax = this.actor.system.xp.max; + if (blasphemies.length > 1) { + XPmax -= 1; + } + + //Remove the blasphemy + const index = blasphemies.indexOf(blasphemyID); + const newBlasphemies = blasphemies.slice(0, Number(index)).concat(blasphemies.slice(Number(index)+1)); + const blasphemyPowers = this.actor.system.currentBlasphemyPowers || []; + console.log(blasphemy); + const newBlasphemyPowers = blasphemyPowers.filter(powerID => {return !(blasphemy.system.powers.includes(powerID));}); + + this.actor.update({ + 'system.currentBlasphemies': newBlasphemies, + 'system.currentBlasphemyPowers': newBlasphemyPowers, + 'system.xp.max': XPmax, + }).then(() => { this.render(false); // Re-render the sheet to reflect changes }); } + _removeSinMarkAbilityButton(event) { + event.preventDefault(); + const sinMarkIndex = event.currentTarget.dataset.markindex; + console.log(sinMarkIndex); + const sinAbilityIndex = event.currentTarget.dataset.abilityindex; + console.log(sinAbilityIndex); + const sinMark = game.items.get(this.actor.system.sinMarks[sinMarkIndex]); + console.log(sinMark); + console.log(this.actor.system.sinMarkAbilities); + const sinMarkAbilities = this.actor.system.sinMarkAbilities.filter(filterID => {return sinMark.system.abilities.includes(filterID) }); + const sinAbilityID = game.items.get(sinMarkAbilities[sinAbilityIndex]).id + + + //Clear out the removed ability. Note that we don't need to worry about whether it's the last item, because we would have called _deleteSinMark then. + const newSinMarkAbilities = this.actor.system.sinMarkAbilities.filter(filterID => {return filterID != sinAbilityID}); + console.log(newSinMarkAbilities); + + this.actor.update({'system.sinMarkAbilities' : newSinMarkAbilities}); + this.actor.render(true); + } + _updateAgendaItem(event) { const index = event.target.dataset.index; const agendaItems = this.actor.system.currentAgendaItems || []; @@ -425,6 +1322,17 @@ export class CainActorSheet extends ActorSheet { } + _onCATSelect(leftClick, event){ + let selectedCat = event.currentTarget.dataset.cat + if(leftClick){ + //set to new category + this.actor.update({["system.CATLEVEL.value"]: selectedCat}); + } + else{ + //set to 0 + this.actor.update({["system.CATLEVEL.value"]: 0}); + } + } _onRollButtonClick(event) { const skill = document.querySelector('select[name="system.skill"]').value; @@ -593,42 +1501,25 @@ export class CainActorSheet extends ActorSheet { }); } - _onOverflowChange(event) { - const checkboxes = document.querySelectorAll('.sinOverflow-checkbox'); - const isChecked = event.currentTarget.checked; - let newValue = 0; - - checkboxes.forEach(checkbox => { - if (checkbox.checked) { - newValue++; - } - }); - - console.log(isChecked); - console.log(newValue); + _sinChange(event) { + let newValue = event.currentTarget.dataset.sin; + let isEqual = this.actor.system.sinOverflow.value == newValue + console.info(isEqual, isEqual ? newValue -1 : newValue) + this.updateActor('system.sinOverflow.value', isEqual ? newValue -1 : newValue); + } - this.actor.update({ 'system.sinOverflow.value': newValue }).then(() => { - this.render(false); // Re-render the sheet to reflect changes - }); + _clearSin(_) { + this.updateActor('system.sinOverflow.value', 0); } _onKitPointsChange(event) { - const checkboxes = document.querySelectorAll('.kit-points-checkbox'); - const isChecked = event.currentTarget.checked; - let newValue = 0; - - checkboxes.forEach(checkbox => { - if (checkbox.checked) { - newValue++; - } - }); + let newValue = event.currentTarget.dataset.kit; + let isEqual = this.actor.system.kitPoints.value == newValue + this.updateActor('system.kitPoints.value', isEqual ? newValue -1 : newValue); + } - console.log(isChecked); - console.log(newValue); - - this.actor.update({ 'system.kitPoints.value': newValue }).then(() => { - this.render(false); // Re-render the sheet to reflect changes - }); + _clearKitPoints(_){ + this.updateActor('system.kitPoints.value', 0); } @@ -753,73 +1644,188 @@ export class CainActorSheet extends ActorSheet { async _rollSinMark(event) { event.preventDefault(); + + // Get all Sin Mark items + const sinMarkItems = game.items.filter(item => item.type === 'sinMark'); + if (sinMarkItems.length === 0) { + ui.notifications.warn("No Sin Mark items found."); + return; + } + + // Roll 1d6 to determine if the user can choose a Sin Mark + const initialRoll = await new Roll('1d6').roll(); + let selectedSinMark; + + if (initialRoll.total === 6) { + // Allow the user to choose a Sin Mark + const chosenIndex = await this._chooseMark(); + selectedSinMark = sinMarkItems[chosenIndex]; + } else { + // Randomly choose a Sin Mark + const sinMarkRoll = await new Roll(`1d${sinMarkItems.length}`).roll(); + selectedSinMark = sinMarkItems[sinMarkRoll.total - 1]; + } + + console.log(selectedSinMark); + + // Get abilities of the selected Sin Mark + const abilities = selectedSinMark.system.abilities.filter(sinMarkID => {return !this.actor.system.sinMarkAbilities.includes(sinMarkID)}) || []; + if (abilities.length === 0) { + ui.notifications.warn("Selected Sin Mark has no abilities."); + return; + } + + // Get current Sin Marks and Sin Mark Abilities + const currentSinMarks = this.actor.system.sinMarks || []; + const currentSinMarkAbilities = this.actor.system.sinMarkAbilities || []; + + // Check if the Sin Mark is already in the list + const existingMarkIndex = currentSinMarks.indexOf(selectedSinMark.id); + let selectedAbility; + + if (existingMarkIndex !== -1) { + // Sin Mark is already in the list, proceed to select an ability + const maxAttempts = 10; + let attempts = 0; + do { + const abilityRoll = await new Roll(`1d${abilities.length}`).roll({async: true}); + selectedAbility = abilities[abilityRoll.total - 1]; + attempts++; + } while (currentSinMarkAbilities.includes(selectedAbility) && attempts < maxAttempts); + + if (attempts >= maxAttempts) { + ui.notifications.warn("Unable to select a unique ability after multiple attempts."); + return; + } + + currentSinMarkAbilities.push(selectedAbility); + } else { + // Sin Mark is not in the list, add it and select an ability + currentSinMarks.push(selectedSinMark.id); + + const abilityRoll = await new Roll(`1d${abilities.length}`).roll({async: true}); + selectedAbility = abilities[abilityRoll.total - 1]; + + currentSinMarkAbilities.push(selectedAbility); + } + + // Update the actor with the new Sin Marks and Sin Mark Abilities + - // Manually roll 1d6 for the mark - const roll = await new Roll('1d6').roll({async: true}); - let markRoll = roll.total; - let markIndex = markRoll - 1; + await this.actor.update({ + 'system.sinMarks' : currentSinMarks, + 'system.sinMarkAbilities': currentSinMarkAbilities + }); - console.log(markRoll); - console.log(markIndex); + console.log(this.actor); + ui.notifications.info(`Rolled Sin Mark: ${selectedSinMark.name} with ability: ${selectedAbility}`); + + this.render(false); // Re-render the sheet to reflect changes + } + + async _evolveSinMark(event) { + event.preventDefault(); + + // Get all Sin Mark items + const sinMarkItems = this._getItemsFromIDs(this.actor.system.sinMarks); + if (sinMarkItems.length === 0) { + ui.notifications.warn("No Sin Mark items found."); + return; + } + + // Roll 1d6 to determine if the user can choose a Sin Mark + let selectedSinMark = sinMarkItems[event.currentTarget.dataset.markindex]; + console.log(selectedSinMark); - // If rolled a 6, allow the user to choose the mark - if (markRoll === 6) { - markIndex = await this._chooseMark(); + // Get abilities of the selected Sin Mark + const abilities = selectedSinMark.system.abilities.filter(sinMarkID => {return !this.actor.system.sinMarkAbilities.includes(sinMarkID)}) || []; + if (abilities.length === 0) { + ui.notifications.warn("Selected Sin Mark has no abilities."); + return; } - const sinMarks = this.actor.system.currentSinMarks || []; - const sinMark = CAIN.sinMarks[markIndex]; + // Get current Sin Marks and Sin Mark Abilities + const currentSinMarks = this.actor.system.sinMarks || []; + const currentSinMarkAbilities = this.actor.system.sinMarkAbilities || []; + + // Check if the Sin Mark is already in the list + const existingMarkIndex = currentSinMarks.indexOf(selectedSinMark.id); + let selectedAbility; - // Check if the mark already exists - const existingMark = sinMarks.find(mark => mark.startsWith(sinMark.name)); - if (existingMark) { - // Manually roll 1d6 for the ability - let newAbility; + if (existingMarkIndex !== -1) { + // Sin Mark is already in the list, proceed to select an ability + const maxAttempts = 10; + let attempts = 0; do { - const abilityRoll = await new Roll('1d6').roll({async: true}); - newAbility = sinMark.abilities[abilityRoll.total - 1]; - } while (existingMark.includes(newAbility)); + const abilityRoll = await new Roll(`1d${abilities.length}`).roll({async: true}); + selectedAbility = abilities[abilityRoll.total - 1]; + attempts++; + } while (currentSinMarkAbilities.includes(selectedAbility) && attempts < maxAttempts); + + if (attempts >= maxAttempts) { + ui.notifications.warn("Unable to select a unique ability after multiple attempts."); + return; + } - // Add the new ability to the existing mark - sinMarks[sinMarks.indexOf(existingMark)] += `, ${newAbility}`; + currentSinMarkAbilities.push(selectedAbility); } else { - // Manually roll 1d6 for the ability - const abilityRoll = await new Roll('1d6').roll({async: true}); - const newAbility = sinMark.abilities[abilityRoll.total - 1]; - console.log(sinMark); + // Sin Mark is not in the list, add it and select an ability + currentSinMarks.push(selectedSinMark.id); - // Format the sinMark as "Name - Ability" - const formattedSinMark = `${sinMark.name} - ${newAbility}`; + const abilityRoll = await new Roll(`1d${abilities.length}`).roll({async: true}); + selectedAbility = abilities[abilityRoll.total - 1]; - // Add the new mark to the sinMarks array - sinMarks.push(formattedSinMark); + currentSinMarkAbilities.push(selectedAbility); } - // Update the actor with the new sinMarks array - await this.actor.update({ 'system.currentSinMarks': sinMarks }); + // Update the actor with the new Sin Marks and Sin Mark Abilities + + + await this.actor.update({ + 'system.sinMarkAbilities': currentSinMarkAbilities + }); + + console.log(this.actor); + ui.notifications.info(`Rolled Sin Mark: ${selectedSinMark.name} with ability: ${selectedAbility}`); + this.render(false); // Re-render the sheet to reflect changes } - + async _deleteSinMark(event) { event.preventDefault(); - const index = event.currentTarget.dataset.index; - const sinMarks = this.actor.system.currentSinMarks || []; - sinMarks.splice(index, 1); - this.actor.update({ 'system.currentSinMarks': sinMarks }).then(() => { - this.render(false); // Re-render the sheet to reflect changes + const index = event.currentTarget.dataset.markindex; + console.log(index); + const sinMarks = this.actor.system.sinMarks || []; + console.log(sinMarks); + const sinMarkAbilities = this.actor.system.sinMarkAbilities || []; + const sinMark = game.items.get(this.actor.system.sinMarks[index]); + console.log(sinMark); + + // Remove the selected Sin Mark and its abilities + const newSinMarks = sinMarks.slice(0, Number(index)).concat(sinMarks.slice(Number(index)+1)); + const newSinMarkAbilities = sinMarkAbilities.filter(sinMarkAbilityID => {return !sinMark.system.abilities.includes(sinMarkAbilityID)}); + + await this.actor.update({ + 'system.sinMarks': newSinMarks, + 'system.sinMarkAbilities': newSinMarkAbilities }); + + this.render(false); // Re-render the sheet to reflect changes } - + async _clearSinMarks(event) { event.preventDefault(); - this.actor.update({ 'system.currentSinMarks': [] }).then(() => { - this.render(false); // Re-render the sheet to reflect changes + await this.actor.update({ + 'system.sinMarks': [], + 'system.sinMarkAbilities': [] }); + + this.render(false); // Re-render the sheet to reflect changes } async _chooseMark() { return new Promise((resolve) => { - const options = CAIN.sinMarks.map((mark, index) => ``).join(''); + const options = game.items.filter(item => item.type === 'sinMark').map((mark, index) => ``).join(''); const content = `
@@ -1087,6 +2093,13 @@ export class CainActorSheet extends ActorSheet { speaker: ChatMessage.getSpeaker({ actor: this.actor }) }); } + + async updateActor(key, value){ + let obj = {} + obj[key] = value + this.actor.update(obj); + } + _onSinTypeSelect(sinType) { const sinTypeMapping = { ogre: { diff --git a/module/sheets/item-sheet.mjs b/module/sheets/item-sheet.mjs index 06750e8..47c8ccd 100644 --- a/module/sheets/item-sheet.mjs +++ b/module/sheets/item-sheet.mjs @@ -12,8 +12,8 @@ export class CainItemSheet extends ItemSheet { static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ['cain', 'sheet', 'item'], - width: 520, - height: 480, + width: 640, + height: 640, tabs: [ { navSelector: '.sheet-tabs', @@ -61,6 +61,36 @@ export class CainItemSheet extends ItemSheet { } ); + if (this.item.type === "agenda") { + context.agendaBoldedTaskData = this.item.system.boldedTasks.map(item => {return game.items.get(item);}); + context.agendaUnboldedTaskData = this.item.system.unboldedTasks.map(item => {return game.items.get(item);}); + context.agendaAbilityData = this.item.system.abilities.map(item => {return game.items.get(item);}); + console.log(this.item.system.abilities); + console.log(context.agendaAbilityData); + context.agenda_tasks = game.items.contents.map(item => { if (item.type === "agendaTask") + return { + id: item.id, + name: item.name + }; return {name: "INVALID"}; + }).filter(item => item.name !== "INVALID"); + context.agenda_abilities = game.items.contents.map(item => { if (item.type === "agendaAbility") + return { + id: item.id, + name: item.name + };; return {name: "INVALID"}; + }).filter(item => item.name !== "INVALID"); + } + if (this.item.type === "blasphemy") { + context.blasphemyPassives = this.item.system.powers.map(item => {return game.items.get(item);}).filter(item => {return item.system.isPassive}); + context.blasphemyPowers = this.item.system.powers.map(item => {return game.items.get(item);}).filter(item => {return !item.system.isPassive}); + } + if (this.item.type === "sinMark") { + console.log("Context:", context) + console.log("Item:", this.item) + context.sinMarkAbilities = this.item.system.abilities.map(item => {return game.items.get(item);}); + } + + context.developerMode = game.settings.get('cain', 'developerMode'); // Add the item's data to context.data for easier access, as well as flags. context.system = itemData.system; context.flags = itemData.flags; @@ -71,6 +101,13 @@ export class CainItemSheet extends ItemSheet { // Prepare active effects for easier access context.effects = prepareActiveEffectCategories(this.item.effects); + // Set CSS variables for colors + const root = document.documentElement; + root.style.setProperty('--primary-color', this.item.system.primaryColor || '#000000'); + root.style.setProperty('--accent-color', this.item.system.accentColor || '#FFFFFF'); + root.style.setProperty('--secondary-color', this.item.system.secondaryColor || '#CCCCCC'); + root.style.setProperty('--text-color', this.item.system.textColor || '#000000'); + return context; } @@ -89,5 +126,132 @@ export class CainItemSheet extends ItemSheet { html.on('click', '.effect-control', (ev) => onManageActiveEffect(ev, this.item) ); + + html.find('#addTaskToAgenda').click(this._addTaskToAgenda.bind(this)); + + // Color pickers + html.find('input[type="color"]').on('input', this._updateColor.bind(this, html)); + + // html click item to open + html.find('.item-click').click((event) => { + console.log('Item clicked'); + console.log(event.currentTarget) + const itemId = $(event.currentTarget).data('id'); + console.log(itemId); + const item = Item.get(itemId); + console.log(item); + if (item) { + item.sheet.render(true); + } + }); + + // Setup color picker functionality + setupColorPicker(html); + + function setupColorPicker(html) { + html.find('.color-picker-toggle').click(() => { + console.log('Color picker toggle clicked'); + const colorPickerContainer = html.find('.color-picker-container'); + console.log(colorPickerContainer); + const colorPickers = html.find('.color-pickers'); + console.log(colorPickerContainer.hasClass('active')); + + if (colorPickerContainer.hasClass('active')) { + console.log('Closing color picker'); + colorPickers.css('transform', 'translateX(-30px)'); + colorPickers.css('opacity', '0'); + colorPickers.css('pointer-events', 'none'); // Disable interaction + setTimeout(() => { + colorPickerContainer.removeClass('active'); + }, 500); // Match the transition duration + } else { + colorPickerContainer.addClass('active'); + setTimeout(() => { + colorPickers.css('transform', 'translateX(0)'); + colorPickers.css('opacity', '1'); + colorPickers.css('pointer-events', 'auto'); // Enable interaction + }, 10); // Small delay to trigger the transition + } + }); + + html.on('click', (event) => { + const colorPickerContainer = html.find('.color-picker-container'); + const colorPickerToggle = html.find('.color-picker-toggle'); + const colorPickers = html.find('.color-pickers'); + + if (!colorPickerContainer.is(event.target) && !colorPickerToggle.is(event.target) && colorPickerContainer.has(event.target).length === 0) { + colorPickerContainer.removeClass('active'); + colorPickers.css('transform', 'translateX(-30px)'); + colorPickers.css('opacity', '0'); + colorPickers.css('pointer-events', 'none'); // Disable interaction + } + }); + } + + // Add ability to the page + html.find('#addAbility').click(this._addAbility.bind(this)); + html.find('#removeAbility').click(this._removeAbility.bind(this)); } -} + + _addTaskToAgenda(event) { + let value = event.currentTarget.parentElement.parentElement.querySelector('#selectedItem').value + + const unboldedTasks = this.item.system.unboldedTasks || []; + const boldedTasks = this.item.system.unboldedTasks || []; + console.log(unboldedTasks); + console.log(boldedTasks); + const newTask = value; + const newTaskItem = game.items.get(newTask); + if (newTaskItem.system.isBold) { + boldedTasks.push(newTask); + this.item.update({'system.boldedTasks': boldedTasks}); + } else { + unboldedTasks.push(newTask); + this.item.update({'system.unboldedTasks': unboldedTasks}); + } + console.log(newTask) + console.log(this.item.system); + } + + _updateColor(html, event) { + const colorType = event.target.getAttribute('data-id'); + const colorValue = event.target.value; + const eventId = event.target.id; + console.log(`Color picker event: ${eventId}`); + console.log(`Updating color: ${colorType} to ${colorValue}`); + this.item.update({[`system.${eventId}`]: colorValue}, {render: false}).then(() => { + html[0].style.setProperty(`--${colorType}`, colorValue); + console.log(`CSS variable --${colorType} set to ${colorValue}`); + }).catch(err => { + console.error(`Error updating item with ${colorType}: ${colorValue}`, err); + }); + } + + async _addAbility(event) { + event.preventDefault(); + const abilityUUID = event.currentTarget.parentElement.querySelector('#abilityUUID').value; + const abilityItem = await fromUuid(abilityUUID); + if (abilityItem) { + const abilities = this.item.system.abilities || []; + abilities.push(abilityItem.id); + await this.item.update({ 'system.abilities': abilities }); + ui.notifications.info(`Added ability: ${abilityItem.name}`); + } else { + ui.notifications.error('Invalid ability UUID'); + } + } + + async _removeAbility(event) { + event.preventDefault(); + const abilities = this.item.system.abilities || []; + if (abilities.length > 0) { + const lastAbilityId = abilities.pop(); + const lastAbilityItem = game.items.get(lastAbilityId); + await this.item.update({ 'system.abilities': abilities }); + ui.notifications.info(`Removed ability: ${lastAbilityItem.name}`); + } else { + ui.notifications.error('No abilities to remove'); + } + } + +} \ No newline at end of file diff --git a/packs/afflictions/000043.ldb b/packs/afflictions/000043.ldb new file mode 100644 index 0000000..c4c4f42 Binary files /dev/null and b/packs/afflictions/000043.ldb differ diff --git a/packs/agenda/000057.log b/packs/afflictions/000047.log similarity index 100% rename from packs/agenda/000057.log rename to packs/afflictions/000047.log diff --git a/packs/afflictions/CURRENT b/packs/afflictions/CURRENT new file mode 100644 index 0000000..a6d5883 --- /dev/null +++ b/packs/afflictions/CURRENT @@ -0,0 +1 @@ +MANIFEST-000046 diff --git a/packs/agenda/LOCK b/packs/afflictions/LOCK similarity index 100% rename from packs/agenda/LOCK rename to packs/afflictions/LOCK diff --git a/packs/afflictions/LOG b/packs/afflictions/LOG new file mode 100644 index 0000000..19f5eed --- /dev/null +++ b/packs/afflictions/LOG @@ -0,0 +1,3 @@ +2024/09/04-18:13:26.259 7ad8 Recovering log #45 +2024/09/04-18:13:26.266 7ad8 Delete type=0 #45 +2024/09/04-18:13:26.266 7ad8 Delete type=3 #44 diff --git a/packs/afflictions/LOG.old b/packs/afflictions/LOG.old new file mode 100644 index 0000000..467d87c --- /dev/null +++ b/packs/afflictions/LOG.old @@ -0,0 +1,3 @@ +2024/09/04-14:04:37.278 8cdc Recovering log #41 +2024/09/04-14:04:37.284 8cdc Delete type=0 #41 +2024/09/04-14:04:37.284 8cdc Delete type=3 #38 diff --git a/packs/afflictions/MANIFEST-000046 b/packs/afflictions/MANIFEST-000046 new file mode 100644 index 0000000..55f765f Binary files /dev/null and b/packs/afflictions/MANIFEST-000046 differ diff --git a/packs/agenda/000023.ldb b/packs/agenda/000023.ldb deleted file mode 100644 index e9a153d..0000000 Binary files a/packs/agenda/000023.ldb and /dev/null differ diff --git a/packs/agenda/CURRENT b/packs/agenda/CURRENT deleted file mode 100644 index 80d9de0..0000000 --- a/packs/agenda/CURRENT +++ /dev/null @@ -1 +0,0 @@ -MANIFEST-000056 diff --git a/packs/agenda/LOG b/packs/agenda/LOG deleted file mode 100644 index d7b3b3f..0000000 --- a/packs/agenda/LOG +++ /dev/null @@ -1,3 +0,0 @@ -2024/08/25-05:39:35.236025 ffff8dfbf120 Recovering log #54 -2024/08/25-05:39:35.264440 ffff8dfbf120 Delete type=3 #52 -2024/08/25-05:39:35.264804 ffff8dfbf120 Delete type=0 #54 diff --git a/packs/agenda/LOG.old b/packs/agenda/LOG.old deleted file mode 100644 index 1c85550..0000000 --- a/packs/agenda/LOG.old +++ /dev/null @@ -1,8 +0,0 @@ -2024/08/24-20:05:21.813863 ffff8e7cf120 Recovering log #50 -2024/08/24-20:05:21.845439 ffff8e7cf120 Delete type=0 #50 -2024/08/24-20:05:21.845731 ffff8e7cf120 Delete type=3 #48 -2024/08/25-05:39:28.842552 ffff8d7af120 Level-0 table #55: started -2024/08/25-05:39:28.842742 ffff8d7af120 Level-0 table #55: 0 bytes OK -2024/08/25-05:39:28.872796 ffff8d7af120 Delete type=0 #53 -2024/08/25-05:39:28.902798 ffff8d7af120 Manual compaction at level-0 from '!items!1PnBSz1PZVcYKaWa' @ 72057594037927935 : 1 .. '!items!umnOFhEQgDzG2eaF' @ 0 : 0; will stop at (end) -2024/08/25-05:39:28.903133 ffff8d7af120 Manual compaction at level-1 from '!items!1PnBSz1PZVcYKaWa' @ 72057594037927935 : 1 .. '!items!umnOFhEQgDzG2eaF' @ 0 : 0; will stop at (end) diff --git a/packs/agenda/MANIFEST-000056 b/packs/agenda/MANIFEST-000056 deleted file mode 100644 index 379ec84..0000000 Binary files a/packs/agenda/MANIFEST-000056 and /dev/null differ diff --git a/packs/agenda2/000144.ldb b/packs/agenda2/000144.ldb new file mode 100644 index 0000000..c4caec1 Binary files /dev/null and b/packs/agenda2/000144.ldb differ diff --git a/packs/blasphemy/000057.log b/packs/agenda2/000148.log similarity index 100% rename from packs/blasphemy/000057.log rename to packs/agenda2/000148.log diff --git a/packs/agenda2/CURRENT b/packs/agenda2/CURRENT new file mode 100644 index 0000000..1021bdc --- /dev/null +++ b/packs/agenda2/CURRENT @@ -0,0 +1 @@ +MANIFEST-000147 diff --git a/packs/blasphemy/LOCK b/packs/agenda2/LOCK similarity index 100% rename from packs/blasphemy/LOCK rename to packs/agenda2/LOCK diff --git a/packs/agenda2/LOG b/packs/agenda2/LOG new file mode 100644 index 0000000..756077a --- /dev/null +++ b/packs/agenda2/LOG @@ -0,0 +1,3 @@ +2024/09/04-18:13:26.217 7ad8 Recovering log #146 +2024/09/04-18:13:26.224 7ad8 Delete type=0 #146 +2024/09/04-18:13:26.224 7ad8 Delete type=3 #145 diff --git a/packs/agenda2/LOG.old b/packs/agenda2/LOG.old new file mode 100644 index 0000000..647e4b7 --- /dev/null +++ b/packs/agenda2/LOG.old @@ -0,0 +1,3 @@ +2024/09/04-14:04:37.237 8cdc Recovering log #141 +2024/09/04-14:04:37.244 8cdc Delete type=0 #141 +2024/09/04-14:04:37.244 8cdc Delete type=3 #138 diff --git a/packs/agenda2/MANIFEST-000147 b/packs/agenda2/MANIFEST-000147 new file mode 100644 index 0000000..e58c80d Binary files /dev/null and b/packs/agenda2/MANIFEST-000147 differ diff --git a/packs/blasphemy/000023.ldb b/packs/blasphemy/000023.ldb deleted file mode 100644 index 121353a..0000000 Binary files a/packs/blasphemy/000023.ldb and /dev/null differ diff --git a/packs/blasphemy/CURRENT b/packs/blasphemy/CURRENT deleted file mode 100644 index 80d9de0..0000000 --- a/packs/blasphemy/CURRENT +++ /dev/null @@ -1 +0,0 @@ -MANIFEST-000056 diff --git a/packs/blasphemy/LOG b/packs/blasphemy/LOG deleted file mode 100644 index 473ba38..0000000 --- a/packs/blasphemy/LOG +++ /dev/null @@ -1,3 +0,0 @@ -2024/08/25-05:39:35.278005 ffff8e7cf120 Recovering log #54 -2024/08/25-05:39:35.311838 ffff8e7cf120 Delete type=3 #52 -2024/08/25-05:39:35.312174 ffff8e7cf120 Delete type=0 #54 diff --git a/packs/blasphemy/LOG.old b/packs/blasphemy/LOG.old deleted file mode 100644 index f878c3e..0000000 --- a/packs/blasphemy/LOG.old +++ /dev/null @@ -1,8 +0,0 @@ -2024/08/24-20:05:21.858261 ffff8efdf120 Recovering log #50 -2024/08/24-20:05:21.890679 ffff8efdf120 Delete type=0 #50 -2024/08/24-20:05:21.890942 ffff8efdf120 Delete type=3 #48 -2024/08/25-05:39:28.791218 ffff8d7af120 Level-0 table #55: started -2024/08/25-05:39:28.791524 ffff8d7af120 Level-0 table #55: 0 bytes OK -2024/08/25-05:39:28.842234 ffff8d7af120 Delete type=0 #53 -2024/08/25-05:39:28.902690 ffff8d7af120 Manual compaction at level-0 from '!folders!FWZZgy9tnHSHa9tk' @ 72057594037927935 : 1 .. '!items!yotw9uAVluGmTHO4' @ 0 : 0; will stop at (end) -2024/08/25-05:39:28.903052 ffff8d7af120 Manual compaction at level-1 from '!folders!FWZZgy9tnHSHa9tk' @ 72057594037927935 : 1 .. '!items!yotw9uAVluGmTHO4' @ 0 : 0; will stop at (end) diff --git a/packs/blasphemy/MANIFEST-000056 b/packs/blasphemy/MANIFEST-000056 deleted file mode 100644 index 17cc610..0000000 Binary files a/packs/blasphemy/MANIFEST-000056 and /dev/null differ diff --git a/packs/blasphemy2/000148.ldb b/packs/blasphemy2/000148.ldb new file mode 100644 index 0000000..1856128 Binary files /dev/null and b/packs/blasphemy2/000148.ldb differ diff --git a/packs/blasphemy2/000152.log b/packs/blasphemy2/000152.log new file mode 100644 index 0000000..bb760b5 Binary files /dev/null and b/packs/blasphemy2/000152.log differ diff --git a/packs/blasphemy2/CURRENT b/packs/blasphemy2/CURRENT new file mode 100644 index 0000000..3f137c9 --- /dev/null +++ b/packs/blasphemy2/CURRENT @@ -0,0 +1 @@ +MANIFEST-000151 diff --git a/packs/items/000058.log b/packs/blasphemy2/LOCK similarity index 100% rename from packs/items/000058.log rename to packs/blasphemy2/LOCK diff --git a/packs/blasphemy2/LOG b/packs/blasphemy2/LOG new file mode 100644 index 0000000..e761a2a --- /dev/null +++ b/packs/blasphemy2/LOG @@ -0,0 +1,3 @@ +2024/09/04-18:13:26.228 6288 Recovering log #150 +2024/09/04-18:13:26.234 6288 Delete type=0 #150 +2024/09/04-18:13:26.234 6288 Delete type=3 #149 diff --git a/packs/blasphemy2/LOG.old b/packs/blasphemy2/LOG.old new file mode 100644 index 0000000..e96b66a --- /dev/null +++ b/packs/blasphemy2/LOG.old @@ -0,0 +1,3 @@ +2024/09/04-14:04:37.247 37e4 Recovering log #145 +2024/09/04-14:04:37.254 37e4 Delete type=0 #145 +2024/09/04-14:04:37.254 37e4 Delete type=3 #142 diff --git a/packs/blasphemy2/MANIFEST-000151 b/packs/blasphemy2/MANIFEST-000151 new file mode 100644 index 0000000..391f3ef Binary files /dev/null and b/packs/blasphemy2/MANIFEST-000151 differ diff --git a/packs/items/000024.ldb b/packs/items/000091.ldb similarity index 100% rename from packs/items/000024.ldb rename to packs/items/000091.ldb diff --git a/packs/tables/000057.log b/packs/items/000145.log similarity index 100% rename from packs/tables/000057.log rename to packs/items/000145.log diff --git a/packs/items/CURRENT b/packs/items/CURRENT index ac18047..3e77273 100644 --- a/packs/items/CURRENT +++ b/packs/items/CURRENT @@ -1 +1 @@ -MANIFEST-000057 +MANIFEST-000144 diff --git a/packs/items/LOG b/packs/items/LOG index 529bd3d..8cb1d28 100644 --- a/packs/items/LOG +++ b/packs/items/LOG @@ -1,3 +1,3 @@ -2024/08/25-05:39:35.194578 ffff8efdf120 Recovering log #55 -2024/08/25-05:39:35.222310 ffff8efdf120 Delete type=3 #53 -2024/08/25-05:39:35.222703 ffff8efdf120 Delete type=0 #55 +2024/09/04-18:13:26.207 75f4 Recovering log #143 +2024/09/04-18:13:26.212 75f4 Delete type=0 #143 +2024/09/04-18:13:26.212 75f4 Delete type=3 #142 diff --git a/packs/items/LOG.old b/packs/items/LOG.old index 2040090..09da94c 100644 --- a/packs/items/LOG.old +++ b/packs/items/LOG.old @@ -1,8 +1,3 @@ -2024/08/24-20:05:21.765034 ffff8dfbf120 Recovering log #51 -2024/08/24-20:05:21.798715 ffff8dfbf120 Delete type=0 #51 -2024/08/24-20:05:21.799058 ffff8dfbf120 Delete type=3 #49 -2024/08/25-05:39:28.873233 ffff8d7af120 Level-0 table #56: started -2024/08/25-05:39:28.873462 ffff8d7af120 Level-0 table #56: 0 bytes OK -2024/08/25-05:39:28.902247 ffff8d7af120 Delete type=0 #54 -2024/08/25-05:39:28.902900 ffff8d7af120 Manual compaction at level-0 from '!folders!DYa1yCl9CNfOahIN' @ 72057594037927935 : 1 .. '!items!zUOSvdFDFvXZqROE' @ 0 : 0; will stop at (end) -2024/08/25-05:39:28.903231 ffff8d7af120 Manual compaction at level-1 from '!folders!DYa1yCl9CNfOahIN' @ 72057594037927935 : 1 .. '!items!zUOSvdFDFvXZqROE' @ 0 : 0; will stop at (end) +2024/09/04-14:04:37.228 b8f8 Recovering log #140 +2024/09/04-14:04:37.234 b8f8 Delete type=0 #140 +2024/09/04-14:04:37.234 b8f8 Delete type=3 #138 diff --git a/packs/items/MANIFEST-000144 b/packs/items/MANIFEST-000144 new file mode 100644 index 0000000..420d4cf Binary files /dev/null and b/packs/items/MANIFEST-000144 differ diff --git a/packs/items/lost/000068.log b/packs/items/lost/000068.log new file mode 100644 index 0000000..e69de29 diff --git a/packs/items/lost/000084.log b/packs/items/lost/000084.log new file mode 100644 index 0000000..e69de29 diff --git a/packs/items/MANIFEST-000057 b/packs/items/lost/MANIFEST-000067 similarity index 67% rename from packs/items/MANIFEST-000057 rename to packs/items/lost/MANIFEST-000067 index efd07bf..2b3a27e 100644 Binary files a/packs/items/MANIFEST-000057 and b/packs/items/lost/MANIFEST-000067 differ diff --git a/packs/items/lost/MANIFEST-000083 b/packs/items/lost/MANIFEST-000083 new file mode 100644 index 0000000..f0ef165 Binary files /dev/null and b/packs/items/lost/MANIFEST-000083 differ diff --git a/packs/sin-marks/000018.ldb b/packs/sin-marks/000018.ldb new file mode 100644 index 0000000..1514bfc Binary files /dev/null and b/packs/sin-marks/000018.ldb differ diff --git a/packs/sin-marks/000062.log b/packs/sin-marks/000062.log new file mode 100644 index 0000000..e69de29 diff --git a/packs/sin-marks/CURRENT b/packs/sin-marks/CURRENT new file mode 100644 index 0000000..ebafc63 --- /dev/null +++ b/packs/sin-marks/CURRENT @@ -0,0 +1 @@ +MANIFEST-000061 diff --git a/packs/sin-marks/LOCK b/packs/sin-marks/LOCK new file mode 100644 index 0000000..e69de29 diff --git a/packs/sin-marks/LOG b/packs/sin-marks/LOG new file mode 100644 index 0000000..9f5460b --- /dev/null +++ b/packs/sin-marks/LOG @@ -0,0 +1,3 @@ +2024/09/04-18:13:26.249 75f4 Recovering log #60 +2024/09/04-18:13:26.255 75f4 Delete type=0 #60 +2024/09/04-18:13:26.255 75f4 Delete type=3 #59 diff --git a/packs/sin-marks/LOG.old b/packs/sin-marks/LOG.old new file mode 100644 index 0000000..b3073ae --- /dev/null +++ b/packs/sin-marks/LOG.old @@ -0,0 +1,3 @@ +2024/09/04-14:04:37.267 b8f8 Recovering log #57 +2024/09/04-14:04:37.274 b8f8 Delete type=0 #57 +2024/09/04-14:04:37.274 b8f8 Delete type=3 #55 diff --git a/packs/sin-marks/MANIFEST-000061 b/packs/sin-marks/MANIFEST-000061 new file mode 100644 index 0000000..d4fcde1 Binary files /dev/null and b/packs/sin-marks/MANIFEST-000061 differ diff --git a/packs/tables/000141.log b/packs/tables/000141.log new file mode 100644 index 0000000..e69de29 diff --git a/packs/tables/CURRENT b/packs/tables/CURRENT index 80d9de0..76519d9 100644 --- a/packs/tables/CURRENT +++ b/packs/tables/CURRENT @@ -1 +1 @@ -MANIFEST-000056 +MANIFEST-000140 diff --git a/packs/tables/LOG b/packs/tables/LOG index bada782..8dc9878 100644 --- a/packs/tables/LOG +++ b/packs/tables/LOG @@ -1,3 +1,3 @@ -2024/08/25-05:39:35.327081 ffff8f7ef120 Recovering log #54 -2024/08/25-05:39:35.353747 ffff8f7ef120 Delete type=3 #52 -2024/08/25-05:39:35.353947 ffff8f7ef120 Delete type=0 #54 +2024/09/04-18:13:26.239 320 Recovering log #139 +2024/09/04-18:13:26.245 320 Delete type=0 #139 +2024/09/04-18:13:26.245 320 Delete type=3 #138 diff --git a/packs/tables/LOG.old b/packs/tables/LOG.old index bec3a5b..dd1cf1a 100644 --- a/packs/tables/LOG.old +++ b/packs/tables/LOG.old @@ -1,8 +1,3 @@ -2024/08/24-20:05:21.906434 ffff8f7ef120 Recovering log #50 -2024/08/24-20:05:21.940867 ffff8f7ef120 Delete type=0 #50 -2024/08/24-20:05:21.941181 ffff8f7ef120 Delete type=3 #48 -2024/08/25-05:39:28.933545 ffff8d7af120 Level-0 table #55: started -2024/08/25-05:39:28.933759 ffff8d7af120 Level-0 table #55: 0 bytes OK -2024/08/25-05:39:28.965190 ffff8d7af120 Delete type=0 #53 -2024/08/25-05:39:29.032325 ffff8d7af120 Manual compaction at level-0 from '!tables!XY3o3oqawjUe6Wba' @ 72057594037927935 : 1 .. '!tables.results!z9VPZiT6MhJzjO6v.vwR7xQw1HrRWhrQn' @ 0 : 0; will stop at (end) -2024/08/25-05:39:29.033457 ffff8d7af120 Manual compaction at level-1 from '!tables!XY3o3oqawjUe6Wba' @ 72057594037927935 : 1 .. '!tables.results!z9VPZiT6MhJzjO6v.vwR7xQw1HrRWhrQn' @ 0 : 0; will stop at (end) +2024/09/04-14:04:37.258 b708 Recovering log #136 +2024/09/04-14:04:37.264 b708 Delete type=0 #136 +2024/09/04-14:04:37.264 b708 Delete type=3 #134 diff --git a/packs/tables/MANIFEST-000056 b/packs/tables/MANIFEST-000140 similarity index 71% rename from packs/tables/MANIFEST-000056 rename to packs/tables/MANIFEST-000140 index 886924c..3bd2d22 100644 Binary files a/packs/tables/MANIFEST-000056 and b/packs/tables/MANIFEST-000140 differ diff --git a/src/scss/components/_items.scss b/src/scss/components/_items.scss index d7636ba..8490cd6 100644 --- a/src/scss/components/_items.scss +++ b/src/scss/components/_items.scss @@ -45,11 +45,13 @@ margin: 0; overflow: hidden; font-size: 13px; - text-align: left; + text-align: center; align-items: center; + justify-content: center; display: flex; h3, h4 { margin: 0; + text-align: center; white-space: nowrap; overflow-x: hidden; } diff --git a/system.json b/system.json index 94486c4..f104abe 100644 --- a/system.json +++ b/system.json @@ -20,7 +20,7 @@ "thumbnail": "systems/cain/assets/cain.png" } ], - "version": "1.0.24", + "version": "1.1.0", "compatibility": { "minimum": 11, "verified": "12" @@ -43,18 +43,18 @@ "path": "packs/items" }, { - "name": "agenda", + "name": "agendas", "label": "Agendas", "system": "cain", "type": "Item", - "path": "packs/agenda" + "path": "packs/agenda2" }, { - "name": "blasphemy", + "name": "blasphemies", "label": "Blasphemies", "system": "cain", "type": "Item", - "path": "packs/blasphemy" + "path": "packs/blasphemy2" }, { "name": "tables", @@ -62,6 +62,20 @@ "system": "cain", "type": "RollTable", "path": "packs/tables" + }, + { + "name": "sin-marks", + "label": "Sin Marks", + "system": "cain", + "type": "Item", + "path": "packs/sin-marks" + }, + { + "name": "afflictions", + "label": "Afflictions", + "system": "cain", + "type": "Item", + "path": "packs/afflictions" } ], "packFolders": [], diff --git a/template.json b/template.json index 8468e3a..8068e43 100644 --- a/template.json +++ b/template.json @@ -3,6 +3,6 @@ "types": ["character", "npc", "mundane"] }, "Item": { - "types": ["item", "agenda", "blasphemy"] + "types": ["item", "agenda", "blasphemy", "blasphemyPower", "agendaTask", "agendaAbility", "sinMark", "sinMarkAbility", "affliction"] } } diff --git a/templates/actor/actor-character-sheet.hbs b/templates/actor/actor-character-sheet.hbs index 3fca083..abdc4ae 100644 --- a/templates/actor/actor-character-sheet.hbs +++ b/templates/actor/actor-character-sheet.hbs @@ -1,14 +1,14 @@ - + {{!-- Sheet Header --}} -
+

-
+
@@ -52,7 +52,9 @@
- +
+ +
@@ -69,10 +71,10 @@ {{!-- Sheet Body --}} -
+
{{!-- Owned Features Tab --}} -
+
- \ No newline at end of file + + + \ No newline at end of file diff --git a/templates/actor/parts/actor-abilities.hbs b/templates/actor/parts/actor-abilities.hbs index 33a6690..f231087 100644 --- a/templates/actor/parts/actor-abilities.hbs +++ b/templates/actor/parts/actor-abilities.hbs @@ -1,4 +1,4 @@ -
+
@@ -33,346 +33,642 @@
- -
-

My Agenda

+

+ {{#if currentAgenda }} + {{currentAgenda.name}} + {{else}} +

Drag agenda onto your sheet.

+ {{/if}} +

-

Agenda Items

+

Agenda Tasks

    - {{#each actor.system.currentAgendaItems as |item index|}} + {{#each currentUnboldedAgendaTasks}}
  • - +

    {{this.system.task}}

  • {{/each}} -
- -
- -
-

Agenda Abilities

-
    - {{#each actor.system.currentAgendaAbilities as |ability index|}} + {{#each currentBoldedAgendaTasks}}
  • - +

    {{this.system.task}}

  • {{/each}}
- + +
+ +
+

Agenda Abilities

+
    + {{#each currentAgendaAbilities}} +
  • + {{this.system.abilityName}} + + + + + + +
  • + {{/each}} +
+
+
+ + +
+ +
+
+

Ability Description

+

+
-
- -
-

Agendas

- {{#if agendas.length}} -
    - {{#each agendas as |item|}} -
  1. -
    -
    - -
    -

    {{item.name}}

    -
    -
    - - - - - +
    + +
    +

    Blasphemies

    + {{#unless blasphemyData}} +
    +

    Drag blasphemy onto your sheet.

    +
    + {{/unless}} + {{#each blasphemyData}} +
    + +
    +

    Passive{{#if (gt passives.length 1)}}s{{/if}}

    +
      + {{#each passives}} +
    • +

      + {{this.system.powerName}} + + - - + + +

      +
      +

      Keywords

      +

      {{this.system.keywords}}

      +

      Power Description

      +

      {{formatted this.system.powerDescription ../../system.CATLEVEL.value}}

    • - {{/each}} -
- {{else}} -

Drag agenda onto your sheet.

- {{/if}} -
- - -
-

Blasphemy Powers

- {{#if blasphemies.length}} -
    - {{#each blasphemies as |item|}} -
  1. -
    -
    - -
    -

    {{item.name}}

    -
    -
    - - - - - + {{/each}} + +

    Powers

    +
- {{else}} -

Drag blasphemies onto your sheet

- {{/if}} + {{/each}} + +
+ + + +
+
+

Keywords

+

+

Power Description

+

+
+ {{/each}} + {{#if currentUnlinkedBlasphemyPowers.length}} +
+

Other Powers

+
    + {{#each currentUnlinkedBlasphemyPowers}} +
  • + {{this.system.powerName}} + + + + + + +
  • + {{/each}} +
+
+ {{/if}} +
+
+
-
\ No newline at end of file diff --git a/templates/actor/parts/actor-features.hbs b/templates/actor/parts/actor-features.hbs index 10071db..7b09453 100644 --- a/templates/actor/parts/actor-features.hbs +++ b/templates/actor/parts/actor-features.hbs @@ -94,12 +94,13 @@

Cat Level

-
+
{{#times 7}} -
- CAT {{@index}} - - +
+
+ CAT {{@index}} +
+
{{lookup sheetConstants.CATSessionNumbers (CainOffset @index -1)}}
{{/times}}
@@ -108,10 +109,32 @@
- {{system.xp.value}} / {{system.xp.max}} - +
+
+ + +
+
+ + +
+ + + +
+
+ {{system.xp.value}} / {{system.xp.max}} +
{{#times system.xp.max}} @@ -122,8 +145,10 @@
{{system.advancements.value}} -
- +
+ {{#times system.advancements.max}} + + {{/times}}
diff --git a/templates/actor/parts/actor-items.hbs b/templates/actor/parts/actor-items.hbs index 385dee9..f76eae1 100644 --- a/templates/actor/parts/actor-items.hbs +++ b/templates/actor/parts/actor-items.hbs @@ -1,8 +1,8 @@

Kit Points & Scrip Amount

-
+
{{#range 0 system.kitPoints.max}} - + {{/range}} {{system.kitPoints.value}} / {{system.kitPoints.max}}
@@ -38,12 +38,12 @@
-

{{item.name}}

+

{{item.name}}

{{item.system.type}}
{{item.system.scripValue}}
@@ -88,21 +88,24 @@ } .item-name-header { - color: #ff0000; + color: white; + font-weight: bold; + justify-content: center; + text-align: center; flex: 2; } .item-type { - color: #00ff00; + color: white; flex: 1; } .item-name h4 { - color: #ff0000; + color: white; } .item-scrip, .item-kit-points { - color: #00ff00; + color: white; } .item-controls a { @@ -111,11 +114,7 @@ } .item-controls a:hover { - color: #ff0000; - } - - .kit-points-checkbox:checked { - background-color: #ff0000; + color: white; } .kit-points-text { diff --git a/templates/actor/parts/actor-sin.hbs b/templates/actor/parts/actor-sin.hbs index 2cb114c..410cf32 100644 --- a/templates/actor/parts/actor-sin.hbs +++ b/templates/actor/parts/actor-sin.hbs @@ -1,36 +1,68 @@ -
+

Sin Overflow

- -
{{#range 0 system.sinOverflow.max}} - + {{#if (lt index ../system.sinOverflow.value)}} + + {{else}} + + + {{/if}} {{/range}}
-
-
-
+

Sin Marks

- {{#if system.currentSinMarks.length}} + {{#if currentSinMarkData.length}}
    - {{#each system.currentSinMarks}} -
  • {{this}}
  • + {{#each currentSinMarkData}} +
  • +

    {{this.sinMark.name}}

    +
      + {{#each this.abilities}} +
    • +
      +

      {{this.name}}

      {{this.description}}

      +
      +
      + +
      +
    • + {{/each}} +
    + +
  • {{/each}}
{{else}} @@ -41,6 +73,94 @@
-
-
\ No newline at end of file +
+ + \ No newline at end of file diff --git a/templates/homebrew-window.hbs b/templates/homebrew-window.hbs new file mode 100644 index 0000000..0f92c7b --- /dev/null +++ b/templates/homebrew-window.hbs @@ -0,0 +1,222 @@ +
+
+
+ + {{! Sheet Tab Navigation }} + + + {{! Sheet Body }} +
+
+ {{!-- {{#each agendaOptions.tasks}} +
  • {{#if this.isBold}}{{/if}}{{this.task}}{{#if this.isBold}}{{/if}}
  • + {{/each}} --}} + +
      + {{#each agendaOptions.tasks}} +
    • + + +
    • + {{/each}} +
    + +
      + {{#each agendaOptions.abilities}} +
    • + + +
      + + + +
      +
    • + {{/each}} +
    + +
    +
    +
    + {{!-- {{#each agendaOptions.tasks}} +
  • {{#if this.isBold}}{{/if}}{{this.task}}{{#if this.isBold}}{{/if}}
  • + {{/each}} --}} + +
      + {{#each blasphemyOptions.powers}} +
    • +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + + +
      +
    • + {{/each}} +
    + +
    +
    + +
    +
    + + \ No newline at end of file diff --git a/templates/item/item-affliction-sheet.hbs b/templates/item/item-affliction-sheet.hbs new file mode 100644 index 0000000..8c8e9a3 --- /dev/null +++ b/templates/item/item-affliction-sheet.hbs @@ -0,0 +1,13 @@ +
    +
    +

    {{item.system.afflictionName}}

    +
    + + {{! Sheet Tab Navigation }} + {{! Sheet Body }} +
    +

    + {{this.item.system.afflictionDescription}} +

    +
    +
    \ No newline at end of file diff --git a/templates/item/item-agenda-sheet.hbs b/templates/item/item-agenda-sheet.hbs index cde6290..3d6c1c3 100644 --- a/templates/item/item-agenda-sheet.hbs +++ b/templates/item/item-agenda-sheet.hbs @@ -1,5 +1,3 @@ -{{! This template is a fallback for when items don't have more specific templates. }} -{{! Generally, you'll want to make more specific templates when possible. }}
    - Description + Tasks + Abilities {{! Sheet Body }}
    +
    +
      + {{#each agendaUnboldedTaskData}} +
    • + {{this.system.task}} +
    • + {{/each}} + {{#each agendaBoldedTaskData}} +
    • + {{this.system.task}} +
    • + {{/each}} +
    + {{#if developerMode}} + +
    + + +
    +
    + +
    + - {{! Description Tab }} -
    - {{! Editors must receive enriched text data from getData to properly handle rolls }} - {{editor - enrichedDescription - target='system.description' - engine='prosemirror' - button=true - editable=editable - }} + {{/if}} +
    +
    + {{! Abilities Tab }} +
    +
      + {{#each agendaAbilityData}} +
    • {{this.system.abilityName}}: {{this.system.abilityDescription}}
    • + {{/each}} +
    \ No newline at end of file diff --git a/templates/item/item-agendaAbility-sheet.hbs b/templates/item/item-agendaAbility-sheet.hbs new file mode 100644 index 0000000..f167557 --- /dev/null +++ b/templates/item/item-agendaAbility-sheet.hbs @@ -0,0 +1,13 @@ +
    +
    +

    {{item.system.abilityName}}

    +
    + + {{! Sheet Tab Navigation }} + {{! Sheet Body }} +
    +

    + {{this.item.system.abilityDescription}} +

    +
    +
    \ No newline at end of file diff --git a/templates/item/item-agendaTask-sheet.hbs b/templates/item/item-agendaTask-sheet.hbs new file mode 100644 index 0000000..a3a2539 --- /dev/null +++ b/templates/item/item-agendaTask-sheet.hbs @@ -0,0 +1,15 @@ +
    +
    + +
    + + {{! Sheet Tab Navigation }} + {{! Sheet Body }} +
    +

    + {{#if this.item.system.isBold}}{{/if}} + {{this.item.system.task}} + {{#if this.item.system.isBold}}{{/if}} +

    +
    +
    \ No newline at end of file diff --git a/templates/item/item-blasphemy-sheet.hbs b/templates/item/item-blasphemy-sheet.hbs index a31f6af..921b1bb 100644 --- a/templates/item/item-blasphemy-sheet.hbs +++ b/templates/item/item-blasphemy-sheet.hbs @@ -1,395 +1,243 @@ -
    +
    - +
    -

    -
    - - -
    -
    - - -
    -
    - - -
    + +
    {{!-- Sheet Tab Navigation --}} {{!-- Sheet Body --}} - {{! Sheet Body }}
    - - {{! Description Tab }} -
    - {{! Editors must receive enriched text data from getData to properly handle rolls }} - {{editor - enrichedDescription - target='system.description' - engine='prosemirror' - button=true - editable=editable - }} -
    - - {{! Attributes Tab }} -
    - {{! As you add new fields, add them in here! }} -
    - - {{system.formula}} -
    -
    - - -
    -
    - - -
    -
    - - -
    +
    + {{#each blasphemyPassives}} + {{> blasphemyPowerPartial this powerItem=this developerMode=../developerMode}} + {{/each}} +
    + +
    + + + +
    -
    +
    + {{#each blasphemyPowers}} + {{> blasphemyPowerPartial this powerItem=this developerMode=../developerMode}} + {{/each}} +
    -
    \ No newline at end of file diff --git a/templates/item/item-blasphemyPower-sheet.hbs b/templates/item/item-blasphemyPower-sheet.hbs new file mode 100644 index 0000000..f0e7d97 --- /dev/null +++ b/templates/item/item-blasphemyPower-sheet.hbs @@ -0,0 +1,2 @@ + +{{> blasphemyPower item powerItem=item developerMode=developerMode}} diff --git a/templates/item/item-sinMark-sheet.hbs b/templates/item/item-sinMark-sheet.hbs new file mode 100644 index 0000000..60e4a95 --- /dev/null +++ b/templates/item/item-sinMark-sheet.hbs @@ -0,0 +1,161 @@ +
    + + +
    + +
    +

    +
    +
    + + {{! Sheet Body }} +
    +

    Sin Abilities

    + + {{#each sinMarkAbilities}} +
    + {{> sinMarkAbility this powerItem=this developerMode=../developerMode}} +
    + {{/each}} + +
    + + + +
    + +
    +
    \ No newline at end of file diff --git a/templates/item/item-sinMarkAbility-sheet.hbs b/templates/item/item-sinMarkAbility-sheet.hbs new file mode 100644 index 0000000..0b4747a --- /dev/null +++ b/templates/item/item-sinMarkAbility-sheet.hbs @@ -0,0 +1,141 @@ +
    + + +
    + +
    +

    +
    +
    + + {{! Sheet Body }} +
    +

    Sin Information

    + {{! Editors must receive enriched text data from getData to properly handle rolls }} +
    + + +
    + +
    + + +
    + +
    + + +
    +
    +
    \ No newline at end of file diff --git a/templates/item/parts/item-blasphemy-power-partial.hbs b/templates/item/parts/item-blasphemy-power-partial.hbs new file mode 100644 index 0000000..9b673ba --- /dev/null +++ b/templates/item/parts/item-blasphemy-power-partial.hbs @@ -0,0 +1,24 @@ +
    +
    +
    +
    + + {{system.powerName}} +
    +
    + + {{#if system.isPassive}}Yes{{else}}No{{/if}} +
    +
    + + {{system.keywords}} +
    +
    + +
    + +
    +

    Description

    +

    {{#formatted system.powerDescription}}{{/formatted}}

    +
    +
    \ No newline at end of file diff --git a/templates/item/parts/item-blasphemy-power-sheet.hbs b/templates/item/parts/item-blasphemy-power-sheet.hbs new file mode 100644 index 0000000..c9292bc --- /dev/null +++ b/templates/item/parts/item-blasphemy-power-sheet.hbs @@ -0,0 +1,215 @@ +
    +
    + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    + +
    +

    Description

    +

    {{formatted system.powerDescription "description"}}

    + +{{#unless disableColorPickers}} +
    + +
    + + + + +
    +
    + {{/unless}} + +
    +
    + + \ No newline at end of file diff --git a/templates/item/parts/item-sin-mark-partial.hbs b/templates/item/parts/item-sin-mark-partial.hbs new file mode 100644 index 0000000..26a30e9 --- /dev/null +++ b/templates/item/parts/item-sin-mark-partial.hbs @@ -0,0 +1,27 @@ +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + + \ No newline at end of file diff --git a/templates/player-overview/skills.hbs b/templates/player-overview/skills.hbs index 06cedd1..70a1721 100644 --- a/templates/player-overview/skills.hbs +++ b/templates/player-overview/skills.hbs @@ -15,14 +15,28 @@

    Current Agenda Items

      - {{#each actor.system.currentAgendaItems}} -
    • {{this.text}}
    • + {{#each agendaTasks}} +
    • {{this.system.task}}
    • {{/each}}

    Current Agenda Abilities

      - {{#each actor.system.currentAgendaAbilities}} -
    • {{this.text}}
    • + {{#each agendaAbilities}} +
    • {{this.system.abilityName}}
    • + {{/each}} +
    +
    +
    +

    Current Blasphemies

    +
      + {{#each blasphemies}} +
    • {{this.system.blasphemyName}}
    • + {{/each}} +
    +

    Current Blasphemy Powers

    +
      + {{#each blasphemyPowers}} +
    • {{this.system.powerName}}
    • {{/each}}
    diff --git a/templates/session-end-advancement.hbs b/templates/session-end-advancement.hbs new file mode 100644 index 0000000..568092e --- /dev/null +++ b/templates/session-end-advancement.hbs @@ -0,0 +1,94 @@ + +
    +

    Survival

    +
    + + +
    +

    Up to 1 from first agenda

    + {{#each unboldedAgendaTasks}} +
    + + +
    + {{else}} + You don't seem to have a primary agenda - consider adding one... + {{/each}} +

    Up to 2 from bold agendas

    + + {{#each boldedAgendaTasks}} +
    + + +
    + {{else}} + You don't seem to have any bolded agendas - consider adding one... + {{/each}} +

    Injuries/Afflictions

    +
    + + +
    + +