Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8470800ee3 | ||
|
dd42e5487c | ||
|
bc71b3d2cc | ||
|
54798bd300 | ||
|
8da6565aae | ||
|
ada2dc40c5 | ||
|
ebed4c3622 | ||
|
bcf1d8827d | ||
|
c9acf8b9ff | ||
|
34c4f8ed5c | ||
|
78ad7fc3ac | ||
|
9f6e16e741 | ||
|
05626878d3 | ||
|
84067f07dc |
401
.gitignore
vendored
401
.gitignore
vendored
@ -1,7 +1,398 @@
|
|||||||
# IDE
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
.idea/
|
## files generated by popular Visual Studio add-ons.
|
||||||
.vs/
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||||
|
|
||||||
# Node Modules
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Ww][Ii][Nn]32/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUnit
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
nunit-*.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# ASP.NET Scaffolding
|
||||||
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.tlog
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
|
coverage*.json
|
||||||
|
coverage*.xml
|
||||||
|
coverage*.info
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# NuGet Symbol Packages
|
||||||
|
*.snupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- [Bb]ackup.rdl
|
||||||
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
node_modules/
|
node_modules/
|
||||||
package-lock.json
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||||
|
*.vbp
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||||
|
*.dsw
|
||||||
|
*.dsp
|
||||||
|
|
||||||
|
# Visual Studio 6 technical files
|
||||||
|
*.ncb
|
||||||
|
*.aps
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# Visual Studio History (VSHistory) files
|
||||||
|
.vshistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
|
.ionide/
|
||||||
|
|
||||||
|
# Fody - auto-generated XML schema
|
||||||
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# VS Code files for those working on multiple tools
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# Windows Installer files from build outputs
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
*.sln.iml
|
||||||
|
146
.gitlab-ci.yml
146
.gitlab-ci.yml
@ -1,84 +1,94 @@
|
|||||||
image: ubuntu:latest
|
|
||||||
|
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- build
|
- compile
|
||||||
- release
|
- release
|
||||||
|
|
||||||
variables:
|
# Compile Job (runs on every commit)
|
||||||
MANIFEST: "system.json"
|
compile:
|
||||||
ZIPFILE: "kidsonbrooms.zip"
|
stage: compile
|
||||||
PACKAGE_REGISTRY_URL: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${CI_PROJECT_NAME}/${CI_COMMIT_TAG}"
|
image: node:14
|
||||||
MANIFEST_RELEASE_URL: "${PACKAGE_REGISTRY_URL}/${MANIFEST}"
|
|
||||||
ZIPFILE_RELEASE_URL: "${PACKAGE_REGISTRY_URL}/${ZIPFILE}"
|
|
||||||
MANIFEST_PERMALINK_URL: "https://gitlab.com/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}/-/releases/${CI_COMMIT_TAG}/downloads/${MANIFEST}"
|
|
||||||
ZIPFILE_PERMALINK_URL: "https://gitlab.com/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}/-/releases/${CI_COMMIT_TAG}/downloads/${ZIPFILE}"
|
|
||||||
dry_run: true
|
|
||||||
|
|
||||||
|
|
||||||
# Build job
|
|
||||||
build:
|
|
||||||
stage: build
|
|
||||||
before_script:
|
|
||||||
# Install Node.js v21.x manually
|
|
||||||
- apt-get update && apt-get install -y curl
|
|
||||||
- curl -fsSL https://deb.nodesource.com/setup_21.x | bash -
|
|
||||||
- apt-get install -y nodejs
|
|
||||||
- node -v # Verify the correct Node.js version
|
|
||||||
# Install Gulp globally
|
|
||||||
- npm install --global gulp-cli
|
|
||||||
- gulp --version # Verify Gulp is installed
|
|
||||||
script:
|
script:
|
||||||
|
- npm install --global gulp-cli
|
||||||
- npm install
|
- npm install
|
||||||
- gulp build
|
- gulp compile
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- kidsonbrooms.zip
|
|
||||||
- system.json
|
|
||||||
- packs/
|
|
||||||
only:
|
only:
|
||||||
- branches
|
- branches
|
||||||
|
|
||||||
# Release job
|
# Release Job (manually triggered with version)
|
||||||
release:
|
release:
|
||||||
stage: release
|
stage: release
|
||||||
rules:
|
image: node:14
|
||||||
- if: $CI_COMMIT_TAG
|
|
||||||
variables:
|
|
||||||
dry_run: "false"
|
|
||||||
before_script:
|
before_script:
|
||||||
# Install Node.js v21.x manually
|
- apt-get update && apt-get install -y curl jq
|
||||||
- apt-get update && apt-get install -y curl
|
|
||||||
- curl -fsSL https://deb.nodesource.com/setup_21.x | bash -
|
|
||||||
- apt-get install -y nodejs
|
|
||||||
- node -v # Verify the correct Node.js version
|
|
||||||
# Install Gulp globally
|
|
||||||
- npm install --global gulp-cli
|
- npm install --global gulp-cli
|
||||||
- gulp --version # Verify Gulp is installed
|
|
||||||
script:
|
|
||||||
- npm install
|
- npm install
|
||||||
|
# Set up SSH agent and add private key for pushing to protected branch
|
||||||
|
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
|
||||||
|
- eval $(ssh-agent -s)
|
||||||
|
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
|
||||||
|
- mkdir -p ~/.ssh
|
||||||
|
- chmod 700 ~/.ssh
|
||||||
|
- ssh-keyscan gitlab.com >> ~/.ssh/known_hosts
|
||||||
|
script:
|
||||||
|
# Check if VERSION is provided
|
||||||
|
- if [ -z "$VERSION" ]; then echo "Error: VERSION variable is required." && exit 1; fi
|
||||||
|
|
||||||
|
# Run Gulp release task (includes zipping)
|
||||||
- gulp release
|
- gulp release
|
||||||
|
|
||||||
# Create GitLab release
|
# Create a release on GitLab
|
||||||
create-release:
|
- export RELEASE_RESPONSE=$(curl --request POST \
|
||||||
stage: release
|
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
|
||||||
image: registry.gitlab.com/gitlab-org/release-cli:latest
|
--header "Content-Type: application/json" \
|
||||||
needs:
|
--data '{
|
||||||
- job: release
|
"name": "Release v'$VERSION'",
|
||||||
rules:
|
"tag_name": "v'$VERSION'",
|
||||||
- if: $CI_COMMIT_TAG
|
"description": "Release v'$VERSION'",
|
||||||
script:
|
"ref": "master",
|
||||||
- echo "Creating GitLab release for $CI_COMMIT_TAG"
|
"assets": {
|
||||||
release:
|
"links": [
|
||||||
name: "$CI_COMMIT_TAG"
|
{
|
||||||
tag_name: "$CI_COMMIT_TAG"
|
"name": "Download kids-on-brooms.zip",
|
||||||
description: "Release $CI_COMMIT_TAG of $CI_PROJECT_NAME."
|
"url": "https://gitlab.com/YOUR_NAMESPACE/YOUR_PROJECT/-/jobs/$CI_JOB_ID/artifacts/download"
|
||||||
assets:
|
}
|
||||||
links:
|
]
|
||||||
- name: "$MANIFEST"
|
}
|
||||||
url: "${MANIFEST_RELEASE_URL}"
|
}' "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/releases")
|
||||||
filepath: "/${MANIFEST}"
|
|
||||||
- name: "$ZIPFILE"
|
|
||||||
url: "${ZIPFILE_RELEASE_URL}"
|
|
||||||
filepath: "/${ZIPFILE}"
|
|
||||||
|
|
||||||
|
# Get the release URL from the response
|
||||||
|
- export RELEASE_URL=$(echo $RELEASE_RESPONSE | jq -r '.assets.links[0].url')
|
||||||
|
|
||||||
|
# Update the system.json file with the release URL
|
||||||
|
- sed -i "s|\"download\":.*|\"download\": \"$RELEASE_URL\",|" system.json
|
||||||
|
|
||||||
|
# Commit the updated system.json and push it to master
|
||||||
|
- git config --global user.name "GitLab CI"
|
||||||
|
- git config --global user.email "ci@gitlab.com"
|
||||||
|
- git add system.json
|
||||||
|
- git commit -m "Update system.json with release URL"
|
||||||
|
- git push origin master
|
||||||
|
|
||||||
|
# Publish the release to the Foundry API
|
||||||
|
- curl -X POST https://api.foundryvtt.com/_api/packages/release_version/ \
|
||||||
|
-H "Authorization: $FOUNDRY_API_KEY" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"id": "Your-Package-ID",
|
||||||
|
"release": {
|
||||||
|
"version": "'$VERSION'",
|
||||||
|
"manifest": "https://gitlab.com/wintermyst/kids-on-brooms/-/raw/master/system.json",
|
||||||
|
"notes": "https://gitlab.com/wintermyst/kids-on-brooms/releases/tag/v'$VERSION'",
|
||||||
|
"compatibility": {
|
||||||
|
"minimum": "12.331",
|
||||||
|
"verified": "12.331",
|
||||||
|
"maximum": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
when: manual
|
||||||
|
allow_failure: false
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- kids-on-brooms.zip
|
||||||
|
expire_in: never
|
10
.npmignore
10
.npmignore
@ -1,10 +0,0 @@
|
|||||||
# IDE
|
|
||||||
.idea/
|
|
||||||
.vs/
|
|
||||||
|
|
||||||
# Node Modules
|
|
||||||
node_modules/
|
|
||||||
npm-debug.log
|
|
||||||
|
|
||||||
# Foundry
|
|
||||||
*.lock
|
|
BIN
Kids_on_Brooms_Cover.jpg
Normal file
BIN
Kids_on_Brooms_Cover.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 915 KiB |
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Joscha Maier
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
2
README.md
Normal file
2
README.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# KidsOnBroomsFoundryVTT
|
||||||
|
The Kids on Brooms System for Foundry VTT
|
@ -1,548 +0,0 @@
|
|||||||
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap");
|
|
||||||
/* Global styles */
|
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap");
|
|
||||||
.window-app {
|
|
||||||
font-family: "Roboto", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.window-app .window-content > * {
|
|
||||||
flex:0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rollable:hover, .rollable:focus {
|
|
||||||
color: #000;
|
|
||||||
text-shadow: 0 0 10px rgb(146, 0, 225);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid {
|
|
||||||
display: grid;
|
|
||||||
gap: 10px;
|
|
||||||
margin: 10px 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-start-2 {
|
|
||||||
grid-column-start: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-span-2 {
|
|
||||||
grid-column-end: span 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-2col {
|
|
||||||
grid-column: span 2/span 2;
|
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-start-3 {
|
|
||||||
grid-column-start: 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-span-3 {
|
|
||||||
grid-column-end: span 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-3col {
|
|
||||||
grid-column: span 3/span 3;
|
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-start-4 {
|
|
||||||
grid-column-start: 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-span-4 {
|
|
||||||
grid-column-end: span 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-4col {
|
|
||||||
grid-column: span 4/span 4;
|
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-start-5 {
|
|
||||||
grid-column-start: 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-span-5 {
|
|
||||||
grid-column-end: span 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-5col {
|
|
||||||
grid-column: span 5/span 5;
|
|
||||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-start-6 {
|
|
||||||
grid-column-start: 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-span-6 {
|
|
||||||
grid-column-end: span 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-6col {
|
|
||||||
grid-column: span 6/span 6;
|
|
||||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-start-7 {
|
|
||||||
grid-column-start: 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-span-7 {
|
|
||||||
grid-column-end: span 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-7col {
|
|
||||||
grid-column: span 7/span 7;
|
|
||||||
grid-template-columns: repeat(7, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-start-8 {
|
|
||||||
grid-column-start: 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-span-8 {
|
|
||||||
grid-column-end: span 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-8col {
|
|
||||||
grid-column: span 8/span 8;
|
|
||||||
grid-template-columns: repeat(8, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-start-9 {
|
|
||||||
grid-column-start: 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-span-9 {
|
|
||||||
grid-column-end: span 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-9col {
|
|
||||||
grid-column: span 9/span 9;
|
|
||||||
grid-template-columns: repeat(9, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-start-10 {
|
|
||||||
grid-column-start: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-span-10 {
|
|
||||||
grid-column-end: span 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-10col {
|
|
||||||
grid-column: span 10/span 10;
|
|
||||||
grid-template-columns: repeat(10, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-start-11 {
|
|
||||||
grid-column-start: 11;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-span-11 {
|
|
||||||
grid-column-end: span 11;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-11col {
|
|
||||||
grid-column: span 11/span 11;
|
|
||||||
grid-template-columns: repeat(11, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-start-12 {
|
|
||||||
grid-column-start: 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-span-12 {
|
|
||||||
grid-column-end: span 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-12col {
|
|
||||||
grid-column: span 12/span 12;
|
|
||||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-group-center {
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-box-direction: normal;
|
|
||||||
-ms-flex-direction: center;
|
|
||||||
flex-direction: center;
|
|
||||||
-ms-flex-wrap: center;
|
|
||||||
flex-wrap: center;
|
|
||||||
-webkit-box-pack: start;
|
|
||||||
-ms-flex-pack: start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
-webkit-box-align: stretch;
|
|
||||||
-ms-flex-align: stretch;
|
|
||||||
align-items: stretch;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-group-left {
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-box-direction: normal;
|
|
||||||
-ms-flex-direction: flex-start;
|
|
||||||
flex-direction: flex-start;
|
|
||||||
-ms-flex-wrap: center;
|
|
||||||
flex-wrap: center;
|
|
||||||
-webkit-box-pack: start;
|
|
||||||
-ms-flex-pack: start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
-webkit-box-align: stretch;
|
|
||||||
-ms-flex-align: stretch;
|
|
||||||
align-items: stretch;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-group-right {
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-box-direction: normal;
|
|
||||||
-ms-flex-direction: flex-end;
|
|
||||||
flex-direction: flex-end;
|
|
||||||
-ms-flex-wrap: center;
|
|
||||||
flex-wrap: center;
|
|
||||||
-webkit-box-pack: start;
|
|
||||||
-ms-flex-pack: start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
-webkit-box-align: stretch;
|
|
||||||
-ms-flex-align: stretch;
|
|
||||||
align-items: stretch;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexshrink {
|
|
||||||
-webkit-box-flex: 0;
|
|
||||||
-ms-flex: 0;
|
|
||||||
flex: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-between {
|
|
||||||
-webkit-box-pack: justify;
|
|
||||||
-ms-flex-pack: justify;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexlarge {
|
|
||||||
-webkit-box-flex: 2;
|
|
||||||
-ms-flex: 2;
|
|
||||||
flex: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-left {
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-box-direction: normal;
|
|
||||||
-ms-flex-direction: flex-start;
|
|
||||||
flex-direction: flex-start;
|
|
||||||
-ms-flex-wrap: center;
|
|
||||||
flex-wrap: center;
|
|
||||||
-webkit-box-pack: start;
|
|
||||||
-ms-flex-pack: start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
-webkit-box-align: stretch;
|
|
||||||
-ms-flex-align: stretch;
|
|
||||||
align-items: stretch;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-right {
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-box-direction: normal;
|
|
||||||
-ms-flex-direction: flex-end;
|
|
||||||
flex-direction: flex-end;
|
|
||||||
-ms-flex-wrap: center;
|
|
||||||
flex-wrap: center;
|
|
||||||
-webkit-box-pack: start;
|
|
||||||
-ms-flex-pack: start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
-webkit-box-align: stretch;
|
|
||||||
-ms-flex-align: stretch;
|
|
||||||
align-items: stretch;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-center {
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-box-direction: normal;
|
|
||||||
-ms-flex-direction: center;
|
|
||||||
flex-direction: center;
|
|
||||||
-ms-flex-wrap: center;
|
|
||||||
flex-wrap: center;
|
|
||||||
-webkit-box-pack: start;
|
|
||||||
-ms-flex-pack: start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
-webkit-box-align: stretch;
|
|
||||||
-ms-flex-align: stretch;
|
|
||||||
align-items: stretch;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-align-input {
|
|
||||||
-webkit-box-flex: 1;
|
|
||||||
-ms-flex: 1;
|
|
||||||
flex: 1;
|
|
||||||
margin-left: auto;
|
|
||||||
max-width: 260px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.window-app {
|
|
||||||
font-family: "Roboto", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rollable:hover, .rollable:focus {
|
|
||||||
color: #000;
|
|
||||||
text-shadow: 0 0 10px rgb(179, 7, 217);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-container {
|
|
||||||
min-height: 200px; /* Adjust this value as needed */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Styles limited to kidsonbrooms sheets */
|
|
||||||
.fvtt-never-stop-blowing-up .item-form {
|
|
||||||
font-family: "Roboto", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fvtt-never-stop-blowing-up .sheet-header {
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-box-orient: horizontal;
|
|
||||||
-webkit-box-direction: normal;
|
|
||||||
-ms-flex-direction: row;
|
|
||||||
flex-direction: row;
|
|
||||||
-ms-flex-wrap: wrap;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
-webkit-box-align: start;
|
|
||||||
-ms-flex-align: start;
|
|
||||||
align-items: flex-start;
|
|
||||||
-webkit-box-pack: start;
|
|
||||||
-ms-flex-pack: start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
-webkit-box-flex: 0;
|
|
||||||
-ms-flex: 0 1 auto;
|
|
||||||
flex: 0 1 auto;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
height: 110px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fvtt-never-stop-blowing-up .sheet-header .profile-img {
|
|
||||||
-webkit-box-flex: 0;
|
|
||||||
-ms-flex: 0 0 100px;
|
|
||||||
flex: 0 0 100px;
|
|
||||||
height: 100px;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .sheet-header .header-fields {
|
|
||||||
-webkit-box-flex: 1;
|
|
||||||
-ms-flex: 1;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .sheet-header h1.charname {
|
|
||||||
height: 50px;
|
|
||||||
padding: 0;
|
|
||||||
margin: 5px 0;
|
|
||||||
border-bottom: 0;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .sheet-header h1.charname input {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up div.editor-border {
|
|
||||||
border: 2px solid rgb(81, 81, 81);
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .sheet-tabs {
|
|
||||||
-webkit-box-flex: 1;
|
|
||||||
-ms-flex: 1;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .sheet-body,
|
|
||||||
.fvtt-never-stop-blowing-up .sheet-body .tab,
|
|
||||||
.fvtt-never-stop-blowing-up .sheet-body .tab .editor {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .tox .tox-editor-container {
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .tox .tox-edit-area {
|
|
||||||
padding: 0 8px;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .selection-row {
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-box-pack: justify;
|
|
||||||
-ms-flex-pack: justify;
|
|
||||||
justify-content: space-between;
|
|
||||||
-webkit-box-align: center;
|
|
||||||
-ms-flex-align: center;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .resource-label {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-header {
|
|
||||||
height: 28px;
|
|
||||||
margin: 2px 0;
|
|
||||||
padding: 0;
|
|
||||||
-webkit-box-align: center;
|
|
||||||
-ms-flex-align: center;
|
|
||||||
align-items: center;
|
|
||||||
background: rgba(0, 0, 0, 0.05);
|
|
||||||
border: 2px groove #eeede0;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-header > * {
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-header .item-name {
|
|
||||||
font-weight: bold;
|
|
||||||
padding-left: 5px;
|
|
||||||
text-align: left;
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-list {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow-y: auto;
|
|
||||||
scrollbar-width: thin;
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-list .item-list {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-list .item {
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-box-align: center;
|
|
||||||
-ms-flex-align: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 2px;
|
|
||||||
border-bottom: 1px solid #c9c7b8;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-list .item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-list .item .item-name {
|
|
||||||
-webkit-box-flex: 2;
|
|
||||||
-ms-flex: 2;
|
|
||||||
flex: 2;
|
|
||||||
margin: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: 13px;
|
|
||||||
text-align: left;
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
color: #191813;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-list .item .item-name h3, .fvtt-never-stop-blowing-up .items-list .item .item-name h4 {
|
|
||||||
margin: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-list .item .item-name .item-image {
|
|
||||||
-webkit-box-flex: 0;
|
|
||||||
-ms-flex: 0 0 30px;
|
|
||||||
flex: 0 0 30px;
|
|
||||||
height: 30px;
|
|
||||||
background-size: 30px;
|
|
||||||
border: none;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-list .item-controls {
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-box-flex: 0;
|
|
||||||
-ms-flex: 0 0 100px;
|
|
||||||
flex: 0 0 100px;
|
|
||||||
-webkit-box-pack: end;
|
|
||||||
-ms-flex-pack: end;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-list .item-controls a {
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center;
|
|
||||||
margin: 0 6px;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-list .item-prop {
|
|
||||||
text-align: center;
|
|
||||||
border-left: 1px solid #c9c7b8;
|
|
||||||
border-right: 1px solid #c9c7b8;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-list .items-header {
|
|
||||||
height: 28px;
|
|
||||||
margin: 2px 0;
|
|
||||||
padding: 0;
|
|
||||||
-webkit-box-align: center;
|
|
||||||
-ms-flex-align: center;
|
|
||||||
align-items: center;
|
|
||||||
background: rgba(0, 0, 0, 0.05);
|
|
||||||
border: 2px groove #eeede0;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-list .items-header > * {
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .items-list .items-header .item-name {
|
|
||||||
padding-left: 5px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .item-formula {
|
|
||||||
-webkit-box-flex: 0;
|
|
||||||
-ms-flex: 0 0 200px;
|
|
||||||
flex: 0 0 200px;
|
|
||||||
padding: 0 8px;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .effects .item .effect-source,
|
|
||||||
.fvtt-never-stop-blowing-up .effects .item .effect-duration,
|
|
||||||
.fvtt-never-stop-blowing-up .effects .item .effect-controls {
|
|
||||||
text-align: center;
|
|
||||||
border-left: 1px solid #c9c7b8;
|
|
||||||
border-right: 1px solid #c9c7b8;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .effects .item .effect-controls {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
.fvtt-never-stop-blowing-up .fvtt-never-stop-blowing-up input:focus,
|
|
||||||
.fvtt-never-stop-blowing-up .fvtt-never-stop-blowing-up textarea:focus,
|
|
||||||
.fvtt-never-stop-blowing-up .fvtt-never-stop-blowing-up select:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: #8102dd;
|
|
||||||
}
|
|
298
gulpfile.js
298
gulpfile.js
@ -3,41 +3,7 @@ const prefix = require('gulp-autoprefixer');
|
|||||||
const sourcemaps = require('gulp-sourcemaps');
|
const sourcemaps = require('gulp-sourcemaps');
|
||||||
const sass = require('gulp-sass')(require('sass'));
|
const sass = require('gulp-sass')(require('sass'));
|
||||||
const zip = require('gulp-zip');
|
const zip = require('gulp-zip');
|
||||||
const fs = require('fs');
|
const { compile } = require('sass');
|
||||||
const fetch = require('node-fetch');
|
|
||||||
const replace = require('gulp-replace');
|
|
||||||
const FormData = require('form-data');
|
|
||||||
|
|
||||||
/* ----------------------------------------- */
|
|
||||||
/* Export Tasks
|
|
||||||
/* ----------------------------------------- */
|
|
||||||
|
|
||||||
exports.default = gulp.series(
|
|
||||||
compileScss,
|
|
||||||
watchUpdates
|
|
||||||
);
|
|
||||||
|
|
||||||
exports.build = gulp.series(
|
|
||||||
compileScss,
|
|
||||||
checkVersion,
|
|
||||||
ensureOutputDirExists,
|
|
||||||
packageCompendiums,
|
|
||||||
updateSystemJson,
|
|
||||||
zipRelease
|
|
||||||
);
|
|
||||||
|
|
||||||
exports.compile = gulp.series(
|
|
||||||
compileScss,
|
|
||||||
ensureOutputDirExists,
|
|
||||||
packageCompendiums,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
exports.release = gulp.series(
|
|
||||||
exports.build,
|
|
||||||
uploadToPackageRegistry,
|
|
||||||
publishToFoundry
|
|
||||||
);
|
|
||||||
|
|
||||||
/* ----------------------------------------- */
|
/* ----------------------------------------- */
|
||||||
/* Compile Sass
|
/* Compile Sass
|
||||||
@ -65,15 +31,28 @@ function compileScss() {
|
|||||||
}))
|
}))
|
||||||
.pipe(gulp.dest("./css"))
|
.pipe(gulp.dest("./css"))
|
||||||
}
|
}
|
||||||
|
const css = gulp.series(compileScss);
|
||||||
|
|
||||||
/* ----------------------------------------- */
|
/* ----------------------------------------- */
|
||||||
/* Watch Updates
|
/* Watch Updates
|
||||||
/* ----------------------------------------- */
|
/* ----------------------------------------- */
|
||||||
|
|
||||||
function watchUpdates() {
|
function watchUpdates() {
|
||||||
gulp.watch(SYSTEM_SCSS, compileScss);
|
gulp.watch(SYSTEM_SCSS, compileScss());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------- */
|
||||||
|
/* Export Tasks
|
||||||
|
/* ----------------------------------------- */
|
||||||
|
|
||||||
|
exports.default = gulp.series(
|
||||||
|
compileScss,
|
||||||
|
watchUpdates
|
||||||
|
);
|
||||||
|
exports.build = gulp.series(
|
||||||
|
compileScss
|
||||||
|
);
|
||||||
|
exports.css = css;
|
||||||
|
|
||||||
/* ----------------------------------------- */
|
/* ----------------------------------------- */
|
||||||
/* Zip Release
|
/* Zip Release
|
||||||
@ -90,249 +69,12 @@ function zipRelease() {
|
|||||||
'!./package.json',
|
'!./package.json',
|
||||||
'!./scss/**/*',
|
'!./scss/**/*',
|
||||||
'!./.github/**/*',
|
'!./.github/**/*',
|
||||||
'!./.gitlab-ci.yml',
|
|
||||||
'!./README.md',
|
|
||||||
'!./compendiums/**/*',
|
|
||||||
'!./*.zip'
|
|
||||||
], { base: '.' })
|
], { base: '.' })
|
||||||
.pipe(zip('kidsonbrooms.zip'))
|
.pipe(zip('kids-on-brooms.zip'))
|
||||||
.pipe(gulp.dest('.'));
|
.pipe(gulp.dest('.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------- */
|
exports.release = gulp.series(
|
||||||
/* Version Check
|
compileScss,
|
||||||
/* ----------------------------------------- */
|
zipRelease
|
||||||
|
);
|
||||||
|
|
||||||
function checkVersion(done) {
|
|
||||||
const Manifest = JSON.parse(fs.readFileSync('system.json'));
|
|
||||||
const manifestVersion = Manifest.version;
|
|
||||||
const gitTag = process.env.CI_COMMIT_TAG;
|
|
||||||
|
|
||||||
if (gitTag && manifestVersion !== gitTag) {
|
|
||||||
console.error(`Version mismatch between tag (${gitTag}) and manifest (${manifestVersion})!`);
|
|
||||||
process.exit(1);
|
|
||||||
} else {
|
|
||||||
console.log(`Version check passed: ${manifestVersion}`);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------- */
|
|
||||||
/* Bundle Compendium
|
|
||||||
/* ----------------------------------------- */
|
|
||||||
|
|
||||||
const { exec } = require('child_process');
|
|
||||||
|
|
||||||
function packageCompendiums(done) {
|
|
||||||
const packsDir = './compendiums'; // Adjust to your compendium source directory
|
|
||||||
const outputDir = './packs';
|
|
||||||
const moduleId = 'kidsonbrooms'; // Replace with your actual module ID
|
|
||||||
|
|
||||||
// Read all subdirectories in the packsDir
|
|
||||||
if (!fs.existsSync(packsDir)) {
|
|
||||||
console.log(`Compendium directory ${packsDir} does not exist. Skipping packaging.`);
|
|
||||||
done();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read all files and directories in the packsDir
|
|
||||||
fs.readdir(packsDir, (err, files) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(`Error reading directory ${packsDir}: ${err}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter to get only directories
|
|
||||||
const folders = files.filter(file => {
|
|
||||||
const fullPath = path.join(packsDir, file);
|
|
||||||
return fs.statSync(fullPath).isDirectory();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (folders.length === 0) {
|
|
||||||
console.log(`No compendium folders found in ${packsDir}. Skipping packaging.`);
|
|
||||||
done();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let completed = 0;
|
|
||||||
folders.forEach(folder => {
|
|
||||||
const packName = folder; // Use the folder name as the pack name
|
|
||||||
const inputPath = path.join(packsDir, folder);
|
|
||||||
const command = `npx fvtt package pack --type System --id ${moduleId} -n "${packName}" --in "${inputPath}" --out "${outputDir}" --yaml`;
|
|
||||||
|
|
||||||
exec(command, (err, stdout, stderr) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(`Error packaging compendium ${packName}:\n${stderr}`);
|
|
||||||
process.exit(1);
|
|
||||||
} else {
|
|
||||||
console.log(`Compendium ${packName} packaged successfully.`);
|
|
||||||
console.log(stdout);
|
|
||||||
completed++;
|
|
||||||
// When all compendiums have been processed, call done()
|
|
||||||
if (completed === folders.length) {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------- */
|
|
||||||
/* Ensure Output Directory Exists
|
|
||||||
/* ----------------------------------------- */
|
|
||||||
|
|
||||||
function ensureOutputDirExists() {
|
|
||||||
const outputDir = './packs';
|
|
||||||
if (!fs.existsSync(outputDir)) {
|
|
||||||
fs.mkdirSync(outputDir);
|
|
||||||
}
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------- */
|
|
||||||
/* Upload to Package Registry
|
|
||||||
/* ----------------------------------------- */
|
|
||||||
|
|
||||||
|
|
||||||
async function uploadToPackageRegistry(done) {
|
|
||||||
const manifestFile = 'system.json';
|
|
||||||
const zipFile = process.env.ZIPFILE || 'kidsonbrooms.zip';
|
|
||||||
const packageRegistryUrl = process.env.PACKAGE_REGISTRY_URL;
|
|
||||||
const ciJobToken = process.env.CI_JOB_TOKEN;
|
|
||||||
|
|
||||||
if (!packageRegistryUrl || !ciJobToken) {
|
|
||||||
console.error('PACKAGE_REGISTRY_URL or CI_JOB_TOKEN is not defined.');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Upload manifest file
|
|
||||||
const manifestUploadUrl = `${packageRegistryUrl}/${manifestFile}`;
|
|
||||||
console.log(`Uploading ${manifestFile} to ${manifestUploadUrl}`);
|
|
||||||
|
|
||||||
let response = await fetch(manifestUploadUrl, {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'JOB-TOKEN': ciJobToken,
|
|
||||||
'Content-Type': 'application/octet-stream',
|
|
||||||
},
|
|
||||||
body: fs.createReadStream(manifestFile),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
console.log(`Uploaded ${manifestFile} successfully.`);
|
|
||||||
} else {
|
|
||||||
console.error(`Failed to upload ${manifestFile}: ${response.statusText}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload zip file
|
|
||||||
const zipUploadUrl = `${packageRegistryUrl}/${zipFile}`;
|
|
||||||
console.log(`Uploading ${zipFile} to ${zipUploadUrl}`);
|
|
||||||
|
|
||||||
response = await fetch(zipUploadUrl, {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'JOB-TOKEN': ciJobToken,
|
|
||||||
'Content-Type': 'application/octet-stream',
|
|
||||||
},
|
|
||||||
body: fs.createReadStream(zipFile),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
console.log(`Uploaded ${zipFile} successfully.`);
|
|
||||||
} else {
|
|
||||||
console.error(`Failed to upload ${zipFile}: ${response.statusText}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
done();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error uploading files: ${error.message}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------- */
|
|
||||||
/* Publish to FoundryVTT
|
|
||||||
/* ----------------------------------------- */
|
|
||||||
|
|
||||||
async function publishToFoundry(done) {
|
|
||||||
const moduleManifestPath = 'system.json';
|
|
||||||
const moduleManifest = JSON.parse(fs.readFileSync(moduleManifestPath));
|
|
||||||
|
|
||||||
const id = moduleManifest.name;
|
|
||||||
const version = moduleManifest.version;
|
|
||||||
const compMin = moduleManifest.compatibility.minimum;
|
|
||||||
const compVer = moduleManifest.compatibility.verified;
|
|
||||||
const compMax = moduleManifest.compatibility.maximum;
|
|
||||||
const manifest = process.env.MANIFEST_PERMALINK_URL || `https://gitlab.com/${process.env.CI_PROJECT_NAMESPACE}/${process.env.CI_PROJECT_NAME}/-/releases/${process.env.CI_COMMIT_TAG}/downloads/${moduleManifestPath}`;
|
|
||||||
const notes = `https://gitlab.com/${process.env.CI_PROJECT_NAMESPACE}/${process.env.CI_PROJECT_NAME}/-/releases/${process.env.CI_COMMIT_TAG}`;
|
|
||||||
|
|
||||||
const dryRun = process.env.dry_run === 'true';
|
|
||||||
const authToken = process.env.FOUNDRY_API_KEY;
|
|
||||||
|
|
||||||
if (!authToken) {
|
|
||||||
console.error('Foundry VTT API authentication token (FOUNDRY_API_KEY) is not defined.');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the payload
|
|
||||||
const payload = {
|
|
||||||
id: "kidsonbrooms",
|
|
||||||
release: {
|
|
||||||
version: version,
|
|
||||||
manifest: manifest,
|
|
||||||
notes: notes,
|
|
||||||
compatibility: {
|
|
||||||
minimum: compMin,
|
|
||||||
verified: compVer,
|
|
||||||
maximum: compMax,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (dryRun) {
|
|
||||||
payload['dry-run'] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the POST request to Foundry VTT API
|
|
||||||
const response = await fetch('https://api.foundryvtt.com/_api/packages/release_version', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
Authorization: authToken,
|
|
||||||
},
|
|
||||||
body: JSON.stringify(payload),
|
|
||||||
});
|
|
||||||
|
|
||||||
const responseData = await response.text();
|
|
||||||
|
|
||||||
if (responseData.includes('success')) {
|
|
||||||
console.log('Successfully published to Foundry VTT:');
|
|
||||||
console.log(JSON.stringify(responseData, null, 2));
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
console.error('Failed to publish to Foundry VTT:');
|
|
||||||
console.error(JSON.stringify(responseData, null, 2));
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------- */
|
|
||||||
/* Update systen.json with Download URL
|
|
||||||
/* ----------------------------------------- */
|
|
||||||
|
|
||||||
function updateSystemJson(done) {
|
|
||||||
const ManifestPath = 'system.json';
|
|
||||||
const Manifest = JSON.parse(fs.readFileSync(ManifestPath));
|
|
||||||
const zipUrl = process.env.ZIPFILE_RELEASE_URL || 'https://gitlab.com/wintermyst/kidsonbrooms/-/raw/master/kidsonbrooms.zip?inline=false';
|
|
||||||
|
|
||||||
Manifest.download = zipUrl;
|
|
||||||
|
|
||||||
fs.writeFileSync(ManifestPath, JSON.stringify(Manifest, null, 2));
|
|
||||||
console.log(`Updated module.json with download URL: ${zipUrl}`);
|
|
||||||
done();
|
|
||||||
}
|
|
23
kidsonbrooms.mjs
Normal file
23
kidsonbrooms.mjs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import * as models from "./modules/dataModel/_system.mjs";
|
||||||
|
import * as sheets from "./modules/sheets/_system.mjs";
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Foundry VTT Initialization */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
const SYSTEM = {
|
||||||
|
id: "kidsonbrooms",
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Hooks.once("init", async function() {
|
||||||
|
console.log("Initialising Kids on Brooms system");
|
||||||
|
|
||||||
|
globalThis.kidsonbrooms = game.system;
|
||||||
|
game.system.CONST = SYSTEM;
|
||||||
|
|
||||||
|
Actors.unregisterSheet("core", ActorSheet);
|
||||||
|
Actors.registerSheet(SYSTEM.id, "{{sheets.BaseActorSheet}}", {types: ["PlayerCharacter"], makeDefault: true});
|
||||||
|
sheets.BaseActorSheet.getTemplate();
|
||||||
|
CONFIG.Actor.dataModels = models.playerCharacterModel;
|
||||||
|
|
||||||
|
})
|
33
lang/en.json
33
lang/en.json
@ -1,9 +1,30 @@
|
|||||||
{
|
{
|
||||||
|
"BaseActor": {
|
||||||
|
"FIELDS": {
|
||||||
|
"age": "Age",
|
||||||
|
"pronouns": "Pronouns",
|
||||||
|
"fear": "Fear",
|
||||||
|
"grade": "Grade",
|
||||||
|
"stats": {
|
||||||
|
"fight": "Fight",
|
||||||
|
"brains": "Brains",
|
||||||
|
"charm": "Charm",
|
||||||
|
"flight": "Flight",
|
||||||
|
"brawn": "Brawn",
|
||||||
|
"grit": "Grit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"NEVERSTOPBLOWINGUP.EffectCreate": "Create Effect",
|
"PlayerCharacter": {
|
||||||
"NEVERSTOPBLOWINGUP.EffectToggle": "Toggle Effect",
|
"FIELDS": {
|
||||||
"NEVERSTOPBLOWINGUP.EffectEdit": "Edit Effect",
|
"description": "Description",
|
||||||
"NEVERSTOPBLOWINGUP.EffectDelete": "Delete Effect",
|
"broom": "Broom",
|
||||||
|
"wand": "Wand",
|
||||||
"NEVERSTOPBLOWINGUP.Add": "Add"
|
"animalFamiliar": "Animal Familiar",
|
||||||
|
"schoolbag": "Schoolbag",
|
||||||
|
"strengths": "Strengths",
|
||||||
|
"adversityTokens": "Adversity Tokens"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,65 +0,0 @@
|
|||||||
/**
|
|
||||||
* Extend the base Actor document by defining a custom roll data structure which is ideal for the Simple system.
|
|
||||||
* @extends {Actor}
|
|
||||||
*/
|
|
||||||
export class NeverStopBlowingUpActor extends Actor {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override getRollData() that's supplied to rolls.
|
|
||||||
*/
|
|
||||||
getRollDataPC() {
|
|
||||||
let data = { ...this.system };
|
|
||||||
|
|
||||||
// Wand bonuses
|
|
||||||
data.wandBonus = {
|
|
||||||
wood: this._getWandBonus(this.system.wand.wood),
|
|
||||||
core: this._getWandBonus(this.system.wand.core)
|
|
||||||
};
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
getRollDataNPC() {
|
|
||||||
let data = { ...this.system};
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getWandBonus(type) {
|
|
||||||
const bonuses = {
|
|
||||||
"Wisteria": { stat: "brains", bonus: 1 },
|
|
||||||
"Hawthorn": { stat: "brains", bonus: 1 },
|
|
||||||
"Pine": { stat: "brawn", bonus: 1 },
|
|
||||||
"Oak": { stat: "brawn", bonus: 1 },
|
|
||||||
"Crabapple": { stat: "fight", bonus: 1 },
|
|
||||||
"Dogwood": { stat: "fight", bonus: 1 },
|
|
||||||
"Birch": { stat: "flight", bonus: 1 },
|
|
||||||
"Bamboo": { stat: "flight", bonus: 1 },
|
|
||||||
"Ironwood": { stat: "grit", bonus: 1 },
|
|
||||||
"Maple": { stat: "grit", bonus: 1 },
|
|
||||||
"Lilac": { stat: "charm", bonus: 1 },
|
|
||||||
"Cherry": { stat: "charm", bonus: 1 },
|
|
||||||
"Parchment": { stat: "brains", bonus: 1 },
|
|
||||||
"Phoenix Feather": { stat: "brains", bonus: 1 },
|
|
||||||
"Owl Feather": { stat: "brains", bonus: 1 },
|
|
||||||
"Gorilla Fur": { stat: "brawn", bonus: 1 },
|
|
||||||
"Ogre’s Fingernail": { stat: "brawn", bonus: 1 },
|
|
||||||
"Hippo’s Tooth": { stat: "brawn", bonus: 1 },
|
|
||||||
"Dragon’s Heartstring": { stat: "fight", bonus: 1 },
|
|
||||||
"Wolf’s Tooth": { stat: "fight", bonus: 1 },
|
|
||||||
"Elk’s Antler": { stat: "fight", bonus: 1 },
|
|
||||||
"Hawk’s Feather": { stat: "flight", bonus: 1 },
|
|
||||||
"Bat’s Bone": { stat: "flight", bonus: 1 },
|
|
||||||
"Changeling’s Hair": { stat: "charm", bonus: 1 },
|
|
||||||
"Gold": { stat: "charm", bonus: 1 },
|
|
||||||
"Mirror": { stat: "charm", bonus: 1 },
|
|
||||||
"Steel": { stat: "grit", bonus: 1 },
|
|
||||||
"Diamond": { stat: "grit", bonus: 1 },
|
|
||||||
"Lion’s Mane": { stat: "grit", bonus: 1 }
|
|
||||||
};
|
|
||||||
|
|
||||||
return bonuses[type] || { stat: "", bonus: 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
export const NEVERSTOPBLOWINGUP = {};
|
|
||||||
|
|
||||||
// Define constants here, such as:
|
|
||||||
NEVERSTOPBLOWINGUP.foobar = {
|
|
||||||
'bas': 'NEVERSTOPBLOWINGUP.bas',
|
|
||||||
'bar': 'NEVERSTOPBLOWINGUP.bar'
|
|
||||||
};
|
|
@ -1,15 +0,0 @@
|
|||||||
/**
|
|
||||||
* Define a set of template paths to pre-load
|
|
||||||
* Pre-loaded templates are compiled and cached for fast access when rendering
|
|
||||||
* @return {Promise}
|
|
||||||
*/
|
|
||||||
export const preloadHandlebarsTemplates = async function() {
|
|
||||||
return loadTemplates([
|
|
||||||
|
|
||||||
// Actor partials.
|
|
||||||
"systems/fvtt-never-stop-blowing-up/templates/actor/parts/actor-features.html",
|
|
||||||
"systems/fvtt-never-stop-blowing-up/templates/actor/parts/actor-adversity.html",
|
|
||||||
"systems/fvtt-never-stop-blowing-up/templates/actor/parts/actor-stats.html",
|
|
||||||
"systems/fvtt-never-stop-blowing-up/templates/actor/parts/actor-npc-stats.html",
|
|
||||||
]);
|
|
||||||
};
|
|
@ -1,332 +0,0 @@
|
|||||||
// Import document classes.
|
|
||||||
import { NeverStopBlowingUpActor } from "./documents/actor.mjs";
|
|
||||||
|
|
||||||
// Import sheet classes.
|
|
||||||
import { NeverStopBlowingUpActorSheet } from "./sheets/actor-sheet.mjs";
|
|
||||||
|
|
||||||
// Import helper/utility classes and constants.
|
|
||||||
import { preloadHandlebarsTemplates } from "./helpers/templates.mjs";
|
|
||||||
import { NEVERSTOPBLOWINGUP } from "./helpers/config.mjs";
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/* Init Hook */
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
Hooks.once('init', async function() {
|
|
||||||
|
|
||||||
// Register the helper
|
|
||||||
Handlebars.registerHelper('capitalizeFirst', function(string) {
|
|
||||||
if (typeof string === 'string') {
|
|
||||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add utility classes and functions to the global game object so that they're more easily
|
|
||||||
// accessible in global contexts.
|
|
||||||
game.kidsonbrooms = {
|
|
||||||
NeverStopBlowingUpActor,
|
|
||||||
_onTakeAdversityToken: _onTakeAdversityToken, // Add the function to the global object
|
|
||||||
_onSpendAdversityTokens: _onSpendAdversityTokens // Add the function to the global object
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add custom constants for configuration.
|
|
||||||
CONFIG.NEVERSTOPBLOWINGUP = NEVERSTOPBLOWINGUP;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set an initiative formula for the system
|
|
||||||
* @type {String}
|
|
||||||
*/
|
|
||||||
CONFIG.Combat.initiative = {
|
|
||||||
formula: "1d20",
|
|
||||||
decimals: 2
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Define custom Document classes
|
|
||||||
CONFIG.Actor.documentClass = NeverStopBlowingUpActor;
|
|
||||||
|
|
||||||
// Register sheet application classes
|
|
||||||
Actors.unregisterSheet("core", ActorSheet);
|
|
||||||
Actors.registerSheet("fvtt-never-stop-blowing-up", NeverStopBlowingUpActorSheet, { makeDefault: true });
|
|
||||||
|
|
||||||
//If there is a new chat message that is a roll we add the adversity token controls
|
|
||||||
Hooks.on("renderChatMessage", (message, html, messageData) => {
|
|
||||||
const adversityControls = html.find('.adversity-controls');
|
|
||||||
if (adversityControls.length > 0) {
|
|
||||||
const messageToEdit = adversityControls.data("roll-id");
|
|
||||||
// Bind event listeners for the controls
|
|
||||||
adversityControls.find(".take-adversity").off("click").click((event) => {
|
|
||||||
|
|
||||||
const actorId = event.currentTarget.dataset.actorId;
|
|
||||||
const actor = game.actors.get(actorId);
|
|
||||||
|
|
||||||
// Check if the current user owns the actor - They can not claim if they are not
|
|
||||||
if (!actor.testUserPermission(game.user, "owner")) {
|
|
||||||
ui.notifications.warn("You don't own this character and cannot take adversity tokens.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the token has already been claimed -- Contigency if the button somehow activates again
|
|
||||||
if (message.getFlag("kidsonbrooms", "tokenClaimed")) {
|
|
||||||
ui.notifications.warn("This adversity token has already been claimed.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_onTakeAdversityToken(event, actor);
|
|
||||||
if (game.user.isGM) {
|
|
||||||
let tokenControls = game.messages.get(message.id);
|
|
||||||
console.log(tokenControls);
|
|
||||||
// Update the chat message content with the button disabled and text changed
|
|
||||||
const updatedContent = tokenControls.content.replace(
|
|
||||||
`<button class="take-adversity" data-actor-id="${actor.id}">Take Adversity Token</button>`,
|
|
||||||
`<button class="take-adversity" data-actor-id="${actor.id}" disabled>Token claimed</button>`
|
|
||||||
);
|
|
||||||
console.log("Removing Button");
|
|
||||||
// Update the message content
|
|
||||||
tokenControls.update({ content: updatedContent });
|
|
||||||
// Set the flag on the chat message to indicate that the token has been claimed
|
|
||||||
tokenControls.setFlag("fvtt-never-stop-blowing-up", "tokenClaimed", true);
|
|
||||||
} else {
|
|
||||||
// Emit a socket request to update the message to show that the token has been claimed
|
|
||||||
game.socket.emit('system.fvtt-never-stop-blowing-up', {
|
|
||||||
action: "takeToken",
|
|
||||||
messageID: message.id,
|
|
||||||
actorID: actor.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
console.log("Send socket message for taking a token");
|
|
||||||
});
|
|
||||||
|
|
||||||
adversityControls.find(".spend-adversity").off("click").click((event) => {
|
|
||||||
//This entails a lot more, so I offloaded it to a new function
|
|
||||||
_onSpendAdversityTokens(event, messageToEdit);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Preload Handlebars templates.
|
|
||||||
return preloadHandlebarsTemplates();
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
/***
|
|
||||||
* This handles the incoming socket requests.
|
|
||||||
* If a player wants to spend tokens on another players roll the gm has to approve first
|
|
||||||
* if a player wants to claim a token we will update the message since they do not have the permissions
|
|
||||||
*/
|
|
||||||
Hooks.once('ready', function() {
|
|
||||||
game.socket.on('system.fvtt-never-stop-blowing-up', async (data) => {
|
|
||||||
console.log("Socket data received:", data);
|
|
||||||
|
|
||||||
if (data.action === "spendTokens") {
|
|
||||||
console.log(`Request to spend tokens: ${data.tokensToSpend} tokens for ${data.rollActorId} by ${data.spendingActorId}`);
|
|
||||||
|
|
||||||
// Only handle the request if the GM is logged in
|
|
||||||
if (!game.user.isGM) {
|
|
||||||
console.log("Not GM, ignoring the token spend request.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The actor who made the roll
|
|
||||||
const rollActor = game.actors.get(data.rollActorId);
|
|
||||||
// The actor who is spending tokens
|
|
||||||
const spendingActor = game.actors.get(data.spendingActorId);
|
|
||||||
|
|
||||||
//If these for some reason do not exist
|
|
||||||
if (!rollActor || !spendingActor) {
|
|
||||||
console.warn("Actor not found:", data.rollActorId, data.spendingActorId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a confirmation dialog for the GM
|
|
||||||
new Dialog({
|
|
||||||
title: "Approve Adversity Token Spending?",
|
|
||||||
content: `<p>${spendingActor.name} wants to spend ${data.tokenCost} adversity tokens on ${rollActor.name}'s roll to increase it by ${data.tokensToSpend}. Approve?</p>`,
|
|
||||||
buttons: {
|
|
||||||
yes: {
|
|
||||||
label: "Yes",
|
|
||||||
callback: async () => {
|
|
||||||
|
|
||||||
|
|
||||||
const currentTokens = spendingActor.system.adversityTokens || 0;
|
|
||||||
|
|
||||||
// Update the spending actor's adversity token count
|
|
||||||
await spendingActor.update({ "system.adversityTokens": currentTokens - data.tokenCost });
|
|
||||||
|
|
||||||
|
|
||||||
// Modify the roll message with the new total
|
|
||||||
await _updateRollMessage(data.rollMessageId, data.tokensToSpend, false);
|
|
||||||
|
|
||||||
console.log(`${spendingActor.name} spent ${data.tokensToSpend} tokens, updated roll total to ${roll.cumulativeTotal}`);
|
|
||||||
ui.notifications.info(`${spendingActor.name} successfully spent ${data.tokensToSpend} tokens.`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
no: {
|
|
||||||
label: "No",
|
|
||||||
callback: () => {
|
|
||||||
ui.notifications.info(`The GM denied ${spendingActor.name}'s request to spend tokens.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
default: "yes"
|
|
||||||
}).render(true);
|
|
||||||
} else if (data.action === "takeToken") {
|
|
||||||
// Only handle the request if the GM is logged in
|
|
||||||
if (!game.user.isGM) {
|
|
||||||
console.log("Not GM, ignoring the token spend request.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let tokenControls = game.messages.get(data.messageID);
|
|
||||||
console.log(tokenControls);
|
|
||||||
// Update the chat message content with the button disabled and text changed
|
|
||||||
const updatedContent = tokenControls.content.replace(
|
|
||||||
`<button class="take-adversity" data-actor-id="${data.actorID}">Take Adversity Token</button>`,
|
|
||||||
`<button class="take-adversity" data-actor-id="${data.actorID}" disabled>Token claimed</button>`
|
|
||||||
);
|
|
||||||
console.log("Removing Button");
|
|
||||||
// Update the message content
|
|
||||||
tokenControls.update({ content: updatedContent });
|
|
||||||
// Set the flag on the chat message to indicate that the token has been claimed
|
|
||||||
tokenControls.setFlag("fvtt-never-stop-blowing-up", "tokenClaimed", true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/***
|
|
||||||
* This function adds the adversity token to the actor that made the roll and logs it
|
|
||||||
*
|
|
||||||
* @param {Event} e - The button click event
|
|
||||||
* @param {Actor} actor - The actor object that made the roll
|
|
||||||
*/
|
|
||||||
async function _onTakeAdversityToken(e, actor) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
|
|
||||||
// Get the chat message ID (assuming it's stored in the dataset)
|
|
||||||
const messageId = e.currentTarget.closest('.message').dataset.messageId;
|
|
||||||
const message = game.messages.get(messageId);
|
|
||||||
|
|
||||||
// Add an adversity token to the actor
|
|
||||||
const currentTokens = actor.system.adversityTokens || 0;
|
|
||||||
await actor.update({ "system.adversityTokens": currentTokens + 1 });
|
|
||||||
|
|
||||||
|
|
||||||
// Notify the user
|
|
||||||
ui.notifications.info(`You gained 1 adversity token.`);
|
|
||||||
console.log(`Gave one adversity token to ${actor.id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
|
||||||
* This function allows players to spend tokens to change a roll. This will automatically be calculated in their sheet
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
async function _onSpendAdversityTokens(e, rollMessageId) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
// The actor who made the roll
|
|
||||||
const rollActorId = e.currentTarget.dataset.actorId;
|
|
||||||
const rollActor = game.actors.get(rollActorId); //technically redundant since it is also done in the main hook, but perfomance is good enuff
|
|
||||||
|
|
||||||
// Get the actor of the player who is spending tokens
|
|
||||||
const spendingPlayerActor = game.actors.get(game.user.character?.id || game.actors.filter(actor => actor.testUserPermission(game.user, "owner"))[0]?.id);
|
|
||||||
|
|
||||||
if (!spendingPlayerActor) {
|
|
||||||
ui.notifications.warn("You don't control any actors.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Get the tokens to be spend from the input field
|
|
||||||
const tokenInput = $(e.currentTarget).closest('.adversity-controls').find('.token-input').val();
|
|
||||||
const tokensToSpend = parseInt(tokenInput, 10);
|
|
||||||
|
|
||||||
if (isNaN(tokensToSpend) || tokensToSpend <= 0) {
|
|
||||||
ui.notifications.warn("Please enter a valid number of tokens.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let tokenCost = tokensToSpend;
|
|
||||||
|
|
||||||
// If the player spending tokens is not the owner of the actor who rolled, they spend double
|
|
||||||
//(note, this is a rule of mine, I have disabled it by default)
|
|
||||||
if ((!spendingPlayerActor.testUserPermission(game.user, "owner") || spendingPlayerActor.id !== rollActorId) && false) {
|
|
||||||
tokenCost = tokensToSpend * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the spending actor has enough adversity tokens
|
|
||||||
if (spendingPlayerActor.system.adversityTokens < tokenCost) {
|
|
||||||
ui.notifications.warn(`You do not have enough adversity tokens.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the player owns the actor who made the roll
|
|
||||||
if (spendingPlayerActor.id === rollActorId) {
|
|
||||||
// The player owns the actor, so they can spend tokens directly without GM approval
|
|
||||||
const currentTokens = spendingPlayerActor.system.adversityTokens || 0;
|
|
||||||
|
|
||||||
// Deduct the tokens from the player
|
|
||||||
await spendingPlayerActor.update({ "system.adversityTokens": currentTokens - tokenCost });
|
|
||||||
|
|
||||||
// Modify the roll message with the new total
|
|
||||||
await _updateRollMessage(rollMessageId, tokensToSpend, true);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// The player does not own the actor, so request GM approval to spend the tokens
|
|
||||||
console.log(`Requesting to spend ${tokensToSpend} tokens for ${rollActor.name} by ${spendingPlayerActor.name} (cost: ${tokenCost})`);
|
|
||||||
|
|
||||||
// Emit a socket request to spend tokens
|
|
||||||
game.socket.emit('system.fvtt-never-stop-blowing-up', {
|
|
||||||
action: "spendTokens",
|
|
||||||
rollActorId: rollActorId,
|
|
||||||
spendingActorId: spendingPlayerActor.id, // Send the player's actor who is spending the tokens
|
|
||||||
tokensToSpend: tokensToSpend,
|
|
||||||
tokenCost: tokenCost,
|
|
||||||
rollMessageId: rollMessageId // Pass message ID to update the roll result
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.notifications.info(`Requested to spend ${tokenCost} tokens for ${rollActor.name}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to send a new message with the updated roll result
|
|
||||||
async function _updateRollMessage(rollMessageId, tokensToSpend, isPlayerOfActor) {
|
|
||||||
const message = game.messages.get(rollMessageId);
|
|
||||||
|
|
||||||
if (!message) {
|
|
||||||
console.error("Message not found with ID:", rollMessageId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve current tokens spent from flags, or initialize to 0 if not found
|
|
||||||
let cumulativeTokensSpent = message.getFlag("fvtt-never-stop-blowing-up", "tokensSpent") || 0;
|
|
||||||
let newTotal = message.getFlag("fvtt-never-stop-blowing-up", "newRollTotal") || message.rolls[0].total;
|
|
||||||
|
|
||||||
/*if(isPlayerOfActor)
|
|
||||||
{
|
|
||||||
// Add the new tokens to the cumulative total
|
|
||||||
cumulativeTokensSpent += tokensToSpend;
|
|
||||||
} else {
|
|
||||||
cumulativeTokensSpent += 2*tokensToSpend;
|
|
||||||
}*/
|
|
||||||
cumulativeTokensSpent += tokensToSpend;
|
|
||||||
newTotal += tokensToSpend;
|
|
||||||
await message.setFlag("fvtt-never-stop-blowing-up", "newRollTotal", newTotal);
|
|
||||||
|
|
||||||
// Update the message's flags to store the cumulative tokens spent
|
|
||||||
await message.setFlag("fvtt-never-stop-blowing-up", "tokensSpent", cumulativeTokensSpent);
|
|
||||||
let newContent = "";
|
|
||||||
if(cumulativeTokensSpent === 1)
|
|
||||||
{
|
|
||||||
newContent = `You have now spent ${cumulativeTokensSpent} token. The new roll total is ${newTotal}.`;
|
|
||||||
} else {
|
|
||||||
newContent = `You have now spent ${cumulativeTokensSpent} tokens. The new roll total is ${newTotal}.`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new chat message to display the updated total
|
|
||||||
await ChatMessage.create({
|
|
||||||
speaker: ChatMessage.getSpeaker({ actor: message.speaker.actor }),
|
|
||||||
content: newContent,
|
|
||||||
type: CONST.CHAT_MESSAGE_STYLES.OTHER,
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,156 +0,0 @@
|
|||||||
/**
|
|
||||||
* Extend the basic ActorSheet with some very simple modifications
|
|
||||||
* @extends {ActorSheet}
|
|
||||||
*/
|
|
||||||
export class NeverStopBlowingUpActorSheet extends ActorSheet {
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
static get defaultOptions()
|
|
||||||
{
|
|
||||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
|
||||||
classes: ["fvtt-never-stop-blowing-up", "sheet", "actor"],
|
|
||||||
width: 800,
|
|
||||||
height: 800,
|
|
||||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "features" }]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
get template()
|
|
||||||
{
|
|
||||||
console.log("template", this.actor)
|
|
||||||
return `systems/fvtt-never-stop-blowing-up/templates/actor/actor-${this.actor.type}-sheet.html`;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
/** @override */
|
|
||||||
async getData()
|
|
||||||
{
|
|
||||||
// Retrieve the data structure from the base sheet.
|
|
||||||
const context = super.getData();
|
|
||||||
|
|
||||||
// Use a safe clone of the actor data for further operations.
|
|
||||||
const actorData = this.document.toObject(false);
|
|
||||||
|
|
||||||
// Add the actor's data to context.data for easier access, as well as flags.
|
|
||||||
context.system = actorData.system;
|
|
||||||
context.flags = actorData.flags;
|
|
||||||
|
|
||||||
// Add roll data for TinyMCE editors.
|
|
||||||
context.rollData = context.actor.getRollData();
|
|
||||||
|
|
||||||
|
|
||||||
console.log(context);
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
activateListeners(html)
|
|
||||||
{
|
|
||||||
super.activateListeners(html);
|
|
||||||
// -------------------------------------------------------------
|
|
||||||
// Everything below here is only needed if the sheet is editable
|
|
||||||
if (!this.isEditable) return;
|
|
||||||
|
|
||||||
// Rollable abilities.
|
|
||||||
html.find('.rollable').click(this._onRoll.bind(this));
|
|
||||||
|
|
||||||
//If the user changes their wand material save that
|
|
||||||
html.find('select[name="system.wand.wood"]').change(event => {
|
|
||||||
const value = event.target.value;
|
|
||||||
this.actor.update({ "system.wand.wood": value });
|
|
||||||
});
|
|
||||||
|
|
||||||
html.find('select[name="system.wand.core"]').change(event => {
|
|
||||||
const value = event.target.value;
|
|
||||||
this.actor.update({ "system.wand.core": value });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle clickable rolls.
|
|
||||||
* @param {Event} event The originating click event
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
async _onRoll(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const element = e.currentTarget;
|
|
||||||
const dataset = element.dataset;
|
|
||||||
|
|
||||||
// Handle rolls that supply the formula directly
|
|
||||||
if (dataset.roll) {
|
|
||||||
let label = dataset.label ? `${dataset.label}` : '';
|
|
||||||
// Get the roll data and include wand bonuses
|
|
||||||
|
|
||||||
let rollData;
|
|
||||||
if(this.actor.type == "character") {
|
|
||||||
rollData = this.actor.getRollDataPC();
|
|
||||||
} else if (this.actor.type == "npc") {
|
|
||||||
rollData = this.actor.getRollDataNPC();
|
|
||||||
} else {
|
|
||||||
console.log("ERROR: UNKNOWN AUTHOR TYPE");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let totalBonus = 0;
|
|
||||||
console.log(dataset.roll);
|
|
||||||
// Apply wood bonus if it matches the stat being rolled for
|
|
||||||
if (rollData.wandBonus.wood.stat === dataset.key) {
|
|
||||||
totalBonus += rollData.wandBonus.wood.bonus;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply core bonus if it matches the stat being rolled for AND it's different from the wood bonus
|
|
||||||
if (rollData.wandBonus.core.stat === dataset.key && rollData.wandBonus.core.stat !== rollData.wandBonus.wood.stat) {
|
|
||||||
totalBonus += rollData.wandBonus.core.bonus;
|
|
||||||
}
|
|
||||||
let rollFormula = dataset.roll + `+${totalBonus}`;
|
|
||||||
let roll = new Roll(rollFormula, rollData);
|
|
||||||
console.log(rollFormula);
|
|
||||||
console.log(rollData);
|
|
||||||
|
|
||||||
// Send the roll message to chat
|
|
||||||
const rollMessage = await roll.toMessage({
|
|
||||||
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
|
|
||||||
flavor: label,
|
|
||||||
rollMode: game.settings.get('core', 'rollMode'),
|
|
||||||
})
|
|
||||||
|
|
||||||
// Now send the follow-up message with the adversity controls
|
|
||||||
await this._sendAdversityControlsMessage(this.actor.id, rollMessage.id);
|
|
||||||
|
|
||||||
return roll;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//This just sends the buttons for the adversity token system
|
|
||||||
async _sendAdversityControlsMessage(actorId, rollMessageId) {
|
|
||||||
// Create the content for the adversity controls
|
|
||||||
const adversityControlsHtml = this._createAdversityControls(actorId, rollMessageId);
|
|
||||||
|
|
||||||
// Send the adversity controls as a follow-up message
|
|
||||||
const controlMessage = await ChatMessage.create({
|
|
||||||
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
|
|
||||||
content: adversityControlsHtml,
|
|
||||||
});
|
|
||||||
|
|
||||||
return controlMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create HTML content for adversity controls
|
|
||||||
_createAdversityControls(actorId, rollMessageId) {
|
|
||||||
return `
|
|
||||||
<div class="adversity-controls" data-roll-id="${rollMessageId}">
|
|
||||||
<button class="take-adversity" data-actor-id="${actorId}">Take Adversity Token</button>
|
|
||||||
<input type="number" class="token-input" value="1" min="1" />
|
|
||||||
<button class="spend-adversity" data-actor-id="${actorId}">Spend Adversity Tokens</button>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
4
modules/dataModel/_system.mjs
Normal file
4
modules/dataModel/_system.mjs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from "./itemModel.mjs";
|
||||||
|
export * from "./dataModel.mjs";
|
||||||
|
export * from "./playerCharacterModel.mjs";
|
||||||
|
export * from "./baseActorModel.mjs"
|
31
modules/dataModel/baseActorModel.mjs
Normal file
31
modules/dataModel/baseActorModel.mjs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import Stat from "./dataModel.mjs"
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Actor base Model */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
export default class ActorGeneral extends foundry.abstract.TypeDataModel
|
||||||
|
{
|
||||||
|
static defineSchema(){
|
||||||
|
return {
|
||||||
|
age: new fields.StringField({required: false}),
|
||||||
|
pronouns: new fields.StringField({required: false}),
|
||||||
|
fear: new fields.StringField({required: false}),
|
||||||
|
grade: new fields.StringField({required: false}),
|
||||||
|
stats: new fields.SchemaField({
|
||||||
|
fight: new fields.EmbeddedDataField(Stat, {required: true, nullable: false, default: new Stat()}),
|
||||||
|
brains: new fields.EmbeddedDataField(Stat, {required: true, nullable: false, default: new Stat()}),
|
||||||
|
charm: new fields.EmbeddedDataField(Stat, {required: true, nullable: false, default: new Stat()}),
|
||||||
|
flight: new fields.EmbeddedDataField(Stat, {required: true, nullable: false, default: new Stat()}),
|
||||||
|
brawn: new fields.EmbeddedDataField(Stat, {required: true, nullable: false, default: new Stat()}),
|
||||||
|
grit: new fields.EmbeddedDataField(Stat, {required: true, nullable: false, default: new Stat()}),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static LOCALISATION_PREFIXES = ["BaseActor"];
|
||||||
|
|
||||||
|
prepareDerivedData() {
|
||||||
|
super.prepareDerivedData();
|
||||||
|
}
|
||||||
|
}
|
71
modules/dataModel/dataModel.mjs
Normal file
71
modules/dataModel/dataModel.mjs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Base Models */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
export default class Stat extends foundry.abstract.DataModel
|
||||||
|
{
|
||||||
|
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
id: new fields.StringField({ required: true, initial: "statID"}),
|
||||||
|
name: new fields.StringField({ required: true, intial: "Stat"}),
|
||||||
|
die: new fields.NumberField({ required: true, nullable: false, initial: "d4"}),
|
||||||
|
modifiers: new fields.ArrayField({ required: true, type: Modifier, default: []}),
|
||||||
|
modifier: new fields.NumberField({required: true, integer: true, initial: 0})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Modifier extends foundry.abstract.DataModel
|
||||||
|
{
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
statID: new fields.StringField({ required: true, initial: "statID"}),
|
||||||
|
name: new fields.StringField({ required: true, initial: "Modifier"}),
|
||||||
|
value: new fields.NumberField({ required: true, integer: true, initial: 0}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Modifier(statID,name,value) {
|
||||||
|
this.statID = statID;
|
||||||
|
this.name = name;
|
||||||
|
this.value = value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Effect Models */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
export class Effect extends foundry.abstract.DataModel
|
||||||
|
{
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
description: new fields.StringField({ required: true, initial: "A EffectDescription" }),
|
||||||
|
modifier: new fields.EmbeddedDataField(Modifier, { required: true, nullable: true, default: null}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Flaw extends foundry.abstract.DataModel
|
||||||
|
{
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
name: new fields.StringField({ required: true, initial: "Flaw"}),
|
||||||
|
description: new fields.StringField({ required: true, initial: "A FlawDescription" })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Strength extends Effect
|
||||||
|
{
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
...super.defineSchema(),
|
||||||
|
name: new fields.StringField({ required: true, initial: "Strength"})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
45
modules/dataModel/itemModel.mjs
Normal file
45
modules/dataModel/itemModel.mjs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import Effect from "./dataModel.mjs";
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Item Models */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* How this will work is when we first load a sheet we load all the items we have and take their effects and apply them to our stats. */
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
export default class KidsOnBroomsItem extends foundry.abstract.TypeDataModel
|
||||||
|
{
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
description: new fields.StringField({ required: true, initial: "An KidsOnBroomsItemDescription" }),
|
||||||
|
effects: new fields.ArrayField({ required: true, type: Effect, default: []}),
|
||||||
|
quantity: new fields.NumberField({required: true, nullable: false, integer: true, initial: 1, min: 0}),
|
||||||
|
price: new fields.NumberField({required: true, nullable: false, integer: true, initial: 0, min: 0}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
KidsOnBroomsItem(name,description,effects) {
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.effects = effects;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Wand extends KidsOnBroomsItem
|
||||||
|
{
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
wood: new fields.EmbeddedDataField(KidsOnBroomsItem, { required: true, nullable: true, default: null}), //These are just KidsOnBroomsItems!
|
||||||
|
core: new fields.EmbeddedDataField(KidsOnBroomsItem, { required: true, nullable: true, default: null}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Broom extends KidsOnBroomsItem
|
||||||
|
{
|
||||||
|
static defineSchema(){
|
||||||
|
return {
|
||||||
|
look: new fields.StringField({required: true, initial: "A broom"}),
|
||||||
|
mechanicalBenefit: new fields.EmbeddedDataField(Effect, {required: false})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
48
modules/dataModel/playerCharacterModel.mjs
Normal file
48
modules/dataModel/playerCharacterModel.mjs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import ActorGeneral from "./baseActorModel.mjs";
|
||||||
|
import {Wand, Broom} from "./itemModel.mjs"
|
||||||
|
import KidsOnBroomsItem from "./itemModel.mjs"
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* PC Model */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
export default class PlayerCharacter extends ActorGeneral
|
||||||
|
{
|
||||||
|
static defineSchema(){
|
||||||
|
return {
|
||||||
|
...super.defineSchema(),
|
||||||
|
description: new fields.StringField({required: false, intial: "Enter your characters description here."}),
|
||||||
|
broom: new fields.EmbeddedDataField(Broom, {nullable: true}),
|
||||||
|
wand: new fields.EmbeddedDataField(Wand, {nullable: true}),
|
||||||
|
animalFamiliar: new fields.StringField({required: false, initial: "Describe your companion!"}),
|
||||||
|
schoolbag: new fields.ArrayField({type: KidsOnBroomsItem, default: []}),
|
||||||
|
strengths: new fields.ArrayField({type: Strength, default: []}),
|
||||||
|
adversityTokens: new fields.NumberField({required: true, nullable: false, integer: true, initial: 3, min: 0})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static LOCALISATION_PREFIXES = ["PlayerCharacter"];
|
||||||
|
|
||||||
|
prepareBaseData() {
|
||||||
|
super.prepareBaseData();
|
||||||
|
let effectsToApply = this.gatherEffects();
|
||||||
|
|
||||||
|
effectsToApply.forEach(element => {
|
||||||
|
console.log(element);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
gatherEffects() {
|
||||||
|
let effectsToApply = new [];
|
||||||
|
|
||||||
|
this.schoolbag.array.forEach(element => {
|
||||||
|
if(element.effects != []) {
|
||||||
|
element.effects.forEach(effect => {
|
||||||
|
effectsToApply.push(effect);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return effectsToApply;
|
||||||
|
}
|
||||||
|
}
|
7
modules/documents/actor.mjs
Normal file
7
modules/documents/actor.mjs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export default class KidsOnBroomsActor extends Actor {
|
||||||
|
constructor(data, context) {
|
||||||
|
super(data, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
1
modules/sheets/_system.mjs
Normal file
1
modules/sheets/_system.mjs
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./actor-sheet.mjs"
|
31
modules/sheets/actor-sheet.mjs
Normal file
31
modules/sheets/actor-sheet.mjs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
const {api, sheets} = foundry.applications;
|
||||||
|
|
||||||
|
|
||||||
|
export default class BaseActorSheet extends api.HandlebarsApplicationMixin(sheets.ActorSheetV2) {
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static get defaultOptions()
|
||||||
|
{
|
||||||
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||||
|
classes: ["kidsonbrooms", "sheet", "actor"],
|
||||||
|
width: 800,
|
||||||
|
height: 800,
|
||||||
|
tabs: [{}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
get template()
|
||||||
|
{
|
||||||
|
console.log("template", this.actor)
|
||||||
|
return `systems/kidsonbrooms/templates/actor/actor-sheet-{$this.actor.type}.html`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getData()
|
||||||
|
{
|
||||||
|
const context = super.getData();
|
||||||
|
|
||||||
|
console.log(context);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
2332
package-lock.json
generated
Normal file
2332
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
39
package.json
39
package.json
@ -1,31 +1,20 @@
|
|||||||
{
|
{
|
||||||
"name": "kidsonbrooms",
|
"name": "kidsonbroomsfoundryvtt",
|
||||||
"version": "1.1.5",
|
"version": "0.1.0",
|
||||||
"description": "CSS compiler for the Kids On Brooms system",
|
"description": "The Kids on Brooms System for Foundry VTT",
|
||||||
|
"main": "kidsonbroomsfoundryvtt.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "gulp build",
|
"sass": "sass --watch scss/kidsOnBrooms.scss css/kidsOnBrooms.css"
|
||||||
"compile": "gulp css",
|
|
||||||
"watch": "gulp",
|
|
||||||
"gulp": "gulp"
|
|
||||||
},
|
|
||||||
"browserslist": [
|
|
||||||
"last 5 versions"
|
|
||||||
],
|
|
||||||
"author": "Joscha Maier",
|
|
||||||
"license": "MIT",
|
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
|
||||||
"form-data": "^4.0.0",
|
|
||||||
"gulp": "^5",
|
|
||||||
"gulp-autoprefixer": "^8",
|
|
||||||
"gulp-replace": "^1.1.4",
|
|
||||||
"gulp-sass": "^5",
|
|
||||||
"gulp-sourcemaps": "^2.6.5",
|
|
||||||
"gulp-zip": "^5.0.1",
|
|
||||||
"kidsonbrooms": "file:",
|
|
||||||
"node-fetch": "^2.7.0"
|
|
||||||
},
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"sass": "^1.79.1"
|
"gulp": "^5.0.0",
|
||||||
|
"mathjs": "^13.1.1",
|
||||||
|
"sass": "^1.79.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"kidsonbroomsfoundryvtt": "file:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
.effects .item {
|
|
||||||
.effect-source,
|
|
||||||
.effect-duration,
|
|
||||||
.effect-controls {
|
|
||||||
text-align: center;
|
|
||||||
border-left: 1px solid #c9c7b8;
|
|
||||||
border-right: 1px solid #c9c7b8;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.effect-controls {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// _effects.scss
|
|
||||||
.kids-on-brooms input:focus,
|
|
||||||
.kids-on-brooms textarea:focus,
|
|
||||||
.kids-on-brooms select:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: $focus-border-color;
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
.item-form {
|
|
||||||
font-family: $font-primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sheet-header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: flex-start; // Use a mixin for flexbox
|
|
||||||
flex: 0 1 auto;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
height: 110px;
|
|
||||||
|
|
||||||
.profile-img {
|
|
||||||
flex: 0 0 100px;
|
|
||||||
height: 100px;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-fields {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1.charname {
|
|
||||||
height: 50px;
|
|
||||||
padding: 0;
|
|
||||||
margin: 5px 0;
|
|
||||||
border-bottom: 0;
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
div.editor-border {
|
|
||||||
border: 2px solid $primary-border-color; // Replace the hardcoded color with a variable
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sheet-tabs {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sheet-body,
|
|
||||||
.sheet-body .tab,
|
|
||||||
.sheet-body .tab .editor {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tox {
|
|
||||||
.tox-editor-container {
|
|
||||||
background: $c-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tox-edit-area {
|
|
||||||
padding: 0 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flexbox container for each row
|
|
||||||
.selection-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between; // Ensures label stays left and input moves to the right
|
|
||||||
align-items: center; // Vertically center the label and input
|
|
||||||
margin-bottom: 10px; // Optional: Add some space between rows
|
|
||||||
}
|
|
@ -1,124 +0,0 @@
|
|||||||
// Section Header
|
|
||||||
.items-header {
|
|
||||||
height: 28px;
|
|
||||||
margin: 2px 0;
|
|
||||||
padding: 0;
|
|
||||||
align-items: center;
|
|
||||||
background: rgba(0, 0, 0, 0.05);
|
|
||||||
border: $border-groove;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-name {
|
|
||||||
font-weight: bold;
|
|
||||||
padding-left: 5px;
|
|
||||||
text-align: left;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Items Lists
|
|
||||||
.items-list {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow-y: auto;
|
|
||||||
scrollbar-width: thin;
|
|
||||||
color: $c-tan;
|
|
||||||
|
|
||||||
// Child lists
|
|
||||||
.item-list {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Individual Item
|
|
||||||
.item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 2px; // Align with the header border
|
|
||||||
border-bottom: 1px solid $c-faint;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Item name and image
|
|
||||||
.item-name {
|
|
||||||
flex: 2;
|
|
||||||
margin: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: 13px;
|
|
||||||
text-align: left;
|
|
||||||
display: flex;
|
|
||||||
color: $c-dark;
|
|
||||||
|
|
||||||
h3, h4 {
|
|
||||||
margin: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-image {
|
|
||||||
flex: 0 0 30px;
|
|
||||||
height: 30px;
|
|
||||||
background-size: 30px;
|
|
||||||
border: none;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Control Buttons
|
|
||||||
.item-controls {
|
|
||||||
display: flex;
|
|
||||||
flex: 0 0 100px;
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center;
|
|
||||||
margin: 0 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Item Properties (like stats or details)
|
|
||||||
.item-prop {
|
|
||||||
text-align: center;
|
|
||||||
border-left: 1px solid $c-faint;
|
|
||||||
border-right: 1px solid $c-faint;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Section Header inside Items List
|
|
||||||
.items-list .items-header {
|
|
||||||
height: 28px;
|
|
||||||
margin: 2px 0;
|
|
||||||
padding: 0;
|
|
||||||
align-items: center;
|
|
||||||
background: rgba(0, 0, 0, 0.05);
|
|
||||||
border: $border-groove;
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-name {
|
|
||||||
padding-left: 5px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional item formula block
|
|
||||||
.item-formula {
|
|
||||||
flex: 0 0 200px;
|
|
||||||
padding: 0 8px;
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
.resource-label {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
// _base.scss
|
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap");
|
|
||||||
|
|
||||||
.window-app {
|
|
||||||
font-family: $font-stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rollable {
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
color: #000;
|
|
||||||
text-shadow: $hover-text-shadow;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
// Flexbox Utility Classes
|
|
||||||
.flex-group-center {
|
|
||||||
@include flexbox(center, center);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-group-left {
|
|
||||||
@include flexbox(flex-start, center);
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-group-right {
|
|
||||||
@include flexbox(flex-end, center);
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexshrink {
|
|
||||||
flex: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-between {
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexlarge {
|
|
||||||
flex: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alignment Utility Classes
|
|
||||||
.align-left {
|
|
||||||
@include flexbox(flex-start, center);
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-right {
|
|
||||||
@include flexbox(flex-end, center);
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-center {
|
|
||||||
@include flexbox(center, center);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only apply the right alignment to specific inputs with this class
|
|
||||||
.right-align-input {
|
|
||||||
flex: 1;
|
|
||||||
margin-left: auto; // Push the input to the far right
|
|
||||||
max-width: 260px; // Optional: Control the width of the input field
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
// _grid.scss
|
|
||||||
.grid {
|
|
||||||
display: grid;
|
|
||||||
gap: 10px;
|
|
||||||
margin: 10px 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@for $i from 2 through 12 {
|
|
||||||
// Create grid-start-* classes for offsets
|
|
||||||
.grid-start-#{$i} {
|
|
||||||
grid-column-start: #{$i};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create grid-span-* classes for column spans
|
|
||||||
.grid-span-#{$i} {
|
|
||||||
grid-column-end: span #{$i};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create grid-*col classes for grid columns
|
|
||||||
.grid-#{$i}col {
|
|
||||||
grid-column: span #{$i} / span #{$i};
|
|
||||||
grid-template-columns: repeat(#{$i}, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
.window-app {
|
|
||||||
font-family: $font-primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rollable {
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
color: #000;
|
|
||||||
text-shadow: 0 0 10px rgb(146, 0, 225);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
0
scss/kidsOnBrooms.scss
Normal file
0
scss/kidsOnBrooms.scss
Normal file
@ -1,27 +0,0 @@
|
|||||||
// Add custom fonts by visiting and search https://fonts.google.com
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
|
|
||||||
// This is the font used for the book, will not buy it but for refrence https://www.myfonts.com/collections/dreadful-font-aiyari?queryId=undefined&index=universal_search_data&objectIDs=5368854002
|
|
||||||
// This is the font used for text https://www.myfonts.com/products/lapidary-333-lapidary-333-434881?queryId=undefined&index=universal_search_data&objectIDs=5468003002
|
|
||||||
// Import utilities.
|
|
||||||
@import 'utils/variables';
|
|
||||||
@import 'utils/typography';
|
|
||||||
@import 'utils/colors';
|
|
||||||
@import 'utils/mixins';
|
|
||||||
|
|
||||||
/* Global styles */
|
|
||||||
@import 'global/window';
|
|
||||||
@import 'global/grid';
|
|
||||||
@import 'global/flex';
|
|
||||||
@import 'global/base';
|
|
||||||
|
|
||||||
.editor-container {
|
|
||||||
min-height: 200px; /* Adjust this value as needed */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Styles limited to kidsonbrooms sheets */
|
|
||||||
.kids-on-brooms {
|
|
||||||
@import 'components/forms';
|
|
||||||
@import 'components/resource';
|
|
||||||
@import 'components/items';
|
|
||||||
@import 'components/effects';
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
$c-white: #fff;
|
|
||||||
$c-black: #000;
|
|
||||||
|
|
||||||
$c-dark: #191813;
|
|
||||||
$c-faint: #c9c7b8;
|
|
||||||
$c-beige: #b5b3a4;
|
|
||||||
$c-tan: #444;
|
|
||||||
|
|
||||||
$primary-border-color: rgb(81, 81, 81);
|
|
||||||
|
|
||||||
$focus-border-color: #8102dd;
|
|
||||||
$hover-text-shadow: 0 0 10px rgb(179, 7, 217);
|
|
||||||
$border-color: #ccc;
|
|
@ -1,25 +0,0 @@
|
|||||||
@mixin element-invisible {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
margin: -1px;
|
|
||||||
border: 0;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
clip: rect(0 0 0 0);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin hide {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the mixin to accept 3 arguments: direction, wrap, and justify
|
|
||||||
@mixin flexbox($direction, $wrap: nowrap, $justify: flex-start, $align: stretch) {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: $direction;
|
|
||||||
flex-wrap: $wrap;
|
|
||||||
justify-content: $justify;
|
|
||||||
align-items: $align;
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
$font-primary: 'Roboto', sans-serif;
|
|
||||||
$font-secondary: 'Roboto', sans-serif;
|
|
@ -1,10 +0,0 @@
|
|||||||
$padding-sm: 5px;
|
|
||||||
$padding-md: 10px;
|
|
||||||
$padding-lg: 20px;
|
|
||||||
|
|
||||||
$border-groove: 2px groove #eeede0;
|
|
||||||
|
|
||||||
$font-stack: "Roboto", sans-serif;
|
|
||||||
$padding: 10px;
|
|
||||||
$border-radius: 5px;
|
|
||||||
$flex-align: center;
|
|
53
system.json
53
system.json
@ -1,26 +1,51 @@
|
|||||||
{
|
{
|
||||||
"id": "fvtt-never-stop-blowing-up",
|
"name": "kidsonbrooms",
|
||||||
"title": "Never Stop Blowing Up",
|
"title": "Kids on Brooms",
|
||||||
"description": "The Never Stop Blowing Up system for FoundryVTT!",
|
"description": "This is a implementation of the Kids on Brooms system in FoundryVTT.",
|
||||||
"version": "12.0.0",
|
"version": "0.1.0",
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"minimum": 12,
|
"minimum": 12,
|
||||||
"verified": 12
|
"verified": 12
|
||||||
},
|
},
|
||||||
|
"esmodules": ["kidsonbrooms.mjs"],
|
||||||
"authors": [{
|
"authors": [{
|
||||||
"name": "Joscha Maier"
|
"name": "Joscha Maier",
|
||||||
},{
|
"url": "https://gitlab.com/wintermyst"
|
||||||
"name": "LeRatierBretonnien"
|
|
||||||
}],
|
}],
|
||||||
"esmodules": ["module/never-stop-blowing-up.mjs"],
|
"documentTypes": {
|
||||||
"styles": ["css/never-stop-blowing-up.css"],
|
"Actor": {
|
||||||
|
"playerCharacter": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"languages": [{
|
||||||
|
"lang": "en",
|
||||||
|
"name": "English",
|
||||||
|
"path": "lang/en.json"
|
||||||
|
}],
|
||||||
|
"packs": [],
|
||||||
|
"packFolders": [],
|
||||||
"socket": true,
|
"socket": true,
|
||||||
"grid": {
|
"url": "https://gitlab.com/wintermyst/kidsonbrooms",
|
||||||
|
"manifest": "https://your/hosted/system/repo/system.json",
|
||||||
|
"download": "https://your/packaged/download/archive.zip",
|
||||||
|
"grid:": {
|
||||||
|
"type": 1,
|
||||||
"distance": 5,
|
"distance": 5,
|
||||||
"units": "ft"
|
"units": "ft"
|
||||||
},
|
},
|
||||||
"primaryTokenAttribute": "system.adversityTokens",
|
"relationships": {
|
||||||
"url": "https://www.uberwald.me/gitea/uberwald/fvtt-never-stop-blowing-up",
|
"requires": [
|
||||||
"manifest": "https://www.uberwald.me/gitea/uberwald/fvtt-never-stop-blowing-up/raw/branch/master/system.json",
|
{
|
||||||
"download": "https://www.uberwald.me/gitea/uberwald/fvtt-never-stop-blowing-up/archive/12.0.0.zip"
|
"id": "lib-wrapper",
|
||||||
|
"type": "module",
|
||||||
|
"compatibility": {
|
||||||
|
"minimum": "1.0.0.0",
|
||||||
|
"verified": "1.12.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"primaryTokenAttribute": "system.adversityTokens"
|
||||||
}
|
}
|
||||||
|
103
template.json
103
template.json
@ -1,103 +0,0 @@
|
|||||||
{
|
|
||||||
"Actor": {
|
|
||||||
"types": ["character", "npc"],
|
|
||||||
"templates": {
|
|
||||||
"base": {
|
|
||||||
"stats": {
|
|
||||||
"stat1": {
|
|
||||||
"name": "fight",
|
|
||||||
"value": "d4",
|
|
||||||
"stat": 0,
|
|
||||||
"magic": 0
|
|
||||||
},
|
|
||||||
"stat2": {
|
|
||||||
"name": "flight",
|
|
||||||
"value": "d4",
|
|
||||||
"stat": 0,
|
|
||||||
"magic": 0
|
|
||||||
},
|
|
||||||
"stat3": {
|
|
||||||
"name": "brains",
|
|
||||||
"value": "d4",
|
|
||||||
"stat": 0,
|
|
||||||
"magic": 0
|
|
||||||
},
|
|
||||||
"stat4": {
|
|
||||||
"name": "brawn",
|
|
||||||
"value": "d4",
|
|
||||||
"stat": 0,
|
|
||||||
"magic": 0
|
|
||||||
},
|
|
||||||
"stat5": {
|
|
||||||
"name": "charm",
|
|
||||||
"value": "d4",
|
|
||||||
"stat": 0,
|
|
||||||
"magic": 0
|
|
||||||
},
|
|
||||||
"stat6": {
|
|
||||||
"name": "grit",
|
|
||||||
"value": "d4",
|
|
||||||
"stat": 0,
|
|
||||||
"magic": 0
|
|
||||||
},
|
|
||||||
"stat7": {
|
|
||||||
"name": "N/A",
|
|
||||||
"value": "d4",
|
|
||||||
"stat": 0,
|
|
||||||
"magic": 0
|
|
||||||
},
|
|
||||||
"stat8": {
|
|
||||||
"name": "N/A",
|
|
||||||
"value": "d4",
|
|
||||||
"stat": 0,
|
|
||||||
"magic": 0
|
|
||||||
},
|
|
||||||
"stat9": {
|
|
||||||
"name": "N/A",
|
|
||||||
"value": "d4",
|
|
||||||
"stat": 0,
|
|
||||||
"magic": 0
|
|
||||||
},
|
|
||||||
"stat10": {
|
|
||||||
"name": "N/A",
|
|
||||||
"value": "d4",
|
|
||||||
"stat": 0,
|
|
||||||
"magic": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"character": {
|
|
||||||
"templates": ["base"],
|
|
||||||
"wounds": {
|
|
||||||
"minor": {"m1": false, "m2": false, "m3": false},
|
|
||||||
"moderate": {"m1": false, "m2": false},
|
|
||||||
"mortal": {"m1": false}
|
|
||||||
},
|
|
||||||
"trope": "",
|
|
||||||
"age": "",
|
|
||||||
"pronouns": "",
|
|
||||||
"fear": "",
|
|
||||||
"motivation": "",
|
|
||||||
"grade":"",
|
|
||||||
"broom": {
|
|
||||||
"name": "",
|
|
||||||
"look": "",
|
|
||||||
"mechanicalbenifit": ""
|
|
||||||
},
|
|
||||||
"wand": {
|
|
||||||
"wood": "",
|
|
||||||
"core": ""
|
|
||||||
},
|
|
||||||
"animalfamiliar":"",
|
|
||||||
"schoolbag": "",
|
|
||||||
"adversityTokens": 0,
|
|
||||||
"tropequestions": "",
|
|
||||||
"strengths": ""
|
|
||||||
},
|
|
||||||
"npc": {
|
|
||||||
"templates": ["base"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
<form class="{{cssClass}} {{actor.type}} flexcol" autocomplete="off">
|
|
||||||
{{!-- Sheet Header --}}
|
|
||||||
<header class="sheet-header">
|
|
||||||
<img class="profile-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" height="100" width="100"/>
|
|
||||||
<div class="header-fields">
|
|
||||||
<h1 class="charname"><input name="name" type="text" value="{{actor.name}}" placeholder="Name"/></h1>
|
|
||||||
|
|
||||||
<div class="resources grid">
|
|
||||||
<div class="resource flex-group-center">
|
|
||||||
<label for="system.trope" class="resource-label">Class</label>
|
|
||||||
<div class="resource-content flexrow flex-center flex-between">
|
|
||||||
<input type="text" name="system.trope" value="{{system.trope}}" data-dtype="String"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
{{!-- Sheet Tab Navigation --}}
|
|
||||||
<nav class="sheet-tabs tabs" data-group="primary">
|
|
||||||
{{!-- Default tab is specified in actor-sheet.mjs --}}
|
|
||||||
<a class="item" data-tab="features">Features</a>
|
|
||||||
<a class="item" data-tab="schoolbag">Inventory</a>
|
|
||||||
<a class="item" data-tab="strengths">Abilities</a>
|
|
||||||
<!-- <a class="item" data-tab="trope">Trope Questions</a> -->
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
{{!-- Sheet Body --}}
|
|
||||||
<section class="sheet-body">
|
|
||||||
|
|
||||||
{{!-- Owned Features Tab --}}
|
|
||||||
<div class="tab features" data-group="primary" data-tab="features">
|
|
||||||
<section class="grid grid-3col">
|
|
||||||
<section class="main grid-span-2">
|
|
||||||
{{> "systems/fvtt-never-stop-blowing-up/templates/actor/parts/actor-features.html"}}
|
|
||||||
{{> "systems/fvtt-never-stop-blowing-up/templates/actor/parts/actor-adversity.html"}}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<aside class="sidebar">
|
|
||||||
{{> "systems/fvtt-never-stop-blowing-up/templates/actor/parts/actor-stats.html"}}
|
|
||||||
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tab schoolbag" data-group="primary" data-tab="schoolbag">
|
|
||||||
{{!-- Schoolbag Tab --}}
|
|
||||||
<div class="tab features editor-border" data-group="primary" data-tab="schoolbag">
|
|
||||||
{{editor schoolbag target="system.schoolbag" engine="prosemirror" button=false collaborate=false editable=true}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{!-- Strengths Tab --}}
|
|
||||||
<div class="tab features editor-border" data-group="primary" data-tab="strengths">
|
|
||||||
{{editor strengths target="system.strengths" engine="prosemirror" button=false collaborate=false editable=true}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{!-- Trope Questions Tab --}}
|
|
||||||
<div class="tab features editor-border" data-group="primary" data-tab="trope">
|
|
||||||
{{editor tropequestions target="system.tropequestions" engine="prosemirror" button=false collaborate=false editable=true}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
</form>
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
|||||||
<form class="{{cssClass}} {{actor.type}} flexcol" autocomplete="off">
|
<form class="{{cssClass}} {{actor.type}} flexcol" autocomplete="off">
|
||||||
|
|
||||||
{{!-- Sheet Header --}}
|
{{!-- Sheet Header --}}
|
||||||
<header class="sheet-header">
|
<header class="sheet-header">
|
||||||
<img class="profile-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" height="100" width="100"/>
|
<img class="profile-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" height="100" width="100"/>
|
||||||
@ -17,19 +16,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
{{!-- Sheet Tab Navigation --}}
|
|
||||||
<nav class="sheet-tabs tabs" data-group="primary">
|
|
||||||
{{!-- Default tab is specified in actor-sheet.mjs --}}
|
|
||||||
<a class="item" data-tab="features">Features</a>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
{{!-- Sheet Body --}}
|
|
||||||
<section class="sheet-body">
|
|
||||||
|
|
||||||
{{!-- Owned Features Tab --}}
|
|
||||||
<div class="tab features" data-group="primary" data-tab="features">
|
|
||||||
{{> "systems/fvtt-never-stop-blowing-up/templates/actor/parts/actor-npc-stats.html"}}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
@ -1,11 +0,0 @@
|
|||||||
<fieldset>
|
|
||||||
<legend>Adversity Tokens</legend>
|
|
||||||
<div class="resource flexcol" >
|
|
||||||
<label for="system.adversity" class="resource-label">
|
|
||||||
Begin the game with 3
|
|
||||||
adversity tokens. Add 1
|
|
||||||
each time you fail a roll.
|
|
||||||
</label>
|
|
||||||
<input type="number" name="system.adversityTokens" value="{{system.adversityTokens}}" data-dtype="Number"/>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
@ -1,183 +0,0 @@
|
|||||||
<section class="grid grid-3col">
|
|
||||||
|
|
||||||
<fieldset class="resource grid-span-3 flexcol">
|
|
||||||
<div class="resource grid-span-3 flexrow">
|
|
||||||
<label for="system.grade" class="resource-label">Species</label>
|
|
||||||
<input type="text" name="system.grade" value="{{system.grade}}" data-dtype="String" />
|
|
||||||
</div>
|
|
||||||
<div class="resource flexrow">
|
|
||||||
<label for="system.age" class="resource-label">Age</label>
|
|
||||||
<input type="text" name="system.age" value="{{system.age}}" data-dtype="String" />
|
|
||||||
</div>
|
|
||||||
<div class="resource grid-span-2 flexrow">
|
|
||||||
<label for="system.pronouns" class="resource-label">Pronouns</label>
|
|
||||||
<input type="text" name="system.pronouns" value="{{system.pronouns}}" data-dtype="String" />
|
|
||||||
</div>
|
|
||||||
<div class="resource grid-span-3 flexrow">
|
|
||||||
<label for="system.fear" class="resource-label">Fear</label>
|
|
||||||
<input type="text" name="system.fear" value="{{system.fear}}" data-dtype="String" />
|
|
||||||
</div>
|
|
||||||
<div class="resource grid-span-3 flexrow">
|
|
||||||
<label for="system.motivation" class="resource-label">Motivation</label>
|
|
||||||
<input type="text" name="system.motivation" value="{{system.motivation}}" data-dtype="String" />
|
|
||||||
</div>
|
|
||||||
<div class="resource grid-span-3 flexrow">
|
|
||||||
<label for="system.description" class="resource-label">Description</label>
|
|
||||||
<input type="text" name="system.description" value="{{system.description}}" data-dtype="String" />
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<!-- <fieldset class="resource grid-span-3 flexcol">
|
|
||||||
<legend>Your Broom</legend> -->
|
|
||||||
|
|
||||||
<!-- Broom Name Input with Dropdown -->
|
|
||||||
<!--
|
|
||||||
<div class="resource flexrow">
|
|
||||||
<label for="broom-name" class="resource-label">Name</label>
|
|
||||||
<input list="broomOptions" id="broom-name" name="system.broom.name" value="{{system.broom.name}}"
|
|
||||||
data-dtype="String" placeholder="Select or Enter Broom Name" oninput="updateBroomDetails()"
|
|
||||||
onblur="updateBroomDetails()">
|
|
||||||
<datalist id="broomOptions">
|
|
||||||
<option value="The Blocker's Broom" data-look="Defensive" data-mechanical="Gain the Guardian Strength"></option>
|
|
||||||
<option value="Bolting 4000" data-look="Fast" data-mechanical="+1 to Flight checks"></option>
|
|
||||||
<option value="The Bruiser" data-look="Intense" data-mechanical="+1 to Fight checks"></option>
|
|
||||||
<option value="Cunning Captain’s Cruiser" data-look="Natural Leader"
|
|
||||||
data-mechanical="Treat Snap Decisions as Planned Actions unless facing fear"></option>
|
|
||||||
<option value="Daredevil’s Duster" data-look="Flashy"
|
|
||||||
data-mechanical="+3 to Charm checks when performing a stunt"></option>
|
|
||||||
<option value="The Daring Dodger 3000" data-look="Ambitious"
|
|
||||||
data-mechanical="Each Adversity Token adds +2 to your roll instead of +1"></option>
|
|
||||||
<option value="Heartwood’s Helper" data-look="Outgoing"
|
|
||||||
data-mechanical="Each successful check grants an ally one Adversity Token"></option>
|
|
||||||
<option value="Mapmaker’s Friend" data-look="Level-Headed"
|
|
||||||
data-mechanical="Cannot get lost if you know the area"></option>
|
|
||||||
<option value="The Mastermind’s Sweeper" data-look="Confident" data-mechanical="+1 to Brains checks"></option>
|
|
||||||
<option value="The Strong Sweep 2500" data-look="Strong" data-mechanical="+1 to Brawn checks"></option>
|
|
||||||
<option value="The Suave Sweeper" data-look="Trustworthy" data-mechanical="+1 to Charm checks"></option>
|
|
||||||
<option value="The Tough Break" data-look="Tough" data-mechanical="+1 to Grit checks"></option>
|
|
||||||
<option value="Valiance 2400" data-look="Brave" data-mechanical="May ignore your fears"></option>
|
|
||||||
<option value="Weasel’s Whisk" data-look="Sneaky" data-mechanical="Gain the Unassuming Strength"></option>
|
|
||||||
</datalist>
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<div class="resource flexrow">
|
|
||||||
<label for="broom-look" class="resource-label">Look</label>
|
|
||||||
<input type="text" id="broom-look" name="system.broom.look" value="{{system.broom.look}}" data-dtype="String" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="resource flexrow">
|
|
||||||
<label for="broom-mechanical" class="resource-label">Mechanical Benefit</label>
|
|
||||||
<textarea id="broom-mechanical" name="system.broom.mechanicalbenefit" data-dtype="String" rows="3"
|
|
||||||
style="resize:none;"></textarea>
|
|
||||||
</div>
|
|
||||||
</fieldset> -->
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function updateBroomDetails() {
|
|
||||||
// Use a short delay to allow browser to properly handle the datalist input
|
|
||||||
setTimeout(function () {
|
|
||||||
const broomNameInput = document.getElementById("broom-name").value.trim();
|
|
||||||
const broomOptions = document.querySelectorAll("#broomOptions option");
|
|
||||||
|
|
||||||
let selectedLook = "";
|
|
||||||
let selectedMechanical = "";
|
|
||||||
|
|
||||||
// Loop through the datalist options to find a matching broom name
|
|
||||||
broomOptions.forEach(option => {
|
|
||||||
if (option.value.toLowerCase() === broomNameInput.toLowerCase()) {
|
|
||||||
selectedLook = option.getAttribute("data-look");
|
|
||||||
selectedMechanical = option.getAttribute("data-mechanical");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update the look and mechanical benefit fields if a predefined broom is selected
|
|
||||||
document.getElementById("broom-look").value = selectedLook || "";
|
|
||||||
document.getElementById("broom-mechanical").value = selectedMechanical || "";
|
|
||||||
}, 100); // Delay of 100 milliseconds
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<fieldset class="resource grid-span-3 flexcol">
|
|
||||||
<legend>Wand Selection</legend>
|
|
||||||
|
|
||||||
<div class="resource-flexrow">
|
|
||||||
<label for="system.wand.wood" class="resource-label">Wood Type</label>
|
|
||||||
<input list="WoodOptions" id="wandWoodChoice" name="system.wand.wood" value="{{system.wand.wood}}"
|
|
||||||
placeholder="Select Wood type" oninput="updateWandWoodDetails()" onblur="updateWandWoodDetails()">
|
|
||||||
<datalist id="WoodOptions">
|
|
||||||
<option value="">Select Wood</option>
|
|
||||||
<option value="Wisteria">(Brains)</option>
|
|
||||||
<option value="Hawthorn">(Brains)</option>
|
|
||||||
<option value="Pine">(Brawn)</option>
|
|
||||||
<option value="Oak">(Brawn)</option>
|
|
||||||
<option value="Crabapple">(Fight)</option>
|
|
||||||
<option value="Dogwood">(Fight)</option>
|
|
||||||
<option value="Birch">(Flight)</option>
|
|
||||||
<option value="Bamboo">(Flight)</option>
|
|
||||||
<option value="Ironwood">(Grit)</option>
|
|
||||||
<option value="Maple">(Grit)</option>
|
|
||||||
<option value="Lilac">(Charm)</option>
|
|
||||||
<option value="Cherry">(Charm)</option>
|
|
||||||
</datalist>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="resource-flexrow">
|
|
||||||
<label for="system.wand.core" class="resource-label">Core Type</label>
|
|
||||||
<input list="CoreOptions" id="wandCoreChoice" name="system.wand.core" value="{{system.wand.core}}"
|
|
||||||
placeholder="Select Core type" oninput="updateWandCoreDetails()" onblur="updateWandCoreDetails()">
|
|
||||||
<datalist id="CoreOptions">
|
|
||||||
<option value="">Select Core</option>
|
|
||||||
<option value="Parchment">(Brains)</option>
|
|
||||||
<option value="Phoenix Feather">(Brains)</option>
|
|
||||||
<option value="Owl Feather">(Brains)</option>
|
|
||||||
<option value="Gorilla Fur">(Brawn)</option>
|
|
||||||
<option value="Ogre’s Fingernail">(Brawn)</option>
|
|
||||||
<option value="Hippo’s Tooth">(Brawn)</option>
|
|
||||||
<option value="Dragon’s Heartstring">(Fight)</option>
|
|
||||||
<option value="Wolf’s Tooth">(Fight)</option>
|
|
||||||
<option value="Elk’s Antler">(Fight)</option>
|
|
||||||
<option value="Hawk’s Feather">(Flight)</option>
|
|
||||||
<option value="Bat’s Bone">(Flight)</option>
|
|
||||||
<option value="Changeling’s Hair">(Charm)</option>
|
|
||||||
<option value="Gold">(Charm)</option>
|
|
||||||
<option value="Mirror">(Charm)</option>
|
|
||||||
<option value="Steel">(Grit)</option>
|
|
||||||
<option value="Diamond">(Grit)</option>
|
|
||||||
<option value="Lion’s Mane">(Grit)</option>
|
|
||||||
</datalist>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset class="resource grid-span-3 flexcol">
|
|
||||||
<legend>Animal Familiar</legend>
|
|
||||||
<div class="resource grid-span-3 flexrow">
|
|
||||||
<label for="system.animalfamiliar" class="resource-label">Animal Familiar</label>
|
|
||||||
<input type="text" name="system.animalfamiliar" value="{{system.animalfamiliar}}" data-dtype="String" />
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
-->
|
|
||||||
<fieldset class="resource grid-span-3 flexcol">
|
|
||||||
<legend>Wounds</legend>
|
|
||||||
<div class="flexrow">
|
|
||||||
<span>Minor</span>
|
|
||||||
{{#each system.wounds.minor as |minorWound key|}}
|
|
||||||
<input type="checkbox" name="system.wounds.minor.{{key}}" {{checked minorWound}} />
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
<div class="flexrow">
|
|
||||||
<span>Moderate</span>
|
|
||||||
{{#each system.wounds.moderate as |minorWound key|}}
|
|
||||||
<input type="checkbox" name="system.wounds.moderate.{{key}}" {{checked minorWound}} />
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
<div class="flexrow">
|
|
||||||
<span>Mortal</span>
|
|
||||||
{{#each system.wounds.mortal as |minorWound key|}}
|
|
||||||
<input type="checkbox" name="system.wounds.mortal.{{key}}" {{checked minorWound}} />
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
</section>
|
|
@ -1,31 +0,0 @@
|
|||||||
<section class="flexcol">
|
|
||||||
{{#each system.stats as |stat key|}}
|
|
||||||
<Fieldset class="grid grid-5col">
|
|
||||||
<legend>{{stat.name}}</legend>
|
|
||||||
<select name="system.stats.{{key}}.value">
|
|
||||||
{{#select stat.value}}
|
|
||||||
<option value="d20">d20</option>
|
|
||||||
<option value="d12">d12</option>
|
|
||||||
<option value="d10">d10</option>
|
|
||||||
<option value="d8">d8</option>
|
|
||||||
<option value="d6">d6</option>
|
|
||||||
<option value="d4">d4</option>
|
|
||||||
{{/select}}
|
|
||||||
</select>
|
|
||||||
<div class="flexrow grid-span-4">
|
|
||||||
<Fieldset class="flexrow">
|
|
||||||
<legend>Stat</legend>
|
|
||||||
<span class="ability-mod rollable" data-roll="{{stat.value}}+{{stat.stat}}" data-label="Stat Roll for {{key}}"><i class="fas fa-dice-d20"></i></span>
|
|
||||||
<input type="text" name="system.stats.{{key}}.stat" value="{{stat.stat}}" data-dtype="String"/>
|
|
||||||
</Fieldset>
|
|
||||||
<!--
|
|
||||||
<Fieldset class="flexrow">
|
|
||||||
<legend>Magic</legend>
|
|
||||||
<span class="ability-mod rollable" data-roll="{{stat.value}}+{{stat.magic}}" data-label="Magic Roll for {{key}}"><i class="fas fa-dice-d20"></i></span>
|
|
||||||
<input type="text" name="system.stats.{{key}}.magic" value="{{stat.magic}}" data-dtype="String"/>
|
|
||||||
</Fieldset>
|
|
||||||
-->
|
|
||||||
</div>
|
|
||||||
</Fieldset>
|
|
||||||
{{/each}}
|
|
||||||
</section>
|
|
@ -1,37 +0,0 @@
|
|||||||
<section class="flexcol">
|
|
||||||
{{#each system.stats as |stat key|}}
|
|
||||||
<Fieldset class="flexrow">
|
|
||||||
<legend><input type="text" value="{{capitalizeFirst stat.name}}" name="system.stats.{{key}}.name"></legend>
|
|
||||||
<div class="flexrow flex-group-center">
|
|
||||||
<!-- Die type dropdown -->
|
|
||||||
<select name="system.stats.{{key}}.value">
|
|
||||||
<option value="d20" {{#if (eq stat.value 'd20')}}selected{{/if}}>d20</option>
|
|
||||||
<option value="d12" {{#if (eq stat.value 'd12')}}selected{{/if}}>d12</option>
|
|
||||||
<option value="d10" {{#if (eq stat.value 'd10')}}selected{{/if}}>d10</option>
|
|
||||||
<option value="d8" {{#if (eq stat.value 'd8')}}selected{{/if}}>d8</option>
|
|
||||||
<option value="d6" {{#if (eq stat.value 'd6')}}selected{{/if}}>d6</option>
|
|
||||||
<option value="d4" {{#if (eq stat.value 'd4')}}selected{{/if}}>d4</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- Stat rolling and input -->
|
|
||||||
<Fieldset class="flexrow flex-group-center">
|
|
||||||
<legend>Stat</legend>
|
|
||||||
<span class="ability-mod rollable" data-roll="1{{stat.value}}x+{{stat.stat}}" data-label="Stat Roll for {{key}}" data-key="{{key}}">
|
|
||||||
<i class="fas fa-dice-d20"></i>
|
|
||||||
</span>
|
|
||||||
<input type="number" name="system.stats.{{key}}.stat" value="{{stat.stat}}" data-dtype="Number"/>
|
|
||||||
</Fieldset>
|
|
||||||
|
|
||||||
<!-- Magic rolling and input
|
|
||||||
<Fieldset class="flexrow flex-group-center">
|
|
||||||
<legend>Magic</legend>
|
|
||||||
<span class="ability-mod rollable" data-roll="1{{stat.value}}x+1d4x+{{stat.stat}}" data-label="Magic Roll for {{key}}" data-key="{{key}}">
|
|
||||||
<i class="fas fa-dice-d20"></i>
|
|
||||||
</span>
|
|
||||||
</Fieldset>-->
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</Fieldset>
|
|
||||||
{{/each}}
|
|
||||||
</section>
|
|
0
templates/item/item-sheet.html
Normal file
0
templates/item/item-sheet.html
Normal file
Loading…
Reference in New Issue
Block a user