System development, WIP

This commit is contained in:
2026-05-05 13:55:42 +02:00
commit c0223977d2
250 changed files with 10362 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
.github/
rules/
node_modules/
+94
View File
@@ -0,0 +1,94 @@
{
"id": "fvtt-machine-gods-noxian-expanse",
"title": "Machine Gods of the Noxian Expanse",
"description": "Foundry VTT system for Machine Gods of the Noxian Expanse.",
"version": "14..0",
"authors": [
{
"name": "Uberwald"
}
],
"compatibility": {
"minimum": "13",
"verified": "14"
},
"esmodules": [
"fvtt-machine-gods-noxian-expanse.mjs"
],
"styles": [
"css/mgne.css"
],
"languages": [
{
"lang": "en",
"name": "English",
"path": "lang/en.json"
}
],
"documentTypes": {
"Actor": {
"character": {
"htmlFields": [
"description",
"notes"
]
},
"creature": {
"htmlFields": [
"description",
"special",
"notes"
]
},
"companion": {
"htmlFields": [
"description",
"notes"
]
}
},
"Item": {
"weapon": {
"htmlFields": [
"description"
]
},
"armor": {
"htmlFields": [
"description"
]
},
"shield": {
"htmlFields": [
"description"
]
},
"equipment": {
"htmlFields": [
"description"
]
},
"resonance-core": {
"htmlFields": [
"description"
]
},
"artifact": {
"htmlFields": [
"description"
]
},
"feature": {
"htmlFields": [
"description"
]
}
}
},
"grid": {
"distance": 10,
"units": "ft"
},
"primaryTokenAttribute": "hp",
"socket": false
}
+94
View File
@@ -0,0 +1,94 @@
{
"id": "fvtt-machine-gods-noxian-expanse",
"title": "Machine Gods of the Noxian Expanse",
"description": "Foundry VTT system for Machine Gods of the Noxian Expanse.",
"version": "14..0",
"authors": [
{
"name": "Uberwald"
}
],
"compatibility": {
"minimum": "13",
"verified": "14"
},
"esmodules": [
"fvtt-machine-gods-noxian-expanse.mjs"
],
"styles": [
"css/mgne.css"
],
"languages": [
{
"lang": "en",
"name": "English",
"path": "lang/en.json"
}
],
"documentTypes": {
"Actor": {
"character": {
"htmlFields": [
"description",
"notes"
]
},
"creature": {
"htmlFields": [
"description",
"special",
"notes"
]
},
"companion": {
"htmlFields": [
"description",
"notes"
]
}
},
"Item": {
"weapon": {
"htmlFields": [
"description"
]
},
"armor": {
"htmlFields": [
"description"
]
},
"shield": {
"htmlFields": [
"description"
]
},
"equipment": {
"htmlFields": [
"description"
]
},
"resonance-core": {
"htmlFields": [
"description"
]
},
"artifact": {
"htmlFields": [
"description"
]
},
"feature": {
"htmlFields": [
"description"
]
}
}
},
"grid": {
"distance": 10,
"units": "ft"
},
"primaryTokenAttribute": "hp",
"socket": false
}
+94
View File
@@ -0,0 +1,94 @@
{
"id": "fvtt-machine-gods-noxian-expanse",
"title": "Machine Gods of the Noxian Expanse",
"description": "Foundry VTT system for Machine Gods of the Noxian Expanse.",
"version": "14.0.0",
"authors": [
{
"name": "Uberwald"
}
],
"compatibility": {
"minimum": "13",
"verified": "14"
},
"esmodules": [
"fvtt-machine-gods-noxian-expanse.mjs"
],
"styles": [
"css/mgne.css"
],
"languages": [
{
"lang": "en",
"name": "English",
"path": "lang/en.json"
}
],
"documentTypes": {
"Actor": {
"character": {
"htmlFields": [
"description",
"notes"
]
},
"creature": {
"htmlFields": [
"description",
"special",
"notes"
]
},
"companion": {
"htmlFields": [
"description",
"notes"
]
}
},
"Item": {
"weapon": {
"htmlFields": [
"description"
]
},
"armor": {
"htmlFields": [
"description"
]
},
"shield": {
"htmlFields": [
"description"
]
},
"equipment": {
"htmlFields": [
"description"
]
},
"resonance-core": {
"htmlFields": [
"description"
]
},
"artifact": {
"htmlFields": [
"description"
]
},
"feature": {
"htmlFields": [
"description"
]
}
}
},
"grid": {
"distance": 10,
"units": "ft"
},
"primaryTokenAttribute": "hp",
"socket": false
}
+94
View File
@@ -0,0 +1,94 @@
{
"id": "fvtt-machine-gods-noxian-expanse",
"title": "Machine Gods of the Noxian Expanse",
"description": "Foundry VTT system for Machine Gods of the Noxian Expanse.",
"version": "14.0.0",
"authors": [
{
"name": "Uberwald"
}
],
"compatibility": {
"minimum": "14",
"verified": "14"
},
"esmodules": [
"fvtt-machine-gods-noxian-expanse.mjs"
],
"styles": [
"css/mgne.css"
],
"languages": [
{
"lang": "en",
"name": "English",
"path": "lang/en.json"
}
],
"documentTypes": {
"Actor": {
"character": {
"htmlFields": [
"description",
"notes"
]
},
"creature": {
"htmlFields": [
"description",
"special",
"notes"
]
},
"companion": {
"htmlFields": [
"description",
"notes"
]
}
},
"Item": {
"weapon": {
"htmlFields": [
"description"
]
},
"armor": {
"htmlFields": [
"description"
]
},
"shield": {
"htmlFields": [
"description"
]
},
"equipment": {
"htmlFields": [
"description"
]
},
"resonance-core": {
"htmlFields": [
"description"
]
},
"artifact": {
"htmlFields": [
"description"
]
},
"feature": {
"htmlFields": [
"description"
]
}
}
},
"grid": {
"distance": 10,
"units": "ft"
},
"primaryTokenAttribute": "hp",
"socket": false
}
+94
View File
@@ -0,0 +1,94 @@
{
"id": "fvtt-machine-gods-noxian-expanse",
"title": "Machine Gods of the Noxian Expanse",
"description": "Foundry VTT system for Machine Gods of the Noxian Expanse.",
"version": "14.0.0",
"authors": [
{
"name": "Uberwald"
}
],
"compatibility": {
"minimum": "14",
"verified": "14"
},
"esmodules": [
"fvtt-machine-gods-noxian-expanse.mjs"
],
"styles": [
"css/mgne.css"
],
"languages": [
{
"lang": "en",
"name": "English",
"path": "lang/en.json"
}
],
"documentTypes": {
"Actor": {
"character": {
"htmlFields": [
"description",
"notes"
]
},
"creature": {
"htmlFields": [
"description",
"special",
"notes"
]
},
"companion": {
"htmlFields": [
"description",
"notes"
]
}
},
"Item": {
"weapon": {
"htmlFields": [
"description"
]
},
"armor": {
"htmlFields": [
"description"
]
},
"shield": {
"htmlFields": [
"description"
]
},
"equipment": {
"htmlFields": [
"description"
]
},
"resonance-core": {
"htmlFields": [
"description"
]
},
"artifact": {
"htmlFields": [
"description"
]
},
"feature": {
"htmlFields": [
"description"
]
}
}
},
"grid": {
"distance": 10,
"units": "ft"
},
"primaryTokenAttribute": "hp",
"socket": false
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+93
View File
@@ -0,0 +1,93 @@
Copyright 2011 The Alegreya Project Authors (https://github.com/huertatipografica/Alegreya)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+93
View File
@@ -0,0 +1,93 @@
Copyright (c) 2012 Natanael Gama (info@ndiscovered.com), with Reserved Font Name 'Cinzel'
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Accelerate Core</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#b89458" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><circle cx="128" cy="128" r="28" fill="#2d2018" stroke="#b89458" stroke-width="5"/><circle cx="128" cy="128" r="12" fill="#dcc8a4"/><path d="M128 70V52" stroke="#b89458" stroke-width="4"/><path d="M128 56V76M128 180v20M56 128h20M180 128h20M74 74l14 14M168 168l14 14M182 74l-14 14M74 182l14-14" stroke="{accent}" stroke-width="4"/><path d="M128 88v-20M128 188v-20M88 128H68M188 128h-20M100 100 86 86M156 156l14 14M156 100l14-14M100 156l-14 14" stroke="{accent}" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Akimbo Hit Priest</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#d7b66a" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M84 94h28l10 16-4 24H88l-12-16Z" fill="#788188" stroke="#d7b66a" stroke-width="3"/><path d="M144 94h28l12 24-10 16h-30l-4-24Z" fill="#788188" stroke="#d7b66a" stroke-width="3"/><path d="M128 64v22" stroke="#dcc8a4" stroke-width="5"/><ellipse cx="128" cy="54" rx="22" ry="10" stroke="#b89458" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Ambivalent Slouch</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#d7b66a" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M104 78c10-8 20-12 32-12 18 0 30 10 36 28-18 6-34 20-42 42-8 22-24 36-50 40 2-50 8-78 24-98Z" fill="#72422a" stroke="#d7b66a" stroke-width="4"/><path d="M126 68c-4 18 4 34 22 44" stroke="#dcc8a4" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>armor</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#788188" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M94 68 116 56h24l22 12 12 36-14 74h-64l-14-74Z" fill="#788188" stroke="#788188" stroke-width="4"/><path d="M108 84h40M104 110h48M100 138h56" stroke="#dcc8a4" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>artifact</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#82b2b3" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><ellipse cx="128" cy="92" rx="46" ry="20" fill="none" stroke="#82b2b3" stroke-width="6"/><path d="M128 108v72" stroke="#dcc8a4" stroke-width="8"/><path d="M96 156h64M108 132l20-18 20 18" stroke="#b89458" stroke-width="5"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Avaricious Gubbingrifter</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#d7b66a" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M96 154c4-34 12-62 28-82l14 8-8 34 18-28 12 8-16 34 20-20 10 12c-16 20-24 42-58 56Z" fill="#dcc8a4" stroke="#d7b66a" stroke-width="4"/><circle cx="92" cy="92" r="10" fill="#95a45d"/><circle cx="166" cy="76" r="10" fill="#95a45d"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Backbiter</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#d7b66a" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M90 170 166 90" stroke="#d7b66a" stroke-width="8"/><path d="M122 74 178 130" stroke="#788188" stroke-width="10"/><path d="M98 178 116 196M160 82l18-18" stroke="#dcc8a4" stroke-width="5"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Backpack</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#95a45d" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><rect x="84" y="72" width="88" height="112" rx="18" fill="#72422a" stroke="#95a45d" stroke-width="4"/><rect x="102" y="94" width="52" height="30" rx="8" fill="#2d2018" stroke="#dcc8a4" stroke-width="3"/><path d="M102 72c0-18 52-18 52 0M96 90c-18 10-22 60-10 88M160 90c18 10 22 60 10 88" stroke="#dcc8a4" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Blast Core</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#b89458" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><circle cx="128" cy="128" r="28" fill="#2d2018" stroke="#b89458" stroke-width="5"/><circle cx="128" cy="128" r="12" fill="#dcc8a4"/><path d="M128 70V52" stroke="#b89458" stroke-width="4"/><path d="M128 56V76M128 180v20M56 128h20M180 128h20M74 74l14 14M168 168l14 14M182 74l-14 14M74 182l14-14" stroke="{accent}" stroke-width="4"/><path d="M128 88v-20M128 188v-20M88 128H68M188 128h-20M100 100 86 86M156 156l14 14M156 100l14-14M100 156l-14 14" stroke="{accent}" stroke-width="4"/><circle cx="128" cy="128" r="44" stroke="#c36b3e" stroke-width="4" stroke-dasharray="4 8"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Cauterize Core</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#b89458" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><circle cx="128" cy="128" r="28" fill="#2d2018" stroke="#b89458" stroke-width="5"/><circle cx="128" cy="128" r="12" fill="#dcc8a4"/><path d="M128 78c18 16 22 28 22 42 0 16-8 28-22 38-14-10-22-22-22-38 0-14 6-26 22-42Z" fill="#c36b3e" stroke="#dcc8a4" stroke-width="3"/><path d="M128 88v-20M128 188v-20M88 128H68M188 128h-20M100 100 86 86M156 156l14 14M156 100l14-14M100 156l-14 14" stroke="{accent}" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Chain Shirt</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#788188" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M90 66 114 56h28l24 10 20 34-22 20v54H92v-54l-22-20Z" fill="#72422a" stroke="#788188" stroke-width="4"/><circle cx="100" cy="92" r="7" stroke="#dcc8a4" stroke-width="2" opacity="0.75"/><circle cx="128" cy="92" r="7" stroke="#dcc8a4" stroke-width="2" opacity="0.75"/><circle cx="156" cy="92" r="7" stroke="#dcc8a4" stroke-width="2" opacity="0.75"/><circle cx="100" cy="114" r="7" stroke="#dcc8a4" stroke-width="2" opacity="0.75"/><circle cx="128" cy="114" r="7" stroke="#dcc8a4" stroke-width="2" opacity="0.75"/><circle cx="156" cy="114" r="7" stroke="#dcc8a4" stroke-width="2" opacity="0.75"/><circle cx="100" cy="136" r="7" stroke="#dcc8a4" stroke-width="2" opacity="0.75"/><circle cx="128" cy="136" r="7" stroke="#dcc8a4" stroke-width="2" opacity="0.75"/><circle cx="156" cy="136" r="7" stroke="#dcc8a4" stroke-width="2" opacity="0.75"/><circle cx="100" cy="158" r="7" stroke="#dcc8a4" stroke-width="2" opacity="0.75"/><circle cx="128" cy="158" r="7" stroke="#dcc8a4" stroke-width="2" opacity="0.75"/><circle cx="156" cy="158" r="7" stroke="#dcc8a4" stroke-width="2" opacity="0.75"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Chart Reading Maniac</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#d7b66a" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M78 82 116 70l24 20 38-12v96l-38 12-24-20-38 12Z" fill="#dcc8a4" stroke="#d7b66a" stroke-width="4"/><circle cx="138" cy="116" r="18" stroke="#c36b3e" stroke-width="4"/><path d="M138 98l8 18-8 18-8-18Z" fill="#0d0907" stroke="#c36b3e" stroke-width="3"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Clothing (Average)</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#788188" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M90 66 114 56h28l24 10 20 34-22 20v54H92v-54l-22-20Z" fill="#72422a" stroke="#788188" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Club</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#c36b3e" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M123 78 137 78 142 188 118 188Z" fill="#72422a" stroke="#c36b3e" stroke-width="3"/><path d="M106 58c12-20 32-20 44 0l-6 24h-32Z" fill="#788188" stroke="#c36b3e" stroke-width="3"/><circle cx="114" cy="70" r="3" fill="#dcc8a4"/><circle cx="128" cy="64" r="3" fill="#dcc8a4"/><circle cx="142" cy="70" r="3" fill="#dcc8a4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Dagger</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#c36b3e" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M128 50 136 86 132 170 124 170 120 86Z" fill="#788188" stroke="#c36b3e" stroke-width="2.5"/><path d="M102 170H154" stroke="#b89458" stroke-width="10"/><path d="M128 170V200" stroke="#dcc8a4" stroke-width="8"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Empower Weapon Core</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#b89458" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><circle cx="128" cy="128" r="28" fill="#2d2018" stroke="#b89458" stroke-width="5"/><circle cx="128" cy="128" r="12" fill="#dcc8a4"/><path d="M128 50 136 86 132 170 124 170 120 86Z" fill="#788188" stroke="#b89458" stroke-width="2.5"/><path d="M102 170H154" stroke="#b89458" stroke-width="10"/><path d="M128 170V200" stroke="#dcc8a4" stroke-width="8"/><path d="M128 88v-20M128 188v-20M88 128H68M188 128h-20M100 100 86 86M156 156l14 14M156 100l14-14M100 156l-14 14" stroke="{accent}" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>equipment</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#95a45d" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><rect x="84" y="72" width="88" height="112" rx="18" fill="#72422a" stroke="#95a45d" stroke-width="4"/><rect x="102" y="94" width="52" height="30" rx="8" fill="#2d2018" stroke="#dcc8a4" stroke-width="3"/><path d="M102 72c0-18 52-18 52 0M96 90c-18 10-22 60-10 88M160 90c18 10 22 60 10 88" stroke="#dcc8a4" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>feature</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#d7b66a" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M94 74h68c0 28-12 48-34 58-22-10-34-30-34-58Z" fill="#dcc8a4" stroke="#d7b66a" stroke-width="4"/><path d="M128 132v24M104 176h48" stroke="#b89458" stroke-width="8"/><path d="M116 92c4 10 10 16 12 26 8-8 14-16 18-28" stroke="#95a45d" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Full Plate</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#788188" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M94 68 116 56h24l22 12 12 36-14 74h-64l-14-74Z" fill="#788188" stroke="#788188" stroke-width="4"/><path d="M108 84h40M104 110h48M100 138h56" stroke="#dcc8a4" stroke-width="4"/><path d="M86 78h20M150 78h20" stroke="#b89458" stroke-width="8"/><path d="M110 176v18M146 176v18" stroke="#b89458" stroke-width="6"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Gambeson</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#788188" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M90 66 114 56h28l24 10 20 34-22 20v54H92v-54l-22-20Z" fill="#72422a" stroke="#788188" stroke-width="4"/><path d="M98 96 158 160M158 96 98 160M128 88V170" stroke="#dcc8a4" stroke-width="3" opacity="0.7"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Halberd</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#c36b3e" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M122 40h12l7 166h-26Z" fill="#72422a" stroke="#c36b3e" stroke-width="3"/><path d="M128 48 154 72 128 98 102 72Z" fill="#788188" stroke="#c36b3e" stroke-width="3"/><path d="M132 82c22-2 36 4 44 18-14 8-28 10-44 6Z" fill="#788188" stroke="#c36b3e" stroke-width="3"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Half Plate</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#788188" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M94 68 116 56h24l22 12 12 36-14 74h-64l-14-74Z" fill="#788188" stroke="#788188" stroke-width="4"/><path d="M108 84h40M104 110h48M100 138h56" stroke="#dcc8a4" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Handaxe</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#c36b3e" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M122 56h12l5 142h-22Z" fill="#72422a" stroke="#c36b3e" stroke-width="3"/><path d="M133 70c26 0 44 10 46 34-18 8-34 9-50 2Z" fill="#788188" stroke="#c36b3e" stroke-width="3"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Heavy Crossbow</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#c36b3e" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M66 110c20-26 104-26 124 0" stroke="#c36b3e" stroke-width="8"/><path d="M74 156h108" stroke="#dcc8a4" stroke-width="6"/><path d="M128 76V198" stroke="#788188" stroke-width="10"/><path d="M92 156 164 106" stroke="#72422a" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Helm</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#788188" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M82 116c0-36 20-58 46-58 30 0 46 22 46 58v54H82Z" fill="#788188" stroke="#788188" stroke-width="4"/><path d="M94 116c8-16 20-24 34-24 16 0 28 8 34 24" stroke="#dcc8a4" stroke-width="4"/><path d="M104 132h48" stroke="#dcc8a4" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Liturgicantal Blesswell</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#d7b66a" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M94 74h68c0 28-12 48-34 58-22-10-34-30-34-58Z" fill="#dcc8a4" stroke="#d7b66a" stroke-width="4"/><path d="M128 132v24M104 176h48" stroke="#b89458" stroke-width="8"/><path d="M116 92c4 10 10 16 12 26 8-8 14-16 18-28" stroke="#95a45d" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Longsword</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#c36b3e" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M128 52 142 88 133 165 123 165 114 88Z" fill="#788188" stroke="#c36b3e" stroke-width="3"/><path d="M102 170H154" stroke="#b89458" stroke-width="10"/><path d="M128 170V200" stroke="#dcc8a4" stroke-width="8"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Lux Relay Idol</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#82b2b3" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M112 70h32l18 30-10 74h-48l-10-74Z" fill="#dcc8a4" stroke="#82b2b3" stroke-width="4"/><circle cx="118" cy="108" r="5" fill="#0d0907"/><circle cx="138" cy="108" r="5" fill="#0d0907"/><path d="M128 76v-18M108 142h40" stroke="#95a45d" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Maul</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#c36b3e" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><rect x="92" y="54" width="72" height="34" rx="8" fill="#788188" stroke="#c36b3e" stroke-width="3"/><path d="M124 88h8l10 118h-28Z" fill="#72422a" stroke="#c36b3e" stroke-width="3"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Medical Supplies</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#95a45d" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M88 96h80v64H88Z" fill="#dcc8a4" stroke="#95a45d" stroke-width="4"/><path d="M128 82v92M96 128h64" stroke="#c36b3e" stroke-width="12"/><path d="M78 110c-8 12-8 26 0 38M178 110c8 12 8 26 0 38" stroke="#72422a" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Medium Shield</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#dcc8a4" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M128 50 182 72v54c0 40-24 66-54 82-30-16-54-42-54-82V72Z" fill="#788188" stroke="#dcc8a4" stroke-width="4"/><path d="M128 68v120M94 114h68" stroke="#dcc8a4" stroke-width="4" opacity="0.8"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Null Forge Spike</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#82b2b3" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M128 48 152 98 128 206 104 98Z" fill="#788188" stroke="#82b2b3" stroke-width="4"/><path d="M112 120h32M118 146h20" stroke="#c36b3e" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Padded Leather</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#788188" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M90 66 114 56h28l24 10 20 34-22 20v54H92v-54l-22-20Z" fill="#72422a" stroke="#788188" stroke-width="4"/><path d="M98 96 158 160M158 96 98 160M128 88V170" stroke="#dcc8a4" stroke-width="3" opacity="0.7"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Processional Halo</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#82b2b3" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><ellipse cx="128" cy="92" rx="46" ry="20" fill="none" stroke="#82b2b3" stroke-width="6"/><path d="M128 108v72" stroke="#dcc8a4" stroke-width="8"/><path d="M96 156h64M108 132l20-18 20 18" stroke="#b89458" stroke-width="5"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Quarterstaff</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#c36b3e" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M124 42h8l10 170h-28Z" fill="#72422a" stroke="#c36b3e" stroke-width="3"/><path d="M114 68h28M112 98h32M116 150h24" stroke="#dcc8a4" stroke-width="4" opacity="0.8"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Rapier</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#c36b3e" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M128 50 136 86 132 170 124 170 120 86Z" fill="#788188" stroke="#c36b3e" stroke-width="2.5"/><path d="M110 170c6-16 30-16 36 0" stroke="#b89458" stroke-width="8"/><path d="M128 170V202" stroke="#dcc8a4" stroke-width="7"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Rations</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#95a45d" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M82 152c0-24 18-44 40-44h18c20 0 36 16 36 36 0 18-14 32-34 32H104c-14 0-22-10-22-24Z" fill="#dcc8a4" stroke="#95a45d" stroke-width="4"/><path d="M90 134c20-6 52-6 74 0" stroke="#72422a" stroke-width="4"/><path d="M110 94h36" stroke="#b89458" stroke-width="6"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>resonance core</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#b89458" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><circle cx="128" cy="128" r="28" fill="#2d2018" stroke="#b89458" stroke-width="5"/><circle cx="128" cy="128" r="12" fill="#dcc8a4"/><path d="M128 88v-20M128 188v-20M88 128H68M188 128h-20M100 100 86 86M156 156l14 14M156 100l14-14M100 156l-14 14" stroke="{accent}" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Rope</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#95a45d" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><circle cx="128" cy="128" r="50" stroke="#95a45d" stroke-width="10"/><circle cx="128" cy="128" r="28" stroke="#dcc8a4" stroke-width="8"/><path d="M84 102c10-18 30-32 44-32 20 0 36 10 50 34" stroke="#72422a" stroke-width="5" opacity="0.8"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Sack</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#95a45d" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M98 74h60l-10 18c18 14 28 36 24 68-4 34-24 48-44 48s-40-14-44-48c-4-32 6-54 24-68Z" fill="#72422a" stroke="#95a45d" stroke-width="4"/><path d="M98 74h60M106 92h44" stroke="#dcc8a4" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>shield</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#dcc8a4" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M128 50 182 72v54c0 40-24 66-54 82-30-16-54-42-54-82V72Z" fill="#788188" stroke="#dcc8a4" stroke-width="4"/><path d="M128 68v120M94 114h68" stroke="#dcc8a4" stroke-width="4" opacity="0.8"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Shiver Lens</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#82b2b3" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><circle cx="120" cy="118" r="34" fill="#2d2018" stroke="#82b2b3" stroke-width="6"/><path d="M143 141l28 28" stroke="#dcc8a4" stroke-width="10"/><path d="M103 100 136 133M128 92 115 146" stroke="#788188" stroke-width="3"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Shortbow</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#c36b3e" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M94 58c30 26 30 114 0 140" stroke="#c36b3e" stroke-width="7"/><path d="M162 58c-30 26-30 114 0 140" stroke="#c36b3e" stroke-width="7"/><path d="M94 58 162 198" stroke="#dcc8a4" stroke-width="2.5"/><path d="M128 72V184" stroke="#788188" stroke-width="5"/><path d="M120 86 128 72 136 86" stroke="#dcc8a4" stroke-width="3"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Spear</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#c36b3e" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M124 42h8l8 170h-24Z" fill="#72422a" stroke="#c36b3e" stroke-width="3"/><path d="M128 40 146 72 128 92 110 72Z" fill="#788188" stroke="#c36b3e" stroke-width="3"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Waterskin</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#95a45d" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M108 68h40l-8 20c18 12 30 34 30 58 0 32-18 54-42 54s-42-22-42-54c0-24 12-46 30-58Z" fill="#72422a" stroke="#95a45d" stroke-width="4"/><path d="M116 66c0-10 24-10 24 0" stroke="#dcc8a4" stroke-width="4"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>weapon</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#c36b3e" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M128 52 142 88 133 165 123 165 114 88Z" fill="#788188" stroke="#c36b3e" stroke-width="3"/><path d="M102 170H154" stroke="#b89458" stroke-width="10"/><path d="M128 170V200" stroke="#dcc8a4" stroke-width="8"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="none">
<title>Whip</title>
<defs>
<linearGradient id="bg" x1="20" y1="20" x2="236" y2="236" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#2d2018"/>
<stop offset="1" stop-color="#16110d"/>
</linearGradient>
<linearGradient id="trim" x1="42" y1="32" x2="214" y2="224" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#dcc8a4"/>
<stop offset="0.45" stop-color="#b89458"/>
<stop offset="1" stop-color="#72422a"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="6" stdDeviation="8" flood-color="#000" flood-opacity="0.45"/>
</filter>
</defs>
<rect x="16" y="16" width="224" height="224" rx="36" fill="url(#bg)"/>
<path d="M128 24 205 52 222 128 205 204 128 232 51 204 34 128 51 52Z" fill="#231913" stroke="url(#trim)" stroke-width="6" filter="url(#shadow)"/>
<path d="M128 38 192 61 206 128 192 195 128 218 64 195 50 128 64 61Z" stroke="#72422a" stroke-width="2.5" opacity="0.65"/>
<path d="M63 80 92 62M193 80 164 62M63 176 92 194M193 176 164 194" stroke="#95a45d" stroke-width="2" opacity="0.45"/>
<circle cx="128" cy="128" r="72" fill="#0d0907" fill-opacity="0.18" stroke="#c36b3e" stroke-opacity="0.35"/>
<g stroke-linecap="round" stroke-linejoin="round"><path d="M98 174c8-42 40-84 76-106" stroke="#72422a" stroke-width="8"/><path d="M96 176c20-18 44-27 68-25 18 2 26 18 36 18" stroke="#c36b3e" stroke-width="4"/><circle cx="84" cy="186" r="12" fill="#dcc8a4" stroke="#c36b3e" stroke-width="3"/></g>
<g fill="#dcc8a4" opacity="0.8">
<circle cx="52" cy="52" r="3"/><circle cx="204" cy="52" r="3"/><circle cx="52" cy="204" r="3"/><circle cx="204" cy="204" r="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 KiB

+1021
View File
File diff suppressed because it is too large Load Diff
+148
View File
@@ -0,0 +1,148 @@
import { ASCII, SYSTEM, SYSTEM_ID, localizeSystemConfig } from "./module/config/system.mjs"
import * as models from "./module/models/_module.mjs"
import * as documents from "./module/documents/_module.mjs"
import * as applications from "./module/applications/_module.mjs"
import MGNERoll from "./module/documents/roll.mjs"
Hooks.once("init", () => {
console.info(ASCII)
console.info(`${SYSTEM_ID} | Initializing system`)
game.mgne = {
SYSTEM,
applications,
documents,
models,
}
CONFIG.Actor.documentClass = documents.MGNEActor
CONFIG.Actor.dataModels = {
character: models.MGNECharacter,
creature: models.MGNECreature,
companion: models.MGNECompanion,
}
CONFIG.Combat.documentClass = documents.MGNECombat
CONFIG.Item.documentClass = documents.MGNEItem
CONFIG.Item.dataModels = {
weapon: models.MGNEWeapon,
armor: models.MGNEArmor,
shield: models.MGNEShield,
equipment: models.MGNEEquipment,
"resonance-core": models.MGNEResonanceCore,
artifact: models.MGNEArtifact,
feature: models.MGNEFeature,
}
foundry.applications.sheets.ActorSheetV2 && foundry.documents.collections.Actors.unregisterSheet(
"core",
foundry.applications.sheets.ActorSheetV2,
{ types: Object.keys(CONFIG.Actor.dataModels) }
)
foundry.appv1?.sheets?.ActorSheet && foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet)
foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, applications.MGNECharacterSheet, { types: ["character"], makeDefault: true, label: SYSTEM.actorTypes.character.label })
foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, applications.MGNECreatureSheet, { types: ["creature"], makeDefault: true, label: SYSTEM.actorTypes.creature.label })
foundry.documents.collections.Actors.registerSheet(SYSTEM_ID, applications.MGNECompanionSheet, { types: ["companion"], makeDefault: true, label: SYSTEM.actorTypes.companion.label })
foundry.applications.sheets.ItemSheetV2 && foundry.documents.collections.Items.unregisterSheet(
"core",
foundry.applications.sheets.ItemSheetV2,
{ types: Object.keys(CONFIG.Item.dataModels) }
)
foundry.appv1?.sheets?.ItemSheet && foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet)
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, applications.MGNEWeaponSheet, { types: ["weapon"], makeDefault: true, label: SYSTEM.itemTypes.weapon.label })
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, applications.MGNEArmorSheet, { types: ["armor"], makeDefault: true, label: SYSTEM.itemTypes.armor.label })
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, applications.MGNEShieldSheet, { types: ["shield"], makeDefault: true, label: SYSTEM.itemTypes.shield.label })
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, applications.MGNEEquipmentSheet, { types: ["equipment"], makeDefault: true, label: SYSTEM.itemTypes.equipment.label })
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, applications.MGNEResonanceCoreSheet, { types: ["resonance-core"], makeDefault: true, label: SYSTEM.itemTypes["resonance-core"].label })
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, applications.MGNEArtifactSheet, { types: ["artifact"], makeDefault: true, label: SYSTEM.itemTypes.artifact.label })
foundry.documents.collections.Items.registerSheet(SYSTEM_ID, applications.MGNEFeatureSheet, { types: ["feature"], makeDefault: true, label: SYSTEM.itemTypes.feature.label })
Handlebars.registerHelper("isEqual", (left, right) => left === right)
})
Hooks.once("setup", () => {
localizeSystemConfig()
})
Hooks.once("ready", () => {
console.info(`${SYSTEM_ID} | Ready`)
})
Hooks.on("renderCombatTracker", (_app, element) => {
const root = element instanceof HTMLElement ? element : element?.[0]
if (!root) return
const footer = root.querySelector(".combat-controls")
if (!footer || footer.querySelector(".mgne-flee-control")) return
const button = document.createElement("button")
button.type = "button"
button.className = "combat-control-lg mgne-flee-control"
button.dataset.action = "mgneFlee"
button.innerHTML = `<i class="fa-solid fa-person-running" inert></i><span>${game.i18n.localize("MGNE.Combat.Flee")}</span>`
button.disabled = !game.combat
button.addEventListener("click", event => {
event.preventDefault()
game.combat?.rollFlee()
})
footer.append(button)
})
Hooks.on("renderChatMessageHTML", (message, element) => {
const root = element instanceof HTMLElement ? element : element?.[0]
if (!root) return
root.querySelectorAll(".mgne-roll-damage-btn").forEach(btn => {
btn.addEventListener("click", async () => {
const actorId = btn.dataset.actorId
const itemId = btn.dataset.itemId
const actor = game.actors.get(actorId)
const item = actor?.items.get(itemId)
if (!actor || !item) {
ui.notifications.warn(game.i18n.localize("MGNE.Notification.ActorOrItemNotFound"))
return
}
await MGNERoll.rollDamage({ actor, item })
})
})
root.querySelectorAll(".mgne-apply-damage-select").forEach(select => {
const isAllowed = game.user.isGM || message.isAuthor
if (!isAllowed) {
select.closest(".chat-apply-actions")?.remove()
return
}
const card = select.closest(".mgne-chat-card")
const damageTotal = parseInt(card?.dataset.damageTotal ?? "0", 10) || 0
const damageCritical = card?.dataset.damageCritical === "true"
const tokens = canvas.scene?.tokens.contents ?? []
for (const token of tokens) {
if (!token.actor) continue
const opt = document.createElement("option")
opt.value = token.id
opt.textContent = token.name
select.appendChild(opt)
}
if (tokens.length === 0) {
const opt = document.createElement("option")
opt.disabled = true
opt.textContent = game.i18n.localize("MGNE.Roll.NoTargetSelected")
select.appendChild(opt)
}
select.addEventListener("change", async event => {
const tokenId = event.target.value
if (!tokenId) return
const token = canvas.scene?.tokens.get(tokenId)
const targetActor = token?.actor
if (!targetActor) return
select.value = ""
await targetActor.applyDamage(damageTotal, { critical: damageCritical, chat: true })
})
})
})
+691
View File
@@ -0,0 +1,691 @@
{
"MGNE": {
"SystemName": "Machine Gods of the Noxian Expanse",
"ActorTypes": {
"character": "Character",
"creature": "Creature",
"companion": "Companion"
},
"ItemTypes": {
"weapon": "Weapon",
"armor": "Armor",
"shield": "Shield",
"equipment": "Equipment",
"resonance-core": "Resonance Core",
"artifact": "Artifact",
"feature": "Feature"
},
"Abilities": {
"agility": "Agility",
"presence": "Presence",
"strength": "Strength",
"toughness": "Toughness"
},
"Conditions": {
"bleeding": "Bleeding",
"blinded": "Blinded",
"burning": "Burning",
"fatigued": "Fatigued",
"infected": "Infected",
"poisoned": "Poisoned",
"prone": "Prone",
"restrained": "Restrained",
"starved": "Starved",
"stunned": "Stunned"
},
"WeaponCategories": {
"melee": "Melee",
"ranged": "Ranged"
},
"Resonations": {
"accelerate": "Accelerate",
"blast": "Blast",
"breathe-water": "Breathe Water",
"cauterize": "Cauterize",
"create-illusion": "Create Illusion",
"distract": "Distract",
"eagle-eye": "Eagle Eye",
"empower-weapon": "Empower Weapon",
"fireball": "Fireball",
"hover": "Hover",
"influence-mind": "Influence Mind",
"knit-flesh": "Knit Flesh",
"light-construct": "Light Construct",
"mirage": "Mirage",
"negate-injury": "Negate Injury",
"paralyze": "Paralyze",
"shield": "Shield",
"shock": "Shock",
"shroud": "Shroud",
"summon-mist": "Summon Mist"
},
"EquipmentSubtypes": {
"gear": "Gear",
"consumable": "Consumable",
"travel": "Travel",
"container": "Container",
"tool": "Tool"
},
"Tabs": {
"overview": "Overview",
"daily": "Daily Resources",
"equipment": "Equipment",
"features": "Features",
"notes": "Notes"
},
"Character": {
"Background": "Background",
"Origin": "Origin",
"Scars": "Scars",
"Motivation": "Motivation",
"Vice": "Vice",
"QuickRest": "Quick Rest",
"QuickRestHelp": "5 minutes, 1 ration, and a swig of water.",
"FullRest": "Full Rest",
"FullRestHelp": "6 hours, 1 ration, and a relatively safe environment.",
"ResetDailyResources": "Reset Daily Resources",
"Omens": "Omens",
"Resonations": "Resonations",
"Remaining": "Remaining",
"ResonancePerDay": "Resonance per Day",
"ArtifactSync": "Artifact Sync",
"CarryingCapacity": "Carrying Capacity",
"Rations": "Rations",
"Kiffol": "Kiffol",
"Weapons": "Weapons",
"AddWeapon": "Add Weapon",
"Armor": "Armor",
"AddArmor": "Add Armor",
"AddShield": "Add Shield",
"AddEquipment": "Add Equipment",
"ResonanceCores": "Resonance Cores",
"AddCore": "Add Core",
"Artifacts": "Artifacts",
"AddArtifact": "Add Artifact",
"AddFeature": "Add Feature",
"RulesSnapshot": "Rules Snapshot",
"FIELDS": {
"abilities": {
"label": "Abilities",
"agility": {
"label": "Agility"
},
"presence": {
"label": "Presence"
},
"strength": {
"label": "Strength"
},
"toughness": {
"label": "Toughness"
}
},
"hp": {
"label": "HP",
"value": {
"label": "Current"
},
"max": {
"label": "Max"
}
},
"omens": {
"label": "Omens",
"current": {
"label": "Current"
},
"die": {
"label": "Die"
}
},
"resonance": {
"label": "Resonance",
"max": {
"label": "Max"
},
"used": {
"label": "Used"
},
"blocked": {
"label": "Blocked"
}
},
"artifactSync": {
"label": "Artifact Sync",
"used": {
"label": "Used"
}
},
"survival": {
"label": "Survival",
"salvationUsed": {
"label": "Salvation Used"
}
},
"conditions": {
"label": "Conditions",
"bleeding": {
"label": "Bleeding"
},
"blinded": {
"label": "Blinded"
},
"burning": {
"label": "Burning"
},
"fatigued": {
"label": "Fatigued"
},
"infected": {
"label": "Infected"
},
"poisoned": {
"label": "Poisoned"
},
"prone": {
"label": "Prone"
},
"restrained": {
"label": "Restrained"
},
"starved": {
"label": "Starved"
},
"stunned": {
"label": "Stunned"
}
},
"carryCapacity": {
"label": "Carrying Capacity"
},
"rations": {
"label": "Rations"
},
"kiffol": {
"label": "Kiffol"
},
"background": {
"label": "Background"
},
"origin": {
"label": "Origin"
},
"scars": {
"label": "Scars"
},
"motivation": {
"label": "Motivation"
},
"vice": {
"label": "Vice"
},
"description": {
"label": "Description"
},
"notes": {
"label": "Notes"
}
}
},
"Creature": {
"Special": "Special",
"FIELDS": {
"abilities": {
"label": "Abilities",
"agility": {
"label": "Agility"
},
"presence": {
"label": "Presence"
},
"strength": {
"label": "Strength"
},
"toughness": {
"label": "Toughness"
}
},
"hp": {
"label": "HP",
"value": {
"label": "Current"
},
"max": {
"label": "Max"
}
},
"morale": {
"label": "Morale"
},
"armor": {
"label": "Armor",
"die": {
"label": "Armor Die"
}
},
"attack": {
"label": "Attack",
"damage": {
"label": "Damage"
}
},
"description": {
"label": "Description"
},
"special": {
"label": "Special"
},
"notes": {
"label": "Notes"
}
}
},
"Companion": {
"TheyValue": "They Value...",
"Trait": "Trait",
"Specialty": "Specialty",
"AdventuringBehavior": "Adventuring Behavior",
"CombatBehavior": "Combat Behavior",
"FIELDS": {
"abilities": {
"label": "Abilities",
"agility": {
"label": "Agility"
},
"presence": {
"label": "Presence"
},
"strength": {
"label": "Strength"
},
"toughness": {
"label": "Toughness"
}
},
"hp": {
"label": "HP",
"value": {
"label": "Current"
},
"max": {
"label": "Max"
}
},
"morale": {
"label": "Morale"
},
"armor": {
"label": "Armor",
"die": {
"label": "Armor Die"
}
},
"attack": {
"label": "Attack",
"damage": {
"label": "Damage"
}
},
"valueText": {
"label": "They Value..."
},
"traitText": {
"label": "Trait"
},
"specialtyText": {
"label": "Specialty"
},
"adventuringBehavior": {
"label": "Adventuring Behavior"
},
"combatBehavior": {
"label": "Combat Behavior"
},
"upkeep": {
"label": "Upkeep"
},
"description": {
"label": "Description"
},
"notes": {
"label": "Notes"
}
}
},
"DataModel": {
"abilities": {
"agility": {
"label": "Agility"
},
"presence": {
"label": "Presence"
},
"strength": {
"label": "Strength"
},
"toughness": {
"label": "Toughness"
}
}
},
"Common": {
"Attack": "Attack",
"ArmorDie": "Armor Die",
"ArtifactId": "Artifact Id",
"Broken": "Broken",
"BurnedOut": "Burned Out",
"Carried": "Carried",
"Category": "Category",
"Conditions": "Conditions",
"Consumable": "Consumable",
"Current": "Current",
"Damage": "Damage",
"Depleted": "Depleted",
"Defense": "Defense",
"Delete": "Delete",
"Description": "Description",
"Die": "Die",
"Edit": "Edit",
"Equip": "Equip",
"Equipped": "Equipped",
"FeatureId": "Feature Id",
"HP": "HP",
"Invoke": "Invoke",
"Label": "Label",
"Limit": "Limit",
"Max": "Max",
"Morale": "Morale",
"NewItem": "New {type}",
"Notes": "Notes",
"Penalty": "Penalty",
"Properties": "Properties",
"Quantity": "Quantity",
"QuantityShort": "Qty",
"Range": "Range",
"Resonation": "Resonation",
"Roll": "Roll",
"Subtype": "Subtype",
"Sync": "Sync",
"Desync": "Desync",
"Synchronized": "Synchronized",
"SynchronizedTo": "Synchronized To",
"Unequip": "Unequip",
"Unsynchronized": "Unsynchronized",
"Usage": "Usage",
"UsageDie": "Usage Die",
"Used": "Used",
"Value": "Value"
},
"Empty": {
"NoArtifacts": "No artifacts yet.",
"NoEquipment": "No equipment yet.",
"NoFeatures": "No features yet.",
"NoResonanceCores": "No resonance cores yet.",
"NoWeapons": "No weapons yet."
},
"RulesSnapshot": {
"Checks": "Checks use d20 + ability vs DR.",
"Attacks": "Melee attacks use Strength, ranged attacks use Presence, defense uses Agility, resonations use Presence.",
"Armor": "Armor is rolled automatically when damage is applied.",
"Breaks": "At 0 HP a character Breaks.",
"Morale": "Morale checks use 2d6 and break on score or higher."
},
"RollDialog": {
"DR": "DR",
"Modifier": "Modifier",
"SpendOmen": "Spend 1 omen to lower DR by 4",
"VsDR": "vs DR"
},
"Initiative": {
"SideRoll": "Initiative side roll: {roll}.",
"PlayersFirst": "Players act first.",
"EnemiesFirst": "Enemies act first.",
"TieBreak": "Use 1d6 + Agility to decide who acts next."
},
"Combat": {
"Flee": "Fleeing",
"FleeNoCombatant": "Select or activate a combatant before resolving fleeing.",
"StudyActions": "Actions spent studying the battlefield",
"StudyHelp": "Each action lowers the target number by 1, to a minimum of 2+."
},
"Notification": {
"ActorOrItemNotFound": "Actor or item not found for damage roll.",
"CannotSyncMore": "{actor} cannot synchronize any more artifacts today.",
"ItemBroken": "{item} is broken.",
"ItemBurnedOut": "{item} is burned out.",
"ItemDepleted": "{item} is already depleted.",
"ResonancePerDayReached": "{actor} has already used all resonations for today."
},
"Roll": {
"AppliedDamage": "Applied {amount}",
"ApplyTo": "Apply to...",
"ApplyDamageTo": "Apply {amount} damage to {target}",
"AppliedDamageText": "Applied damage: {amount}.",
"ArmorAbsorbed": "Armor absorbed {amount}.",
"ArmorDegradedCritical": "Critical: {item} armor downgraded to {die}.",
"ArmorNothingToDegrade": "Critical: no armor to downgrade.",
"AttackFumble": "Attack fumble: the weapon breaks.",
"BreakText": "Break: {text}",
"CheckLabel": "{ability} Check",
"CheckSubtitle": "{ability} vs DR {dr}",
"CriticalAttack": "Critical attack: your next damage roll for this attack is doubled, and the target's armor is downgraded by one step.",
"CriticalDamageApplied": "Critical damage applied.",
"CurrentDie": "Current die {die}",
"DamageSourceItem": "{item}",
"DamageSourceWithActor": "{item} from {actor}",
"DefenseCheck": "Defense Check",
"DefenseCritical": "Defense crit: you may make an immediate counterattack.",
"DefenseFumble": "Defense fumble: the next damage you take is doubled, then your armor is downgraded by one step.",
"DefenseFumbleApplied": "Defense fumble: {item} is downgraded to {die}.",
"DefenseFumbleNoArmor": "Defense fumble: no armor remained to downgrade.",
"DirectDamage": "Direct damage",
"DowngradedTo": "Downgraded to {die}",
"FleeEscaped": "Escaped",
"FleeKilled": "Caught and killed",
"FleeLabel": "{actor} Attempts to Flee",
"FleeNoStudyActions": "No time was spent studying the battlefield.",
"FleeStudyActions": "Studied the battlefield for {count} action(s).",
"FleeSubtitle": "Escape on {target}+",
"FullRestLabel": "{actor} Takes a Full Rest",
"HPNow": "HP now {hp}",
"HPNowMax": "HP now {hp}/{max}.",
"InvocationLabel": "{item} Invocation",
"ItemAttackLabel": "{item} Attack",
"ItemDamageLabel": "{item} Damage",
"ItemNowDepleted": "The item is depleted.",
"ItemUsageLabel": "{item} Usage",
"MoraleBrokenText": "The actor breaks down and may flee, surrender, or grant +d4 damage to opponents.",
"MoraleCheck": "Morale Check",
"NoChange": "No change",
"NoTargetSelected": "No target selected",
"OmensReset": "Omens reset to {omens} ({die}: {roll}).",
"OutcomeBroken": "Broken",
"OutcomeCriticalSuccess": "Critical Success",
"OutcomeFailure": "Failure",
"OutcomeFumble": "Fumble",
"OutcomeRolled": "Rolled",
"OutcomeSteady": "Steady",
"OutcomeSuccess": "Success",
"QuickRestLabel": "{actor} Takes a Quick Rest",
"RestoredHP": "Restored {amount} HP",
"RollDamage": "Roll Damage",
"TakesDamageLabel": "{actor} Takes Damage",
"TargetName": "Target: {target}",
"TargetSubtitle": "Target {target}"
},
"Chat": {
"DamageSummary": "Absorbed {absorbed}, applied {appliedDamage}, HP now {hp}.",
"Formula": "Formula",
"Result": "Result",
"Mode": {
"action": "Action",
"apply-damage": "Damage",
"check": "Check",
"damage": "Damage",
"flee": "Fleeing",
"generic": "Roll",
"morale": "Morale",
"rest": "Rest",
"usage": "Usage"
}
},
"Weapon": {
"FIELDS": {
"description": {
"label": "Description"
},
"category": {
"label": "Category"
},
"damage": {
"label": "Damage"
},
"range": {
"label": "Range"
},
"properties": {
"label": "Properties"
},
"usageDie": {
"label": "Usage Die"
},
"quantity": {
"label": "Quantity"
},
"equipped": {
"label": "Equipped"
},
"broken": {
"label": "Broken"
}
}
},
"Armor": {
"FIELDS": {
"description": {
"label": "Description"
},
"armorDie": {
"label": "Armor Die"
},
"penalty": {
"label": "Penalty"
},
"equipped": {
"label": "Equipped"
},
"broken": {
"label": "Broken"
}
}
},
"Shield": {
"FIELDS": {
"description": {
"label": "Description"
},
"armorDie": {
"label": "Armor Die"
},
"penalty": {
"label": "Penalty"
},
"equipped": {
"label": "Equipped"
},
"broken": {
"label": "Broken"
}
}
},
"Equipment": {
"FIELDS": {
"description": {
"label": "Description"
},
"subtype": {
"label": "Subtype"
},
"quantity": {
"label": "Quantity"
},
"carried": {
"label": "Carried"
},
"equipped": {
"label": "Equipped"
},
"usageDie": {
"label": "Usage Die"
},
"consumable": {
"label": "Consumable"
}
}
},
"ResonanceCore": {
"FIELDS": {
"description": {
"label": "Description"
},
"resonationId": {
"label": "Resonation"
},
"usageDie": {
"label": "Usage Die"
},
"burnedOut": {
"label": "Burned Out"
}
}
},
"Artifact": {
"FIELDS": {
"description": {
"label": "Description"
},
"artifactId": {
"label": "Artifact Id"
},
"usageDie": {
"label": "Usage Die"
},
"synchronized": {
"label": "Synchronized"
},
"broken": {
"label": "Broken"
},
"synchronizedTo": {
"label": "Synchronized To"
}
}
},
"Feature": {
"FIELDS": {
"description": {
"label": "Description"
},
"featureId": {
"label": "Feature Id"
}
}
}
},
"TYPES": {
"Actor": {
"character": "Character",
"creature": "Creature",
"companion": "Companion"
},
"Item": {
"weapon": "Weapon",
"armor": "Armor",
"shield": "Shield",
"equipment": "Equipment",
"resonance-core": "Resonance Core",
"artifact": "Artifact",
"feature": "Feature"
}
}
}
+220
View File
@@ -0,0 +1,220 @@
.application.mgne,
.mgne-chat-card {
--mgne-bg: @bg-char;
--mgne-panel: @bg-panel;
--mgne-panel-soft: @bg-panel-soft;
--mgne-border: fade(@bone, 35%);
--mgne-border-strong: fade(@gold-acid, 42%);
--mgne-accent: @ember-bright;
--mgne-accent-alt: @verdigris;
--mgne-text: @parchment;
--mgne-text-soft: @dust;
--mgne-title: @bone;
--mgne-shadow: @shadow-heavy;
}
.application.mgne {
.body-copy();
color: @parchment;
.window-header {
background:
linear-gradient(90deg, fade(@blood, 40%), fade(@ember, 18%) 25%, transparent 70%),
linear-gradient(180deg, color-mix(in srgb, @bg-char 80%, black), @bg-void);
border-bottom: 1px solid fade(@gold-acid, 35%);
color: @bone;
.window-title {
.caps-heading();
font-size: 1rem;
text-shadow: 0 0 16px fade(@ember-bright, 25%);
}
.header-control {
font-family: var(--font-awesome, "Font Awesome 6 Pro");
font-style: normal;
font-variant: normal;
text-rendering: auto;
}
.header-control::before,
i[class^="fa"],
i[class*=" fa"] {
font-family: inherit;
font-style: normal;
font-variant: normal;
text-rendering: auto;
}
.header-control.fa-solid,
.header-control.fas,
.fa-solid,
.fas {
font-weight: 900;
}
.header-control.fa-regular,
.header-control.far,
.fa-regular,
.far {
font-weight: 400;
}
}
.window-content {
background:
radial-gradient(circle at top left, fade(@ember, 12%), transparent 28%),
radial-gradient(circle at top right, fade(@verdigris, 10%), transparent 24%),
linear-gradient(180deg, fade(@bg-char, 30%), fade(@bg-void, 28%)),
url("@{page-bg-url}") center top / cover no-repeat,
linear-gradient(180deg, @bg-char, @bg-void 120%);
color: @parchment;
min-height: 0;
overflow-y: auto;
scrollbar-gutter: stable;
}
}
.application.mgne.character {
.window-content {
display: flex;
flex-direction: column;
overflow: hidden;
}
}
.application.mgne *,
.mgne-chat-card * {
box-sizing: border-box;
}
.application.mgne input,
.application.mgne select,
.application.mgne textarea,
.application.mgne button,
.application.mgne label,
.application.mgne p,
.application.mgne span,
.application.mgne small,
.mgne-chat-card {
.body-copy();
}
.application.mgne input,
.application.mgne select,
.application.mgne textarea {
width: 100%;
border: 1px solid fade(@bone, 22%);
border-radius: @radius-sm;
background:
linear-gradient(180deg, fade(@bone, 4%), transparent 60%),
@bg-input;
color: @parchment;
padding: 0.4rem 0.52rem;
box-shadow: inset 0 1px 0 fade(white, 4%);
&:focus {
outline: none;
border-color: fade(@ember-bright, 70%);
box-shadow: 0 0 0 1px fade(@ember-bright, 25%), inset 0 1px 0 fade(white, 6%);
}
}
.application.mgne select {
appearance: none;
padding-right: 1.3rem;
background-image:
linear-gradient(45deg, transparent 50%, fade(@gold-acid, 78%) 50%),
linear-gradient(135deg, fade(@gold-acid, 78%) 50%, transparent 50%),
linear-gradient(180deg, fade(@bone, 4%), transparent 60%);
background-position:
calc(100% - 11px) calc(50% - 2px),
calc(100% - 7px) calc(50% - 2px),
0 0;
background-size: 4px 4px, 4px 4px, auto;
background-repeat: no-repeat;
}
.application.mgne textarea {
min-height: 4.5rem;
resize: vertical;
}
.application.mgne button {
.metal-button();
padding: 0.34rem 0.72rem;
}
.application.mgne label {
.caps-heading();
color: @label-ink;
font-size: 0.74rem;
}
.application.mgne fieldset {
.panel-shell();
.ornate-frame();
margin: 0;
}
.application.mgne legend {
.caps-heading();
color: @gold-acid;
display: inline-block;
position: relative;
top: 0.12rem;
font-size: 0.82rem;
line-height: 1.1;
padding: 0.04rem 0.32rem;
}
.application.mgne code,
.mgne-chat-card code {
font-family: "Courier New", monospace;
background: fade(@bg-void, 75%);
color: @bone;
padding: 0.12rem 0.35rem;
border-radius: 4px;
}
.application.mgne .empty-state {
color: fade(@ash, 94%);
font-style: italic;
letter-spacing: 0.03em;
}
.application.mgne .rollable {
color: @ember-bright;
text-shadow: 0 0 12px fade(@ember, 18%);
&:hover {
color: lighten(@ember-bright, 10%);
}
}
#combat .combat-controls .mgne-flee-control {
display: flex;
align-items: center;
justify-content: center;
gap: 0.45rem;
width: 100%;
min-height: 2rem;
margin-top: 0.35rem;
border: 1px solid fade(@gold-acid, 28%);
border-radius: @radius-sm;
background:
linear-gradient(180deg, fade(@blood, 26%), fade(@ember, 14%)),
fade(@bg-input, 92%);
color: @bone;
font-family: @font-body;
font-weight: 700;
&:hover:not(:disabled) {
border-color: fade(@ember-bright, 52%);
color: lighten(@bone, 6%);
}
&:disabled {
opacity: 0.5;
}
}
+215
View File
@@ -0,0 +1,215 @@
.mgne-chat-card {
.panel-shell();
.ornate-frame();
display: flex;
flex-direction: column;
gap: 0.6rem;
padding: 0.68rem;
color: @iron;
background:
radial-gradient(circle at top right, fade(@verdigris, 10%), transparent 22%),
linear-gradient(135deg, fade(@ember, 10%), transparent 38%),
linear-gradient(180deg, fade(@bone, 4%), transparent 24%),
linear-gradient(180deg, fade(@bg-char, 22%), fade(@bg-void, 18%)),
url("@{page-bg-url}") right bottom / cover no-repeat,
@bg-panel;
}
.mgne-chat-card .chat-card-header {
display: flex;
gap: 0.6rem;
align-items: center;
}
.mgne-chat-card .chat-card-header img {
width: 36px;
height: 36px;
object-fit: cover;
border-radius: 50%;
border: 1px solid fade(@gold-acid, 38%);
box-shadow: 0 0 0 2px fade(@parchment, 6%);
}
.mgne-chat-card .chat-eyebrow {
display: none;
}
.mgne-chat-card h3 {
.caps-heading();
margin: 0;
color: @label-ink;
font-size: 0.82rem;
}
.mgne-chat-card .chat-actor,
.mgne-chat-card .chat-subtitle,
.mgne-chat-card .chat-formula,
.mgne-chat-card .chat-special {
margin: 0;
}
.mgne-chat-card .chat-actor {
color: @iron;
}
.mgne-chat-card .chat-formula {
color: @iron;
code {
color: @parchment;
background: fade(@bg-void, 75%);
padding: 0.1em 0.42em;
border-radius: 3px;
}
}
.mgne-chat-card .chat-result-line {
display: flex;
justify-content: space-between;
align-items: end;
padding: 0.38rem 0.55rem;
border: 1px solid fade(@gold-acid, 28%);
border-radius: @radius-sm;
background: fade(@bg-void, 78%);
}
.mgne-chat-card .chat-result-label {
.caps-heading();
font-size: 0.62rem;
color: @parchment;
}
.mgne-chat-card .chat-result-total {
font-family: @font-display;
font-size: 1.08rem;
color: @parchment;
}
.mgne-chat-card .chat-outcome {
.caps-heading();
margin: 0;
padding: 0.3rem 0.55rem;
border-radius: @radius-sm;
background: fade(@bg-void, 72%);
border-left: 3px solid fade(@ember-bright, 70%);
color: lighten(@ember-bright, 12%);
font-size: 0.8rem;
}
.mgne-chat-card .chat-special {
padding: 0.42rem 0.55rem;
border-left: 3px solid fade(@ember-bright, 55%);
background: fade(@bg-input, 85%);
color: @parchment;
}
.mgne-chat-card.outcome-critical-success,
.mgne-chat-card.outcome-success,
.mgne-chat-card.outcome-steady {
border-color: fade(@verdigris, 42%);
.chat-outcome {
color: lighten(@verdigris, 28%);
border-left-color: fade(@verdigris, 80%);
}
}
.mgne-chat-card.outcome-failure,
.mgne-chat-card.outcome-broken,
.mgne-chat-card.outcome-fumble {
border-color: fade(@blood, 48%);
.chat-outcome {
color: lighten(@ember-bright, 18%);
border-left-color: fade(@ember-bright, 80%);
}
}
.mgne-chat-card.mode-apply-damage .chat-result-total,
.mgne-chat-card.mode-damage .chat-result-total {
color: lighten(@ember-bright, 8%);
}
.mgne-chat-card .chat-card-actions {
margin-top: 0.2rem;
}
.mgne-roll-damage-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 0.45rem;
width: 100%;
padding: 0.38rem 0.7rem;
border: 1px solid @ember-bright;
border-radius: @radius-sm;
background: @ember;
color: #f8ede0;
font-family: @font-display;
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.06em;
cursor: pointer;
transition: background 0.15s, border-color 0.15s;
.dmg-formula {
color: #fff5e8;
font-size: 0.75rem;
font-weight: 700;
}
&:hover {
background: lighten(@ember, 8%);
border-color: lighten(@ember-bright, 12%);
color: #fff;
}
&.is-critical {
border: 1px solid @gold-acid;
background: darken(@gold-acid, 10%);
color: #fff5d0;
font-weight: 700;
.dmg-formula {
color: #fff;
}
&:hover {
background: @gold-acid;
color: #fff;
}
}
}
.chat-apply-actions {
margin-top: 0.35rem;
}
.mgne-apply-damage-select {
width: 100%;
padding: 0.35rem 0.6rem;
border: 1px solid @ember-bright;
border-radius: @radius-sm;
background: @bg-char;
color: @bone;
font-family: @font-body;
font-size: 0.8rem;
font-weight: 600;
cursor: pointer;
transition: border-color 0.15s, background 0.15s;
option {
background: @bg-char;
color: @bone;
}
&:hover {
border-color: lighten(@ember-bright, 12%);
background: rgba(183, 70, 31, 0.18);
}
&:focus {
outline: none;
border-color: @ember-bright;
}
}
+57
View File
@@ -0,0 +1,57 @@
.application.mgne.roll-dialog .window-content {
padding: 0.55rem;
background:
radial-gradient(circle at top left, fade(@ember-bright, 14%), transparent 26%),
linear-gradient(180deg, fade(@bg-char, 26%), fade(@bg-void, 22%)),
url("@{page-bg-url}") center center / cover no-repeat,
linear-gradient(180deg, @bg-char, @bg-void 120%);
}
.application.mgne.roll-dialog .dialog-content {
padding: 0;
}
.application.mgne.roll-dialog .mgne-roll-dialog {
background:
linear-gradient(135deg, fade(@ember, 10%), transparent 32%),
linear-gradient(180deg, fade(@bg-char, 94%), fade(@bg-void, 98%)),
@bg-void;
border: 1px solid fade(@gold-acid, 28%);
border-radius: @radius-sm;
box-shadow: @shadow-heavy, @shadow-inset;
.ornate-frame();
display: flex;
flex-direction: column;
gap: 0.55rem;
padding: 0.7rem;
> p:first-child {
.caps-heading();
font-size: 0.9rem;
color: @gold-acid;
margin: 0;
}
> p:nth-child(2) {
margin: 0;
color: @bone;
font-style: italic;
}
label {
color: @parchment;
}
strong {
color: @parchment;
}
}
.application.mgne.roll-dialog .mgne-roll-dialog label,
.application.mgne.roll-dialog .mgne-roll-dialog .checkbox-line {
color: @parchment;
}
.application.mgne.roll-dialog .form-footer button {
min-width: 132px;
}
+13
View File
@@ -0,0 +1,13 @@
@font-face {
font-family: "CastorTwoMGNE";
src: url("../assets/fonts/CASTOR TWO W01 REGULAR.TTF") format("truetype");
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: "LoraMGNE";
src: url("../assets/fonts/LORA-REGULAR.TTF") format("truetype");
font-weight: 400;
font-style: normal;
}
+7
View File
@@ -0,0 +1,7 @@
@import "fonts.less";
@import "variables.less";
@import "mixins.less";
@import "base.less";
@import "sheets.less";
@import "dialogs.less";
@import "chat.less";
+57
View File
@@ -0,0 +1,57 @@
.panel-shell() {
position: relative;
background:
linear-gradient(180deg, fade(@bone, 4%), transparent 24%),
linear-gradient(135deg, fade(@ember, 8%), transparent 40%),
linear-gradient(180deg, fade(@bg-char, 52%), fade(@bg-void, 34%)),
url("@{page-bg-url}") center center / cover no-repeat,
linear-gradient(180deg, @bg-panel 0%, color-mix(in srgb, @bg-panel 88%, black) 100%);
border: @border-main;
border-radius: @radius-md;
box-shadow: @shadow-heavy, @shadow-inset;
}
.ornate-frame() {
position: relative;
&::before {
content: "";
position: absolute;
inset: 6px;
pointer-events: none;
border: 1px solid fade(@gold-acid, 16%);
border-radius: calc(@radius-md - 4px);
}
}
.caps-heading() {
font-family: @font-display;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.body-copy() {
font-family: @font-body;
line-height: 1.45;
}
.metal-button() {
.caps-heading();
border: 1px solid fade(@gold-acid, 44%);
border-radius: 999px;
background:
linear-gradient(180deg, fade(@ember-bright, 20%), fade(@blood, 10%)),
linear-gradient(135deg, fade(@bone, 10%), transparent 40%),
@bg-input;
color: @parchment;
box-shadow: inset 0 1px 0 fade(white, 8%), 0 6px 14px fade(black, 20%);
transition: transform 120ms ease, box-shadow 120ms ease, border-color 120ms ease, color 120ms ease;
&:hover,
&:focus {
border-color: fade(@ember-bright, 70%);
color: lighten(@parchment, 8%);
transform: translateY(-1px);
box-shadow: inset 0 1px 0 fade(white, 14%), 0 8px 18px fade(@blood, 26%);
}
}
+643
View File
@@ -0,0 +1,643 @@
.application.mgne .mgne-sheet {
display: flex;
flex-direction: column;
gap: 0.65rem;
}
.application.mgne.character .mgne-sheet-header {
flex: 0 0 auto;
}
.application.mgne .sheet-header {
display: grid;
grid-template-columns: 148px 1fr;
gap: 0.8rem;
align-items: start;
}
.application.mgne.item-sheet .sheet-header {
grid-template-columns: 111px 1fr;
}
.application.mgne .actor-portrait,
.application.mgne .item-portrait {
width: 100%;
max-width: 148px;
aspect-ratio: 1 / 1;
object-fit: cover;
border-radius: @radius-lg;
border: 1px solid fade(@gold-acid, 40%);
box-shadow:
0 12px 24px fade(black, 34%),
inset 0 0 0 2px fade(@parchment, 8%);
background:
radial-gradient(circle at 30% 20%, fade(@bone, 8%), transparent 30%),
@bg-input;
}
.application.mgne.item-sheet .item-portrait {
max-width: 111px;
}
.application.mgne .header-fields,
.application.mgne .resource-bar,
.application.mgne .ability-grid,
.application.mgne .inventory-section,
.application.mgne .daily-resources-panel,
.application.mgne .tab-panel {
.panel-shell();
.ornate-frame();
padding: 0.65rem;
}
.application.mgne .resource-bar,
.application.mgne .ability-grid,
.application.mgne .grid.two,
.application.mgne .grid.three,
.application.mgne .condition-value-grid,
.application.mgne .condition-flag-grid {
display: grid;
gap: 0.5rem;
}
.application.mgne .resource-bar {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.application.mgne .resource-bar-core {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.application.mgne .resource-bar-daily {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.application.mgne .resource-bar-equipment {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.application.mgne .ability-grid {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.application.mgne .grid.two {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.application.mgne .grid.three {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.application.mgne.item-sheet .item-form-grid {
display: grid;
gap: 0.65rem 0.9rem;
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}
.application.mgne.item-sheet .item-form-grid-two {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.application.mgne.item-sheet .item-form-grid-three {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.application.mgne.item-sheet .item-form-row {
display: grid;
grid-template-columns: minmax(6rem, max-content) minmax(0, 1fr);
align-items: center;
gap: 0.55rem;
min-width: 0;
> label {
margin: 0;
line-height: 1.1;
min-width: 0;
}
> input,
> select {
width: 100%;
min-width: 0;
}
}
.application.mgne .condition-value-grid {
grid-template-columns: max-content 1fr;
align-items: center;
margin-bottom: 0.6rem;
}
.application.mgne .condition-flag-grid {
grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
gap: 0.3rem 0.6rem;
}
.application.mgne .resource-box,
.application.mgne .ability-card {
position: relative;
display: flex;
flex-direction: column;
gap: 0.28rem;
padding: 0.52rem;
border-radius: @radius-md;
border: 1px solid fade(@iron, 30%);
background:
linear-gradient(180deg, fade(@bone, 4%), transparent 30%),
linear-gradient(180deg, fade(@ember, 7%), transparent 80%),
@bg-panel-soft;
}
.application.mgne .ability-card {
min-height: 0;
flex-direction: row;
align-items: center;
justify-content: space-between;
gap: 0.45rem;
padding: 0.38rem 0.46rem;
}
.application.mgne .resource-box-track,
.application.mgne .resource-box-single {
justify-content: space-between;
}
.application.mgne .resource-box-compact {
gap: 0.18rem;
padding: 0.36rem 0.42rem;
> label {
font-size: 0.68rem;
letter-spacing: 0.11em;
}
}
.application.mgne .resource-box-inline {
flex-direction: row;
align-items: center;
gap: 0.5rem;
> label {
flex: 0 0 auto;
margin: 0;
}
}
.application.mgne .resource-box-inline .resource-track,
.application.mgne .resource-box-inline .numeric-pill {
flex: 1 1 auto;
}
.application.mgne .resource-box-inline-track {
align-items: flex-start;
> label {
padding-top: 1rem;
}
}
.application.mgne .resource-box-inline-track .numeric-caption {
color: @label-ink;
}
.application.mgne .resource-box-inline-track .numeric-caption-strong {
padding: 0.08rem 0.34rem;
border-radius: 999px;
background: fade(@gold-acid, 18%);
border: 1px solid fade(@label-ink, 28%);
color: darken(@label-ink, 4%);
font-size: 0.6rem;
letter-spacing: 0.11em;
}
.application.mgne .resource-box-inline-track .numeric-cluster {
align-items: center;
}
.application.mgne .resource-box-inline-track .numeric-input {
width: 3.4rem;
min-width: 3.4rem;
}
.application.mgne .resource-box-inline-single .numeric-pill {
flex: 0 0 auto;
}
.application.mgne .resource-box-inline-single .numeric-input {
width: 3.2rem;
min-width: 3.2rem;
}
.application.mgne .header-resource {
align-self: stretch;
}
.application.mgne .header-resource .resource-box {
height: 100%;
}
.application.mgne .resource-track {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 0.32rem;
align-items: end;
}
.application.mgne .resource-track-die {
grid-template-columns: 1fr 1fr;
}
.application.mgne .numeric-cluster {
display: flex;
flex-direction: column;
gap: 0.18rem;
}
.application.mgne .numeric-caption {
.caps-heading();
color: fade(@label-soft, 94%);
font-size: 0.58rem;
letter-spacing: 0.12em;
}
.application.mgne .resource-box-compact .numeric-caption {
font-size: 0.52rem;
letter-spacing: 0.09em;
}
.application.mgne .track-separator {
align-self: center;
color: fade(@gold-acid, 72%);
font-family: @font-display;
font-size: 1rem;
line-height: 1;
}
.application.mgne .numeric-input {
min-width: 0;
text-align: center;
text-align-last: center;
font-variant-numeric: tabular-nums;
font-weight: 700;
font-size: 1rem;
padding-inline: 0.2rem;
}
.application.mgne .resource-box-compact .numeric-input,
.application.mgne .resource-box-compact .compact-select {
min-height: 30px;
padding: 0.18rem 0.16rem;
font-size: 0.9rem;
}
.application.mgne .numeric-input-readonly {
color: @bone;
}
.application.mgne .compact-select {
text-align: center;
text-align-last: center;
font-variant-numeric: tabular-nums;
font-weight: 700;
padding-inline: 0.35rem;
}
.application.mgne .numeric-pill {
display: flex;
align-items: center;
justify-content: center;
padding: 0.18rem;
border-radius: @radius-md;
border: 1px solid fade(@gold-acid, 18%);
background: fade(@bg-input, 64%);
}
.application.mgne .resource-box-compact .numeric-pill {
padding: 0.1rem;
}
.application.mgne .numeric-pill-suffix {
gap: 0.35rem;
justify-content: space-between;
}
.application.mgne .numeric-suffix {
flex: 0 0 auto;
color: @gold-acid;
font-weight: 700;
font-size: 1rem;
line-height: 1;
}
.application.mgne .resource-meta {
display: inline-flex;
align-self: flex-start;
margin-top: 0.12rem;
padding: 0.16rem 0.44rem;
border-radius: 999px;
border: 1px solid fade(@verdigris, 20%);
color: lighten(@verdigris, 18%);
background: fade(@verdigris, 10%);
font-size: 0.72rem;
}
.application.mgne .resource-box-compact .resource-track {
gap: 0.22rem;
}
.application.mgne .resource-box-compact .numeric-cluster {
gap: 0.1rem;
}
.application.mgne .resource-box-compact .track-separator {
font-size: 0.88rem;
}
.application.mgne .resource-box-compact .resource-meta {
margin-top: 0.06rem;
padding: 0.1rem 0.34rem;
font-size: 0.64rem;
}
.application.mgne .resource-inline {
display: flex;
gap: 0.32rem;
align-items: center;
span {
color: @dust;
}
}
.application.mgne .sheet-tabs {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
flex: 0 0 auto;
justify-content: center;
}
.application.mgne .tab-button {
min-width: 110px;
}
.application.mgne .tab-button.active {
border-color: fade(@ember-bright, 70%);
background:
linear-gradient(180deg, fade(@ember-bright, 26%), fade(@blood, 12%)),
linear-gradient(135deg, fade(@parchment, 12%), transparent 45%),
@bg-input;
color: lighten(@parchment, 6%);
}
.application.mgne .tab-panel:not(.active) {
display: none;
}
.application.mgne.character .tab-panel.active {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
}
.application.mgne .inventory-section {
display: flex;
flex-direction: column;
gap: 0.4rem;
}
.application.mgne .daily-resources-panel {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.application.mgne .section-heading {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 0.5rem;
flex-wrap: wrap;
}
.application.mgne .section-heading h2 {
.caps-heading();
margin: 0;
color: @bone;
font-size: 0.9rem;
}
.application.mgne .section-heading small {
display: inline-block;
margin-top: 0.12rem;
color: @dust;
font-size: 0.72rem;
}
.application.mgne .inventory-header,
.application.mgne .inline-buttons,
.application.mgne .item-actions,
.application.mgne .sheet-actions {
display: flex;
gap: 0.35rem;
align-items: center;
flex-wrap: wrap;
}
.application.mgne .resource-box-actions {
display: flex;
justify-content: flex-start;
margin-top: 0.12rem;
}
.application.mgne .resource-box-actions-rest {
gap: 0.32rem;
flex-wrap: wrap;
> button {
flex: 1 1 0;
min-width: 0;
padding-inline: 0.5rem;
white-space: nowrap;
}
}
.application.mgne .inventory-header {
justify-content: space-between;
}
.application.mgne .inventory-header h3 {
.caps-heading();
margin: 0;
color: @bone;
font-size: 0.82rem;
}
.application.mgne .item-row {
position: relative;
display: grid;
grid-template-columns: 2fr 1fr 1fr auto;
gap: 0.45rem;
align-items: center;
padding: 0.48rem 0.2rem 0.48rem 0.55rem;
border-radius: @radius-sm;
border: 1px solid transparent;
background:
linear-gradient(90deg, fade(@ember, 9%), transparent 35%),
linear-gradient(180deg, fade(@parchment, 2%), fade(@bg-void, 12%)),
fade(@bg-input, 80%);
&:hover {
border-color: fade(@gold-acid, 28%);
background:
linear-gradient(90deg, fade(@ember, 12%), transparent 35%),
linear-gradient(180deg, fade(@parchment, 4%), fade(@bg-void, 12%)),
fade(@bg-input, 88%);
}
}
.application.mgne .item-name {
color: @parchment;
font-weight: 700;
letter-spacing: 0.02em;
font-size: 0.95rem;
}
.application.mgne .check-grid {
display: flex;
gap: 0.7rem;
flex-wrap: wrap;
}
.application.mgne.item-sheet .item-check-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
gap: 0.45rem 0.9rem;
align-items: center;
}
.application.mgne.item-sheet .check-grid > label {
display: inline-flex;
align-items: center;
gap: 0.35rem;
flex: 0 0 auto;
width: fit-content;
margin: 0;
}
.application.mgne .checkbox-line {
display: inline-flex;
gap: 0.35rem;
align-items: center;
width: fit-content;
&.active {
color: @ember-bright;
font-weight: 600;
}
}
.application.mgne .check-grid input[type="checkbox"],
.application.mgne .checkbox-line input[type="checkbox"] {
--checkbox-size: 0.9rem;
flex: 0 0 0.9rem;
width: 0.9rem;
height: 0.9rem;
min-width: 0.9rem;
padding: 0;
margin: 0;
position: relative;
}
@media (max-width: 820px) {
.application.mgne.item-sheet .item-form-grid-two,
.application.mgne.item-sheet .item-form-grid-three {
grid-template-columns: minmax(0, 1fr);
}
}
.application.mgne .ability-label {
display: inline-flex;
flex: 1 1 auto;
align-items: center;
justify-content: flex-start;
min-width: 0;
}
.application.mgne .resource-label-accent {
color: @ember-bright;
text-shadow: 0 0 12px fade(@ember, 18%);
}
.application.mgne .ability-score {
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 58px;
min-height: 32px;
padding: 0.08rem;
border-radius: @radius-sm;
border: 1px solid fade(@gold-acid, 14%);
background: fade(@bg-input, 54%);
}
.application.mgne .ability-input {
font-family: @font-display;
font-size: 1.05rem;
line-height: 1;
color: @bone;
padding: 0.12rem;
}
.application.mgne select.ability-input {
flex: 1 1 auto;
width: 100%;
}
.application.mgne .ability-score-text {
flex-direction: column;
gap: 0.02rem;
}
.application.mgne .ability-defense-main {
.caps-heading();
color: @bone;
font-size: 0.8rem;
}
.application.mgne .ability-defense-sub {
color: @dust;
font-size: 0.62rem;
}
.application.mgne .character .ability-card:nth-child(odd),
.application.mgne .character .resource-box:nth-child(odd) {
background:
linear-gradient(180deg, fade(@verdigris, 10%), transparent 55%),
linear-gradient(180deg, fade(@parchment, 4%), transparent 35%),
@bg-panel-soft;
}
.application.mgne .creature .resource-box,
.application.mgne .companion .resource-box,
.application.mgne .creature .ability-card,
.application.mgne .companion .ability-card {
background:
linear-gradient(180deg, fade(@blood, 11%), transparent 60%),
linear-gradient(180deg, fade(@parchment, 4%), transparent 35%),
@bg-panel-soft;
}
@media (max-width: 960px) {
.application.mgne .sheet-header,
.application.mgne .resource-bar,
.application.mgne .ability-grid,
.application.mgne .grid.two,
.application.mgne .grid.three {
grid-template-columns: 1fr;
}
.application.mgne .item-row {
grid-template-columns: 1fr;
}
}
+29
View File
@@ -0,0 +1,29 @@
@font-display: "CastorTwoMGNE", "Palatino Linotype", serif;
@font-body: "LoraMGNE", "Book Antiqua", serif;
@page-bg-url: "../assets/ui/page_background.webp";
@bg-void: #13100f;
@bg-char: #1b1512;
@bg-panel: rgba(41, 30, 24, 0.88);
@bg-panel-soft: rgba(64, 47, 37, 0.66);
@bg-input: rgba(17, 12, 10, 0.72);
@parchment: #ccb292;
@bone: #ab8b68;
@dust: #7e664f;
@ash: #5f4d40;
@label-ink: #6e3d2a;
@label-soft: #81533b;
@iron: #52453c;
@copper: #8d5f3f;
@verdigris: #4f7d73;
@ember: #b7461f;
@ember-bright: #dd6b2d;
@blood: #7f1d17;
@gold-acid: #c49a45;
@shadow-heavy: 0 14px 30px rgba(0, 0, 0, 0.36);
@shadow-inset: inset 0 1px 0 rgba(255, 236, 203, 0.08), inset 0 0 0 1px rgba(255, 236, 203, 0.03);
@border-main: 1px solid fade(@bone, 34%);
@border-strong: 1px solid fade(@gold-acid, 45%);
@radius-sm: 6px;
@radius-md: 8px;
@radius-lg: 12px;
+12
View File
@@ -0,0 +1,12 @@
export { default as MGNEActorSheet } from "./sheets/base-actor-sheet.mjs"
export { default as MGNEItemSheet } from "./sheets/base-item-sheet.mjs"
export { default as MGNECharacterSheet } from "./sheets/character-sheet.mjs"
export { default as MGNECreatureSheet } from "./sheets/creature-sheet.mjs"
export { default as MGNECompanionSheet } from "./sheets/companion-sheet.mjs"
export { default as MGNEWeaponSheet } from "./sheets/weapon-sheet.mjs"
export { default as MGNEArmorSheet } from "./sheets/armor-sheet.mjs"
export { default as MGNEShieldSheet } from "./sheets/shield-sheet.mjs"
export { default as MGNEEquipmentSheet } from "./sheets/equipment-sheet.mjs"
export { default as MGNEResonanceCoreSheet } from "./sheets/resonance-core-sheet.mjs"
export { default as MGNEArtifactSheet } from "./sheets/artifact-sheet.mjs"
export { default as MGNEFeatureSheet } from "./sheets/feature-sheet.mjs"
@@ -0,0 +1,7 @@
import MGNEItemSheet from "./base-item-sheet.mjs"
export default class MGNEArmorSheet extends MGNEItemSheet {
static PARTS = {
main: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/armor.hbs" },
}
}
@@ -0,0 +1,7 @@
import MGNEItemSheet from "./base-item-sheet.mjs"
export default class MGNEArtifactSheet extends MGNEItemSheet {
static PARTS = {
main: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/artifact.hbs" },
}
}
@@ -0,0 +1,156 @@
import { SYSTEM } from "../../config/system.mjs"
import { buildSharedSelectOptions } from "./select-options.mjs"
import { enrichHTMLFields } from "./rich-text.mjs"
const { HandlebarsApplicationMixin } = foundry.applications.api
export default class MGNEActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
static DEFAULT_OPTIONS = {
classes: ["mgne", "actor-sheet"],
position: {
width: 900,
height: "auto",
},
window: {
resizable: true,
},
form: {
submitOnChange: true,
},
actions: {
editImage: MGNEActorSheet.onEditImage,
changeTab: MGNEActorSheet.onChangeTab,
createItem: MGNEActorSheet.onCreateItem,
editItem: MGNEActorSheet.onEditItem,
deleteItem: MGNEActorSheet.onDeleteItem,
toggleEquipped: MGNEActorSheet.onToggleEquipped,
syncArtifact: MGNEActorSheet.onSyncArtifact,
resetDaily: MGNEActorSheet.onResetDaily,
rollResonancePerDay: MGNEActorSheet.onRollResonancePerDay,
quickRest: MGNEActorSheet.onQuickRest,
fullRest: MGNEActorSheet.onFullRest,
},
}
async _prepareContext() {
const systemFields = this.document.system.schema.fields
return {
fields: this.document.schema.fields,
systemFields,
actor: this.document,
system: this.document.system,
source: this.document.toObject(),
config: SYSTEM,
enrichedFields: await enrichHTMLFields(this.document.system, systemFields),
isEditable: this.isEditable,
selectOptions: buildSharedSelectOptions(),
}
}
_onRender(context, options) {
super._onRender?.(context, options)
this.element.querySelectorAll(".rollable").forEach(element => {
element.addEventListener("click", this._onRoll.bind(this))
})
}
async _onRoll(event) {
const target = event.currentTarget
const itemId = target.closest("[data-item-id]")?.dataset.itemId
const rollType = target.dataset.rollType
switch (rollType) {
case "ability":
return this.document.rollAbility(target.dataset.abilityId)
case "defense":
return this.document.rollDefense()
case "weapon":
return this.document.rollWeapon(itemId)
case "profile-attack":
return this.document.rollProfileAttack()
case "profile-damage":
return this.document.rollProfileDamage()
case "damage":
return this.document.rollDamage(itemId)
case "resonation":
return this.document.rollResonation(itemId)
case "morale":
return this.document.rollMorale()
case "usage":
return this.document.rollUsage(itemId)
default:
return null
}
}
static async onEditImage(_event, target) {
const attr = target.dataset.edit
const current = foundry.utils.getProperty(this.document, attr)
const picker = new FilePicker({
current,
type: "image",
callback: path => this.document.update({ [attr]: path }),
top: this.position.top + 40,
left: this.position.left + 10,
})
return picker.browse()
}
static onChangeTab(_event, target) {
const group = target.dataset.group
const tab = target.dataset.tab
this.tabGroups[group] = tab
this.render()
}
static async onCreateItem(_event, target) {
const itemType = target.dataset.itemType
const typeLabel = SYSTEM.itemTypes[itemType]?.label ?? itemType
const [created] = await this.document.createEmbeddedDocuments("Item", [{
name: game.i18n.format("MGNE.Common.NewItem", { type: typeLabel }),
type: itemType,
img: SYSTEM.itemTypes[itemType]?.icon,
}])
created?.sheet?.render(true)
}
static async onEditItem(_event, target) {
const itemId = target.closest("[data-item-id]")?.dataset.itemId
const item = this.document.items.get(itemId)
return item?.sheet?.render(true)
}
static async onDeleteItem(_event, target) {
const itemId = target.closest("[data-item-id]")?.dataset.itemId
const item = this.document.items.get(itemId)
if (!item) return null
return item.delete()
}
static async onToggleEquipped(_event, target) {
const itemId = target.closest("[data-item-id]")?.dataset.itemId
return this.document.toggleItemEquipped(itemId)
}
static async onSyncArtifact(_event, target) {
const itemId = target.closest("[data-item-id]")?.dataset.itemId
return this.document.syncArtifact(itemId)
}
static async onResetDaily() {
return this.document.resetDaily()
}
static async onRollResonancePerDay() {
return this.document.rollResonancePerDay()
}
static async onQuickRest() {
return this.document.quickRest()
}
static async onFullRest() {
return this.document.fullRest()
}
}
@@ -0,0 +1,56 @@
import { SYSTEM } from "../../config/system.mjs"
import { buildSharedSelectOptions, numericOptions } from "./select-options.mjs"
import { enrichHTMLFields } from "./rich-text.mjs"
const { HandlebarsApplicationMixin } = foundry.applications.api
export default class MGNEItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
static DEFAULT_OPTIONS = {
classes: ["mgne", "item-sheet"],
position: {
width: 720,
height: "auto",
},
window: {
resizable: true,
},
form: {
submitOnChange: true,
},
actions: {
editImage: MGNEItemSheet.onEditImage,
},
}
async _prepareContext() {
const selectOptions = buildSharedSelectOptions()
const systemFields = this.document.system.schema.fields
if (this.document.type === "armor") selectOptions.penalties = numericOptions(0, 6, this.document.system.penalty)
if (this.document.type === "shield") selectOptions.penalties = numericOptions(0, 4, this.document.system.penalty)
return {
fields: this.document.schema.fields,
systemFields,
item: this.document,
system: this.document.system,
source: this.document.toObject(),
config: SYSTEM,
enrichedFields: await enrichHTMLFields(this.document.system, systemFields),
isEditable: this.isEditable,
selectOptions,
}
}
static async onEditImage(_event, target) {
const attr = target.dataset.edit
const current = foundry.utils.getProperty(this.document, attr)
const picker = new FilePicker({
current,
type: "image",
callback: path => this.document.update({ [attr]: path }),
top: this.position.top + 40,
left: this.position.left + 10,
})
return picker.browse()
}
}
@@ -0,0 +1,99 @@
import MGNEActorSheet from "./base-actor-sheet.mjs"
import { SYSTEM } from "../../config/system.mjs"
import { buildCharacterSelectOptions } from "./select-options.mjs"
export default class MGNECharacterSheet extends MGNEActorSheet {
static DEFAULT_OPTIONS = {
classes: ["character"],
position: {
width: 1040,
height: 760,
},
}
static PARTS = {
main: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/character-main.hbs" },
tabs: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/character-tabs.hbs" },
overview: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/character-overview.hbs" },
daily: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/character-daily.hbs" },
equipment: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/character-equipment.hbs" },
features: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/character-features.hbs" },
notes: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/character-notes.hbs" },
}
tabGroups = { sheet: "overview" }
getTabs() {
const tabs = {
overview: { id: "overview", group: "sheet", label: game.i18n.localize("MGNE.Tabs.overview") },
daily: { id: "daily", group: "sheet", label: game.i18n.localize("MGNE.Tabs.daily") },
equipment: { id: "equipment", group: "sheet", label: game.i18n.localize("MGNE.Tabs.equipment") },
features: { id: "features", group: "sheet", label: game.i18n.localize("MGNE.Tabs.features") },
notes: { id: "notes", group: "sheet", label: game.i18n.localize("MGNE.Tabs.notes") },
}
for (const tab of Object.values(tabs)) {
tab.active = this.tabGroups[tab.group] === tab.id
tab.cssClass = tab.active ? "active" : ""
}
return tabs
}
async _prepareContext() {
const context = await super._prepareContext()
context.tabs = this.getTabs()
context.abilityList = SYSTEM.abilityOrder.map(id => ({
id,
...SYSTEM.abilities[id],
value: context.source.system.abilities?.[id]?.value ?? 0,
}))
context.selectOptions = {
...context.selectOptions,
...buildCharacterSelectOptions(context.system),
}
return context
}
async _preparePartContext(partId, context) {
const doc = this.document
switch (partId) {
case "overview":
context.tab = context.tabs.overview
context.valueConditions = Object.entries(doc.system.conditions ?? {})
.filter(([id]) => SYSTEM.conditions[id]?.hasValue)
.map(([id, cond]) => ({
id,
label: SYSTEM.conditions[id].label,
value: cond.value,
options: context.selectOptions.conditionValues,
}))
context.flagConditions = Object.entries(doc.system.conditions ?? {})
.filter(([id]) => !SYSTEM.conditions[id]?.hasValue)
.map(([id, cond]) => ({
id,
label: SYSTEM.conditions[id].label,
active: cond.active,
}))
break
case "daily":
context.tab = context.tabs.daily
break
case "equipment":
context.tab = context.tabs.equipment
context.weapons = doc.itemTypes.weapon
context.armors = doc.itemTypes.armor
context.shields = doc.itemTypes.shield
context.equipmentItems = doc.itemTypes.equipment
context.cores = doc.itemTypes["resonance-core"]
context.artifacts = doc.itemTypes.artifact
break
case "features":
context.tab = context.tabs.features
context.features = doc.itemTypes.feature
break
case "notes":
context.tab = context.tabs.notes
break
}
return context
}
}
@@ -0,0 +1,26 @@
import MGNEActorSheet from "./base-actor-sheet.mjs"
import { SYSTEM } from "../../config/system.mjs"
export default class MGNECompanionSheet extends MGNEActorSheet {
static DEFAULT_OPTIONS = {
classes: ["companion"],
position: {
width: 820,
height: 700,
},
}
static PARTS = {
main: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/companion-main.hbs" },
}
async _prepareContext() {
const context = await super._prepareContext()
context.abilityList = SYSTEM.abilityOrder.map(id => ({
id,
...SYSTEM.abilities[id],
value: context.source.system.abilities?.[id]?.value ?? 0,
}))
return context
}
}
@@ -0,0 +1,26 @@
import MGNEActorSheet from "./base-actor-sheet.mjs"
import { SYSTEM } from "../../config/system.mjs"
export default class MGNECreatureSheet extends MGNEActorSheet {
static DEFAULT_OPTIONS = {
classes: ["creature"],
position: {
width: 760,
height: 640,
},
}
static PARTS = {
main: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/creature-main.hbs" },
}
async _prepareContext() {
const context = await super._prepareContext()
context.abilityList = SYSTEM.abilityOrder.map(id => ({
id,
...SYSTEM.abilities[id],
value: context.source.system.abilities?.[id]?.value ?? 0,
}))
return context
}
}
@@ -0,0 +1,7 @@
import MGNEItemSheet from "./base-item-sheet.mjs"
export default class MGNEEquipmentSheet extends MGNEItemSheet {
static PARTS = {
main: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/equipment.hbs" },
}
}
@@ -0,0 +1,7 @@
import MGNEItemSheet from "./base-item-sheet.mjs"
export default class MGNEFeatureSheet extends MGNEItemSheet {
static PARTS = {
main: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/feature.hbs" },
}
}
@@ -0,0 +1,7 @@
import MGNEItemSheet from "./base-item-sheet.mjs"
export default class MGNEResonanceCoreSheet extends MGNEItemSheet {
static PARTS = {
main: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/resonance-core.hbs" },
}
}
+17
View File
@@ -0,0 +1,17 @@
export async function enrichHTMLFields(data, schemaFields) {
const enrichedFields = {}
for (const [key, field] of Object.entries(schemaFields ?? {})) {
if (field instanceof foundry.data.fields.HTMLField) {
enrichedFields[key] = await foundry.applications.ux.TextEditor.implementation.enrichHTML(data?.[key] ?? "", { async: true })
continue
}
if (field instanceof foundry.data.fields.SchemaField) {
const nested = await enrichHTMLFields(data?.[key], field.fields)
if (Object.keys(nested).length) enrichedFields[key] = nested
}
}
return enrichedFields
}
@@ -0,0 +1,50 @@
import { SYSTEM } from "../../config/system.mjs"
function normalizeMax(max, current) {
return Math.max(max ?? 0, Number.isFinite(current) ? current : 0)
}
export function numericOptions(min, max, current = null) {
const resolvedMin = Math.min(min, Number.isFinite(current) ? current : min)
const resolvedMax = normalizeMax(max, current)
return Array.from({ length: Math.max(0, resolvedMax - resolvedMin) + 1 }, (_, index) => {
const value = resolvedMin + index
return { value, label: String(value) }
})
}
export function objectOptions(choices) {
return Object.entries(choices).map(([value, label]) => ({ value, label }))
}
export function dieMax(die) {
if (typeof die !== "string" || !die.startsWith("d")) return 0
const faces = Number.parseInt(die.slice(1), 10)
return Number.isFinite(faces) ? faces : 0
}
export function buildSharedSelectOptions() {
return {
abilityValues: numericOptions(-3, 6),
conditionValues: numericOptions(0, 12),
moraleValues: numericOptions(2, 12),
armorPenalties: numericOptions(0, 6),
shieldPenalties: numericOptions(0, 4),
weaponCategories: objectOptions(SYSTEM.weaponCategories),
usageDice: objectOptions(SYSTEM.usageDieChoices),
armorDice: objectOptions(SYSTEM.armorDieChoices),
omenDice: objectOptions(SYSTEM.omenDieChoices),
resonanceList: objectOptions(SYSTEM.resonanceList),
equipmentSubtypes: objectOptions(SYSTEM.equipmentSubtypes),
artifactIds: objectOptions(SYSTEM.artifactChoices),
featureIds: objectOptions(SYSTEM.featureChoices),
}
}
export function buildCharacterSelectOptions(system) {
return {
omenCurrent: numericOptions(0, dieMax(system.omens?.die), system.omens?.current),
resonanceUsed: numericOptions(0, system.resonance?.max ?? 0, system.resonance?.used),
artifactSyncUsed: numericOptions(0, system.syncLimit ?? 0, system.artifactSync?.used),
}
}
@@ -0,0 +1,7 @@
import MGNEItemSheet from "./base-item-sheet.mjs"
export default class MGNEShieldSheet extends MGNEItemSheet {
static PARTS = {
main: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/shield.hbs" },
}
}
@@ -0,0 +1,7 @@
import MGNEItemSheet from "./base-item-sheet.mjs"
export default class MGNEWeaponSheet extends MGNEItemSheet {
static PARTS = {
main: { template: "systems/fvtt-machine-gods-noxian-expanse/templates/weapon.hbs" },
}
}
+269
View File
@@ -0,0 +1,269 @@
export const SYSTEM_ID = "fvtt-machine-gods-noxian-expanse"
export const ASCII = `
Machine Gods of the Noxian Expanse
`
const dieChoiceLabels = values => Object.fromEntries(values.map(value => [value, value === "depleted" ? "Depleted" : value.toUpperCase()]))
const keyedNameChoices = (items, key) => Object.fromEntries(items.map(item => [item[key], item.name]))
const toIconSlug = name => name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")
const itemIcon = slug => `systems/${SYSTEM_ID}/assets/icons/${slug}.svg`
const withItemIcons = items => items.map(item => ({ ...item, img: itemIcon(toIconSlug(item.name)) }))
let localized = false
function localizeEntryLabels(entries, prefix) {
for (const [key, entry] of Object.entries(entries)) {
entry.label = game.i18n.localize(`${prefix}.${key}`)
}
}
function localizeChoiceLabels(entries, prefix) {
for (const key of Object.keys(entries)) {
entries[key] = game.i18n.localize(`${prefix}.${key}`)
}
}
export const SYSTEM = {
id: SYSTEM_ID,
actorTypes: {
character: { id: "character", label: "Character" },
creature: { id: "creature", label: "Creature" },
companion: { id: "companion", label: "Companion" },
},
itemTypes: {
weapon: { id: "weapon", label: "Weapon", icon: itemIcon("weapon") },
armor: { id: "armor", label: "Armor", icon: itemIcon("armor") },
shield: { id: "shield", label: "Shield", icon: itemIcon("shield") },
equipment: { id: "equipment", label: "Equipment", icon: itemIcon("equipment") },
"resonance-core": { id: "resonance-core", label: "Resonance Core", icon: itemIcon("resonance-core") },
artifact: { id: "artifact", label: "Artifact", icon: itemIcon("artifact") },
feature: { id: "feature", label: "Feature", icon: itemIcon("feature") },
},
abilities: {
agility: { id: "agility", label: "Agility" },
presence: { id: "presence", label: "Presence" },
strength: { id: "strength", label: "Strength" },
toughness: { id: "toughness", label: "Toughness" },
},
abilityOrder: ["agility", "presence", "strength", "toughness"],
conditions: {
bleeding: { id: "bleeding", label: "Bleeding", hasValue: true },
blinded: { id: "blinded", label: "Blinded" },
burning: { id: "burning", label: "Burning" },
fatigued: { id: "fatigued", label: "Fatigued", hasValue: true },
infected: { id: "infected", label: "Infected" },
poisoned: { id: "poisoned", label: "Poisoned" },
prone: { id: "prone", label: "Prone" },
restrained: { id: "restrained", label: "Restrained" },
starved: { id: "starved", label: "Starved" },
stunned: { id: "stunned", label: "Stunned", hasValue: true },
},
usageDice: ["d12", "d10", "d8", "d6", "d4", "depleted"],
usageDieChoices: dieChoiceLabels(["d12", "d10", "d8", "d6", "d4", "depleted"]),
armorDice: ["d12", "d10", "d8", "d6", "d4", "d2", "0"],
armorDieChoices: dieChoiceLabels(["d12", "d10", "d8", "d6", "d4", "d2", "0"]),
omenDice: ["d2", "d4", "d6", "d8"],
omenDieChoices: dieChoiceLabels(["d2", "d4", "d6", "d8"]),
weaponCategories: {
melee: "Melee",
ranged: "Ranged",
},
resonanceList: {
accelerate: "Accelerate",
blast: "Blast",
"breathe-water": "Breathe Water",
cauterize: "Cauterize",
"create-illusion": "Create Illusion",
distract: "Distract",
"eagle-eye": "Eagle Eye",
"empower-weapon": "Empower Weapon",
fireball: "Fireball",
hover: "Hover",
"influence-mind": "Influence Mind",
"knit-flesh": "Knit Flesh",
"light-construct": "Light Construct",
mirage: "Mirage",
"negate-injury": "Negate Injury",
paralyze: "Paralyze",
shield: "Shield",
shock: "Shock",
shroud: "Shroud",
"summon-mist": "Summon Mist",
},
starterWeapons: withItemIcons([
{ name: "Club", damage: "1d4", category: "melee", range: "Touch" },
{ name: "Dagger", damage: "1d4", category: "melee", range: "Touch" },
{ name: "Handaxe", damage: "1d6", category: "melee", range: "Touch" },
{ name: "Quarterstaff", damage: "1d4", category: "melee", range: "Touch" },
{ name: "Whip", damage: "1d4", category: "melee", range: "Near" },
{ name: "Shortbow", damage: "1d6", category: "ranged", range: "Near/Far" },
{ name: "Spear", damage: "1d6", category: "melee", range: "Near" },
{ name: "Longsword", damage: "1d8", category: "melee", range: "Touch" },
{ name: "Heavy Crossbow", damage: "1d10", category: "ranged", range: "Far" },
{ name: "Rapier", damage: "1d6", category: "melee", range: "Touch" },
{ name: "Halberd", damage: "1d10", category: "melee", range: "Near" },
{ name: "Maul", damage: "1d10", category: "melee", range: "Touch" },
]),
starterArmor: withItemIcons([
{ name: "Clothing (Average)", type: "armor", armorDie: "d2", penalty: 0 },
{ name: "Helm", type: "armor", armorDie: "d2", penalty: 0 },
{ name: "Medium Shield", type: "shield", armorDie: "d4", penalty: 0 },
{ name: "Gambeson", type: "armor", armorDie: "d4", penalty: 0 },
{ name: "Padded Leather", type: "armor", armorDie: "d4", penalty: 1 },
{ name: "Chain Shirt", type: "armor", armorDie: "d6", penalty: 1 },
{ name: "Half Plate", type: "armor", armorDie: "d8", penalty: 2 },
{ name: "Full Plate", type: "armor", armorDie: "d10", penalty: 2 },
]),
starterEquipment: withItemIcons([
{ name: "Rations", type: "equipment", subtype: "consumable", quantity: 1, usageDie: "d8", consumable: true },
{ name: "Waterskin", type: "equipment", subtype: "travel", quantity: 1, usageDie: "d8", consumable: true },
{ name: "Sack", type: "equipment", subtype: "container", quantity: 1, usageDie: "depleted", consumable: false },
{ name: "Backpack", type: "equipment", subtype: "container", quantity: 1, usageDie: "depleted", consumable: false },
{ name: "Rope", type: "equipment", subtype: "tool", quantity: 1, usageDie: "depleted", consumable: false },
{ name: "Medical Supplies", type: "equipment", subtype: "tool", quantity: 1, usageDie: "d6", consumable: true },
]),
equipmentSubtypes: {
gear: "Gear",
consumable: "Consumable",
travel: "Travel",
container: "Container",
tool: "Tool",
},
starterCores: withItemIcons([
{ name: "Accelerate Core", type: "resonance-core", resonationId: "accelerate", usageDie: "d6" },
{ name: "Blast Core", type: "resonance-core", resonationId: "blast", usageDie: "d6" },
{ name: "Cauterize Core", type: "resonance-core", resonationId: "cauterize", usageDie: "d6" },
{ name: "Empower Weapon Core", type: "resonance-core", resonationId: "empower-weapon", usageDie: "d6" },
]),
sampleArtifacts: withItemIcons([
{ name: "Shiver Lens", type: "artifact", artifactId: "shiver-lens", usageDie: "d6", description: "A cracked lens that reveals hidden heat signatures." },
{ name: "Processional Halo", type: "artifact", artifactId: "processional-halo", usageDie: "d6", description: "A floating crown of light that amplifies solemn commands." },
{ name: "Null Forge Spike", type: "artifact", artifactId: "null-forge-spike", usageDie: "d4", description: "A ritual spike used to jam malfunctioning relics into silence." },
{ name: "Lux Relay Idol", type: "artifact", artifactId: "lux-relay-idol", usageDie: "d6", description: "A palm-sized relay that stores and redirects ambient luminance." },
]),
artifactChoices: keyedNameChoices([
{ name: "Shiver Lens", artifactId: "shiver-lens" },
{ name: "Processional Halo", artifactId: "processional-halo" },
{ name: "Null Forge Spike", artifactId: "null-forge-spike" },
{ name: "Lux Relay Idol", artifactId: "lux-relay-idol" },
], "artifactId"),
sampleFeatures: withItemIcons([
{ name: "Akimbo Hit Priest", type: "feature", featureId: "akimbo-hit-priest", description: "Once per round after hitting with an attack, suffer Fatigued (1) to attack again." },
{ name: "Ambivalent Slouch", type: "feature", featureId: "ambivalent-slouch", description: "Gain +1 carrying capacity and reduce armor or shield Agility penalties by 1." },
{ name: "Avaricious Gubbingrifter", type: "feature", featureId: "avaricious-gubbingrifter", description: "Gain three random resonance cores and -1 DR to invoking resonations." },
{ name: "Backbiter", type: "feature", featureId: "backbiter", description: "When you win initiative, first-round attacks bypass 2d6 armor." },
{ name: "Chart Reading Maniac", type: "feature", featureId: "chart-reading-maniac", description: "Gain an extra exploration turn per day and reroll one exploration or ruin table per day." },
{ name: "Liturgicantal Blesswell", type: "feature", featureId: "liturgicantal-blesswell", description: "Restore d4 HP d4 times per full rest and repair one usage die by a step once per day." },
]),
featureChoices: keyedNameChoices([
{ name: "Akimbo Hit Priest", featureId: "akimbo-hit-priest" },
{ name: "Ambivalent Slouch", featureId: "ambivalent-slouch" },
{ name: "Avaricious Gubbingrifter", featureId: "avaricious-gubbingrifter" },
{ name: "Backbiter", featureId: "backbiter" },
{ name: "Chart Reading Maniac", featureId: "chart-reading-maniac" },
{ name: "Liturgicantal Blesswell", featureId: "liturgicantal-blesswell" },
], "featureId"),
companions: {
noble: {
name: "Beguiled Noble",
hp: 6,
morale: 9,
armorDie: "d4",
attackLabel: "Blade",
attackDamage: "1d6",
},
mercenary: {
name: "Dustland Mercenary",
hp: 8,
morale: 7,
armorDie: "d4",
attackLabel: "Cleaver",
attackDamage: "1d4+2",
},
pickpocket: {
name: "Scrapling Pickpocket",
hp: 3,
morale: 4,
armorDie: "0",
attackLabel: "Folding Knife",
attackDamage: "1d2",
},
cantor: {
name: "Silicon Cantor",
hp: 4,
morale: 6,
armorDie: "0",
attackLabel: "Book Thump",
attackDamage: "1",
},
},
tables: {
eucatastrophe: [
"Recover d4 HP.",
"Invoke the resonation again immediately.",
"Synchronize one additional artifact for the day.",
"Gain -1 DR to invoking resonations for five minutes.",
"Increase daily resonation uses by 2 for the day.",
"Gain +1 carrying capacity for the day.",
"Gain -4 DR to your next check within five minutes.",
"Gain +1 max HP for the day.",
"Recover one omen.",
"Borrow a random nearby feature or increase a random ability by 1 until end of day."
],
catastrophe: [
"You are reduced to 0 HP.",
"The resonation targets the wrong creature.",
"A random nearby artifact explodes.",
"The resonation is cast twice at random targets.",
"You become Restrained for five minutes.",
"You are knocked Prone and suffer Stunned (2).",
"Your worn armor is consumed.",
"Suffer Fatigued (1), doubling if repeated the same day.",
"Discard one omen.",
"All artifacts desynchronize and you cannot invoke resonations for the day."
],
triumphs: [
"Discover a useful resource.",
"Gain -3 DR to your next check within five minutes.",
"Discard a condition or recover d2 HP.",
"Grant a nearby ally -2 DR on their next check.",
"Ignore all penalties on your next check.",
"Discard Prone and Restrained, then move immediately.",
"Restore a weapon or armor usage die by one step.",
"Take an immediate action or find 3d10 coins.",
"Take an immediate action or gain -2 DR to your next check.",
"Move one point from one ability to another for one hour."
],
mishaps: [
"Downgrade involved equipment usage dice or suffer d4 damage bypassing armor.",
"A hostile random creature investigates.",
"Start an uncontrolled fire or suffer d4 damage bypassing armor.",
"Suffer Infected.",
"Suffer +1 DR on checks until you fully rest.",
"You cannot invoke resonations for d8 hours.",
"Suffer +3 DR on your next check this hour.",
"Suffer d6 damage bypassing armor, minimum 1 HP remaining.",
"Suffer Bleeding (1) in combat or d4 damage bypassing armor outside combat.",
"Roll twice on the mishaps table and take both results."
],
breakResults: {
1: "Unconscious for d4 rounds, then wake with d4 HP.",
2: "As above, and a random working limb becomes useless until treated.",
3: "As above, and without emergency treatment you die in d2 hours.",
4: "Immediate death."
}
}
}
export function localizeSystemConfig() {
if (localized) return
localized = true
localizeEntryLabels(SYSTEM.actorTypes, "MGNE.ActorTypes")
localizeEntryLabels(SYSTEM.itemTypes, "MGNE.ItemTypes")
localizeEntryLabels(SYSTEM.abilities, "MGNE.Abilities")
localizeEntryLabels(SYSTEM.conditions, "MGNE.Conditions")
localizeChoiceLabels(SYSTEM.weaponCategories, "MGNE.WeaponCategories")
localizeChoiceLabels(SYSTEM.resonanceList, "MGNE.Resonations")
localizeChoiceLabels(SYSTEM.equipmentSubtypes, "MGNE.EquipmentSubtypes")
SYSTEM.usageDieChoices.depleted = game.i18n.localize("MGNE.Common.Depleted")
}
+4
View File
@@ -0,0 +1,4 @@
export { default as MGNEActor } from "./actor.mjs"
export { default as MGNECombat } from "./combat.mjs"
export { default as MGNEItem } from "./item.mjs"
export { default as MGNERoll } from "./roll.mjs"
+386
View File
@@ -0,0 +1,386 @@
import MGNERoll from "./roll.mjs"
import { SYSTEM, SYSTEM_ID } from "../config/system.mjs"
const t = key => game.i18n.localize(key)
const f = (key, data = {}) => game.i18n.format(key, data)
const PENDING_DAMAGE_FLAG = "pendingDamageBonus"
const PENDING_DEFENSE_FLAG = "pendingDefenseFumble"
function formatActionLabel(baseLabel, actionLabel) {
const trimmedBase = `${baseLabel ?? ""}`.trim()
if (!trimmedBase) return actionLabel
return trimmedBase.toLowerCase().endsWith(actionLabel.toLowerCase()) ? trimmedBase : `${trimmedBase} ${actionLabel}`
}
function normalizeGenericActionLabel(baseLabel, genericLabel) {
const trimmedBase = `${baseLabel ?? ""}`.trim()
if (!trimmedBase) return genericLabel
const normalizedWords = trimmedBase
.split(/\s+/)
.filter(Boolean)
.map(word => word.toLowerCase())
return normalizedWords.every(word => word === genericLabel.toLowerCase()) ? genericLabel : trimmedBase
}
function maxDieByRank(items, property) {
const rank = { d12: 0, d10: 1, d8: 2, d6: 3, d4: 4, d2: 5, 0: 6, depleted: 7 }
return [...items].sort((left, right) => (rank[left.system[property]] ?? 99) - (rank[right.system[property]] ?? 99))[0] ?? null
}
export default class MGNEActor extends Actor {
async setPendingDamageBonus(contextId, multiplier = 2) {
await this.setFlag(SYSTEM_ID, PENDING_DAMAGE_FLAG, { contextId, multiplier })
}
async consumePendingDamageBonus(contextId) {
const bonus = this.getFlag(SYSTEM_ID, PENDING_DAMAGE_FLAG)
if (!bonus || bonus.contextId !== contextId) return null
await this.unsetFlag(SYSTEM_ID, PENDING_DAMAGE_FLAG)
return bonus
}
async setPendingDefenseFumble() {
await this.setFlag(SYSTEM_ID, PENDING_DEFENSE_FLAG, { active: true })
}
async consumePendingDefenseFumble() {
const pending = this.getFlag(SYSTEM_ID, PENDING_DEFENSE_FLAG)
if (!pending?.active) return null
await this.unsetFlag(SYSTEM_ID, PENDING_DEFENSE_FLAG)
return pending
}
getEquippedArmorItems() {
return this.items.filter(item => ["armor", "shield"].includes(item.type) && item.system.equipped && !item.system.broken && item.system.armorDie !== "0")
}
getArmorRollFormula() {
const dice = this.getEquippedArmorItems().map(item => `1${item.system.armorDie}`)
return dice.length ? dice.join(" + ") : "0"
}
async degradeArmorStep(steps = 1) {
const item = maxDieByRank(this.getEquippedArmorItems(), "armorDie")
if (!item) return null
const nextDie = MGNERoll.stepDownDie(item.system.armorDie, steps, SYSTEM.armorDice)
const updates = { "system.armorDie": nextDie }
if (nextDie === "0") {
updates["system.broken"] = true
updates["system.equipped"] = false
}
await item.update(updates)
return { item, nextDie }
}
async rollAbility(abilityId, options = {}) {
return MGNERoll.promptCheck({
actor: this,
abilityId,
label: options.label ?? f("MGNE.Roll.CheckLabel", { ability: SYSTEM.abilities[abilityId]?.label ?? abilityId }),
baseDR: options.baseDR ?? 12,
rollType: options.rollType ?? "check",
item: options.item ?? null,
})
}
async rollDefense() {
const result = await this.rollAbility("agility", { label: t("MGNE.Roll.DefenseCheck"), rollType: "defense" })
if (result?.fumble) await this.setPendingDefenseFumble()
return result
}
async rollWeapon(itemId) {
const item = this.items.get(itemId)
if (!item) return null
if (item.system.broken) {
ui.notifications.warn(f("MGNE.Notification.ItemBroken", { item: item.name }))
return null
}
const abilityId = item.system.category === "ranged" ? "presence" : "strength"
const result = await this.rollAbility(abilityId, {
label: f("MGNE.Roll.ItemAttackLabel", { item: item.name }),
rollType: "attack",
item,
})
if (!result) return null
if (result.fumble) await item.update({ "system.broken": true })
if (result.critical) {
await this.setPendingDamageBonus(item.id)
}
return result
}
async rollDamage(itemId) {
const item = this.items.get(itemId)
if (!item) return null
return MGNERoll.rollDamage({
actor: this,
item,
targetActor: MGNERoll.getFirstTargetActor(),
})
}
async rollProfileAttack() {
const attackBaseLabel = normalizeGenericActionLabel(this.system.attack?.label ?? t("MGNE.Common.Attack"), t("MGNE.Common.Attack"))
const attackLabel = formatActionLabel(attackBaseLabel, t("MGNE.Common.Attack"))
const result = await this.rollAbility("strength", {
label: attackLabel,
rollType: "attack",
})
if (result?.critical) {
await this.setPendingDamageBonus("profile-attack")
}
return result
}
async rollProfileDamage() {
const attackBaseLabel = normalizeGenericActionLabel(this.system.attack?.label ?? t("MGNE.Common.Attack"), t("MGNE.Common.Attack"))
const damageLabel = attackBaseLabel === t("MGNE.Common.Attack")
? t("MGNE.Common.Damage")
: formatActionLabel(attackBaseLabel, t("MGNE.Common.Damage"))
return MGNERoll.rollFlatDamage({
actor: this,
label: damageLabel,
formula: this.system.attack?.damage ?? "1",
targetActor: MGNERoll.getFirstTargetActor(),
})
}
async rollResonancePerDay({ resetUsed = true, apply = true } = {}) {
const resonanceRoll = await (new Roll("1d4")).evaluate()
const presence = this.system.abilities?.presence?.value ?? 0
const max = Math.max(1, presence + resonanceRoll.total)
const updates = { "system.resonance.max": max }
if (resetUsed) updates["system.resonance.used"] = 0
if (apply) await this.update(updates)
return { resonanceRoll, max }
}
async rollResonation(itemId) {
const item = this.items.get(itemId)
if (!item || item.type !== "resonance-core") return null
if (item.system.burnedOut || item.system.usageDie === "depleted") {
ui.notifications.warn(f("MGNE.Notification.ItemBurnedOut", { item: item.name }))
return null
}
if ((this.system.resonance?.used ?? 0) >= (this.system.resonance?.max ?? 0)) {
ui.notifications.warn(f("MGNE.Notification.ResonancePerDayReached", { actor: this.name }))
return null
}
const result = await this.rollAbility("presence", {
label: f("MGNE.Roll.InvocationLabel", { item: item.name }),
rollType: "resonance",
item,
})
if (!result) return null
await this.update({ "system.resonance.used": (this.system.resonance.used ?? 0) + 1 })
if (!result.success) {
await this.applyDamage(1, { sourceItem: item, ignoreArmor: true, chat: false })
}
return result
}
async rollMorale() {
return MGNERoll.rollMorale(this)
}
async toggleItemEquipped(itemId) {
const item = this.items.get(itemId)
if (!item) return null
if (item.system.broken) {
ui.notifications.warn(f("MGNE.Notification.ItemBroken", { item: item.name }))
return null
}
return item.update({ "system.equipped": !item.system.equipped })
}
async rollUsage(itemId) {
const item = this.items.get(itemId)
if (!item) return null
return item.rollUsage()
}
async quickRest() {
const roll = await (new Roll("1d4")).evaluate()
const hp = this.system.hp?.value ?? 0
const hpMax = this.system.hp?.max ?? hp
const healed = Math.max(0, Math.min(hpMax, hp + roll.total) - hp)
const newHp = Math.min(hpMax, hp + roll.total)
await this.update({ "system.hp.value": newHp })
await MGNERoll.createRestCard({
actor: this,
label: f("MGNE.Roll.QuickRestLabel", { actor: this.name }),
subtitle: t("MGNE.Character.QuickRestHelp"),
roll,
outcome: f("MGNE.Roll.RestoredHP", { amount: healed }),
specialText: f("MGNE.Roll.HPNowMax", { hp: newHp, max: hpMax }),
})
return { roll, healed, newHp }
}
async fullRest() {
const hp = this.system.hp?.value ?? 0
const hpMax = this.system.hp?.max ?? hp
const healRoll = await (new Roll("1d6")).evaluate()
const omenDie = this.system.omens?.die ?? "d2"
const omenRoll = await (new Roll(`1${omenDie}`)).evaluate()
const healed = Math.max(0, Math.min(hpMax, hp + healRoll.total) - hp)
const newHp = Math.min(hpMax, hp + healRoll.total)
await this.update({
"system.hp.value": newHp,
"system.omens.current": omenRoll.total,
})
await MGNERoll.createRestCard({
actor: this,
label: f("MGNE.Roll.FullRestLabel", { actor: this.name }),
subtitle: t("MGNE.Character.FullRestHelp"),
roll: healRoll,
outcome: f("MGNE.Roll.RestoredHP", { amount: healed }),
specialText: `${f("MGNE.Roll.HPNowMax", { hp: newHp, max: hpMax })} ${f("MGNE.Roll.OmensReset", {
omens: omenRoll.total,
die: omenDie.toUpperCase(),
roll: omenRoll.total,
})}`,
})
return { healRoll, omenRoll, healed, newHp, omens: omenRoll.total }
}
async syncArtifact(itemId) {
const item = this.items.get(itemId)
if (!item || item.type !== "artifact") return null
if (item.system.synchronized) {
return item.update({
"system.synchronized": false,
"system.synchronizedTo": "",
})
}
const syncLimit = Math.max(0, this.system.syncLimit ?? this.system.abilities?.toughness?.value ?? 0)
const used = this.system.artifactSync?.used ?? 0
if (used >= syncLimit) {
ui.notifications.warn(f("MGNE.Notification.CannotSyncMore", { actor: this.name }))
return null
}
await this.update({ "system.artifactSync.used": used + 1 })
return item.update({
"system.synchronized": true,
"system.synchronizedTo": this.id,
})
}
async resetDaily() {
const omenDie = this.system.omens?.die ?? "d2"
const omenRoll = await (new Roll(`1${omenDie}`)).evaluate()
const { max: resonanceMax } = await this.rollResonancePerDay({ resetUsed: false, apply: false })
await this.update({
"system.omens.current": omenRoll.total,
"system.resonance.max": resonanceMax,
"system.resonance.used": 0,
"system.artifactSync.used": 0,
"system.survival.salvationUsed": false,
})
const syncedArtifacts = this.items.filter(item => item.type === "artifact" && item.system.synchronized)
for (const artifact of syncedArtifacts) {
await artifact.update({
"system.synchronized": false,
"system.synchronizedTo": "",
})
}
}
async applyDamage(amount, { sourceActor = null, sourceItem = null, ignoreArmor = false, critical = false, chat = true } = {}) {
const defenseFumble = await this.consumePendingDefenseFumble()
const incomingDamage = defenseFumble ? amount * 2 : amount
const hp = this.system.hp?.value ?? 0
let armorRoll = null
let absorbed = 0
if (!ignoreArmor) {
const formula = this.getArmorRollFormula()
if (formula !== "0") {
armorRoll = await (new Roll(formula)).evaluate()
absorbed = armorRoll.total
}
}
let appliedDamage = Math.max(0, incomingDamage - absorbed)
let nextHp = hp - appliedDamage
let breakText = ""
let defenseFumbleText = ""
let criticalArmorText = ""
if (this.type === "character" && hp > 0 && nextHp < 0 && !(this.system.survival?.salvationUsed)) {
nextHp = 0
appliedDamage = hp
await this.update({ "system.survival.salvationUsed": true })
}
if (nextHp <= 0 && hp > 0) {
const breakRoll = await (new Roll("1d4")).evaluate()
breakText = SYSTEM.tables.breakResults[breakRoll.total] ?? ""
}
await this.update({ "system.hp.value": nextHp })
if (defenseFumble) {
const degraded = await this.degradeArmorStep()
defenseFumbleText = degraded
? f("MGNE.Roll.DefenseFumbleApplied", { item: degraded.item.name, die: degraded.nextDie.toUpperCase() })
: t("MGNE.Roll.DefenseFumbleNoArmor")
}
if (critical) {
const degraded = await this.degradeArmorStep()
criticalArmorText = degraded
? f("MGNE.Roll.ArmorDegradedCritical", { item: degraded.item.name, die: degraded.nextDie.toUpperCase() })
: t("MGNE.Roll.ArmorNothingToDegrade")
}
if (chat) {
await MGNERoll.applyDamageCard({
actor: this,
sourceActor,
sourceItem,
amount: incomingDamage,
armorRoll,
appliedDamage,
newHp: nextHp,
breakText,
defenseFumbleText,
criticalArmorText,
})
}
return {
amount: incomingDamage,
absorbed,
appliedDamage,
newHp: nextHp,
breakText,
defenseFumbleText,
criticalArmorText,
summary: f("MGNE.Chat.DamageSummary", { absorbed, appliedDamage, hp: nextHp }),
}
}
}
+139
View File
@@ -0,0 +1,139 @@
import MGNERoll from "./roll.mjs"
import { SYSTEM_ID } from "../config/system.mjs"
const t = key => game.i18n.localize(key)
const f = (key, data = {}) => game.i18n.format(key, data)
const INITIATIVE_FLAG = "initiativeState"
const SIDE_BONUS = 20
const FLEE_BASE_TARGET = 5
const FLEE_MIN_TARGET = 2
function isPlayerSide(combatant) {
return Boolean(combatant?.actor?.hasPlayerOwner)
}
function fleeDialogContent() {
const options = Array.from({ length: FLEE_BASE_TARGET - FLEE_MIN_TARGET + 1 }, (_, index) => {
const value = index
return `<option value="${value}">${value}</option>`
}).join("")
return `
<form class="mgne-flee-dialog">
<div class="form-group">
<label>${t("MGNE.Combat.StudyActions")}</label>
<select name="studyActions">${options}</select>
<p class="notes">${t("MGNE.Combat.StudyHelp")}</p>
</div>
</form>
`
}
export default class MGNECombat extends Combat {
getFleeCombatant(combatantId = null) {
if (combatantId) return this.combatants.get(combatantId) ?? null
const controlledCombatant = canvas.tokens?.controlled?.[0]?.combatant ?? null
return this.combatant ?? controlledCombatant
}
async getInitiativeState() {
const existing = this.getFlag(SYSTEM_ID, INITIATIVE_FLAG)
if (existing && Number.isFinite(existing.sideRoll)) return existing
const sideRoll = await (new Roll("1d6")).evaluate()
const initiativeState = {
sideRoll: sideRoll.total,
playersActFirst: sideRoll.total >= 4,
}
await this.setFlag(SYSTEM_ID, INITIATIVE_FLAG, initiativeState)
await ChatMessage.create({
speaker: ChatMessage.getSpeaker(),
content: `<p>${f("MGNE.Initiative.SideRoll", { roll: initiativeState.sideRoll })}</p><p>${t(initiativeState.playersActFirst ? "MGNE.Initiative.PlayersFirst" : "MGNE.Initiative.EnemiesFirst")}</p><p>${t("MGNE.Initiative.TieBreak")}</p>`,
})
return initiativeState
}
async rollInitiative(ids, { updateTurn = true } = {}) {
const combatantIds = typeof ids === "string" ? [ids] : Array.from(ids ?? [])
if (!combatantIds.length) return this
const currentCombatantId = this.combatant?.id ?? null
const initiativeState = await this.getInitiativeState()
const updates = []
for (const id of combatantIds) {
const combatant = this.combatants.get(id)
if (!combatant) continue
const agility = combatant.actor?.system?.abilities?.agility?.value ?? 0
const roll = await (new Roll("1d6 + @agility", { agility })).evaluate()
const actsWithPriority = isPlayerSide(combatant) === initiativeState.playersActFirst
updates.push({
_id: id,
initiative: roll.total + (actsWithPriority ? SIDE_BONUS : 0),
})
}
if (updates.length) await this.updateEmbeddedDocuments("Combatant", updates)
if (updateTurn && currentCombatantId) {
const turn = this.turns.findIndex(combatant => combatant.id === currentCombatantId)
if (turn >= 0) await this.update({ turn })
}
return this
}
async rollFlee({ combatantId = null } = {}) {
const combatant = this.getFleeCombatant(combatantId)
if (!combatant?.actor) {
ui.notifications.warn(t("MGNE.Combat.FleeNoCombatant"))
return null
}
const dialogData = await foundry.applications.api.DialogV2.wait({
window: { title: t("MGNE.Combat.Flee") },
classes: ["mgne", "roll-dialog"],
content: fleeDialogContent(),
buttons: [{
label: t("MGNE.Combat.Flee"),
icon: "fa-solid fa-person-running",
callback: (_event, button) => {
const value = Number.parseInt(button.form?.elements.studyActions?.value ?? "0", 10)
return { studyActions: Math.max(0, Math.min(FLEE_BASE_TARGET - FLEE_MIN_TARGET, value || 0)) }
},
}],
rejectClose: false,
})
if (!dialogData) return null
const studyActions = dialogData.studyActions ?? 0
const target = Math.max(FLEE_MIN_TARGET, FLEE_BASE_TARGET - studyActions)
const roll = await (new Roll("1d6")).evaluate()
const escaped = roll.total >= target
if (escaped) {
await combatant.delete()
} else {
await combatant.actor.update({ "system.hp.value": 0 })
await combatant.update({ defeated: true })
}
await MGNERoll.createActionCard({
mode: "flee",
actor: combatant.actor,
label: f("MGNE.Roll.FleeLabel", { actor: combatant.name }),
subtitle: f("MGNE.Roll.FleeSubtitle", { target }),
roll,
outcome: t(escaped ? "MGNE.Roll.FleeEscaped" : "MGNE.Roll.FleeKilled"),
specialText: studyActions > 0
? f("MGNE.Roll.FleeStudyActions", { count: studyActions })
: t("MGNE.Roll.FleeNoStudyActions"),
})
return { combatant, roll, escaped, target, studyActions }
}
}
+17
View File
@@ -0,0 +1,17 @@
import MGNERoll from "./roll.mjs"
import { SYSTEM } from "../config/system.mjs"
export default class MGNEItem extends Item {
prepareBaseData() {
super.prepareBaseData()
const fallbackIcon = SYSTEM.itemTypes[this.type]?.icon
if (!fallbackIcon) return
if (!this._source.img || this._source.img === "icons/svg/item-bag.svg") {
this.updateSource({ img: fallbackIcon })
}
}
async rollUsage() {
return MGNERoll.rollUsage(this)
}
}
+340
View File
@@ -0,0 +1,340 @@
import { SYSTEM, SYSTEM_ID } from "../config/system.mjs"
const t = key => game.i18n.localize(key)
const f = (key, data = {}) => game.i18n.format(key, data)
function joinParts(parts) {
return parts.filter(Boolean).join(" ")
}
function getChatModeLabel(mode) {
return game.i18n.localize(`MGNE.Chat.Mode.${mode}`) || mode
}
function pickRandom(list = []) {
if (!list.length) return ""
return list[Math.floor(Math.random() * list.length)]
}
function stepDownDie(die, steps = 1, track = SYSTEM.usageDice) {
let current = die
for (let step = 0; step < steps; step += 1) {
const index = track.indexOf(current)
if (index === -1) return current
current = track[Math.min(index + 1, track.length - 1)]
}
return current
}
function getFirstTargetActor() {
const target = game.user?.targets ? Array.from(game.user.targets)[0] : null
return target?.actor ?? null
}
function serializeForm(form) {
return Array.from(form.elements).reduce((acc, element) => {
if (!element.name) return acc
acc[element.name] = element.type === "checkbox" ? element.checked : element.value
return acc
}, {})
}
function numericOptions(min, max, current = null) {
const resolvedMin = Math.min(min, Number.isFinite(current) ? current : min)
const resolvedMax = Math.max(max, Number.isFinite(current) ? current : max)
return Array.from({ length: (resolvedMax - resolvedMin) + 1 }, (_, index) => {
const value = resolvedMin + index
return { value, label: String(value) }
})
}
async function renderCard(context) {
const outcomeClass = `${context.outcome ?? ""}`
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-|-$/g, "")
const eyebrow = context.eyebrow ?? getChatModeLabel(context.mode ?? "generic")
const normalizedEyebrow = `${eyebrow}`.trim().toLowerCase()
const normalizedLabel = `${context.label ?? ""}`.trim().toLowerCase()
return foundry.applications.handlebars.renderTemplate(`systems/${SYSTEM_ID}/templates/chat-message.hbs`, {
...context,
modeClass: context.mode ?? "generic",
eyebrow: "",
outcomeClass,
})
}
export default class MGNERoll {
static async promptCheck({ actor, abilityId, label, baseDR = 12, rollType = "check", item = null }) {
const abilityLabel = SYSTEM.abilities[abilityId]?.label ?? abilityId
const content = await foundry.applications.handlebars.renderTemplate(`systems/${SYSTEM_ID}/templates/roll-dialog.hbs`, {
actorName: actor.name,
label,
abilityLabel,
baseDR,
drOptions: numericOptions(6, 20, baseDR),
modifierOptions: numericOptions(-6, 6, 0),
omens: actor.system.omens?.current ?? 0,
rollType,
})
const dialogData = await foundry.applications.api.DialogV2.wait({
window: { title: label },
classes: ["mgne", "roll-dialog"],
content,
buttons: [
{
label: t("MGNE.Common.Roll"),
icon: "fa-solid fa-dice-d20",
callback: (_event, button) => serializeForm(button.form),
},
],
rejectClose: false,
})
if (!dialogData) return null
const modifier = Number.parseInt(dialogData.modifier ?? 0, 10) || 0
const spendOmen = Boolean(dialogData.spendOmen)
const dr = (Number.parseInt(dialogData.dr ?? baseDR, 10) || baseDR) - (spendOmen ? 4 : 0)
const abilityValue = actor.system.abilities?.[abilityId]?.value ?? 0
const sign = modifier >= 0 ? "+" : "-"
const formula = modifier === 0 ? `1d20 + ${abilityValue}` : `1d20 + ${abilityValue} ${sign} ${Math.abs(modifier)}`
const roll = await (new Roll(formula)).evaluate()
const natural = roll.dice?.[0]?.results?.[0]?.result ?? roll.total
if (spendOmen && (actor.system.omens?.current ?? 0) > 0) {
await actor.update({ "system.omens.current": Math.max(0, actor.system.omens.current - 1) })
}
const critical = natural === 20
const fumble = natural === 1
const success = critical || (!fumble && roll.total >= dr)
const outcome = critical ? t("MGNE.Roll.OutcomeCriticalSuccess") : fumble ? t("MGNE.Roll.OutcomeFumble") : success ? t("MGNE.Roll.OutcomeSuccess") : t("MGNE.Roll.OutcomeFailure")
let specialText = ""
if (critical && rollType === "resonance") specialText = pickRandom(SYSTEM.tables.eucatastrophe)
else if (fumble && rollType === "resonance") specialText = pickRandom(SYSTEM.tables.catastrophe)
else if (critical) {
specialText = rollType === "attack"
? t("MGNE.Roll.CriticalAttack")
: rollType === "defense"
? t("MGNE.Roll.DefenseCritical")
: pickRandom(SYSTEM.tables.triumphs)
} else if (fumble) {
specialText = rollType === "attack" ? t("MGNE.Roll.AttackFumble") : rollType === "defense" ? t("MGNE.Roll.DefenseFumble") : pickRandom(SYSTEM.tables.mishaps)
}
const showDamageButton = rollType === "attack" && (success || critical) && !!item
const contentHtml = await renderCard({
mode: "check",
actorName: actor.name,
actorImg: actor.img,
label,
subtitle: f("MGNE.Roll.CheckSubtitle", { ability: abilityLabel, dr }),
formula: roll.formula,
total: roll.total,
outcome,
specialText,
showDamageButton,
damageActorId: showDamageButton ? actor.id : null,
damageItemId: showDamageButton ? item.id : null,
damageFormula: showDamageButton ? (item.system.damage || "1") : null,
damageCritical: showDamageButton && critical,
})
await ChatMessage.create({
speaker: ChatMessage.getSpeaker({ actor }),
rolls: [roll],
content: contentHtml,
})
return { roll, success, critical, fumble, outcome, specialText, dr, abilityId, item }
}
static async rollMorale(actor) {
const target = actor.system.morale ?? 7
const roll = await (new Roll("2d6")).evaluate()
const broken = roll.total >= target
const contentHtml = await renderCard({
mode: "morale",
actorName: actor.name,
actorImg: actor.img,
label: t("MGNE.Roll.MoraleCheck"),
subtitle: f("MGNE.Roll.TargetSubtitle", { target }),
formula: roll.formula,
total: roll.total,
outcome: broken ? t("MGNE.Roll.OutcomeBroken") : t("MGNE.Roll.OutcomeSteady"),
specialText: broken ? t("MGNE.Roll.MoraleBrokenText") : "",
})
await ChatMessage.create({
speaker: ChatMessage.getSpeaker({ actor }),
rolls: [roll],
content: contentHtml,
})
return { roll, broken }
}
static async rollDamage({ actor, item }) {
const damageBonus = await actor.consumePendingDamageBonus(item.id)
const multiplier = damageBonus?.multiplier ?? 1
const baseFormula = item.system.damage || "1"
const formula = multiplier > 1 ? `${multiplier} * (${baseFormula})` : baseFormula
const roll = await (new Roll(formula)).evaluate()
const isCritical = multiplier > 1
const contentHtml = await renderCard({
mode: "damage",
actorName: actor.name,
actorImg: actor.img,
label: `${item.name} Damage`,
subtitle: null,
formula: roll.formula,
total: roll.total,
outcome: t("MGNE.Roll.OutcomeRolled"),
specialText: isCritical ? t("MGNE.Roll.CriticalDamageApplied") : "",
showApplyButton: true,
damageTotal: roll.total,
damageCritical: isCritical,
})
await ChatMessage.create({
speaker: ChatMessage.getSpeaker({ actor }),
rolls: [roll],
content: contentHtml,
})
return { roll }
}
static async rollFlatDamage({ actor, label, formula }) {
const damageBonus = await actor.consumePendingDamageBonus("profile-attack")
const multiplier = damageBonus?.multiplier ?? 1
const baseFormula = formula || "1"
const resolvedFormula = multiplier > 1 ? `${multiplier} * (${baseFormula})` : baseFormula
const roll = await (new Roll(resolvedFormula)).evaluate()
const isCritical = multiplier > 1
const contentHtml = await renderCard({
mode: "damage",
actorName: actor.name,
actorImg: actor.img,
label,
subtitle: null,
formula: roll.formula,
total: roll.total,
outcome: t("MGNE.Roll.OutcomeRolled"),
specialText: isCritical ? t("MGNE.Roll.CriticalDamageApplied") : "",
showApplyButton: true,
damageTotal: roll.total,
damageCritical: isCritical,
})
await ChatMessage.create({
speaker: ChatMessage.getSpeaker({ actor }),
rolls: [roll],
content: contentHtml,
})
return { roll }
}
static async rollUsage(item) {
const currentDie = item.system.usageDie
if (!currentDie || currentDie === "depleted") {
ui.notifications.warn(f("MGNE.Notification.ItemDepleted", { item: item.name }))
return null
}
const roll = await (new Roll(`1${currentDie}`)).evaluate()
const depleted = roll.total <= 2
const nextDie = depleted ? stepDownDie(currentDie) : currentDie
const updates = { "system.usageDie": nextDie }
if (item.type === "resonance-core" && nextDie === "depleted") updates["system.burnedOut"] = true
await item.update(updates)
const contentHtml = await renderCard({
mode: "usage",
actorName: item.parent?.name ?? item.name,
actorImg: item.img,
label: f("MGNE.Roll.ItemUsageLabel", { item: item.name }),
subtitle: f("MGNE.Roll.CurrentDie", { die: currentDie.toUpperCase() }),
formula: roll.formula,
total: roll.total,
outcome: depleted ? f("MGNE.Roll.DowngradedTo", { die: nextDie.toUpperCase() }) : t("MGNE.Roll.NoChange"),
specialText: depleted && nextDie === "depleted" ? t("MGNE.Roll.ItemNowDepleted") : "",
})
await ChatMessage.create({
speaker: ChatMessage.getSpeaker({ actor: item.parent ?? null }),
rolls: [roll],
content: contentHtml,
})
return { roll, depleted, nextDie }
}
static async applyDamageCard({ actor, sourceActor = null, sourceItem = null, amount, armorRoll = null, appliedDamage, newHp, breakText = "", defenseFumbleText = "", criticalArmorText = "" }) {
const contentHtml = await renderCard({
mode: "apply-damage",
actorName: actor.name,
actorImg: actor.img,
label: f("MGNE.Roll.TakesDamageLabel", { actor: actor.name }),
subtitle: sourceItem
? (sourceActor
? f("MGNE.Roll.DamageSourceWithActor", { item: sourceItem.name, actor: sourceActor.name })
: f("MGNE.Roll.DamageSourceItem", { item: sourceItem.name }))
: t("MGNE.Roll.DirectDamage"),
formula: armorRoll?.formula ?? "",
total: amount,
outcome: f("MGNE.Roll.HPNow", { hp: newHp }),
specialText: joinParts([
defenseFumbleText,
armorRoll ? f("MGNE.Roll.ArmorAbsorbed", { amount: armorRoll.total }) : "",
f("MGNE.Roll.AppliedDamageText", { amount: appliedDamage }),
criticalArmorText,
breakText ? f("MGNE.Roll.BreakText", { text: breakText }) : "",
]),
})
await ChatMessage.create({
speaker: ChatMessage.getSpeaker({ actor }),
rolls: armorRoll ? [armorRoll] : [],
content: contentHtml,
})
}
static async createRestCard({ actor, label, subtitle, roll, outcome, specialText = "" }) {
return this.createActionCard({ mode: "rest", actor, label, subtitle, roll, outcome, specialText })
}
static async createActionCard({ mode = "action", actor = null, label, subtitle = "", roll, outcome, specialText = "" }) {
const contentHtml = await renderCard({
mode,
actorName: actor?.name ?? "",
actorImg: actor?.img ?? "",
label,
subtitle,
formula: roll.formula,
total: roll.total,
outcome,
specialText,
})
await ChatMessage.create({
speaker: ChatMessage.getSpeaker({ actor }),
rolls: [roll],
content: contentHtml,
})
}
static getFirstTargetActor() {
return getFirstTargetActor()
}
static stepDownDie(die, steps = 1, track = SYSTEM.usageDice) {
return stepDownDie(die, steps, track)
}
}
+10
View File
@@ -0,0 +1,10 @@
export { default as MGNECharacter } from "./character.mjs"
export { default as MGNECreature } from "./creature.mjs"
export { default as MGNECompanion } from "./companion.mjs"
export { default as MGNEWeapon } from "./weapon.mjs"
export { default as MGNEArmor } from "./armor.mjs"
export { default as MGNEShield } from "./shield.mjs"
export { default as MGNEEquipment } from "./equipment.mjs"
export { default as MGNEResonanceCore } from "./resonance-core.mjs"
export { default as MGNEArtifact } from "./artifact.mjs"
export { default as MGNEFeature } from "./feature.mjs"

Some files were not shown because too many files have changed in this diff Show More