Compare commits

...

1422 Commits

Author SHA1 Message Date
Stefan Zermatten
719af548f0 Merge branch 'version-2-dev' into version-2 2022-08-15 15:42:54 +02:00
Stefan Zermatten
f2a1861279 Fixed Slot cards not using markdown 2022-08-15 15:38:57 +02:00
Stefan Zermatten
38c3b6ff1f Fixed tier paid benefit error text 2022-08-15 15:38:36 +02:00
Stefan Zermatten
23e848fe40 Fixed hit dice recovery calculation on long rest 2022-08-15 15:12:14 +02:00
Stefan Zermatten
4d6cdf50bd Fixed tree search input missing types 2022-08-15 14:47:36 +02:00
Stefan Zermatten
1cf9f3b5fd Fixed conditional benefits on abilities not showing on skills 2022-08-15 14:36:20 +02:00
Stefan Zermatten
8164b79667 Improved markdown formatting
Fixed pre-code breaking out of containers
2022-08-15 14:19:42 +02:00
Stefan Zermatten
360df79004 Fixed after save trigger not firing when no targets 2022-08-15 12:31:56 +02:00
Stefan Zermatten
a8f163ff33 Removed trigger.summary 2022-08-15 12:29:58 +02:00
Stefan Zermatten
36b3b80850 Moved triggers in action props to run before children 2022-08-15 12:07:57 +02:00
Stefan Zermatten
1d22f4c054 Hid tags on trigger form when they're not needed 2022-08-15 10:59:35 +02:00
Stefan Zermatten
99e4e8d6bb Fixed some issues with effect tag targeting 2022-08-15 10:53:38 +02:00
Stefan Zermatten
2bb3265356 Fixed client error in creature form 2022-08-15 10:32:27 +02:00
Stefan Zermatten
263f2d8424 Fixed failing tests 2022-08-15 09:38:34 +02:00
Stefan Zermatten
ee0e764294 Refactored entire action engine
Triggers needed action context to function outside of the action engine
proper, so now it's been abstracted into its own class
2022-08-13 00:22:32 +02:00
Stefan Zermatten
13fc0c0b12 Triggers can fire on character sheet checks 2022-08-12 19:52:58 +02:00
Stefan Zermatten
ecfeeaccd9 Breadcrumbs now show when editing property 2022-08-12 19:20:23 +02:00
Stefan Zermatten
b324fb1f03 Stopped triggers from firing if they are inactive 2022-08-12 19:19:58 +02:00
Stefan Zermatten
8d34cc1369 Fixed trigger form hint text mentioning slots instead of trigger 2022-08-12 18:10:24 +02:00
Stefan Zermatten
839c2488b2 Merge branch 'version-2' into version-2-dev 2022-08-12 17:57:18 +02:00
Stefan Zermatten
fd79bc2bb3 Removed empty dependency loop errors 2022-07-26 14:09:51 +02:00
Stefan Zermatten
1050442606 Children of triggers are now inactive on the sheet 2022-07-26 13:43:55 +02:00
Stefan Zermatten
53ed271ea2 Calculation errors moved to the build page
Can be hidden, restyled to improve usability
in light mode
2022-07-26 13:33:05 +02:00
Stefan Zermatten
6ccbf204eb Turned FileColelction logging off for production 2022-07-26 12:04:13 +02:00
Stefan Zermatten
d44d4e0315 Increased timeout and retries on S3 config 2022-07-26 11:53:18 +02:00
Stefan Zermatten
2b8f7e4927 Fixed fillers with type not showing in slot fill 2022-07-26 11:33:47 +02:00
Stefan Zermatten
65e7ce6dce removed unused import 2022-07-26 09:10:24 +02:00
Stefan Zermatten
24cc87d6f7 Fixed deleted libraryNodes being shown in library 2022-07-26 09:07:20 +02:00
Stefan Zermatten
03578b2066 fixed classes without variable names breaking the sheet 2022-07-25 15:21:38 +02:00
Stefan Zermatten
6ea882a053 Fixed trigger conditions not working on rest 2022-07-25 15:13:47 +02:00
Stefan Zermatten
bec65be170 Merge branch 'version-2-dev' into version-2 2022-07-25 09:40:31 +02:00
Stefan Zermatten
0483a7effc Tag targeting attributes and skills with effects
now works like normal effects would
2022-07-24 22:32:40 +02:00
Stefan Zermatten
4c5c537f29 Improved slot tag handling 2022-07-24 21:50:31 +02:00
Stefan Zermatten
a0c2822dac Added "extra" damage type
Takes on the same damage type as the last damage applied during the
same action, otherwise deals "extra" damage
2022-07-24 20:38:42 +02:00
Stefan Zermatten
27a21aed59 Added error alert to stat tab for dependency loops 2022-07-24 19:59:43 +02:00
Stefan Zermatten
1da2d319fb Improved handling of tag targeting 2022-07-24 15:22:07 +02:00
Stefan Zermatten
82879aaa60 Added admin override to view permission 2022-07-24 15:12:12 +02:00
Stefan Zermatten
be654d5d45 Added tree root dialog to breadcrumbs 2022-07-24 14:46:27 +02:00
Stefan Zermatten
f88ffcf0c3 Hardened archive upload slightly 2022-07-24 14:17:39 +02:00
Stefan Zermatten
8b62bac83f Added classes and level up button to build tab 2022-07-24 14:17:27 +02:00
Stefan Zermatten
e698b4b838 Added Dai as Paragon 2022-07-21 09:05:27 +02:00
Stefan Zermatten
566d6a4fef Triggers 🤫 2022-07-20 15:57:38 +02:00
Stefan Zermatten
6f7e742eb9 More of the sheet conforms to library allowances 2022-07-20 00:09:58 +02:00
Stefan Zermatten
0c06f60b7e Fixed typo "Transfer Onwership" 2022-07-19 19:25:12 +02:00
Stefan Zermatten
f8e9131bdd Vastly improved new character UX
Characters now can limit which libraries they allow
2022-07-18 13:45:14 +02:00
Stefan Zermatten
bf9639ae59 Library Collections UI built 2022-07-17 22:48:48 +02:00
Stefan Zermatten
ee89a052bc Added libraryCollections on server 2022-07-13 23:16:25 +02:00
Stefan Zermatten
59ef7527b7 Fixed some errors with character insertion/deletion 2022-07-09 12:53:44 +02:00
Stefan Zermatten
b8a03245ea Level up dialog now working 2022-07-05 15:40:55 +02:00
Stefan Zermatten
1a71c2cfa7 Began implementing class level up UI 2022-07-04 13:55:41 +02:00
Stefan Zermatten
292388dead Iterated on class UI 2022-06-29 15:35:12 +02:00
Stefan Zermatten
00272e7b55 Action cards now show their decendants 2022-06-29 15:27:12 +02:00
Stefan Zermatten
f07f05ca2c Moved creature variables to their own collection
Another big change to the engine, expect bugs
2022-06-29 14:54:25 +02:00
Stefan Zermatten
9dd84a83d2 Started removing scope from creature doc 2022-06-24 10:50:35 +02:00
Stefan Zermatten
b2f89eceee Fixed some bugs with characters not recomputing
TODO: remove .variables cache from creature document, it's not viable
2022-06-23 08:39:48 +02:00
Stefan Zermatten
b484a27637 iterated on class form to match new schema 2022-06-23 08:39:15 +02:00
Stefan Zermatten
da5143693f Iterated on class UI 2022-06-21 11:08:45 +02:00
Stefan Zermatten
9cc4186171 Reduced data load in slot fill dialog 2022-06-21 11:00:50 +02:00
Stefan Zermatten
9f59a6cf86 Fixed long folder names not truncating correctly 2022-06-20 15:11:39 +02:00
Stefan Zermatten
fdaa035bfb Fixed Shakespearean typo in slot filer description 2022-06-20 14:50:04 +02:00
Stefan Zermatten
b31760af0c Don't show children expansion panel if not needed 2022-06-20 14:22:13 +02:00
Stefan Zermatten
a8ffa2f786 Fixed denormalized creature variables not removed 2022-06-20 13:05:38 +02:00
Stefan Zermatten
9b1ec46064 Added children properties to prop edit dialog 2022-06-08 12:22:51 +02:00
Stefan Zermatten
11f373ddd8 Improved slotCard UI 2022-06-07 23:27:14 +02:00
Stefan Zermatten
e7e8f938ed Improved archive storage calculations and errors 2022-06-07 23:21:48 +02:00
Stefan Zermatten
28934baac9 Implemented archive upload handling 2022-06-07 23:01:06 +02:00
Stefan Zermatten
385ac17812 Added big slot cards to build tab, improved build tab 2022-06-07 21:43:35 +02:00
Stefan Zermatten
a04935c5b4 Updated packages 2022-06-07 18:51:07 +02:00
Stefan Zermatten
ccc861b6fa Improved character subscription performance
By limiting fields that can trigger an autorun
2022-05-11 15:52:44 +02:00
Stefan Zermatten
6702f431d0 Fixed bug where removed library nodes kept showing 2022-05-11 15:52:02 +02:00
Stefan Zermatten
1b3efae81a Replaced manual recompute calls with dirty flag settings 2022-05-11 15:42:29 +02:00
Stefan Zermatten
7a35c66904 Removed performance logging from loading creatures 2022-05-11 13:33:56 +02:00
Stefan Zermatten
78cd8ffc8d Creatures are now cached in memory for computation
Also removed dependency group calculation because the optimisation isn't
as useful to reduce DB calls if the creature is in memory anyway
2022-05-11 13:30:33 +02:00
Stefan Zermatten
23fa6fe634 Progress on dependency updates 2022-05-09 16:32:15 +02:00
Stefan Zermatten
caf50d1578 Merge branch 'version-2' into version-2-dev 2022-05-09 12:46:09 +02:00
Stefan Zermatten
df7889edd9 Reduced fields loaded by library tree view
This should improve performance a little for large libraries,
at the expense of loading when a property is selected
2022-05-09 11:23:46 +02:00
Stefan Zermatten
ddc7f87a4a Replaced unsupported cron manager with new one 2022-05-03 11:29:27 +02:00
Stefan Zermatten
33fa22c187 Removed stray log 2022-05-02 23:31:52 +02:00
Stefan Zermatten
2e3f0320f3 Added dependency grouping, but commented out for now until it's needed 2022-05-02 23:31:10 +02:00
Stefan Zermatten
0b7c20e616 Added APM monitoring 2022-05-02 18:48:41 +02:00
Stefan Zermatten
abb8890070 Build card being converted into build tree
Still need to be able to delete fillers
2022-05-02 12:32:14 +02:00
Stefan Zermatten
8dbcae1060 Fixed FAB appearing on wrong tabs 2022-05-01 23:43:31 +02:00
Stefan Zermatten
3a18bce7e6 Added error message for unsupported accessors 2022-05-01 22:54:30 +02:00
Stefan Zermatten
3e97baaaaa Progress on storing user images 2022-04-25 16:16:17 +02:00
Stefan Zermatten
ea32c54f57 Fixed massive writes to creature.variables on calc
Now only writes changed variables, preventing oplog from being
polluted with massive updates
2022-04-25 13:57:39 +02:00
Stefan Zermatten
6b724cf365 Dicecloud instances without db version numbers won't go into migration mode 2022-04-25 11:16:02 +02:00
Stefan Zermatten
8b44c83741 Added archive upload UI 2022-04-23 11:35:11 +02:00
Stefan Zermatten
2ca9ac5342 Added storage stats to the account page 2022-04-23 09:52:20 +02:00
Stefan Zermatten
7609e916c6 Implemented remove button on archive files 2022-04-23 09:35:29 +02:00
Stefan Zermatten
f440e030cf Fixed bug in generating error messages when out of character slots 2022-04-22 11:31:59 +02:00
Stefan Zermatten
13b6689ba4 Progress on file system UI 2022-04-22 11:30:59 +02:00
Stefan Zermatten
b28bcbe079 Work on general UI for user files 2022-04-21 22:08:18 +02:00
Stefan Zermatten
ffa6353a3f Merge branch 'version-2' into version-2-dev 2022-04-21 20:25:23 +02:00
Stefan Zermatten
33f60c2c6d Merge branch 'version-2' of https://github.com/ThaumRystra/DiceCloud into version-2 2022-04-21 10:59:22 +02:00
Stefan Zermatten
dcc95486b3 fixed parser error when using incorrect call args 2022-04-21 10:59:19 +02:00
Stefan Zermatten
88bc223daa Began working on build tab 2022-04-20 13:56:06 +02:00
Stefan Zermatten
cbc42f8500 fix: buff descriptions aren't being calculated 2022-04-15 18:13:03 +02:00
Stefan Zermatten
cc24690a67 Fixed bug where children of rolls would be applied 2022-04-09 15:02:44 +02:00
Stefan Zermatten
4d5cb3ed50 Merge branch 'version-2-dev' into version-2 2022-04-08 18:41:48 +02:00
Stefan Zermatten
c3d9ee7589 Default health bars are now the app's primary color 2022-04-08 18:40:53 +02:00
Stefan Zermatten
d9f572504d Merge branch 'version-2' into version-2-dev 2022-04-08 17:59:20 +02:00
Stefan Zermatten
cb80f3a6da Custom health bar colors. Also works for setting mid and low health colors 2022-04-08 17:58:45 +02:00
Stefan Zermatten
e89d415e7e Fixed error handling of compute engine crashes 2022-03-09 08:24:55 +02:00
Stefan Zermatten
ac8f19bcfb Hotfix for skills without effects breaking calculations 2022-03-09 08:09:16 +02:00
Stefan Zermatten
788cbb182d Action system improvements
- Actions/spells now display their summary, not their description
- All save branches and attack branches run when there are no targets
- Improved action logging
- Index branch lets you customise a choice of children to run
2022-03-09 01:31:09 +02:00
Stefan Zermatten
c68667be9c Added data validation diagnostics for offline use 2022-03-08 15:04:51 +02:00
Stefan Zermatten
fada07e048 Improved handling of poorly migrated archive creatures 2022-03-08 14:12:11 +02:00
Stefan Zermatten
12fc9b1be3 Added summary field back to spell form 2022-03-08 13:17:39 +02:00
Stefan Zermatten
e7f718c785 Prevented updates from running on the server if they fail client validation 2022-03-08 13:15:48 +02:00
Stefan Zermatten
9732db8d67 Hid damage multiplier card if there are no multipliers 2022-03-05 18:56:34 +02:00
Stefan Zermatten
73ca6dc364 Damage multipliers are now applied to damage dealt 2022-03-05 18:40:18 +02:00
Stefan Zermatten
10242b596f Updated test cases to account for new damage multipliers 2022-03-05 17:59:50 +02:00
Stefan Zermatten
782f2cdc73 Added default tags to properties
#type, damageType, skillType, attributeType, reset
2022-03-05 17:52:15 +02:00
Stefan Zermatten
a8ebf6a1de Tags now wrap in damage multiplier viewer 2022-03-05 17:48:52 +02:00
Stefan Zermatten
7dcd0aeff2 Fixed single-select combobox not showing rules errors 2022-03-05 16:30:53 +02:00
Stefan Zermatten
a19e7d0514 Prevented errors from crashing archive restoration 2022-03-05 16:24:42 +02:00
Stefan Zermatten
2442ae4fa0 Overhauled damage multipliers UX
Form and viewer revamp
custom damage types
Variables: `bludgeoning.resistance`
2022-03-05 16:23:21 +02:00
Stefan Zermatten
545050cfa3 Fixed attack disadvantage being treated as advantage 2022-03-04 16:09:00 +02:00
Stefan Zermatten
b876c2801d Greyed out inactive props in the tree 2022-03-04 12:51:48 +02:00
Stefan Zermatten
698c9c7bbf Fixed adjustment error when trying to adjust a property that isn't set 2022-03-04 12:51:28 +02:00
Stefan Zermatten
7544243640 Fixed buffs not crystalising variables correctly 2022-03-04 12:51:06 +02:00
Stefan Zermatten
4b4e3a8928 Improve hover highlight UI effects for cards in dark mode
In light mode a change in elevation changes the drop shadow, but this is 
all but invisible in dark mode, so I added a highlight to the cards when 
hovering to show that the card can be expanded with a click
2022-03-03 17:21:59 +02:00
Stefan Zermatten
92a588bfcc Added slotFillerCondition field to class levels, same as in slot filler 2022-03-03 16:19:54 +02:00
Stefan Zermatten
43e956eb6a Fixed skills not obeying baseValue correctly 2022-03-03 15:55:07 +02:00
Stefan Zermatten
c4429f5dd7 Item quantity adjustment button now shows loading while in progress 2022-03-03 15:54:44 +02:00
Stefan Zermatten
4edfe1bcb9 Fixed inventory calculation to include item quantities 2022-03-03 15:53:58 +02:00
Stefan Zermatten
473a9f0253 Inlined a bunch of discord webhook text to help format messages better 2022-02-28 16:55:15 +02:00
Stefan Zermatten
94cdca4f31 Fixed uses left not logging correctly in actions 2022-02-28 16:25:42 +02:00
Stefan Zermatten
10d0a3f763 Added attack roll back to spell form 2022-02-28 16:13:52 +02:00
Stefan Zermatten
afe6c044cd Fixed dependency graph not building correctly for resources consumed 2022-02-28 00:02:55 +02:00
Stefan Zermatten
e6c7d79d7d Overhauled spell casting UX 2022-02-27 22:14:32 +02:00
Stefan Zermatten
49fa9cc470 Fixed parser to allow $ and x.0.thing in variable references 2022-02-26 19:36:56 +02:00
Stefan Zermatten
3646c13355 Merge branch 'version-2-dev' into version-2 2022-02-26 17:35:58 +02:00
Stefan Zermatten
27665e0bdc Finished roll check and roll attack buttons from stats page 2022-02-26 17:35:26 +02:00
Stefan Zermatten
fea29e60b7 Fixed inactive effects showing up on skill detail view 2022-02-26 15:21:08 +02:00
Stefan Zermatten
653f05012a Reversed the order of the creature compute dependency graph traversal
By doing this the traversal happens mostly in tree order, which is a 
better assumption of starting point in cases where there are dependency 
loops
2022-02-26 14:58:38 +02:00
Stefan Zermatten
7ee4a22d77 Fixed error where dependency loops including classLevels break the sheet 2022-02-26 13:06:00 +02:00
Stefan Zermatten
59c69a46a8 Attacks can now be rolled with advantage from the stats tab
TODO the action viewer as well still
2022-02-25 13:44:09 +02:00
Stefan Zermatten
f79a6d98ec Updated meteor 2022-02-25 12:27:59 +02:00
Stefan Zermatten
0ffa736143 Fixed dbv1 migration to match applied data patches 2022-02-25 12:27:52 +02:00
Stefan Zermatten
f1b4071c46 Inline calculation fields now reduce 2022-02-25 12:27:26 +02:00
Stefan Zermatten
249ece352c Fixed missing slot filler description 2022-02-25 10:28:09 +02:00
Stefan Zermatten
4fe3f30090 Merge branch 'version-2' of https://github.com/ThaumRystra/DiceCloud into version-2 2022-02-24 10:59:16 +02:00
Stefan Zermatten
44d3fbc065 Fixed slot filler viewer not having markdown for the description 2022-02-24 10:59:02 +02:00
Stefan Zermatten
b1feb126df Fixed inventory weight and value fields 2022-02-24 02:39:39 +02:00
Stefan Zermatten
69f9636688 Fixed spell lists and class levels not computing inline calculations 2022-02-23 17:01:12 +02:00
Stefan Zermatten
5383804af7 Fixed error with damage failing to apply if existing damage was undefined 2022-02-23 16:17:34 +02:00
Stefan Zermatten
0b8c88daef Began work on buttons to make rolls from the sheet 2022-02-23 16:08:04 +02:00
Stefan Zermatten
5b6bff91a4 Added resolve function to allow users to force a calculation to reduce 2022-02-23 12:58:12 +02:00
Stefan Zermatten
52453b46e9 Fixed experience not appearing as a variable after computation 2022-02-23 11:44:59 +02:00
Stefan Zermatten
78c67a4fd6 Fixed incorrect use of parser toString in places 2022-02-23 11:07:02 +02:00
Stefan Zermatten
90b277e181 Fixed not operator !working 2022-02-22 19:16:03 +02:00
Stefan Zermatten
dc4d0416a2 Fixed spells disabled by toggles still appearing in spell lists 2022-02-22 19:07:40 +02:00
Stefan Zermatten
12a0dff43f Hacked over ddp error that was not updating removed field correctly 2022-02-22 18:31:06 +02:00
Stefan Zermatten
b9f79f1c51 Fixed buffs missing from stats page 2022-02-22 18:10:04 +02:00
Stefan Zermatten
92d32e7cf8 Fixed tag layout in effect viewer for many tags overlapping one another 2022-02-22 18:07:18 +02:00
Stefan Zermatten
80460ceaed Fixed not found calculation warnings showing [object Object]
They were using the wrong "toString" method
2022-02-22 18:02:57 +02:00
Stefan Zermatten
8f30c1419c Fixed slots and slot fillers not calculating their conditions correctly
Also fixes slot fullness calculation
2022-02-22 17:59:12 +02:00
Stefan Zermatten
4c6d70b084 Fixed . in effect stat targets breaking entire sheet 2022-02-22 17:30:45 +02:00
Stefan Zermatten
ee2b400ee6 Fixed spell list card not showing maxPrepared spells correctly 2022-02-22 16:38:50 +02:00
Stefan Zermatten
ef8aafc1a1 Fixed storagepath for production 2022-02-22 13:03:37 +02:00
Stefan Zermatten
b68637e525 Updated node version 2022-02-22 12:31:47 +02:00
Stefan Zermatten
537eb310a8 Merge branch 'version-2-dev' into version-2 2022-02-22 12:07:10 +02:00
Stefan Zermatten
352c4d516d Fixed effect form not showing targetField value when set 2022-02-15 16:19:00 +02:00
Stefan Zermatten
378da71f5d Effects targeting calculations by tag now work in the engine and actions 2022-02-15 15:59:41 +02:00
Stefan Zermatten
e0f621cc44 Added data and UI for effects targeting calculations by tag
Still need to:
- update engine to compute calculations with effects.
- Add UI for effects applied to each calculation
2022-02-14 16:26:49 +02:00
Stefan Zermatten
359f18988c Account functionality extended, API authentication implemented
- Can now add a second email address to your account and delete one of 
your email addresses
- Reset password now works
- Resetting the password of an account without a password set will set 
one
- Email templates overhauled
- Login tokens limited to close previously devastating ($800 database 
bill) security hole
- Login with REST API now works
- Once logged in, authentication of API calls with token works
- Creatures can now be fetched using the API
2022-02-10 19:02:18 +02:00
Stefan Zermatten
3948d20f46 Allowed cross origin requests to REST API 2022-02-10 10:37:23 +02:00
Stefan Zermatten
76982e234c Merge pull request #285 from WeslleyNasRocha/patch-1
tableLookup function was returning a string
2022-02-10 09:22:29 +02:00
Stefan Zermatten
db222362bb Merge pull request #283 from ThaumRystra/dependabot/npm_and_yarn/app/tar-6.1.11
Bump tar from 6.1.6 to 6.1.11 in /app
2022-02-10 09:20:42 +02:00
Stefan Zermatten
7b02899824 Merge pull request #289 from ThaumRystra/dependabot/npm_and_yarn/app/marked-4.0.10
Bump marked from 0.8.2 to 4.0.10 in /app
2022-02-10 09:20:18 +02:00
Stefan Zermatten
15ead403a5 Added UI for action branches 2022-02-09 16:47:38 +02:00
Stefan Zermatten
2bdd60b5e8 Fixed issue where migrating attributes lost their base value calculation 2022-02-09 12:37:16 +02:00
Stefan Zermatten
78c313e3d1 Archives and restore now works to S3 and file system
If a file is stored on the file system and s3 settings later become 
available it is still correctly fetched from the file system.
2022-02-03 11:48:03 +02:00
Stefan Zermatten
2abaa86795 Began work on moving file storage to s3, not working yet 2022-01-19 16:01:07 +02:00
Stefan Zermatten
90820452af Updated packages 2022-01-16 20:27:14 +02:00
dependabot[bot]
3b438c8ba4 Bump marked from 0.8.2 to 4.0.10 in /app
Bumps [marked](https://github.com/markedjs/marked) from 0.8.2 to 4.0.10.
- [Release notes](https://github.com/markedjs/marked/releases)
- [Changelog](https://github.com/markedjs/marked/blob/master/.releaserc.json)
- [Commits](https://github.com/markedjs/marked/compare/v0.8.2...v4.0.10)

---
updated-dependencies:
- dependency-name: marked
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-15 02:25:34 +00:00
Stefan Zermatten
d845a8f17e Added big scary message to future me to backup before migrating data 2021-12-29 14:42:36 +02:00
Stefan Zermatten
ed1873babe App can now go into maintenance mode locking out routing 2021-12-29 14:25:01 +02:00
Stefan Zermatten
2cf19d1ee5 Fixed typo breaking archive restore 2021-12-27 18:04:22 +02:00
Stefan Zermatten
2cd784c92b Updated Meteor to 2.5.1 2021-12-27 17:48:34 +02:00
Stefan Zermatten
dca55d1d00 Reduced bundle size using dynamic imports for Vue components 2021-12-27 16:29:43 +02:00
Stefan Zermatten
1dc03c8a28 Fixed failing test 2021-12-20 12:22:13 +02:00
Stefan Zermatten
510136e07f When restoring archived files, migration happens automatically 2021-12-20 12:20:50 +02:00
Stefan Zermatten
a5f5baf1cc DB archive characters are migrated and moved to file archive on migrate 2021-12-20 11:53:02 +02:00
Stefan Zermatten
1e10d8751b Archive now uses file system instead of collection 2021-12-19 12:20:09 +02:00
Stefan Zermatten
211659f759 Fixed styling of app name on mobile installed versions 2021-12-14 09:48:39 +02:00
Stefan Zermatten
86f5da3ca5 Fixed rolling straight to the log to use new parser interface 2021-12-08 10:24:06 +02:00
Stefan Zermatten
a58fd8860d Fixed descriptions having borders when not appropriate 2021-12-08 09:31:02 +02:00
Stefan Zermatten
6e22e4286f Fixed referencing variable.description in a calculation
Whether the description's inline calculations are calculated or not is 
not defined.
2021-12-08 09:23:29 +02:00
Stefan Zermatten
e34f29f952 Computations now occupy their own nodes on the dependency graph
This mitigates most issues with properties having self-loops, 
particularly in cases like Strength where the value `strength` is used 
in the description of Strength
2021-12-07 21:05:24 +02:00
Stefan Zermatten
6698d2fd74 Added migration to fix caclulation changes
Migrate `.currentValue` to `.value` and `.value` to `.total`
2021-11-18 16:28:30 +02:00
Stefan Zermatten
e3a1eff751 Progress all over the place with viewer, forms, small engine tweaks 2021-10-21 22:18:01 +02:00
Stefan Zermatten
1b5bb981e9 Updated viewers
Action, classlevel, constant, container, damage multiplier, damage, 
effect, feature, folder, item
2021-10-19 17:19:35 +02:00
Stefan Zermatten
d6be0ae9f4 Continued work on UI viewers 2021-10-18 13:46:51 +02:00
Stefan Zermatten
417ff6e210 Buffs no longer have the "applied" field, it was redundant
Because children of actions are always inactive in the new engine, buffs 
that are children of actions are inactive while buffs elsewhere on the 
character sheet are active, making it redundant to keep the extra field
2021-10-18 13:46:38 +02:00
Stefan Zermatten
7a11a4aa22 Improved action viewer, fixed bugs found along the way 2021-10-18 12:48:26 +02:00
Stefan Zermatten
f2369277f1 Fixed bug from misspelling .overridden as .overriden 2021-10-18 11:27:43 +02:00
Stefan Zermatten
bc6c857b6b UI work to improve look and feel of Viewers 2021-10-17 23:28:39 +02:00
Stefan Zermatten
247353f0ed Small progress on tabletop 2021-10-16 19:05:35 +02:00
Stefan Zermatten
ea68cdf86f Migrating UI for new data structures 2021-10-15 11:12:40 +02:00
Stefan Zermatten
f3c52999e8 Fixed action rewrite build errors 2021-10-10 20:08:29 +02:00
Stefan Zermatten
eebfbfd636 Refactored tabletop methods 2021-10-10 19:58:41 +02:00
Stefan Zermatten
51d3fbbcb7 Completed first pass at action system re-write. Untested 2021-10-10 19:44:02 +02:00
Stefan Zermatten
0cdec4a429 Start of action system re-write 2021-10-09 12:36:06 +02:00
Stefan Zermatten
0097696cc8 Began merging attacks into actions 2021-10-06 14:57:13 +02:00
Weslley Rocha
b9008314a2 converting string to number 2021-10-05 10:14:40 -03:00
Stefan Zermatten
1a14393031 Parsed calculations are now cached between calculations
Parsing is one of the more expensive computations done to characters, so 
the parser results are now stored on the DB and only updated if they are 
dirty. A hash is used to determine if the calculation has changed since 
the last computation
2021-10-03 20:59:04 +02:00
Stefan Zermatten
c2d430ad23 Fixed tableLookup returning string instead of number 2021-10-03 19:21:35 +02:00
Stefan Zermatten
b78517b61f Finished migrating parser to be object orientation free. All tests pass 2021-10-03 13:54:17 +02:00
Stefan Zermatten
d30184434c Merge branch 'version-2-dev' of https://github.com/ThaumRystra/DiceCloud into version-2-dev 2021-10-01 13:41:41 +02:00
Stefan Zermatten
feffa45cf7 Began work on rewriting parser without object orientation
Parsing is expensive, if the parse tree can be stored on the DB it can 
save a lot of compute time, but mongo can't store Classes, so we 
re-write without classes
2021-10-01 13:41:22 +02:00
Stefan Zermatten
7dac0cf3f3 Removed color max length, using regex instead 2021-09-29 18:32:12 +02:00
Stefan Zermatten
cb1fd38df3 Optimized some slow parts of the engine.
Last low hanging fruit: parsing is slow, cache parsed calculations
2021-09-29 15:54:14 +02:00
Stefan Zermatten
cb10b53a10 Updated forms and some UI for new data structure 2021-09-28 14:25:11 +02:00
Stefan Zermatten
b471d0c5cf improved calculation fields 2021-09-27 19:25:11 +02:00
Stefan Zermatten
6dc7e12582 Fixed computation bug for compile level calcs 2021-09-27 18:14:04 +02:00
Stefan Zermatten
b0ee5cd304 Continued iterating on calculations, added failing test for bugs found 2021-09-27 17:26:52 +02:00
Stefan Zermatten
85e8756d1d Fixed parse errors not showing up on calculations 2021-09-27 16:39:44 +02:00
Stefan Zermatten
111040e789 Began migrating the rest of the codebase to the new computation engine 2021-09-27 15:39:18 +02:00
Stefan Zermatten
fdea748441 Tore out the old engine, left some wounds 2021-09-27 14:28:32 +02:00
Stefan Zermatten
2228802dd3 moved v1 migrations to the migrations folder 2021-09-27 13:44:43 +02:00
Stefan Zermatten
fe83b5cbc5 Fixed migration errors when tested against a copy of the live dataset 2021-09-27 13:42:37 +02:00
Stefan Zermatten
5e83a88132 Added migrations for all properties 2021-09-27 11:21:10 +02:00
Stefan Zermatten
091e367d27 Fixed suggested parent of class levels to be class 2021-09-27 11:20:40 +02:00
Stefan Zermatten
706303862f Fixed class level not having computed description 2021-09-27 11:06:48 +02:00
Weslley Nascimento Rocha
875b2f7c04 tableLookup function was returning a string
tableLookup function was returning a string instead of number
2021-09-25 16:36:59 -03:00
Stefan Zermatten
2cb14146d4 Tested and fixed skill compuations 2021-09-23 15:44:50 +02:00
Stefan Zermatten
8ea04fc786 Implemented effect and proficiency inheritance from abilities to skills 2021-09-23 15:22:28 +02:00
Stefan Zermatten
c0a9a1251d Tested and fixed effect computations 2021-09-23 15:22:01 +02:00
Stefan Zermatten
2f893710e2 Tested and fixed damage multiplier computations 2021-09-23 13:40:11 +02:00
Stefan Zermatten
734df59fd1 Tested and fixed inventory computation 2021-09-23 12:41:03 +02:00
Stefan Zermatten
e63dd2560a tested and fixed constant node computations 2021-09-23 12:03:25 +02:00
Stefan Zermatten
347bd8e476 Tested and fixed class level computations 2021-09-23 11:41:15 +02:00
Stefan Zermatten
1270e320ce Basic testing for attribute calculations 2021-09-17 15:11:29 +02:00
Stefan Zermatten
a660ccc458 Lots of progress testing and fixing computation engine 2021-09-16 14:31:28 +02:00
Stefan Zermatten
dfd7ad4af5 Got tests running on single property character 2021-09-15 15:15:18 +02:00
Stefan Zermatten
856fc41429 Began the long road of testing computations 2021-09-14 22:48:30 +02:00
Stefan Zermatten
8f93179187 Moved inventory computation to after toggles, added class levels computation 2021-09-14 16:18:36 +02:00
Stefan Zermatten
5c84836238 More engine rewrite progress, starting to get messy again 2021-09-13 16:12:04 +02:00
Stefan Zermatten
b877a8b45f Computation engine rewrite continues 2021-09-10 19:51:03 +02:00
Stefan Zermatten
28ec7082ee Fixed typo in slot form 2021-09-10 17:14:39 +02:00
Stefan Zermatten
55bca633fc Substantial progress on rebuilding computation engine 2021-09-09 13:47:41 +02:00
Stefan Zermatten
23e99565dc Started inventory calculation 2021-09-09 01:31:20 +02:00
Stefan Zermatten
06da15c44a Began rebuilding computation engine to be dependency graph centric 2021-09-08 17:23:00 +02:00
Stefan Zermatten
43f056ae95 more small progress on migration 2021-09-07 15:48:51 +02:00
Stefan Zermatten
b0980d26ac Iterated on migration :( 2021-09-06 23:59:52 +02:00
Stefan Zermatten
e79b8fda3b Improved migration code substantially, wrote migrations for more properties 2021-09-06 17:40:57 +02:00
Stefan Zermatten
235560eb44 Started on DBv1 migration 2021-09-06 11:36:42 +02:00
Stefan Zermatten
fc0cc6e689 References now merge children, fixed infinite reference loops 2021-09-05 18:51:29 +02:00
dependabot[bot]
c9d4d874aa Bump tar from 6.1.6 to 6.1.11 in /app
Bumps [tar](https://github.com/npm/node-tar) from 6.1.6 to 6.1.11.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v6.1.6...v6.1.11)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-31 18:45:06 +00:00
Stefan Zermatten
7f95680559 Updated thanks page to reflect that Sam is no longer my fiancee, since we're married now <3 2021-08-30 16:32:31 +02:00
Stefan Zermatten
6e51df363b Added increment buttons to all attributes with a value 2021-08-27 13:21:08 +02:00
Stefan Zermatten
608ab4e588 Added undo delete and recycle bin to library 2021-08-27 13:00:01 +02:00
Stefan Zermatten
518880fa5c Fixed error where searching for properties to insert while having other properties selected could prevent any insert from happening at all 2021-08-27 12:24:01 +02:00
Stefan Zermatten
f043c41e12 Fixed fab being confused by hiding the spells tab 2021-08-14 10:44:17 +02:00
Stefan Zermatten
0277de76c4 Removed stray console log 2021-08-12 19:56:01 +02:00
Stefan Zermatten
ffc3211ff9 Fixed some issues with slot filler searching 2021-08-12 18:28:53 +02:00
Stefan Zermatten
8162c76185 Added basic ownership transfer for shared documents 2021-08-10 19:01:31 +02:00
Stefan Zermatten
e21586e9ce Added reasonable storage limits to most string and array schemas 2021-08-10 18:12:55 +02:00
Stefan Zermatten
4c2155d8ff Buffs applied property can now be set in both library and character 2021-08-10 17:02:27 +02:00
Stefan Zermatten
44cc46ed22 Added the ability to add buffs as already applied straight from a library 2021-08-10 16:47:05 +02:00
Stefan Zermatten
d2b5d5f01d Fixed swiping between tabs when spells tab is hidden 2021-08-10 15:20:04 +02:00
Stefan Zermatten
4492c47b00 Fixed item tree node view only using quantity display if quantity is a number 2021-08-10 15:03:30 +02:00
Stefan Zermatten
6600cea9fa Fixed references display in tree views 2021-08-10 15:02:24 +02:00
Stefan Zermatten
741a9b080a Finished implementing new slot fill dialog, including ability to test slots 2021-08-10 14:35:27 +02:00
Stefan Zermatten
b041db22e4 Merge branch 'version-2' into version-2-dev 2021-08-10 13:31:32 +02:00
Stefan Zermatten
a465e2ce87 Continued implementing new slot dialog 2021-08-10 13:29:11 +02:00
Stefan Zermatten
8ecefb03ad Started re-implementing slot fill dialog with different design pattern 2021-08-10 10:32:56 +02:00
Stefan Zermatten
9f62a78eb0 Began work on implementing string and array storage limits 2021-08-09 22:34:04 +02:00
Stefan Zermatten
16e2b1249f Increased slot filler publication load quantity to 50 2021-08-09 20:54:29 +02:00
Stefan Zermatten
a35f9221a2 Fixed granting XP being accidentally locked behind patreon paid benefits 2021-08-09 18:06:04 +02:00
Stefan Zermatten
6364549d50 Added pretty url's from v1 2021-08-09 18:00:55 +02:00
Stefan Zermatten
d999fb46a7 Fixed a warning about source map failure when trying to define a scss variable 2021-08-09 17:57:35 +02:00
Stefan Zermatten
ec01a2adb5 Merge pull request #274 from GuillaumeDerval/version-2
Fix items/buffs/... refresh after casting a spell
2021-08-09 17:11:41 +02:00
Guillaume Derval
1f64558100 Fixed items/buffs/... refresh after casting a spell 2021-08-07 23:20:26 +02:00
Stefan Zermatten
19a2798bf7 Fixed tree item search highlighting in dark mode 2021-08-02 01:13:15 +02:00
Stefan Zermatten
a5f2c2e0d2 Removed duplicate property button on tree tab 2021-08-02 00:54:59 +02:00
Stefan Zermatten
ee174210fd Added search to library tree views 2021-08-02 00:29:56 +02:00
Stefan Zermatten
1e38295164 All properties added to the sheet now use the type/library/create UX 2021-08-01 23:28:04 +02:00
Stefan Zermatten
758cb2f8bc Fixed search icons 2021-07-31 21:53:18 +02:00
Stefan Zermatten
36bb3c3181 New UX for inserting properties from libraries including text search and multi-add 2021-07-31 21:49:15 +02:00
Stefan Zermatten
02434de34c Drastically improved tree tab search UX for locating parts of the sheet 2021-07-31 15:19:54 +02:00
Stefan Zermatten
0dc0bea53e fixes #262, emails from DiceCloud should now be from no-reply@dicecloud.com 2021-07-27 16:59:09 +02:00
Stefan Zermatten
c392119430 Added suggested parents to PROPERTIES for use later with user guidance 2021-07-27 16:36:19 +02:00
Stefan Zermatten
4e2e0ca364 Improved display of referenced properties 2021-07-27 16:21:55 +02:00
Stefan Zermatten
4a8b72f163 Made sure the unsubscribe button always shows on libraries you are subscribed to 2021-07-27 15:51:09 +02:00
Stefan Zermatten
d916dc2b78 Removed add library node buttons from libraries the user deosn't have edit permissions for 2021-07-27 15:44:26 +02:00
Stefan Zermatten
56860ba96d Fixed proficiency Tree node view not showing the name if it is defined 2021-07-27 15:40:16 +02:00
Stefan Zermatten
b607755f9f Fixed attribute base value calculation errors not being cleared if no new errors were made 2021-07-27 15:37:57 +02:00
Stefan Zermatten
86d8fa4325 Fixed items not animating correctly on insert 2021-07-27 15:33:41 +02:00
Stefan Zermatten
2b08249e5e removed lazy from v-menu which caused a console warning 2021-07-27 15:29:05 +02:00
Stefan Zermatten
3133e664d5 Reduced stats computation precision to round off small decimal floating point errors 2021-07-27 15:28:44 +02:00
Stefan Zermatten
48f32e0a8d Removed floating point small decimal oddities in parts of inventory tab 2021-07-27 15:21:35 +02:00
Stefan Zermatten
c72785c9e7 Added components to spell viewer 2021-07-27 15:00:10 +02:00
Stefan Zermatten
421ff2aa7d Fixed DISABLE_PATREON not working, it's now a Meteor setting instead of an ENV variable 2021-07-27 14:31:54 +02:00
Stefan Zermatten
9a9e6491b9 Improved usability with better hints in property forms and property type selection 2021-07-26 18:19:29 +02:00
Stefan Zermatten
332258705c Added service worker 2021-07-21 13:44:55 +02:00
Stefan Zermatten
73ef109d4d Added calculation errors that were missing 2021-07-20 10:37:32 +02:00
Stefan Zermatten
fc240a34c4 Changed tiers to their open beta configuration 2021-07-17 12:50:38 +02:00
Stefan Zermatten
8ac4028f38 Removed limit from guest tiers for closed beta 2021-07-14 00:59:32 +02:00
Stefan Zermatten
2849df1974 Merge branch 'version-2-dev' into version-2 2021-07-13 12:45:59 +02:00
Stefan Zermatten
3fa2cca7ae Locked dark mode to paid accounts only 2021-07-13 12:39:56 +02:00
Stefan Zermatten
a0b53af6d7 Fixed awkward padding in character limit alert 2021-07-13 12:25:15 +02:00
Stefan Zermatten
51c709f7a5 Changed tier limitations for closed beta 2021-07-13 12:23:56 +02:00
Stefan Zermatten
28d67409aa Changed some patreon nudging 2021-07-13 12:23:28 +02:00
Stefan Zermatten
7e97bcb6d8 Updated packages 2021-07-13 12:04:36 +02:00
Stefan Zermatten
4e87737a3e Fixed add-property button not re-appearing when adding a reference property 2021-07-12 18:11:48 +02:00
Stefan Zermatten
822fa4619f Fixed missing icons in calculation errors 2021-07-12 18:00:21 +02:00
Stefan Zermatten
80ba44a28f Added ancestor references to action context so that #spellList references work, closes #270 2021-07-12 17:56:35 +02:00
Stefan Zermatten
c3c079731e Fixed equipment creating ghost items on drag if the equipment folder is deleted from the character 2021-07-12 17:13:18 +02:00
Stefan Zermatten
039b7046b2 Fixed attributes not hiding when redundant 2021-07-12 17:03:32 +02:00
Stefan Zermatten
8eaad3600f Fixed computation error with base values 2021-07-12 14:56:12 +02:00
Stefan Zermatten
5cf78932e6 removed stray debug text 2021-07-12 14:47:41 +02:00
Stefan Zermatten
d43d364175 Removed stray console log 2021-07-12 14:46:37 +02:00
Stefan Zermatten
0ad4c71189 Fixed some missing icons 2021-07-12 14:45:24 +02:00
Stefan Zermatten
e8c6f26a0b fix skills UI bugs and icon consistency for skills 2021-07-12 14:39:05 +02:00
Stefan Zermatten
8804c80a56 Fixed skills not showing their base value in the effects list 2021-07-12 14:15:17 +02:00
Stefan Zermatten
0d21ab758e Fixed negative base values being ignored 2021-07-12 14:12:20 +02:00
Stefan Zermatten
2ecb0e2671 Fix: DC missing from spell list viewer in library 2021-07-12 13:39:08 +02:00
Stefan Zermatten
f3b9b62486 Fixed styling of inventory for denser item list tiles 2021-07-12 12:50:56 +02:00
Stefan Zermatten
29a4575760 Fixed a stray empty list item action taking space on creatures in lists 2021-06-22 15:19:46 +02:00
Stefan Zermatten
a6fbf71b36 Added folders to the sidebar character list 2021-06-22 15:13:59 +02:00
Stefan Zermatten
86d9383af0 Archive UI 2021-06-22 14:59:18 +02:00
Stefan Zermatten
3db589f775 Fixed button property for new Vuetify version 2021-06-22 14:59:02 +02:00
Stefan Zermatten
e96755927f Added archive dialog, empty for now 2021-06-21 16:42:47 +02:00
Stefan Zermatten
66dc0ee34f Added icon to indicate sharing status of characters 2021-06-21 16:37:02 +02:00
Stefan Zermatten
54bf21c57c Edit permission is no longer patreon-only 2021-06-21 16:32:24 +02:00
Stefan Zermatten
5f5fe801f6 Added method to transfer character ownership 2021-06-21 16:06:29 +02:00
Stefan Zermatten
a451afcbaf Prevented new characters being added if you are at your character limit 2021-06-21 16:06:10 +02:00
Stefan Zermatten
848e961e3b Refactored creature methods to their own folders 2021-06-21 15:20:04 +02:00
Stefan Zermatten
c5aca81131 Removed stray console logs 2021-06-20 13:35:23 +02:00
Stefan Zermatten
814e371148 Can now move creatures between folders using drag and drop 2021-06-20 13:32:28 +02:00
Stefan Zermatten
69f4bbf360 Added CRUD API and UI for creature folders 2021-06-20 12:41:08 +02:00
Stefan Zermatten
6b2d74a165 Fixed parties -> creatureFolders publications and ui 2021-06-18 12:59:39 +02:00
Stefan Zermatten
cf05aea80a Fixed Parties references 2021-06-18 10:49:01 +02:00
Stefan Zermatten
1a2d4b22bb Renamed parties to creatureFolders 2021-06-18 10:48:12 +02:00
Stefan Zermatten
81d52a1847 Enabled editing of attribute damage in library forms 2021-06-18 10:43:27 +02:00
Stefan Zermatten
e3fc56a844 Added backend architecture to archive and restore creatures. 2021-06-11 12:03:31 +02:00
Stefan Zermatten
64fceb9c38 Added environmental variables to readme for self-hosting 2021-06-10 15:19:14 +02:00
Stefan Zermatten
9994c1f32a New users now get subscribed to the default libraries as defined by env 2021-06-10 15:18:54 +02:00
Stefan Zermatten
7056c5b37b Added character slot limitations to tiers; added no-patreon tier for self hosting 2021-06-10 12:25:17 +02:00
Stefan Zermatten
1ad1d1f23d Migrated from Google material design icons to vuetify default MDI 2021-06-01 12:34:51 +02:00
Stefan Zermatten
c65c8f3299 Added note to attempt to keep children of reference nodes 2021-04-29 16:13:22 +02:00
Stefan Zermatten
4faea42371 Merge branch 'version-2-dev' into version-2 2021-04-29 15:53:24 +02:00
Stefan Zermatten
9825872576 Implemented Reference properties 2021-04-29 15:52:24 +02:00
Stefan Zermatten
85b536bc46 Added default array for stat proficiencies as well 2021-04-29 11:52:47 +02:00
Stefan Zermatten
9aa8203dcc Fixed bug where effects in stat computation could be undefined 2021-04-29 11:50:16 +02:00
Stefan Zermatten
217133137b Added note to improve query performance with root ancestor targeting 2021-04-29 11:34:58 +02:00
Stefan Zermatten
aef7dbcbb3 Fixed bug in stat computation dependency tracking 2021-04-29 11:22:13 +02:00
Stefan Zermatten
6ff750417f Fixed error in stat computation 2021-04-24 23:25:58 +02:00
Stefan Zermatten
a9eacfab03 Unprepared spells without lists now correctly show up when unprepared 2021-04-22 16:06:31 +02:00
Stefan Zermatten
1f633621b7 Fixed a bug with functions accepting rolled arguments 2021-04-22 15:59:12 +02:00
Stefan Zermatten
9f3c8bef34 Removed stray console log 2021-04-22 15:54:41 +02:00
Stefan Zermatten
8a83e7d8a1 Fixed back button appearing in embedded dialogs 2021-04-22 15:42:44 +02:00
Stefan Zermatten
a28182f3e9 Added missing half rounded down icon for skills in stats tab 2021-04-22 15:40:26 +02:00
Stefan Zermatten
3d122e062f Added the distinction between half rounded up or down for proficiencies 2021-04-22 15:39:14 +02:00
Stefan Zermatten
e9a273244a Improved Effect and Proficiency UI in attribute and skill viewers 2021-04-22 15:12:49 +02:00
Stefan Zermatten
1de3122254 Updated UI to hide extra attributes and skills with same variable name 2021-04-22 15:12:21 +02:00
Stefan Zermatten
298db01e5b Updated computation engine to handle multiple attributes and skills with the same variable name 2021-04-22 15:11:49 +02:00
Stefan Zermatten
727101cd63 Updated Meteor 2021-04-22 15:10:47 +02:00
Stefan Zermatten
d4d002cf31 Fixed an error when targeting an ability score with a proficiency 2021-04-15 12:00:11 +02:00
Stefan Zermatten
2150bd6da4 Added breadcrumbs to creature properties 2021-04-13 14:17:31 +02:00
Stefan Zermatten
e1df145675 Add property button now in creature property dialogs 2021-04-13 11:53:50 +02:00
Stefan Zermatten
1eb78756ac Fixed console error if creature is deleted while sheet is still showing 2021-04-13 11:53:18 +02:00
Stefan Zermatten
ce9b9199ec Fixed dialog stacking animation 2021-04-13 11:41:14 +02:00
Stefan Zermatten
cfb1414494 Start character sheet on the character details dialog instead of build 2021-04-13 11:06:46 +02:00
Stefan Zermatten
4abd689c9f Use DiceCloud 5e base as default for new characters 2021-04-13 11:06:25 +02:00
Stefan Zermatten
f0e443fba2 When hiding the spells tab or tree tab, only change tabs if on one of those 2021-04-13 11:05:25 +02:00
Stefan Zermatten
52e7deedc6 Leave character page before deleting to prevent UI errors 2021-04-13 10:50:35 +02:00
Stefan Zermatten
15d593db79 Properties quick-inserted from the sheet now go into folders in the tree 2021-04-12 16:04:04 +02:00
Stefan Zermatten
e30754ef26 Added method to insert property to a tagged parent 2021-04-12 15:35:25 +02:00
Stefan Zermatten
255ac529b3 Added more default properties to creatures 2021-04-12 15:35:12 +02:00
Stefan Zermatten
c8b5ada5b9 Changed all form input fields to outlined style instead of filled 2021-04-12 14:21:50 +02:00
Stefan Zermatten
0c24238069 Fixed not found page for Vuetify 2 2021-04-11 18:15:56 +02:00
Stefan Zermatten
66847430ad Fixed sign in and register pages not being built with Vuetify 2 components 2021-04-11 18:05:36 +02:00
Stefan Zermatten
bfb860605f Creature properties now duplicate with up to 50 children 2021-04-11 14:47:41 +02:00
Stefan Zermatten
d87524418a Fixed bug where character sheet fab could be permanently hidden by closing the insert from library dialog 2021-04-11 13:52:25 +02:00
Stefan Zermatten
5f97592ed3 Updated package-lock for last commit 2021-04-11 13:33:38 +02:00
Stefan Zermatten
562991216f Moved ignore-styles dep from devDependencies to dependencies 2021-04-11 13:23:47 +02:00
Stefan Zermatten
109b89022e removed id from comment 2021-04-11 13:09:16 +02:00
Stefan Zermatten
d4ca07ce9c Began working on character migration code 2021-04-11 13:08:41 +02:00
Stefan Zermatten
885607f685 Moved the tree fab to the toolbar with smart parenting 2021-04-11 12:36:14 +02:00
Stefan Zermatten
81460f8835 Added file missed in last commit 2021-04-11 12:15:51 +02:00
Stefan Zermatten
cce5f9b926 Merge branch 'version-2-dev' of https://github.com/ThaumRystra/DiceCloud into version-2-dev 2021-04-11 12:15:33 +02:00
Stefan Zermatten
7d3a51de9d Moved ancestry setting responsibility to trusted code 2021-04-11 12:15:30 +02:00
Stefan Zermatten
fc774fcc5e Moved ancestry setting responsibility to trusted code 2021-04-11 12:14:39 +02:00
Stefan Zermatten
0f37a49b95 Fixed bug where wrong fab would show on character tab if spell tab was hidden 2021-04-11 11:01:31 +02:00
Stefan Zermatten
9814e20091 Added the ability to hide spells and tree tab. Tree tab hidden by default 2021-04-11 10:43:33 +02:00
Stefan Zermatten
d89cb77040 Fixed no-experiences Icon 2021-04-11 10:29:09 +02:00
Stefan Zermatten
8590d29abf Improved animation feel of character sheet fab 2021-04-11 10:21:14 +02:00
Stefan Zermatten
9298754dc9 Fixed character sheet title not updating correctly 2021-04-09 12:44:54 +02:00
Stefan Zermatten
e2d6d40bb3 Duplicating library nodes now duplicates up to 50 descendants 2021-04-09 12:36:44 +02:00
Stefan Zermatten
152677b023 Library nodes are now smarter about where in the tree they are inserted based on the currently selected node 2021-04-09 12:36:14 +02:00
Stefan Zermatten
838e2ed35f Fixed toolbar colors for vuetify 2 2021-03-28 14:29:56 +02:00
Stefan Zermatten
60ae1ef604 Fixed bug where editing a field and immediately changing selected property would apply the change to the new property rather than the old one 2021-03-28 13:56:45 +02:00
Stefan Zermatten
ecfe5f1360 Fixed spell casting buttons having the wrong color in dark mode 2021-03-28 13:14:38 +02:00
Stefan Zermatten
292d3c3f37 Tree titles now have hover. Fixed primary color theme switching 2021-03-28 13:09:47 +02:00
Stefan Zermatten
3c26bb2fc6 Reworked log data format, overhauled snackbar 2021-03-28 12:31:39 +02:00
Stefan Zermatten
ada1355c29 Added UI for filtered out slot fillers allowing loading more 2021-03-27 14:39:00 +02:00
Stefan Zermatten
2662af8ea2 Fixed misalignment on ability score tiles 2021-03-27 13:47:02 +02:00
Stefan Zermatten
26b68dccef Fixed style of attribute cards 2021-03-27 13:45:10 +02:00
Stefan Zermatten
0717f8e8d7 Fixed broken and insecure packages 2021-03-27 13:34:33 +02:00
Stefan Zermatten
5cf0330e03 Added library node insert button to library page, no automatic parenting 2021-03-26 12:49:08 +02:00
Stefan Zermatten
1978a2e4c7 Fixed Error when referencing slotLevel in a spell 2021-03-26 11:18:14 +02:00
Stefan Zermatten
5a2e500348 Fixed constants under toggles triggering calculation of those toggles before class levels are defined 2021-03-26 11:11:15 +02:00
Stefan Zermatten
7d4356592a Iterated on card color scheme 2021-03-26 11:06:52 +02:00
Stefan Zermatten
2c448d1748 Fixed some pages background colors 2021-03-26 10:00:37 +02:00
Stefan Zermatten
4f96d817d5 Change card and background color scheme 2021-03-26 09:48:29 +02:00
Stefan Zermatten
f9998eabc4 Fixed tabs using the wrong primary color 2021-03-26 09:35:19 +02:00
Stefan Zermatten
623cff584c Fixed form section expansion 2021-03-26 09:26:31 +02:00
Stefan Zermatten
aa34508cb3 Fixed hovering on toolbar cards 2021-03-26 09:26:16 +02:00
Stefan Zermatten
6678bc1cea Fixed library subscriptions, again 2021-03-25 14:12:02 +02:00
Stefan Zermatten
0324b9f7c3 Fixed some tooltips to vuetify 2 2021-03-25 13:14:40 +02:00
Stefan Zermatten
ccac142ec6 Fixed some cards not animating elevation change 2021-03-25 13:08:48 +02:00
Stefan Zermatten
fe3fa56541 Continued migrating UI to vuetify 2 2021-03-25 12:54:44 +02:00
Stefan Zermatten
480da6fc7d ES Lint fix migration to vuetify 2 2021-03-25 10:20:13 +02:00
Stefan Zermatten
6ffb48b7b6 Began migration to Vuetify 2.x expect a lot to be broken 2021-03-24 16:23:39 +02:00
Stefan Zermatten
82150df5e0 Updated packages and dependencies 2021-03-24 14:38:47 +02:00
Stefan Zermatten
9a120a6e9a Added description to class level viewer 2021-03-23 15:07:47 +02:00
Stefan Zermatten
f385c2857e Fixed library forward arrow being disabled if you can't edit the library 2021-03-12 09:34:20 +02:00
Stefan Zermatten
11a2851ac4 Fixed slots with computed expected quantity not hiding when full 2021-03-10 14:51:38 +02:00
Stefan Zermatten
313382fb82 Fixed library subscription issues 2021-03-10 14:40:14 +02:00
Stefan Zermatten
b9ae337a64 Merge branch 'version-2-dev' of https://github.com/ThaumRystra/DiceCloud into version-2-dev 2021-03-02 14:32:08 +02:00
Stefan Zermatten
4dc0a6159b Animated log entries 2021-03-02 14:32:05 +02:00
Stefan Zermatten
e00dfe1532 Changed the color of the log background 2021-03-02 14:31:35 +02:00
Stefan Zermatten
28e1fcabd5 Fixed damage properties by name failing if no properties were found 2021-03-02 14:10:14 +02:00
Stefan Zermatten
2c0496b44b Fixed properties not being made inactive by toggles 2021-03-02 13:56:53 +02:00
Stefan Zermatten
89adda60ec Reworked single page libraries to be more in line with the library view 2021-03-02 13:05:38 +02:00
Stefan Zermatten
8c3710cda3 Started work on single page libraries 2021-03-02 00:24:54 +02:00
Stefan Zermatten
b501b9d830 Fixed crash in skill calculation when level is overridden by an attribute 2021-03-01 18:40:55 +02:00
Stefan Zermatten
574f8373e7 Fixed crash when indexing a non-array node, added more array node errors 2021-03-01 14:47:46 +02:00
Stefan Zermatten
a7ecdecec1 Prevented contextual variables #type from being written to creature variable list 2021-03-01 14:22:03 +02:00
Stefan Zermatten
0aa59a4bfc Fixed creature not recomputing correctly when weight carried changes 2021-03-01 14:15:21 +02:00
Stefan Zermatten
8f0ff3245e Fixed containers still carrying their own weight if their contents are weightless and they aren't carried 2021-03-01 14:15:01 +02:00
Stefan Zermatten
9a2d10b7ed Fixed new library button hiding and not coming back 2021-03-01 14:08:12 +02:00
Stefan Zermatten
a8aa1923a8 Fixed spells having a stray deativatedBySelf flag 2021-03-01 14:01:34 +02:00
Stefan Zermatten
57fa162c89 Fixed stray errors from unepexted types 2021-03-01 13:37:19 +02:00
Stefan Zermatten
4d548c901c Ensured property exists before attempting to damage it 2021-03-01 13:32:46 +02:00
Stefan Zermatten
a97be2f93a Made constants work in calculations performed after recomputation 2021-03-01 13:27:48 +02:00
Stefan Zermatten
1276f872a0 Removed unused function 2021-03-01 12:11:22 +02:00
Stefan Zermatten
7daab97297 Made toggles function properly when nested under inactive properties and each other 2021-03-01 11:55:43 +02:00
Stefan Zermatten
2e3704d096 Prevented resources from writing unchanged data to the database 2021-03-01 11:42:50 +02:00
Stefan Zermatten
7283a27727 Constants should now respect toggles 2021-03-01 11:42:23 +02:00
Stefan Zermatten
3517636b8b Reworked toggles, again, to try and catch more edge cases. Made toggles set the inactive status of their property children in the compute step instead of the inactive denormalisation step 2021-03-01 11:41:59 +02:00
Stefan Zermatten
e617ef9b75 Merge branch 'version-2' into version-2-dev 2021-03-01 10:18:55 +02:00
Stefan Zermatten
cd45ae1442 Fixed buffs not recomputing correctly because of inactive properties not being activated 2021-03-01 10:07:24 +02:00
Stefan Zermatten
bcedd548c7 Fixed: If usesUsed was undefined, usesLeft of an action was NaN 2021-03-01 10:06:31 +02:00
Stefan Zermatten
dc53e38efe Libraries only fetch their data whene expanded 2021-02-27 10:49:10 +02:00
Stefan Zermatten
e381b3b24d Merge branch 'version-2' of https://github.com/ThaumRystra/DiceCloud into version-2 2021-02-26 09:48:22 +02:00
Stefan Zermatten
111d971bc2 Added attacks and actions to stats tab quick insert 2021-02-26 09:48:18 +02:00
Stefan Zermatten
bf4ce4f9f7 Hotfix: Adding properties to the tree, type selection fixed 2021-02-25 19:43:17 +02:00
Stefan Zermatten
2a983b0a94 User accounts can now be deleted with some UI to prevent accidental deletion 2021-02-25 14:28:51 +02:00
Stefan Zermatten
a5460bba0b Added floating action button to add properties directly to the sheet 2021-02-25 12:37:32 +02:00
Stefan Zermatten
df361236f5 Hotfix: Containers total weight now showing correctly on inventory tab 2021-02-24 15:18:32 +02:00
Stefan Zermatten
e1d670fe9f Fixed: buffs 2021-02-24 15:05:53 +02:00
Stefan Zermatten
1e9f0515e5 Contents that are weightless are now summed and stored on the container 2021-02-24 14:22:52 +02:00
Stefan Zermatten
0404020335 Added weights and content weight to containers UI 2021-02-24 14:07:20 +02:00
Stefan Zermatten
c248d8f4a0 Weight carried, Net worth, and Attunement implemented and exposed in UI 2021-02-24 13:41:30 +02:00
Stefan Zermatten
8d95da8b7a Fixed a bug where certain base values would be strings instead of numbers in effect aggregators 2021-02-24 11:58:04 +02:00
Stefan Zermatten
e11ab39864 Added tableLookup function 2021-02-24 11:57:40 +02:00
Stefan Zermatten
331fcef9ad Fixed: Error message when focus grabbing element is missing on form 2021-02-24 10:06:25 +02:00
Stefan Zermatten
7e3bff9677 Show creature milestone level and xp if creature has both 2021-02-24 10:05:11 +02:00
Stefan Zermatten
1b650b26b6 Fixed: using creature stats like XP in calculations 2021-02-24 10:01:02 +02:00
Stefan Zermatten
5925605962 Fixed property edit buttons no longer get pushed by long property name 2021-02-24 09:52:51 +02:00
Stefan Zermatten
dee1265b69 Fixed: Inline calculations in libarries now display as expected 2021-02-24 09:46:52 +02:00
Stefan Zermatten
3d3ec3bcf2 Increaed number of slot fillers loaded by the slot fill dialog to 20 2021-02-24 09:23:55 +02:00
Stefan Zermatten
dce2c92516 Added attack roll bonus and dc to spell list. Use them in spells with #spellList.dcResult and #spellList.attackRollBonusResult 2021-02-23 15:21:20 +02:00
Stefan Zermatten
0fe2780983 Added property viewer for Toggle properties 2021-02-23 15:07:07 +02:00
Stefan Zermatten
e126cdd3cb Added property viewer for slot filler 2021-02-23 14:59:53 +02:00
Stefan Zermatten
d69ada0db4 Slot quantity is now a computed value, added property viewer for slots 2021-02-23 14:53:47 +02:00
Stefan Zermatten
858915b25b Added viewer for Saving Throw properties 2021-02-23 14:38:20 +02:00
Stefan Zermatten
d10a7eca14 Added viewer for Roll properties 2021-02-23 14:29:48 +02:00
Stefan Zermatten
671d17018c Added a viewer for Constant properties 2021-02-23 14:23:00 +02:00
Stefan Zermatten
f2883d320f Improved Attribute damage viewer 2021-02-23 13:59:26 +02:00
Stefan Zermatten
aad0c7249e Removed stray log to console 2021-02-23 12:47:34 +02:00
Stefan Zermatten
612fcca68c Only split properties accross targets if there are targets 2021-02-22 14:30:50 +02:00
Stefan Zermatten
12939c46de made saves walk children when not targeted at self 2021-02-22 14:28:38 +02:00
Stefan Zermatten
3801b17fde Attacks can now critical hit. criticalHitTarget overrides the roll required 2021-02-22 14:07:12 +02:00
Stefan Zermatten
88133a2fa3 Saving throws now work in actions 2021-02-22 12:38:21 +02:00
Stefan Zermatten
d00eedac19 Rolls now work in actions 2021-02-22 11:55:08 +02:00
Stefan Zermatten
6571fb860a Toggles now work in actions to make choices based on action context 2021-02-22 11:36:30 +02:00
Stefan Zermatten
8148f4d701 Fixed: Library nodes are published in order to prevent disordered building of the tree 2021-02-21 17:05:09 +02:00
Stefan Zermatten
523c34b719 Fixed: Slots that use conditions now only hide on falsey value (false, 0, '') 2021-02-21 17:01:24 +02:00
Stefan Zermatten
e833fba870 Fixed: bug that stopped buffs being deleted 2021-02-21 16:35:35 +02:00
Stefan Zermatten
f3e191c12e Fixed: Inserting properties to the tree now animate correctly to the inserted property 2021-02-20 16:00:40 +02:00
Stefan Zermatten
33415275a3 Item tiles are now smaller in the inventory view 2021-02-20 15:53:58 +02:00
Stefan Zermatten
3b1151d987 Fixed notes without summaries are no longer oversized 2021-02-20 15:52:15 +02:00
Stefan Zermatten
4288f98f7c Fixed: stats with no ability selected have an ability modifier of 0 instead of NaN 2021-02-20 15:50:25 +02:00
Stefan Zermatten
1a2ef8a4a2 Fixed: markdown images no longer overflow their container width 2021-02-20 15:45:45 +02:00
Stefan Zermatten
10e9a5faa8 Notes now show both summary and description in viewer 2021-02-20 15:41:30 +02:00
Stefan Zermatten
53594c0004 Fixed: items in containers not following tree order 2021-02-20 15:38:51 +02:00
Stefan Zermatten
e068675b46 Fixed and improved: Discord webhooks are working again with a new format 2021-02-20 15:27:20 +02:00
Stefan Zermatten
067f5df36e Fixed: Emptying the search bar in slot filler dialog now correctly clears the search 2021-02-20 10:17:35 +02:00
Stefan Zermatten
6113d86059 Fixed typo in calling a function in Constants autovalue 2021-02-16 11:19:50 +02:00
Stefan Zermatten
e3862bcdd9 Fixed constants autovalue 2021-02-16 10:49:18 +02:00
Stefan Zermatten
299f5a06dd Moved inline cacultion regex to a constant to ensure constistency 2021-02-16 10:14:26 +02:00
Stefan Zermatten
2776850311 Fixed missing import 2021-02-12 11:45:32 +02:00
Stefan Zermatten
3e6221309e Notes now have a summary and a description, some data migration my be needed 2021-02-12 11:44:03 +02:00
Stefan Zermatten
a078ce3d5d Description fields should now show calculation errors 2021-02-12 11:43:13 +02:00
Stefan Zermatten
b9e0475d07 Markdown now follow Github Flavor Markdown, including single line breaks 2021-02-12 11:11:12 +02:00
Stefan Zermatten
2b345c1f77 Improved error handling for most calculations 2021-02-12 11:00:44 +02:00
Stefan Zermatten
fed87f0a84 Fixed: items and spells should no longer be draggable when you don't have edit permission 2021-02-12 09:41:24 +02:00
Stefan Zermatten
b116be1238 Fixed flickering when inserting properties from library by ensuring consistent ID generation 2021-02-12 00:43:56 +02:00
Stefan Zermatten
ae373330ab Improved slot fill dialog UI, abandoned column layout in favour of wrapping flex rows 2021-02-12 00:18:29 +02:00
Stefan Zermatten
dcb535c84e Moved slot filler search to server side, limited docs in subscription 2021-02-11 22:09:43 +02:00
Stefan Zermatten
8c477ad4b1 Fixed constants not being found when used as the only thing in a calculation 2021-02-11 18:01:30 +02:00
Stefan Zermatten
eb2dd3bba1 Fixed computation dependency aggregation broken by refactoring 2021-02-11 16:27:22 +02:00
Stefan Zermatten
dc4808c70a fixed snackbars being blank 2021-02-11 16:20:33 +02:00
Stefan Zermatten
16c8c1db81 Improved descriptions in log entries 2021-02-11 16:16:22 +02:00
Stefan Zermatten
92a5c1e6c3 Improved log entries for actions 2021-02-11 16:08:31 +02:00
Stefan Zermatten
439eadf079 Condensed logs to a single card per action 2021-02-11 15:48:23 +02:00
Stefan Zermatten
d7083cf242 Hid unused cards on the stats tab 2021-02-11 13:48:10 +02:00
Stefan Zermatten
3af5e820ca Stopped inactive effects showing up in attributes 2021-02-11 13:38:34 +02:00
Stefan Zermatten
20aaab4dea Hid inactive notes 2021-02-11 13:14:31 +02:00
Stefan Zermatten
81cdf282ea Stopped inactive properties from showing computed inline fields, since they are not recomputed while inactive 2021-02-11 13:12:35 +02:00
Stefan Zermatten
3313ed0297 Added constants to the UI and Computation Engine 2021-02-11 13:03:31 +02:00
Stefan Zermatten
25fd5c18e8 Fixed casting broken by refactoring 2021-02-11 10:13:35 +02:00
Stefan Zermatten
5b9bb6e4bc Fixed spending resources for actions broken by refactoring 2021-02-11 10:13:23 +02:00
Stefan Zermatten
74370f6fec Performance optimization: Removed creature document from injection to prevent uneccessary Vue re-rendering 2021-02-11 10:04:28 +02:00
Stefan Zermatten
565ddccba6 fixed broken import 2021-02-04 16:55:50 +02:00
Stefan Zermatten
4ea4348a02 Now writing partial recalculations to creature variables 2021-02-04 16:52:26 +02:00
Stefan Zermatten
280f30dab5 Improved dependencies-only recalculations and fixed many calculation bugs 2021-02-04 16:16:51 +02:00
Stefan Zermatten
6d1e3f078c Optimised when certain recompute functions are called to prevent unccessary work 2021-02-04 13:59:08 +02:00
Stefan Zermatten
326d1bd165 Refactored computation into folders 2021-02-04 13:33:20 +02:00
Stefan Zermatten
87fa941f63 Merge branch 'version-2' of https://github.com/ThaumRystra/DiceCloud into version-2 2021-02-04 11:38:55 +02:00
Stefan Zermatten
449a4fba7d Refactored creature property methods into separate documents, might have broken a lot of things 2021-02-04 11:38:29 +02:00
Stefan Zermatten
9ff096ec0f Added modulo operator 2021-02-03 09:33:00 +02:00
Stefan Zermatten
f9f0186d95 Fixed error where parser was not creating accessor nodes correctly 2021-02-03 00:39:11 +02:00
Stefan Zermatten
60ea545ee9 Started implementing constant property 2021-02-02 16:36:23 +02:00
Stefan Zermatten
aaa5d0b63b Allowed effects and calculations to target nearest ancestors of #type 2021-02-02 16:11:59 +02:00
Stefan Zermatten
69c72e0987 Fixed parenthesis being discarded from compiled calculations in cases where they should not be 2021-02-02 15:13:49 +02:00
Stefan Zermatten
a6df4df534 Fixed some fields not storing strings when compiling calculations 2021-02-02 15:07:31 +02:00
Stefan Zermatten
8b8f9bea6f Removed character variables from creatures injected into character sheet as context 2021-01-31 20:13:45 +02:00
Stefan Zermatten
e7a27e4b83 Fixed hit dice breaking long rests 2021-01-31 19:45:14 +02:00
Stefan Zermatten
e14e875c42 Fixed conMod not being signed in hit dice tiles 2021-01-31 19:43:09 +02:00
Stefan Zermatten
a7898bfe4b Fixed props not having a default array for embedded calculations 2021-01-31 19:42:49 +02:00
Stefan Zermatten
aee899e181 Removed all UI computations from viewers and components 2021-01-31 18:42:17 +02:00
Stefan Zermatten
a5284bf6e8 Made 'always prepared' spells show up in casting list 2021-01-30 11:22:29 +02:00
Stefan Zermatten
0ea3f7a975 Migrated fields to embedded property calculations 2021-01-29 13:04:53 +02:00
Stefan Zermatten
1167538977 Denormalised inline calculations to property documents, needs to be referenced by UI still 2021-01-29 12:29:01 +02:00
Stefan Zermatten
9c799e3dc9 Added search to slot fill dialog 2021-01-28 17:09:37 +02:00
Stefan Zermatten
2bf749c4f1 Fixed: Layout of slot fill dialog is no longer broken by tall images, background is now the correct color 2021-01-28 16:48:33 +02:00
Stefan Zermatten
db83d5f82a Fix: Tabs and tab headers no longer lose sync when changing characters 2021-01-28 16:17:03 +02:00
Stefan Zermatten
d596061fa8 Fixed: Attributes that aren't ability scores now have their modifiers removed correctly 2021-01-28 16:05:47 +02:00
Stefan Zermatten
78efe639ed Fixed: Zero effect passive bonus now causes passive skill value to show. 2021-01-28 16:01:58 +02:00
Stefan Zermatten
e5bde38745 Removed console timers from recompute function: it's not the primary slowdown 2021-01-28 15:00:36 +02:00
Stefan Zermatten
83f2047dbe Replaced expensive getActiveProperties with cheaper filter by inactive field 2021-01-28 14:29:10 +02:00
Stefan Zermatten
fc03097ed8 Patreon access is now given by tier instead of by price paid 2021-01-28 12:15:45 +02:00
Stefan Zermatten
252ac23391 Merge branch 'version-2' of https://github.com/ThaumRystra/DiceCloud into version-2 2021-01-27 22:25:19 +02:00
Stefan Zermatten
531ddce6a0 Added dependency tracking to computations for future optimization effort 2021-01-27 22:24:28 +02:00
Stefan Zermatten
3cdeb73c30 Stopped removed items from showing up in inventory containers 2021-01-25 12:38:46 +02:00
Stefan Zermatten
c780c11e3f Improved spell slot casting dialog with search, filters, spell details 2021-01-22 14:09:23 +02:00
Stefan Zermatten
100c93b5ae Stopped dialog animation mocking transparent elements' background color 2021-01-22 12:20:33 +02:00
Stefan Zermatten
a4e6dd1d66 Added spellcasting to the stats page, click the icon next to a spell slot to cast 2021-01-19 16:10:34 +02:00
Stefan Zermatten
1b3b6362f7 Exposed spellSlotLevelValue to creature variables store 2021-01-19 12:07:40 +02:00
Stefan Zermatten
bdf4074e3c Added first pass at denormalizing inventory data, needs testing and integration 2021-01-15 17:41:06 +02:00
Stefan Zermatten
9492b2d8b8 Characters now start with a slot for a base. New characters start on the character tab with the build dialog open. 2021-01-14 12:28:51 +02:00
Stefan Zermatten
27f1f4e720 Fixed a bug with dialog animations hiding buttons by failing to clean up 2021-01-12 13:51:36 +02:00
Stefan Zermatten
d63b8c835d Fixed bug in last release where unlimited slots were always hidden on hide when full 2021-01-12 13:51:11 +02:00
Stefan Zermatten
85f3881935 Removed alert about data loss, most data structures are stable and it's being alarmist 2021-01-12 13:40:02 +02:00
Stefan Zermatten
5e4299e6db removed stray console logging 2021-01-12 13:24:08 +02:00
Stefan Zermatten
0e663f36db Slot fillers that count as more than one slot now update the space left correctly.
Slot fillers removed from a library can no longer be added to a slot.
If a slot has limited space left, this will be reflected on the error 
finding slots message
2021-01-12 13:22:48 +02:00
Stefan Zermatten
de9ea5922c Fixed bugs when a spell list does not have limit on prepared spells 2021-01-12 13:10:36 +02:00
Stefan Zermatten
a2cfe43e74 Disabled the direct damage input in the attribute form: it can't be edited anyway 2021-01-12 13:04:25 +02:00
Stefan Zermatten
026c11c13b Made sure attributes show their currentValue instead of Value 2021-01-12 13:03:33 +02:00
Stefan Zermatten
403f2663c2 Fixed bugs with item display, equipment will now automatically move to the first property with the 'equipment' tag, carried items will move to the first property with the 'carried' tag 2021-01-12 12:54:02 +02:00
Stefan Zermatten
28c042343e All children of infinite slots will now hide when "hide when full" is active 2021-01-12 10:36:07 +02:00
Stefan Zermatten
3f116875a1 Children of slots now display in the correct order 2021-01-12 10:35:37 +02:00
Stefan Zermatten
ae5b4b7d5c Made inactive toggle decendants specifically included when recomputing active properties 2021-01-11 22:03:54 +02:00
Stefan Zermatten
75835d74f6 Merge pull request #259 from Ganonsmasher/version-2
Found a fix for toggles.
2020-12-17 17:19:54 +02:00
Ganonsmasher
583b652fc4 Found a fix for toggles.
It doesn't fix the problem of their property being absent on the stats page cards, but they can compute properly and disable their children with this.
2020-12-15 18:08:29 -05:00
Stefan Zermatten
df317a8942 Merge branch 'version-2' of https://github.com/ThaumRystra/DiceCloud into version-2 2020-11-20 14:21:52 +02:00
Stefan Zermatten
ff0e9b1ff9 Added Lord of Junk to Patreon Paragons 2020-11-20 14:13:47 +02:00
Stefan Zermatten
fa24430a7f Fixed parsing of variable names with numbers and stacked dice rolls like dd8-> 1d(1d8) 2020-11-13 10:04:37 +02:00
Stefan Zermatten
fde2f821e7 Fixed parser not handling whitespace 2020-11-12 21:44:08 +02:00
Stefan Zermatten
827430c987 Fixed edit permission errors for some creature prop methods 2020-11-12 21:25:48 +02:00
Stefan Zermatten
2a1aa02e97 Added true and false keywords, fixed grammar ambiguity in if statements 2020-11-12 15:11:24 +02:00
Stefan Zermatten
aeb347084f Fixed ambiguitiy in grammar caused by previous fixes 2020-11-12 13:47:10 +02:00
Stefan Zermatten
005bc162cb Fixed != not being matched because ! was matched first. Fixed presedence for & | and relational operators 2020-11-12 13:45:14 +02:00
Stefan Zermatten
9941d91bb8 Fixed != operator, separated == and ===, != and !== for strictness control 2020-11-12 13:44:01 +02:00
Stefan Zermatten
23c77690a1 Healing damage type now heals instead of damaging 2020-11-12 13:01:09 +02:00
Stefan Zermatten
525b528d9a Added attribute damage and self damage results to actions and log. 2020-11-12 12:57:48 +02:00
Stefan Zermatten
3917f63d5e Increased subscription rate limit to prevent infinitely spinning characters 2020-11-12 11:04:04 +02:00
Stefan Zermatten
bd056ab042 Improved subscription permissions, should now work as expected for public documents 2020-11-12 10:48:46 +02:00
Stefan Zermatten
cd84b2562a Fixed an error with finding deployed version SHA 2020-11-10 14:48:09 +02:00
Stefan Zermatten
c7436ffb1e Caught error where git couldn't be used to get version 2020-11-10 14:37:32 +02:00
Stefan Zermatten
be7d7f898f disabled short and log rest buttons if user has no edit permission 2020-11-10 14:22:09 +02:00
Stefan Zermatten
3024168e95 Replaced old parser with new parser 2020-11-10 14:07:22 +02:00
Stefan Zermatten
1f0678b50b Added not operator to the parser 2020-11-05 15:32:01 +02:00
Stefan Zermatten
4dad2c41e5 Updated parser to accept underscores in variable names 2020-11-05 14:50:44 +02:00
Stefan Zermatten
46385dd9b2 Build details with no slots hidden moved to a dialog 2020-11-05 14:27:01 +02:00
Stefan Zermatten
7cb65954b5 Added the ability to hide slots when full 2020-11-05 14:05:17 +02:00
Stefan Zermatten
749799d869 Denormalised slot fill total to database 2020-11-05 13:35:55 +02:00
Stefan Zermatten
3293dad671 Limited what fields are included when fetching recompute documents to improve performance 2020-11-05 12:59:48 +02:00
Stefan Zermatten
88df942c59 Fixed an error with missing identity details in patreon request 2020-11-05 12:59:26 +02:00
Stefan Zermatten
9722bbc667 Characters now recompute on subscribe if they haven't been computed in the current version 2020-11-04 14:27:31 +02:00
Stefan Zermatten
2fb0ba79c6 began work to get inactive state of properties denormalised 2020-11-03 15:57:14 +02:00
Stefan Zermatten
3f7ddd62fc Merge branch 'version-2' of https://github.com/ThaumRystra/DiceCloud1 into version-2 2020-10-27 10:41:06 +02:00
Stefan Zermatten
227d6c5aae Markdown and calculations now supported in slot filler descriptions 2020-10-27 10:39:20 +02:00
Stefan Zermatten
147ef97576 Markdown now supported in slot filler descriptions 2020-10-27 10:34:01 +02:00
Stefan Zermatten
54806b0f3c Merge branch 'version-2' of https://github.com/ThaumRystra/DiceCloud1 into version-2 2020-10-19 11:39:24 +02:00
Stefan Zermatten
1165158d46 Forced creatures to reorder their docs before recomputing 2020-10-19 11:39:21 +02:00
Stefan Zermatten
32de70cd45 Class levels now have a description field 2020-10-17 19:33:56 +02:00
Stefan Zermatten
68499e4de5 You can now click on properties filling slots to view their details dialog 2020-10-17 19:28:54 +02:00
Stefan Zermatten
3f4cb8e26b Added undo buttons for deleting properties off a creature 2020-10-17 19:10:37 +02:00
Stefan Zermatten
ebab41838c Used tree node views in slot fill selection 2020-10-17 16:56:23 +02:00
Stefan Zermatten
e8da7a6c17 Moved snackbars to their own store and component 2020-10-17 16:06:27 +02:00
Stefan Zermatten
46189c68df All property forms now allow tags 2020-10-17 13:42:24 +02:00
Stefan Zermatten
9fa997ed24 Started moving snackbars to vue store, still needs to be separated into its own module 2020-10-16 13:38:58 +02:00
Stefan Zermatten
e3bf6557ec fixed bug in dialog store 2020-10-16 10:35:51 +02:00
Stefan Zermatten
7aa3e5a217 Stringified errors from scheduled deletion 2020-10-16 10:23:34 +02:00
Stefan Zermatten
dc1b025090 Increased delete job frequency to 10 minutes 2020-10-16 10:00:07 +02:00
Stefan Zermatten
2e370a9884 Fixed removed slots not being hidden 2020-10-15 16:42:57 +02:00
Stefan Zermatten
384fa076f1 hotfix tags not filtering correctly 2020-10-15 16:12:32 +02:00
Stefan Zermatten
7922e30ddc Added tags to some properties. Added condition to class levels 2020-10-15 16:00:32 +02:00
Stefan Zermatten
1ba4f76763 Class levels can now have conditions 2020-10-15 15:57:19 +02:00
Stefan Zermatten
839f91c3b2 Fixed icons not going to dark mode when slot filling cards are selected 2020-10-15 15:30:11 +02:00
Stefan Zermatten
26567ce840 slot fill cards with pictures no longer get icons 2020-10-15 15:25:10 +02:00
Stefan Zermatten
2a729a4eca Made slot fill dialog a list of cards to leverage pretty slotFillers 2020-10-15 15:24:14 +02:00
Stefan Zermatten
ed17d9e2d2 Added slotfiller property type to increase control over slot filling 2020-10-15 14:54:58 +02:00
Stefan Zermatten
8e9405b5ad Allowed slots with unlimited children, improved slot ui text 2020-10-15 13:50:46 +02:00
Stefan Zermatten
7fc783dcad Removed recompute button 2020-10-15 13:50:14 +02:00
Stefan Zermatten
b15ad7e51a Merge branch 'version-2' of https://github.com/ThaumRystra/DiceCloud1 into version-2 2020-10-15 13:49:49 +02:00
Stefan Zermatten
8a3d2474fc Merge pull request #254 from JoeZwet/version-2
fix: prevent discord mention exploit
2020-10-15 13:48:30 +02:00
Joe van der Zwet
09371e7d54 add requested changes 2020-10-16 00:47:32 +13:00
Joe van der Zwet
0776d33909 fix: prevent discord mention exploit 2020-10-16 00:36:32 +13:00
Stefan Zermatten
6e98d71c3c Improved slot UI look and feel 2020-10-15 13:00:29 +02:00
Stefan Zermatten
8f89f4b63f Ensured all subscriptions return empty arrays instead of errors or ready 2020-10-15 12:34:46 +02:00
Stefan Zermatten
c0070d017e Removed debugging code 2020-10-14 15:54:16 +02:00
Stefan Zermatten
51569592ab First implementation on Slots UI 2020-10-14 14:45:26 +02:00
Stefan Zermatten
d2cb86ac27 Fixed broken logging for actions 2020-10-14 11:33:25 +02:00
Stefan Zermatten
bde9183158 Log optimistic UI now fixed, rolls are now instant 2020-10-14 11:25:05 +02:00
Stefan Zermatten
0cc9e01754 Renamed, moved LogTab to CharacterLog 2020-10-14 09:37:00 +02:00
Stefan Zermatten
9856471202 Stopped log making toast if it's visible 2020-10-14 09:33:17 +02:00
Stefan Zermatten
4f77782a7a log messages are now aligned right 2020-10-13 13:53:30 +02:00
Stefan Zermatten
5f13aaa031 Fixed empty strings in log input 2020-10-13 13:44:06 +02:00
Stefan Zermatten
1321cf6a96 Moved log tab to right drawer 2020-10-13 13:42:18 +02:00
Stefan Zermatten
dee8249f61 Creature logs are now removed with creatures 2020-10-13 12:43:55 +02:00
Stefan Zermatten
0af0afc0d0 Discord webhooks now mirror character log 2020-10-13 12:42:02 +02:00
Stefan Zermatten
a104fc8a87 Fixing broken casing on file pt.2 2020-10-06 10:55:12 +02:00
Stefan Zermatten
46f452987f Fixing broken casing on file 2020-10-06 10:54:52 +02:00
Stefan Zermatten
a87cb1286a Improved custom rolls on log tab 2020-10-06 09:53:08 +02:00
Stefan Zermatten
844588cdbf Started adding text input to log tab 2020-09-30 16:24:33 +02:00
Stefan Zermatten
a6a96fc19f Started work on character log for rolls to be stored 2020-09-29 22:34:30 +02:00
Stefan Zermatten
75ab43da00 Started work on UI for rolling checks 2020-09-29 16:37:28 +02:00
Stefan Zermatten
df7000889b fixed security deps 2020-09-29 10:54:37 +02:00
Stefan Zermatten
65754dea80 removed damage multipliers from health bar card, it has its own card 2020-09-29 10:54:27 +02:00
Stefan Zermatten
30cca39e7c Merge branch 'version-2' of https://github.com/ThaumRystra/DiceCloud1 into version-2 2020-09-28 13:58:03 +02:00
Stefan Zermatten
5ad5c914fb Added gitignore file for renders 2020-09-24 19:00:28 +02:00
Stefan Zermatten
f27550362a Fixed various parser bugs, implemented unary operators 2020-09-18 22:13:12 +02:00
Stefan Zermatten
50f7977a60 Fixed patreon update write location 2020-09-18 20:37:16 +02:00
Stefan Zermatten
bc5c465a32 Started work on checks 2020-09-18 14:00:29 +02:00
Stefan Zermatten
c8ddf9d547 Added the ability to double all number of dice to roll using context 2020-09-18 12:24:08 +02:00
Stefan Zermatten
6570665c1e Added functions and ensured the context was being passed around correctly 2020-09-18 11:52:44 +02:00
Stefan Zermatten
06f17a6d33 Parser now uses context to store details of the computation 2020-09-18 10:14:53 +02:00
Stefan Zermatten
b69ad6c306 Removed unused parser code 2020-09-10 11:39:27 +02:00
Stefan Zermatten
5dec760452 Parser now works with variables passed into scope 2020-09-10 11:38:28 +02:00
Stefan Zermatten
ede4e1367d Continued work on parser, now calling functions and rolling correctly 2020-09-10 00:14:24 +02:00
Stefan Zermatten
81645df2a6 Lots of work on the parser including testing interface 2020-09-09 17:09:50 +02:00
Stefan Zermatten
445171ce80 Added preferences subheader to accounts page 2020-09-09 13:58:48 +02:00
Stefan Zermatten
dedab7b046 Added patreon tier refresh button, autorefresh tier on login daily 2020-09-09 13:55:21 +02:00
Stefan Zermatten
a5c16ba83a Overhauled inventory tab again. Closer in functionality to V1 2020-08-22 00:36:17 +02:00
Stefan Zermatten
46501f2759 Spells that aren't prepared no longer count as active properties 2020-08-21 16:34:52 +02:00
Stefan Zermatten
a6ed1004be Added Resistance, Vulnerability, and Immunity to the health bar card 2020-08-21 16:27:01 +02:00
Stefan Zermatten
8539356b9e Added UI to prepare spells 2020-08-21 16:04:31 +02:00
Stefan Zermatten
93db5e9288 Made library link readonly instead of disabled so it can be copied 2020-08-10 04:23:55 +02:00
Stefan Zermatten
b4cb91a892 Multiple libraries can now be opened, allowing library items to be moved 2020-08-10 04:19:54 +02:00
Stefan Zermatten
9c93747845 Fixed bug where array accessors were attempting to use the substitution engine prematurely 2020-08-10 04:14:53 +02:00
Stefan Zermatten
a51154e434 Prevented test webhooks being sent in production 2020-07-26 19:56:08 +02:00
Stefan Zermatten
1bde0db0ba Fixed features showing up when disabled by an ancestor 2020-07-26 19:53:07 +02:00
Stefan Zermatten
bc001202ec Fixed spell list tiles not being opaque 2020-07-26 19:47:44 +02:00
Stefan Zermatten
c7985af83b Continued work on tabletops, hidden from main app and all methods disallowed for non-admins 2020-07-26 19:45:07 +02:00
Stefan Zermatten
0f20cd4bd9 Implemented drag and drop on spells page 2020-07-26 19:39:50 +02:00
Stefan Zermatten
1ac01941c7 Fixed bug where multiple classes woudn't show up in persona tab 2020-07-26 16:43:26 +02:00
Stefan Zermatten
95d8d2cb9a Started work on tabletop view 2020-07-17 23:31:12 +02:00
Stefan Zermatten
47345b3694 Experimenting with webhooks. 2020-07-13 16:38:24 +02:00
Stefan Zermatten
308168791b Made dX rolls work as 1dX 2020-06-30 15:15:55 +02:00
Stefan Zermatten
7be4280508 Began implementing dice rolls in the maths parser 2020-06-30 14:40:20 +02:00
Stefan Zermatten
56f9e82326 Improved spells tab to be more in line with the v1 implementation 2020-06-29 14:52:47 +02:00
Stefan Zermatten
6ddea8a8ab Improved slot schema, added ui for slots 2020-06-29 14:15:49 +02:00
Stefan Zermatten
e1ddfb2cab Fixed a bug where registering a property without disabledByToggle breaks recompuation 2020-06-24 18:20:56 +02:00
Stefan Zermatten
d36d5b15d0 hide unused spell card 2020-06-24 18:17:35 +02:00
Stefan Zermatten
2af687361e Improved spell appearance in spell tab 2020-06-23 01:36:48 +02:00
Stefan Zermatten
e572807082 Attributes of type spell slot now store their slot level 2020-06-23 01:36:21 +02:00
Stefan Zermatten
c44aeac198 Added 'prepared' field to spells 2020-06-22 13:45:47 +02:00
Stefan Zermatten
757cf5c34b Fixed spells not being able to be inserted or editing in characters 2020-06-22 13:45:35 +02:00
Stefan Zermatten
8cbfec25b3 Added the setting to swap ability scores and modifiers to the account page 2020-06-22 13:22:53 +02:00
Stefan Zermatten
c4dc5895aa Relaxed rate limiting on icon search, improved error messaging 2020-06-22 00:20:40 +02:00
Stefan Zermatten
cffe0ee574 Added minimal UI to display applied buffs 2020-06-22 00:14:07 +02:00
Stefan Zermatten
ce51be7b8e moved proficiencies after actions on the stats tab 2020-06-21 23:57:19 +02:00
Stefan Zermatten
315073bd8e Refactored actions and let actions apply buffs to self 2020-06-21 23:54:51 +02:00
Stefan Zermatten
50b99ef54f Improved performance of adding library properties with many decendants 2020-06-21 23:24:07 +02:00
Stefan Zermatten
9b01f5fb45 Improved actions UI, Actions (including spells) can now have icons 2020-06-17 13:23:13 +02:00
Stefan Zermatten
389785f5db Fixed bug where library large screen view won't scroll 2020-06-17 13:22:48 +02:00
Stefan Zermatten
e1bfb173ab Overhauled action detail view 2020-06-16 13:51:58 +02:00
Stefan Zermatten
ecba587253 Fixed a bug with proficiency forms not editing proficiency correctly 2020-06-16 12:35:50 +02:00
Stefan Zermatten
3f540d0f14 Overhaul of character action components, actions now consume resources 2020-06-15 22:30:27 +02:00
Stefan Zermatten
dc18734d1f Backend work to support actions consuming their resources on use 2020-06-13 23:11:49 +02:00
Stefan Zermatten
1535e00093 Denormalized some calculations into recomputation step 2020-06-07 21:08:53 +02:00
Stefan Zermatten
5198c655e9 Added subscription rate limiting 2020-06-06 14:30:15 +02:00
Stefan Zermatten
8d41643136 Increased damage property rate limit to 4/s 2020-06-06 14:25:23 +02:00
Stefan Zermatten
ea8d036c72 Added rate limiting to all methods 2020-06-06 14:23:13 +02:00
Stefan Zermatten
93d566e263 Exposed methods and publications to http requests, changed method names 2020-06-06 12:31:07 +02:00
Stefan Zermatten
b4da32f9ab Fixed soft removed documents never getting permanently removed 2020-06-05 23:08:31 +02:00
Stefan Zermatten
986fe8fd93 Added an autofocus field to most forms 2020-06-05 22:39:21 +02:00
Stefan Zermatten
dd4596851e Improved class level viewer and tree node view 2020-06-05 22:25:22 +02:00
Stefan Zermatten
bc3fc9574a Added loading and empty state to experience list 2020-06-05 22:20:40 +02:00
Stefan Zermatten
db1ae5db3d Iterated on XP system 2020-06-05 21:48:28 +02:00
Stefan Zermatten
d1e7eb2fa0 Added basic XP system 2020-06-05 16:14:26 +02:00
Stefan Zermatten
efb8b87a2d Alphabetized properties by displayed name 2020-05-31 22:39:15 +02:00
Stefan Zermatten
b04b915c7b Removed stray logging 2020-05-31 22:36:27 +02:00
Stefan Zermatten
21b823f85c Dark mode now with 20% more dark 2020-05-31 22:25:04 +02:00
Stefan Zermatten
4631579181 Character toolbar now correctly uses dark and light text where appropriate 2020-05-31 22:22:42 +02:00
Stefan Zermatten
edf3920e84 Character sheet toolbars now match the color of the character 2020-05-31 22:16:38 +02:00
Stefan Zermatten
fb91fd12df Set up custom icons for most properties 2020-05-31 21:03:45 +02:00
Stefan Zermatten
19f4735412 Icon search field now focuses when the menu is opened 2020-05-31 19:18:49 +02:00
Stefan Zermatten
fb2f1efa72 Property insert forms now have color selectors 2020-05-31 19:00:32 +02:00
Stefan Zermatten
f7ee09470e Improved container and item forms and viewers 2020-05-31 18:50:00 +02:00
Stefan Zermatten
a5c42fea19 Made custom svg icons work anywhere a regular icon would work 2020-05-31 18:49:46 +02:00
Stefan Zermatten
8f81614294 Weight and value are no longer required on containers 2020-05-31 15:59:37 +02:00
Stefan Zermatten
c56cebc652 Fixed dark/light font color swapping not working in dark mode 2020-05-31 15:58:21 +02:00
Stefan Zermatten
d24fb5661d re-enabled computation on client side for optimistic UI 2020-05-30 23:56:55 +02:00
Stefan Zermatten
4bdc254627 Improved item viewer significantly, including increment button. 2020-05-30 23:36:27 +02:00
Stefan Zermatten
db652ac47f Update paragon avatar 2020-05-30 19:28:54 +02:00
Stefan Zermatten
32c9283569 Library items can now correctly store icons 2020-05-30 18:19:57 +02:00
Stefan Zermatten
060c44f384 Added svg icons, currently only for items 2020-05-30 18:04:48 +02:00
Stefan Zermatten
8138cd98f1 Added quantities to item tree views 2020-05-30 12:52:15 +02:00
Stefan Zermatten
5195e3fad5 Made health bar input accept negative sign from copy-paste and mobile (hopefully) 2020-05-30 12:45:11 +02:00
Stefan Zermatten
eb97a98644 removed dead reference to reset multipliers 2020-05-30 12:34:00 +02:00
Stefan Zermatten
51845c62a7 Skill base values now work in a way consistent with attribute base values 2020-05-30 12:32:05 +02:00
Stefan Zermatten
56cd48da9d Fixed health bars not hiding 2020-05-29 00:38:12 +02:00
Stefan Zermatten
298f659829 Fixed broken import 2020-05-28 23:48:01 +02:00
Stefan Zermatten
b99d1a00f5 Fixed small issues with hit dice on long rest. rests trigger recomputations now 2020-05-28 23:43:03 +02:00
Thaum Rystra
15ad8b1f5d Added short and long rest buttons, closes #87 2020-05-28 23:17:25 +02:00
Thaum Rystra
d4804e5292 Made minimum variable name 2 characters long 2020-05-28 21:26:31 +02:00
Thaum Rystra
36c23e1eb5 Made hiding stats that aren't targeted by effects or proficiencies an option 2020-05-28 21:06:40 +02:00
Thaum Rystra
9236f3e477 Added calculation errors to attributes and toggles 2020-05-28 20:33:08 +02:00
Thaum Rystra
cd413ba64f Added icon for set effects 2020-05-28 20:17:16 +02:00
Thaum Rystra
2c671acf72 Made sure effects without calculations don't have computed results 2020-05-28 20:14:19 +02:00
Thaum Rystra
44e726417e Convert mathjs objects to strings in evaluations 2020-05-28 20:10:33 +02:00
Thaum Rystra
7f2401da81 Referencing a missing variable in an effect now returns zero, not an error 2020-05-28 19:58:52 +02:00
Thaum Rystra
d31f980002 Added paragon's title 2020-05-28 17:25:44 +02:00
Thaum Rystra
4c8512af80 Rounding only occurs on numbers, preventing uneccessary type casting of attribute values 2020-05-28 16:06:00 +02:00
Thaum Rystra
edf68b1355 Properties in dropdowns are sorted by order again, rather than name 2020-05-28 15:59:04 +02:00
Thaum Rystra
868b9e11fa Added 'set' operation to effects, it overrides all other numerical effects 2020-05-28 15:58:48 +02:00
Thaum Rystra
14f5c3e797 improved field naming for damage multiplier tag targeting 2020-05-28 15:47:02 +02:00
Thaum Rystra
66e25c53d0 Fixed paragon's avatar image 2020-05-28 15:46:39 +02:00
Thaum Rystra
7a75d34246 Added healing damage type 2020-05-28 15:41:46 +02:00
Thaum Rystra
70a6c817cb Organised images, added about page, tweaked home page 2020-05-28 15:27:55 +02:00
Thaum Rystra
56879f1911 Removing a property in the character sheet tree now unselects that property 2020-05-28 13:03:35 +02:00
Thaum Rystra
6d12bcb063 Public libraries no longer require login to view 2020-05-28 13:00:03 +02:00
Thaum Rystra
1c26b7717c Fixed saving throw fields that weren't working, added name to saving throws 2020-05-28 12:29:41 +02:00
Thaum Rystra
a41b267364 Use embedded property dialog in tree tab. Colors for creature properties 2020-05-25 19:36:14 +02:00
Thaum Rystra
dfb144b8dc Added color picking to library properties 2020-05-25 19:09:55 +02:00
Thaum Rystra
2859bf0e00 Added fade transition to library dialog 2020-05-25 18:41:22 +02:00
Thaum Rystra
469822d4d7 In organize mode, new library properties get placed under the selected node 2020-05-25 18:33:38 +02:00
Thaum Rystra
c7de96c8c3 Added "move" button to library property menu 2020-05-25 18:15:35 +02:00
Thaum Rystra
f7cbee27f9 Made selecting a property from a library use the mobile friendly library 2020-05-25 17:39:25 +02:00
Thaum Rystra
b61dd6e81a Added maximum length of ancestors array 2020-05-25 17:25:49 +02:00
Thaum Rystra
add0cac31d Added "duplicate" option to library properties 2020-05-25 17:23:36 +02:00
Thaum Rystra
e9c643699c Made tab swiping sync with the tab list 2020-05-25 17:07:38 +02:00
Thaum Rystra
3ec0f9500c Overhauled library UI to work on small screens 2020-05-25 16:43:28 +02:00
Thaum Rystra
a55c1382b1 Fixed skills not computing below zero 2020-05-24 04:30:14 +02:00
Thaum Rystra
7571806cd0 Made user profiles optional 2020-05-23 12:07:42 +02:00
Thaum Rystra
afa641a290 made ids optional in users publication 2020-05-21 16:52:36 +02:00
Thaum Rystra
81131ddb9f Allowed non-patreons to view, but not edit, sheets and libraries 2020-05-21 16:50:06 +02:00
Thaum Rystra
7a442d8fb9 Improved publications to be reactive to permission changes 2020-05-21 15:13:30 +02:00
Thaum Rystra
26d836767b Fixed some broken forms 2020-05-21 14:57:35 +02:00
Thaum Rystra
c4a52ca744 Display an error if the character can't be found or viewed 2020-05-21 13:36:59 +02:00
Thaum Rystra
b640ce457f Removed unused story files 2020-05-21 12:49:40 +02:00
Thaum Rystra
13a0d66433 Disabled various buttons when the user doesn't have edit permission 2020-05-21 12:47:02 +02:00
Thaum Rystra
47aad6d186 Added UI to unshare a view-only character with yourself 2020-05-20 16:52:05 +02:00
Thaum Rystra
32eb85a099 Refactored all forms to disable all fields without edit permission 2020-05-20 16:40:47 +02:00
Thaum Rystra
048b150c88 Significantly improved performance of interacting with large library trees 2020-05-19 01:28:29 +02:00
Thaum Rystra
65d367942e Got cards to behave themselves in columns and not overflow width 2020-05-19 01:03:18 +02:00
Thaum Rystra
e0fc5abe7b Improved invite landing page UI 2020-05-19 00:28:31 +02:00
Thaum Rystra
18c9474570 Separated attacks and actions into two separate cards 2020-05-19 00:19:23 +02:00
Thaum Rystra
4a039e769b Added viewers, tree node viewers and fixed forms for new damage schema 2020-05-19 00:15:05 +02:00
Thaum Rystra
93ab67a91b Fixed failure to recompute creature on tree reorganize 2020-05-18 23:53:34 +02:00
Thaum Rystra
4352ca5f0d Inventory items can now be equipped 2020-05-18 23:03:34 +02:00
Thaum Rystra
fe11c9ec23 Tree nodes are no longer lazy - smoother animations when expanding nodes 2020-05-18 20:22:48 +02:00
Thaum Rystra
37d6b32ea3 Action 'uses' now shows up as the computed value 2020-05-18 20:22:00 +02:00
Thaum Rystra
7592332637 Fixed setDocToLastOrder not working with new ordering design 2020-05-18 20:09:21 +02:00
Thaum Rystra
397ff82c43 Organizing the tree now causes a character recomputation where relevant 2020-05-18 19:58:28 +02:00
Thaum Rystra
7e3815a699 Fixed glitchy reordering of trees 2020-05-18 02:03:31 +02:00
Thaum Rystra
9214529284 rewrote entire ordering structure for ancestor trees 2020-05-18 02:03:14 +02:00
Thaum Rystra
60f5588e7d Prevented description viewers from keeping zombie text after description is deleted 2020-05-17 18:13:06 +02:00
Thaum Rystra
ad3bec3521 Began working on bringing forms and UI in line with data structure overhaul 2020-05-17 00:06:19 +02:00
Thaum Rystra
ca5ded7ded Class levels now recompute properly 2020-05-16 22:51:17 +02:00
Thaum Rystra
5c0a2a4d6c Overhauled computations to allow for toggles :'( that sucked 2020-05-16 22:03:21 +02:00
Thaum Rystra
7024adecaf created a general way to fetch the active properties of an ancestor 2020-05-16 17:08:57 +02:00
Thaum Rystra
9b6f259358 Library node edit form no longer uses stored variants 2020-05-16 14:27:54 +02:00
Thaum Rystra
3642d1d249 Workaround for Firefox not obeying break-inside: avoid; 2020-05-16 14:19:13 +02:00
Thaum Rystra
cd2727b61c Can now link google account 2020-05-16 14:07:22 +02:00
Thaum Rystra
acb9dc342a Improved handling of character avatars, added portraits 2020-05-16 13:40:54 +02:00
Thaum Rystra
d59d8cb54f Library insert forms no longer used stored variants 2020-05-16 12:51:59 +02:00
Thaum Rystra
2c988b8717 Fixed an error where incorrectly targeted effects would cause computation error 2020-05-15 17:27:34 +02:00
Thaum Rystra
3af48649f7 Added some guards against missing properties 2020-05-15 16:51:58 +02:00
Thaum Rystra
79e03e0e63 Separated tool, weapon, armor, and language proficiencies into separate cards 2020-05-15 16:43:37 +02:00
Thaum Rystra
2d788f0c07 ability scores now pass on their skill effects to checks and skills 2020-05-15 16:38:28 +02:00
Thaum Rystra
891fd00b5f Skills now correctly denormalise their passive bonus, conditional benefits, advantage, and fail effects 2020-05-15 16:23:57 +02:00
Thaum Rystra
41b05064c8 login redirects now carry over to the register page 2020-05-15 15:16:59 +02:00
Thaum Rystra
cf110db67d Effect stats input now uses chips 2020-05-15 15:09:46 +02:00
Thaum Rystra
db696574f5 Comboboxes now clear search text when selecting an option 2020-05-15 15:09:34 +02:00
Thaum Rystra
4478628200 Reworked how bare symbols are handled, which should fix simplification 2020-05-15 14:44:08 +02:00
Thaum Rystra
cd8a557120 Let effects autofill skills as well as attributes 2020-05-15 14:29:07 +02:00
Thaum Rystra
5f95471bb6 Added transitions to tree tab property viewer 2020-05-15 14:23:32 +02:00
Thaum Rystra
a62726ae3a Allowed long property viewers in tree tab to scroll 2020-05-15 13:57:09 +02:00
Thaum Rystra
59fa5bcd8c Improved spacing on cards 2020-05-15 13:54:43 +02:00
Thaum Rystra
4b3f068d87 Fixed character sheet tabs not taking up the full screen height 2020-05-15 13:38:31 +02:00
Thaum Rystra
a771d896a8 Prevented dialog titles from overflowing 2020-05-15 13:31:07 +02:00
Thaum Rystra
b439befd47 Stopped tree view overflowing horizontally 2020-05-15 13:23:34 +02:00
Thaum Rystra
b44a18c28c Made tree tab work on mobile. prevented overflow of long titles 2020-05-15 13:18:27 +02:00
Thaum Rystra
bae2f4181a removed old default docs 2020-05-15 11:10:30 +02:00
Thaum Rystra
9b46a91641 Fixed missing Creature imports 2020-05-15 11:10:21 +02:00
Thaum Rystra
fc9467177b hotfix to prevent character sheet going black if DamageMultipliers isn't present 2020-05-14 15:32:54 +02:00
Thaum Rystra
136dd5fd29 Removed stray logging 2020-05-14 15:25:58 +02:00
Thaum Rystra
cb34363a4e Damage multipliers now compute and show up on the character sheet 2020-05-14 15:22:23 +02:00
Thaum Rystra
a4d6adacff Fixed damage multiplier forms and viewers 2020-05-14 13:43:12 +02:00
Thaum Rystra
29588a87d0 Fixed dependency loops causing a stack overflow. Added level variable 2020-05-14 13:32:46 +02:00
Thaum Rystra
97fcb76454 Added if function 2020-05-13 10:42:20 +02:00
Thaum Rystra
9069ee8e35 Improved effect, skill, and attribute viewers 2020-05-13 10:28:39 +02:00
Thaum Rystra
170bac6934 Pruned unused components 2020-05-13 09:43:01 +02:00
Thaum Rystra
809426b183 Improved effect components 2020-05-13 09:42:24 +02:00
Thaum Rystra
e8728166a9 Moved effect components to components folder 2020-05-13 09:34:00 +02:00
Thaum Rystra
5046a847cf Quality pass over all publications, fixed public documents permission error 2020-05-13 09:29:29 +02:00
Thaum Rystra
b6c7ea8c4f Improved handling of tiers, made guest tier funcitonal, improved invites 2020-05-12 15:28:43 +02:00
Thaum Rystra
bbda0ea1b6 Invites can now be managed to some extent 2020-05-12 14:11:43 +02:00
Thaum Rystra
47206ccfc4 Continued implementing sharing 2020-05-12 12:27:24 +02:00
Thaum Rystra
dd213feb0a Added label to username edit field 2020-05-09 13:53:47 +02:00
Thaum Rystra
30ab216dc1 removed redundant publications 2020-05-09 13:45:11 +02:00
Thaum Rystra
ea88a9c41e Libraries can now be shared from the library edit dialog 2020-05-09 13:40:01 +02:00
Thaum Rystra
f0e22dc1ca Made feedback page go to discord 2020-05-09 13:33:33 +02:00
Thaum Rystra
8c608937bb Username can now be changed 2020-05-09 13:15:03 +02:00
Thaum Rystra
c3ed3d55ce Locked friends page since it's not implemented 2020-05-08 17:12:03 +02:00
Thaum Rystra
323cac9405 corrected not implemented page to 'beta' instead of 'alpha' 2020-05-08 16:58:01 +02:00
Thaum Rystra
62689174e2 removed stray console.log 2020-05-08 16:31:58 +02:00
Thaum Rystra
6c8e9037e1 Fixed function being replaced with object['value'] accessors 2020-05-08 16:20:00 +02:00
Thaum Rystra
e934f92e8b Removed starter characters from home page, fixed social buttons not displaying 2020-05-08 16:10:04 +02:00
Thaum Rystra
8aea5c4fc6 Fixed property viewer showing zombie fields from previously viewed properties 2020-05-08 16:04:49 +02:00
Thaum Rystra
5f87bbc4f5 Used custom labels on tree tab buttons to prevent tooltip formatting shenanigans 2020-05-08 15:59:59 +02:00
Thaum Rystra
4d0ff52853 Removing effects or other properties now correctly triggers a recalculation of the character 2020-05-08 15:51:54 +02:00
Thaum Rystra
6506cfd727 Removed extraneous variable name field from spell list form 2020-05-08 15:49:16 +02:00
Thaum Rystra
f486c3f176 Fixed armor proficiency skilltype not existing 2020-05-08 15:47:36 +02:00
Thaum Rystra
eed11e8833 fixed attacks not displaying if no actions display 2020-05-08 15:43:50 +02:00
Thaum Rystra
46585406df Fixed buffs not being insertable 2020-05-08 15:42:31 +02:00
Thaum Rystra
5597acb0ea Fixed spells not persisting checkbox values 2020-05-08 15:37:07 +02:00
Thaum Rystra
3fa9077124 fixed typo, added tier 2020-05-07 16:14:50 +02:00
Thaum Rystra
cb8d311fdc Merge branch 'version-2' of https://github.com/ThaumRystra/DiceCloud into version-2 2020-05-07 15:53:52 +02:00
Thaum Rystra
eb3f8c9105 fixed countdown page for not-logged in users 2020-05-07 15:53:24 +02:00
Thaum Rystra
82a2241e4d fixed countdown page for not-logged in users 2020-05-07 15:50:44 +02:00
Thaum Rystra
3804323322 Added countdown page 2020-05-07 15:42:42 +02:00
Thaum Rystra
31185a4b12 Invite system created, mostly done, but timeboxed for now 2020-05-07 14:52:18 +02:00
Thaum Rystra
4ca47d3a62 Improved account page 2020-05-02 18:22:04 +02:00
Thaum Rystra
26662b939c Improved data risk warning 2020-05-02 18:21:55 +02:00
Thaum Rystra
48e54c42b4 Improved usability and UI for Features. Fixed embedded computations 2020-05-02 17:54:22 +02:00
Thaum Rystra
d649fb9d54 Greatly improved look and feel of site navigation 2020-05-02 17:09:56 +02:00
Thaum Rystra
073578b90d Made all login Patreon only, limited some functionality to $5 patrons 2020-04-30 22:38:27 +02:00
Thaum Rystra
1ca6bc834a Removed variable names from items, now only use tags. Items can also be attuned 2020-04-30 14:52:41 +02:00
Thaum Rystra
4180e153dd Computation now handles class levels 2020-04-30 14:26:41 +02:00
Thaum Rystra
15db76a2fe Added proficiencies for languages tools and weapons to the stats tab 2020-04-30 14:26:28 +02:00
Thaum Rystra
1763358642 Ui tweaks 2020-04-29 14:00:58 +02:00
Thaum Rystra
f63faa867b Fixed deleted health bars still showing on character sheet 2020-04-29 12:48:21 +02:00
Thaum Rystra
a2fe8d950a Fixed computations throwing errors when not provided with context 2020-04-29 12:44:09 +02:00
Thaum Rystra
d63565633e Zero value stats now appear in character sheet 2020-04-29 11:10:23 +02:00
Thaum Rystra
4fd62d17bd Updated markdown santizing because the old implementation was deprecated 2020-04-29 11:03:47 +02:00
Thaum Rystra
ed9cfee9f9 Added autocomplete for fields that expect variable names 2020-04-29 10:53:06 +02:00
Thaum Rystra
966fcc449d Hit dice now explicitly set their size 2020-04-28 17:12:22 +02:00
Thaum Rystra
eae35062d0 Allowed attributes to take calculations as their base value 2020-04-28 16:23:52 +02:00
Thaum Rystra
ee0fb72d56 Ensured that properties that are removed don't show up on the character sheet 2020-04-28 14:51:53 +02:00
Thaum Rystra
528e53fc9b Fixed proficiency calculations 2020-04-27 13:56:04 +02:00
Thaum Rystra
17c9d270e6 Fixed modifiers not being computed and displayed 2020-04-27 13:19:23 +02:00
Thaum Rystra
a2bae8d12a Fixed an issue with clearing forms not correctly unsetting a value 2020-04-26 11:37:51 +02:00
Thaum Rystra
13cb9253c3 Added resource forms to actions 2020-04-26 10:28:40 +02:00
Thaum Rystra
88ed912d18 in mathjs node.eval is deprecated, replaced with .evaluate 2020-04-26 10:28:23 +02:00
Thaum Rystra
6746f5f989 Added results viewer 2020-04-26 09:42:32 +02:00
Thaum Rystra
b1328e4cf5 Action and attack components show up correctly on character sheet 2020-04-24 15:10:58 +02:00
Thaum Rystra
7bf0e959d7 reinstalled all npm packages 2020-04-24 14:07:42 +02:00
Thaum Rystra
ed35d2e984 Began work on viewers for attacks and actions 2020-04-23 19:32:48 +02:00
Thaum Rystra
a6bdfe247c Removed multipliers from reset field 2020-04-23 18:55:13 +02:00
Thaum Rystra
a86176a20d fixed action reset menu clearing throwing an error 2020-04-23 15:37:30 +02:00
Thaum Rystra
4e57bd4a73 Unified the add buttons for results as a single menu button 2020-04-23 15:37:05 +02:00
Thaum Rystra
95bfcd79c9 Added UI backend that can do computations with context 2020-04-23 14:26:05 +02:00
Thaum Rystra
7416101a34 Computation writes variables available/computed to the creature document 2020-04-16 17:57:18 +02:00
Thaum Rystra
2164174218 Fixed all effects getting added to unassigned effects even when they were assigned 2020-04-16 15:48:07 +02:00
Thaum Rystra
1717ee4bc7 Made spells into a special kind of action 2020-04-16 15:21:31 +02:00
Thaum Rystra
e06196a54c Fixed buffs not being able to be added to actions after creation 2020-04-04 19:09:00 +02:00
Thaum Rystra
6008d8b47a Made parent target of forms optional again 2020-04-04 18:44:57 +02:00
Thaum Rystra
e77513110b Refactored all forms for updated code style 2020-04-04 18:40:08 +02:00
Thaum Rystra
1856e90d12 Changed data structure around attacks and their consumed resources 2020-04-04 18:18:38 +02:00
Thaum Rystra
97111741bf updated dependencies 2020-04-04 14:30:22 +02:00
Thaum Rystra
fc5576397a Added quote rules to eslint 2020-04-04 11:45:08 +02:00
Thaum Rystra
53b1f16d2f replaced jscsrc and jshint with eslint 2020-04-04 11:31:07 +02:00
Stefan Zermatten
2981813751 Creature computations working again 2020-03-23 11:59:04 +02:00
Stefan Zermatten
74fef2bd39 Refactored computations again, split into multiple files, lots still to do 2020-03-17 16:13:18 +02:00
Stefan Zermatten
1a0c2bca78 Began refactoring character computations 2020-03-16 17:28:53 +02:00
Stefan Zermatten
5ed8e08993 Fixed effects not being able to be added to stored buffs 2020-03-16 14:16:50 +02:00
Stefan Zermatten
e3c6949491 Added search to character tree tab 2020-03-13 16:09:43 +02:00
Stefan Zermatten
e0e7693fff Improved app layout a bit 2020-03-13 14:56:59 +02:00
Stefan Zermatten
7fe2292c2a Added persona page 2020-03-13 14:56:43 +02:00
Stefan Zermatten
9290c9570c Moved organize buttons for inventory and spells tab to the right 2020-03-13 12:23:23 +02:00
Stefan Zermatten
24725381d7 Added spells tab 2020-03-13 12:02:57 +02:00
Stefan Zermatten
c1aacb9ebe Containers that have ancestor containers no longer show up in top level inventory 2020-03-13 11:42:19 +02:00
Stefan Zermatten
0e71d1c719 Moved spell lists to advanced section of spells form 2020-03-13 11:39:30 +02:00
Stefan Zermatten
2381769ea2 Inventory now uses filtered tree views to display items in containers 2020-03-13 11:23:19 +02:00
Stefan Zermatten
adfe1dc613 Markdown now works in property descriptions for viewers 2020-03-13 10:12:01 +02:00
Stefan Zermatten
27300190d3 Clicking container headers now works as expected 2020-03-12 16:51:50 +02:00
Stefan Zermatten
d90894d7c6 Fixed items not showing up in containers 2020-03-12 16:43:06 +02:00
Stefan Zermatten
9e7c1ce405 Get vue-meteor-tracker to freeze meteor data to stop a infinite flush loops 2020-03-12 16:40:37 +02:00
Stefan Zermatten
d00ff000ce Added inventory tab 2020-03-12 15:51:49 +02:00
Stefan Zermatten
e26031368d fixed inserting library node not finding the node to animate dialog to 2020-03-09 15:07:08 +02:00
Stefan Zermatten
9ac6b510e4 Added tree of sub-properties to property dialog 2020-03-09 14:29:11 +02:00
Stefan Zermatten
e67b4c72e3 Removed stray console log 2020-03-09 12:58:14 +02:00
Stefan Zermatten
1c700121ca removed accounts-meld 2020-03-09 12:56:40 +02:00
Stefan Zermatten
8e4694f63e Removed enabled/disabled property of features 2020-03-09 12:44:30 +02:00
Stefan Zermatten
827d567ac2 Fixed stats being displayed for all subscribed creatures instead of the current creature 2020-03-09 12:34:11 +02:00
Stefan Zermatten
43a08eb034 Health bar refactored to not use keycodes, which are deprecated 2020-03-09 12:08:39 +02:00
Stefan Zermatten
ea5ac42ec0 fixed padding on dark mode button 2020-03-09 10:47:39 +02:00
Stefan Zermatten
1be09e48ef Clearable selects now unset the property when cleared 2020-03-09 10:44:40 +02:00
Stefan Zermatten
625455da09 Re-organized ui/properties folder 2020-03-06 10:15:38 +02:00
Stefan Zermatten
4a25c22b64 Fixed spell and resource card adjustments 2020-03-06 10:02:39 +02:00
Stefan Zermatten
53e9be1407 Removed fibres from dependencies, meteor should come with it 2020-03-06 09:15:04 +02:00
Stefan Zermatten
7055f4d990 Added node versions to the package file to help the host build the app 2020-03-06 08:57:13 +02:00
Stefan Zermatten
797b6a0d88 Updated Meteor and npm packages 2020-03-05 15:31:13 +02:00
Stefan Zermatten
7afbfa1816 Fixed no-op bulk writes 2020-03-05 14:28:32 +02:00
Stefan Zermatten
d6877905c9 removed empty console error 2020-03-05 14:09:21 +02:00
Stefan Zermatten
69e8a307fc Removed a bunch of console noise 2020-03-05 14:05:32 +02:00
Stefan Zermatten
66e70c8c94 Libraries can now be deleted 2020-03-05 14:00:16 +02:00
Thaum Rystra
dfa3b057b0 Fixed property adjustments on the stats page 2020-03-04 09:56:06 +02:00
Thaum Rystra
f3d86ef274 Characters can now be deleted 2020-03-03 17:49:35 +02:00
Thaum Rystra
46a0e92402 Improved sharing dialog, setting a sheet as public now working 2020-03-03 17:00:05 +02:00
Stefan Zermatten
d0c8131d5f Fixed syntax errors breaking the build 2020-03-02 16:35:16 +02:00
Stefan Zermatten
5578dca6e9 began implementing sharing dialog 2020-03-02 16:31:57 +02:00
Stefan Zermatten
724f9574a2 Added basic creature document editing UI 2020-03-02 15:45:55 +02:00
Stefan Zermatten
2bd7c2908f Added remove creature method 2020-03-02 10:03:58 +02:00
Thaum Rystra
692a7983f8 Fixed inserting new characters. No wizard though 2020-02-29 17:31:21 +02:00
Stefan Zermatten
acb9369100 Removed floating action button from features tab 2020-02-18 15:44:32 +02:00
Stefan Zermatten
21dbab1877 Improved effect form and view 2020-02-18 14:05:15 +02:00
Stefan Zermatten
86926e593c Got creature recomputation to do things in sheet 2020-02-18 13:27:31 +02:00
Stefan Zermatten
81cec77c9e Added write methods to creature properties 2020-02-18 12:14:38 +02:00
Stefan Zermatten
acb8a073de Updated packes 2020-02-18 12:14:24 +02:00
Stefan Zermatten
6996d2a99e updated node packages 2020-02-10 09:53:59 +02:00
Stefan Zermatten
83b810ce3d Specified that variable names must be used in ammunition references 2020-01-27 13:50:27 +02:00
Stefan Zermatten
328480d2c5 Sidebar no longer refers to "alpha" build in preparation for beta release 2020-01-27 13:50:11 +02:00
Stefan Zermatten
8e4c6252cd Added a universal dialog for creature properties 2020-01-27 13:49:50 +02:00
Stefan Zermatten
c3cc4c881d Migrated creature computations to use the new data structure for creature properties 2020-01-27 11:21:52 +02:00
Stefan Zermatten
4ee7307e34 Migrating character sheet to new data format 2020-01-16 08:55:53 +02:00
Stefan Zermatten
eabc0aa32e Groundwork for default libraries and slots 2019-11-13 11:54:27 +02:00
Stefan Zermatten
ae0b060f01 Added inserting library subtrees as character property subtrees 2019-11-05 10:56:04 +02:00
Stefan Zermatten
79a4488a28 Fixed a layout issue with library and node view 2019-11-04 13:38:42 +02:00
Stefan Zermatten
dfa13ef2c3 Disabled character list 2019-11-04 13:30:46 +02:00
Stefan Zermatten
f6d80f6ae4 Made big improvements to library page in preparation of adding library nodes to character sheets 2019-11-04 13:27:31 +02:00
Stefan Zermatten
f4d613a20b Started working on getting creature property insertion working 2019-09-27 11:06:33 +02:00
Stefan Zermatten
73f193460d Fixed Roll form 2019-09-26 11:29:46 +02:00
Stefan Zermatten
ba4cc1a496 Fixed more odering bugs, still flashes before being sorted though :( 2019-09-25 16:08:01 +02:00
Stefan Zermatten
21030d186a Added saving throws, messed with data structure a bunch, and updated forms to suit 2019-09-25 14:38:20 +02:00
Stefan Zermatten
4fcae9b3f9 Fixed a bug where moving documents around deleted docs breaks the ordering 2019-09-25 14:37:54 +02:00
Stefan Zermatten
66d11d58f3 Data changes to make attacks top level objects 2019-09-19 16:03:46 +02:00
Stefan Zermatten
63a20b2bef Moved subschema folder 2019-09-16 10:24:13 +02:00
Stefan Zermatten
88a2a506b0 Renamed RollResult to RollResults 2019-09-16 09:39:04 +02:00
Stefan Zermatten
710c578119 Fiddled with rolls and saves 2019-09-02 13:49:01 +02:00
Stefan Zermatten
a8b3fc3f2f Started restructuring the library with attacks, saves, and limited parenting 2019-08-12 16:42:30 +02:00
Stefan Zermatten
6f4710bee3 Roll modifiers are effects that target rolls based on their tags 2019-08-06 16:37:52 +02:00
Stefan Zermatten
50621ca269 Damage multipliers can now target or exclude tags 2019-08-06 16:37:38 +02:00
Stefan Zermatten
16d61eb708 Effects can now impact multiple stats 2019-08-06 16:36:37 +02:00
Stefan Zermatten
b110eadb7c Rerouted pages that shouldn't work to an info alert making that clear 2019-08-06 13:17:04 +02:00
Stefan Zermatten
45c84e28a3 Improved property viewers, added some new ones 2019-08-06 13:16:46 +02:00
Stefan Zermatten
b70634f5de removed old property components 2019-08-06 10:37:37 +02:00
Stefan Zermatten
e3f18fab69 Moved sidebar component to layout folder 2019-08-06 10:37:25 +02:00
Stefan Zermatten
56f3af3c4b Client can't read node environment... changed storybook flag to public setting 2019-08-06 10:13:55 +02:00
Stefan Zermatten
a58def26d1 Added storybook to production if SHOW_STORYBOOK env variable is true 2019-08-06 10:03:27 +02:00
Stefan Zermatten
0014c691d2 Added alternative text for missing property viewers 2019-08-05 12:10:50 +02:00
Stefan Zermatten
5436b12108 Added more property viewers 2019-08-05 12:04:26 +02:00
Stefan Zermatten
d45b184170 moved effect viewer 2019-08-02 13:00:16 +02:00
Stefan Zermatten
0b184c4e85 Library node editing now includes editing sub-documents and soft removal 2019-08-02 12:35:59 +02:00
Stefan Zermatten
745f4fd353 Library nodes can now be edited :D 2019-08-02 10:47:29 +02:00
Stefan Zermatten
3c4f3e26f8 Added edit button to library node view 2019-08-01 16:06:43 +02:00
Stefan Zermatten
67ea67148f Added a description field to attributes 2019-08-01 15:45:15 +02:00
Stefan Zermatten
f37ff919fb Fixed library node creation dialog 2019-08-01 15:25:52 +02:00
Stefan Zermatten
76b6501b31 Improved library view layout 2019-08-01 14:39:15 +02:00
Stefan Zermatten
229a5dddcf Added attribute property viewer, incomplete 2019-08-01 14:03:51 +02:00
Stefan Zermatten
e29c77dc67 Changed experience title to name for consistency 2019-08-01 14:03:29 +02:00
Stefan Zermatten
c87a3a3f60 Moved ui/forms to ui/properties/forms 2019-08-01 12:07:57 +02:00
Stefan Zermatten
9f7d6b8ae7 began work on generalized property viewer 2019-08-01 12:03:40 +02:00
Stefan Zermatten
4ccf999fc7 Trees can now do selection 2019-08-01 12:03:15 +02:00
Stefan Zermatten
549418b395 Iteration on library UI 2019-07-31 15:04:52 +02:00
Stefan Zermatten
14fe48efb3 Moved imports/ui/creature/properties to /imports/ui/properties 2019-07-31 12:02:26 +02:00
Stefan Zermatten
18eeaf4884 Added back button to library page 2019-07-31 12:00:49 +02:00
Stefan Zermatten
4f93ad3e9b Trees can now be freely re-arranged :D 2019-07-31 11:52:11 +02:00
Stefan Zermatten
d0304da4fd Began making generic tree re-arranging methods, still buggy 2019-07-30 16:47:21 +02:00
Stefan Zermatten
4b7ff2146f Purged all references to the schema factory, use SCHEMA_OPTIONS constant instead 2019-07-30 15:21:30 +02:00
Stefan Zermatten
2385b69720 Removed all separate property collections to be replaced with a single "creature property" collection 2019-07-30 15:13:39 +02:00
Stefan Zermatten
31bc3663a7 Moved properties out of creature folder, since they apply to library nodes as well now 2019-07-30 14:50:08 +02:00
Stefan Zermatten
cbdd72e09b Some changes to how parenting and ordering of docs interface 2019-07-30 14:48:49 +02:00
Stefan Zermatten
438f128641 Moved parenting folder from /api/creatures/ to /api/ 2019-07-30 13:07:31 +02:00
Stefan Zermatten
3460cbf5a0 Significantly improved tree view of libraries 2019-07-30 12:49:20 +02:00
Stefan Zermatten
da561bfc83 Added night mode to account settings 2019-07-30 10:09:01 +02:00
Stefan Zermatten
eb63a7a7f3 made XP optional in experiences, so that experiences can just be journal entries 2019-07-30 10:01:23 +02:00
Stefan Zermatten
b5ceb7fad0 updated npm packages 2019-07-30 09:59:18 +02:00
Stefan Zermatten
fb5b2b8ada Removed creature from property selector, creatures aren't properties 2019-07-30 09:59:10 +02:00
Stefan Zermatten
ea628ed379 Fixed some broken switches 2019-07-29 13:25:58 +02:00
Stefan Zermatten
d35fa447a3 Added item and container forms 2019-07-29 13:25:47 +02:00
Stefan Zermatten
73b43574ee Added spell and spell list forms 2019-07-29 12:49:19 +02:00
Stefan Zermatten
02cb690325 Spells now reference their spell lists directly 2019-07-25 15:44:45 +02:00
Stefan Zermatten
5662ce3666 Added tags to actions 2019-07-25 14:04:36 +02:00
Stefan Zermatten
45e36ac3d4 Added skill form, abstracted proficiency selection 2019-07-23 12:37:30 +02:00
Stefan Zermatten
812a6679d5 Added roll form 2019-07-23 12:02:19 +02:00
Stefan Zermatten
1bfb48c672 Added forms for Class level, damage multiplier, experience, folder, note, proficiency 2019-07-23 11:28:26 +02:00
Stefan Zermatten
946b47ea61 Gave buff targets a default value 2019-07-22 13:51:32 +02:00
Stefan Zermatten
bd3f676919 Fixed some import errors 2019-07-22 13:45:30 +02:00
Stefan Zermatten
4062d79e90 Reorganized forms into their own folder 2019-07-22 13:05:11 +02:00
Stefan Zermatten
dfa302a4a9 Completed Action form, effects form, reworked form event api 2019-07-22 12:43:57 +02:00
Stefan Zermatten
baa1c0967c Update meteor and npm packages to fix security vulnerabilities 2019-07-19 14:27:34 +02:00
Stefan Zermatten
a0dc36557c Added action form, but without buffs 2019-07-19 14:07:22 +02:00
Stefan Zermatten
0c002ae5cd Moved some fields to advanced section of attribute form 2019-07-18 16:25:52 +02:00
Stefan Zermatten
11d3b0fa8d Improved style of attribute form 2019-07-18 14:36:39 +02:00
Stefan Zermatten
d28d6de684 Inserting library nodes into libraries now works as expected 2019-07-03 14:19:16 +02:00
Stefan Zermatten
93d8a8d33e Library attribute insert form complete 2019-07-02 17:28:07 +02:00
Stefan Zermatten
4abb5edbf3 Got library attribute form onto the screen :D 2019-06-28 14:48:02 +02:00
Stefan Zermatten
9757da2cae Started work on library node insert forms 2019-06-27 16:52:28 +02:00
Stefan Zermatten
bd4fb58935 updated packages 2019-06-24 10:28:44 +02:00
Stefan Zermatten
1add44f0e7 renamed SmartInput to SmartInputMixin 2019-06-24 10:28:33 +02:00
Stefan Zermatten
99db45e522 Added function to convert libraryNode lists into forest of object trees 2019-06-20 16:43:18 +02:00
Stefan Zermatten
7a59b4542e added library publication 2019-06-20 16:42:05 +02:00
Stefan Zermatten
335608f6a9 Fixed library subscription 2019-06-14 13:07:26 +02:00
Stefan Zermatten
4d47584f4f Routed library page and cleaned up errors 2019-06-13 17:07:31 +02:00
Stefan Zermatten
48fe0d3608 Started on library page UI 2019-05-31 15:36:19 +02:00
Stefan Zermatten
9a194a20cb Added library list UI 2019-05-10 13:05:21 +02:00
Stefan Zermatten
de183297fc Updated some NPM packages to fix vulnerabilities 2019-05-10 11:32:43 +02:00
Stefan Zermatten
d921ad46d8 Merge pull request #215 from pspeter3/docker-development
Add Docker Compose
2019-04-29 09:37:34 +02:00
Phips Peter
00d02a3bb5 Add Docker Compose
In order for a developer to run DiceCloud locally, all they need to do is run `docker-compose up --build`. This does take a very long time to execute but prevents a developer from needing to deal with configuration.
2019-04-25 16:48:27 -07:00
Stefan Zermatten
b0caffae1a Added better data to the Tree node list story 2019-04-24 14:05:16 +02:00
Stefan Zermatten
e71bfb2691 Improved tree view 2019-04-24 13:22:31 +02:00
Stefan Zermatten
fc24cf4a5b Improved tree view with drag and drop 2019-04-15 15:40:42 +02:00
Stefan Zermatten
062e554629 Added comments to libraries 2019-04-15 12:39:42 +02:00
Stefan Zermatten
a1d77cdaab added update method to library nodes 2019-04-15 12:27:14 +02:00
Stefan Zermatten
f4011abf7b Moved all creature related API to the creature folder 2019-04-15 12:09:37 +02:00
Stefan Zermatten
aa9802b34e Iterated on library nodes 2019-04-15 12:04:17 +02:00
Stefan Zermatten
dabb54b0a3 Refactored UI folder structure 2019-04-15 11:44:27 +02:00
Stefan Zermatten
05867c61dd Started on tree view for real 2019-04-12 13:37:47 +02:00
Thaum Rystra
ea968ad955 Merge branch 'version-2' of https://github.com/ThaumRystra/DiceCloud into version-2 2019-04-06 10:56:57 +02:00
Thaum Rystra
b7b0ac9c00 Separated parser class nodes and began writing compile methods 2019-04-06 10:56:53 +02:00
Stefan Zermatten
ab2ea36541 Added markdown support to features 2019-04-05 13:05:45 +02:00
Stefan Zermatten
b35e08b9b4 Iterated on features UI 2019-04-03 16:50:22 +02:00
Stefan Zermatten
021a53ef36 Moved properties schema, added 'removeProperty' method 2019-04-03 16:50:12 +02:00
Stefan Zermatten
681e669e76 Refactored schemas to make properties all implicitely children 2019-04-03 12:38:01 +02:00
Stefan Zermatten
28ffcc87b4 updated package name 2019-04-03 10:28:04 +02:00
Stefan Zermatten
888a7ea592 Removed Bowerrc, nothing uses bower anymore 2019-04-03 10:27:11 +02:00
Stefan Zermatten
053a7a36a6 Began generalizing insert forms for character properties to reduce duplicated code 2019-04-01 16:58:15 +02:00
Stefan Zermatten
6d68796a11 Fixed nasty bug where mixins were bashing the schemas passed to them 2019-04-01 16:57:29 +02:00
Stefan Zermatten
18493afbbf Collated update methods into an index, fixed typo 2019-04-01 15:51:11 +02:00
Stefan Zermatten
a94f437ba8 methods now use correct mixins 2019-04-01 13:48:39 +02:00
Stefan Zermatten
d21827106c Moved parse tree classes out of grammar.js started working on compilation. Broke the build 2019-03-29 14:08:09 +02:00
Stefan Zermatten
caf7f3efb9 Moved parser into main source folders 2019-03-27 12:23:27 +02:00
Stefan Zermatten
484b73d836 Moved parser javascript into single block 2019-03-27 11:25:34 +02:00
Stefan Zermatten
ee788c952a Improved grammar 2019-03-27 11:06:43 +02:00
Stefan Zermatten
ffa98d76fc Began writing a custom parser for calculations 2019-03-26 16:32:24 +02:00
Stefan Zermatten
5bb5f047f4 Moved a lot of functionality to mixins, improved parenting 2019-03-19 15:57:21 +02:00
Stefan Zermatten
1146d56324 Fixed jshint warnings 2019-03-19 09:31:14 +02:00
Stefan Zermatten
ef83d54fd9 Reorganized folder structure, removed legacy v1 code 2019-03-14 10:10:58 +02:00
Stefan Zermatten
18e3f653f3 Added insert and update methods for all properties 2019-03-13 14:03:03 +02:00
Stefan Zermatten
572aca5906 Moved name to properties schema 2019-03-13 09:40:20 +02:00
Stefan Zermatten
f3d19d3b38 Changed instances to encounters 2019-03-13 09:31:46 +02:00
Stefan Zermatten
94f6631a7d Overhauled data models to make actions and libraries more universal 2019-03-12 16:47:20 +02:00
Stefan Zermatten
febb65a513 Re-wrote parenting, should be significantly faster, more maintainable 2019-03-08 13:57:24 +02:00
Stefan Zermatten
15c11e16ab Began adding generic child lists of effects, proficiencies, etc. 2019-03-06 11:58:40 +02:00
Stefan Zermatten
8772e539da Made initiative a skill of type "check" instead of a modifier 2019-02-27 16:08:20 +02:00
Stefan Zermatten
4e7c0c5a90 Removed skill type limit from proficiencies that are included in computation 2019-02-27 15:51:44 +02:00
Stefan Zermatten
0ae2cc5545 Changed creature computations to leverage the MathJS parser (big deal) 2019-02-27 15:49:28 +02:00
Stefan Zermatten
687b517164 Added spell slot display, improved resource card 2019-02-26 16:37:19 +02:00
Stefan Zermatten
8305596373 Added resource cards 2019-02-26 14:11:59 +02:00
Stefan Zermatten
8f624b24a9 Organised components into folders 2019-02-26 11:27:48 +02:00
Stefan Zermatten
a2432c7161 Moved attribute related components into their own folder 2019-02-26 11:20:01 +02:00
Stefan Zermatten
b7a4a3d3fa Added simple feature UI components and insertion dialog 2019-02-25 15:38:57 +02:00
Stefan Zermatten
e5ff116208 Made the stats FAB fixed instead of absolute 2019-02-21 10:57:28 +02:00
Stefan Zermatten
24f3431900 Removed all spaces around v-icon's text, because it causes alignment issues 2019-02-20 15:48:35 +02:00
Stefan Zermatten
4f402830d8 Added attribute insertion UI and API 2019-02-20 14:30:04 +02:00
Stefan Zermatten
9e208ad3b3 Dialog stack callbacks can now return a return element ID to animate to 2019-02-20 14:29:33 +02:00
Stefan Zermatten
9d027aeabf Allowed smart inputs to accept errors as props 2019-02-20 14:27:20 +02:00
Stefan Zermatten
97ec5d4b5c Return element IDs can now be set when popping the dialog stack 2019-02-20 10:30:41 +02:00
Stefan Zermatten
6c421c3a98 Smart inputs now run all their debounced functions before they get destroyed 2019-02-20 10:30:07 +02:00
Stefan Zermatten
665a9d716a Unset modifiers of attributes that aren't abilities 2019-02-20 09:27:37 +02:00
Stefan Zermatten
aa7a426e9f Fixed some issues with computation 2019-02-19 11:12:46 +02:00
Stefan Zermatten
da6909a997 Added skill dialog 2019-02-19 11:12:37 +02:00
Stefan Zermatten
3129b86ef0 Improved action and attack schemas 2019-02-18 15:52:17 +02:00
Stefan Zermatten
e63ae96cb5 Changed dialog stack from using element ids to data-ids to allow duplicate ids to work 2019-02-18 15:17:31 +02:00
Stefan Zermatten
a31a70f435 Made health bar editing compatible with dark mode 2019-02-18 14:26:46 +02:00
Stefan Zermatten
c5899e0816 Improved dialog stack handling of scrolling while the stack is open 2019-02-18 13:41:36 +02:00
Stefan Zermatten
d30ee06e33 Added edit tab to base dialogs, added edit screen to attribute dialog 2019-02-18 13:41:21 +02:00
Stefan Zermatten
5d45788521 Fixed a bug with smart inputs getting stuck loading if you set them to their original value and tabbed away 2019-02-15 14:50:08 +02:00
Stefan Zermatten
23cd5d6a12 Added effect edit expansion panel 2019-02-15 14:25:11 +02:00
Stefan Zermatten
38da95256b Made icon search compatible with dark mode 2019-02-15 12:48:24 +02:00
Stefan Zermatten
f773a20a59 Made hit dice list tiles compatible with dark mode 2019-02-15 12:24:52 +02:00
Stefan Zermatten
23654cd09c Made effect edit compatible with dark mode 2019-02-15 12:21:02 +02:00
Stefan Zermatten
09b2c38b43 Made color picker compatible with dark mode 2019-02-15 12:17:12 +02:00
Stefan Zermatten
1103248574 Abstracted effect list tile into its own component and made it dark mode compatible 2019-02-15 12:03:57 +02:00
Stefan Zermatten
96bb072dad Made health bar compatible with dark mode 2019-02-15 11:46:25 +02:00
Stefan Zermatten
3f6b3fe4c1 Made ability list tiles compatible with dark mode 2019-02-13 15:47:25 +02:00
Stefan Zermatten
ad7a485778 Added dark mode 2019-02-13 14:44:25 +02:00
Stefan Zermatten
58949e14fe Added a tree view of the character, fixed the issue it revealed 2019-02-11 16:15:57 +02:00
Stefan Zermatten
8bcb4407e6 Added method to persist hit dice arrow buttons to database 2019-02-11 13:55:43 +02:00
Stefan Zermatten
fd15e8a6c7 Removed duplicate hit dice effects 2019-02-11 13:53:11 +02:00
Stefan Zermatten
938573a4b4 Improved attribute dialogs 2019-02-07 15:36:47 +02:00
Stefan Zermatten
465f6645b7 Fixed colors of character sheet and base dialog toolbars 2019-02-07 13:56:48 +02:00
Stefan Zermatten
4b25373c7c Abstracted text fields into smart input components 2019-02-07 13:53:44 +02:00
Stefan Zermatten
5142b8e1a0 Merge branch 'version-2' of https://github.com/ThaumRystra/DiceCloud1 into version-2 2019-02-07 10:02:50 +02:00
Thaum Rystra
f8e83ebe7e Added text-field component to edit database text without it getting bashed by data cleaning 2019-02-06 19:38:27 +02:00
Stefan Zermatten
b67926e0fc Added schema defaults to all schemas to prevent strings from being trimmed 2019-02-06 17:32:08 +02:00
Stefan Zermatten
bf2e9439cf Added debouncing for ui update functions 2019-02-06 17:26:56 +02:00
Stefan Zermatten
de4509ab6a made reset options clearable 2019-02-06 15:28:23 +02:00
Stefan Zermatten
f42f9590d2 Updated packages 2019-02-06 15:28:02 +02:00
Stefan Zermatten
29c5ed2194 Improved select menus 2019-02-06 14:46:21 +02:00
Stefan Zermatten
cf0440a8db Added color picker 2019-02-06 13:43:11 +02:00
Stefan Zermatten
4917729f29 Added basic attribute edit form 2019-02-06 11:16:32 +02:00
Stefan Zermatten
fe8e72d225 Changed primary color to red, since form elements default to it, secondary color is now grey 2019-02-06 10:57:47 +02:00
Stefan Zermatten
0e913b1f63 Items now have a number of uses which can be used up. 2019-02-06 10:08:22 +02:00
Stefan Zermatten
58f26cf849 Added sensible defaults to character creation dialog 2019-02-04 14:49:28 +02:00
Stefan Zermatten
836b10d499 Fixed some bugs in creature computations, removed debugging logs 2019-02-04 14:49:14 +02:00
Stefan Zermatten
8d4146f242 Added constitution bonus to hitpoints as a default effect 2019-02-04 14:48:25 +02:00
Stefan Zermatten
7742e5deef Gave defaults to adjustments and effects for attributes in case they have not been set yet 2019-02-04 14:30:41 +02:00
Stefan Zermatten
41c69ae040 Allowed character creation dialog to be cancelled 2019-02-04 14:30:17 +02:00
Stefan Zermatten
c6e7f8eeb6 Added effect editing component, abstracted out operation icons 2019-02-04 14:30:04 +02:00
Stefan Zermatten
e3e7b76f02 Results of effects are now stored on the effect, fixed defaults to suit 2019-02-04 14:29:03 +02:00
Stefan Zermatten
2dcbc91ccd Removed value from effects, calculation now stores the effect value even if it's a number 2019-02-04 14:28:21 +02:00
Stefan Zermatten
2fcf3047ee made sure healthBar elements that will be animated to have solid backgrounds 2019-01-31 10:34:50 +02:00
Stefan Zermatten
a95ae88a31 Fixed dialog animations to elements with no box shadow 2019-01-31 10:34:17 +02:00
Stefan Zermatten
f6aca08f63 Added missing import 2019-01-31 10:33:59 +02:00
Stefan Zermatten
87ee39b5bd Updated Readme 2019-01-31 10:08:01 +02:00
Stefan Zermatten
d466bfe428 Allowed skills to come with a base proficiency 2019-01-31 09:27:27 +02:00
Stefan Zermatten
e357b29145 Added labels to healthBars and improved attribute dialog opening 2019-01-30 16:38:47 +02:00
Stefan Zermatten
a80d070533 Refactored ability dialogs as generalised attribute dialogs 2019-01-30 14:10:46 +02:00
Stefan Zermatten
80d369f0d4 Got healthbars persisting data to the database 2019-01-30 13:34:45 +02:00
Stefan Zermatten
f6b0c746cc Improved ability dialogs 2019-01-29 16:36:13 +02:00
Stefan Zermatten
4584499019 Added A way to get Game-icons.net icons into the database (it's not secure yet), plus some icon ui 2019-01-28 16:26:39 +02:00
Stefan Zermatten
77d2f87373 Fixed an issue caused by storing components on the store, added ability dialog 2019-01-24 16:45:02 +02:00
Stefan Zermatten
8c0edfaa93 Added separate return element ids to dialogs 2019-01-24 15:11:42 +02:00
Stefan Zermatten
2e6ef52594 Dialog stack animations complete 2019-01-24 14:40:38 +02:00
Stefan Zermatten
00e8cbc1c8 Added dialog animations, still working on box shadows 2019-01-23 16:49:58 +02:00
Stefan Zermatten
e8a0e86548 Added creature view and edit permissions 2019-01-23 16:49:47 +02:00
Stefan Zermatten
60dfba3b46 Completed the stats tab, conditions not added yet 2019-01-21 16:03:05 +02:00
Stefan Zermatten
e43718f034 Removed outdated comment 2019-01-18 13:27:56 +02:00
Stefan Zermatten
36022e4bc4 Added hit dice tiles 2019-01-18 13:00:44 +02:00
Stefan Zermatten
0497223804 Added head request for roboto so that all font weights work 2019-01-18 12:54:40 +02:00
Stefan Zermatten
ef9512f0ec Moved storybook to route of UI 2019-01-18 12:18:16 +02:00
Stefan Zermatten
369dae17ee Storybook now has the option not to wrap a story in a card 2019-01-18 11:34:16 +02:00
Stefan Zermatten
4728d06c0b Added attribute cards 2019-01-18 10:38:28 +02:00
Stefan Zermatten
2d3cb367da Added ability score list tiles 2019-01-17 16:37:37 +02:00
Stefan Zermatten
f2137e26b2 Removed the space between a skill name and the conditional benefit astrisk 2019-01-17 15:35:16 +02:00
Stefan Zermatten
0bd654e557 Gave health bars a background fade so that you can click away from them to close the edit box 2019-01-17 15:26:07 +02:00
Stefan Zermatten
f06c4adb32 Corrrected the full proficiency icon 2019-01-17 15:07:12 +02:00
Stefan Zermatten
fabf377f72 Added Skill list tiles 2019-01-17 15:01:45 +02:00
Stefan Zermatten
03c244e7c9 Added health bar story and improved health bar functionality 2019-01-17 15:01:34 +02:00
Stefan Zermatten
ea7eabf27d Updated packages and meteor version, now need NO_HMR=1 when running in dev mode 2019-01-17 15:01:16 +02:00
Stefan Zermatten
bbbed216eb Added A dev-only storybook 2019-01-17 15:00:44 +02:00
Stefan Zermatten
c5cb98dc9f Added basic health bar with editing popup 2019-01-09 14:40:44 +02:00
Stefan Zermatten
aa6d973ae1 Character sheet toolbars now take on their character's color 2019-01-09 14:40:31 +02:00
Stefan Zermatten
76da2c8393 Added Character sheet 2018-12-21 12:17:49 +02:00
Stefan Zermatten
e0195499e5 Fixed broken sidebar links 2018-12-21 10:51:33 +02:00
Stefan Zermatten
798cf3edd7 Characters now insert with intelligent defaults based on the character wizard 2018-12-19 14:15:56 +02:00
Stefan Zermatten
13669fdc91 Refactored character insertion and continued work on creation dialog 2018-12-18 14:20:17 +02:00
Stefan Zermatten
dcc460d9e6 Migrations 2018-11-28 11:48:49 +02:00
Stefan Zermatten
c7fcb4de0c More migrations... 2018-11-15 15:22:20 +02:00
Stefan Zermatten
a5e7bd95c7 Indexed publications and imported them when the server starts 2018-11-15 11:23:57 +02:00
Stefan Zermatten
51919297df moved publications to imports folder 2018-11-15 11:20:49 +02:00
Stefan Zermatten
5b80032fa1 Migrated publications to import format 2018-11-15 11:20:25 +02:00
Stefan Zermatten
ade614dd5f Finished migrating creatures to simpl-schema 2018-11-15 11:11:26 +02:00
Stefan Zermatten
c4984c07bf More progress on migrating schema 2018-11-12 16:23:43 +02:00
Stefan Zermatten
b49ab67390 Removed .idea folder 2018-10-29 12:28:59 +02:00
Stefan Zermatten
c7f0c0a4c4 Removed old polymer components 2018-10-29 12:28:33 +02:00
Stefan Zermatten
7cda854d22 Continued migrating to Simpl-Schema and imports 2018-10-19 14:01:23 +02:00
Stefan Zermatten
989706483a updated CustomBuffs 2018-10-12 13:44:36 +02:00
Stefan Zermatten
53a1137848 Updated simpl-schema and collection2, started untangling the mess that made 2018-10-12 13:38:51 +02:00
Stefan Zermatten
e3065f089f Moved effects to default character stats 2018-10-12 12:38:24 +02:00
Stefan Zermatten
6aaa4ebe00 Added base values to skills to make implementing non player creatures easier 2018-10-12 12:35:15 +02:00
Stefan Zermatten
158615c25c moved exports to the function definitions, rather than bottom of file 2018-10-12 12:24:56 +02:00
Stefan Zermatten
4cd46fe209 Refactored creature computation with more comments 2018-10-12 12:14:15 +02:00
Stefan Zermatten
04059709eb Got creature computation working again after moving to imports dir 2018-10-12 11:59:29 +02:00
Stefan Zermatten
d117570165 renamed model to api 2018-10-12 09:28:16 +02:00
Stefan Zermatten
4b900d5664 lowercased all model directories 2018-10-12 09:21:03 +02:00
Stefan Zermatten
189a1d0a16 Started work on migrating model to lazy evaluation 2018-10-12 09:18:18 +02:00
Stefan Zermatten
ca8223ccad Moved the model into the imports directory 2018-10-12 09:01:23 +02:00
Stefan Zermatten
b2eed9a672 Renamed Characters to Creatures 2018-10-12 08:52:14 +02:00
Stefan Zermatten
d330e15dce Added character creation dialog 2018-10-11 16:39:55 +02:00
Stefan Zermatten
4ac56a31de Successful registration pushes the user to the character list 2018-10-11 11:50:19 +02:00
Stefan Zermatten
41a25151fc Rolled back 1.8 update because it broke the build 2018-10-11 11:39:27 +02:00
Stefan Zermatten
e1c1b727ed updated Meteor to 1.8 2018-10-11 10:44:07 +02:00
Stefan Zermatten
dc8185df98 Got basic dialog working, no morph animation yet 2018-10-09 16:24:43 +02:00
Stefan Zermatten
89820780b5 Added accounts page 2018-10-05 12:55:21 +02:00
Stefan Zermatten
d0ce162315 Made sure some links use vue router 2018-10-03 16:37:12 +02:00
Stefan Zermatten
6835f5f4f9 Removed blaze, old client side code 2018-10-03 11:14:23 +02:00
Stefan Zermatten
e9d5e85e75 Started on dialog stack 2018-10-02 08:51:04 +02:00
Stefan Zermatten
c46f8c5171 Speed dial fixed 2018-10-02 08:50:45 +02:00
Thaum Rystra
f41ff1c52f Started work on vue sign-in 2018-10-01 09:38:03 +02:00
Stefan Zermatten
7f418c26da Started work on character list page 2018-09-28 13:07:32 +02:00
Stefan Zermatten
acdc084905 Home and sidebar implemented 2018-09-28 13:07:12 +02:00
Stefan Zermatten
6a73d3576a Added Vue, looks like this branch is now going to be DiceCloud V2 2018-09-27 14:55:27 +02:00
Stefan Zermatten
d82031ab0d Added indices, fixed bugs 2018-09-27 09:39:21 +02:00
Stefan Zermatten
cf33bfcce1 Fixed compile errors 2018-09-25 16:04:30 +02:00
Stefan Zermatten
005433268d Added migration code 2018-09-25 15:23:52 +02:00
Stefan Zermatten
ae470642c1 Computed characters are now written back to the database 2018-09-25 11:51:06 +02:00
Stefan Zermatten
dd89556b7f Moved recompute character XP and Weight Carried around 2018-09-25 11:10:47 +02:00
Stefan Zermatten
b3c2176de8 Added weight carried recomputation 2018-09-25 09:07:43 +02:00
Stefan Zermatten
9fccc3be61 Renamed addDefaultAttributes to addDefaultStats 2018-08-28 09:57:09 +02:00
Stefan Zermatten
bc26a77f92 Merge branch 'feature-server-computation' of https://github.com/ThaumRystra/DiceCloud1 into feature-server-computation 2018-08-28 09:55:26 +02:00
Stefan Zermatten
fa3cca7193 Added baseValue to attributes 2018-08-28 09:55:22 +02:00
Thaum Rystra
662fc91e17 New characters get the appropriate attributes/skills/damageMultipliers 2018-08-25 20:19:17 +02:00
Stefan Zermatten
b4506fd939 Began abstracting default abilities, damage multipliers and skills into rulesets. 2018-08-24 13:30:31 +02:00
Stefan Zermatten
5c40a84660 Changed Characters to creatures, as the new attributes system is generalisable 2018-08-24 12:14:03 +02:00
Stefan Zermatten
7c9687955d Added Models for attributes, skills and damage multipliers 2018-08-24 12:12:38 +02:00
Stefan Zermatten
b21a91b0aa Added very basic testing, got attribute calculations working 2018-08-24 10:58:59 +02:00
Stefan Zermatten
f4b1da0c80 Comments and some refactoring 2018-08-23 16:17:42 +02:00
Stefan Zermatten
755e7fba30 Started the big move to server-side computation. 2018-08-23 15:55:21 +02:00
Stefan Zermatten
41c90bb69f Updated Meteor 2018-08-23 11:45:38 +02:00
Stefan Zermatten
68432541db Merge pull request #168 from Frogvall/master
Adding advantage/disadvantage arrows to initiative stat card
2018-08-23 11:34:02 +02:00
Frogvall
c417c45db1 Cleaned up a bit 2018-06-20 14:33:34 +02:00
Frogvall
3f3caf63e4 Add css and js implementation of previously naïve approach
Bonus: Learned some meteor :)
2018-06-20 14:25:18 +02:00
Jonas
49c5a1fcb1 Added adv class to skills in stat cards
Added arrows to initiative, for example
2018-06-18 00:51:20 +02:00
Stefan Zermatten
9fb37148fa Merge pull request #167 from mommothazaz123/master
Add raw character JSON output, improve ratelimiter rules of API
2018-06-08 09:26:52 +02:00
Andrew Zhu
a67d7fb4ea Merge branch 'master' into master 2018-06-07 02:06:34 -07:00
Andrew Zhu
8e0f19742b fix some branch conflicts 2018-06-07 02:03:12 -07:00
Andrew Zhu
216e502c8a add permission check to JSONcharacter 2018-06-07 01:38:29 -07:00
Andrew Zhu
1a18d1f816 update README to reflect dir name change 2018-06-07 01:08:41 -07:00
Andrew Zhu
c099e3173b rename /rpg-docs to /app 2018-06-07 01:07:49 -07:00
Andrew Zhu
de93636c7c Revert "add mixmax:smart-disconnect to reduce server load"
This reverts commit 465a61f80f.
2018-06-07 01:03:16 -07:00
Stefan Zermatten
5e263443b3 Updated dependencies 2018-06-04 15:42:23 +02:00
Stefan Zermatten
8c3a891254 Merge branch 'bugfix-163' 2018-06-04 15:29:14 +02:00
Stefan Zermatten
e737067990 Made sure empty party names take up some space so they can be edited 2018-06-04 15:28:08 +02:00
Andrew Zhu
465a61f80f add mixmax:smart-disconnect to reduce server load 2018-05-27 22:58:02 -07:00
Andrew Zhu
bbf42aaf97 make ratelimiter actually match, return time to reset on hitting ratelimit 2018-05-25 01:39:35 -07:00
Andrew Zhu
b20d086a24 add character JSON endpoint, improve ratelimit rules 2018-05-25 01:34:39 -07:00
Stefan Zermatten
52baf297ca Changed rpg-docs folder to app 2018-05-21 14:21:07 +02:00
Thaum Rystra
45e9f491ff Merge branch 'feature-blacklist' 2018-04-02 14:27:24 +02:00
Thaum Rystra
742b26b0de Added rate limiting logging and an error page for hitting the rate limit on opening characters 2018-04-02 14:26:55 +02:00
Stefan Zermatten
164ba78c81 Added blacklist checks and rate limit logging
Needs testing
2018-03-12 09:22:04 +02:00
Thaum Rystra
e27211b24d Merge branch 'enhancement-154' 2018-03-03 20:13:03 +02:00
Thaum Rystra
30987752cc Hotfix - Remove localhost from image link on printed character sheet 2018-03-03 17:28:42 +02:00
Thaum Rystra
058ee2691f Merge branch 'feature-print'
# Conflicts:
#	rpg-docs/package-lock.json
2018-03-03 17:18:16 +02:00
Thaum Rystra
f0cf7f4956 Added QR code and finished page 1 2018-03-03 17:13:36 +02:00
Thaum Rystra
75c8720b04 Moved printed character sheets to their own page
This makes sure the entire printed sheet is rendered before the browser  attempts to print it, solving all manner of errors
2018-03-03 11:13:16 +02:00
Thaum Rystra
f73f2f670f Formatted all existing printed character sheet fields nicely 2018-03-02 21:40:21 +02:00
Thaum Rystra
c6e62e1cfa Added borders to ability scores and AC 2018-03-02 07:25:37 +02:00
Jacob
4e574c0f51 Added "clear" (reset to 0) button to resource cards 2018-02-24 14:49:42 +00:00
Jacob
80b195b7f7 Added reset button to resource cards 2018-02-24 14:42:41 +00:00
Thaum Rystra
67956d9a42 Fixed dropdown boxes being clipped in dialogs, updated Meteor 2018-01-26 18:18:49 +02:00
Stefan Zermatten
64b3ca6066 Added proficiencies to printed sheet; some fixes 2017-12-12 15:54:21 +02:00
Stefan Zermatten
8349f7da9b Merge branch 'master' into feature-print 2017-12-06 09:22:57 +02:00
Stefan Zermatten
0636042878 Added core-js because an error message told me to 2017-11-22 14:34:54 +02:00
Stefan Zermatten
6207ffa516 Updated meteor 2017-11-21 10:53:07 +02:00
Stefan Zermatten
9d33612054 Merge branch 'feature-rate-limiting-api'
closes #141
2017-11-21 08:50:03 +02:00
Stefan Zermatten
face6387a0 Fixed attacks not being added to spells added with multi-add
Fixes #145
2017-11-01 15:55:07 +02:00
Stefan Zermatten
b308595dac Implement both tokens and rate limiting to API
Closes #141, but still needs better UI on failure
2017-10-12 16:24:12 +02:00
Stefan Zermatten
1d2de197a4 Replaced trello with github on home page 2017-09-28 14:04:42 +02:00
Stefan Zermatten
a3d790b47d Merge branch 'feature-manage-user' 2017-09-28 13:47:46 +02:00
Stefan Zermatten
efe6dd87db Added link to change password
closes #78
2017-09-28 13:47:30 +02:00
Stefan Zermatten
5b33a6e783 Fixed username dialog title 2017-09-28 13:22:21 +02:00
Stefan Zermatten
8730fab40b Moved username dialog to own folder 2017-09-28 13:21:14 +02:00
Stefan Zermatten
992776bb40 Merge branch 'feature-onboarding' 2017-09-28 13:18:52 +02:00
Stefan Zermatten
bc9ec4421c Polished onboarding, removed stray logs
closes #102
2017-09-28 13:17:33 +02:00
Stefan Zermatten
4c31ab601c Improved new user experience and fixed errors on character delete 2017-09-28 13:03:54 +02:00
Stefan Zermatten
c4e77c7eae Replaced all paper-tooltip with custom, working version 2017-09-28 11:47:03 +02:00
Stefan Zermatten
2cd6e27f70 Finished basic new user experience 2017-09-28 10:26:45 +02:00
Stefan Zermatten
f6b2dde479 Added basic onboarding steps 2017-09-27 16:19:00 +02:00
Stefan Zermatten
44da62a962 Lowered subscription caching to improve performance 2017-09-26 15:11:03 +02:00
Stefan Zermatten
4e96047e90 Added rate limiting to heavy subscriptions 2017-09-26 14:59:41 +02:00
Stefan Zermatten
212986ac37 Fixed missing charId 2017-09-26 13:54:33 +02:00
Stefan Zermatten
877f516565 Added efficient computation of characters to replace heavy export functionality 2017-09-26 13:36:01 +02:00
Thaum Rystra
750022f0f1 Added missing indices 2017-09-24 04:02:15 +02:00
Stefan Zermatten
614284c73d Updated Meteor and some packages 2017-09-22 12:52:21 +02:00
Stefan Zermatten
6528fc8bab Improved vMix export
closes #138
2017-09-22 11:41:32 +02:00
Stefan Zermatten
020930b2e4 Checked if old hitpoint slider exists before resetting it
closes #135
2017-09-22 11:03:00 +02:00
Stefan Zermatten
dcd76e06e1 Merge branch 'fixbug-137-multiadd-spells-delete-themselves' 2017-09-22 10:43:04 +02:00
Stefan Zermatten
8a58002415 Fixed spells in the library using their library ID's in the spells collection
closes #137
2017-09-22 10:42:34 +02:00
Stefan Zermatten
535fcd77cf Added missing attributes to vmix export 2017-09-13 16:01:05 +02:00
Stefan Zermatten
7c2aed26a4 Fixed vMix export, included vMix parties 2017-09-13 14:01:07 +02:00
Stefan Zermatten
fab052050a Merge branch 'feature-vmix' 2017-09-13 10:01:46 +02:00
Stefan Zermatten
b7bdb141c8 Added basic vMix server side api 2017-09-13 10:01:32 +02:00
Stefan Zermatten
00a050d337 Added basic printing functionality 2017-09-11 09:27:01 +02:00
Stefan Zermatten
0b8fabde14 Merge branch 'enhancement-106' 2017-09-08 10:48:44 +02:00
Stefan Zermatten
3336e177d9 Merge branch 'enhancement-spell-library-style'
closes #106
2017-09-08 10:46:41 +02:00
Stefan Zermatten
2e9440e325 Merge branch 'enhancement-spell-library-style' 2017-09-08 09:47:53 +02:00
Stefan Zermatten
e4ac400cbd Replaced spell library spell selection highlighting with checkboxes
Makes it more intuitive that multiple items can be selected
2017-09-08 09:47:43 +02:00
Stefan Zermatten
f600999c5f Guarded against error when hiding feature description portion of card 2017-09-08 09:46:58 +02:00
Stefan Zermatten
0af905699a Uncommented buffs from spell library 2017-09-08 09:46:00 +02:00
Stefan Zermatten
6e900cfaae Merge pull request #123 from Dumbgenius/spells-improvements
Spells improvements
2017-09-08 08:30:56 +02:00
Stefan Zermatten
1f42d3c622 Merge pull request #128 from Dumbgenius/fix-127
[#127] Arrow library items should give 20 arrows rather than one "Arrows (20)"
2017-09-08 08:29:56 +02:00
Stefan Zermatten
ee453d968f Merge pull request #134 from Dumbgenius/fixbug-attackview-damage-type
attackView now correctly shows damage type
2017-09-07 16:31:26 +02:00
Stefan Zermatten
0850e59b30 Merge pull request #122 from Dumbgenius/feature-22-temp-hp, requires migration
Feature - TempHP as a character attribute,
2017-09-07 16:29:55 +02:00
Stefan Zermatten
f3e44cf033 Moved polymer styling to app-theme 2017-09-07 16:26:09 +02:00
Stefan Zermatten
907f9d15d4 Merge pull request #131 from Dumbgenius/feature-custom-buffs
Feature - custom buffs
2017-09-07 14:42:05 +02:00
Stefan Zermatten
4c3d5d40dd Merge branch 'master' into feature-custom-buffs 2017-09-07 14:41:33 +02:00
Stefan Zermatten
ef0deb20aa Swapped dropdown box for full character list 2017-09-07 14:36:45 +02:00
Stefan Zermatten
b43ee08d75 Fixed some editing bugs on new buffs, improved style 2017-09-07 14:33:59 +02:00
Stefan Zermatten
7e7f1ec997 Merge pull request #132 from Dumbgenius/enhancement-blank-descriptions
Removed blank bit of card on features with no short description
2017-09-07 12:03:32 +02:00
Stefan Zermatten
0f652f5c74 Merge pull request #126 from Dumbgenius/feature-unsharing-characters
Added ability to "unshare" a character.
2017-09-07 12:01:14 +02:00
Stefan Zermatten
3e02875eaf Merge pull request #130 from Dumbgenius/enhancement-85
Added "carried" checkbox to container edit screen (resolves #85)
2017-09-07 11:54:11 +02:00
Stefan Zermatten
b9a5230344 Merge pull request #129 from Dumbgenius/enhancement-84
Added attacks to features (resolves #84)
2017-09-07 11:49:03 +02:00
Stefan Zermatten
28780b96c3 Merge pull request #120 from Dumbgenius/feature-drag-drop-spells
Feature - drag and drop spells
2017-09-07 11:43:34 +02:00
Jacob
bec0b33805 attackView now correctly shows damage type 2017-09-06 18:45:43 +01:00
Jacob
ad9ccbe7ef Removed blank bit of card on features with no short description 2017-09-05 14:31:26 +01:00
Jacob
e2933c2df5 Fixed a couple of places where "Buff" should've been "Condition" 2017-09-05 13:56:15 +01:00
Jacob
87583fdac6 Added buff details to apply buff dialog 2017-09-05 13:48:26 +01:00
Jacob
68e1382aed Buffs/conditions now have a delete button on the list on Stats 2017-09-04 20:36:13 +01:00
Jacob
7b62c82e32 Buff and condition view dialogs now only have the delete button 2017-09-04 20:16:56 +01:00
Jacob
6dd92586a4 Conditions are now separate from buffs, like in #109 2017-09-04 20:16:38 +01:00
Jacob
b3d0db1f02 Merge branch 'master' into feature-custom-buffs 2017-09-04 18:22:04 +01:00
Jacob
5f35c71c9d Added "carried" checkbox to container edit screen
Resolves #85
2017-09-04 15:50:57 +01:00
Jacob
85baf4e5d1 Added attacks to features (resolves #84) 2017-09-04 15:35:52 +01:00
Jacob
15d797131e Fixes #127. Also fixes it for other ammo as well as iron spikes 2017-09-04 15:22:45 +01:00
Stefan Zermatten
06ac9f70c8 Merge pull request #119 from Dumbgenius/srd-fixes
SRD fixes,

Merging this pull request doesn't change the deployed version of DiceCloud, but indicates that the database has been updated using the affected JSON files
2017-09-04 10:50:42 +02:00
Jacob
471a3e274e Refactored code which applies buffs 2017-08-10 02:49:29 +01:00
Jacob
d4031dc4a7 Removed an unnecessary console log 2017-08-09 16:11:09 +01:00
Jacob
18e5ab3f21 Can now set target of buffs
...to "Self only", "Others only", or "Both".
2017-08-09 16:08:19 +01:00
Jacob
c9fe2f17a0 If buff's target is "self", dialog is no longer shown 2017-08-09 15:48:12 +01:00
Jacob
818cb3905f Can now access the name of public characters for buffs
the buffDialog can now access the name of a publicly-available character
so it can display their name as the caster
2017-08-09 15:30:33 +01:00
Jacob
64ef426035 Applying character no longer duplicated in buff apply list 2017-08-09 15:30:25 +01:00
Jacob
2b188f1a8d Merge branch 'master' into feature-custom-buffs
# Conflicts:
#	rpg-docs/client/views/character/inventory/itemDialog/itemDialog.html
#	rpg-docs/client/views/character/spells/spellDialog/spellDialog.html
2017-08-09 15:03:54 +01:00
Jacob
08735ea4f7 Added ability to add/edit buffs 2017-08-09 15:01:20 +01:00
Jacob
bce1b85600 Relocated the buff list to be inline in stats.html 2017-08-09 11:39:18 +01:00
Jacob
0d023e2ba3 Adding conditions now works again 2017-08-09 11:36:45 +01:00
Jacob
dad575de64 Buffs are now split into custom buffs and conditions 2017-08-09 11:20:49 +01:00
Jacob
cb648b4a28 Buffs now display who and what they were applied by. 2017-08-09 11:13:24 +01:00
Jacob
1279137362 Buffs now display attacks and proficiencies in buffDialog 2017-08-09 10:42:34 +01:00
Jacob
3c06529906 buffViewList is now split into "Conditions" and "Buffs" 2017-08-09 10:16:50 +01:00
Jacob
a3b0c6cafd Added a schema/collection pair for CustomBuffs 2017-08-09 10:06:11 +01:00
Jacob
a1d9f7f5bb Added ability to remove self from readers.
Closes #125.
2017-08-09 02:18:46 +01:00
Jacob
3b03e9c71c Spell library now allows you to select multiple spells 2017-07-30 23:27:29 +01:00
Jacob
0eea6f2386 Cantrips now display as "<school> cantrip" rather than "Level 0 <school>"
Also fixed spacing if "(ritual)" tag was not present
2017-07-30 21:20:27 +01:00
Jacob
8f8714d3e5 Altered spacing of headers within baseDialog 2017-07-30 21:14:02 +01:00
Jacob
7c38e8d70a Added newline before At Higher Levels on Healing Word. 2017-07-30 20:22:59 +01:00
Jacob
826859ca3f Added migration to add TempHP to all characters 2017-07-29 22:41:37 +01:00
Jacob
2ca13fbb56 Renamed tempHitPointSlider to extraHitPointSlider in the css 2017-07-29 21:45:02 +01:00
Jacob
6d801e0178 Renamed old "Temporary HP" to "Extra HP" (THP is now the actual attribute) 2017-07-29 21:42:48 +01:00
Jacob
9ddac7d5cd Temporary HP (the attribute) now appears on Stats page 2017-07-29 21:25:05 +01:00
Jacob
7a6f751e30 THP added as a character attribute 2017-07-29 20:09:48 +01:00
Jacob
2389768234 statOrder is now set programatically from an array
This makes it far easier to add new stats, as we don't have to update
every single number manually.
2017-07-29 19:20:26 +01:00
Jacob
c76fe99148 Can now move and copy spells between characters 2017-07-26 21:02:36 +01:00
Jacob
53afaa4f37 CTRL-dragging now copies spells 2017-07-26 19:54:18 +01:00
Jacob
3599b5fbc4 Added basic drag-and-drop functionality between spell lists
Closes #105
2017-07-26 19:23:41 +01:00
Jacob
275fb1ca65 Scrying's tables are now actual Markdown tables 2017-07-21 15:46:33 +01:00
Jacob
d5d937b04a Reincarnate's race table is now an actual Markdown table 2017-07-21 15:38:17 +01:00
Jacob
aa554adbce Teleport's familiarity table is now a table 2017-07-21 15:30:39 +01:00
Jacob
cb739eb207 Removed indents after double-newlines in spells SRD.
They don't actually render in Markdown, and if there was an indent of >=4
spaces it caused the entire next line to be rendered as code (for example
in the Animate Objects spell).
2017-07-21 14:58:29 +01:00
Jacob
66df2ea4aa Animate Objects' stats are now in a table 2017-07-21 14:43:37 +01:00
Stefan Zermatten
73d1419ee9 Merge pull request #114 from Dumbgenius/misc-enhancements
Miscellaneous enhancements
2017-07-21 13:12:22 +02:00
Stefan Zermatten
681ef614c7 Move earth now level 6 2017-07-21 13:03:31 +02:00
Stefan Zermatten
2bdbcb2e79 Fixed speak with animals
#118
2017-07-21 13:02:50 +02:00
Stefan Zermatten
c119fcfbb8 Freezing sphere now level 6
#118
2017-07-21 13:00:28 +02:00
Stefan Zermatten
deb5db8657 Daylight now level 3 2017-07-21 12:59:54 +02:00
Stefan Zermatten
49522580e3 Replaced line breaks with double line breaks in SRD spells 2017-07-21 12:50:50 +02:00
Stefan Zermatten
5b50f20128 Fixed spell attacks from library assignment of default attack bonus 2017-07-21 12:49:04 +02:00
Stefan Zermatten
52fa97c952 removed default values for library attacks where they aren't needed 2017-07-21 12:48:03 +02:00
Stefan Zermatten
e7a5ce8241 Replaced all mentions of saving throws with DC {DC} saving throw 2017-07-21 12:02:17 +02:00
Stefan Zermatten
d1b9043e1f Added change procedure for updating from srd JSON 2017-07-21 11:53:42 +02:00
Stefan Zermatten
9ffc5649f7 Fixed tooltip suffix icons and moved textarea icons outside of textarea 2017-07-21 11:02:02 +02:00
Stefan Zermatten
15e6c12c03 Replaced shitty paper-tooltip with custom css tooltip 2017-07-21 11:01:18 +02:00
Jacob
e89b877326 Merge branch 'fixbug-116' into misc-enhancements 2017-07-20 16:18:49 +01:00
Jacob
aff2f1f438 Parties are now sorted in both character lists. 2017-07-20 16:18:15 +01:00
Jacob
71d1e9e9e8 Added links in the guide to adam-p's Markdown Cheatsheet
and to the original specifaction for Markdown.
2017-07-20 12:21:01 +01:00
Jacob
0696fd8447 Updated the guide to talk about Markdown support as well 2017-07-20 12:10:17 +01:00
Jacob
b2db33e0f3 Added attacks to SRD cantrips
they scale by level: {floor((Level+1)/6)+1}dX
2017-07-20 11:33:44 +01:00
Jacob
0c2842b84a All "At Higher Levels" are now bold and italic, like in the actual PHB 2017-07-19 12:34:01 +01:00
Jacob
789658cfe7 Spells imported from SRD now have attack bonus set by default 2017-07-19 12:31:50 +01:00
Jacob
3be3da777f Now we can use "attackBonus" in spell attacks, make that the default 2017-07-19 12:19:26 +01:00
Jacob
0e53f157d2 Spell attack bonus and DC are now available in spell attacks. 2017-07-19 12:13:49 +01:00
Jacob
24cc4fd2b1 Background proficiencies now correctly open background dialog
Previously, when clicking on non-skill proficiencies from a character's
background, it would open a blank rectangle as its parent dialog. Now, it
correctly opens the Background dialog.
2017-07-18 23:01:14 +01:00
Jacob
1f0ea689dc Multiple instances of the same proficiency are now merged
For tool/language/weapon/armour proficiencies, on the stats/persona page.
2017-07-18 22:33:28 +01:00
Jacob
11adf9da04 Merge branch 'fixbug-107' into misc-enhancements
# Conflicts:
#	rpg-docs/client/views/character/spells/spellDialog/spellDialog.html
2017-07-18 22:05:31 +01:00
Jacob
5ca81056f9 Merge branch 'feature-swap-stat-modifier' into misc-enhancements 2017-07-18 21:58:29 +01:00
Jacob
6fc469f934 Added setting to swap stats and modifiers
Although Schemas.Character was changed, a database migration should
not be required due to the way characterSettings handles it (since
undefined is a falsy value).
2017-07-18 21:58:00 +01:00
Jacob
0c7948afdd Changed all "1 action" casting times to "action" so it displays properly
i.e. it displays as "Evocation action" rather than "Evocation 1 action"
for example

Also did the same for "1 bonus action" -> "bonus action"
2017-07-18 20:24:12 +01:00
Jacob
42ffc79499 Spell list now displays whether a spell requires GP. 2017-07-18 20:22:44 +01:00
Jacob
0240209410 Removed buffViewList that shouldn't have been there 2017-07-18 20:20:04 +01:00
Jacob
c4c1afa669 Merge branch 'guide-update-1.2.8' into misc-enhancements
# Conflicts:
#	rpg-docs/client/views/guide/guide.css
#	rpg-docs/client/views/guide/guide.html
2017-07-18 19:58:35 +01:00
Jacob
47ac090e9d Merge branch 'testing_dg' into misc-enhancements
# Conflicts:
#	rpg-docs/.meteor/packages
2017-07-18 19:56:26 +01:00
Jacob
aadc83391f Adding an attack to a spell now has attack bonus set by the spell list
Default damage is also changed to 1d10 fire for spells
2017-07-18 19:52:12 +01:00
Jacob
54fb398056 Merge branch 'master' into guide-update-1.2.8 2017-07-18 11:11:37 +01:00
Jacob
073094f6dd Merge branch 'master' into fixbug-107 2017-07-18 11:11:14 +01:00
Jacob
bb95fe0d9a Merge branch 'master' into feature-conditions 2017-07-18 11:10:04 +01:00
Jacob
832ed0c1ff A sort of hacky fix to the issue - text wrapping doesn't quite work 2017-07-17 04:43:39 +01:00
Jacob
17a390a8aa Changed Model of Buffs for forwards compatibility
Added a parent attribute and made Buffs a child, so that buffs can be
added to items, spells and features.
2017-07-17 02:41:51 +01:00
Jacob
e65a2db0d2 Changed guide to use Markdown rather than HTML formatting (mostly)
This is both for ease of editing and so that that it can be copied
into the GitHub wiki.

The <iron-icon>s are left in.
2017-07-16 12:07:13 +01:00
Jacob
b3ef43eb70 Added ability to add/remove basic conditions
Conditions are those listed in ./lib/methods/conditions.js
2017-07-16 04:27:32 +01:00
Jacob
be92ef224c Preliminary additions 2017-07-15 19:54:23 +01:00
Jacob
6729bcad64 Updated the guide to reflect more recent updates (1.2.8) 2017-07-15 17:13:06 +01:00
Jacob
7af07e7ba3 Updated the guide to reflect more recent updates. 2017-07-15 17:07:39 +01:00
Jacob
f44914ab84 Added six extra loading hints. 2017-07-15 15:39:51 +01:00
Jacob
59c7eff46a Hit Dice are now called "d6 Hit Dice", etc. rather than just "d6" 2017-07-15 15:17:14 +01:00
Stefan Zermatten
2a332a2965 Merge branch 'feature-parties' 2017-07-14 17:09:47 +02:00
Stefan Zermatten
44a1daf6f8 Grouping characters by party now works
closes #75, finally
2017-07-14 17:09:30 +02:00
Stefan Zermatten
ac23afac5d Stopped characters with poor names from having failed URL's 2017-07-13 17:16:23 +02:00
Stefan Zermatten
a411fb2b43 Fixed migration for failed slug strings 2017-07-13 16:44:23 +02:00
Stefan Zermatten
35b6fe20ae Reset migrations to line up with current DiceCloud server 2017-07-13 16:16:00 +02:00
Stefan Zermatten
54f055d3ef Merge branch 'bugfix-model' 2017-07-13 16:03:23 +02:00
Stefan Zermatten
2bacb296ba Fixed new effects dialog animation
makes some progress on #72
2017-07-13 16:01:43 +02:00
Stefan Zermatten
8b061f7aa9 Improved look and feel of effect editing 2017-07-13 15:53:03 +02:00
Stefan Zermatten
6a84c83644 Made passive scores show up for all skills with bonuses to passive score
closes #56
2017-07-13 13:42:04 +02:00
Stefan Zermatten
3227cd0934 Add character names to the end of their URL's
Closes #21, makes links to characters human readable
2017-07-13 13:14:04 +02:00
Stefan Zermatten
089feae26f Spell slots are now available in calculations
closes #11
2017-07-13 10:56:42 +02:00
Stefan Zermatten
99c72d1e10 Made names optional for all collections
closes #92
2017-07-13 10:39:43 +02:00
Stefan Zermatten
1e67afbe6f Made DC available in spell descriptions as a variable
closes #101
2017-07-13 10:30:16 +02:00
Stefan Zermatten
1cfec1ca45 Allowed carryMultiplier to be a decimal
closes #100
2017-07-13 09:50:20 +02:00
Stefan Zermatten
09d1ac9ba3 Merge branch 'bugfix-improved-initiative' 2017-07-11 08:53:46 +02:00
Stefan Zermatten
834b9cf384 Added passive perception to improved initiative export
closes #97
2017-07-11 08:52:47 +02:00
Stefan Zermatten
37291b347a Fixed vulnerabilities, resistances, immunities export
closes #96
2017-07-11 08:42:21 +02:00
Stefan Zermatten
efdfbeb59e Moved export to lib 2017-07-11 08:12:52 +02:00
Thaum Rystra
a165f9b253 Merge branch 'feature-improved-initiative-export'
closes #95
2017-07-10 21:02:10 +02:00
Thaum Rystra
4a6fca98bc Cleaned up export dialog, fixed copying 2017-07-10 20:59:00 +02:00
Stefan Zermatten
35464128a0 Added basic export for improved initiative 2017-07-10 16:26:38 +02:00
Stefan Zermatten
398f8a8a2a Merge branch 'bugfix-style' 2017-06-28 09:33:20 +02:00
Stefan Zermatten
812a1784b2 Improved display of home page on smaller devices 2017-06-28 09:27:24 +02:00
Stefan Zermatten
8fa9cd0148 Prevented spell titles from overflowing their item in spell lists
closes #83
2017-06-28 09:26:52 +02:00
Stefan Zermatten
0e0662cc9a Added slightly specific rule to let headings wrap
closes #90
2017-06-28 09:26:13 +02:00
Stefan Zermatten
ad4e3f5b20 Stopped inventory items being separated from their containers
hopefully fixes #91
2017-06-28 09:25:25 +02:00
Stefan Zermatten
4cd058e1fe Set max of slider before value
Prevents unnecessary capping of temp HP to 100
Fixes #89
2017-06-08 09:48:51 +02:00
Stefan Zermatten
8f30cee4d3 Hotfixed column layout with translateZ(0) hack 2017-06-07 09:46:38 +02:00
Stefan Zermatten
d7f7eb2e6a Fixed some spells in the library 2017-06-07 09:35:59 +02:00
Stefan Zermatten
a59cf1162f added return value to remove old soft removed docs 2017-05-29 11:58:19 +02:00
Stefan Zermatten
7bc80da99e Moved cron setup to startup 2017-05-29 11:35:30 +02:00
Stefan Zermatten
2ddc520bb6 Added cron job to remove old documents 2017-05-29 11:16:46 +02:00
Stefan Zermatten
d92bb0f4d4 Replace tap event with click event for mini-FABs
fixes #68, probably
2017-05-16 10:36:40 +02:00
Stefan Zermatten
abcfe57add Fixed results not being used when popping dialogs
Fixes #66
2017-05-11 11:08:00 +02:00
988 changed files with 54463 additions and 16188 deletions

View File

@@ -1,13 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="HtmlUnknownAttribute" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myValues">
<value>
<list size="0" />
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
</profile>
</component>

View File

@@ -1,7 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>

56
.jscsrc
View File

@@ -1,56 +0,0 @@
{
"requireOperatorBeforeLineBreak": true,
"requireCamelCaseOrUpperCaseIdentifiers": true,
"maximumLineLength": {
"value": 80,
"allowComments": true,
"allowRegex": true
},
"validateIndentation": "\t",
"validateQuoteMarks": "\"",
"disallowMultipleLineStrings": true,
"disallowMixedSpacesAndTabs": "smart",
"disallowTrailingWhitespace": true,
"disallowSpaceAfterPrefixUnaryOperators": true,
"disallowMultipleVarDecl": false,
"disallowNewlineBeforeBlockStatements": true,
"disallowKeywordsOnNewLine": ["else"],
"requireSpaceAfterKeywords": [
"if",
"else",
"for",
"while",
"do",
"switch",
"return",
"try",
"catch"
],
"requireSpaceBeforeBinaryOperators": [
"=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=",
"&=", "|=", "^=", "+=",
"+", "-", "*", "/", "%", "<<", ">>", ">>>", "&",
"|", "^", "&&", "||", "===", "==", ">=",
"<=", "<", ">", "!=", "!=="
],
"requireSpaceAfterBinaryOperators": true,
"requireSpacesInConditionalExpression": true,
"requireSpacesInForStatement": true,
"requireTrailingComma": {
"ignoreSingleValue": true,
"ignoreSingleLine": true
},
"requireLineFeedAtFileEnd": true,
"disallowSpacesInAnonymousFunctionExpression": {
"beforeOpeningRoundBrace": true
},
"disallowSpacesInsideObjectBrackets": "all",
"disallowSpacesInsideArrayBrackets": "all",
"disallowSpacesInsideParentheses": true,
"disallowMultipleLineBreaks": true,
"disallowNewlineBeforeBlockStatements": true
}

View File

@@ -1,4 +0,0 @@
{
"undef": false,
"esversion": 6
}

14
Dockerfile Normal file
View File

@@ -0,0 +1,14 @@
FROM ubuntu:latest
RUN apt-get update --quiet \
&& apt-get install --quiet --yes \
bsdtar \
curl \
git
RUN ln --symbolic --force $(which bsdtar) $(which tar)
RUN useradd --create-home --shell /bin/bash dicecloud
USER dicecloud
WORKDIR /home/dicecloud
RUN curl https://install.meteor.com/?release=1.8.0.2 | sh
ENV PATH="${PATH}:/home/dicecloud/.meteor"
COPY dev.sh ./dev.sh
ENTRYPOINT ./dev.sh

View File

@@ -1,13 +1,89 @@
RPG Docs
DiceCloud
========
This is the repo for [DiceCloud](dicecloud.com).
DiceCloud is a free, auditable, real-time character sheet for D&D 5e.
Philosophy
----------
Setting up your character on DiceCloud takes a little longer than
just filling it in on a paper character sheet would. The goal of using an
online sheet is to make actually playing the game more streamlined, and
ultimately more fun. So putting a little extra effort into setting up a
character now pays off over and over again once you're playing.
The idea is to track where each number comes from, and allow you to easily make
changes on the fly. Let's look at a hypothetical example.
> You need to swim through a sunken section of dungeon to fetch the quest's Thing.
> You'll need to take off your magical Plate Armor of +1 Constitution to swim
> without sinking, of course.
>
> Taking it off will take away that disadvantage on
> stealth checks, change your armor class, your speed and your constitution, and
> which in turn changes your hit points and your constitution saving throw.
> Working out all those changes in the middle of a game will drag the game to a
> halt.
>
> Fortunately you have DiceCloud, so it's a matter of dragging
> your Plate Armor +1 Con from your "equipment" box to your "backpack" box and
> you're done. Your hitpoints change correctly, your saving throws are up to date,
> your armor class goes back to reflecting the fact that you have natural armor
> from being a dragonborn. Your character sheet keeps up and you
> ultimately get more time to play the game. Huzzah!
Getting started
---------------
`git clone https://github.com/ThaumRystra/DiceCloud1 dicecloud`
Running DiceCloud locally, either to host it yourself away from an internet
connection, or to contribute to developing it further, is fairly
straightforward and it should work on Linux, Windows, and Mac.
You'll need to have installed:
- [git](https://www.atlassian.com/git/tutorials/install-git)
- [Meteor](https://www.meteor.com/install)
Then, it's just a matter of cloning this repository into a folder, and running
`meteor` in the app directory.
`git clone https://github.com/ThaumRystra/DiceCloud dicecloud`
`cd dicecloud`
`cd rpg-docs`
`bower install`
`cd app`
`meteor npm install`
`meteor`
You should see this:
```
=> Started proxy.
=> [HMR] Dev server listening on port 3003.
=> Started MongoDB.
=> Started your app.
=> App running at: http://localhost:3000/
```
Environmental Variables
-----------------------
```
MAIL_URL=smtp://<your smtp mail url>
METEOR_SETTINGS={ "public": { "environment": "production", "patreon": { "clientId": "<your patreon client ID>", "campaignId": "<your campaign id>" } }, "patreon": { "clientSecret": "<your client secret>", "creatorAccessToken": "<your creator access token>" } }
MONGO_OPLOG_URL=mongodb+srv://<your url for the oplog account of your mongo database>
MONGO_URL=mongodb+srv://<your url for the read/write account of your mongo database>
NPM_CONFIG_PRODUCTION=true
PROJECT_DIR=app
ROOT_URL=https://<url of your DiceCloud instance>
DEFAULT_LIBRARIES=<comma separated list of library ids that will be subscribed by default: "abc123,def456">
```
To disable Patreon features and unlock all paid restrictions for all users of your deployment, replace
`"patreon": { "clientId": ... }"` with `"disablePatreon": true` in the public key of the METEOR_SETTINGS environment variable.
Alternatively run `meteor run --settings exampleMeteorSettings.json` to start the app with the example settings that disable Patreon by default.
Now, visiting [](http://localhost:3000/) should show you an empty instance of
DiceCloud running.

View File

@@ -1,9 +1,12 @@
.meteor/local
.meteor/meteorite
.demeteorized
.cache
.vscode
settings.json
public/components
public/_imports.html
private/oldClient
nohup.out
node_modules
dump

View File

@@ -13,3 +13,7 @@ notices-for-facebook-graph-api-2
1.3.0-split-minifiers-package
1.4.0-remove-old-dev-bundle-link
1.4.1-add-shell-server-package
1.4.3-split-account-service-packages
1.5-add-dynamic-import-package
1.7-split-underscore-from-meteor-base
1.8.3-split-jquery-from-blaze

50
app/.meteor/packages Normal file
View File

@@ -0,0 +1,50 @@
# Meteor packages used by this project, one per line.
#
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
accounts-password@2.3.1
random@1.2.0
underscore@1.0.10
dburles:mongo-collection-instances
accounts-google@1.4.0
email@2.2.1
meteor-base@1.5.1
mobile-experience@1.1.0
mongo@1.15.0
session@1.2.0
tracker@1.2.0
logging@1.3.1
reload@1.3.1
ejson@1.1.2
check@1.3.1
standard-minifier-js@2.8.0
shell-server@0.5.0
ecmascript@0.16.2
es5-shim@4.8.0
service-configuration@1.3.0
dynamic-import@0.7.2
ddp-rate-limiter@1.1.0
rate-limit@1.0.9
mdg:validated-method
static-html@1.3.2
aldeed:collection2
aldeed:schema-index
accounts-patreon
bozhao:link-accounts
peerlibrary:reactive-publish
simple:rest
simple:rest-method-mixin
mikowals:batch-insert
peerlibrary:subscription-data
seba:minifiers-autoprefixer
zer0th:meteor-vuetify-loader
akryum:vue-component
akryum:vue-router2
percolate:migrations
meteortesting:mocha
ostrio:files
simple:rest-bearer-token-parser
simple:rest-json-error-handler
littledata:synced-cron
mdg:meteor-apm-agent

1
app/.meteor/release Normal file
View File

@@ -0,0 +1 @@
METEOR@2.7.3

129
app/.meteor/versions Normal file
View File

@@ -0,0 +1,129 @@
accounts-base@2.2.3
accounts-google@1.4.0
accounts-oauth@1.4.1
accounts-password@2.3.1
accounts-patreon@0.1.0
akryum:npm-check@0.1.2
akryum:vue-component@0.15.2
akryum:vue-component-dev-client@0.4.7
akryum:vue-component-dev-server@0.1.4
akryum:vue-router2@0.2.3
aldeed:collection2@3.5.0
aldeed:schema-index@3.0.0
allow-deny@1.1.1
autoupdate@1.8.0
babel-compiler@7.9.0
babel-runtime@1.5.1
base64@1.0.12
binary-heap@1.0.11
blaze-tools@1.1.3
boilerplate-generator@1.7.1
bozhao:link-accounts@2.6.1
caching-compiler@1.2.2
caching-html-compiler@1.2.1
callback-hook@1.4.0
check@1.3.1
coffeescript@2.4.1
coffeescript-compiler@2.4.1
dburles:mongo-collection-instances@0.3.6
ddp@1.4.0
ddp-client@2.5.0
ddp-common@1.4.0
ddp-rate-limiter@1.1.0
ddp-server@2.5.0
diff-sequence@1.1.1
dynamic-import@0.7.2
ecmascript@0.16.2
ecmascript-runtime@0.8.0
ecmascript-runtime-client@0.12.1
ecmascript-runtime-server@0.11.0
ejson@1.1.2
email@2.2.1
es5-shim@4.8.0
fetch@0.1.1
geojson-utils@1.0.10
google-oauth@1.4.2
hot-code-push@1.0.4
html-tools@1.1.3
htmljs@1.1.1
http@2.0.0
id-map@1.1.1
inter-process-messaging@0.1.1
lai:collection-extensions@0.3.0
launch-screen@1.3.0
littledata:synced-cron@1.5.1
livedata@1.0.18
localstorage@1.2.0
logging@1.3.1
mdg:meteor-apm-agent@3.5.0
mdg:validated-method@1.2.0
meteor@1.10.0
meteor-base@1.5.1
meteortesting:browser-tests@1.3.5
meteortesting:mocha@2.0.3
meteortesting:mocha-core@8.1.2
mikowals:batch-insert@1.3.0
minifier-css@1.6.0
minifier-js@2.7.4
minimongo@1.8.0
mobile-experience@1.1.0
mobile-status-bar@1.1.0
modern-browsers@0.1.8
modules@0.18.0
modules-runtime@0.13.0
mongo@1.15.0
mongo-decimal@0.1.3
mongo-dev-server@1.1.0
mongo-id@1.0.8
mongo-livedata@1.0.12
npm-mongo@4.3.1
oauth@2.1.2
oauth2@1.3.1
ordered-dict@1.1.0
ostrio:cookies@2.7.2
ostrio:files@2.0.1
patreon-oauth@0.1.0
peerlibrary:assert@0.3.0
peerlibrary:check-extension@0.7.0
peerlibrary:computed-field@0.10.0
peerlibrary:data-lookup@0.3.0
peerlibrary:extend-publish@0.6.0
peerlibrary:fiber-utils@0.10.0
peerlibrary:reactive-mongo@0.4.1
peerlibrary:reactive-publish@0.10.0
peerlibrary:server-autorun@0.8.0
peerlibrary:subscription-data@0.8.0
percolate:migrations@1.0.3
promise@0.12.0
raix:eventemitter@1.0.0
random@1.2.0
rate-limit@1.0.9
react-fast-refresh@0.2.3
reactive-dict@1.3.0
reactive-var@1.0.11
reload@1.3.1
retry@1.1.0
routepolicy@1.1.1
seba:minifiers-autoprefixer@2.0.1
service-configuration@1.3.0
session@1.2.0
sha@1.0.9
shell-server@0.5.0
simple:json-routes@2.3.1
simple:rest@1.2.1
simple:rest-bearer-token-parser@1.1.1
simple:rest-json-error-handler@1.1.1
simple:rest-method-mixin@1.1.0
socket-stream-client@0.5.0
spacebars-compiler@1.3.1
standard-minifier-js@2.8.0
static-html@1.3.2
templating-tools@1.2.2
tmeasday:check-npm-versions@1.0.2
tracker@1.2.0
typescript@4.5.4
underscore@1.0.10
url@1.3.2
webapp@1.13.1
webapp-hashing@1.1.0
zer0th:meteor-vuetify-loader@0.1.41

View File

@@ -1,9 +1,9 @@
<head>
<meta name="viewport" content="width=device-width initial-scale=1.0, user-scalable=no">
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width initial-scale=1.0, user-scalable=no">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<link href='https://fonts.googleapis.com/css?family=Roboto:400,300,300italic,400italic,500,500italic,700,700italic,900,900italic,100italic,100&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png?v=lk6WXp6Pmj">
<link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png?v=lk6WXp6Pmj">
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png?v=lk6WXp6Pmj">
@@ -23,4 +23,17 @@
<meta name="msapplication-TileColor" content="#b91d1d">
<meta name="msapplication-TileImage" content="/mstile-144x144.png?v=lk6WXp6Pmj">
<meta name="theme-color" content="#d12929">
<style type="text/css" media="print">
@page {
margin: 0mm;
}
html {
margin: 0px;
}
* {
-webkit-transition: none !important;
transition: none !important;
}
</style>
</head>

3
app/client/index.html Normal file
View File

@@ -0,0 +1,3 @@
<body>
<div id="app"></div>
</body>

7
app/client/main.js Normal file
View File

@@ -0,0 +1,7 @@
import '/imports/api/simpleSchemaConfig.js';
import '/imports/ui/vueSetup.js';
import '/imports/ui/styles/stylesIndex.js';
import '/imports/client/config.js';
import '/imports/client/serviceWorker.js';
import 'ngraph.graph';

View File

@@ -0,0 +1,6 @@
{
"public": {
"environment": "production",
"disablePatreon": true
}
}

View File

@@ -0,0 +1,52 @@
import { createS3FilesCollection } from '/imports/api/files/s3FileStorage.js';
import SimpleSchema from 'simpl-schema';
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed.js';
import { CreaturePropertySchema } from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { CreatureSchema } from '/imports/api/creature/creatures/Creatures.js';
const ArchiveCreatureFiles = createS3FilesCollection({
collectionName: 'archiveCreatureFiles',
storagePath: Meteor.isDevelopment ? '/DiceCloud/archiveCreatures/' : 'assets/app/archiveCreatures',
onBeforeUpload(file) {
// Allow upload files under 10MB, and only in json format
if (file.size > 10485760) {
return 'Please upload with size equal or less than 10MB';
}
if (!/json/i.test(file.extension)){
return 'Please upload only a JSON file';
}
return true;
},
onAfterUpload(file) {
if (Meteor.isServer) incrementFileStorageUsed(file.userId, file.size);
}
});
let archiveSchema = new SimpleSchema({
meta: {
type: Object,
blackbox: true,
},
creature: CreatureSchema,
properties: {
type: Array,
},
'properties.$': CreaturePropertySchema,
experiences: {
type: Array,
},
'experiences.$': {
type: Object,
blackbox: true,
},
logs: {
type: Array,
},
'logs.$': {
type: Object,
blackbox: true,
},
});
export default ArchiveCreatureFiles;
export { archiveSchema };

View File

@@ -0,0 +1,78 @@
import SCHEMA_VERSION from '/imports/constants/SCHEMA_VERSION.js';
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions.js';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
import Experiences from '/imports/api/creature/experience/Experiences.js';
import { removeCreatureWork } from '/imports/api/creature/creatures/methods/removeCreature.js';
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles.js';
export function getArchiveObj(creatureId){
// Build the archive document
const creature = Creatures.findOne(creatureId);
const properties = CreatureProperties.find({'ancestors.id': creatureId}).fetch();
const experiences = Experiences.find({creatureId}).fetch();
const logs = CreatureLogs.find({creatureId}).fetch();
let archiveCreature = {
meta: {
type: 'DiceCloud V2 Creature Archive',
schemaVersion: SCHEMA_VERSION,
archiveDate: new Date(),
},
creature,
properties,
experiences,
logs,
};
return archiveCreature;
}
export function archiveCreature(creatureId){
const archive = getArchiveObj(creatureId);
const buffer = Buffer.from(JSON.stringify(archive, null, 2));
ArchiveCreatureFiles.write(buffer, {
fileName: `${archive.creature.name || archive.creature._id}.json`,
type: 'application/json',
userId: archive.creature.owner,
meta: {
schemaVersion: SCHEMA_VERSION,
creatureId: archive.creature._id,
creatureName: archive.creature.name,
},
}, (error) => {
if (error){
throw error;
} else {
removeCreatureWork(creatureId);
}
}, true);
}
const archiveCreatureToFile = new ValidatedMethod({
name: 'Creatures.methods.archiveCreatureToFile',
validate: new SimpleSchema({
'creatureId': {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 10,
timeInterval: 5000,
},
async run({creatureId}) {
assertOwnership(creatureId, this.userId);
if (Meteor.isServer){
archiveCreature(creatureId, this.userId);
} else {
removeCreatureWork(creatureId);
}
},
});
export default archiveCreatureToFile;

View File

@@ -0,0 +1,3 @@
import '/imports/api/creature/archive/methods/archiveCreatureToFile.js';
import '/imports/api/creature/archive/methods/restoreCreatureFromFile.js';
import '/imports/api/creature/archive/methods/removeArchiveCreature.js';

View File

@@ -0,0 +1,40 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles.js';
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed.js';
const removeArchiveCreature = new ValidatedMethod({
name: 'ArchiveCreatureFiles.methods.removeArchiveCreature',
validate: new SimpleSchema({
'fileId': {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
async run({ fileId }) {
// fetch the file
const file = ArchiveCreatureFiles.findOne({ _id: fileId }).get();
if (!file) {
throw new Meteor.Error('File not found',
'The requested creature archive does not exist');
}
// Assert ownership
const userId = file?.userId;
if (!userId || userId !== this.userId) {
throw new Meteor.Error('Permission denied',
'You can only restore creatures you own');
}
//Remove the archive once the restore succeeded
ArchiveCreatureFiles.remove({ _id: fileId });
// Update the user's file storage limits
incrementFileStorageUsed(userId, -file.size);
},
});
export default removeArchiveCreature;

View File

@@ -0,0 +1,104 @@
import SCHEMA_VERSION from '/imports/constants/SCHEMA_VERSION.js';
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
import Experiences from '/imports/api/creature/experience/Experiences.js';
import { removeCreatureWork } from '/imports/api/creature/creatures/methods/removeCreature.js';
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles.js';
import assertHasCharactersSlots from '/imports/api/creature/creatures/methods/assertHasCharacterSlots.js';
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed.js';
import verifyArchiveSafety from '/imports/api/creature/archive/methods/verifyArchiveSafety.js';
let migrateArchive;
if (Meteor.isServer){
migrateArchive = require('/imports/migrations/server/migrateArchive.js').default;
}
function restoreCreature(archive, userId){
if (SCHEMA_VERSION < archive.meta.schemaVersion){
throw new Meteor.Error('Incompatible',
'The archive file is from a newer version. Update required to read.')
}
// Migrate and verify the archive meets the current schema
migrateArchive(archive);
// Asset that the archive is safe
verifyArchiveSafety(archive);
// Don't upload creatures twice
const existingCreature = Creatures.findOne(archive.creature._id, {
fields: { _id: 1 }
});
if (existingCreature) throw new Meteor.Error('Already exists',
'The creature you are trying to restore already exists.')
// Ensure the user owns the restored creature
archive.creature.owner = userId;
// Insert the creature sub documents
// They still have their original _id's
Creatures.insert(archive.creature);
try {
// Add all the properties
if (archive.properties && archive.properties.length){
CreatureProperties.batchInsert(archive.properties);
}
if (archive.experiences && archive.experiences.length){
Experiences.batchInsert(archive.experiences);
}
if (archive.logs && archive.logs.length){
CreatureLogs.batchInsert(archive.logs);
}
} catch (e) {
// If the above fails, delete the inserted creature
removeCreatureWork(archive.creature._id);
throw e;
}
}
const restoreCreaturefromFile = new ValidatedMethod({
name: 'Creatures.methods.restoreCreaturefromFile',
validate: new SimpleSchema({
'fileId': {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 10,
timeInterval: 5000,
},
async run({fileId}) {
// fetch the file
const file = ArchiveCreatureFiles.findOne({_id: fileId}).get();
if (!file){
throw new Meteor.Error('File not found',
'The requested creature archive does not exist');
}
// Assert ownership
const userId = file?.userId;
if (!userId || userId !== this.userId){
throw new Meteor.Error('Permission denied',
'You can only restore creatures you own');
}
assertHasCharactersSlots(this.userId);
if (Meteor.isServer){
// Read the file data
const archive = await ArchiveCreatureFiles.readJSONFile(file);
restoreCreature(archive, this.userId);
}
//Remove the archive once the restore succeeded
ArchiveCreatureFiles.remove({ _id: fileId });
// Update the user's file storage limits
incrementFileStorageUsed(userId, -file.size);
},
});
export default restoreCreaturefromFile;

View File

@@ -0,0 +1,28 @@
import { slice } from 'lodash';
import PER_CREATURE_LOG_LIMIT from '/imports/api/creature/log/CreatureLogs.js';
export default function verifyArchiveSafety({ meta, creature, properties, experiences, logs }){
const creatureId = creature._id;
// Check lengths of arrays
if (logs.length > PER_CREATURE_LOG_LIMIT) {
logs = slice(logs, 0, PER_CREATURE_LOG_LIMIT);
}
// Check that everything belongs to the right creature
logs.forEach(log => {
if (log.creatureId !== creatureId) {
throw new Meteor.Error('Malicious log', 'Log contains an entry for the wrong creature');
}
});
experiences.forEach(experience => {
if (experience.creatureId !== creatureId) {
throw new Meteor.Error('Malicious experience', 'Experiences contains an entry for the wrong creature');
}
});
properties.forEach(prop => {
if (prop.ancestors[0].id !== creatureId) {
throw new Meteor.Error('Malicious prop', 'Properties contains an entry for the wrong creature');
}
});
}

View File

@@ -0,0 +1,39 @@
import SimpleSchema from 'simpl-schema';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
let CreatureFolders = new Mongo.Collection('creatureFolders');
let creatureFolderSchema = new SimpleSchema({
name: {
type: String,
trim: false,
optional: true,
max: STORAGE_LIMITS.name,
},
creatures: {
type: Array,
defaultValue: [],
},
'creatures.$': {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
owner: {
type: String,
regEx: SimpleSchema.RegEx.Id,
index: 1,
},
archived: {
type: Boolean,
optional: true,
},
order: {
type: Number,
defaultValue: 0,
},
});
CreatureFolders.attachSchema(creatureFolderSchema);
import '/imports/api/creature/creatureFolders/methods.js/index.js';
export default CreatureFolders;

View File

@@ -0,0 +1,4 @@
import '/imports/api/creature/creatureFolders/methods.js/insertCreatureFolder.js';
import '/imports/api/creature/creatureFolders/methods.js/updateCreatureFolderName.js';
import '/imports/api/creature/creatureFolders/methods.js/removeCreatureFolder.js';
import '/imports/api/creature/creatureFolders/methods.js/moveCreatureToFolder.js';

View File

@@ -0,0 +1,46 @@
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
const insertCreatureFolder = new ValidatedMethod({
name: 'creatureFolders.methods.insert',
validate: null,
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run() {
// Ensure logged in
let userId = this.userId;
if (!userId) {
throw new Meteor.Error('creatureFolders.methods.insert.denied',
'You need to be logged in to insert a folder');
}
// Limit folders to 50 per user
let existingFolders = CreatureFolders.find({
owner: userId
}, {
fields: {order: 1},
sort: {order :-1}
});
if (existingFolders.count() >= 50){
throw new Meteor.Error('creatureFolders.methods.insert.denied',
'You can not have more than 50 folders');
}
// Make the new folder the last in the order
let order = 0;
let lastFolder = existingFolders.fetch()[0];
if (lastFolder){
order = (lastFolder.order || 0) + 1;
}
// Insert
return CreatureFolders.insert({
name: 'Folder',
owner: userId,
order,
});
},
});
export default insertCreatureFolder;

View File

@@ -0,0 +1,45 @@
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
const moveCreatureToFolder = new ValidatedMethod({
name: 'creatureFolders.methods.moveCreatureToFolder',
validate: null,
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({creatureId, folderId}) {
// Ensure logged in
let userId = this.userId;
if (!userId) {
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
'You need to be logged in to remove a folder');
}
// Check that this folder is owned by the user
if (folderId){
let existingFolder = CreatureFolders.findOne(folderId);
if (existingFolder.owner !== userId){
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
'This folder does not belong to you');
}
}
// Remove from other folders
CreatureFolders.update({
owner: userId
}, {
$pull: {creatures: creatureId},
}, {
multi: true,
});
if (folderId){
// Add to this folder
CreatureFolders.update(folderId, {
$addToSet: {creatures: creatureId},
});
}
},
});
export default moveCreatureToFolder;

View File

@@ -0,0 +1,31 @@
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
const removeCreatureFolder = new ValidatedMethod({
name: 'creatureFolders.methods.remove',
validate: null,
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({_id}) {
// Ensure logged in
let userId = this.userId;
if (!userId) {
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
'You need to be logged in to remove a folder');
}
// Check that this folder is owned by the user
let existingFolder = CreatureFolders.findOne(_id);
if (existingFolder.owner !== userId){
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
'This folder does not belong to you');
}
// Remove
return CreatureFolders.remove(_id);
},
});
export default removeCreatureFolder;

View File

@@ -0,0 +1,43 @@
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
const reorderCreatureFolder = new ValidatedMethod({
name: 'creatureFolders.methods.reorder',
validate: null,
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({_id, order}) {
// Ensure logged in
let userId = this.userId;
if (!userId) {
throw new Meteor.Error('creatureFolders.methods.reorder.denied',
'You need to be logged in to reorder a folder');
}
// Check that this folder is owned by the user
let existingFolder = CreatureFolders.findOne(_id);
if (existingFolder.owner !== userId){
throw new Meteor.Error('creatureFolders.methods.reorder.denied',
'This folder does not belong to you');
}
// First give it the new order, it should end in 0.5 putting it between two other docs
CreatureFolders.update(_id, {$set: {order}});
this.unblock();
// Reorder all the folders with integer numbers in this new order
CreatureFolders.find({
owner: userId
}, {
fields: {order: 1,},
sort: {order: -1}
}).forEach((folder, index) => {
if (folder.order !== index){
CreatureFolders.update(_id, {$set: {order: index}})
}
});
},
});
export default reorderCreatureFolder;

View File

@@ -0,0 +1,31 @@
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
const updateCreatureFolderName = new ValidatedMethod({
name: 'creatureFolders.methods.updateName',
validate: null,
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({_id, name}) {
// Ensure logged in
let userId = this.userId;
if (!userId) {
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
'You need to be logged in to update a folder');
}
// Check that this folder is owned by the user
let existingFolder = CreatureFolders.findOne(_id);
if (existingFolder.owner !== userId){
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
'This folder does not belong to you');
}
// Update
return CreatureFolders.update(_id, {$set: {name}});
},
});
export default updateCreatureFolderName;

View File

@@ -0,0 +1,112 @@
import { Mongo } from 'meteor/mongo';
import SimpleSchema from 'simpl-schema';
import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema.js';
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema.js';
import propertySchemasIndex from '/imports/api/properties/computedPropertySchemasIndex.js';
import { storedIconsSchema } from '/imports/api/icons/Icons.js';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
let CreatureProperties = new Mongo.Collection('creatureProperties');
let CreaturePropertySchema = new SimpleSchema({
_id: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
_migrationError: {
type: String,
optional: true,
},
type: {
type: String,
allowedValues: Object.keys(propertySchemasIndex),
},
tags: {
type: Array,
defaultValue: [],
maxCount: STORAGE_LIMITS.tagCount,
},
'tags.$': {
type: String,
max: STORAGE_LIMITS.tagLength,
},
disabled: {
type: Boolean,
optional: true,
},
icon: {
type: storedIconsSchema,
optional: true,
max: STORAGE_LIMITS.icon,
},
// Reference to the library node that this property was copied from
libraryNodeId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
optional: true,
},
});
const DenormalisedOnlyCreaturePropertySchema = new SimpleSchema({
// Denormalised flag if this property is inactive on the sheet for any reason
// Including being disabled, or a decendent of a disabled property
inactive: {
type: Boolean,
optional: true,
index: 1,
removeBeforeCompute: true,
},
// Denormalised flag if this property was made inactive by an inactive
// ancestor. True if this property has an inactive ancestor even if this
// property is itself inactive
deactivatedByAncestor: {
type: Boolean,
optional: true,
index: 1,
removeBeforeCompute: true,
},
// Denormalised flag if this property was made inactive because of its own
// state
deactivatedBySelf: {
type: Boolean,
optional: true,
index: 1,
removeBeforeCompute: true,
},
// Denormalised flag if this property was made inactive because of a toggle
// calculation. Either an ancestor toggle calculation or its own.
deactivatedByToggle: {
type: Boolean,
optional: true,
index: 1,
removeBeforeCompute: true,
},
// When this is true on any property, the creature needs to be recomputed
dirty: {
type: Boolean,
// Default to true because new properties cause a recomputation
defaultValue: true,
optional: true,
},
});
CreaturePropertySchema.extend(DenormalisedOnlyCreaturePropertySchema);
for (let key in propertySchemasIndex){
let schema = new SimpleSchema({});
schema.extend(propertySchemasIndex[key]);
schema.extend(CreaturePropertySchema);
schema.extend(ColorSchema);
schema.extend(ChildSchema);
schema.extend(SoftRemovableSchema);
CreatureProperties.attachSchema(schema, {
selector: {type: key}
});
}
export default CreatureProperties;
export {
DenormalisedOnlyCreaturePropertySchema,
CreaturePropertySchema,
};

View File

@@ -0,0 +1,5 @@
import Creatures from '/imports/api/creature/creatures/Creatures.js';
export default function getRootCreatureAncestor(property){
return Creatures.findOne(property.ancestors[0].id);
}

View File

@@ -0,0 +1,63 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import SimpleSchema from 'simpl-schema';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
const adjustQuantity = new ValidatedMethod({
name: 'creatureProperties.adjustQuantity',
validate: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
operation: {
type: String,
allowedValues: ['set', 'increment']
},
value: Number,
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({_id, operation, value}) {
// Permissions
let property = CreatureProperties.findOne(_id);
let rootCreature = getRootCreatureAncestor(property);
assertEditPermission(rootCreature, this.userId);
// Do work
adjustQuantityWork({property, operation, value});
},
});
export function adjustQuantityWork({property, operation, value}){
// Check if property has quantity
let schema = CreatureProperties.simpleSchema(property);
if (!schema.allowsKey('quantity')){
throw new Meteor.Error(
'Adjust quantity failed',
`Property of type "${property.type}" doesn't have a quantity`
);
}
if (operation === 'set'){
CreatureProperties.update(property._id, {
$set: {quantity: value, dirty: true}
}, {
selector: property
});
} else if (operation === 'increment'){
// value here is 'damage'
value = -value;
let currentQuantity = property.quantity;
if (currentQuantity + value < 0) value = -currentQuantity;
CreatureProperties.update(property._id, {
$inc: { quantity: value },
$set: { dirty: true }
}, {
selector: property
});
}
}
export default adjustQuantity;

View File

@@ -0,0 +1,125 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import SimpleSchema from 'simpl-schema';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers.js';
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
const damageProperty = new ValidatedMethod({
name: 'creatureProperties.damage',
validate: new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
operation: {
type: String,
allowedValues: ['set', 'increment']
},
value: Number,
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 20,
timeInterval: 5000,
},
run({ _id, operation, value }) {
// Get action context
const prop = CreatureProperties.findOne(_id);
if (!prop) throw new Meteor.Error(
'Damage property failed', 'Property doesn\'t exist'
);
const creatureId = prop.ancestors[0].id;
const actionContext = new ActionContext(creatureId, [creatureId], this);
// Check permissions
assertEditPermission(actionContext.creature, this.userId);
// Check if property can take damage
let schema = CreatureProperties.simpleSchema(prop);
if (!schema.allowsKey('damage')){
throw new Meteor.Error(
'Damage property failed',
`Property of type "${prop.type}" can't be damaged`
);
}
const result = damagePropertyWork({ prop, operation, value, actionContext });
// Insert the log
actionContext.writeLog();
return result;
},
});
export function damagePropertyWork({ prop, operation, value, actionContext }) {
// Save the value to the scope before applying the before triggers
if (operation === 'increment') {
if (value >= 0) {
actionContext.scope['$damage'] = value;
} else {
actionContext.scope['$healing'] = -value;
}
} else {
actionContext.scope['$set'] = value;
}
applyTriggers(actionContext.triggers?.damageProperty?.before, prop, actionContext);
// fetch the value from the scope after the before triggers, in case they changed them
if (operation === 'increment') {
if (value >= 0) {
value = actionContext.scope['$damage'];
} else {
value = -actionContext.scope['$healing'];
}
} else {
value = actionContext.scope['$set'];
}
let damage, newValue, increment;
if (operation === 'set'){
const total = prop.total || 0;
// Set represents what we want the value to be after damage
// So we need the actual damage to get to that value
damage = total - value;
// Damage can't exceed total value
if (damage > total && !prop.ignoreLowerLimit) damage = total;
// Damage must be positive
if (damage < 0 && !prop.ignoreUpperLimit) damage = 0;
newValue = prop.total - damage;
// Write the results
CreatureProperties.update(prop._id, {
$set: { damage, value: newValue, dirty: true }
}, {
selector: prop
});
} else if (operation === 'increment'){
let currentValue = prop.value || 0;
let currentDamage = prop.damage || 0;
increment = value;
// Can't increase damage above the remaining value
if (increment > currentValue && !prop.ignoreLowerLimit) increment = currentValue;
// Can't decrease damage below zero
if (-increment > currentDamage && !prop.ignoreUpperLimit) increment = -currentDamage;
damage = currentDamage + increment;
newValue = prop.total - damage;
// Write the results
CreatureProperties.update(prop._id, {
$inc: { damage: increment, value: -increment },
$set: { dirty: true },
}, {
selector: prop
});
}
applyTriggers(actionContext.triggers?.damageProperty?.after, prop, actionContext);
if (operation === 'set') {
return damage;
} else if (operation === 'increment') {
return increment;
}
}
export default damageProperty;

View File

@@ -0,0 +1,96 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import {
setLineageOfDocs,
renewDocIds
} from '/imports/api/parenting/parenting.js';
import { reorderDocs } from '/imports/api/parenting/order.js';
var snackbar;
if (Meteor.isClient){
snackbar = require(
'/imports/ui/components/snackbars/SnackbarQueue.js'
).snackbar
}
const DUPLICATE_CHILDREN_LIMIT = 50;
const duplicateProperty = new ValidatedMethod({
name: 'creatureProperties.duplicate',
validate: new SimpleSchema({
_id: {
type: String,
regEx: SimpleSchema.RegEx.Id,
}
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({_id}) {
let property = CreatureProperties.findOne(_id);
let creature = getRootCreatureAncestor(property);
assertEditPermission(creature, this.userId);
// Renew the doc ID
let randomSrc = DDP.randomStream('duplicateProperty');
let propertyId = randomSrc.id();
property._id = propertyId;
// Get all the descendants
let nodes = CreatureProperties.find({
'ancestors.id': _id,
removed: {$ne: true},
}, {
limit: DUPLICATE_CHILDREN_LIMIT + 1,
sort: {order: 1},
}).fetch();
// Alert the user if the limit was hit
if (nodes.length > DUPLICATE_CHILDREN_LIMIT){
nodes.pop();
if (Meteor.isClient){
snackbar({
text: `Only the first ${DUPLICATE_CHILDREN_LIMIT} children were duplicated`,
});
}
}
// re-map all the ancestors
setLineageOfDocs({
docArray: nodes,
newAncestry : [
...property.ancestors,
{id: propertyId, collection: 'creatureProperties'}
],
oldParent : {id: _id, collection: 'creatureProperties'},
});
// Give the docs new IDs without breaking internal references
renewDocIds({docArray: nodes});
// Order the root node
property.order += 0.5;
// Mark the sheet as needing recompute
property.dirty = true;
// Insert the properties
CreatureProperties.batchInsert([property, ...nodes]);
// Tree structure changed by inserts, reorder the tree
reorderDocs({
collection: CreatureProperties,
ancestorId: property.ancestors[0].id,
});
return propertyId;
},
});
export default duplicateProperty;

View File

@@ -0,0 +1,51 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { organizeDoc } from '/imports/api/parenting/organizeMethods.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js';
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js';
// Equipping or unequipping an item will also change its parent
const equipItem = new ValidatedMethod({
name: 'creatureProperties.equip',
validate({_id, equipped}){
if (!_id) throw new Meteor.Error('No _id', '_id is required');
if (equipped !== true && equipped !== false) {
throw new Meteor.Error('No equipped', 'equipped is required to be true or false');
}
},
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({_id, equipped}) {
let item = CreatureProperties.findOne(_id);
if (item.type !== 'item') throw new Meteor.Error('wrong type',
'Equip and unequip can only be performed on items');
let creature = getRootCreatureAncestor(item);
assertEditPermission(creature, this.userId);
CreatureProperties.update(_id, {
$set: { equipped, dirty: true },
}, {
selector: {type: 'item'},
});
let tag = equipped ? BUILT_IN_TAGS.equipment : BUILT_IN_TAGS.carried;
let parentRef = getParentRefByTag(creature._id, tag);
if (!parentRef) parentRef = {id: creature._id, collection: 'creatures'};
organizeDoc.call({
docRef: {
id: _id,
collection: 'creatureProperties',
},
parentRef,
order: Number.MAX_SAFE_INTEGER,
skipRecompute: true,
});
},
});
export default equipItem;

View File

@@ -0,0 +1,45 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
const flipToggle = new ValidatedMethod({
name: 'creatureProperties.flipToggle',
validate({_id}){
if (!_id) throw new Meteor.Error('No _id', '_id is required');
},
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({_id}) {
// Permission
let property = CreatureProperties.findOne(_id, {
fields: {type: 1, ancestors: 1, enabled: 1, disabled: 1}
});
if (property.type !== 'toggle'){
throw new Meteor.Error('wrong property',
'This method can only be applied to toggles');
}
if (!property.enabled && !property.disabled){
throw new Meteor.Error('Computed toggle',
'Can\'t flip a toggle that is computed')
}
let rootCreature = getRootCreatureAncestor(property);
assertEditPermission(rootCreature, this.userId);
// Invert the current value, disabled is the canonical store of value
const currentValue = !property.disabled;
CreatureProperties.update(_id, {$set: {
enabled: !currentValue,
disabled: currentValue,
dirty: true,
}}, {
selector: {type: 'toggle'},
});
},
});
export default flipToggle;

View File

@@ -0,0 +1,13 @@
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
export default function getParentRefByTag(creatureId, tag){
let prop = CreatureProperties.findOne({
'ancestors.id': creatureId,
removed: {$ne: true},
inactive: {$ne: true},
tags: tag,
}, {
sort: {order: 1},
});
return prop && {id: prop._id, collection: 'creatureProperties'};
}

View File

@@ -0,0 +1,63 @@
export default function getSlotFillFilter({slot, libraryIds}){
let filter = {
removed: {$ne: true},
$and: []
};
if (libraryIds){
filter['ancestors.id'] = {$in: libraryIds};
}
if (slot.slotType){
filter.$and.push({
$or: [{
type: slot.slotType
},{
type: 'slotFiller',
slotFillerType: slot.slotType,
}]
});
} else if (slot.type === 'class') {
filter.$and.push({
$or: [{
type: 'classLevel',
},{
type: 'slotFiller',
slotFillerType: 'classLevel',
}]
});
if (slot.variableName) {
filter.variableName = slot.variableName;
}
// Only search for levels the class needs
if (slot.missingLevels && slot.missingLevels.length) {
filter.level = {$in: slot.missingLevels};
} else {
filter.level = (slot.level || 0) + 1;
}
}
let tagsOr = [];
let tagsNin = [];
if (slot.slotTags && slot.slotTags.length){
tagsOr.push({tags: {$all: slot.slotTags}});
}
if (slot.extraTags && slot.extraTags.length){
slot.extraTags.forEach(extra => {
if (!extra.tags || !extra.tags.length) return;
if (extra.operation === 'OR'){
tagsOr.push({tags: {$all: extra.tags}});
} else if (extra.operation === 'NOT'){
tagsNin.push(...extra.tags);
}
});
}
if (tagsOr.length){
filter.$or = tagsOr;
}
if (tagsNin.length){
filter.$and.push({tags: {$nin: tagsNin}});
}
if (!filter.$and.length){
delete filter.$and;
}
return filter;
}

View File

@@ -0,0 +1,13 @@
import '/imports/api/creature/creatureProperties/methods/adjustQuantity.js';
import '/imports/api/creature/creatureProperties/methods/damageProperty.js';
import '/imports/api/creature/creatureProperties/methods/duplicateProperty.js';
import '/imports/api/creature/creatureProperties/methods/equipItem.js';
import '/imports/api/creature/creatureProperties/methods/insertProperty.js';
import '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
import '/imports/api/creature/creatureProperties/methods/pullFromProperty.js';
import '/imports/api/creature/creatureProperties/methods/pushToProperty.js';
import '/imports/api/creature/creatureProperties/methods/restoreProperty.js';
import '/imports/api/creature/creatureProperties/methods/selectAmmoItem.js';
import '/imports/api/creature/creatureProperties/methods/softRemoveProperty.js';
import '/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js';
import '/imports/api/creature/creatureProperties/methods/flipToggle.js';

View File

@@ -0,0 +1,145 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import SimpleSchema from 'simpl-schema';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { reorderDocs } from '/imports/api/parenting/order.js';
import { getAncestry } from '/imports/api/parenting/parenting.js';
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js';
import { RefSchema } from '/imports/api/parenting/ChildSchema.js';
import { getHighestOrder } from '/imports/api/parenting/order.js';
const insertProperty = new ValidatedMethod({
name: 'creatureProperties.insert',
validate: new SimpleSchema({
creatureProperty: {
type: Object,
blackbox: true,
},
parentRef: RefSchema,
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({creatureProperty, parentRef}) {
// get the new ancestry for the properties
let {parentDoc, ancestors} = getAncestry({parentRef});
// Check permission to edit
let rootCreature;
if (parentRef.collection === 'creatures'){
rootCreature = parentDoc;
} else if (parentRef.collection === 'creatureProperties'){
rootCreature = getRootCreatureAncestor(parentDoc);
} else {
throw `${parentRef.collection} is not a valid parent collection`
}
assertEditPermission(rootCreature, this.userId);
creatureProperty.parent = parentRef;
creatureProperty.ancestors = ancestors;
return insertPropertyWork({
property: creatureProperty,
creature: rootCreature,
});
},
});
const insertPropertyAsChildOfTag = new ValidatedMethod({
name: 'creatureProperties.insertAsChildOfTag',
validate: new SimpleSchema({
creatureProperty: {
type: Object,
blackbox: true,
},
creatureId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
tag: {
type: String,
max: 20,
},
tagDefaultName: {
type: String,
max: 20,
optional: true,
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({creatureProperty, creatureId, tag, tagDefaultName}) {
let parentRef = getParentRefByTag(creatureId, tag);
if (!parentRef){
// Use the creature as the parent and mark that we need to insert the folder first later
var insertFolderFirst = true;
parentRef = {id: creatureId, collection: 'creatures'};
}
// get the new ancestry for the properties
let {parentDoc, ancestors} = getAncestry({parentRef});
// Check permission to edit
let rootCreature;
if (parentRef.collection === 'creatures'){
rootCreature = parentDoc;
} else if (parentRef.collection === 'creatureProperties'){
rootCreature = getRootCreatureAncestor(parentDoc);
} else {
throw `${parentRef.collection} is not a valid parent collection`
}
assertEditPermission(rootCreature, this.userId);
// Add the folder first if we need to
if (insertFolderFirst){
let order = getHighestOrder({
collection: CreatureProperties,
ancestorId: parentRef.id,
}) + 1;
let id = CreatureProperties.insert({
type: 'folder',
name: tagDefaultName || (tag.charAt(0).toUpperCase() + tag.slice(1)),
tags: [tag],
parent: parentRef,
ancestors: [parentRef],
order,
});
// Make the folder our new parent
let newParentRef = {id, collection: 'creatureProperties'};
ancestors = [parentRef, newParentRef];
parentRef = newParentRef;
creatureProperty.order = order + 1;
}
creatureProperty.parent = parentRef;
creatureProperty.ancestors = ancestors;
return insertPropertyWork({
property: creatureProperty,
creature: rootCreature,
});
},
});
export function insertPropertyWork({property, creature}){
delete property._id;
property.dirty = true;
let _id = CreatureProperties.insert(property);
// Tree structure changed by insert, reorder the tree
reorderDocs({
collection: CreatureProperties,
ancestorId: creature._id,
});
return _id;
}
export default insertProperty;
export { insertPropertyAsChildOfTag };

View File

@@ -0,0 +1,245 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
import { RefSchema } from '/imports/api/parenting/ChildSchema.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import {
setLineageOfDocs,
getAncestry,
renewDocIds
} from '/imports/api/parenting/parenting.js';
import { reorderDocs } from '/imports/api/parenting/order.js';
import { setDocToLastOrder } from '/imports/api/parenting/order.js';
import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
const insertPropertyFromLibraryNode = new ValidatedMethod({
name: 'creatureProperties.insertPropertyFromLibraryNode',
validate: new SimpleSchema({
nodeIds: {
type: Array,
max: 20,
},
'nodeIds.$': {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
parentRef: {
type: RefSchema,
},
order: {
type: Number,
optional: true,
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({nodeIds, parentRef, order}) {
// get the new ancestry for the properties
let {parentDoc, ancestors} = getAncestry({parentRef});
// Check permission to edit
let rootCreature;
if (parentRef.collection === 'creatures'){
rootCreature = parentDoc;
} else if (parentRef.collection === 'creatureProperties'){
rootCreature = getRootCreatureAncestor(parentDoc);
} else {
throw `${parentRef.collection} is not a valid parent collection`
}
assertEditPermission(rootCreature, this.userId);
// {libraryId: hasViewPermission}
//let libraryPermissionMemoir = {};
let node;
nodeIds.forEach(nodeId => {
// TODO: Check library view permission for each node before starting
node = insertPropertyFromNode(nodeId, ancestors, order);
});
// get one of the root inserted docs
let rootId = node._id;
// Tree structure changed by inserts, reorder the tree
reorderDocs({
collection: CreatureProperties,
ancestorId: rootCreature._id,
});
// Return the docId of the last property, the inserted root property
return rootId;
},
});
function insertPropertyFromNode(nodeId, ancestors, order){
// Fetch the library node and its decendents, provided they have not been
// removed
// TODO: Check permission to read the library this node is in
let node = LibraryNodes.findOne({
_id: nodeId,
removed: {$ne: true},
});
if (!node) {
if (Meteor.isClient) return;
else {
throw new Meteor.Error(
'Insert property from library failed',
`No library document with id '${nodeId}' was found`
);
}
}
let oldParent = node.parent;
let nodes = LibraryNodes.find({
'ancestors.id': nodeId,
removed: {$ne: true},
}).fetch();
// Convert all references into actual nodes
nodes = reifyNodeReferences(nodes);
// The root node is first in the array of nodes
// It must get the first generated ID to prevent flickering
nodes = [node, ...nodes];
// set libraryNodeIds
storeLibraryNodeReferences(nodes);
// re-map all the ancestors
setLineageOfDocs({
docArray: nodes,
newAncestry: ancestors,
oldParent,
});
// Give the docs new IDs without breaking internal references
renewDocIds({
docArray: nodes,
collectionMap: {'libraryNodes': 'creatureProperties'}
});
// Order the root node
if (order === undefined){
setDocToLastOrder({
collection: CreatureProperties,
doc: node,
});
} else {
node.order = order;
}
// Mark all nodes as dirty
dirtyNodes(nodes);
// Insert the creature properties
CreatureProperties.batchInsert(nodes);
return node;
}
function storeLibraryNodeReferences(nodes){
nodes.forEach(node => {
if (node.libraryNodeId) return;
node.libraryNodeId = node._id;
});
}
function dirtyNodes(nodes) {
nodes.forEach(node => {
node.dirty = true;
});
}
// Covert node references into actual nodes
// TODO: check permissions for each library a reference node references
function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0){
depth += 1;
// New nodes added this function
let newNodes = [];
// Filter out the reference nodes we replace
let resultingNodes = nodes.filter(node => {
// This isn't a reference node, continue as normal
if (node.type !== 'reference') return true;
// We have gone too deep, keep the reference node as an error
if (depth >= 10){
if (Meteor.isClient) console.warn('Reference depth limit exceeded');
node.cache = {error: 'Reference depth limit exceeded'};
return true;
}
let referencedNode
try {
referencedNode = fetchDocByRef(node.ref);
referencedNode.order = node.order;
// We are definitely replacing this node, so add it to the list
visitedRefs.add(node._id);
} catch (e){
node.cache = {error: e.reason || e.message || e.toString()};
return true;
}
// Get all the descendants of the referenced node
let descendents = LibraryNodes.find({
'ancestors.id': referencedNode._id,
removed: {$ne: true},
}, {
sort: {order: 1},
}).fetch();
// We are adding the referenced node and its descendants
let addedNodes = [referencedNode, ...descendents];
// re-map all the ancestors to parent the new sub-tree into our existing
// node tree
setLineageOfDocs({
docArray: addedNodes,
newAncestry: node.ancestors,
oldParent: referencedNode.parent,
});
// Filter all the looped references
addedNodes = addedNodes.filter(addedNode => {
// Add all non-reference nodes
if (addedNode.type !== 'reference'){
return true;
}
// If this exact reference has already been resolved before, filter it out
if (visitedRefs.has(addedNode._id)){
return false;
} else {
// Otherwise mark it as visited, and keep it
visitedRefs.add(addedNode._id);
return true;
}
});
// Before renewing Ids make sure the library node reference is stored
storeLibraryNodeReferences(addedNodes);
// Give the new referenced sub-tree new ids
// The referenced node must get the id of the ref node so that the
// descendants of the ref node keep their ancestry intact
renewDocIds({
docArray: addedNodes,
idMap: { [referencedNode._id]: node._id },
});
// Reify the subtree as well with recursion
addedNodes = reifyNodeReferences(addedNodes, visitedRefs, depth);
// Store the new nodes from this inner loop without altering the array
// we are looping over
newNodes.push(...addedNodes);
});
// We are done filtering the array, we can add the new nodes to it
resultingNodes.push(...newNodes);
return resultingNodes;
}
export default insertPropertyFromLibraryNode;

View File

@@ -0,0 +1,32 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
const pullFromProperty = new ValidatedMethod({
name: 'creatureProperties.pull',
validate: null,
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({_id, path, itemId}){
// Permissions
let property = CreatureProperties.findOne(_id);
let rootCreature = getRootCreatureAncestor(property);
assertEditPermission(rootCreature, this.userId);
// Do work
CreatureProperties.update(_id, {
$pull: { [path.join('.')]: { _id: itemId } },
$set: { dirty: true }
}, {
selector: {type: property.type},
getAutoValues: false,
});
}
});
export default pullFromProperty;

View File

@@ -0,0 +1,49 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import { get } from 'lodash';
const pushToProperty = new ValidatedMethod({
name: 'creatureProperties.push',
validate: null,
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({_id, path, value}){
// Permissions
let property = CreatureProperties.findOne(_id);
let rootCreature = getRootCreatureAncestor(property);
assertEditPermission(rootCreature, this.userId);
let joinedPath = path.join('.');
// Respect maxCount
let schema = CreatureProperties.simpleSchema(property);
let maxCount = schema.get(joinedPath, 'maxCount');
if (Number.isFinite(maxCount)){
let array = get(property, path);
let currentCount = array ? array.length : 0;
if (currentCount >= maxCount){
throw new Meteor.Error(
'Array is full',
`Cannot have more than ${maxCount} values`
);
}
}
// Do work
CreatureProperties.update(_id, {
$push: { [joinedPath]: value },
$set: { dirty: true },
}, {
selector: {type: property.type},
});
}
});
export default pushToProperty;

View File

@@ -0,0 +1,36 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import SimpleSchema from 'simpl-schema';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { restore } from '/imports/api/parenting/softRemove.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
const restoreProperty = new ValidatedMethod({
name: 'creatureProperties.restore',
validate: new SimpleSchema({
_id: SimpleSchema.RegEx.Id
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({_id}){
// Permissions
let property = CreatureProperties.findOne(_id);
let rootCreature = getRootCreatureAncestor(property);
assertEditPermission(rootCreature, this.userId);
// Do work
restore({
_id,
collection: CreatureProperties,
extraUpdates: {
$set: { dirty: true }
},
});
}
});
export default restoreProperty;

View File

@@ -0,0 +1,46 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import SimpleSchema from 'simpl-schema';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
const selectAmmoItem = new ValidatedMethod({
name: 'creatureProperties.selectAmmoItem',
validate: new SimpleSchema({
actionId: SimpleSchema.RegEx.Id,
itemId: SimpleSchema.RegEx.Id,
itemConsumedIndex: Number,
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({actionId, itemId, itemConsumedIndex}) {
// Permissions
let action = CreatureProperties.findOne(actionId);
let rootCreature = getRootCreatureAncestor(action);
assertEditPermission(rootCreature, this.userId);
// Check that this index has a document to edit
let itemConsumed = action.resources.itemsConsumed[itemConsumedIndex];
if (!itemConsumed){
throw new Meteor.Error('Resouce not found',
'Could not set ammo, because the ammo document was not found');
}
let itemToLink = CreatureProperties.findOne(itemId);
if (!itemToLink){
throw new Meteor.Error('Item not found',
'Could not set ammo: the item was not found');
}
let path = `resources.itemsConsumed.${itemConsumedIndex}.itemId`;
CreatureProperties.update(actionId, {
$set: { [path]: itemId, dirty: true }
}, {
selector: action,
});
},
});
export default selectAmmoItem;

View File

@@ -0,0 +1,30 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import SimpleSchema from 'simpl-schema';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { softRemove } from '/imports/api/parenting/softRemove.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
const softRemoveProperty = new ValidatedMethod({
name: 'creatureProperties.softRemove',
validate: new SimpleSchema({
_id: SimpleSchema.RegEx.Id
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({_id}){
// Permissions
let property = CreatureProperties.findOne(_id);
let rootCreature = getRootCreatureAncestor(property);
assertEditPermission(rootCreature, this.userId);
// Do work
softRemove({_id, collection: CreatureProperties});
}
});
export default softRemoveProperty;

View File

@@ -0,0 +1,49 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
const updateCreatureProperty = new ValidatedMethod({
name: 'creatureProperties.update',
validate({_id, path}){
if (!_id) throw new Meteor.Error('No _id', '_id is required');
// We cannot change these fields with a simple update
switch (path[0]){
case 'type':
case 'order':
case 'parent':
case 'ancestors':
case 'damage':
throw new Meteor.Error('Permission denied',
'This property can\'t be updated directly');
}
},
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({_id, path, value}) {
// Permission
let property = CreatureProperties.findOne(_id, {
fields: {type: 1, ancestors: 1}
});
let rootCreature = getRootCreatureAncestor(property);
assertEditPermission(rootCreature, this.userId);
let pathString = path.join('.');
let modifier;
// unset empty values
if (value === null || value === undefined){
modifier = { $unset: {[pathString]: 1}, $set: { dirty: true } };
} else {
modifier = { $set: {[pathString]: value, dirty: true } };
}
CreatureProperties.update(_id, modifier, {
selector: {type: property.type},
});
},
});
export default updateCreatureProperty;

View File

@@ -0,0 +1,12 @@
import computeCreature from '/imports/api/engine/computeCreature.js';
/**
* Recomputes all ancestor creatures of this property
*/
export default function recomputeCreaturesByProperty(property){
for (let ref of property.ancestors){
if (ref.collection === 'creatures') {
computeCreature.call(ref.id);
}
}
}

View File

@@ -0,0 +1,21 @@
//set up the collection for creature variables
let CreatureVariables = new Mongo.Collection('creatureVariables');
// Unique index on _creatureId
if (Meteor.isServer) {
CreatureVariables._ensureIndex({ _creatureId: 1 }, { unique: true })
}
/** No schema because the structure isn't known until compute time
* Expect documents to looke like:
* {
* _id: "nE8Ngd6K4L4jSxLY2",
* _creatureId: "nE8Ngd6K4L4jSxLY2", // indexed reference to the creature
* explicitlyDefinedVariableName: {...some creatureProperty}
* implicitVariableName: {value: 10},
* undefinedVariableName: {},
* }
* Where top level fields that don't start with `_` are variables on the sheet
**/
export default CreatureVariables;

View File

@@ -0,0 +1,198 @@
import SimpleSchema from 'simpl-schema';
import deathSaveSchema from '/imports/api/properties/subSchemas/DeathSavesSchema.js'
import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema.js';
import SharingSchema from '/imports/api/sharing/SharingSchema.js';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
//set up the collection for creatures
let Creatures = new Mongo.Collection('creatures');
let CreatureSettingsSchema = new SimpleSchema({
//slowed down by carrying too much?
useVariantEncumbrance: {
type: Boolean,
optional: true,
},
//hide spellcasting tab
hideSpellcasting: {
type: Boolean,
optional: true,
},
// Swap around the modifier and stat
swapStatAndModifier: {
type: Boolean,
optional: true,
},
// Hide all the unused stats
hideUnusedStats: {
type: Boolean,
optional: true,
},
// Show the tree tab
showTreeTab: {
type: Boolean,
optional: true,
},
// Hide the spells tab
hideSpellsTab: {
type: Boolean,
optional: true,
},
// Hide calculation errors
hideCalculationErrors: {
type: Boolean,
optional: true,
},
// How much each hitDice resets on a long rest
hitDiceResetMultiplier: {
type: Number,
optional: true,
min: 0,
max: 1,
},
discordWebhook: {
type: String,
optional: true,
max: STORAGE_LIMITS.url,
},
});
let CreatureSchema = new SimpleSchema({
// Strings
name: {
type: String,
defaultValue: '',
optional: true,
max: STORAGE_LIMITS.name,
},
alignment: {
type: String,
optional: true,
max: STORAGE_LIMITS.name,
},
gender: {
type: String,
optional: true,
max: STORAGE_LIMITS.name,
},
picture: {
type: String,
optional: true,
max: STORAGE_LIMITS.url,
},
avatarPicture: {
type: String,
optional: true,
max: STORAGE_LIMITS.url,
},
// Libraries
allowedLibraries: {
type: Array,
optional: true,
maxCount: 100,
},
'allowedLibraries.$': {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
allowedLibraryCollections: {
type: Array,
optional: true,
maxCount: 100,
},
'allowedLibraryCollections.$': {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
// Mechanics
deathSave: {
type: deathSaveSchema,
defaultValue: {},
},
// Stats that are computed and denormalised outside of recomputation
denormalizedStats: {
type: Object,
defaultValue: {},
},
// Sum of all XP gained by this character
'denormalizedStats.xp': {
type: SimpleSchema.Integer,
defaultValue: 0,
},
// Sum of all levels granted by milestone XP
'denormalizedStats.milestoneLevels': {
type: SimpleSchema.Integer,
defaultValue: 0,
},
// Does the character need a recompute?
dirty: {
type: Boolean,
optional: true,
},
// Version of computation engine that was last used to compute this creature
computeVersion: {
type: String,
optional: true,
},
type: {
type: String,
defaultValue: 'pc',
allowedValues: ['pc', 'npc', 'monster'],
},
damageMultipliers: {
type: Object,
blackbox: true,
defaultValue: {}
},
variables: {
type: Object,
blackbox: true,
defaultValue: {}
},
computeErrors: {
type: Array,
optional: true,
},
'computeErrors.$': {
type: Object,
},
'computeErrors.$.type': {
type: String,
},
'computeErrors.$.details' : {
type: Object,
blackbox: true,
optional: true,
},
// Tabletop
tabletop: {
type: String,
regEx: SimpleSchema.RegEx.id,
optional: true,
},
initiativeRoll: {
type: SimpleSchema.Integer,
optional: true,
},
// Settings
settings: {
type: CreatureSettingsSchema,
defaultValue: {},
},
});
CreatureSchema.extend(ColorSchema);
CreatureSchema.extend(SharingSchema);
Creatures.attachSchema(CreatureSchema);
export default Creatures;
export { CreatureSchema };
import '/imports/api/engine/actions/doAction.js';

View File

@@ -0,0 +1,29 @@
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import {
assertEditPermission as editPermission,
assertViewPermission as viewPermission,
assertOwnership as ownership
} from '/imports/api/sharing/sharingPermissions.js';
function getCreature(creature, fields){
if (typeof creature === 'string'){
return Creatures.findOne(creature, {fields});
} else {
return creature;
}
}
export function assertOwnership(creature, userId){
creature = getCreature(creature, {owner: 1});
ownership(creature, userId);
}
export function assertEditPermission(creature, userId) {
creature = getCreature(creature, {owner: 1, writers: 1});
editPermission(creature, userId);
}
export function assertViewPermission(creature, userId) {
creature = getCreature(creature, {owner: 1, readers:1, writers: 1, public: 1});
viewPermission(creature, userId);
}

View File

@@ -0,0 +1,47 @@
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js';
export default function defaultCharacterProperties(creatureId){
if (!creatureId) throw 'creatureId is required';
const creatureRef = {collection: 'creatures', id: creatureId};
let randomSrc = DDP.randomStream('defaultProperties');
const inventoryId = randomSrc.id();
const inventoryRef = {collection: 'creatureProperties', id: inventoryId};
return [
{
type: 'propertySlot',
name: 'Ruleset',
description: {text: 'Choose a starting point for your character, this will define the basic setup of your character sheet. Without a base ruleset, your sheet will be empty.'},
slotTags: ['base'],
tags: [],
quantityExpected: {calculation: '1'},
hideWhenFull: true,
spaceLeft: 1,
totalFilled: 0,
order: 0,
parent: creatureRef,
ancestors: [creatureRef],
}, {
_id: inventoryId,
type: 'folder',
name: 'Inventory',
tags: [BUILT_IN_TAGS.inventory],
order: 1,
parent: creatureRef,
ancestors: [creatureRef],
}, {
type: 'folder',
name: 'Equipment',
tags: [BUILT_IN_TAGS.equipment],
order: 2,
parent: inventoryRef,
ancestors: [creatureRef, inventoryRef],
}, {
type: 'folder',
name: 'Carried',
tags: [BUILT_IN_TAGS.carried],
order: 3,
parent: inventoryRef,
ancestors: [creatureRef, inventoryRef],
},
];
}

View File

@@ -0,0 +1,5 @@
import getSlug from 'speakingurl';
export default function getCreatureUrlName({name}){
return getSlug(name, {maintainCase: true}) || '-';
}

View File

@@ -0,0 +1,22 @@
import { getUserTier } from '/imports/api/users/patreon/tiers.js';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
export default function assertHasCharactersSlots(userId) {
if (characterSlotsRemaining(userId) <= 0) {
throw new Meteor.Error('characterSlotLimit',
'No character slots left')
}
}
export function characterSlotsRemaining(userId) {
let tier = getUserTier(userId);
const currentCharacterCount = Creatures.find({
owner: userId,
}, {
fields: { _id: 1 },
}).count();
if (tier.characterSlots === -1) {
return Number.POSITIVE_INFINITY;
}
return tier.characterSlots - currentCharacterCount;
}

View File

@@ -0,0 +1,90 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import {assertEditPermission} from '/imports/api/sharing/sharingPermissions.js';
import SimpleSchema from 'simpl-schema';
import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin.js';
const changeAllowedLibraries = new ValidatedMethod({
name: 'creatures.changeAllowedLibraries',
mixins: [RateLimiterMixin, simpleSchemaMixin],
schema: new SimpleSchema({
_id: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
allowedLibraries: {
type: Array,
optional: true,
maxCount: 100,
},
'allowedLibraries.$': {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
allowedLibraryCollections: {
type: Array,
optional: true,
maxCount: 100,
},
'allowedLibraryCollections.$': {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
}),
rateLimit: {
numRequests: 10,
timeInterval: 5000,
},
run({_id, allowedLibraries, allowedLibraryCollections}) {
let creature = Creatures.findOne(_id);
assertEditPermission(creature, this.userId);
let $set;
if (allowedLibraries) {
$set = { allowedLibraries }
}
if (allowedLibraryCollections) {
if (!$set) $set = {};
$set.allowedLibraryCollections = allowedLibraryCollections;
}
if (!$set) return;
Creatures.update(_id, {$set});
},
});
const toggleAllUserLibraries = new ValidatedMethod({
name: 'creatures.removeLibraryLimits',
mixins: [RateLimiterMixin, simpleSchemaMixin],
schema: new SimpleSchema({
_id: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
value: {
type: Boolean,
},
}),
rateLimit: {
numRequests: 10,
timeInterval: 5000,
},
run({_id, value}) {
if (value) {
Creatures.update(_id, {
$unset: {
allowedLibraryCollections: 1,
allowedLibraries: 1,
},
});
} else {
Creatures.update(_id, {
$set: {
allowedLibraryCollections: [],
allowedLibraries: [],
},
});
}
},
});
export {changeAllowedLibraries, toggleAllUserLibraries};

View File

@@ -0,0 +1,5 @@
import '/imports/api/creature/creatures/methods/insertCreature.js';
import '/imports/api/creature/creatures/methods/removeCreature.js';
import '/imports/api/creature/creatures/methods/restCreature.js';
import '/imports/api/creature/creatures/methods/updateCreature.js';
import '/imports/api/creature/creatures/methods/changeAllowedLibraries.js';

View File

@@ -0,0 +1,104 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin.js';
import Creatures, { CreatureSchema } from '/imports/api/creature/creatures/Creatures.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import defaultCharacterProperties from '/imports/api/creature/creatures/defaultCharacterProperties.js';
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
import assertHasCharactersSlots from '/imports/api/creature/creatures/methods/assertHasCharacterSlots.js';
import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js';
import getCreatureLibraryIds from '/imports/api/library/getCreatureLibraryIds.js';
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
import { insertExperienceForCreature } from '/imports/api/creature/experience/Experiences.js';
import SimpleSchema from 'simpl-schema';
const insertCreature = new ValidatedMethod({
name: 'creatures.insertCreature',
mixins: [RateLimiterMixin, simpleSchemaMixin],
schema: CreatureSchema.pick(
'name',
'gender',
'alignment',
'allowedLibraries',
'allowedLibraryCollections',
).extend({
'startingLevel': {
type: SimpleSchema.Integer,
min: 0,
},
}),
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({ name, gender, alignment, startingLevel,
allowedLibraries, allowedLibraryCollections }) {
const userId = this.userId
if (!userId) {
throw new Meteor.Error('Creatures.methods.insert.denied',
'You need to be logged in to insert a creature');
}
assertHasCharactersSlots(userId);
// Create the creature document
let creatureId = Creatures.insert({
owner: userId,
name,
gender,
alignment,
allowedLibraries,
allowedLibraryCollections,
});
// Insert experience to get character to starting level
if (startingLevel) {
insertExperienceForCreature({
experience: {
name: 'Starting level',
levels: startingLevel,
creatureId
},
creatureId,
userId,
});
}
// Insert the default properties
// Not batchInsert because we want the properties cleaned by the schema
let baseId, rulesetSlot;
defaultCharacterProperties(creatureId).forEach(prop => {
let id = CreatureProperties.insert(prop);
if (prop.name === 'Ruleset'){
baseId = id;
rulesetSlot = prop;
}
});
// If the user only has a single ruleset subscribed, use it by default
if (Meteor.isServer) {
insertDefaultRuleset(creatureId, baseId, userId, rulesetSlot);
}
return creatureId;
},
});
// If the user only has a single ruleset subscribed, insert it by default
function insertDefaultRuleset(creatureId, baseId, userId, slot) {
const libraryIds = getCreatureLibraryIds(creatureId, userId);
const filter = getSlotFillFilter({ slot, libraryIds });
const fillCursor = LibraryNodes.find(filter, { fields: { _id: 1 } });
const numRulesets = fillCursor.count();
if (numRulesets === 1) {
const ruleset = fillCursor.fetch()[0]
insertPropertyFromLibraryNode.call({
nodeIds: [ruleset._id],
parentRef: {id: baseId, collection: 'creatureProperties'},
order: 0.5,
});
}
}
export default insertCreature;

View File

@@ -0,0 +1,43 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions.js';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
import Experiences from '/imports/api/creature/experience/Experiences.js';
function removeRelatedDocuments(creatureId){
CreatureVariables.remove({_creatureId: creatureId});
CreatureProperties.remove({'ancestors.id': creatureId});
CreatureLogs.remove({creatureId});
Experiences.remove({creatureId});
}
const removeCreature = new ValidatedMethod({
name: 'Creatures.methods.removeCreature', // DDP method name
validate: new SimpleSchema({
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({charId}) {
assertOwnership(charId, this.userId)
this.unblock();
removeCreatureWork(charId)
},
});
export function removeCreatureWork(creatureId){
Creatures.remove(creatureId);
removeRelatedDocuments(creatureId);
}
export default removeCreature;

View File

@@ -0,0 +1,144 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
import { union } from 'lodash';
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers.js';
const restCreature = new ValidatedMethod({
name: 'creature.methods.rest',
validate: new SimpleSchema({
creatureId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
restType: {
type: String,
allowedValues: ['shortRest', 'longRest'],
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({ creatureId, restType }) {
// Get action context
const actionContext = new ActionContext(creatureId, [creatureId], this);
// Check permissions
assertEditPermission(actionContext.creature, this.userId);
// Join, sort, and apply before triggers
const beforeTriggers = union(
actionContext.triggers.anyRest?.before, actionContext.triggers[restType]?.before
).sort((a, b) => a.order - b.order);
applyTriggers(beforeTriggers, null, actionContext);
// Rest
actionContext.addLog({
name: restType === 'shortRest' ? 'Short rest' : 'Long rest',
});
doRestWork(restType, actionContext);
// Join, sort, and apply after triggers
const afterTriggers = union(
actionContext.triggers.anyRest?.after, actionContext.triggers[restType]?.after
).sort((a, b) => a.order - b.order);
applyTriggers(afterTriggers, null, actionContext);
// Insert log
actionContext.writeLog();
},
});
function doRestWork(restType, actionContext) {
const creatureId = actionContext.creature._id;
// Long rests reset short rest properties as well
let resetFilter;
if (restType === 'shortRest'){
resetFilter = 'shortRest'
} else {
resetFilter = {$in: ['shortRest', 'longRest']}
}
// Only apply to active properties
let filter = {
'ancestors.id': creatureId,
reset: resetFilter,
removed: { $ne: true },
inactive: { $ne: true },
};
// update all attribute's damage
filter.type = 'attribute';
CreatureProperties.update(filter, {
$set: {
damage: 0,
dirty: true,
}
}, {
selector: {type: 'attribute'},
multi: true,
});
// Update all action-like properties' usesUsed
filter.type = {$in: [
'action',
'attack',
'spell'
]};
CreatureProperties.update(filter, {
$set: {
usesUsed: 0,
dirty: true,
}
}, {
selector: {type: 'action'},
multi: true,
});
// Reset half hit dice on a long rest, starting with the highest dice
if (restType === 'longRest'){
let hitDice = CreatureProperties.find({
'ancestors.id': creatureId,
type: 'attribute',
attributeType: 'hitDice',
removed: {$ne: true},
inactive: {$ne: true},
}, {
fields: {
hitDiceSize: 1,
damage: 1,
total: 1,
}
}).fetch();
// Use a collator to do sorting in natural order
let collator = new Intl.Collator('en', {
numeric: true, sensitivity: 'base'
});
// Get the hit dice in decending order of hitDiceSize
let compare = (a, b) => collator.compare(b.hitDiceSize, a.hitDiceSize)
hitDice.sort(compare);
// Get the total number of hit dice that can be recovered this rest
let totalHd = hitDice.reduce((sum, hd) => sum + (hd.total || 0), 0);
let resetMultiplier = actionContext.creature.settings.hitDiceResetMultiplier || 0.5;
let recoverableHd = Math.max(Math.floor(totalHd*resetMultiplier), 1);
// recover each hit dice in turn until the recoverable amount is used up
let amountToRecover, resultingDamage;
hitDice.forEach(hd => {
if (!recoverableHd) return;
amountToRecover = Math.min(recoverableHd, hd.damage || 0);
if (!amountToRecover) return;
recoverableHd -= amountToRecover;
resultingDamage = hd.damage - amountToRecover;
CreatureProperties.update(hd._id, {
$set: {
damage: resultingDamage,
dirty: true,
}
}, {
selector: {type: 'attribute'},
});
});
}
}
export default restCreature;

View File

@@ -0,0 +1,45 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import {assertEditPermission} from '/imports/api/sharing/sharingPermissions.js';
const updateCreature = new ValidatedMethod({
name: 'creatures.update',
validate({_id, path}){
if (!_id) return false;
// Allowed fields
let allowedFields = [
'name',
'alignment',
'gender',
'picture',
'avatarPicture',
'color',
'settings',
];
if (!allowedFields.includes(path[0])){
throw new Meteor.Error('Creatures.methods.update.denied',
'This field can\'t be updated using this method');
}
},
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({_id, path, value}) {
let creature = Creatures.findOne(_id);
assertEditPermission(creature, this.userId);
if (value === undefined || value === null){
Creatures.update(_id, {
$unset: {[path.join('.')]: 1},
});
} else {
Creatures.update(_id, {
$set: {[path.join('.')]: value},
});
}
},
});
export default updateCreature;

View File

@@ -0,0 +1,184 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
let Experiences = new Mongo.Collection('experiences');
let ExperienceSchema = new SimpleSchema({
name: {
type: String,
optional: true,
max: STORAGE_LIMITS.name,
},
// The amount of XP this experience gives
xp: {
type: SimpleSchema.Integer,
optional: true,
min: 0,
},
// Setting levels instead of value grants whole levels
levels: {
type: SimpleSchema.Integer,
optional: true,
min: 0,
index: 1,
},
// The real-world date that it occured, usually sorted by date
date: {
type: Date,
autoValue: function() {
// If the date isn't set, set it to now
if (!this.isSet) {
return new Date();
}
},
index: 1,
},
creatureId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
index: 1,
},
});
Experiences.attachSchema(ExperienceSchema);
const insertExperienceForCreature = function({experience, creatureId, userId}){
if (experience.xp){
Creatures.update(creatureId, {
$inc: { 'denormalizedStats.xp': experience.xp },
$set: { dirty: true },
});
}
if (experience.levels) {
Creatures.update(creatureId, {
$inc: { 'denormalizedStats.milestoneLevels': experience.levels },
$set: { dirty: true },
});
}
experience.creatureId = creatureId;
let id = Experiences.insert(experience);
return id;
};
const insertExperience = new ValidatedMethod({
name: 'experiences.insert',
validate: new SimpleSchema({
experience: {
type: ExperienceSchema.omit('creatureId'),
},
creatureIds: {
type: Array,
max: 12,
},
'creatureIds.$': {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({experience, creatureIds}) {
let userId = this.userId;
if (!userId) {
throw new Meteor.Error('Experiences.methods.insert.denied',
'You need to be logged in to insert an experience');
}
let insertedIds = [];
creatureIds.forEach(creatureId => {
assertEditPermission(creatureId, userId);
let id = insertExperienceForCreature({experience, creatureId, userId});
insertedIds.push(id);
});
return insertedIds;
},
});
const removeExperience = new ValidatedMethod({
name: 'experiences.remove',
validate: new SimpleSchema({
experienceId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({experienceId}) {
let userId = this.userId;
if (!userId) {
throw new Meteor.Error('Experiences.methods.remove.denied',
'You need to be logged in to remove an experience');
}
let experience = Experiences.findOne(experienceId);
if (!experience) return;
let creatureId = experience.creatureId
assertEditPermission(creatureId, userId);
if (experience.xp){
Creatures.update(creatureId, {
$inc: { 'denormalizedStats.xp': -experience.xp },
$set: { dirty: true },
});
}
if (experience.levels) {
Creatures.update(creatureId, {
$inc: { 'denormalizedStats.milestoneLevels': -experience.levels },
$set: { dirty: true },
});
}
experience.creatureId = creatureId;
let numRemoved = Experiences.remove(experienceId);
return numRemoved;
},
});
const recomputeExperiences = new ValidatedMethod({
name: 'experiences.recompute',
validate: new SimpleSchema({
creatureId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({creatureId}) {
let userId = this.userId;
if (!userId) {
throw new Meteor.Error('Experiences.methods.recompute.denied',
'You need to be logged in to recompute a creature\'s experiences');
}
assertEditPermission(creatureId, userId);
let xp = 0;
let milestoneLevels = 0;
Experiences.find({
creatureId
}, {
fields: {xp: 1, levels: 1}
}).forEach(experience => {
xp += experience.xp || 0;
milestoneLevels += experience.levels || 0;
});
Creatures.update(creatureId, {$set: {
'denormalizedStats.xp': xp,
'denormalizedStats.milestoneLevels': milestoneLevels,
dirty: true,
}});
},
});
export default Experiences;
export { ExperienceSchema, insertExperience, insertExperienceForCreature, removeExperience, recomputeExperiences };

View File

@@ -0,0 +1,50 @@
import SimpleSchema from 'simpl-schema';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
let ExperienceSchema = new SimpleSchema({
title: {
type: String,
optional: true,
max: STORAGE_LIMITS.name,
},
// Potentially long description of the event
description: {
type: String,
optional: true,
max: STORAGE_LIMITS.description,
},
// The real-world date that it occured
date: {
type: Date,
autoValue: function() {
// If the date isn't set, set it to now
if (!this.isSet) {
return new Date();
}
},
},
// The date in-world of this event
worldDate: {
type: String,
optional: true,
max: STORAGE_LIMITS.name,
},
// Tags to better find this entry later
tags: {
type: Array,
defaultValue: [],
maxCount: STORAGE_LIMITS.tagCount,
},
'tags.$': {
type: String,
max: STORAGE_LIMITS.tagLength,
},
// ID of the journal this entry belongs to
journalId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
index: 1,
}
});
export { ExperienceSchema };

View File

@@ -0,0 +1,214 @@
import SimpleSchema from 'simpl-schema';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables.js';
import LogContentSchema from '/imports/api/creature/log/LogContentSchema.js';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import {assertEditPermission} from '/imports/api/creature/creatures/creaturePermissions.js';
import {parse, prettifyParseError} from '/imports/parser/parser.js';
import resolve, { toString } from '/imports/parser/resolve.js';
const PER_CREATURE_LOG_LIMIT = 100;
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
if (Meteor.isServer){
var sendWebhookAsCreature = require('/imports/server/discord/sendWebhook.js').sendWebhookAsCreature;
}
let CreatureLogs = new Mongo.Collection('creatureLogs');
let CreatureLogSchema = new SimpleSchema({
content: {
type: Array,
defaultValue: [],
maxCount: STORAGE_LIMITS.logContentCount,
},
'content.$': {
type: LogContentSchema,
},
// The real-world date that it occured, usually sorted by date
date: {
type: Date,
autoValue: function() {
// If the date isn't set, set it to now
if (!this.isSet) {
return new Date();
}
},
index: 1,
},
creatureId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
index: 1,
},
creatureName: {
type: String,
optional: true,
max: STORAGE_LIMITS.name,
},
});
CreatureLogs.attachSchema(CreatureLogSchema);
function removeOldLogs(creatureId){
// Find the first log that is over the limit
let firstExpiredLog = CreatureLogs.find({
creatureId
}, {
sort: {date: -1},
skip: PER_CREATURE_LOG_LIMIT,
});
if (!firstExpiredLog) return;
// Remove all logs older than the one over the limit
CreatureLogs.remove({
creatureId,
date: {$lte: firstExpiredLog.date},
});
}
function logToMessageData(log){
let embed = {
fields: [],
};
log.content.forEach(field => {
if (!field.name) field.name = '\u200b';
if (!field.value) field.value = '\u200b';
embed.fields.push(field);
});
return { embeds: [embed] };
}
function logWebhook({log, creature}){
if (Meteor.isServer){
sendWebhookAsCreature({
creature,
data: logToMessageData(log),
});
}
}
const insertCreatureLog = new ValidatedMethod({
name: 'creatureLogs.methods.insert',
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
validate: new SimpleSchema({
log: CreatureLogSchema.omit('date'),
}).validator(),
run({log}){
const creatureId = log.creatureId;
const creature = Creatures.findOne(creatureId, {fields: {
readers: 1,
writers: 1,
owner: 1,
'settings.discordWebhook': 1,
name: 1,
avatarPicture: 1,
}});
assertEditPermission(creature, this.userId);
// Build the new log
let id = insertCreatureLogWork({log, creature, method: this})
return id;
},
});
export function insertCreatureLogWork({log, creature, method}){
// Build the new log
if (typeof log === 'string'){
log = {content: [{value: log}]};
}
if (!log.content?.length) return;
log.date = new Date();
// Insert it
let id = CreatureLogs.insert(log);
if (Meteor.isServer){
method?.unblock();
removeOldLogs(creature._id);
logWebhook({log, creature});
}
return id;
}
function equalIgnoringWhitespace(a, b){
if (typeof a !== 'string' || typeof b !== 'string') return a === b;
return a.replace(/\s/g,'') === b.replace(/\s/g, '');
}
const logRoll = new ValidatedMethod({
name: 'creatureLogs.methods.logForCreature',
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
validate: new SimpleSchema({
roll: {
type: String,
},
creatureId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
}).validator(),
run({roll, creatureId}){
const creature = Creatures.findOne(creatureId, {fields: {
readers: 1,
writers: 1,
owner: 1,
'settings.discordWebhook': 1,
name: 1,
avatarPicture: 1,
}});
assertEditPermission(creature, this.userId);
const variables = CreatureVariables.findOne({ _creatureId: creatureId });
let logContent = []
let parsedResult = undefined;
try {
parsedResult = parse(roll);
} catch (e){
let error = prettifyParseError(e);
logContent.push({name: 'Parse Error', value: error});
}
if (parsedResult) try {
let {
result: compiled,
context
} = resolve('compile', parsedResult, variables);
const compiledString = toString(compiled);
if (!equalIgnoringWhitespace(compiledString, roll)) logContent.push({
value: roll
});
logContent.push({
value: compiledString
});
let {result: rolled} = resolve('roll', compiled, variables, context);
let rolledString = toString(rolled);
if (rolledString !== compiledString) logContent.push({
value: rolledString
});
let {result} = resolve('reduce', rolled, variables, context);
let resultString = toString(result);
if (resultString !== rolledString) logContent.push({
value: resultString
});
} catch (e){
console.error(e);
logContent = [{name: 'Calculation error'}];
}
const log = {
content: logContent,
creatureId,
date: new Date(),
};
let id = insertCreatureLogWork({log, creature, method: this});
return id;
},
});
export default CreatureLogs;
export { CreatureLogSchema, insertCreatureLog, logRoll, PER_CREATURE_LOG_LIMIT};

View File

@@ -0,0 +1,51 @@
import SimpleSchema from 'simpl-schema';
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
import RollDetailsSchema from '/imports/api/properties/subSchemas/RollDetailsSchema.js';
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
let LogContentSchema = new SimpleSchema({
// The name of the field, included in discord webhook message
name: {
type: String,
optional: true,
max: STORAGE_LIMITS.name,
},
// The details of the field, included in discord webhook message
// Markdown support
value: {
type: String,
optional: true,
max: STORAGE_LIMITS.summary,
},
// Inline with other content fields
inline: {
type: Boolean,
optional: true,
},
context: {
type: Object,
optional: true,
},
'context.errors':{
type: Array,
defaultValue: [],
maxCount: STORAGE_LIMITS.errorCount,
},
'context.errors.$': {
type: ErrorSchema,
},
'context.rolls': {
type: Array,
defaultValue: [],
maxCount: STORAGE_LIMITS.rollCount,
},
'context.rolls.$': {
type: RollDetailsSchema,
},
'context.doubleRolls': {
type: Boolean,
optional: true,
},
});
export default LogContentSchema;

View File

@@ -0,0 +1,52 @@
import {
assertEditPermission,
assertViewPermission,
assertOwnership,
} from '/imports/api/creature/creatures/creaturePermissions.js';
// Checks if the method has permission to run on the document. If the document
// has a charId, that creature is checked, otherwise if it has an _id and the
// collection is defined in the method options, that document is fetched to
// determine its charId, otherwise a getCharId method can be defined to perform
// a special search for the required creature
//
// Because this mixin injects the charId into argument objects that don't
// already contain it, it should always come last in the mixin list, so that it
// the outermost wrapper of the run function
export default function creaturePermissionMixin(methodOptions){
let assertPermission;
if (methodOptions.permission === 'owner'){
assertPermission = assertOwnership;
} else if (methodOptions.permission === 'edit'){
assertPermission = assertEditPermission;
} else if (methodOptions.permission === 'view'){
assertPermission = assertViewPermission;
} else {
throw "`permission` missing in method options";
}
let getCharId;
if (methodOptions.getCharId){
getCharId = methodOptions.getCharId;
} else if (methodOptions.collection) {
getCharId = function({_id}){
return methodOptions.collection.findOne(_id, {
fields: {charId: 1}
}).charId;
};
} else {
getCharId = function(){
throw "`getCharId` or `collection` missing in method options," +
" or {charId} missing in call";
};
}
let runFunc = methodOptions.run;
methodOptions.run = function(doc, ...rest){
// Store the charId on the doc for other mixins if it had to be fetched
doc.charId = doc.charId || getCharId.apply(this, arguments);
assertPermission(doc.charId, this.userId);
return runFunc.call(this, doc, ...rest);
};
return methodOptions;
}

View File

@@ -0,0 +1,59 @@
import {
updateChildren,
updateDescendants,
} from '/imports/api/parenting/parenting.js';
import { inheritedFields } from '/imports/api/parenting/ChildSchema.js';
import MONGO_OPERATORS from '/imports/constants/MONGO_OPERATORS.js';
// This mixin can be safely applied to all update methods which are validated
// with the updateSchemaMixin. It will propagate updates to fields which
// are inherited and normalised on the parent or ancestor docs
// It should have neglible performance impact for updates that aren't inherited
function propagateInheritanceUpdate({_id, update}){
let childModifier = {};
let descendantModifier = {};
// For each operator
for (let operator of MONGO_OPERATORS){
// If the operator is in the update, for each field
if (update[operator]) for (let field in update[operator]){
// Get the top level field that was actually modified
const indexOfDot = field.indexOf('.');
let modifiedField;
if (indexOfDot !== -1) {
modifiedField = field.substring(0, indexOfDot);
} else {
modifiedField = field;
}
// If that field is updated and inherited
if (inheritedFields.has(modifiedField)){
// Perform the same update on the descendants
if (!childModifier[operator]) childModifier[operator] = {};
if (!descendantModifier[operator]) descendantModifier[operator] = {};
childModifier[operator][`parent.${field}`] = update[operator][field];
descendantModifier[operator][`ancestors.$.${field}`] = update[operator][field];
}
}
}
// Update the parent object of its children
updateChildren({
parentId: _id,
modifier: childModifier,
});
// Update the ancestors object of its descendants
updateDescendants({
ancestorId: _id,
modifier: descendantModifier,
});
}
export default function propagateInheritanceUpdateMixin(methodOptions){
let runFunc = methodOptions.run;
methodOptions.run = function({_id, update}){
const result = runFunc.apply(this, arguments);
propagateInheritanceUpdate({_id, update});
return result;
};
return methodOptions;
}

View File

@@ -0,0 +1,17 @@
import computeCreature from '/imports/api/engine/computeCreature.js';
export default function recomputeCreatureMixin(methodOptions){
let runFunc = methodOptions.run;
methodOptions.run = function({charId}){
const result = runFunc.apply(this, arguments);
if (
methodOptions.skipRecompute &&
methodOptions.skipRecompute.apply(this, arguments)
) {
return result;
}
computeCreature(charId);
return result;
};
return methodOptions;
}

View File

@@ -0,0 +1,27 @@
import SimpleSchema from 'simpl-schema';
import { setDocToLastOrder } from '/imports/api/parenting/order.js';
export function setDocToLastMixin(methodOptions){
// Make sure the doc has a charId
// This mixin should come before simpleSchemaMixin so that it can extend the
// schema before it is turned into a validate function
if (methodOptions.validate){
throw new Meteor.Error(`setDocToLastMixin should come before simpleSchemaMixin`);
}
methodOptions.schema = new SimpleSchema({
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
}).extend(methodOptions.schema);
let collection = methodOptions.collection;
if (!collection){
throw new Meteor.Error("`collection` required in method options for setDocToLastMixin");
}
let runFunc = methodOptions.run;
methodOptions.run = function(doc){
setDocToLastOrder({collection, doc});
return runFunc.apply(this, arguments);
};
return methodOptions;
}

View File

@@ -0,0 +1,51 @@
// Copied from https://github.com/sethjgore/meteor-simple-schema-mixin
// and updated to simpl-schema npm package
import SimpleSchema from 'simpl-schema';
export default function simpleSchemaMixin(methodOptions) {
// If the user didn't give us a schema and they did give us a validate, assume
// that they are choosing to use the validate way of doing things in this call.
// If they've built a wrapper around ValidateMethod that includes this mixin
// all the time, this could happen semi-"intentionally". There may be times they
// just don't want to use a schema and have specified a "validate" option. So
// returning the unchanged options instead of an error seems proper.
if ((typeof methodOptions.schema === 'undefined'
&& typeof methodOptions.validate !== 'undefined')
|| (typeof methodOptions.schema !== 'undefined' && methodOptions.schema === null
&& typeof methodOptions.validate !== 'undefined' && methodOptions.validate !== null)) {
return methodOptions;
}
// If they truly gave us both... that just doesn't seem proper.
if (methodOptions.validate && methodOptions.validate !== null) {
throw new Meteor.Error(
'simpleSchemaMixin.options',
'"schema" and "validate" options cannot be used together');
}
// Note that setting them both null will make it through, defaulting to the
// schema = null behavior (enforce no args) instead of the validate = null
// behavior (do no validation).
const newOptions = methodOptions;
newOptions.schemaValidatorOptions =
newOptions.schemaValidatorOptions ||
{ clean: true, filter: false };
let simpleSchema;
if (!newOptions.schema || newOptions.schema === null) {
// Allow simply leaving off both the schema and validate specifications
// or setting them to "null" as a shorthand. In this case, unlike
// the straight default validate or typical coder's call to validator,
// we will ENFORCE the Method be called without parameters because of
// the "filter: false" above.
simpleSchema = new SimpleSchema({});
} else if (newOptions.schema instanceof SimpleSchema) {
// In this one case, we can save ourselves the time to make a schema out
// of the schema.
simpleSchema = newOptions.schema;
} else {
simpleSchema = new SimpleSchema(newOptions.schema);
}
newOptions.validate = simpleSchema.validator(newOptions.schemaValidatorOptions);
return newOptions;
};

View File

@@ -0,0 +1,67 @@
const argumentSchema = new SimpleSchema({
_id: SimpleSchema.RegEx.Id,
update: {
type: Object,
blackbox: true,
},
});
// Modified simpleSchemaMixin
import SimpleSchema from 'simpl-schema';
export default function updateSchemaMixin(methodOptions) {
// If the user didn't give us a schema and they did give us a validate, assume
// that they are choosing to use the validate way of doing things in this call.
if ((
typeof methodOptions.schema === 'undefined' &&
typeof methodOptions.validate !== 'undefined'
) || (
typeof methodOptions.schema !== 'undefined' &&
methodOptions.schema === null &&
typeof methodOptions.validate !== 'undefined' &&
methodOptions.validate !== null
)) {
return methodOptions;
}
// If they truly gave us both... that just doesn't seem proper.
if (methodOptions.validate && methodOptions.validate !== null) {
throw new Meteor.Error(
'simpleSchemaMixin.options',
'"schema" and "validate" options cannot be used together');
}
// Note that setting them both null will make it through, defaulting to the
// schema = null behavior (enforce no args) instead of the validate = null
// behavior (do no validation).
// Apply default validator options if none are provided
methodOptions.schemaValidatorOptions =
methodOptions.schemaValidatorOptions ||
{ clean: true, modifier: true };
// Make the update schema a SimpleSchema, if it isn't already
let updateSchema;
if (methodOptions.schema instanceof SimpleSchema) {
updateSchema = methodOptions.schema;
} else {
updateSchema = new SimpleSchema(methodOptions.schema);
}
// Set up the new validation
methodOptions.validate = function(args){
argumentSchema.validate(args);
updateSchema.validate(
{$set: args.update},
methodOptions.schemaValidatorOptions
);
};
// Give a default run function if one isn't supplied
if (!methodOptions.run){
methodOptions.run = function({_id, update}){
return methodOptions.collection.update(_id, {$set: update});
};
}
return methodOptions;
}

View File

@@ -0,0 +1,78 @@
import { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/log/CreatureLogs.js';
import {
getCreature, getVariables, getPropertiesOfType
} from '/imports/api/engine/loadCreatures.js';
import { groupBy, remove } from 'lodash';
export default class ActionContext{
constructor(creatureId, targetIds = [], method) {
// Get the creature
this.creature = getCreature(creatureId)
if (!this.creature) {
throw new Meteor.Error('No Creature', `No creature could be found with id: ${creatureId}`)
}
// Create a log
this.log = CreatureLogSchema.clean({
creatureId: creatureId,
creatureName: this.creature.name,
});
// Get the variables of the acting creature
this.creature.variables = getVariables(creatureId);
delete this.creature.variables._id;
delete this.creature.variables._creatureId;
// Alias as scope
this.scope = this.creature.variables;
// Get the targets and their variables
this.targets = [];
targetIds.forEach(targetId => {
let target;
if (targetId === creatureId) {
target = this.creature;
} else {
target = getCreature(targetId);
target.variables = getVariables(targetId);
delete target.variables._id;
delete target.variables._creatureId;
}
this.targets.push(target);
});
// Store a reference to the method for inserting the log
this.method = method;
// Get triggers
this.triggers = getPropertiesOfType(creatureId, 'trigger');
// Remove deleted or inactive triggers
remove(this.triggers, trigger => trigger.removed || trigger.inactive);
// Sort triggers by order
this.triggers.sort((a, b) => a.order - b.order);
// Group the triggers into triggers.<event>.<timing> or
// triggers.doActionProperty.<propertyType>.<timing>
this.triggers = groupBy(this.triggers, 'event');
for (let event in this.triggers) {
if (event === 'doActionProperty') {
this.triggers[event] = groupBy(this.triggers[event], 'actionPropertyType');
for (let propertyType in this.triggers[event]) {
this.triggers[event][propertyType] = groupBy(this.triggers[event][propertyType], 'timing');
}
} else {
this.triggers[event] = groupBy(this.triggers[event], 'timing');
}
}
}
addLog(content) {
if (content.name || content.value){
this.log.content.push(content);
}
}
writeLog() {
insertCreatureLogWork({
log: this.log,
creature: this.creature,
method: this.method,
});
}
}

View File

@@ -0,0 +1,27 @@
import action from './applyPropertyByType/applyAction.js';
import adjustment from './applyPropertyByType/applyAdjustment.js';
import branch from './applyPropertyByType/applyBranch.js';
import buff from './applyPropertyByType/applyBuff.js';
import damage from './applyPropertyByType/applyDamage.js';
import note from './applyPropertyByType/applyNote.js';
import roll from './applyPropertyByType/applyRoll.js';
import savingThrow from './applyPropertyByType/applySavingThrow.js';
import toggle from './applyPropertyByType/applyToggle.js';
const applyPropertyByType = {
action,
adjustment,
branch,
buff,
damage,
note,
roll,
savingThrow,
spell: action,
toggle,
};
export default function applyProperty(node, actionContext, ...rest) {
actionContext.scope[`#${node.node.type}`] = node.node;
applyPropertyByType[node.node.type]?.(node, actionContext, ...rest);
}

View File

@@ -0,0 +1,301 @@
import recalculateInlineCalculations from './shared/recalculateInlineCalculations.js';
import recalculateCalculation from './shared/recalculateCalculation.js';
import rollDice from '/imports/parser/rollDice.js';
import applyProperty from '../applyProperty.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { adjustQuantityWork } from '/imports/api/creature/creatureProperties/methods/adjustQuantity.js';
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
export default function applyAction(node, actionContext) {
applyNodeTriggers(node, 'before', actionContext);
const prop = node.node;
let targets = actionContext.targets;
if (prop.target === 'self') targets = [actionContext.creature];
// Log the name and summary
let content = { name: prop.name };
if (prop.summary?.text){
recalculateInlineCalculations(prop.summary, actionContext);
content.value = prop.summary.value;
}
actionContext.addLog(content);
// Spend the resources
const failed = spendResources(prop, actionContext);
if (failed) return;
const attack = prop.attackRoll || prop.attackRollBonus;
// Attack if there is an attack roll
if (attack && attack.calculation){
if (targets.length){
targets.forEach(target => {
applyAttackToTarget({attack, target, actionContext});
// Apply the children, but only to the current target
actionContext.targets = [target];
applyChildren(node, actionContext);
});
} else {
applyAttackWithoutTarget({attack, actionContext});
applyChildren(node, actionContext);
}
} else {
applyChildren(node, actionContext);
}
}
function applyAttackWithoutTarget({attack, actionContext}){
delete actionContext.scope['$attackHit'];
delete actionContext.scope['$attackMiss'];
delete actionContext.scope['$criticalHit'];
delete actionContext.scope['$criticalMiss'];
delete actionContext.scope['$attackRoll'];
recalculateCalculation(attack, actionContext);
const scope = actionContext.scope;
let {
resultPrefix,
result,
criticalHit,
criticalMiss,
} = rollAttack(attack, scope);
let name = criticalHit ? 'Critical Hit!' : criticalMiss ? 'Critical Miss!' : 'To Hit';
if (scope['$attackAdvantage'] === 1){
name += ' (Advantage)';
} else if(scope['$attackAdvantage'] === -1){
name += ' (Disadvantage)';
}
if (!criticalMiss){
scope['$attackHit'] = {value: true}
}
if (!criticalHit){
scope['$attackMiss'] = {value: true};
}
actionContext.addLog({
name,
value: `${resultPrefix}\n**${result}**`,
inline: true,
});
}
function applyAttackToTarget({attack, target, actionContext}){
const scope = actionContext.scope;
delete scope['$attackHit'];
delete scope['$attackMiss'];
delete scope['$criticalHit'];
delete scope['$criticalMiss'];
delete scope['$attackDiceRoll'];
delete scope['$attackRoll'];
recalculateCalculation(attack, actionContext);
let {
resultPrefix,
result,
criticalHit,
criticalMiss,
} = rollAttack(attack, scope);
if (target.variables.armor){
const armor = target.variables.armor.value;
let name = criticalHit ? 'Critical Hit!' :
criticalMiss ? 'Critical Miss!' :
result > armor ? 'Hit!' : 'Miss!';
if (scope['$attackAdvantage'] === 1){
name += ' (Advantage)';
} else if(scope['$attackAdvantage'] === -1){
name += ' (Disadvantage)';
}
actionContext.addLog({
name,
value: `${resultPrefix}\n**${result}**`,
inline: true,
});
if (criticalMiss || result < armor){
scope['$attackMiss'] = {value: true};
} else {
scope['$attackHit'] = {value: true};
}
} else {
actionContext.addLog({
name: 'Error',
value:'Target has no `armor`',
});
actionContext.addLog({
name: criticalHit ? 'Critical Hit!' : criticalMiss ? 'Critical Miss!' : 'To Hit',
value: `${resultPrefix}\n**${result}**`,
inline: true,
});
}
}
function rollAttack(attack, scope){
const rollModifierText = numberToSignedString(attack.value, true);
let value, resultPrefix;
if (scope['$attackAdvantage'] === 1){
const [a, b] = rollDice(2, 20);
if (a >= b) {
value = a;
resultPrefix = `1d20 [ ${a}, ~~${b}~~ ] ${rollModifierText}`;
} else {
value = b;
resultPrefix = `1d20 [ ~~${a}~~, ${b} ] ${rollModifierText}`;
}
} else if (scope['$attackAdvantage'] === -1){
const [a, b] = rollDice(2, 20);
if (a <= b) {
value = a;
resultPrefix = `1d20 [ ${a}, ~~${b}~~ ] ${rollModifierText}`;
} else {
value = b;
resultPrefix = `1d20 [ ~~${a}~~, ${b} ] ${rollModifierText}`;
}
} else {
value = rollDice(1, 20)[0];
resultPrefix = `1d20 [${value}] ${rollModifierText}`
}
scope['$attackRoll'] = {value};
const result = value + attack.value;
const {criticalHit, criticalMiss} = applyCrits(value, scope);
return {resultPrefix, result, value, criticalHit, criticalMiss};
}
function applyCrits(value, scope){
let criticalHitTarget = scope.criticalHitTarget?.value || 20;
let criticalHit = value >= criticalHitTarget;
let criticalMiss;
if (criticalHit){
scope['$criticalHit'] = {value: true};
} else {
criticalMiss = value === 1;
if (criticalMiss){
scope['$criticalMiss'] = {value: true};
}
}
return {criticalHit, criticalMiss};
}
function applyChildren(node, actionContext) {
applyNodeTriggers(node, 'after', actionContext);
node.children.forEach(child => applyProperty(child, actionContext));
}
function spendResources(prop, actionContext){
// Check Uses
if (prop.usesLeft <= 0){
actionContext.addLog({
name: 'Error',
value: `${prop.name || 'action'} does not have enough uses left`,
});
return true;
}
// Resources
if (prop.insufficientResources){
actionContext.addLog({
name: 'Error',
value: 'This creature doesn\'t have sufficient resources to perform this action',
});
return true;
}
// Items
let itemQuantityAdjustments = [];
let spendLog = [];
let gainLog = [];
try {
prop.resources.itemsConsumed.forEach(itemConsumed => {
recalculateCalculation(itemConsumed.quantity, actionContext);
if (!itemConsumed.itemId){
throw 'No ammo was selected for this prop';
}
let item = CreatureProperties.findOne(itemConsumed.itemId);
if (!item || item.ancestors[0].id !== prop.ancestors[0].id){
throw 'The prop\'s ammo was not found on the creature';
}
if (!item.equipped){
throw 'The selected ammo is not equipped';
}
if (
!itemConsumed.quantity.value ||
!isFinite(itemConsumed.quantity.value)
) return;
itemQuantityAdjustments.push({
property: item,
operation: 'increment',
value: itemConsumed.quantity.value,
});
let logName = item.name;
if (itemConsumed.quantity.value > 1 || itemConsumed.quantity.value < -1){
logName = item.plural || logName;
}
if (itemConsumed.quantity.value > 0){
spendLog.push(logName + ': ' + itemConsumed.quantity.value);
} else if (itemConsumed.quantity.value < 0){
gainLog.push(logName + ': ' + -itemConsumed.quantity.value);
}
});
} catch (e){
actionContext.addLog({
name: 'Error',
value: e,
});
return true;
}
// No more errors should be thrown after this line
// Now that we have confirmed that there are no errors, do actual work
//Items
itemQuantityAdjustments.forEach(adjustQuantityWork);
// Use uses
if (prop.usesLeft){
CreatureProperties.update(prop._id, {
$inc: {usesUsed: 1}
}, {
selector: prop
});
actionContext.addLog({
name: 'Uses left',
value: prop.usesLeft - 1,
inline: true,
});
}
// Damage stats
prop.resources.attributesConsumed.forEach(attConsumed => {
recalculateCalculation(attConsumed.quantity, actionContext);
if (!attConsumed.quantity?.value) return;
let stat = actionContext.scope[attConsumed.variableName];
if (!stat){
spendLog.push(stat.name + ': ' + ' not found');
return;
}
damagePropertyWork({
prop: stat,
operation: 'increment',
value: attConsumed.quantity.value,
actionContext,
});
if (attConsumed.quantity.value > 0){
spendLog.push(stat.name + ': ' + attConsumed.quantity.value);
} else if (attConsumed.quantity.value < 0){
gainLog.push(stat.name + ': ' + -attConsumed.quantity.value);
}
});
// Log all the spending
if (gainLog.length) actionContext.addLog({
name: 'Gained',
value: gainLog.join('\n'),
inline: true,
});
if (spendLog.length) actionContext.addLog({
name: 'Spent',
value: spendLog.join('\n'),
inline: true,
});
}

View File

@@ -0,0 +1,61 @@
import applyProperty from '../applyProperty.js';
import recalculateCalculation from './shared/recalculateCalculation.js';
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
export default function applyAdjustment(node, actionContext){
applyNodeTriggers(node, 'before', actionContext);
const prop = node.node;
const damageTargets = prop.target === 'self' ? [actionContext.creature] : actionContext.targets;
if (!prop.amount) {
return applyChildren(node, actionContext);
}
// Evaluate the amount
recalculateCalculation(prop.amount, actionContext);
const value = +prop.amount.value;
if (!isFinite(value)) {
return applyChildren(node, actionContext);
}
if (damageTargets?.length) {
damageTargets.forEach(target => {
let stat = target.variables[prop.stat];
if (!stat?.type) {
actionContext.addLog({
name: 'Error',
value: `Could not apply attribute damage, creature does not have \`${prop.stat}\` set`
});
return applyChildren(node, actionContext);
}
damagePropertyWork({
prop: stat,
operation: prop.operation,
value,
actionContext,
});
actionContext.addLog({
name: 'Attribute damage',
value: `${prop.stat}${prop.operation === 'set' ? ' set to' : ''}` +
` ${value}`,
inline: true,
});
});
} else {
actionContext.addLog({
name: 'Attribute damage',
value: `${prop.stat}${prop.operation === 'set' ? ' set to' : ''}` +
` ${value}`,
inline: true,
});
}
return applyChildren(node, actionContext);
}
function applyChildren(node, actionContext){
applyNodeTriggers(node, 'after', actionContext);
node.children.forEach(child => applyProperty(child, actionContext));
}

View File

@@ -0,0 +1,80 @@
import applyProperty from '../applyProperty.js';
import recalculateCalculation from './shared/recalculateCalculation.js';
import rollDice from '/imports/parser/rollDice.js';
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
export default function applyBranch(node, actionContext){
applyNodeTriggers(node, 'before', actionContext);
const applyChildren = function(){
applyNodeTriggers(node, 'after', actionContext);
node.children.forEach(child => applyProperty(child, actionContext));
};
const scope = actionContext.scope;
const targets = actionContext.targets;
const prop = node.node;
switch(prop.branchType){
case 'if':
recalculateCalculation(prop.condition, actionContext);
if (prop.condition?.value) applyChildren();
break;
case 'index':
if (node.children.length){
recalculateCalculation(prop.condition, actionContext);
if (!isFinite(prop.condition?.value)) {
actionContext.addLog({
name: 'Branch Error',
value: 'Index did not resolve into a valid number'
});
break;
}
let index = Math.floor(prop.condition?.value);
if (index < 1) index = 1;
if (index > node.children.length) index = node.children.length;
applyNodeTriggers(node, 'after', actionContext);
applyProperty(node.children[index - 1], actionContext);
}
break;
case 'hit':
if (scope['$attackHit']?.value){
if (!targets.length) actionContext.addLog({value: '**On hit**'});
applyChildren();
}
break;
case 'miss':
if (scope['$attackMiss']?.value){
if (!targets.length) actionContext.addLog({value: '**On miss**'});
applyChildren();
}
break;
case 'failedSave':
if (scope['$saveFailed']?.value){
if (!targets.length) actionContext.addLog({value: '**On failed save**'});
applyChildren();
}
break;
case 'successfulSave':
if (scope['$saveSucceeded']?.value){
if (!targets.length) actionContext.addLog({value: '**On save**',});
applyChildren();
}
break;
case 'random':
if (node.children.length){
let index = rollDice(1, node.children.length)[0] - 1;
applyNodeTriggers(node, 'after', actionContext);
applyProperty(node.children[index], actionContext);
}
break;
case 'eachTarget':
if (targets.length) {
targets.forEach(target => {
applyNodeTriggers(node, 'after', actionContext);
actionContext.targets = [target]
node.children.forEach(child => applyProperty(child, actionContext));
});
} else {
applyChildren();
}
break;
}
}

View File

@@ -0,0 +1,128 @@
import {
setLineageOfDocs,
renewDocIds
} from '/imports/api/parenting/parenting.js';
import {setDocToLastOrder} from '/imports/api/parenting/order.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import computedSchemas from '/imports/api/properties/computedPropertySchemasIndex.js';
import applyFnToKey from '/imports/api/engine/computation/utility/applyFnToKey.js';
import { get } from 'lodash';
import resolve, { map, toString } from '/imports/parser/resolve.js';
import symbol from '/imports/parser/parseTree/symbol.js';
import logErrors from './shared/logErrors.js';
import { insertCreatureLog } from '/imports/api/creature/log/CreatureLogs.js';
import cyrb53 from '/imports/api/engine/computation/utility/cyrb53.js';
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
export default function applyBuff(node, actionContext){
applyNodeTriggers(node, 'before', actionContext);
const prop = node.node;
let buffTargets = prop.target === 'self' ? [actionContext.creature] : actionContext.targets;
// Then copy the decendants of the buff to the targets
let propList = [prop];
function addChildrenToPropList(children){
children.forEach(child => {
propList.push(child.node);
addChildrenToPropList(child.children);
});
}
addChildrenToPropList(node.children);
crystalizeVariables({propList, actionContext});
let oldParent = {
id: prop.parent.id,
collection: prop.parent.collection,
};
buffTargets.forEach(target => {
// Apply the buff
copyNodeListToTarget(propList, target, oldParent);
//Log the buff
if (prop.name || prop.description?.value){
if (target._id === actionContext.creature._id){
// Targeting self
actionContext.addLog({
name: prop.name,
value: prop.description?.value,
});
} else {
// Targeting other
insertCreatureLog.call({
log: {
creatureId: target._id,
content: [{
name: prop.name,
value: prop.description?.value,
}],
}
});
}
}
});
applyNodeTriggers(node, 'after', actionContext);
// Don't apply the children of the buff, they get copied to the target instead
}
function copyNodeListToTarget(propList, target, oldParent){
let ancestry = [{collection: 'creatures', id: target._id}];
setLineageOfDocs({
docArray: propList,
newAncestry: ancestry,
oldParent,
});
renewDocIds({
docArray: propList,
});
setDocToLastOrder({
collection: CreatureProperties,
doc: propList[0],
});
CreatureProperties.batchInsert(propList);
}
/**
* Replaces all variables with their resolved values
* except variables of the form `$target.thing.total` become `thing.total`
*/
function crystalizeVariables({propList, actionContext}){
propList.forEach(prop => {
computedSchemas[prop.type].computedFields().forEach( calcKey => {
applyFnToKey(prop, calcKey, (prop, key) => {
const calcObj = get(prop, key);
if (!calcObj?.parseNode) return;
calcObj.parseNode = map(calcObj.parseNode, node => {
// Skip nodes that aren't symbols or accessors
if (
node.parseType !== 'accessor' && node.parseType !== 'symbol'
) return node;
// Handle variables
if (node.name === '$target'){
// strip $target
if (node.parseType === 'accessor'){
node.name = node.path.shift();
if (!node.path.length){
return symbol.create({name: node.name})
}
} else {
// Can't strip symbols
actionContext.addLog({
name: 'Error',
value: 'Variable `$target` should not be used without a property: $target.property',
});
}
return node;
} else {
// Resolve all other variables
const {result, context} = resolve('reduce', node, actionContext.scope);
logErrors(context.errors, actionContext);
return result;
}
});
calcObj.calculation = toString(calcObj.parseNode);
calcObj.hash = cyrb53(calcObj.calculation);
});
});
});
}

View File

@@ -0,0 +1,231 @@
import { some, intersection, difference, remove } from 'lodash';
import applyProperty from '../applyProperty.js';
import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js';
import resolve, { Context, toString } from '/imports/parser/resolve.js';
import logErrors from './shared/logErrors.js';
import applyEffectsToCalculationParseNode from '/imports/api/engine/actions/applyPropertyByType/shared/applyEffectsToCalculationParseNode.js';
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
import {
getPropertiesOfType
} from '/imports/api/engine/loadCreatures.js';
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
export default function applyDamage(node, actionContext){
applyNodeTriggers(node, 'before', actionContext);
const applyChildren = function(){
applyNodeTriggers(node, 'after', actionContext);
node.children.forEach(child => applyProperty(child, actionContext));
};
const prop = node.node;
const scope = actionContext.scope;
// Skip if there is no parse node to work with
if (!prop.amount?.parseNode) return;
// Choose target
let damageTargets = prop.target === 'self' ? [actionContext.creature] : actionContext.targets;
// Determine if the hit is critical
let criticalHit = scope['$criticalHit']?.value &&
prop.damageType !== 'healing' // Can't critically heal
;
// Double the damage rolls if the hit is critical
let context = new Context({
options: {doubleRolls: criticalHit},
});
// Gather all the lines we need to log into an array
const logValue = [];
const logName = prop.damageType === 'healing' ? 'Healing' : 'Damage';
// roll the dice only and store that string
applyEffectsToCalculationParseNode(prop.amount, actionContext.log);
const {result: rolled} = resolve('roll', prop.amount.parseNode, scope, context);
if (rolled.parseType !== 'constant'){
logValue.push(toString(rolled));
}
logErrors(context.errors, actionContext);
// Reset the errors so we don't log the same errors twice
context.errors = [];
// Resolve the roll to a final value
const {result: reduced} = resolve('reduce', rolled, scope, context);
logErrors(context.errors, actionContext);
// Store the result
if (reduced.parseType === 'constant'){
prop.amount.value = reduced.value;
} else if (reduced.parseType === 'error'){
prop.amount.value = null;
} else {
prop.amount.value = toString(reduced);
}
let damage = +reduced.value;
// If we didn't end up with a constant of finite amount, give up
if (reduced?.parseType !== 'constant' || !isFinite(reduced.value)){
return applyChildren();
}
// Round the damage to a whole number
damage = Math.floor(damage);
// Convert extra damage into the stored type
if (prop.damageType === 'extra' && scope['$lastDamageType']) {
prop.damageType = scope['$lastDamageType'];
}
// Store current damage type
if (prop.damageType !== 'healing') {
scope['$lastDamageType'] = prop.damageType;
}
// Memoise the damage suffix for the log
let suffix = (criticalHit ? ' critical ' : ' ') +
prop.damageType +
(prop.damageType !== 'healing' ? ' damage ': '');
if (damageTargets && damageTargets.length) {
// Iterate through all the targets
damageTargets.forEach(target => {
// Apply weaknesses/resistances/immunities
damage = applyDamageMultipliers({
target,
damage,
damageProp: prop,
logValue
});
actionContext.target = [target];
// Deal the damage to the target
let damageDealt = dealDamage({
target,
damageType: prop.damageType,
amount: damage,
actionContext
});
// Log the damage done
if (target._id === actionContext.creature._id){
// Target is same as self, log damage as such
logValue.push(`**${damageDealt}** ${suffix} to self`);
} else {
logValue.push(`Dealt **${damageDealt}** ${suffix} ${target.name && ' to '}${target.name}`);
// Log the damage received on that creature's log as well
insertCreatureLog.call({
log: {
creatureId: target._id,
content: [{
name,
value: `Recieved **${damageDealt}** ${suffix}`,
}],
}
});
}
});
} else {
// There are no targets, just log the result
logValue.push(`**${damage}** ${suffix}`);
}
actionContext.addLog({
name: logName,
value: logValue.join('\n'),
inline: true,
});
return applyChildren();
}
function applyDamageMultipliers({target, damage, damageProp, logValue}){
const damageType = damageProp?.damageType;
if (!damageType) return damage;
const multiplier = target?.variables?.[damageType];
if (!multiplier) return damage;
const damageTypeText = damageType == 'healing' ? 'healing': `${damageType} damage`;
if (
multiplier.immunity &&
some(multiplier.immunities, multiplierAppliesTo(damageProp))
){
logValue.push(`Immune to ${damageTypeText}`);
return 0;
} else {
if (
multiplier.resistance &&
some(multiplier.resistances, multiplierAppliesTo(damageProp))
){
logValue.push(`Resistant to ${damageTypeText}`);
damage = Math.floor(damage / 2);
}
if (
multiplier.vulnerability &&
some(multiplier.vulnerabilities, multiplierAppliesTo(damageProp))
){
logValue.push(`Vulnerable to ${damageTypeText}`);
damage = Math.floor(damage * 2);
}
}
return damage;
}
function multiplierAppliesTo(damageProp){
return multiplier => {
const hasRequiredTags = difference(
multiplier.includeTags, damageProp.tags
).length === 0;
const hasNoExcludedTags = intersection(
multiplier.excludeTags, damageProp.tags
).length === 0;
return hasRequiredTags && hasNoExcludedTags;
}
}
function dealDamage({target, damageType, amount, actionContext}){
// Get all the health bars and do damage to them
let healthBars = getPropertiesOfType(target._id, 'attribute');
// Keep only the healthbars that can take damage/healing
remove(healthBars, (bar) =>
bar.attributeType !== 'healthBar' ||
bar.inactive ||
bar.removed ||
bar.overridden ||
(amount >= 0 && bar.healthBarNoDamage) ||
(amount < 0 && bar.healthBarNoHealing)
);
// Sort healthbars by damage/healing order or tree order as a fallback
healthBars.sort((a, b) => {
let diff;
if (amount >= 0) {
diff = a.healthBarDamageOrder - b.healthBarDamageOrder;
} else {
diff = a.healthBarHealingOrder - b.healthBarHealingOrder;
}
if (Number.isFinite(diff)) {
return diff;
} else {
return a.order - b.order;
}
});
// Deal the damage to each healthbar in order until all damage is done
const totalDamage = amount;
let damageLeft = totalDamage;
if (damageType === 'healing') damageLeft = -totalDamage;
healthBars.forEach(healthBar => {
if (damageLeft === 0) return;
let damageAdded = damagePropertyWork({
prop: healthBar,
operation: 'increment',
value: damageLeft,
actionContext
});
damageLeft -= damageAdded;
});
return totalDamage;
}

View File

@@ -0,0 +1,27 @@
import recalculateInlineCalculations from './shared/recalculateInlineCalculations.js';
import applyProperty from '../applyProperty.js';
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
export default function applyNote(node, actionContext){
applyNodeTriggers(node, 'before', actionContext);
const prop = node.node;
// Log Name, summary
let content = { name: prop.name };
if (prop.summary?.text){
recalculateInlineCalculations(prop.summary, actionContext);
content.value = prop.summary.value;
}
if (content.name || content.value){
actionContext.addLog(content);
}
// Log description
if (prop.description?.text){
recalculateInlineCalculations(prop.description, actionContext);
actionContext.addLog({value: prop.description.value});
}
// Apply triggers
applyNodeTriggers(node, 'after', actionContext);
// Apply children
node.children.forEach(child => applyProperty(child, actionContext));
}

View File

@@ -0,0 +1,61 @@
import applyProperty from '../applyProperty.js';
import logErrors from './shared/logErrors.js';
import applyEffectsToCalculationParseNode from '/imports/api/engine/actions/applyPropertyByType/shared/applyEffectsToCalculationParseNode.js';
import resolve, { toString } from '/imports/parser/resolve.js';
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
export default function applyRoll(node, actionContext){
applyNodeTriggers(node, 'before', actionContext);
const prop = node.node;
const applyChildren = function(){
applyNodeTriggers(node, 'after', actionContext);
node.children.forEach(child => applyProperty(child, actionContext));
};
if (prop.roll?.calculation){
const logValue = [];
// roll the dice only and store that string
applyEffectsToCalculationParseNode(prop.roll, actionContext);
const {result: rolled, context} = resolve('roll', prop.roll.parseNode, actionContext.scope);
if (rolled.parseType !== 'constant'){
logValue.push(toString(rolled));
}
logErrors(context.errors, actionContext);
// Reset the errors so we don't log the same errors twice
context.errors = [];
// Resolve the roll to a final value
const {result: reduced} = resolve('reduce', rolled, actionContext.scope, context);
logErrors(context.errors, actionContext);
// Store the result
if (reduced.parseType === 'constant'){
prop.roll.value = reduced.value;
} else if (reduced.parseType === 'error'){
prop.roll.value = null;
} else {
prop.roll.value = toString(reduced);
}
// If we didn't end up with a constant of finite amount, give up
if (reduced?.parseType !== 'constant' || !isFinite(reduced.value)){
return applyChildren();
}
const value = reduced.value;
actionContext.scope[prop.variableName] = value;
logValue.push(`**${value}**`);
if (!prop.silent){
actionContext.addLog({
name: prop.name,
value: logValue.join('\n'),
inline: true,
});
}
}
return applyChildren();
}

View File

@@ -0,0 +1,104 @@
import rollDice from '/imports/parser/rollDice.js';
import recalculateCalculation from './shared/recalculateCalculation.js';
import applyProperty from '../applyProperty.js';
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
export default function applySavingThrow(node, actionContext){
applyNodeTriggers(node, 'before', actionContext);
const prop = node.node;
let saveTargets = prop.target === 'self' ? [actionContext.creature] : actionContext.targets;
recalculateCalculation(prop.dc, actionContext);
const dc = (prop.dc?.value);
if (!isFinite(dc)){
actionContext.addLog({
name: 'Error',
value: 'Saving throw requires a DC',
});
return node.children.forEach(child => applyProperty(child, actionContext));
}
actionContext.addLog({
name: prop.name,
value: `DC **${dc}**`,
inline: true,
});
const scope = actionContext.scope;
// If there are no save targets, apply all children as if the save both
// succeeeded and failed
if (!saveTargets?.length){
scope['$saveFailed'] = {value: true};
scope['$saveSucceeded'] = { value: true };
applyNodeTriggers(node, 'after', actionContext);
return node.children.forEach(child => applyProperty(child, actionContext));
}
// Each target makes the saving throw
saveTargets.forEach(target => {
delete scope['$saveFailed'];
delete scope['$saveSucceeded'];
delete scope['$saveDiceRoll'];
delete scope['$saveRoll'];
const applyChildren = function () {
applyNodeTriggers(node, 'after', actionContext);
actionContext.targets = [target]
node.children.forEach(child => applyProperty(child, actionContext));
};
const save = target.variables[prop.stat];
if (!save){
actionContext.addLog({
name: 'Saving throw error',
value: 'No saving throw found: ' + prop.stat,
});
return applyChildren();
}
const rollModifierText = numberToSignedString(save.value, true);
let value, values, resultPrefix;
if (save.advantage === 1){
const [a, b] = rollDice(2, 20);
if (a >= b) {
value = a;
resultPrefix = `Advantage\n1d20 [ ${a}, ~~${b}~~ ] ${rollModifierText}`;
} else {
value = b;
resultPrefix = `Advantage\n1d20 [ ~~${a}~~, ${b} ] ${rollModifierText}`;
}
} else if (save.advantage === -1){
const [a, b] = rollDice(2, 20);
if (a <= b) {
value = a;
resultPrefix = `Disadvantage\n1d20 [ ${a}, ~~${b}~~ ] ${rollModifierText}`;
} else {
value = b;
resultPrefix = `Disadvantage\n1d20 [ ~~${a}~~, ${b} ] ${rollModifierText}`;
}
} else {
values = rollDice(1, 20);
value = values[0];
resultPrefix = `1d20 [ ${value} ] ${rollModifierText}`
}
scope['$saveDiceRoll'] = {value};
const result = value + save.value || 0;
scope['$saveRoll'] = {value: result};
const saveSuccess = result >= dc;
if (saveSuccess){
scope['$saveSucceeded'] = {value: true};
} else {
scope['$saveFailed'] = {value: true};
}
actionContext.addLog({
name: saveSuccess ? 'Successful save' : 'Failed save',
value: resultPrefix + '\n**' + result + '**',
inline: true,
});
return applyChildren();
});
}

View File

@@ -0,0 +1,13 @@
import applyProperty from '../applyProperty.js';
import recalculateCalculation from './shared/recalculateCalculation.js';
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
export default function applyToggle(node, actionContext){
applyNodeTriggers(node, 'before', actionContext);
const prop = node.node;
recalculateCalculation(prop.condition, actionContext);
if (prop.condition?.value) {
applyNodeTriggers(node, 'after', actionContext);
return node.children.forEach(child => applyProperty(child, actionContext));
}
}

View File

@@ -0,0 +1,24 @@
import operator from '/imports/parser/parseTree/operator.js';
import { parse } from '/imports/parser/parser.js';
import logErrors from './logErrors.js';
export default function applyEffectsToCalculationParseNode(calcObj, actionContext){
if (!calcObj.effects) return;
calcObj.effects.forEach(effect => {
if (effect.operation !== 'add') return;
if (!effect.amount) return;
if (effect.amount.value === null) return;
let effectParseNode;
try {
effectParseNode = parse(effect.amount.value.toString());
calcObj.parseNode = operator.create({
left: calcObj.parseNode,
right: effectParseNode,
operator: '+',
fn: 'add'
});
} catch (e){
logErrors([e], actionContext)
}
});
}

View File

@@ -0,0 +1,7 @@
export default function logErrors(errors, actionContext){
errors?.forEach(error => {
if (error.type !== 'info'){
actionContext.addLog({name: 'Error', value: error.message});
}
});
}

View File

@@ -0,0 +1,11 @@
import evaluateCalculation from '/imports/api/engine/computation/utility/evaluateCalculation.js';
import applyEffectsToCalculationParseNode from '/imports/api/engine/actions/applyPropertyByType/shared/applyEffectsToCalculationParseNode.js';
import logErrors from './logErrors.js';
export default function recalculateCalculation(calc, actionContext, context){
if (!calc?.parseNode) return;
calc._parseLevel = 'reduce';
applyEffectsToCalculationParseNode(calc, actionContext.log);
evaluateCalculation(calc, actionContext.scope, context);
logErrors(calc.errors, actionContext.log);
}

View File

@@ -0,0 +1,13 @@
import embedInlineCalculations from '/imports/api/engine/computation/utility/embedInlineCalculations.js';
import recalculateCalculation from './recalculateCalculation.js'
export default function recalculateInlineCalculations(inlineCalcObj, actionContext){
// Skip if there are no calculations
if (!inlineCalcObj?.inlineCalculations?.length) return;
// Recalculate each calculation with the current scope
inlineCalcObj.inlineCalculations.forEach(calc => {
recalculateCalculation(calc, actionContext);
});
// Embed the new calculated values
embedInlineCalculations(inlineCalcObj);
}

View File

@@ -0,0 +1,111 @@
import recalculateCalculation from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateCalculation.js';
import recalculateInlineCalculations from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateInlineCalculations.js';
import { getPropertyDecendants } from '/imports/api/engine/loadCreatures.js';
import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js';
import applyProperty from '/imports/api/engine/actions/applyProperty.js';
import { difference, intersection } from 'lodash';
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags.js';
export function applyNodeTriggers(node, timing, actionContext) {
const prop = node.node;
const type = prop.type;
const triggers = actionContext.triggers?.doActionProperty?.[type]?.[timing];
if (triggers) {
triggers.forEach(trigger => {
applyTrigger(trigger, prop, actionContext);
});
}
}
export function applyTriggers(triggers = [], prop, actionContext) {
// Apply the triggers
triggers.forEach(trigger => {
applyTrigger(trigger, prop, actionContext)
});
}
export function applyTrigger(trigger, prop, actionContext) {
// If there is a prop we are applying the trigger from,
// don't fire if the tags don't match
if (prop && !triggerMatchTags(trigger, prop)) {
return;
}
// Prevent trigger from firing if it's inactive
if (trigger.inactive) {
return;
}
// Prevent triggers from firing if their condition is false
if (trigger.condition?.parseNode) {
recalculateCalculation(trigger.condition, actionContext);
if (!trigger.condition.value) return;
}
// Prevent triggers from firing themselves in a loop
if (trigger.firing) {
/*
log.content.push({
name: trigger.name || 'Trigger',
value: 'Trigger can\'t fire itself',
inline: true,
});
*/
return;
}
trigger.firing = true;
// Fire the trigger
const content = {
name: trigger.name || 'Trigger',
value: trigger.description,
inline: false,
}
if (trigger.description?.text){
recalculateInlineCalculations(trigger.description, actionContext);
content.value = trigger.description.value;
}
actionContext.addLog(content);
// Get all the trigger's properties and apply them
const properties = getPropertyDecendants(actionContext.creature._id, trigger._id);
properties.sort((a, b) => a.order - b.order);
const propertyForest = nodeArrayToTree(properties);
propertyForest.forEach(node => {
applyProperty(node, actionContext);
});
trigger.firing = false;
}
function triggerMatchTags(trigger, prop) {
let matched = false;
const propTags = getEffectivePropTags(prop);
// Check the target tags
if (
!trigger.targetTags?.length ||
difference(trigger.targetTags, propTags).length === 0
) {
matched = true;
}
// Check the extra tags
trigger.extraTags?.forEach(extra => {
if (extra.operation === 'OR') {
if (matched) return;
if (
!extra.tags.length ||
difference(extra.tags, propTags).length === 0
) {
matched = true;
}
} else if (extra.operation === 'NOT') {
if (
extra.tags.length &&
intersection(extra.tags, propTags)
) {
return false;
}
}
});
return matched;
}

View File

@@ -0,0 +1,100 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js';
import {
getProperyAncestors, getPropertyDecendants
} from '/imports/api/engine/loadCreatures.js';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import applyProperty from './applyProperty.js';
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
const doAction = new ValidatedMethod({
name: 'creatureProperties.doAction',
validate: new SimpleSchema({
actionId: SimpleSchema.RegEx.Id,
targetIds: {
type: Array,
defaultValue: [],
maxCount: 20,
optional: true,
},
'targetIds.$': {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
scope: {
type: Object,
blackbox: true,
optional: true,
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 10,
timeInterval: 5000,
},
run({ actionId, targetIds = [], scope }) {
// Get action context
let action = CreatureProperties.findOne(actionId);
const creatureId = action.ancestors[0].id;
const actionContext = new ActionContext(creatureId, targetIds, this);
// Check permissions
assertEditPermission(actionContext.creature, this.userId);
actionContext.targets.forEach(target => {
assertEditPermission(target, this.userId);
});
const ancestors = getProperyAncestors(creatureId, action._id);
ancestors.sort((a, b) => a.order - b.order);
const properties = getPropertyDecendants(creatureId, action._id);
properties.push(action);
properties.sort((a, b) => a.order - b.order);
// Do the action
doActionWork({properties, ancestors, actionContext, methodScope: scope});
// Recompute all involved creatures
Creatures.update({
_id: { $in: [creatureId, ...targetIds] }
}, {
$set: {dirty: true},
});
},
});
export default doAction;
export function doActionWork({
properties, ancestors, actionContext, methodScope = {},
}){
// get the docs
const ancestorScope = getAncestorScope(ancestors);
const propertyForest = nodeArrayToTree(properties);
if (propertyForest.length !== 1){
throw new Meteor.Error(`The action has ${propertyForest.length} top level properties, expected 1`);
}
// Include the ancestry and method scope in the context scope
Object.assign(actionContext.scope, ancestorScope, methodScope);
// Apply the top level property, it is responsible for applying its children
// recursively
applyProperty(propertyForest[0], actionContext);
// Insert the log
actionContext.writeLog();
}
// Assumes ancestors are in tree order already
function getAncestorScope(ancestors){
let scope = {};
ancestors.forEach(prop => {
scope[`#${prop.type}`] = prop;
});
return scope;
}

View File

@@ -0,0 +1,53 @@
import '/imports/api/simpleSchemaConfig.js';
//import testTypes from './testTypes/index.js';
import { doActionWork } from './doAction.js';
import { CreatureLogSchema } from '/imports/api/creature/log/CreatureLogs.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
function cleanProp(prop){
let schema = CreatureProperties.simpleSchema(prop);
return schema.clean(prop);
}
function cleanCreature(creature){
let schema = Creatures.simpleSchema(creature);
return schema.clean(creature);
}
// Fake ActionContext to test actions with
const creatureId = 'actionTestCreatureId';
const creatureName = 'Action Test Creature';
const testActionContext = {
creature: cleanCreature({
_id: creatureId,
}),
log: CreatureLogSchema.clean({
creatureId: creatureId,
creatureName: creatureName,
}),
scope: {},
addLog(content) {
if (content.name || content.value){
this.log.content.push(content);
}
},
writeLog: () => { },
}
const action = cleanProp({
type: 'action',
});
const actionAncestors = [];
describe('Do Action', function(){
it('Does an empty action', function(){
doActionWork({
properties: [action],
ancestors: actionAncestors,
actionContext: testActionContext,
methodScope: {},
});
});
//testTypes.forEach(test => it(test.text, test.fn));
});

View File

@@ -0,0 +1,126 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import {
getProperyAncestors, getPropertyDecendants
} from '/imports/api/engine/loadCreatures.js';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
import { doActionWork } from '/imports/api/engine/actions/doAction.js';
import { CreatureLogSchema } from '/imports/api/creature/log/CreatureLogs.js';
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
const doAction = new ValidatedMethod({
name: 'creatureProperties.doCastSpell',
validate: new SimpleSchema({
spellId: SimpleSchema.RegEx.Id,
slotId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
optional: true,
},
targetIds: {
type: Array,
defaultValue: [],
maxCount: 20,
optional: true,
},
'targetIds.$': {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
scope: {
type: Object,
blackbox: true,
optional: true,
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 10,
timeInterval: 5000,
},
run({ spellId, slotId, targetIds = [], scope = {} }) {
// Get action context
let spell = CreatureProperties.findOne(spellId);
const creatureId = spell.ancestors[0].id;
const actionContext = new ActionContext(creatureId, targetIds, this);
// Check permissions
assertEditPermission(actionContext.creature, this.userId);
actionContext.targets.forEach(target => {
assertEditPermission(target, this.userId);
});
const ancestors = getProperyAncestors(creatureId, spell._id);
ancestors.sort((a, b) => a.order - b.order);
const properties = getPropertyDecendants(creatureId, spell._id);
properties.push(spell);
properties.sort((a, b) => a.order - b.order);
// Spend the appropriate slot
let slotLevel = spell.level || 0;
let slot;
actionContext.scope['slotLevel'] = slotLevel;
if (slotId && !spell.castWithoutSpellSlots){
slot = CreatureProperties.findOne(slotId);
if (!slot){
throw new Meteor.Error('No slot',
'Slot not found to cast spell');
}
if (!slot.value){
throw new Meteor.Error('No slot',
'Slot depleted');
}
if (slot.attributeType !== 'spellSlot'){
throw new Meteor.Error('Not a slot',
'The given property is not a valid spell slot');
}
if (!slot.spellSlotLevel?.value){
throw new Meteor.Error('No slot level',
'Slot does not have a spell slot level');
}
if (slot.spellSlotLevel.value < spell.level){
throw new Meteor.Error('Slot too small',
'Slot is not large enough to cast spell');
}
slotLevel = slot.spellSlotLevel.value;
damagePropertyWork({
prop: slot,
operation: 'increment',
value: 1,
actionContext,
});
}
// Post the slot level spent to the log
if (slot?.spellSlotLevel?.value){
actionContext.addLog({
name: `Casting using a level ${slotLevel} spell slot`
});
} else if (slotLevel) {
actionContext.addLog({
name: `Casting at level ${slotLevel}`
});
}
// Do the action
doActionWork({
properties, ancestors, actionContext, methodScope: scope,
});
// Force the characters involved to recalculate
Creatures.update({
_id: { $in: [creatureId, ...targetIds] }
}, {
$set: { dirty: true },
});
},
});
export default doAction;

View File

@@ -0,0 +1,108 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
import rollDice from '/imports/parser/rollDice.js';
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers.js';
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
const doCheck = new ValidatedMethod({
name: 'creatureProperties.doCheck',
validate: new SimpleSchema({
propId: SimpleSchema.RegEx.Id,
scope: {
type: Object,
blackbox: true,
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 10,
timeInterval: 5000,
},
run({propId, scope}) {
const prop = CreatureProperties.findOne(propId);
const creatureId = prop.ancestors[0].id;
const actionContext = new ActionContext(creatureId, [creatureId], this);
Object.assign(actionContext.scope, scope);
// Check permissions
assertEditPermission(actionContext.creature, this.userId);
// Do the check
doCheckWork({prop, actionContext});
},
});
export default doCheck;
export function doCheckWork({prop, actionContext}){
applyTriggers(actionContext.triggers.check?.before, prop, actionContext);
rollCheck(prop, actionContext);
applyTriggers(actionContext.triggers.check?.after, prop, actionContext);
// Insert the log
actionContext.writeLog();
}
function rollCheck(prop, actionContext) {
const scope = actionContext.scope;
// get the modifier for the roll
let rollModifier;
let logName = `${prop.name} check`;
if (prop.type === 'skill'){
rollModifier = prop.value;
if (prop.skillType === 'save'){
if (prop.name.match(/save/i)){
logName = prop.name;
} else {
logName = prop.name ? `${prop.name} save` : 'Saving Throw';
}
}
} else if (prop.type === 'attribute'){
if (prop.attributeType === 'ability'){
rollModifier = prop.modifier;
} else {
rollModifier = prop.value;
}
} else {
throw (`${prop.type} not supported for checks`);
}
const rollModifierText = numberToSignedString(rollModifier, true);
let value, values, resultPrefix;
if (scope['$checkAdvantage'] === 1){
logName += ' (Advantage)';
const [a, b] = rollDice(2, 20);
if (a >= b) {
value = a;
resultPrefix = `1d20 [ ${a}, ~~${b}~~ ] ${rollModifierText} = `;
} else {
value = b;
resultPrefix = `1d20 [ ~~${a}~~, ${b} ] ${rollModifierText} = `;
}
} else if (scope['$checkAdvantage'] === -1){
logName += ' (Disadvantage)';
const [a, b] = rollDice(2, 20);
if (a <= b) {
value = a;
resultPrefix = `1d20 [ ${a}, ~~${b}~~ ] ${rollModifierText} = `;
} else {
value = b;
resultPrefix = `1d20 [ ~~${a}~~, ${b} ] ${rollModifierText} = `;
}
} else {
values = rollDice(1, 20);
value = values[0];
resultPrefix = `1d20 [ ${value} ] ${rollModifierText} = `
}
const result = (value + rollModifier) || 0;
actionContext.addLog({
name: logName,
value: `${resultPrefix} **${result}**`,
});
}

View File

@@ -0,0 +1,2 @@
import './doCastSpell.js';
import './doCheck.js';

View File

@@ -0,0 +1,40 @@
import { EJSON } from 'meteor/ejson';
import createGraph from 'ngraph.graph';
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags.js';
export default class CreatureComputation {
constructor(properties, creature, variables){
// Set up fields
this.originalPropsById = {};
this.propsById = {};
this.propsWithTag = {};
this.scope = {};
this.props = properties;
this.dependencyGraph = createGraph();
this.errors = [];
this.creature = creature;
this.variables = variables;
// Store properties for easy access later
properties.forEach(prop => {
// Store a copy of the unmodified prop
// EJSON clone is ~4x faster than lodash cloneDeep for EJSONable objects
this.originalPropsById[prop._id] = EJSON.clone(prop);
// Store by id
this.propsById[prop._id] = prop;
// Store sets of ids in each tag
getEffectivePropTags(prop).forEach(tag => {
if (!tag) return;
if (this.propsWithTag[tag]) {
this.propsWithTag[tag].push(prop._id);
} else {
this.propsWithTag[tag] = [prop._id];
}
});
// Store the prop in the dependency graph
this.dependencyGraph.addNode(prop._id, prop);
});
}
}

View File

@@ -0,0 +1,43 @@
import walkDown from '/imports/api/engine/computation/utility/walkdown.js';
export default function computeInactiveStatus(node){
const prop = node.node;
if (!isActive(prop)){
// Mark prop inactive due to self
prop.inactive = true;
prop.deactivatedBySelf = true;
}
if(!childrenActive(prop)){
// Mark children as inactive due to ancestor
walkDown(node.children, child => {
child.node.inactive = true;
child.node.deactivatedByAncestor = true;
});
}
}
function isActive(prop){
if (prop.disabled) return false;
switch (prop.type){
// Unprepared spells are inactive
case 'spell': return !!prop.prepared || !!prop.alwaysPrepared;
default: return true;
}
}
function childrenActive(prop){
// Children of disabled properties are always inactive
if (prop.disabled) return false;
switch (prop.type){
// Only equipped items have active children
case 'item': return !!prop.equipped;
// The children of actions, spells, and triggers are always inactive
case 'action': return false;
case 'spell': return false;
case 'trigger': return false;
// The children of notes are always inactive
case 'note': return false;
// Other children are active
default: return true;
}
}

View File

@@ -0,0 +1,21 @@
/**
* Only computes `totalFilled`, need to compute `quantityExpected.value`
* before `spacesLeft` can be computed
*/
export default function computeSlotQuantityFilled(node, dependencyGraph){
let slot = node.node;
if (slot.type !== 'propertySlot') return;
slot.totalFilled = 0;
node.children.forEach(child => {
let childProp = child.node;
dependencyGraph.addLink(slot._id, childProp._id, 'slotFill');
if (
childProp.type === 'slotFiller' &&
Number.isFinite(childProp.slotQuantityFilled)
){
slot.totalFilled += childProp.slotQuantityFilled;
} else {
slot.totalFilled++;
}
});
}

View File

@@ -0,0 +1,17 @@
import walkDown from '/imports/api/engine/computation/utility/walkdown.js';
export default function computeToggleDependencies(node, dependencyGraph){
const prop = node.node;
// Only for toggles that aren't inactive and aren't set to enabled or disabled
if (
prop.inactive ||
prop.type !== 'toggle' ||
prop.disabled ||
prop.enabled
) return;
walkDown(node.children, child => {
child.node._computationDetails.toggleAncestors.push(prop);
// The child nodes depend on the toggle condition compuation
dependencyGraph.addLink(child.node._id, prop._id, 'toggle');
});
}

View File

@@ -0,0 +1,51 @@
import findAncestorByType from '/imports/api/engine/computation/utility/findAncestorByType.js';
import { traverse } from '/imports/parser/resolve.js';
export default function linkCalculationDependencies(dependencyGraph, prop, {propsById}){
prop._computationDetails.calculations.forEach(calcObj => {
// Store resolved ancestors
const memo = {
// ancestors: {} //this gets added if there are resolved ancestors
};
// Add this calculation to the dependency graph
const calcNodeId = `${prop._id}.${calcObj._key}`;
dependencyGraph.addNode(calcNodeId, calcObj);
// Traverse the parsed calculation looking for variable names
traverse(calcObj.parseNode, node => {
// Skip nodes that aren't symbols or accessors
if (node.parseType !== 'symbol' && node.parseType !== 'accessor') return;
// Link ancestor references as direct property dependencies
if (node.name[0] === '#'){
let ancestorProp = getAncestorProp(
node.name.slice(1), memo, prop, propsById
);
if (!ancestorProp) return;
// Link the ancestor prop as a direct dependency
dependencyGraph.addLink(
calcNodeId, ancestorProp._id, 'ancestorReference'
);
} else {
// Link variable name references as variable dependencies
dependencyGraph.addLink(
calcNodeId, node.name, 'variableReference'
);
}
});
// Store the resolved ancestors in this calculation's local scope
if (memo.ancestors) {
calcObj._localScope = { ...calcObj._localScope, ...memo.ancestors};
}
});
}
function getAncestorProp(type, memo, prop, propsById){
if (memo.ancestors && memo.ancestors['#' + type]){
return memo.ancestors['#' + type];
} else {
var ancestorProp = findAncestorByType( prop, type, propsById );
if (!memo.ancestors) memo.ancestors = {};
memo.ancestors['#' + type] = ancestorProp;
return ancestorProp;
}
}

View File

@@ -0,0 +1,62 @@
/**
* Performs a depth first traversal of the character tree, summing the container
* and inventory contents on the way up the tree
*/
export default function linkInventory(forest, dependencyGraph){
// The stack of properties to still navigate
const stack = [...forest];
// The current containers we are inside of
const containerStack = [];
while(stack.length){
const top = stack[stack.length - 1];
const prop = top.node;
if (prop._computationDetails.inventoryChildrenVisited){
if (prop.type === 'container') containerStack.pop();
stack.pop();
handleProp(prop, containerStack, dependencyGraph);
} else {
// Add all containers to the stack when we first visit them
if (prop.type === 'container'){
containerStack.push(top.node);
}
// Push children onto the stack and mark this as children are visited
stack.push(...top.children);
prop._computationDetails.inventoryChildrenVisited = true;
}
}
}
function handleProp(prop, containerStack, dependencyGraph){
// Skip props that aren't part of the inventory
if (prop.type !== 'item' && prop.type !== 'container') return;
// Determine if this property is carried, items are carried by default
let carried = prop.type === 'container' ? prop.carried : true;
// Item-specific links
if (prop.type === 'item'){
if (prop.attuned){
dependencyGraph.addLink('itemsAttuned', prop._id, 'attunedItem');
}
if (prop.equipped){
dependencyGraph.addLink('weightEquipment', prop._id, 'equippedItem');
dependencyGraph.addLink('valueEquipment', prop._id, 'equippedItem');
}
}
// Get the parent container
const container = containerStack[containerStack.length - 1];
if (container){
// The container depends on this prop for its contents data
dependencyGraph.addLink(container._id, prop._id, 'containerContents');
} else {
// There is no parent container, the character totals depend on this prop
dependencyGraph.addLink('weightTotal', prop._id, 'inventoryStats');
dependencyGraph.addLink('valueTotal', prop._id, 'inventoryStats');
if (carried){
dependencyGraph.addLink('weightCarried', prop._id, 'inventoryStats');
dependencyGraph.addLink('valueCarried', prop._id, 'inventoryStats');
}
}
}

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