From ea18464bf730accb70f10985b5a8d44f50ab0d7a Mon Sep 17 00:00:00 2001 From: sladecraven Date: Sun, 27 Nov 2022 08:58:19 +0100 Subject: [PATCH] Init system --- fonts/middlesaxonytext.ttf | Bin 0 -> 67040 bytes fonts/zag_bold.otf | Bin 0 -> 35968 bytes fonts/zag_regular.otf | Bin 0 -> 35976 bytes images/.directory | 4 + images/dice/.directory | 4 + images/dice/cancel_icon.webp | Bin 0 -> 10958 bytes images/dice/d10-grey.svg | 1 + images/dice/d10black.svg | 1 + images/dice/d6_1.png | Bin 0 -> 1559 bytes images/dice/d6_1.webp | Bin 0 -> 442 bytes images/dice/d6_2.png | Bin 0 -> 2145 bytes images/dice/d6_2.webp | Bin 0 -> 688 bytes images/dice/d6_3.png | Bin 0 -> 2825 bytes images/dice/d6_3.webp | Bin 0 -> 952 bytes images/dice/d6_4.png | Bin 0 -> 3139 bytes images/dice/d6_4.webp | Bin 0 -> 1214 bytes images/dice/d6_5.png | Bin 0 -> 3793 bytes images/dice/d6_5.webp | Bin 0 -> 1502 bytes images/dice/d6_6.png | Bin 0 -> 4196 bytes images/dice/d6_6.webp | Bin 0 -> 1748 bytes images/dice/perspective-dice-five.webp | Bin 0 -> 10440 bytes lang/en.json | 3 + modules/dark-stars-actor-sheet.js | 224 +++ modules/dark-stars-actor.js | 808 +++++++++ modules/dark-stars-combat.js | 30 + modules/dark-stars-commands.js | 144 ++ modules/dark-stars-hotbar.js | 68 + modules/dark-stars-item-sheet.js | 170 ++ modules/dark-stars-item.js | 25 + modules/dark-stars-main.js | 116 ++ modules/dark-stars-npc-sheet.js | 209 +++ modules/dark-stars-roll-dialog.js | 84 + modules/dark-stars-token.js | 6 + modules/dark-stars-utility.js | 595 +++++++ styles/simple.css | 1497 +++++++++++++++++ styles/unused.html | 60 + system.json | 214 +++ template.json | 205 +++ templates/actors/actor-sheet.hbs | 530 ++++++ templates/actors/npc-sheet.hbs | 413 +++++ templates/apps/roll-dialog-generic.hbs | 167 ++ templates/chat/chat-attack-defense-result.hbs | 75 + templates/chat/chat-generic-result.hbs | 149 ++ templates/chat/chat-opposed-fail.hbs | 11 + templates/chat/chat-request-defense.hbs | 46 + templates/items/item-armor-sheet.hbs | 92 + templates/items/item-condition-sheet.hbs | 64 + templates/items/item-equipment-sheet.hbs | 31 + templates/items/item-feat-sheet.hbs | 35 + templates/items/item-money-sheet.hbs | 28 + templates/items/item-poison-sheet.hbs | 28 + templates/items/item-race-sheet.hbs | 28 + templates/items/item-shield-sheet.hbs | 55 + templates/items/item-skill-sheet.hbs | 105 ++ templates/items/item-spell-sheet.hbs | 49 + templates/items/item-weapon-sheet.hbs | 84 + templates/items/post-item.hbs | 8 + templates/partials/editor-notes-gm.hbs | 6 + .../partials/partial-actor-ability-block.hbs | 19 + .../partials/partial-actor-equipment.hbs | 37 + templates/partials/partial-actor-status.hbs | 39 + .../partials/partial-item-description.hbs | 8 + templates/partials/partial-item-nav.hbs | 5 + .../partials/partial-options-abilities.hbs | 7 + .../partial-options-equipment-types.hbs | 13 + templates/partials/partial-options-level.hbs | 14 + templates/partials/partial-options-range.hbs | 15 + templates/partials/partial-roll-select.hbs | 4 + 68 files changed, 6633 insertions(+) create mode 100755 fonts/middlesaxonytext.ttf create mode 100755 fonts/zag_bold.otf create mode 100755 fonts/zag_regular.otf create mode 100644 images/.directory create mode 100644 images/dice/.directory create mode 100644 images/dice/cancel_icon.webp create mode 100644 images/dice/d10-grey.svg create mode 100644 images/dice/d10black.svg create mode 100644 images/dice/d6_1.png create mode 100644 images/dice/d6_1.webp create mode 100644 images/dice/d6_2.png create mode 100644 images/dice/d6_2.webp create mode 100644 images/dice/d6_3.png create mode 100644 images/dice/d6_3.webp create mode 100644 images/dice/d6_4.png create mode 100644 images/dice/d6_4.webp create mode 100644 images/dice/d6_5.png create mode 100644 images/dice/d6_5.webp create mode 100644 images/dice/d6_6.png create mode 100644 images/dice/d6_6.webp create mode 100644 images/dice/perspective-dice-five.webp create mode 100644 lang/en.json create mode 100644 modules/dark-stars-actor-sheet.js create mode 100644 modules/dark-stars-actor.js create mode 100644 modules/dark-stars-combat.js create mode 100644 modules/dark-stars-commands.js create mode 100644 modules/dark-stars-hotbar.js create mode 100644 modules/dark-stars-item-sheet.js create mode 100644 modules/dark-stars-item.js create mode 100644 modules/dark-stars-main.js create mode 100644 modules/dark-stars-npc-sheet.js create mode 100644 modules/dark-stars-roll-dialog.js create mode 100644 modules/dark-stars-token.js create mode 100644 modules/dark-stars-utility.js create mode 100644 styles/simple.css create mode 100644 styles/unused.html create mode 100644 system.json create mode 100644 template.json create mode 100644 templates/actors/actor-sheet.hbs create mode 100644 templates/actors/npc-sheet.hbs create mode 100644 templates/apps/roll-dialog-generic.hbs create mode 100644 templates/chat/chat-attack-defense-result.hbs create mode 100644 templates/chat/chat-generic-result.hbs create mode 100644 templates/chat/chat-opposed-fail.hbs create mode 100644 templates/chat/chat-request-defense.hbs create mode 100644 templates/items/item-armor-sheet.hbs create mode 100644 templates/items/item-condition-sheet.hbs create mode 100644 templates/items/item-equipment-sheet.hbs create mode 100644 templates/items/item-feat-sheet.hbs create mode 100644 templates/items/item-money-sheet.hbs create mode 100644 templates/items/item-poison-sheet.hbs create mode 100644 templates/items/item-race-sheet.hbs create mode 100644 templates/items/item-shield-sheet.hbs create mode 100644 templates/items/item-skill-sheet.hbs create mode 100644 templates/items/item-spell-sheet.hbs create mode 100644 templates/items/item-weapon-sheet.hbs create mode 100644 templates/items/post-item.hbs create mode 100644 templates/partials/editor-notes-gm.hbs create mode 100644 templates/partials/partial-actor-ability-block.hbs create mode 100644 templates/partials/partial-actor-equipment.hbs create mode 100644 templates/partials/partial-actor-status.hbs create mode 100644 templates/partials/partial-item-description.hbs create mode 100644 templates/partials/partial-item-nav.hbs create mode 100644 templates/partials/partial-options-abilities.hbs create mode 100644 templates/partials/partial-options-equipment-types.hbs create mode 100644 templates/partials/partial-options-level.hbs create mode 100644 templates/partials/partial-options-range.hbs create mode 100644 templates/partials/partial-roll-select.hbs diff --git a/fonts/middlesaxonytext.ttf b/fonts/middlesaxonytext.ttf new file mode 100755 index 0000000000000000000000000000000000000000..ceb8483bc2894adde65eec08b443c690b7851fed GIT binary patch literal 67040 zcmeFa37jQWeKuU@)V^2U+U~xuz4yJ{_fGfpbkDxeFdNJ;0|UdpBM8H$2%?~Aud ziW-fGq9pj5sJO(4#+@XZMFfi{JZAPu-<%-MXjF zdH&CTJ*POrah%TG#PQtNNxMheJ3qDgF^(h4u=kW5Yqsur&AK1{Ge^XG@xFWZrH5Wr zyu+R0I047-FF*g1>(0Bex8u)X|M#$;73X!AqA&M4rukSyThq*T4*)hW6fq07FFTLHiYRxX3tN0_@Zv)!) zV|>Q0g*`pS&BZVN2Oin>T`ooDxQN`(CCFX)wad69c_$}xZ^S;jus|qB$f?-(Nv_TP z3s)dpI0N6IXq!(ST9_vmo_~MgadHo*k>A7dZ^HB4*ncyp;yfICjpK{lBlzx>oQdz{ zJGGKao!?{3l7_H(rHne;C`{xb6?) z`5ruF)wP@4*+&zGp&wUc-zKlDI zpbz8zV}1CfjX!DO=W(C1{yctTeR=%0)R(M3PuMs z#+>#0(ipRTj>m4t!pG2utRKh7INHo0Nwxqw^7Z^AJc2ktA}Aon${2MibJCD`)^+%~d~>(FV=rH9ckkS+0z zV7p@yneEFRxfU|YBruCJo`&ZgkWi6xxEr|-a3A6x;6BRzF-G+p++T6u=Kh`g8Fz$R zAmd~;*-Z|SYst&V8|fG6m*}^PPSG!>i}_-)ST5F!W5wy>+Tw>w<#MX_apofT zZtjEJhjGS_;f$Z*{+#<8?jOJakH=?RMRt)hamJV8j99r@{z ze}0sE^npk3fAm9-e(=%z9)16#?|k$vkG}37KK>66eCHE?r#av3_Xb+I8QWHD&;Q@0 z&;^%qFXH}^yMmJ_V^H=Ztgny%M~-qN1n61+^}or3UPW-T3|>~bFJMd!&g3l4#+bQW zg7Yx?0T*%+m*i4hn#*8hb6lP)p#Mu;nX7PBuEy252G`_T+yHPh#0_&J+$cB3je|)$ z+ypntb-5n5jGN-7x#ippw}P7mH?QPY0j&>nYq@pYdTs-^k=umc+JfHN#%|~K{_pw|KK=-IJEZOSf4{T%=EL#x1Kj($KjiM@&gU-TF5xbX z_xTtj_O}xI-bFa_3A&b^Mi0|F=>zoh^ild4ukcC!CHzA|Lby_RNGysMh@X(k(u<@w zN)Jh2m%b}KCeO$-%3<|q+INl9%t!1Qr|Z5e@r1uG_;S=tK9b&?*_Az*+nE2W!sDeq z<-e(}u6?J`ZJsy44I~D344gG^75?rRczp0ZL)_4fLyru%hd(iL{mAD=>F9^X%3~iM zPmEtM{`K~B`>h?Wv$^y8oyR9GnRsaOoXIaw&Udx$Y2N!I;;1d^)UHIAye}2(<7k%~O zjhAqj{Mn`SvgT#)y8L@D+JD8ZS3LgWjaU8P>eH_N-K!tH`X`5l!->NKhgTjx@9<5B z-+B0RhaWloox?xBCcLJ7%{kY+<(facR=GBR?RNZ~dF_?gzWmznUi)TN#p4xAlLTdS;<|1w+Z(He^n;zkx{K2vOkTY{J$XxX z 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 literal 0 HcmV?d00001 diff --git a/fonts/zag_bold.otf b/fonts/zag_bold.otf new file mode 100755 index 0000000000000000000000000000000000000000..40888675bbeb8fd0bc4c683c510e29bcb7abfcaf GIT binary patch literal 35968 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 literal 0 HcmV?d00001 diff --git a/fonts/zag_regular.otf b/fonts/zag_regular.otf new file mode 100755 index 0000000000000000000000000000000000000000..0574594f426e3b4931a44063fa965df039b10a53 GIT binary patch literal 35976 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

Tq*Zq;_K#HS zHpX_w4#xA0>IJdWllpL5(m3=0%=bX2fN(@(&!q8-f<5|JBT>8 zPaN#C7vUi6R^nhEN*Rn10GI5XGK}R!a6H*04n5*Jkj95NSmQP@mD3n)z6htmVsWs{ zVxXDP!nx9IwBDsNr8wA!sr12pRJ%wb}GahAZ;Pe+* zPwFXgY>ha!MjTrs4n3t6DAo*lG*AbW4&?Fvpxdn{HiZ>oQ-vyc(sWs!d zHRGXMJHpgD2`nuE(yk*+(h?x;CeVOf36MrezL5ZF-9R%V?THc~jbdmolK^SmSm{nd zuTO-u4j_$4iL6^9q}@cAb_$7*Rs}TS93~Oc+JV%U6Cte?Nb^e)hw+2P4{L6>vIMaYnQbg=gCGfZmX7#M+M}%!2)m7}M4QeOZDZ&a8|W*>(T} z(5H-OM@kdOWd+$X5T1vVZ6n&T0vN)Q4V*U=`WrF6od$-pRuQl&NHVCe5u@B*U=*i` zhF=+B^FUxMYZAv=C2+1pmYl?LlHnB~gm9Wv_@@yzq54wm7`b(fund(;>q#T}JJ}?Y zv5=AG4kP+IX;saVYZz-8X-+Z1bEqyCxu%yGI~lteui$AKto;j+W}Gx`@if@s4#Ebs zQ5x#f4WwOj8jn0C%qb^8@|glln$S8`KrdLpgmyUtoDM%Upx)2bY^0LRum5Aydhkc0d_D6p)5I! z<6C*5LE7n2Z&o?;RbP&*UGUeY0n!^s?`^K%o%VUl2EJuNJOPCQJo z|8c>Lx_1DlKyx$v^DfX6y~Yd+(9>zq*$fNZ2hM;#W?0}X&>N+g*#c%*pcCQQ{L~jU zGV}V%%oZ@i0*#27!;_92W4rcfy)hm@tr*)c{d#f2fK=R2ZW_ZGN-~xDp z89qt1pz{PXW+Dpj zld+4DP8iIXkJ@pnMJEgvw8AYQt!gZ={~aK$YAmq%pMm6W7M`Omu<(6^>2Ae>(-j4f z*7Fv4R~3*}H5OQvB+z=^0?+RRhGHbNz^arooH2r9=3~aOz^_P?Sk4v4NNXDlY)DV( z76v00$E5R9vX})HBhBf~#R7{_m~LJy82za9ogAiBjRkfiq*aXtBQrgvGXV>%M@VM^ z7Fdsv&IBy59;Knxz6J9*A+7c;n8yj}Ouz!`-2t(V)`aP>UK!8>t(guR?f`nh_tG)O z-U3d8P151Xlzs-h1veFF*>v_~Q1dv~EZ8a?p4<)eWeI+;TRJ?M;;ApE!_E{#tD|(- znQ$I@N;<4f(n8?X>9BSNFpT9yFh)Y>boSeH*f||xvU588mg3`C!vxkL5v`EUy*Qn{ z7j#M1mUbEGus`LZwNpCJv+1yZ9r#4F9?n3m?*VDolY#o)0s1o19ySA--vS2WMlXXs zBmH+#5(#NNvjDyF4A9I^>F#v_=8Mz9Le%aA&Y?&` zCQHbKggXe6giMx@$r3VQnR^0GERZXQZ=7@Z#yN*?oOAfbIfrkYbNI$Nhi{y7_{KSh zZ=7@Z#yJPRaRL&EMUaEpkix_q$U%!70TO4Rko75KeF|BhLe{5{^(kb13R$00_RLcD z%u@EuQufSJ_RLcD%u@EuQufSJ_RLcD%u@EuQjD?Jgi`j*QufSJi~ya8AL`2 zoVS+q)^grj&RffQYdJ4gG4RG(&RffQYdP;>V@97e9& zK-$sNbNYHtU(f05Iek5+ujlmjoW7pZALaB%IsH*if0WZ7<@856{ZUSTl+!mLeHnOp zG~(e_ALAA7#xdeGuwON>Up25_HLzbbuwUVB6S_69Up25_;oc0IH?UtduwUUu2(`n# z4Mw~M=(86{e$@bdjswZBaN>wju7Ulkf&Hq1{i*?arX!yGs)7Bgf&Hq1{i=ces)7Bg zf&Hq1{R%5zPER9S16#Y1`)?!n-$w4gjog15x&JnD|83;{+sOU5k^65W_uoeDzm3p2 z3_L~Zzm3p}koqrH+~~heEWe56H?jOCmfytkn^=Am%Wq=&O)S5O7M9<_@>^Jb3(Id|`K>I!mF2gx{8pCV%JN%Tek;pwW%;cv zzm?^;viw$--^%h^S$-?aZ)N!x_>SNL-w|AZ4|juuN`3YMeE2?)_L3Kns~t!?!3*5q zFQC8Q1Yecz*)MWWyvVsOB3CD3=sxix@=}=Y6E7k?g=s~7iE~}zT$ec4CC+t;b6w(G zmpIoY&UJ}%b#ksw&eh4eIyo2a8Bi9^-60>lC6t8~9EYi_F3#1(xw<%47w78YTwR>2 zi*t2xt}ZU?3ftrg+vE!Jc7u~kHo1bl_km=SD{PZ1$lHi8jlEYO;U<{Iv>*S1bAeO{ zzMu*>NzV(iID;_(jND$tAOuUBp2+BLs}N^TC&M!bgdGIc;68$Ea1E!81n>J>FrI%D zew8`N2FPZ~=E>q@8TfmrEe_zOI`ngJb{OpN2Zxs(-g0=~!P~K)qqpPwUbQ;J`eZzy*tKX+0&z3lY;6Wdh#RabhC>-}w?NqzGBoOv=z?X31v zXQ(%->-*;Sd#j(Z-~N7A`up}@*}uO3w*$T$@L*ukQ)QYI=RVFE&SlO!oKHEo4eB#! z^q@}%T^;o8pr2d@yL{l1=i(8}H1MLXy3~h#Xr}l!@3N9th za5M+f3@Bq?wW9;raToCY2c#jM9B}}NHTRGBe%uq7`3PPdvEqpHNa=_VM{GD^ITFW_ zRwXp1(TGOl8I5K%mN_6LID%leIie?lfrya3MB*tDOOd#Uz3}V@$Oe~@pW9=8!E=fs ztse6diIqs4L}DZoACcIIcD??FlwdxBg-9GkVjvR#kl2S4@FC_Q@eYY~NSs4r91`D< z*oMS4B&H$p3?JKvw5Bj2@eE-h_!O}WiDO6>0weN~t?VHgQatr9)_(E7Y(L@=5{r;H zgv8b&{vfdjX%#|z5IQX<)*x{Pi7`lgLAqI`^)N97i6=-bLAq5U-U_WYi9JJ189KeE z^)aoEX>Cl5K;i=u8;}??!~`TBAh7_614s-&;{Wx7=YUa%+{FDO9tqttktY%7j~IW% zO(Iqbas7ztM?62eNg$3NG5kovZR7?E0P{8VXF69PUKXFBBc7Ni#5*B2A8}5I(LyT} zVzUsJ1%FL|T+@(}I6TDOBkmsUE@{s|tUbF z_NWKVX0*E`wwYZrP6N<$iGM`wBg#oV4r~&5DOr$C|4AQv3)2pOEcNpvOOYP`@FcQ5 zc@m8ploKbNI9av#Q`+N@eW{n-LN0qRqkc*@xQm?B%gJlO4uGfH`zg*}f9t0^Ac6X6 zB_vRbR6^rQwrD5D3i2@GsVdkfhzUk)Bw~S)Zx92F_+P~SBJLOMrHJ=MtS{o!5aWwZ zz3I%`-bdO|3XLc2D5d>T?;?hm-Ro&y1G4}n5?6rkwC#4mNyP80*$1`z9J+xEj6T8K zDg35=Ix$PQuYkWnw;goXK{p*>f3q#9XOS)JD;`?!&}!$gms0(oN6H_Vn?=kll1;2E z;$#sci}+af`sajQ#gl&jjWw+5S=2a1^ia1ros3JZUv8jklMNBH{|?O+(7X>c?~Ru018z_s)}FXh#FQeQ6nPbKq=+F!{3!Bf;zm&`5-*Ba zQKYTC@8A@ZHND0smvoj+EGXhY5d(_&PjrHQ4tmjfC7o8%StXrR661+ZBZ=)a37Q{a zt`jkxi04EsC*n8}!-@D!#BL&P6ET~J*F>x);xrMXiTF&!W+E;VF`0`752;KmEv$XvV#d(cuVAeg^T0 zBe9a`e2N%J#781F5^<4;i9|dkVj|-j;($wp#(2r<` zPGbr&%+&|NEW5{QvNd<3!wtp@MF9$?1fH?eo@_Mkn^8EAQeM-)15B)$%D z0qm^zN;{~kvEdl>oep)~s(dk@lRy+eCIn*BOZ=bPNysaF#dhj=)2 zGiSH(X-K8f|2U-L#0HeG8G4=Qy#fPjy=j3!sPG{tFLjD{yJdLvf*r@+Lo?EjM=M}(F);fQ(}m9e==Ov-F2r!5+Y`IS*LZ9sUJJ2Wh||KO z54?xiEW~9YCJT)|#8miKZ@GfF|GYM{WRf?bHMLg#W0shPWs8HQrtP)f3$@ z(Ath}XNYk^H#5XGp%oc1O=#w|ubRO)Mws{|bPq$GZnr8peUM6=61vkPJ_#`=h)Y7J z5Omi&Avg4UOJP@{WX6MGHzq6THC!7bFF721OjhxEkyAkB&KLEcPk z58`?d(}Q>(#PT4H2j5cQ-q^mzyu~%JYY%QR(*LtJ-()7oQRF8U2XQ$5v!As7<|p=( z64K1RW+M(xf0T@SK71bA?e{AhTKQi`eW=xGq@Zz_)`m1f;8qVG;$!^l`s8Qidc3Nn zvZ$@dGid)uBUK0c4qhgKZ}xshoG5!ZYx+GiZ-9=xM}(u#astQqZ_YqXAhDcc}PIoCjh&5Z~cV zXp@bxhk8pYdW(g54#aXGjsr0qh~Gf$2I4jlvw?UG#A+Z;12Gzi&p>Pj;xZ7Efp`qW zVjvC!F&K!yKlvij0h?68CVSy0XJC(eutyi{aUAyOhCT52G5Ae<0%8;RK`&ww&`CV8 z2<+<#T9MQ2at0-2Gh4tuqTGcIXm3vYp1r6)I8^YB1e8p-Idm6p->Z{nkiU~w-Mr$X zwQ3bw>@Kv!-_}7Jy7{Mjf4cRjJAb-0uxp3E!a*F}^wTV2*RGLo_~#C%TcR`*^yIr@MGw`y!r3`6IBzEm(m@d0NZiFV}GUPBo_y zp6=ag2S<1A_Sv3RT~(+l?K!DefSHUriSF3xhMn%$>2{sDf=EO6>h_WTXZRqEKe)+7 zoV_+!(;|*;(d}m?cTl6d|1!$o`SaKLRzvUxvbFbVo!tM06ug_u+IK zPIuvS6K-$c+k7KL_d#_3O?Qm`s1bd&fG&%60(1%X?AJfjg#3(aVZpmj5?PiUQpzY{_(`?=FTwk@rl>5iIqp!W6R zujo;~!fv!W{pV9RTIbu(B>wem={EW&-5}e0&=J(*@eDv?Ik*JSLI!Hoj|L3Ff|A`d;_qNmjzL)<0 z5{_V;uJ}LD-~a0;3jg)9vH!1A(qCfzSI$TrFbde$!45JHx&f5!ba2C1tHP~efvo~> z4*92lwu!cId_J)q#ODM~l=+{HcDn!a&vqI=kDfn%ggmW}!}i~pL+}rh+HKb;j9E;u z%d%D4aBhLwU%;$JY0msh84fsi?L`_xR~5$oKKOCKTTKSw9_lH)Gee1Uus`4|;U)a^ z!y4ih>_lG0`!EIw6Y)cD!}t(y51E4VDraz$7UM4nbMWH@22mka)us5E2IkBGoUhTl zmuBD$s~Tr)H8{ia!5LOP&e)FPy-R*LtLnnJ+7A1jZoxQh z3&+V?EKbzoahgUi+XKHD97C|gP4xM+WJ05f_XfN61Y?sU%kp$>~@LEx?ZwC1e7rZZfW$lIun%kkv>}_2~s} zM+4F~qBI%Gy8vkyQJw?Wir$bp80T*;Nau;4Ua-qFpf7%Uu?BM1K!y`VIu)JIXQHHy z66b?y@Fmj8fpv0Zot|Kwo?xr@Vx68~og8siXg?dRMqbiP$(DA4rH?`mY3YdfSDi<> zq@|3tl;d4imk~oPfLm@{Yt!3=WWE3KmPPtH+S*UB4wG4j^%1PY9jwD0tCg~ z?bGWyDYU2k*M4%bpl{FX{dWx>-D?0|=_Bigcv_vGQ8RGB?$LKIa+<)UcE2kx<#2eyJaoGwP z8K7LiYnbp3EI}cI*Wv0;=q(opD21nGg1ZcF8x!7^fx9OJ%Ft?XMZ9%PFvxHM4Ei?C zb@B2rymbt(9K-v-&`fx<8JrhyJ0rKw70{>x@Ld6I42Omv3yXT;-Dk2qLCA5y3&dnd zImtozpts=TBn)-M3&rrRDs&t4M|3~5J`F5-0k7D?->|}6&?oRGL|}{xTA7e0gOdmt zan=iXcNx8CHCdn+vRx1ayijV7fTL-60h|f%HNz{|;5m2|8u|?!8ShC$7eWuh>!k2@ zG`J4@2hIb(5snDxt#BQ@+YBy_UIjO&chNQp=wEo<8QzwL*QMcoX~HQP-jRk^q`|}B zW_V{BUYLfShwcYYLx&JP!&~;f z_(~9N3Fz=L6cLYC)e6F7ddHj$6%0Y`;HB_p1!~kA2EvJu@V#ud?1;m&4zD;wI2?Dl z18#-h@lD4M91V_p93O&X5!vg!e6YMsepUXHVw%FH)GMbcgOpLqBg*qmYNzo|2~M}3 zc0R7M(Wh^p!F`hY+<0=+lVwjHe6sn;i|S|9f$B{4D)mlvow{3n zudl4HbKfz2=k(p#x2f}@BpSrJ^q%mmXHRaASXRULbbE31wxpvU7 zLFt3?2OSu6c+lU$*0}9D*mbgNj%$-!FSp)qo^JcxKG*iqzNGchZr8SIe;&MI@ZX;v z^z@9U4Nqr1z47ULx=7tV-7URdKV5IsFVbi0cj?dS@4Cy~_YUzMvSLWtkZVJ353vpH zKXk;<$wPgICJtRbbo0>ap=XD74|{&t%fsFq<~=NQSi-P{!&VF{8&)~&zTgKJon58&jdcR;F+z@93LScF=)h?5$}$eGa_X~_K4CEUyiUo zoA>PAXTKZSXQb;$_mMdxFN}I|RMx1iU#K5URPRzt&#H^_#WA7cQ{s8?89&v)kn=s$ zH6DtA+a69+cI3-Nz4(Ok`_8L3^uH)?PQE%uCq1DMpYYHeuMe56cUSs_g!m##Y*nZ& zlFB5GG4%wPWgTB@5_TP2zE}HoSJ&5iajvra{m)*Fj$e|TsuNv29%d_-cdROMv##mY zR9}6xxh^z7C(Ts`goXyhCzS14sLNL_+myX&gWKkLnpJrP`EKgfs$#L9rrmnlQ?cx! znm z!WR?=5f&9~BNcfC`^69P*USHQZmgn843K*$BBcQNL=JzXn2)ep3|OwRPU@MY@fqti za@z9~B-aFz1oq=_1F;`#O|jb4Exm2;zy9@V!hkJZ#1+jqbcQK4%+ByWoGubQnL zH`U{1y`R#*VqQ(ME-WcNaE{xX%CQ$d_^zS7^7wk)?DfavTeR&;^Q^+}^lGUnR<+JL z2ZmkXrBF*#V@y3;VM@(+QLoRHUX{kVNn@L&i9fud zQ?C?1Hd)6by-b|xC{7h`YjSH>*5}sUo#aw=I<|cN*61y+KejA5kg+S?GCwuew+@7>|LQhHLZ4iX(qVqdXH>}{6) zOJe4a@ZzbLtnO>HI~rEL;3i<0*d9j$AFPic~)02FG1DG$|RDl;pCEox97+cI`~x zlB8G9O;U;FJ%Q;eDZJEV^%ob&Vtd}zcy>ROwe<{E?#SAds#EV;p%U*|cW8cosdfBI zugs91(4A9U%|B6nQhVZ)*s1#Gl+)+M&Uo?VIg+50hAR`+EZeUWTiT=wQKe`H6c*Qxif5_3gAafK{Td|M-_ZchLH)_1MH=x!*F#GDGM&^7Ejc;cwr$I4&E9)D@f z%hM&5u1#_6boJ?zr((Ty^OgSoA=7jo%1>Ji0s8SuFYj2NnKKVv)2TO<`>VvAP+77` zVWQ3RfmWHiA#0~jTr8Ee^}MT4Z!OuZ68|D@&|H7-$^@O%R}2?dwo5w{7E7j4FKt$) zZCSWQCyr8}@6OQ=S9;Ej@t*DbahJ|ZAt~~^y>Dv2{qDFZ=+*o3RO0Af?l=9uyGd$` z&dQ%(IW{K3Cw-ntC%&jWcVhqP%F3m&2X)281qJ!5^7CC+7pz{rx?pYL#tmx=oj&mk zTJNtN^XeRTy;%R?NsU*ockC>!xA%cIy*NeQ=IpE~C@@mWPh(e@H( z_Kuh(-?C@p#|PYgR_<6_lBSa`d5U#!D>9a*F3!+pq-Lb1r(`W$ydW#XoM=ikCoNmF zG;8T%CreURRD_#)M~SIts##WO{aE9z3_9r5l%We;6z&=5_J&fD-SYkZ*7wInneI#F zv6zeA>vgBr?Y(f!?UeHG<66d#d&Pg4PCVr7t@Mu!@ypX?<}FyYDBo#z{`|wONOJbX zfm5HHjGeA~MT*um=hlY$L!{@-Xzv-`mF*DNQdieppi>9entB}3YjQ=6<}+pJuE3q4 z+q2d#D$H8Dr>uP4f$}|@50>rRUCl-F@2S2k%`xO zd5Ndml+r0trQwh|W0g43S`M+FTDNMxopf>hgvpbh%h7c#m7mM6Z#kxIJsj$I9OWK&t%o~_%qZ`) z?eVVEkOTV3Ir2ryt;X2(iQ2b6^n6<{O;Wyfamuv<-R{b@+bZ;1o)upx$-Czyo>q#_ z=SuE>l$2UYEop~J;|lavpZyx?v@$#WbpOH;Sbk93nNq+kuosjsf8*B#9} zWDIqa!j<8M^iW-J?);iMH?@?Suj-lI>tTd4t!OD6|LL|NJ;Q%)m!4LnEMH>Mt0!7V zhn39mVB|IYT3c$ii(QjkJnT?sioZKOus74 zvPEL9<7YiN8n1_cZBwXci#b2bL`U(8X4$f(Im@-n7H`;=srN~c&)avvxK~?IQL=Tb ze(l<#HHF&Z(gi8y`j$iT`h=K@7_G^?)RLxGM=VvX6?Zv`ldKII(Zy48k(A0jFS+RS zAVblfFSn+P_}(U09}_=s6IZv(mbHlm9gbGHb-m`takoYceeIh)`iSFdwgv9UC7qxQyUU)W{I7`e!6TTqc(KN5Dj}LTPv1&zLjxKR))TW36C%=O5?KN&i`RfXbbh|PREUC$J z5=XzG`OCWvZxra}7KCkXaLcbK+Pb;aseH@sl3m)}RhFoo`V)KQ=fZq<&(*&Ey7wRS z`RS_{7A|yJoV+|W&F!6YzTf5Q8uE^&__@u_3yKfaMul&yPS($gmU|y+i~B_T$1fUx z)aRA1EX~>MRIN5mpVVV`OTF} zw{)J){vifG|4_WkyAGdvgP%^FRF1*^c+XRs9hsX`b&^u?$_MXH(2r0~xb*%vy6>dz zZ3^iHYmeqT>EKvJKxl2V{zv7py4q%)I%-Rbs=&HW^I%WAb+7XH;eE$-E%}GS{M`bT z0TH2oy0<*PP_))#RC!zJOY6jUJwH=?*V*~K{;IM$^(fk~YR~q@X1AltZ#~;z%GZs_ z|8V*^H?`=UFAkr0eWDoKE)H(*dS85cK(;t&%K3BRfb*Zs7F7cUVc>>s*1pyuntjTS z*)3CbFDeq#Q&aWQPs)J-$IppwZHj$W#(4e2_OFx&GWMoy*X`V1wztYna_ZLX*`AiD zk5$G-rzPmT#K8(N4{Jq9Guq>Y(Jyp}8r{H&$Hj?CaloDS+q!|9kKeiNDGkuKJA13d z0CBjOY7paPRxj%t8fl_Z(!Ag?dh}@I%F}V4{M+rfwYTq3j-k>B&8(=vz`)p=Mw;jv z4^}rcR)u)$K2>}_rR%M?Jg2^@lU`K5+BWrzFWWDFrxOKnga(Dwh!0J7#cbJ1akxQD zG>FeQieq{N&5d>`!}GfINxMRPOy1*vh=km1|4ZmXuXBi87Zg zX_@?-ctf!>bHl<#3zjU((V4?72D2gZMVAp`qI}cR#YI`#g$oxa>!lkQsHFZGsy#iL zw^b?DSw$*;57p;A#v!W1~%_XJ0|w}u)t;Bu(^^$ zxa43E<%WBKh{|=@?z?w#h4-#$JEv86I|X=kM0uXVOhu2iVw#7J?Bcpl>P zqL1cae%+QW`!@!;G>cy;ZcVx{Au=o@Ax2l_Bj34a*T!wymcyX|`fz1cM#$zE?Zo%q zo1l+Yj=%igb=_ETtfo@gTvvBY*PM4aIneEGWpGehP@Zm1XkCk5ERuFDYWkV?c7gSb^D)`{yiZGTV}>@6rO*uAE9&EeH`PA%(O8_sI;+vHz1!vzG`XSGF{&C zyroF7Y*kj?VyBgPD^|J%EcFYXp`D`q{;STfi}i*1g{$4xtXh-5Mz=P9?HU9Tqg$(_ z^-jL7U}OHK{LN0D&M2aw05g+XeL|d$*`$ycqyK@$sP!d|D4>X*k7#weJV^Ckq$+zu zPLaOsYySed`sgar6e@ZcM2`?LS!R7-40pV2ouK(i*>-yWsZ*yS-_gN$RAW!SG`t$+?8GG17vp5LEK>Z~ z@doCtK$_wQ&up!cni8#B@ur)kX@B^4R^*-7%i z{RfkeXw^Q}L~(;GT`YDKoq7^9(JFO<)%xid=VTwZect9MF6#;OQq7gpP2v}jH+_p{ zZq0Z}*%mKXFV0Zq_YBiqRdnC?Iw;>F=@9(tzUM)?y5vhFjgxw5%F5TQDc3JOFV8k* ztjN|z#T{*3t$#Cr;}ZGHS?wNIwd$+ZcyXWXUe9VxNYH-cnw6__3-Vlxve)IT%Pv{A zVd=W%n-_1$+p_W7OD-WtR;t^1RBL$jn!nl@`arl&8|BUS1}8&j~-UatA-v##rUai;Q* zldim)tBcPwWTr%wjO_mLW|3K3V!#1q3*YH8XW?5WOberg*oPEfQSKYUKtrhI1a)pu0F3RCd5sOP!5&6i`I&QPQ7r8 z344h#@abkOoAuK5c4eM;UH;XqE2F~kzro4W<%y+o&xh}}i`NxcMz`QhU~M_F9J2nU zK(%9AdBrYMnNfd0>aFp5V8oJ1THN!xSyt??fig37c^>jRT^DZZt$RI{2Sld=RmE21 zFqft3F*}-iCYWV^6vH)jhZf8))nAsz$S*2h6Fru0O+FC0#yxj(7T{;{NYizgW^W7B#sn`2PuDMPlKXK* zAW|IdsZyJBIz+tNKC4~y#^itY8I^h=cA%rJi!{;UOAYDzWk=+*9!_o-ixlNihcox( zIendb<;ox3;49bP?V6ywE#1+~35g7t8CTha`7A_vxTdNF!*C!B`K-Nd+nvRql!X^R z+j=r)`ze>QIAftnn-sY?EM336LGJw^MWObml|-%e593suRMLlKT%lE%IsYUEI<{F& z8Znsb-96OIX5^%gh&9pjlX+tlL==XrjD3XbHjT4Ob8YNcTf(&##Am{YzowrJ}}aQa<;oJ+r4b}@(QON zOD&r$T66kh<1+o?h|HkHA-T)*vU9VSQ{olprEHh0iUX%=t{gn$q^5oPIrOMKp3;xXz|fRH-JHDO znqzL_H=fcr%2%&`)V=FSab3A?lyXj3*qqeZP1}-nhB@+ts@Ak>ZFlE|oB4XR5gyXu zJX!T!`689IkMnSqx~{ZT)!s7*n+2sf%(=Bb#8;2Af~ep*I%$MxR)|fKi{@kHpWeAR zK3^vdQ$(4J(fS8pDd%rXtI~;M6xZ`V|Llg_4P{Gw=LaRa^P9F`s&|u`9(ZfktSnfW zuUorZA&xB4NF&Al;s~t&f@kCG!GE4%Zmv%K@g`GGkXiPoN_}{___npyENky^(JX}? zW1XAp=lSU$sOd-%=Yh{Ea;+ES%O3XCh+`FBWvg%sF;)?qYK+l8_*xmiEvZVU&YG?& zQHf`tSE-ZNs4#sT7thA225yt`6$uHcG5UvtI>bMUX&q9Un5`7EF-g{ymtry#uWCe< z^qRsmZ)Ws#?X>At7xMILRp-xCetPCi*qgfZ3h~Lj7tf8+zA$!{+|W z{&WJv5cZS@Q@6+HqyY-`#x15EZ?x(U&OI|cq^nYlc-7MCTN zW|pn$)h>R1QgNbX{)}>czHe~+N`lr$IbBfXlrKM!vvuL=g>4Hz$xa;n0TajV;AhPLmbs9~j?E|G;T_hJTcg)<w_MaK|v zQ06CN^7B7*dN{be<&F2Ry`qx_f20uy7s#6r2m9f0$$wg=D%11C)}(_dd5jK2%bEKv$T98F%H5B$s1N%ZnD4F5ci; zR#AKqi+`^Xn&~sE+Vk|*r*N)~*4Caq744x%?uFUu*;%@%%D}nnmu<}2u-J8ZMp}+V ztKJHC=^51P;h=VLr>Ic2b{KqcIMD$gXb!&o(R$sd>l!Z~bNfa4rw=c_sFU0k(ZO%Z*Og?KWHd>N`j>)&e0CUYU>)~EwRWH6Y3&(%zPK=Z= zxJfjKe-{mo%gIkBJnY-i(^q^0%bpsHj&Ww$XqEZ~76ev#oGQ(8>;v}*Y3MRfmt_~E zQP#i6KTvG)EuSnt{h-n*ws^;a3hnla(*4EyJ-_UyV^Hfy@By_LM(c@-&6=3PZIx0(K zE16a@v)J1ycHNE)8ePi|6zbO;Ed02zdi{l>uHuVpu44>d{hD088RL_9%K4JQ*b;R$ z@l47m$tP2r#n(mAWs)LvOrRt;jEVX(sypiQidj2+xA{3qr_PHfmFlJF8i$?5BOOwOLfmgXD?hv?bKe}T^s$GyQ6cSn za6&#WV@oX7nTw@PlX%-yW9pfR{&GruU6XI!B=?lQS724)7Zg@|GzaUFL#t}b-Mnb- zO;;pW9L%ZI9<189ZKr;kQeCu0CF>A7HKJBgxT+&ZD`~~?iW4V}wCg&QBiE@!FY%J* z?;XSJk!^?^wN9l@#k@Wi^ZJB$-=9cJtJkkg`no{3FaPkS@+v2(sJ&72_Ie9vEn(l!UjxA$-W_TZH*Qt$<4i>Y; z7c^t9e01Zh&MSXetMgbV_sliS3DEk79B$E{R356>bFf@@bkF{#vu>iZm*lLBHYX&d z>+e)zIUoG6ZU6UWUJ zUmf*FU1Wg#3y;InGty8uX-Jsl{?>;&^{7W@r|Dw3W?RYXgDD$uJo;hHb0WvZqpv~Zp*IZ%Jf~cBVE)pk?S36 zy2iIUXL;7b>}9T5nOO^#EMB^3>Eb1e7FjaV7H4Fp=dCEQKtg_Q{>p+pv7hv=OG|xn zRd{PkrR$2d+3RzPa#!aR=B{=U7oxpqS1+=pL@%_sE?-P1=nG8?%$a87USP@3-jGqS z68Cp`Vtcs z>J@`s4yBokd!I_HRuiL=qI#t)FqsywUAS?%e({VW*#(8`E7p{{t}Rt>dH8r7&3 zpY6a>$$zSf7C0Ts2X)doO~;waGiN`I_E@NzGA(-gj2V^Zb!xH1zLm3fV1Mz=8Uh+^5|1#iRwS*!OwB#%j%q1yGm z@~f`%-|D4Lr74=}bK?W&2JSnn6Frn?8V@uzHpR}+wT;$%$5$B5bzuSixXsn6ORG)R zC1RClj!KmMD%S+hDPFoQvt+63=B(1Jjaj9ON>-I^yxHLrbkvfWnU#^bD9crPPb$@n zRp|z9y%W_hF5o%~9RPnsr{pTC8|BEOXtoc+27qOE+b0SXEm5Wt&TAm1&V_ zVa6g?aisL#I4lD$L84rV^B&B^N5v-;IMJ4zL^p+JzFg8u zPF|vubX4lC%*UoW`D1_8y`BM@;P9fH^339F*Rn-hGE0_h$|}jvE=UMqyJ-tG0C8+Vwm12`3YlBy7ss>UzGs57s=kl|ms+IJuoW5ZV8Kim}&iG9a&N_>X+fuZt z=@!$HwF@@q=$DSF&qay}kbrwlaZb-Li>h$R`dqzvY)h{Ey?YkbYWkK=-_&{KChIP- z_%*XE-a{q&eV~%Ih>9UtJg>r$S(eG_Efxd4l%aF^6g+AlJfL3(mqlIIeQK)?izD5$7x0Tc`(h%D|9k*ymVkX3}H zQJ}kNmTs122?=6k5f#A=Pyx4?piyJc#2Atp=Vg+-)a|+W-kb}0@O{u!RoAVmbE@k6 z|M>xYxav&HqSl4Y3u!4XD8VHnRkItLfQu0MNT&j}RdcS^)T!TYcZSpr5c`c?X(vT* z9qN*Qbc#5Tyf-UB77^3cS0w-RIraF8?pSywVlj`(YUxg@PXym{sC9 zA9#RGkRajLd%zFMAgoV+_#JwDLXCdkv0?16+P=;GU%iTI$~87S}Hb0SbLfXhytjm;oM5%Fang`I6Lp8gYG zMn^#`zK~P{zRVNxr87Bk1OXrKOdd@56JMGSb#N~HT;LsdKmh(e5}Yh)OHdczS%C_D zkte!}yB*LBraAe`Dma+l6lsrcUC z71%+EcdkXk9=tgM8DTXZEAV_Jj`Bfx|3T;AtoAx)PG>z|%7FRN!fu z@F=MgYfM7{2s_P(zyeRPU1u_@Q)`vg^Rjh^b@}BPhMbbjY{!jupGRoR>uU`9c{Wj1 z`s&I`El?W@o~XlRCUXD`#3pKtw`Q-C_kZ?pBcv**n`MHoPe9kp=A)bZQPz9!qa}n* z)GY;y-vvoktQYKonN+}OHu@IYj{k5#`=ESQ-8p>heUn5i{0cX)pZ8={D{9gWFrFN3 zetD)ouOusXxv$-co!Sahl|eVppfglfR?yO&@McL}A3hcdZ@&B%ip8J6a322b3&RmR ze#JD1kA*?q$XjR^@5Bb|8DDf=fdn)Dp#pZTFB-S?=Tk??75KlvgUv7j*ajWS+W}}C z=qUdwsOsglut}o4k`-_}XPm z0lvq-mW3Jv(0i~7LL7hB1En5O;D2KMuS&^Y7{sz*6u|Qi2-rY=X&p{5^@90<68r{I zjGeuiV92y8Fl4qizapbDwU z1ar+SY%WN5z5*fA#gyLo7S1#nSfef~%25~1D=H|?Ey|~lXQq{A$lOn#Y~T^2+w=?#j>RX7eX zi*tb2vqR)p&jMQr*e3XUTm&n$8`mXZlhu1jIAB@8CZ(%VQcPLRW>dAfKD|B#aJ+5e zOj%1)EtF-{19u&@9bEte;JIW)$!LL0mD$}Ujg?3WCyB)mF5Bor_Ce+COF!9EEj8tGU+>Z?a!YeYp8RaBv7{kNiosFzd#a zhuL(bm^Bd77Vj3Y9bLN9Cb@PeamBL1PpBZ%PexaNtfAdX*R5M+4-of*aj$PVN65YR z_nsOU{%n6Z5R*|<^sar|wng{!aZ)}Vgm+uS2`%;zUfbm0DKKt=-Fjg*{S{gFX-iK_ z51tD$s$hJf|1li_(!1i1Qa%E)-Hv@om@GljrAd97LCr<;Ry0w3P88bl$An)+ecngt zz0$~iNID62@WUr#uu*aekFr<-czq1;x&*tDmoE2?0-yc%3U4R*R2!_UBOYP@A-t1} z_>+;;Qa7)eV!q9~?@ZSvEhm&Op+M63pn&xQ+)ZkquubU=iX^2XIk7&oxxrM^!b#KW zESoiLCFIc(ifdRI`+u*#!Y}y?{{h=#Gb>Y+-NHZE11a*ESuZ4SNIWEaC5I#mNv5Px zGAJ35{50Erw&!fk?5}4(wz9Jdx2myfwYqNg3kYy2>$%qc*1^^h)<>+>)@Q9pt#4UR z5le|cB9w?AVu=!>lek0-6Ss*6#B-7+mym8`D7lY}CzHryWHs4Lwv(sHi{uD-n|x0G z27e$;LcK=WQV!H2%7yZx{HY)+jEbc8QU|G2DuXJfG*lU-r5dOX>MS)t4O64kZEBo) zKs}}=so$vorC*`{Lfg@DdI9|o{Vu(V-arS^VRS6LmyV+o=rlT)R?}L#imsia%5O6q=*hM83<<{{W2(Pgvl&ALB;^1E~0S_kmUJ ztHR#o0Gz$`aN8sC+<|B^FyiCzi10tr*ZKCo^L>E67`CS`qoxd1&pWI?tjnuV!QGpY zy?C8nXPB;{p{A;G9)n%2-B39|F{dj|j?N-?3du!aLa$w&28%)a0|P{`$iUWzbj6!6 z>0cpTz$`^O(PqTv=0YqgMhN9cv^o0ly>@$=+mubRx|&K;o!n4$yil9N#3P|}hJBw( zrIvr?`5oZ68HgGR5Vf$BVBiW2@D2>Ag0zB#aS1Q5ee3Glk0rk?61-4NeRF?WA4Mb^_fAGh_`wnP3;<&JA;-d@wIRmn1Py0b`D4gYA zV@FGe+J|IUhU5I@!d%LCOZ=9=kk3Xq=|bd_?NM%Pbj7nIL}Fo3#%z6%!LYrQkImQ z7N?QFoIl2!sKdYB6#^a$DdU3QyfkfHwoztoG`7nva$l-!>Qs#*!2kmvGAZUrW^+s- z`)u^?w{kq;FHBHjyJEw(?*bf!C;Z^Y6*oas#Ncb|5njw!vZ9_etuC!j{4hfnO&&jG z0Il)Qq}E?uV9&7X;u3YSeX;J?QG$__jN$wnR*MiLbK9_fC8Vs}zF_cWypdhKIVK3W z{hMkIG6Oez`do$tJ%QV zFrYemV0u2HmTX|4JK{sQ5MQXh?yr=&t@n173qBOXV((E1?cwsh441EYlwy>jVDRIk zNc31X+ZLts0NQ36vkWu@N2 zeR%xZ?qS)r;f|YfahFx=s|^MS#k~NlF<5^H>#P{KV=nYWZY?lR|Zo0_glRjQ1QjYeaAqe+#@ zNu$nzC}#!ExFKZVLW+S0l`%j$#NuY)z8DdvPYcSJ-uOfyH`3B`)6(I*^%Uk#2y@}{ zbvMD26Qx$3`1SYU28Yil*prhMvhVp!d)S!hVO_EP16|V|HY$3Uzn2TgKuX9d1P-}r zhdg^u-J@>Zt`4C`Y&FlfJ#;U9PZPOD?Ml0-2izaY7!ct{??yc{BK(n$5i{3j7NfAN zQA!wm-7<$23X2J$5NG%b8Nv!`TknpG97v7fn8d#Lvshd$VNPG?#y|`KszXM^wm~U~ z_YpPc|Aa2gwY6}OK#0ba=am&y=Ja>kLFXY*!#Zbu;eAh5Q+w>VQC{#RQJ~5!%#$%} zTw#NvS=MB%Yt%CArkKZq6)ajw3^p;PN#`|JN-s6Sj2K*HRnGrU3}TWqAatnBXo^Hk z0(x{1F-gE}Cf8&%Ljlm7N_Yu0XKhoxNOLlnSKzey*qG(rGT%q*d?H}d*9O^sIaYkuC;)S&lO5@QK zc(rW$cO@8$JCxvmu|=V&kvlYu5RX-s`8X3+JvB|0m|U0M$Ow6Ayo7ItcJRO;+#Q1( znG~af;bDMSFJa!__>O$4`v%3FXlrWYa4E?oChku=bRo))ISRA6Db5D+&Yn!{!83!U Wa%oF@`rc-_RAKRgE)|vt`1&6$fA2B? literal 0 HcmV?d00001 diff --git a/images/.directory b/images/.directory new file mode 100644 index 0000000..a68682e --- /dev/null +++ b/images/.directory @@ -0,0 +1,4 @@ +[Dolphin] +Timestamp=2022,9,12,23,3,3.4699999999999998 +Version=4 +VisibleRoles=Details_text,Details_size,Details_modificationtime,Details_creationtime,CustomizedDetails diff --git a/images/dice/.directory b/images/dice/.directory new file mode 100644 index 0000000..737d67d --- /dev/null +++ b/images/dice/.directory @@ -0,0 +1,4 @@ +[Dolphin] +Timestamp=2022,7,19,23,15,34.73 +Version=4 +VisibleRoles=Details_text,Details_size,Details_modificationtime,Details_creationtime,CustomizedDetails diff --git a/images/dice/cancel_icon.webp b/images/dice/cancel_icon.webp new file mode 100644 index 0000000000000000000000000000000000000000..186c23ffdc02c7a5be95967843e5f4dc9f58d4f3 GIT binary patch literal 10958 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 \ No newline at end of file diff --git a/images/dice/d10black.svg b/images/dice/d10black.svg new file mode 100644 index 0000000..048209d --- /dev/null +++ b/images/dice/d10black.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/dice/d6_1.png b/images/dice/d6_1.png new file mode 100644 index 0000000000000000000000000000000000000000..ff0809247f42a2ea3ca3e234372df022ce12bd5d GIT binary patch literal 1559 zcmcIkZ8Q@K7+$C?t2nsDB+``FaK@sEYRKH$F1tqTnr~tq8=K3@%14v0D7WVO47*)X zhOQcxG!mU*X`L8}xNN0l!>)X^JOA(B-gBP!Iq!4c^F05aTwk2`Ze55j007wSgYm>` zn7IYe4$Uq~zi6c)9U9ulAEcQyP*jfQ-buv-(EtFg#4T*gfqFS;L=(E#dHS!E7(|9XguYGp!+I^dk$?-_-lTU8Ezz7V{Q{#L}j;H0~rOw4~X*Z*TIXlKVUshI-=o)=Co88>fl0kJT5C{Z) zGsRG$Z0W;bg)>M;OD>n63JNOA{dd%OT3vF%$>y_#L2izoxjTqI4o8R;Df{XqM>_<9 zUWKAXT31~ml}ZPeS_7B;ZLHjxYtX#>d}Qy?6YD(79YCNLY_H?`?T0b*@`i@IQx-P~ zp9ju*cx0`vt=yN^vF{AXAAeieTwXT9VzK8*q_@eFo14rh%=NvluC5&mPTGJ4ZlNk$@!?L-# z=C-yA{SUITvywqM4b#qU@o{ii(~OA8KkGS7JWOB@&5?tE(eD#^z4< z=xFJdD3zl@14BdQh|7tI!;ALE+=7m+EiCZjIH?D90s;fEWsN}5nb4ph##_{Nk8Ba4 zptcs@HgUkEYP)#M`|F5yL6lTYWenw8s+_!@ z7XDJs=kv=$O=vEaL1yZ*{Cw~rS(#zQwav>cE;hiDUw;%lC{z~^CNFAU0lp*>@1u&( zPXygsn^i>N*OzEsUaUFfw`UKBZEbBe(K$}MCa%6^6+=g{x*cCn#I7?sNo^AeShA88 z&0XKTGDybP>-ZxJQa3121Dk||ryc|~8o_8}G|kP$XtFh5K&_0}MnlURQA%BB=MoGw zLVU>%n9k$zVjD$3_Ko+iUZn>%k;)K6c695bM;g{+-`E2XhP}Go&SWyJ;c#U1;{!0W z&aN)=M4S-*q{-&} z>S{nwPfx1)yPHa-YHn@4HaLi_tg1S=f6t*?m{aWN)iihifB^UJX&sYa`Mu8MuE=q@ z9@GaN8F>g|YRXC5P$~`VNl(%x^_zo+0kP}yuG$r>@$V5L8C`YxN4CiSd0$zb%@Bs^ zvsh!_M`0q0x3~8%yt9C7$5U8kEHz`R)hu1`EgQ!kRH+^aGoO%1-2m$=dcBi_tY9iIpL2n-hJFsx{Iq3q^YjH_Fe!0{{15z zsnEE&|3s!{gxj~u+_@+J;TWIGKBCSAd%?sK(UcU$c)F#}w1Cg~&?7g7G>E&`z>Kt{5j1YOz^3Kh(Scv;hCmmnsl7Fp;%>ID{) zYy}8BFi7kdwR7@?_6OrVhy>@oaa;!BsCFHjyexk5sHvKz?qX1=gXy={-bS zIdPKP<>2~HgT*-c(rs>L@4x0OR4+S^xk5 literal 0 HcmV?d00001 diff --git a/images/dice/d6_2.png b/images/dice/d6_2.png new file mode 100644 index 0000000000000000000000000000000000000000..69c29558041f7920404fd87e7ff9e5cf264c41aa GIT binary patch literal 2145 zcmcJRi96KWAIFDcCVnycS;Cksqik0Qm&uYr&BUd$FVnS*!KE?GShLjFhS1D}kt}5i zWtkFlafuM3=^7$3)?cBKB_opfeY?-^U-+Hp`JB%==kq-8^E~HsUY~O^E;(E{DyAR? z0)dWV&^A{9pYYQlqCkK9MyNXA4wFzAX9&>ZAvh{9KSD%%kU${e@Si3`McC>Ai}GY! zcd}C$o*eBP5deygj@At(gpmAwi2=G{5rOws%@jZ&@HEE8$~mU!b8#5@-$}5(nMFgz z2luN->TXJ*Y?A&qB)V!B+@}X8Ra|-zccsAb1xt@sc1l*(mP)4_##o_{PUwGWUl|yE zhNA8(zcYT~Dc&ouKD}lI{kFKc+WdWbprcf7D*5$V!c5p#F7GoTS`1_Z^_D7&6hbAS zk?AR%BvpNVDHRn#8`R$3!C{_1-jH-cyny{%bQ_PFljGVZ*F|e>MZzo-_tyM(?|g(Z zI4X!@YuS~4sma-`xH&RX-^fTLxeJus)ft*zSP|0G-w!FgcW;=>rQEzJ1W0newK!BI z58zOE{K*Is$+vOh{y1M|Q0@RbMPQ>@Mn5Q=OS8YOy=UY7t0xmL=txe!5(}X`8>=#PTHZ$DX&)XSHkRY z`2|Mb9kS>%HoLsM-1_a%nLv(DRFo^%ol&pX*x0CX`m~$*_UHHPpr9a{VSa_Sy`!U~ zC>U%sP+wC+H_WNf&f*cqb-sTXWD-qa7JYPbYH)Dyu#T6s?X%|QUDyS#OwYo`?2qr; zg;6mZ^Qmw25m+m4UFMh!B2sx-48=9=DP5TLBm?7Pii(dTMKDU}Rtf@v2*L`TFCSh! z&Zt*DUoJ&a4w~%38GM6Tb7c?)2FEltH4CHOwUDZQ>iGgWF&dN*7w=x>wI$@JEsK>` zk{`d#tghB5T|l?K2%UU(s)aG0T=~4t~@Jt6E7_* z`zT*dK_OGytsV_`=n05mu^f>dQR}^|ip1?ztYB-a>yIYU(bRfJx4Sz#F{7iSvy)9BODiiWJVGCE|L2G%dKMNr zGtZOMR8@r^yEUXugr>d5TVK5jXD}Gj{Bv9*RWmc##Kc5DhqM2^NF#GSu3achINmC3 zZ4hDwEf5EzkxUv{LS-2Sq$s6=1i>u+j~f8F|8HQ305<`EtQyJK*a(k5*jEC0(YtF; zy?uL|f3i6xLPIMdCgyl{c6QLUYrUPFon`(g6pB_+Jemw>z6q%#eX(P!Dto zY_D89H#cShovy+uwR`2)IMMTK*mOS!QQ_p}b#~E6GU(na(S(+lmwN4e^Mkgw_YJ0S z%fzhQTs@di}bVmV!DrrR)}%0sv4T z9>2fmF>GLMZH;zx>|0-Fm2Dm)(SK=gXpl@zP2KsndFqx0vBkT8V4#Z~Hd8R<*q>`* zY@7zLac1UgPF9xBOh>}b7GG`zc9pK)UQuyaB0brKX-bZaoZH>vKf-OK;g;Lm+k*%M z{6v6IUS1xtjrR!un!>hd7Hl2$R5e8$m9_5%QmMSI=heKrEy5$k= zk^ALK#BUMF@&i?_+FDvrC8gI-2Zgy@E^&R<%H>S#xu*i43KX4<&Iw?eE|HPm?Qt>` zA>dcOw-h{(8E(LBydelP3cr2WT}`?KS{5~9gM#l^)YQ7Z+0wq&G|F19u0 zJv&Uz%*^{qY)0x%f`?N~jL9cHKm1@n0imug;{4)|rn0IkaNwJgqd{<#g3wH^vZdvT z?(XjD`udkIvlZ1Fsz#Ps{(TZ=@jFn*Qj6ZPIvTG|9l1koORwkWaj5{7yqNYz-x;p!Nqq-oAMO3rO*FF_rT^q1a>;Bu`u8}0K(Wh K*wmwZDE|d4VG2b6 literal 0 HcmV?d00001 diff --git a/images/dice/d6_2.webp b/images/dice/d6_2.webp new file mode 100644 index 0000000000000000000000000000000000000000..9a37e7004cf6a8caf7a4726f06300f1639244768 GIT binary patch literal 688 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 b=}ySMK~%?)jGK}q00000NkvXXu0mjfCunFA literal 0 HcmV?d00001 diff --git a/images/dice/d6_3.webp b/images/dice/d6_3.webp new file mode 100644 index 0000000000000000000000000000000000000000..69f91b1e8d59879f624ddb6ff64dc3140ac60cc2 GIT binary patch literal 952 zcmV;p14sN)Nk&Gn0{{S5MM6+kP&go@0{{Rp7yz9CDt!Qb06tA1j6@=$A)32fFen7X zw15j{pA+@@{kq7%-@njsr@SllHug4G;wMU;J%8SMaeu-6_s*Z?|Koq>|7Q3K{#*Tb z{zqUBLm$+$m$2yMmh>lxb_{{;o*U-!O6){zNACw&bQ!zq5E6oV7o|n4w-acxBJkm0 zA*f)Bs;uD8DI{p$z>bn)IsKpOn6+5uBf1$W(nEpQa7DOtpLX3N;DU z3)Vz}CE(s*Pk7N(QfhplL@dBdPJa}WDV`g_dsOhN&^zE1{bAwx5wpLq+n?Kh!GX@l7=f^=m4kv>It4+v6akI z|L?2*)JUY8vqGMc1L|l@Bt%(KD}#e0Ekz??`zA$WS8%U$G%7^RIVM<1hS^!gK&SZ3 ag<$f80001#lio%E literal 0 HcmV?d00001 diff --git a/images/dice/d6_4.png b/images/dice/d6_4.png new file mode 100644 index 0000000000000000000000000000000000000000..58a8dc93cf2f8f954eca548cf0281ebafd8f768b GIT binary patch literal 3139 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 literal 0 HcmV?d00001 diff --git a/images/dice/d6_5.png b/images/dice/d6_5.png new file mode 100644 index 0000000000000000000000000000000000000000..d2f754338d2083b61025de89a8781573ba9c7de7 GIT binary patch literal 3793 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 literal 0 HcmV?d00001 diff --git a/images/dice/d6_5.webp b/images/dice/d6_5.webp new file mode 100644 index 0000000000000000000000000000000000000000..ac3c34a021dbe21ba1331f99c2b69a28f613592d GIT binary patch literal 1502 zcmV<41tI!UNk&H21pok7MM6+kP&gpU1pok0A^@EMDt!Qb06s+?i$o%!p&UI7L?{GA zw15j`e;og1?GO6z3Eu05Wq#X!6NLTZU&+77{Ks*HW=lx%F}4}XdkVu-#idOf;P$4 zowy@bP6^AX6Om)DQ#!)FgP2gO0=0Q_!yn6Ya6T4fgh<7!EqA3HEA+0-LsPXkp@V|@|LOtdYolJX5s^kX@SGNv zadgngRCDnM{`M0*<&vkP;Orvf_`ChB#D&`~w+Gzgx${xy#WJu!x?B$m{MJxgdGGX! zxO+Qu1xPv;VvqJ;OZz@T6QnqStHfmV(E>tWQPGCOJ$c(290l*V>lNwXklsc*+8jHi zz0}MTDl^T01uqA6USGI}%%Paab9xgod?_WkmKDqUdNjOu@WMCn^&Je3N5htBxR+sG z%deTP;Rk=8Bj`6RXz)cNxj?qY`t2HsGAM{_Jr&5pz>(mKpjePo7GXyicpn zUJ4q~%z3$}|9!D1_=@G_gPEDahNpgJp!8ZJl#pH706BKB(xYbxpXCN zos>62z4^1^5Abd#4~6A0ITCt9(Wof3pVJrfT5OX&Hn!jF^A5?g22BX7gu5r!3U%-# zQ{YEg-xe;{37{#boR+b^9S+qda80$FQB8h@Bv3wOUwz~b{Kt0sGh@~vS%*W%q~$ULa0r9oNo z)LpL`E@CaOY$H(qqx?tA{$8`!VUZH_iPHh(1bp}ds-9T;(fT}<#PB9c<9(ns89 z;r}~d8Y(teCS*#(fSw_`9(!rxK&eKlNQGiFPnEUauY zL!uXT%dUqVd*sJimU`4XTSIM&tHgfeCV8DA&O5}P#I@`0SEPs=jO>Ixo{HcoamO!q z{4@&95a&8$R(8`KJjDXjUcNhhN|Zi44UVenjTJ5lLUszRsy8fSDQqVbu!8rTq zD(qo4#?!$*V%|{ftyE+&Jy{LED#DFN3zkNTTdyEnSsO*?1_H1%;(Fp~+p4af|B1?Q z<^4Q-+s;2s;@>A1Uyuoutes33W^yo!;b9Fp?Z;ERMb|Nnvc%r5T`RuA753V~m!fgHWAeegJP zh4#C7GnJ+7be&M6oR|46&B}>v>;|Wc7wQiIiww!v{+2%geraR<7nBRi2c@$3GR{Z% zzcqd>x&%=k4V!{uYZv+hlKt;^#wSCa2{x8nN`>uPrP;T_e{ndlV4I`?KEuUul}G>p E0NT{<*8l(j literal 0 HcmV?d00001 diff --git a/images/dice/d6_6.png b/images/dice/d6_6.png new file mode 100644 index 0000000000000000000000000000000000000000..caaf47ec740663073b6e4d55556cdf05d174dafa GIT binary patch literal 4196 zcmXY#2Rzl^|HrQpS!IOK%{Q)*nT*J|GP^h9hJ1B>t(#5uC_7uSyGB;_y&>}=E8-%> zb-Uk7$R;C^k}bdE|NY;`*TT=#)6p-`&c^`~ z7#Jwy;^F4|z|PA-#?!|scSDUI0y#g2(79#yDDQiIFv@I6puf=JKK&b(_4?YQ)InA{ zB)c?BlabXxW=YwBt7k;Fhu^W@yHS*}60Y^ZUDT)Vg~y4-%O|sE9{Rw@^FrC((aub? zZIV9YEz$!Hg)@TZlCOA8`wj;Ww@Du6JU-F1-rd(AWi6(?ZzSKVH3L!BQ&> z51=gnm27%qH}@`PcGmX9vZe6i#fuTIU%ys`OWF=P1sNNkm$InU^sowmEmqcaMjGZS zP3@0|-6q{yFaNJ?sH8RYFf@#_26bGHjg8eTAxG$yq^(DzIVnSq^8d8@F?oDxp#d)s z4iCFYr1OnvX?g|*Egco*3^86DzMpOy;hY>x*Yipip zVP+Q1lNb|ty(!wdrk_nf>7`Z?yIIBiBJ;|vt|xTu7*oaL-I?(I7#7~ANQ_T!Umr$a zpK%5~LC-5|-FtGp8E&?wprYjQCEl?iM_faZ623RbINa>rsa;9M+`EU(K0rf>M51!| zpO9DnFD^;`{knI0+S-WC%;03DBn5_`g^Y}k^BEVYAxfq{;;XC6!fXwo2m~T;S-02_ z9$5JYco_=)p{jiJRrWmR|iw!FSog0%5zp~-x#zried{-he8pB3}b@Tg~$W1vbm~aR)M0G>r z2ha>+q8SMUdLg#F@|jlu#HSbKo~5Kj5ixesQCx61e3Ua@!Ry6qsw+O8BTBENE&;1F zimHr=fDEhTl^-4+%Fi5WrpGI?v$I2(V;#$Iz*>Rz~d^{TfvdkZNJ@cpEel(@x3`}zI6W^)dXxVSic&B`Lbi;K&rDLQQS$Bzc%Yl|x@ z@dN@v-j##kQ;x&kZqoVkGhe7fk*Zbm_rgle#>t1GIKiZrRKNd^BJ7A~%jH?(*!=xzV}DTLIjpv(z) zwm4Z@IW{(;qTA?ffqL+dL9Apk z#!xXoC=g#)Cx^q~BG%Si!H^7%&CS|CjBr#}P!J&bMvP2MOzhm;#G~ZrgeUQ~O1a%s zDt7Ig(RUf$4z3tvS!8J`%+W@7;yMVR);Wrur-A}wS{Zp{WP}}OCgJ7dlP;WR2yP+p zlOy>?0^teSnZAU4_VVS+FUiiD6_i_#9x0cWmPX{}3J|jYHS~P=Fy_-I6LDGDxNl=M z+CYq0&z+-%M4+n~lJw~8?CersykG_qc*fY+c!IO)a)bL|uL!gx zr0dzUXMAm_zb0;%T!CnU%yxHiDRPer@e2rG_P~kThwRP8fedg63Jz{_Pl|&Kv2WGZq%x&*_I?n>Ce7A6R8D~pS;;A-o~OIVlR zXO%kecqSlb4#qGTEZW&WUU6%!vS!d)EMhVdg|@WZBn#t~Ss`7Wp@+61A$fi`713yP zcHq0&p59*iODaCSuO4vac+^>BH$m*~>0HTE4LsM{)|NvQTRpF1f3Vo$ZGH(JcB!wg zFPpgB^FBi?r>~}}s@=EdfoQw|Ja|Wn`<9kmjsb|DE;G8T)z#Go3n?inzD?Z1*QfY{ z@GHNAfT-1G21qy9$(LJ3F>r1OP&UKtg(a(Q(J_tIqU@*JA2<9d81Qp@#9E=~{8u+W zy3EV}+?DH7=KL!bbWJEpAxd@8Y}Mg(Dkl$5pF(Fvg;>6@x~ORE)4z&Z-n==DM^m;^ zdwct9NhcrWNfF4h7yjqcR=vHwXBQTbqN{lr85zqu<@B)K-M6-Pce6Uo z3&RdpL6Mqqu3JfMh<`JFskp6bnN+>fXHB zA09@k^a-b^VH(cQLInhRqNC}@H%ba``n&2$7mp2uZV`FwVe?{ES^Sfa4XzmOm z9VLOaK%q2Crp>i2Ed})S^i1H_ZXXV#Cg(797T4f#LFlDRIFb2{jbsN02aKCL7HrE@ z6?Jrcy!E;6L2n&?(?K=tNLl0PM^7;q!pMkc%CUzB_v+QVy}d`b6n*^tiQgxi9{|az z$xh$SOL(H^VGC7nnYe%o(tc?ir>89o0y#J zw!Nn5R902+Xe>F+uME?EnZisd&RQj zqAu?4z3uI4&z?SgvBlZj-Az+cTzpMdmQ*YCdLTp_LTj=+WxMxxYBBr5#?zU;#xj7HueA*il7650M+nr4?AuhnLvyD z{{0(AS;dXI_}Du+aS^ynFhSnla=-w0(r;^OLX?!0KH+Zo0(C%Do8K|1XiHkwYFt%R zR5T0jH@u;xrBz#5nP~n!XJlq3Wo~XR9gGw5XMec?$XIK)ZR&cp>sowL65&mga9{=& zD{?0v;!(&&rBa7Zk&mQtSmmAofox27STe(5%b&{bn2qtJK8ucy<~!A}SFeyeA|s&i zex90=#@ebXH_!vgsk64lLg&Cj8`>H(+yG+RdO&K}qQy-n1gSce*W5 z_7q8KtUH*oSZpyp(^9Tyr`;ziQXG)@M@*rLFN1+7*D~k=2tt>I6A8`}dGPRp`yrjR z>+ckmlo*zlmi{Bbvyl6_Qf{_hUhHdYYuT=#%FgC#+aC}Ud5NwZx}l-!l)X|QXyc^d za!S{kidE5!)dGJHp9h}TbC>m|zGb=(XkC5% zD5HvnJbn8hB$Bxy)A~-9ObmcT(YhFRE-pF_4h{p%+dl{2bN~kL0%`>8ayK&i=dkML zwFJovK==koq%3t0XJ>JHM@LSQSZm?*POdB$R(hf zg}|pk7Eg!nT76nAJ|)Y*O>2piB*$)$>nyGWbf$%6llv3@Z=+iWa2@@d^WQUtL4D6! zk_{vksGD4hs2A#gaHBA2?dmWgy7=#&7ekK!G`Z)LCV<&LRjHdYv#^L*%3Ov*4IA^G z0jLwW;bCiF@C=q<@8B>uFmN_0$db*(o%Jz@NpEN8cySE^R0gD4c7DF0G*hRgzkAZo zPQb^30d^}fK|w(&aq(II$UC8Q5ANROkU=-zHWLE;Yhh(I)SRr8Ffvr|Ynjv!RSkIg z-hLixTEAg(=FFM)06%bGe3qD)``ct8?CZ~^57~ug?={T=?Q?pk&W~ z@gk(!oCRH-iT=g#UVu$~Z#Jsd|C{-QZ19Y*leTCuDFHcf|=l&=6?|_fx-_(EM|9$qc_AC2O>@)iH|J6k- ztwH?>2=JD2zPXjjzT^OQgN+{02YvLMm_;AZ<%ir-weX}EPoaz)Ly7v< zA)9?t^krhr52W;ssEJ$zYDbDk2~WGIWLfn90RH!5ApWGl!lnA2ev(!D!uaKNeSFvQ z*S$MhZ+$@@{yvAJubmgMZ;S)M_Rloj@EMb9FdfJl$Hzxk>y{=Uy`~DQ(U^HANyB;P zwrz#B#}P-k1by{%G4bQU{Z=&GX$wG1NuqzgY%Um@2H5E1%bITmb*V6da$$-*s7d zz0{tCzCG^fl;c>)a45P^5>!h4t%E*(0bjK}E{w*wvL*ATx=3b7-=w=@Wr=f1(*xdJ z2k-9)EUJCzB5kKf4KJxOxODyr3VW2!X-Fyy9n=P?x6Hi}Ak}(Pf{Oh43ef(YJi0-? z+1eDF$JsCgnbDw&zFLF-HVIHNp8@QEGhI&rclYree5VvV84X~Z!Z_afO_a!l9Y6g& zGH~Ch4ZeaxP+9XH`aRV?)Or}R#$9kdxuNZi;j{cEHuJy>1fGXn83k>Uuam%x^sj#R zj%sy*K$fp9hQijXhfr=2-~ar^QYL@?G3|HUz&C5d=8-Rf{6b=Fd?DIK*4W<#uZOyk z>(YhRQ~5h!=uD28;0ozU3&dFVD=uc6d0Ps;qJ#=cyx?>c)K7)FBjhQ@ax4cA!Xko^!?bTOSyBx1=e2L5!#`#bKE@*fQiW z1nlTmvrOHJI;Zs_hJL8YYkOXPGI`*70mGvZ9=3Gzt1Ra;U(aI%6vOrcD({as58fdo zkNuBG){O4dQVx{NTzu#HE<>1DJ6ACdH@xd~*e`&LdQjxYbq zV{yygVVuWP5rJZ>Eui$*4}E@)|CXPZTk2YQ-%;HD;=`z|*~Egozs!>l(-Hx=&{8_| z1j~6|JqEQ^biz8l>aJt$vsdv;s7LGM*A4q6L{kXjPLbwfJ!VuNE$$8cnm#}hE2Kt~ z@%7Dm`b!MqDYVx^pl(e5tql_UZE}a&mjt;uw>_TVTz=Sg+HrJNHH50%PEnOoH(voh z7E!)6L^TF)&Tcu6X;^F{X~)_5U3hC~V)o&A43w_}1JZOax9(bUu;SERTW5thOyQ0+9DoI@TX>z`81 z>CaZ_#$~R}>Uj#jxiYx0t6hI5@&P0#pqSr8`pU?au3HQocgL^W0GSXx5$`^+oVc;ZLI7D%3~Z4S|W3 zgu190BMDc>3fE}(YNNC*64utt{N};)LakjK-XI0qm>w_n$kww_1$6+Rn1AMoKb}*U zF_!6g9%uSKkJ7#=DlFy8zID@#j;k8;;2I!6@CAa0;7)+xPH=Y!Zh_$LL4&&`xVyW%yWai2bD#S= zf1Gpsd1mIFs(QP-s(ZJ)Ym}rVBs%B;KwVr^K~3R<1}Xpm@1Pe23_u41NQ;RnWWhpT z0oW`nLu&_ksLI;L(OyYX6s)1C1xDHi5C9N>17HChUkx3;i6|(1`p5czE;lm(%s-k* zmVfH{pKbnEDTa};gCPLGfT0!;!*BMEP^=Hd+%Asa{=rF5jAv-})d-49p_tho+CeCu z`$ul@U-;-BZ2B+$*C{-E6(upKZ+K8S*z~_)ga3vN&FrnAHt(S})JE1e(E4FD{tFxZ zgFXMj)>cl?zWrPN@etkEMpYU5q=sIkfFvLd$N>rf7%%{w0CT_!a0FPOPaCMj0Z@YK zMgJE*@xOcpD9ZrKG6xKyjE{gVU=4izmk<0i22dMl`A=*eOgK6I6~Um30sz9w>+2;A z03gKyz;n#&>tpuo>vIkOz%2nlhs}TZwpjr1;TkHB`Hze`69BLR0idqyKQenxd3g_#{3@uF3sJjH=8E}?pG_pHe z^I1+#v|Q*_O9|A{-XPvi3r6jp=p*c+9MTN89-P>)*9zc9J_ z>VqH>$J!iYwqKs{{S_f;ML;FA%fY8;bo%Q3@NCORhA%EA5;I3Ri7OJnta~L~XO^BM zGn~LdH2jN)7AEBV?dnwd^1>y0aGS(s1Cs9dC6gAjHKMyljj;#CZy5?w;!k66x%1kL z#7N}iq*sk)i3ToSf>F4rD70GDqpxV$NH05#C2Q6RYHClubCxTE6zr514NgypTGDJ<;H-kLL#c(QI$$sg}c#d9OXyU&y*N=dIWQ|KdVS?}rv z#|@uFB3aJ!QuE6JqpMC6F2hioDhU8KTKBrAR69AgP?G;Hgj5{!Cue&2gKK`l^9<@e z5A;e*tj+v6Ewj&tuv39FFgIMD?xB7au zo0luApYwk26&8?&a2JqP4q{b0%6g=yusg1(hv8|x%CN(Xhq%q7Lp-mXmeTBvq)ug9m}!B z%)B~~pn=e4c*cLBeF`MWTKgbyOgy`~m8M8NQIVK4KQ%H)>D(Ub7!_?^ zxeC{U-me#vFx0%n4tlpwbKgavDkv8-I8KvZV_-1@W2yQQ14e?!$i{GI7EVxSVe4wi zB(|8#w`|QvH+Ni3A_XSg9r^V|{xl-d&fZS>25MD2#Cf-G#^YbO>~vQ#zQjFBc)q81ZW>YXTV?Q*@PXndXt(<>V?AT+%%$xMet|+8_xaIgFRdX>>^8wQg zsV__)HJ%nH-qre=hht_vx%t3RToo{_*nc4 z^zL|(2?^_}KB+-{4D1)y*{%Wu8_Y1nZ*&fLd93kR)VfUU)=q*+})p)U?vRIzq-QdNawkaYX15CaV^ot*#XWjG| za4PtC$CPmh6=_R~jSkkVnH)fav$S?N;+{$2KN8A+u}PN+R`F3t>UAk1Qf9I*>-|te zN~34@sY9ZJY0;*AJE()%{ z-)^yUQ~dH=l82q6CRse8kD-}e&-y98$HzVt7icF#spMYP20vNK(f>$~w?!<#HP{dC z`hj6<_Hm!~4Q1I=G@m^jqw&|z#|H5C_MuFfzmf0xl`X@^BIqIRr_`~stciY?Lyz8I zTd|7~R76U(eA+{JCm&zDRLR&wU6IuQ<8!dj>sM22bkU)EW(i2^q|~*j{i({hT1#8w zn-pbC1d8Wfa*!(Rs#QCF<-m^yqZOdmwE@ZFW1sb?LEn{2R|sgB9LND zZX^3UJCveNkcQDkUv8!#z9z|1xB2u2(zj*<_od{&XKb$!kUT&vNX_>ZB26rlzVGX& zMC8wBxV7}Z{kgD0q!e0Eh7h!uy{YVCk}BEUduOX(U4GwVfH*!<*aK(&*YvZ@k|yV0 z-{k_KSM@Th8M#V`KtA%mXH&5dQq?Z(VxeR7BrXhcvioqEK5sazuj}7SseCsVEWw!T z)5g0)nq#NzOcr(eRD#?08Q7eXoOVwOhoyw=hNS<)7gCls7#ifnUG#D z4(N`bMFvcKF6iC1*Lmb1+$V9t^seEG9GPYG6HdlD@36Hpzzc&(1a896f`s>}=Sh+cKqX4b=+r=j;H?Vh$T1^tv$K}{J1NquPPF)^bIDN1 zOw13hAcK4P55M@-Lc}~PS*2xR!TMX`NV#_hiEf&Y!bAyp`zT6>{G@eRQ{CEH`@Tio z=F4c*t{hs$x;8i=SZ{i6789dRba`9l#e{R;&_`&cge$32?AyXt*@aVp z$HPXHuEqb1C>uV90<#Z=qIDWR@==$&zG@Yny;k&Zz z3$sTjT#jleTs&oxSoju|Z>n+1u<&=K+2Zix0NIlqZqDEYTg(2~v7V?E`)JAjlABv@ z7)93el3;p4xG^U~8G7tB;?f~wKE!TNf{TWWYBkl!8Co9A@nWv`6HUYz=Y2J5vTGCIeP5dk(~clRddUDl z9PNHu`qUo$Ij_p`3kl0V^GqQyz6$pIeD@qW#deq1=lw>mDkFn^Jf6N1DQP*CX7a>D zYQG@=GDd1D|Ab5ftFxKuFz;u^xwtG;?Zc9)D&3La8kWc`ziYZ3tv9wnw zF|g%PV+E?Y393P70kt5CZ%1t6v5azEb2<`enp@|Rbl(Ks;HJuz3Q(k2Qp9^a#}nyZm2>AqV_ctYY^Z z=YQb93 zm^V}~`g<+SX)MugrEo@qk7RXOLz=-FEx*d5pyTqmn|@R@w;+!6v37WtAm?a4`0lP+ z1KP%wf3OX=hCKQiT$SP=`B~BTF(+#SYpN`1txJD|+1+5+dEfEG^gdB>Na<5G|D#szkZVzz+N*D z@}ei{-B(SF8_T z^H1*&dWgXmM^zQ*`!xiD?t4lkA*1SCl&~OC|tT zYd=>nyARmDw0gUWTTKG2pgzRW)~NSBslmb-o`20$kD=ocw))Ha0! zP1h_b_~O0snFYF-w4E9qL*^;EabZHz?Xu@sdM+H*+4R69uZrACdo5$5@CSch?jKEc zCbADR?P@~-g0aWJn$)1Dl2YD8mmw(*V18lq#f~!Qr{$(^Q4SeOP++t}o>=LpV{7pF zX-7e;>dzSKPq=LPjB#|sY}$e%VDW9?#G*fqh82r<4ce^u%C zG+sZ0Yfhq(Ceq|JrtTPnWYCZ*&nb2ke4iqDvugrJ`qyG--d_9W)u}ayu3GWCJ~<}H zCTCm$=MOW@1NmY#8KhgX0jFzmYI!$~hMp# z)Gc)-Kbr8CyffVk9ENh;BBWUF+{tt)VTaPeYaCAqlA7m?8Qa`$Tkvb#h#LMU1(G z{UqO8{vZ2SfVp$od(=x^m8fDc3+$Y4`z$SX3&F3pBxHXq1 zD*1`KQp8puf;cf$F@}cvLN@JZ-KZNO@1m($7@BO8dtjpsDo(amMUkX}92GfL{aLj@ zLAxU}clOD~o!S#B4tOdD*V*;hUbxT5Xp{&P+i)Xh_tduV{brjDKVJ-K-tKN^I`Yb( z-6crN${1B3%Mb{SF+ zIb|=NAx5YjC0HL3zMKx(al+MZX%My)7(CU@it+uFg4*n_@T^Wh7yII5 zN`cX$w5Tth;ZKSISGmcg;Eg~jYOyB z$bED?4MSuQv#B0A&sO`iW?N*Yeu#Q#AmMl#4lgaIt#PYP7_w!e!m!9j{%1w3tL9^j z8fT*2y`YFysz;Go-7m1lE5pXN3D_wt&e}ZN0QM%kYMUDE@(Dll3ci2wP1W(Y5M-MI z@paZ<Fn6%m)cf?eJ1V^^4YT0>Dgey$}Zy~)ZxCufnF@w zKj$7_ps{zfbE;rKAoL)wVoYzu%?=S{#cSwSR+$j%29VQShx4uG<@#Mqzh#f%D&{Qw zq(_Eq40wt+CqYbH8Bp66HKtNA#bFFj|4ySi(+5g*nd9j$h)ijyyz%W|=g|qPolfm@ z3`aKQVSix5N6LC}xtFQclBYU=4?AY*IpjfrRgQjsWBhURx?;r%*NOxWeXSG=zzYc$#Khq7m0t7iQu5W+C z@ld=f`%aZ`j_P~kvvRG9)E$8Ant|iOp`~F)%m`y}KXao6on4ubEmb@I=RhZqU!ag% z;b=uTIAm&*kVkm~#MpG=uvJGA>kg zK7YuiM~)2-4)?T-AB$#j!swoe=h@fzj}(>Vp45H`x8ld#$f$f#ctW-;s_BF8vp1i< z2}|!(jtHbcCdk;QPkq;1WXv1l!9Pd2i=oL9=NVZA({9|l`2tXpkMhv;H<3=H*q*W# zrB0;*5QP`12Lm&>TckIp$808Fh{E4Psa$Jr1;TU&q#8Zqb$N8~S}vmmS*e`<$6DV5{5J5^ zCQ?S&kQwp&>U4WZ>D+>}Ec(NncLxTreo2oNOpj9(@K~%LiPNjO-b4Plxq*e!K9*CI zRHfpK`Si5=w{s34!S{XWeI9T(DO(##IfEg@4|{KtNxkgr{hpC zdM9FH%s}Z4#Nid`fS%`j)SR#@W7Q+vbV~h6aZWL%N}!c9RpjlX#XjtweTKTo6JM17 zf+d+0rB6B9n4XVQ=tJk=*NLDq2XGLZ`Yar;I|iRkTym75UjMs(DxP^o_r>#0UGQWK zQSxU^3{tW_fu#+WagyLP`bK_B3FN-SlE-OfUa!5mN$K9ErJEmSO&Obt7lo_bTacNa zk;MVyz`MOvjVSAe5o$m4A+PwB14tFN(JAL$yqv_I48V{EN& z=sFsw>pfWQ=s@5O;qRF%I^||eHEVoQdo?wwMnBE70`uE0$zCRnoxAbqz=F{HL%fAX$qW((jsr+6mIuh2#3PMc zqM_-#JyWoVbj%*i_&d)UiON*atO7}%4}vN3-h&2F9?YJ4^HpkqlAd=Jr-}wUX$C7-J8uHKQ3RE9gtQpE38x`FTvtMZS-Nzc3 zSj|D16*e6V*k$Drzp^`?8sw{JL~qlHC@pAq2l+_pvkHd42?8i67lxB3IF2vSG)@Ow56|P zM=-^l&_ua1O9Mw?U;ty}^HnyVYZVptRB_#M0o69saz-f}{-NMmgX;WIVeq5>07go} zi4v=jr8jcXT9clTT5-#1%D1er>vZP?rr7&i8wrJBp@7d?@jc!)c@#wlXKq9-IYumz zh_%C94=+LXcxAT5F!(k{H5H$ThJxLbAo-7xe4F~2mw&pnvNkr~m|R+IItYCNoGPrV zgffkT+Uv&r4sQ3nic(Y)YG5)H-l7wEO!rf%_r$*|V(;-x8P4cs!g;R{Fje~~kD@(L z=R6^XG3n94&cttJ`u#x!qvmD{orh$pekV|LINoFPko3)#*KAS%aY^>(2x_D1$>{+M zmsaLiI&1r?Opj44ix%i7igTkds-TZJ8@4QAfhoUf_ZLN}*!+V6@jwik%b(!5j0|U= zO<*-)L}5=rMIWXH;kovF*-c4vlv;yaDe$(N-m?N0bLWHP;mL*AIbXm#<5eGv+zwN0 zm_b*+8IWx!wmC2_1uNj_BLhVLPa& z=w+vaJhjyrgRMQ1|5|MtLl<&G+OnH@<)HmN7!sLz8ChNim(#Rnric0R8V>#L3Kl>E z00m(n9flDMLk}YqE1DwJo=260G3BVKe3<8EYpS{4`!92h`!9~CV zaA&ZaML93Rw<~AXiR-UTFQ-01w?h@ePA|I;ne#lJKbWq=won(g4p%E~izP(06JXKkVPGC!7h7sYbQdAJ27T=O-p|+Rn=)au ztV8ssa{h|uYGcLd>&T$OdpBk)pc?kxC=XfmG~L$Pva|_VXTD<(f&Q-nD1-T(g7;|o zp1WvAe7;qMz*q7^nNzW125wd{2D}0Va3M~Tr$x+{9V1Jh3^pK*Zb3l9FQ>cRn!Mig z*Y85LokOP!;v}gpRHnnTY8OA5nYA71sd@*eF{L-oLSH)2**$o~3OI#8B3oVX!*!2} zhD#&YWI=`R(IT&x-*VEmaQnkl;N|@N>&IYqPE^UWB~GnPqFZ1H($in|q3B5(S#)m8 z&q8SOZ#w>TXIQG&d!Bk`{QcuuwzFu2V^XWPrF!r@kB2yU*2HZ4S7P7x3qoFXOyDKw zJKPLTkj=xSkxoWZ)zR@5&Y4MWXm6^C^hXBita+2BAAC2;QTw(4t%Z# z02slf1ZS(zzR)dRV&luWqZ~^jnoJ`A#zVuDcdIZ{*ljwu9un(VNSYw?p7z zW)&X;vaz0t`D&kOfiq-uUHv7YBak@ObunShhM_C~m zkzGWVjr4r|p;w8^sreAZHuGN$uRDk2m2op{8-44{FiTp6!d8xqSnqg3&G(EtGz5Kd z+LDXb`%#&m$@uhx~xZ~UeRHXt;$gx4o^QQIMOUVt5qB8zDll30~p`m!skq>2{ql&WWg zUIipri$shzB7&3GeAD7sqPSqZ<3s7 zon5vi`Z>G(IYUqs_DNX3A-LcowdfCW9E|Hcn!b zfys_N~?M@YnuQ zNvS#ly{^Kp>y!+)@TFt;>`5KJMkMN2opN7| zWqe4j)yASp*bOym%?KkZahHx_zjJO+6ED)QAhU|v=$_<&F&^$^>bSm$x-^B?U>98Z z^yp;_THSX0p%{U94qc2S+3+zM^DZUXJWje_2$Nv3LaOUN%;(X5UvLjVIC_j#c(UMJI~p@WQ!yGz9%j{Qu+cER^>4BBQVn5S3Dxg?&fNt*FqWo89Rt_pyZgVG zw4=ecQ+j0qo9@pRrLJ%8r7>-~83lg?DXzKg+?U2p8B+o#C(dJ8iK#_Her1Xy(o=3# zOiE(GDIAul@&aO}1J*LU+$A*k4VUNKjkk4gqXUjobJrfiIl`tS5|`8ZglYURtSlX( z^KlrljokyU&#hT*kHbUzmjkp7=KKow(Z0hV9^uJ(1*GUrTfUBD_J_=37*Z+X#F$7% zqgj`A{tlykV=Ipo{QDdGOm;y`!vjhMhlCHEDI+gEcX2BZtl_R&ZIY0*zEb@mUhSc` zXFt8Tf3yw%6=Q~{f7OowSvFHMwsJ0q5EZLg)tV?FVGt=LQt54;oca6YchgRa0o0Vv z;V`u!@=;54@I|4Hd|#dHDS(xITKJ$+UGH2$O6CK)OkhnlHXcS6A^dWC&em?iCo3}^Tr|pOJ~gS)EDK4J zFGo`4GW@;q65LJH^&Wfprm|olc;u#s@!E&hK2eXoQ~z{)&IXfxqY>X!#2jZ6R?U0Yqo_9Xx16I6Cr!YI3eEQ7Q?KSdtOmP|LJ!H>O*tGYR>GYm9O18Jei~ec!dlaL|8HR~!CmxU#?7SZv_O;(Wlm2~} zB3T>zZR-H{hA}i)_vkY8ynN&KezT1DFsjFI__0?Vcg_JK zB}KVDy(-CNZ%SoU_iZaP!7?U?)zUm2;@9^5ei#iozzg%SVDRgX1OWdB`$b#o literal 0 HcmV?d00001 diff --git a/lang/en.json b/lang/en.json new file mode 100644 index 0000000..077404a --- /dev/null +++ b/lang/en.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/modules/dark-stars-actor-sheet.js b/modules/dark-stars-actor-sheet.js new file mode 100644 index 0000000..4178aef --- /dev/null +++ b/modules/dark-stars-actor-sheet.js @@ -0,0 +1,224 @@ +/** + * Extend the basic ActorSheet with some very simple modifications + * @extends {ActorSheet} + */ + +import { DarkStarsUtility } from "./dark-stars-utility.js"; + +/* -------------------------------------------- */ +export class DarkStarsActorSheet extends ActorSheet { + + /** @override */ + static get defaultOptions() { + + return mergeObject(super.defaultOptions, { + classes: ["fvtt-dark-stars", "sheet", "actor"], + template: "systems/fvtt-dark-stars/templates/actor-sheet.html", + width: 960, + height: 720, + tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "skills" }], + dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }], + editScore: true + }); + } + + /* -------------------------------------------- */ + async getData() { + const objectData = this.object.system + let actorData = duplicate(objectData) + + let formData = { + title: this.title, + id: this.actor.id, + type: this.actor.type, + img: this.actor.img, + name: this.actor.name, + editable: this.isEditable, + cssClass: this.isEditable ? "editable" : "locked", + data: actorData, + limited: this.object.limited, + skills: this.actor.getSkills( ), + weapons: this.actor.checkAndPrepareEquipments( duplicate(this.actor.getWeapons()) ), + armors: this.actor.checkAndPrepareEquipments( duplicate(this.actor.getArmors())), + shields: this.actor.checkAndPrepareEquipments( duplicate(this.actor.getShields())), + spells: this.actor.checkAndPrepareEquipments( duplicate(this.actor.getLore())), + equipments: this.actor.checkAndPrepareEquipments(duplicate(this.actor.getEquipmentsOnly()) ), + equippedWeapons: this.actor.checkAndPrepareEquipments(duplicate(this.actor.getEquippedWeapons()) ), + equippedArmor: this.actor.getEquippedArmor(), + equippedShield: this.actor.getEquippedShield(), + feats: duplicate(this.actor.getFeats()), + subActors: duplicate(this.actor.getSubActors()), + race: duplicate(this.actor.getRace()), + moneys: duplicate(this.actor.getMoneys()), + encCapacity: this.actor.getEncumbranceCapacity(), + saveRolls: this.actor.getSaveRoll(), + conditions: this.actor.getConditions(), + description: await TextEditor.enrichHTML(this.object.system.biodata.description, {async: true}), + notes: await TextEditor.enrichHTML(this.object.system.biodata.notes, {async: true}), + containersTree: this.actor.containersTree, + encCurrent: this.actor.encCurrent, + options: this.options, + owner: this.document.isOwner, + editScore: this.options.editScore, + isGM: game.user.isGM + } + this.formData = formData; + + console.log("PC : ", formData, this.object); + return formData; + } + + + /* -------------------------------------------- */ + /** @override */ + activateListeners(html) { + super.activateListeners(html); + + // Everything below here is only needed if the sheet is editable + if (!this.options.editable) return; + + html.bind("keydown", function(e) { // Ignore Enter in actores sheet + if (e.keyCode === 13) return false; + }); + + // Update Inventory Item + html.find('.item-edit').click(ev => { + const li = $(ev.currentTarget).parents(".item") + let itemId = li.data("item-id") + const item = this.actor.items.get( itemId ); + item.sheet.render(true); + }); + // Delete Inventory Item + html.find('.item-delete').click(ev => { + const li = $(ev.currentTarget).parents(".item") + DarkStarsUtility.confirmDelete(this, li) + }) + html.find('.item-add').click(ev => { + let dataType = $(ev.currentTarget).data("type") + this.actor.createEmbeddedDocuments('Item', [{ name: "NewItem", type: dataType }], { renderSheet: true }) + }) + + html.find('.equip-activate').click(ev => { + const li = $(ev.currentTarget).parents(".item") + let itemId = li.data("item-id") + this.actor.equipActivate( itemId) + }); + html.find('.equip-deactivate').click(ev => { + const li = $(ev.currentTarget).parents(".item") + let itemId = li.data("item-id") + this.actor.equipDeactivate( itemId) + }); + + html.find('.subactor-edit').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let actorId = li.data("actor-id"); + let actor = game.actors.get( actorId ); + actor.sheet.render(true); + }); + + html.find('.subactor-delete').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let actorId = li.data("actor-id"); + this.actor.delSubActor(actorId); + }); + html.find('.quantity-minus').click(event => { + const li = $(event.currentTarget).parents(".item"); + this.actor.incDecQuantity( li.data("item-id"), -1 ); + } ); + html.find('.quantity-plus').click(event => { + const li = $(event.currentTarget).parents(".item"); + this.actor.incDecQuantity( li.data("item-id"), +1 ); + } ); + + html.find('.ammo-minus').click(event => { + const li = $(event.currentTarget).parents(".item") + this.actor.incDecAmmo( li.data("item-id"), -1 ); + } ); + html.find('.ammo-plus').click(event => { + const li = $(event.currentTarget).parents(".item") + this.actor.incDecAmmo( li.data("item-id"), +1 ) + } ); + + html.find('.roll-ability').click((event) => { + const abilityKey = $(event.currentTarget).data("ability-key"); + this.actor.rollAbility(abilityKey); + }); + html.find('.roll-skill').click((event) => { + const li = $(event.currentTarget).parents(".item") + const skillId = li.data("item-id") + this.actor.rollSkill(skillId) + }); + + html.find('.roll-weapon').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const skillId = li.data("item-id") + this.actor.rollWeapon(skillId) + }); + html.find('.roll-armor-die').click((event) => { + this.actor.rollArmorDie() + }); + html.find('.roll-shield-die').click((event) => { + this.actor.rollShieldDie() + }); + html.find('.roll-target-die').click((event) => { + this.actor.rollDefenseRanged() + }); + + html.find('.roll-save').click((event) => { + const saveKey = $(event.currentTarget).data("save-key") + this.actor.rollSave(saveKey) + }); + + + html.find('.lock-unlock-sheet').click((event) => { + this.options.editScore = !this.options.editScore; + this.render(true); + }); + html.find('.item-link a').click((event) => { + const itemId = $(event.currentTarget).data("item-id"); + const item = this.actor.getOwnedItem(itemId); + item.sheet.render(true); + }); + html.find('.item-equip').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + this.actor.equipItem( li.data("item-id") ); + this.render(true); + }); + + html.find('.update-field').change(ev => { + const fieldName = $(ev.currentTarget).data("field-name"); + let value = Number(ev.currentTarget.value); + this.actor.update( { [`${fieldName}`]: value } ); + }); + } + + /* -------------------------------------------- */ + /** @override */ + setPosition(options = {}) { + const position = super.setPosition(options); + const sheetBody = this.element.find(".sheet-body"); + const bodyHeight = position.height - 192; + sheetBody.css("height", bodyHeight); + return position; + } + + /* -------------------------------------------- */ + async _onDropItem(event, dragData) { + console.log(">>>>>> DROPPED!!!!") + const item = fromUuidSync(dragData.uuid) + if (item == undefined) { + item = this.actor.items.get( item.id ) + } + let ret = await this.actor.preprocessItem( event, item, true ) + if ( ret ) { + super._onDropItem(event, dragData) + } + } + + /* -------------------------------------------- */ + /** @override */ + _updateObject(event, formData) { + // Update the Actor + return this.object.update(formData); + } +} diff --git a/modules/dark-stars-actor.js b/modules/dark-stars-actor.js new file mode 100644 index 0000000..ef22ea7 --- /dev/null +++ b/modules/dark-stars-actor.js @@ -0,0 +1,808 @@ +/* -------------------------------------------- */ +import { DarkStarsUtility } from "./dark-stars-utility.js"; +import { DarkStarsRollDialog } from "./dark-stars-roll-dialog.js"; + +/* -------------------------------------------- */ +const coverBonusTable = { "nocover": 0, "lightcover": 2, "heavycover": 4, "entrenchedcover": 6 }; +const statThreatLevel = ["agi", "str", "phy", "com", "def", "per"] +const __subkey2title = { + "melee-dmg": "Melee Damage", "melee-atk": "Melee Attack", "ranged-atk": "Ranged Attack", + "ranged-dmg": "Ranged Damage", "dmg-res": "Damare Resistance" +} + +/* -------------------------------------------- */ +/* -------------------------------------------- */ +/** + * Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system. + * @extends {Actor} + */ +export class DarkStarsActor extends Actor { + + /* -------------------------------------------- */ + /** + * Override the create() function to provide additional SoS functionality. + * + * This overrided create() function adds initial items + * Namely: Basic skills, money, + * + * @param {Object} data Barebones actor data which this function adds onto. + * @param {Object} options (Unused) Additional options which customize the creation workflow. + * + */ + + static async create(data, options) { + + // Case of compendium global import + if (data instanceof Array) { + return super.create(data, options); + } + // If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic + if (data.items) { + let actor = super.create(data, options); + return actor; + } + + if (data.type == 'character') { + const skills = await DarkStarsUtility.loadCompendium("fvtt-dark-stars.skills"); + data.items = skills.map(i => i.toObject()) + } + if (data.type == 'npc') { + } + + return super.create(data, options); + } + + /* -------------------------------------------- */ + prepareBaseData() { + } + + /* -------------------------------------------- */ + async prepareData() { + super.prepareData(); + } + + /* -------------------------------------------- */ + computeHitPoints() { + if (this.type == "character") { + let hp = duplicate(this.system.secondary.hp) + let max = (this.system.abilities.str.value + this.system.abilities.con.value) * 6 + if (max != hp.max || hp.value > max) { + hp.max = max + hp.value = max // Init case + this.update({ 'system.secondary.hp': hp }) + } + } + } + /* -------------------------------------------- */ + computeEffortPoints() { + if (this.type == "character") { + let effort = duplicate(this.system.secondary.effort) + let max = (this.system.abilities.con.value + this.system.abilities.int.value) * 6 + if (max != effort.max || effort.value > max) { + effort.max = max + effort.value = max // Init case + this.update({ 'system.secondary.effort': effort }) + } + } + } + + /* -------------------------------------------- */ + prepareDerivedData() { + + if (this.type == 'character' || game.user.isGM) { + this.system.encCapacity = this.getEncumbranceCapacity() + this.buildContainerTree() + this.computeHitPoints() + this.computeEffortPoints() + } + + super.prepareDerivedData(); + } + + /* -------------------------------------------- */ + _preUpdate(changed, options, user) { + + super._preUpdate(changed, options, user); + } + + /* -------------------------------------------- */ + getEncumbranceCapacity() { + return 1; + } + + /* -------------------------------------------- */ + getMoneys() { + let comp = this.items.filter(item => item.type == 'money'); + DarkStarsUtility.sortArrayObjectsByName(comp) + return comp; + } + /* -------------------------------------------- */ + getFeats() { + let comp = duplicate(this.items.filter(item => item.type == 'feat') || []); + DarkStarsUtility.sortArrayObjectsByName(comp) + return comp; + } + /* -------------------------------------------- */ + getFeatsWithDie() { + let comp = duplicate(this.items.filter(item => item.type == 'feat' && item.system.isfeatdie) || []); + DarkStarsUtility.sortArrayObjectsByName(comp) + return comp; + } + getFeatsWithSL() { + let comp = duplicate(this.items.filter(item => item.type == 'feat' && item.system.issl) || []); + DarkStarsUtility.sortArrayObjectsByName(comp) + return comp; + } + /* -------------------------------------------- */ + getLore() { + let comp = duplicate(this.items.filter(item => item.type == 'spell') || []); + DarkStarsUtility.sortArrayObjectsByName(comp) + return comp; + } + getEquippedWeapons() { + let comp = duplicate(this.items.filter(item => item.type == 'weapon' && item.system.equipped) || []); + DarkStarsUtility.sortArrayObjectsByName(comp) + return comp; + } + /* -------------------------------------------- */ + getArmors() { + let comp = duplicate(this.items.filter(item => item.type == 'armor') || []); + DarkStarsUtility.sortArrayObjectsByName(comp) + return comp; + } + getEquippedArmor() { + let comp = this.items.find(item => item.type == 'armor' && item.system.equipped) + if (comp) { + return duplicate(comp) + } + return undefined + } + /* -------------------------------------------- */ + getShields() { + let comp = duplicate(this.items.filter(item => item.type == 'shield') || []); + DarkStarsUtility.sortArrayObjectsByName(comp) + return comp; + } + getEquippedShield() { + let comp = this.items.find(item => item.type == 'shield' && item.system.equipped) + if (comp) { + return duplicate(comp) + } + return undefined + } + /* -------------------------------------------- */ + getRace() { + let race = this.items.filter(item => item.type == 'race') + return race[0] ?? []; + } + /* -------------------------------------------- */ + checkAndPrepareEquipment(item) { + } + + /* -------------------------------------------- */ + checkAndPrepareEquipments(listItem) { + for (let item of listItem) { + this.checkAndPrepareEquipment(item) + } + return listItem + } + + /* -------------------------------------------- */ + getConditions() { + let comp = duplicate(this.items.filter(item => item.type == 'condition') || []); + DarkStarsUtility.sortArrayObjectsByName(comp) + return comp; + } + /* -------------------------------------------- */ + getWeapons() { + let comp = duplicate(this.items.filter(item => item.type == 'weapon') || []); + DarkStarsUtility.sortArrayObjectsByName(comp) + return comp; + } + /* -------------------------------------------- */ + getItemById(id) { + let item = this.items.find(item => item.id == id); + if (item) { + item = duplicate(item) + } + return item; + } + + /* -------------------------------------------- */ + getSkills() { + let comp = duplicate(this.items.filter(item => item.type == 'skill') || []) + for (let skill of comp) { + DarkStarsUtility.updateSkill(skill) + } + DarkStarsUtility.sortArrayObjectsByName(comp) + return comp + } + + /* -------------------------------------------- */ + getRelevantAbility(statKey) { + let comp = duplicate(this.items.filter(item => item.type == 'skill' && item.system.ability == ability) || []); + return comp; + } + + + /* -------------------------------------------- */ + async equipItem(itemId) { + let item = this.items.find(item => item.id == itemId) + if (item && item.system) { + if (item.type == "armor") { + let armor = this.items.find(item => item.id != itemId && item.type == "armor" && item.system.equipped) + if (armor) { + ui.notifications.warn("You already have an armor equipped!") + return + } + } + if (item.type == "shield") { + let shield = this.items.find(item => item.id != itemId && item.type == "shield" && item.system.equipped) + if (shield) { + ui.notifications.warn("You already have a shield equipped!") + return + } + } + let update = { _id: item.id, "system.equipped": !item.system.equipped }; + await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity + } + } + + /* -------------------------------------------- */ + compareName(a, b) { + if (a.name < b.name) { + return -1; + } + if (a.name > b.name) { + return 1; + } + return 0; + } + + /* ------------------------------------------- */ + getEquipments() { + return this.items.filter(item => item.type == 'shield' || item.type == 'armor' || item.type == "weapon" || item.type == "equipment"); + } + /* ------------------------------------------- */ + getEquipmentsOnly() { + return duplicate(this.items.filter(item => item.type == "equipment") || []) + } + + /* ------------------------------------------- */ + getSaveRoll() { + return { + reflex: { + "label": "Reflex Save", + "img": "systems/fvtt-dark-stars/images/icons/saves/reflex_save.webp", + "value": this.system.abilities.agi.value + this.system.abilities.wit.value + }, + fortitude: { + "label": "Fortitude Save", + "img": "systems/fvtt-dark-stars/images/icons/saves/fortitude_save.webp", + "value": this.system.abilities.str.value + this.system.abilities.con.value + }, + willpower: { + "label": "Willpower Save", + "img": "systems/fvtt-dark-stars/images/icons/saves/will_save.webp", + "value": this.system.abilities.int.value + this.system.abilities.cha.value + } + } + } + + /* ------------------------------------------- */ + async buildContainerTree() { + let equipments = duplicate(this.items.filter(item => item.type == "equipment") || []) + for (let equip1 of equipments) { + if (equip1.system.iscontainer) { + equip1.system.contents = [] + equip1.system.contentsEnc = 0 + for (let equip2 of equipments) { + if (equip1._id != equip2.id && equip2.system.containerid == equip1.id) { + equip1.system.contents.push(equip2) + let q = equip2.system.quantity ?? 1 + equip1.system.contentsEnc += q * equip2.system.weight + } + } + } + } + + // Compute whole enc + let enc = 0 + for (let item of equipments) { + //item.data.idrDice = DarkStarsUtility.getDiceFromLevel(Number(item.data.idr)) + if (item.system.equipped) { + if (item.system.iscontainer) { + enc += item.system.contentsEnc + } else if (item.system.containerid == "") { + let q = item.system.quantity ?? 1 + enc += q * item.system.weight + } + } + } + for (let item of this.items) { // Process items/shields/armors + if ((item.type == "weapon" || item.type == "shield" || item.type == "armor") && item.system.equipped) { + let q = item.system.quantity ?? 1 + enc += q * item.system.weight + } + } + + // Store local values + this.encCurrent = enc + this.containersTree = equipments.filter(item => item.system.containerid == "") // Returns the root of equipements without container + + } + + /* -------------------------------------------- */ + async rollArmor(rollData) { + let armor = this.getEquippedArmor() + if (armor) { + + } + return { armor: "none" } + } + + /* -------------------------------------------- */ + async incDecHP(formula) { + let dmgRoll = new Roll(formula+"[dark-starsorange]").roll({ async: false }) + await DarkStarsUtility.showDiceSoNice(dmgRoll, game.settings.get("core", "rollMode")) + let hp = duplicate(this.system.secondary.hp) + hp.value = Number(hp.value) + Number(dmgRoll.total) + this.update({ 'system.secondary.hp': hp }) + return Number(dmgRoll.total) + } + + /* -------------------------------------------- */ + getAbility(abilKey) { + return this.system.abilities[abilKey]; + } + + /* -------------------------------------------- */ + async addObjectToContainer(itemId, containerId) { + let container = this.items.find(item => item.id == containerId && item.system.iscontainer) + let object = this.items.find(item => item.id == itemId) + if (container) { + if (object.system.iscontainer) { + ui.notifications.warn("Only 1 level of container allowed") + return + } + let alreadyInside = this.items.filter(item => item.system.containerid && item.system.containerid == containerId); + if (alreadyInside.length >= container.system.containercapacity) { + ui.notifications.warn("Container is already full !") + return + } else { + await this.updateEmbeddedDocuments("Item", [{ _id: object.id, 'system.containerid': containerId }]) + } + } else if (object && object.system.containerid) { // remove from container + console.log("Removeing: ", object) + await this.updateEmbeddedDocuments("Item", [{ _id: object.id, 'system.containerid': "" }]); + } + } + + /* -------------------------------------------- */ + async preprocessItem(event, item, onDrop = false) { + let dropID = $(event.target).parents(".item").attr("data-item-id") // Only relevant if container drop + let objectID = item.id || item._id + this.addObjectToContainer(objectID, dropID) + return true + } + + /* -------------------------------------------- */ + async equipGear(equipmentId) { + let item = this.items.find(item => item.id == equipmentId); + if (item && item.system) { + let update = { _id: item.id, "system.equipped": !item.system.equipped }; + await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity + } + } + /* -------------------------------------------- */ + getInitiativeScore(combatId, combatantId) { + if (this.type == 'character') { + this.rollMR(true, combatId, combatantId) + } + console.log("Init required !!!!") + return -1; + } + + /* -------------------------------------------- */ + getSubActors() { + let subActors = []; + for (let id of this.system.subactors) { + subActors.push(duplicate(game.actors.get(id))) + } + return subActors; + } + /* -------------------------------------------- */ + async addSubActor(subActorId) { + let subActors = duplicate(this.system.subactors); + subActors.push(subActorId); + await this.update({ 'system.subactors': subActors }); + } + /* -------------------------------------------- */ + async delSubActor(subActorId) { + let newArray = []; + for (let id of this.system.subactors) { + if (id != subActorId) { + newArray.push(id); + } + } + await this.update({ 'system.subactors': newArray }); + } + + /* -------------------------------------------- */ + syncRoll(rollData) { + this.lastRollId = rollData.rollId; + DarkStarsUtility.saveRollData(rollData); + } + + /* -------------------------------------------- */ + getOneSkill(skillId) { + let skill = this.items.find(item => item.type == 'skill' && item.id == skillId) + if (skill) { + skill = duplicate(skill); + } + return skill; + } + + /* -------------------------------------------- */ + async deleteAllItemsByType(itemType) { + let items = this.items.filter(item => item.type == itemType); + await this.deleteEmbeddedDocuments('Item', items); + } + + /* -------------------------------------------- */ + async addItemWithoutDuplicate(newItem) { + let item = this.items.find(item => item.type == newItem.type && item.name.toLowerCase() == newItem.name.toLowerCase()) + if (!item) { + await this.createEmbeddedDocuments('Item', [newItem]); + } + } + + /* -------------------------------------------- */ + async incrementSkillExp(skillId, inc) { + let skill = this.items.get(skillId) + if (skill) { + await this.updateEmbeddedDocuments('Item', [{ _id: skill.id, 'system.exp': skill.system.exp + inc }]) + let chatData = { + user: game.user.id, + rollMode: game.settings.get("core", "rollMode"), + whisper: [game.user.id].concat(ChatMessage.getWhisperRecipients('GM')), + content: `

${this.name} has gained 1 exp in the skill ${skill.name} (exp = ${skill.system.exp})= 25) { + await this.updateEmbeddedDocuments('Item', [{ _id: skill.id, 'system.exp': 0, 'system.explevel': skill.system.explevel + 1 }]) + let chatData = { + user: game.user.id, + rollMode: game.settings.get("core", "rollMode"), + whisper: [game.user.id].concat(ChatMessage.getWhisperRecipients('GM')), + content: `
${this.name} has gained 1 exp SL in the skill ${skill.name} (new exp SL : ${skill.system.explevel}) != 0) { + const updated = await this.updateEmbeddedDocuments('Item', [{ _id: objetQ.id, 'system.quantity': newQ }]) // pdates one EmbeddedEntity + } + } + } + /* -------------------------------------------- */ + async incDecAmmo(objetId, incDec = 0) { + let objetQ = this.items.get(objetId) + if (objetQ) { + let newQ = objetQ.system.ammocurrent + incDec; + if (newQ >= 0 && newQ <= objetQ.system.ammomax) { + const updated = await this.updateEmbeddedDocuments('Item', [{ _id: objetQ.id, 'system.ammocurrent': newQ }]); // pdates one EmbeddedEntity + } + } + } + + /* -------------------------------------------- */ + isForcedAdvantage() { + return this.items.find(cond => cond.type == "condition" && cond.system.advantage) + } + isForcedDisadvantage() { + return this.items.find(cond => cond.type == "condition" && cond.system.disadvantage) + } + isForcedRollAdvantage() { + return this.items.find(cond => cond.type == "condition" && cond.system.rolladvantage) + } + isForcedRollDisadvantage() { + return this.items.find(cond => cond.type == "condition" && cond.system.rolldisadvantage) + } + isNoAdvantage() { + return this.items.find(cond => cond.type == "condition" && cond.system.noadvantage) + } + isNoAction() { + return this.items.find(cond => cond.type == "condition" && cond.system.noaction) + } + isAttackDisadvantage() { + return this.items.find(cond => cond.type == "condition" && cond.system.attackdisadvantage) + } + isDefenseDisadvantage() { + return this.items.find(cond => cond.type == "condition" && cond.system.defensedisadvantage) + } + isAttackerAdvantage() { + return this.items.find(cond => cond.type == "condition" && cond.system.targetadvantage) + } + + /* -------------------------------------------- */ + getCommonRollData(abilityKey = undefined) { + let noAction = this.isNoAction() + if (noAction) { + ui.notifications.warn("You can't do any actions du to the condition : " + noAction.name) + return + } + + let rollData = DarkStarsUtility.getBasicRollData() + rollData.alias = this.name + rollData.actorImg = this.img + rollData.actorId = this.id + rollData.img = this.img + rollData.featsDie = this.getFeatsWithDie() + rollData.featsSL = this.getFeatsWithSL() + rollData.armors = this.getArmors() + rollData.conditions = this.getConditions() + rollData.featDieName = "none" + rollData.featSLName = "none" + rollData.rollAdvantage = "none" + rollData.advantage = "none" + rollData.disadvantage = "none" + rollData.forceAdvantage = this.isForcedAdvantage() + rollData.forceDisadvantage = this.isForcedDisadvantage() + rollData.forceRollAdvantage = this.isForcedRollAdvantage() + rollData.forceRollDisadvantage = this.isForcedRollDisadvantage() + rollData.noAdvantage = this.isNoAdvantage() + if (rollData.defenderTokenId) { + let defenderToken = game.canvas.tokens.get(rollData.defenderTokenId) + let defender = defenderToken.actor + + // Distance management + let token = this.token + if (!token) { + let tokens = this.getActiveTokens() + token = tokens[0] + } + if (token) { + const ray = new Ray(token.object?.center || token.center, defenderToken.center) + rollData.tokensDistance = canvas.grid.measureDistances([{ ray }], { gridSpaces: false })[0] / canvas.grid.grid.options.dimensions.distance + } else { + ui.notifications.info("No token connected to this actor, unable to compute distance.") + return + } + if (defender) { + rollData.forceAdvantage = defender.isAttackerAdvantage() + rollData.advantageFromTarget = true + } + } + + if (abilityKey) { + rollData.ability = this.getAbility(abilityKey) + rollData.selectedKill = undefined + } + + console.log("ROLLDATA", rollData) + + return rollData + } + + /* -------------------------------------------- */ + rollAbility(abilityKey) { + let rollData = this.getCommonRollData(abilityKey) + rollData.mode = "ability" + if (rollData.target) { + ui.notifications.warn("You are targetting a token with a skill : please use a Weapon instead.") + return + } + DarkStarsUtility.rollDarkStars(rollData) + } + + /* -------------------------------------------- */ + rollSkill(skillId) { + let skill = this.items.get(skillId) + if (skill) { + if (skill.system.islore && skill.system.level == 0) { + ui.notifications.warn("You can't use Lore Skills with a SL of 0.") + return + } + skill = duplicate(skill) + DarkStarsUtility.updateSkill(skill) + let abilityKey = skill.system.ability + let rollData = this.getCommonRollData(abilityKey) + rollData.mode = "skill" + rollData.skill = skill + rollData.img = skill.img + if (rollData.target) { + ui.notifications.warn("You are targetting a token with a skill : please use a Weapon instead.") + return + } + this.startRoll(rollData) + } + } + + /* -------------------------------------------- */ + rollWeapon(weaponId) { + let weapon = this.items.get(weaponId) + if (weapon) { + weapon = duplicate(weapon) + let skill = this.items.find(item => item.name.toLowerCase() == weapon.system.skill.toLowerCase()) + if (skill) { + skill = duplicate(skill) + DarkStarsUtility.updateSkill(skill) + let abilityKey = skill.system.ability + let rollData = this.getCommonRollData(abilityKey) + rollData.mode = "weapon" + rollData.skill = skill + rollData.weapon = weapon + rollData.img = weapon.img + if (!rollData.forceDisadvantage) { // This is an attack, check if disadvantaged + rollData.forceDisadvantage = this.isAttackDisadvantage() + } + /*if (rollData.weapon.system.isranged && rollData.tokensDistance > DarkStarsUtility.getWeaponMaxRange(rollData.weapon) ) { + ui.notifications.warn(`Your target is out of range of your weapon (max: ${DarkStarsUtility.getWeaponMaxRange(rollData.weapon)} - current : ${rollData.tokensDistance})` ) + return + }*/ + this.startRoll(rollData) + } else { + ui.notifications.warn("Unable to find the relevant skill for weapon " + weapon.name) + } + } + } + + /* -------------------------------------------- */ + rollDefenseMelee(attackRollData) { + let weapon = this.items.get(attackRollData.defenseWeaponId) + if (weapon) { + weapon = duplicate(weapon) + let skill = this.items.find(item => item.name.toLowerCase() == weapon.system.skill.toLowerCase()) + if (skill) { + skill = duplicate(skill) + DarkStarsUtility.updateSkill(skill) + let abilityKey = skill.system.ability + let rollData = this.getCommonRollData(abilityKey) + rollData.defenderTokenId = undefined // Cleanup + rollData.mode = "weapondefense" + rollData.shield = this.getEquippedShield() + rollData.attackRollData = duplicate(attackRollData) + rollData.skill = skill + rollData.weapon = weapon + rollData.img = weapon.img + if (!rollData.forceDisadvantage) { // This is an attack, check if disadvantaged + rollData.forceDisadvantage = this.isDefenseDisadvantage() + } + + this.startRoll(rollData) + } else { + ui.notifications.warn("Unable to find the relevant skill for weapon " + weapon.name) + } + } else { + ui.notifications.warn("Weapon not found ! ") + } + } + + /* -------------------------------------------- */ + rollDefenseRanged(attackRollData) { + let rollData = this.getCommonRollData() + rollData.defenderTokenId = undefined // Cleanup + rollData.mode = "rangeddefense" + if ( attackRollData) { + rollData.attackRollData = duplicate(attackRollData) + rollData.effectiveRange = DarkStarsUtility.getWeaponRange(attackRollData.weapon) + rollData.tokensDistance = attackRollData.tokensDistance // QoL copy + } + rollData.sizeDice = DarkStarsUtility.getSizeDice(this.system.biodata.size) + rollData.distanceBonusDice = 0 //Math.max(0, Math.floor((rollData.tokensDistance - rollData.effectiveRange) + 0.5)) + rollData.hasCover = "none" + rollData.situational = "none" + rollData.useshield = false + rollData.shield = this.getEquippedShield() + this.startRoll(rollData) + } + + /* -------------------------------------------- */ + rollShieldDie() { + let shield = this.getEquippedShield() + if (shield) { + shield = duplicate(shield) + let rollData = this.getCommonRollData() + rollData.mode = "shield" + rollData.shield = shield + rollData.useshield = true + rollData.img = shield.img + this.startRoll(rollData) + } + } + + /* -------------------------------------------- */ + async rollArmorDie(rollData = undefined) { + let armor = this.getEquippedArmor() + if (armor) { + armor = duplicate(armor) + let reduce = 0 + let multiply = 1 + let disadvantage = false + let advantage = false + let messages = ["Armor applied"] + + if (rollData) { + if (DarkStarsUtility.isArmorLight(armor) && DarkStarsUtility.isWeaponPenetrating(rollData.attackRollData.weapon)) { + return { armorIgnored: true, nbSuccess: 0, messages: ["Armor ignored : Penetrating weapons ignore Light Armors."] } + } + if (DarkStarsUtility.isWeaponPenetrating(rollData.attackRollData.weapon)) { + messages.push("Armor reduced by 1 (Penetrating weapon)") + reduce = 1 + } + if (DarkStarsUtility.isWeaponLight(rollData.attackRollData.weapon)) { + messages.push("Armor with advantage (Light weapon)") + advantage = true + } + if (DarkStarsUtility.isWeaponHeavy(rollData.attackRollData.weapon)) { + messages.push("Armor with disadvantage (Heavy weapon)") + disadvantage = true + } + if (DarkStarsUtility.isWeaponHack(rollData.attackRollData.weapon)) { + messages.push("Armor reduced by 1 (Hack weapon)") + reduce = 1 + } + if (DarkStarsUtility.isWeaponUndamaging(rollData.attackRollData.weapon)) { + messages.push("Armor multiplied by 2 (Undamaging weapon)") + multiply = 2 + } + } + let diceColor = armor.system.absorprionroll + let armorResult = await DarkStarsUtility.getRollTableFromDiceColor(diceColor, false) + console.log("Armor log", armorResult) + let armorValue = Math.max(0, (Number(armorResult.text) + reduce) * multiply) + if (advantage || disadvantage) { + let armorResult2 = await DarkStarsUtility.getRollTableFromDiceColor(diceColor, false) + let armorValue2 = Math.max(0, (Number(armorResult2.text) + reduce) * multiply) + if (advantage) { + armorValue = (armorValue2 > armorValue) ? armorValue2 : armorValue + messages.push(`Armor advantage - Roll 1 = ${armorValue} - Roll 2 = ${armorValue2}`) + } + if (disadvantage) { + armorValue = (armorValue2 < armorValue) ? armorValue2 : armorValue + messages.push(`Armor disadvantage - Roll 1 = ${armorValue} - Roll 2 = ${armorValue2}`) + } + } + armorResult.armorValue = armorValue + if (!rollData) { + ChatMessage.create({ content: "Armor result : " + armorValue }) + } + messages.push("Armor result : " + armorValue) + return { armorIgnored: false, nbSuccess: armorValue, rawArmor: armorResult.text, messages: messages } + } + return { armorIgnored: true, nbSuccess: 0, messages: ["No armor equipped."] } + } + + /* -------------------------------------------- */ + rollSave(saveKey) { + let saves = this.getSaveRoll() + let save = saves[saveKey] + if (save) { + save = duplicate(save) + let rollData = this.getCommonRollData() + rollData.mode = "save" + rollData.save = save + if (rollData.target) { + ui.notifications.warn("You are targetting a token with a save roll - Not authorized.") + return + } + this.startRoll(rollData) + } + + } + /* -------------------------------------------- */ + async startRoll(rollData) { + this.syncRoll(rollData) + let rollDialog = await DarkStarsRollDialog.create(this, rollData) + rollDialog.render(true) + } + +} diff --git a/modules/dark-stars-combat.js b/modules/dark-stars-combat.js new file mode 100644 index 0000000..e13d1d8 --- /dev/null +++ b/modules/dark-stars-combat.js @@ -0,0 +1,30 @@ +import { DarkStarsUtility } from "./dark-stars-utility.js"; + +/* -------------------------------------------- */ +export class DarkStarsCombat extends Combat { + + /* -------------------------------------------- */ + async rollInitiative(ids, formula = undefined, messageOptions = {} ) { + ids = typeof ids === "string" ? [ids] : ids; + for (let cId = 0; cId < ids.length; cId++) { + const c = this.combatants.get(ids[cId]); + let id = c._id || c.id; + let initBonus = c.actor ? c.actor.getInitiativeScore( this.id, id ) : -1; + await this.updateEmbeddedDocuments("Combatant", [ { _id: id, initiative: initBonus } ]); + } + + return this; + } + + /* -------------------------------------------- */ + _onUpdate(changed, options, userId) { + } + + /* -------------------------------------------- */ + static async checkTurnPosition() { + while (game.combat.turn > 0) { + await game.combat.previousTurn() + } + } + +} diff --git a/modules/dark-stars-commands.js b/modules/dark-stars-commands.js new file mode 100644 index 0000000..f096902 --- /dev/null +++ b/modules/dark-stars-commands.js @@ -0,0 +1,144 @@ +/* -------------------------------------------- */ + +import { DarkStarsUtility } from "./dark-stars-utility.js"; +import { DarkStarsRollDialog } from "./dark-stars-roll-dialog.js"; + +/* -------------------------------------------- */ +const __saveFirstToKey = { r: "reflex", f: "fortitude", w: "willpower"} + +/* -------------------------------------------- */ +export class DarkStarsCommands { + + static init() { + if (!game.system.cruciblerpg.commands) { + const crucibleCommands = new DarkStarsCommands(); + crucibleCommands.registerCommand({ path: ["/rtarget"], func: (content, msg, params) => DarkStarsCommands.rollTarget(msg, params), descr: "Launch the target roll window" }); + crucibleCommands.registerCommand({ path: ["/rsave"], func: (content, msg, params) => DarkStarsCommands.rollSave(msg, params), descr: "Performs a save roll" }); + game.system.cruciblerpg.commands = crucibleCommands; + } + } + + constructor() { + this.commandsTable = {}; + } + + /* -------------------------------------------- */ + registerCommand(command) { + this._addCommand(this.commandsTable, command.path, '', command); + } + + /* -------------------------------------------- */ + _addCommand(targetTable, path, fullPath, command) { + if (!this._validateCommand(targetTable, path, command)) { + return; + } + const term = path[0]; + fullPath = fullPath + term + ' ' + if (path.length == 1) { + command.descr = `${fullPath}: ${command.descr}`; + targetTable[term] = command; + } + else { + if (!targetTable[term]) { + targetTable[term] = { subTable: {} }; + } + this._addCommand(targetTable[term].subTable, path.slice(1), fullPath, command) + } + } + + /* -------------------------------------------- */ + _validateCommand(targetTable, path, command) { + if (path.length > 0 && path[0] && command.descr && (path.length != 1 || targetTable[path[0]] == undefined)) { + return true; + } + console.warn("crucibleCommands._validateCommand failed ", targetTable, path, command); + return false; + } + + + /* -------------------------------------------- */ + /* Manage chat commands */ + processChatCommand(commandLine, content = '', msg = {}) { + // Setup new message's visibility + let rollMode = game.settings.get("core", "rollMode"); + if (["gmroll", "blindroll"].includes(rollMode)) msg["whisper"] = ChatMessage.getWhisperRecipients("GM"); + if (rollMode === "blindroll") msg["blind"] = true; + msg["type"] = 0; + + let command = commandLine[0].toLowerCase(); + let params = commandLine.slice(1); + + return this.process(command, params, content, msg); + } + + /* -------------------------------------------- */ + process(command, params, content, msg) { + return this._processCommand(this.commandsTable, command, params, content, msg); + } + + /* -------------------------------------------- */ + _processCommand(commandsTable, name, params, content = '', msg = {}, path = "") { + console.log("===> Processing command") + let command = commandsTable[name]; + path = path + name + " "; + if (command && command.subTable) { + if (params[0]) { + return this._processCommand(command.subTable, params[0], params.slice(1), content, msg, path) + } + else { + this.help(msg, command.subTable); + return true; + } + } + if (command && command.func) { + const result = command.func(content, msg, params); + if (result == false) { + DarkStarsCommands._chatAnswer(msg, command.descr); + } + return true; + } + return false; + } + + /* -------------------------------------------- */ + static _chatAnswer(msg, content) { + msg.whisper = [game.user.id]; + msg.content = content; + ChatMessage.create(msg); + } + + /* -------------------------------------------- */ + static rollTarget(msg, params) { + const speaker = ChatMessage.getSpeaker() + let actor + if (speaker.token) actor = game.actors.tokens[speaker.token] + if (!actor) actor = game.actors.get(speaker.actor) + if (!actor) { + return ui.notifications.warn(`Select your actor to run the macro`) + } + actor.rollDefenseRanged() + } + + /* -------------------------------------------- */ + static rollSave(msg, params) { + console.log(msg, params) + if ( params.length == 0) { + ui.notifications.warn("/rsave command error : syntax is /rsave reflex, /rsave fortitude or /rsave willpower") + return + } + let saveKey = params[0].toLowerCase() + if ( saveKey.length > 0 && (saveKey[0] == "r" || saveKey[0] == "f" || saveKey[0] == "w")) { + const speaker = ChatMessage.getSpeaker() + let actor + if (speaker.token) actor = game.actors.tokens[speaker.token] + if (!actor) actor = game.actors.get(speaker.actor) + if (!actor) { + return ui.notifications.warn(`Select your actor to run the macro`) + } + actor.rollSave( __saveFirstToKey[saveKey[0]] ) + } else { + ui.notifications.warn("/rsave syntax error : syntax is /rsave reflex, /rsave fortitude or /rsave willpower") + } + } + +} \ No newline at end of file diff --git a/modules/dark-stars-hotbar.js b/modules/dark-stars-hotbar.js new file mode 100644 index 0000000..708f81a --- /dev/null +++ b/modules/dark-stars-hotbar.js @@ -0,0 +1,68 @@ + +export class DarkStarsHotbar { + + static async addToHotbar(item, slot) { + let command = `game.system.cruciblerpg.DarkStarsHotbar.rollMacro("${item.name}", "${item.type}");`; + let macro = game.macros.contents.find(m => (m.name === item.name) && (m.command === command)); + if (!macro) { + macro = await Macro.create({ + name: item.name, + type: "script", + img: item.img, + command: command + }, { displaySheet: false }) + } + await game.user.assignHotbarMacro(macro, slot); + } + + /** + * Create a macro when dropping an entity on the hotbar + * Item - open roll dialog for item + * Actor - open actor sheet + * Journal - open journal sheet + */ + static initDropbar() { + + Hooks.on("hotbarDrop", (bar, documentData, slot) => { + + // Create item macro if rollable item - weapon, spell, prayer, trait, or skill + if (documentData.type == "Item") { + let item = fromUuidSync(documentData.uuid) + if (item == undefined) { + item = this.actor.items.get(documentData.uuid) + } + if (item && (item.type =="weapon" || item.type =="skill")) { + this.addToHotbar(item, slot) + return false + } + } + + return true; + }); + } + + /** Roll macro */ + static rollMacro(itemName, itemType, bypassData) { + const speaker = ChatMessage.getSpeaker() + let actor + if (speaker.token) actor = game.actors.tokens[speaker.token] + if (!actor) actor = game.actors.get(speaker.actor) + if (!actor) { + return ui.notifications.warn(`Select your actor to run the macro`) + } + + let item = actor.items.find(it => it.name === itemName && it.type == itemType) + if (!item) { + return ui.notifications.warn(`Unable to find the item of the macro in the current actor`) + } + + // Trigger the item roll + if (item.type === "weapon") { + return actor.rollWeapon(item.id) + } + if (item.type === "skill") { + return actor.rollSkill(item.id) + } + } + +} diff --git a/modules/dark-stars-item-sheet.js b/modules/dark-stars-item-sheet.js new file mode 100644 index 0000000..46c8bfa --- /dev/null +++ b/modules/dark-stars-item-sheet.js @@ -0,0 +1,170 @@ +import { DarkStarsUtility } from "./dark-stars-utility.js"; + +/** + * Extend the basic ItemSheet with some very simple modifications + * @extends {ItemSheet} + */ +export class DarkStarsItemSheet extends ItemSheet { + + /** @override */ + static get defaultOptions() { + + return mergeObject(super.defaultOptions, { + classes: ["fvtt-dark-stars", "sheet", "item"], + template: "systems/fvtt-dark-stars/templates/item-sheet.html", + dragDrop: [{ dragSelector: null, dropSelector: null }], + width: 620, + height: 550, + tabs: [{navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description"}] + }); + } + + /* -------------------------------------------- */ + _getHeaderButtons() { + let buttons = super._getHeaderButtons(); + // Add "Post to chat" button + // We previously restricted this to GM and editable items only. If you ever find this comment because it broke something: eh, sorry! + buttons.unshift( + { + class: "post", + icon: "fas fa-comment", + onclick: ev => { } + }) + return buttons + } + + /* -------------------------------------------- */ + /** @override */ + setPosition(options = {}) { + const position = super.setPosition(options); + const sheetBody = this.element.find(".sheet-body"); + const bodyHeight = position.height - 192; + sheetBody.css("height", bodyHeight); + if (this.item.type.includes('weapon')) { + position.width = 640; + } + return position; + } + + /* -------------------------------------------- */ + async getData() { + + if ( this.object.type == "skill") { + DarkStarsUtility.updateSkill(this.object) + } + let objectData = duplicate(this.object.system) + + let itemData = objectData + let formData = { + title: this.title, + id: this.id, + type: this.object.type, + img: this.object.img, + name: this.object.name, + editable: this.isEditable, + cssClass: this.isEditable ? "editable" : "locked", + weaponSkills: DarkStarsUtility.getWeaponSkills(), + shieldSkills: DarkStarsUtility.getShieldSkills(), + description: await TextEditor.enrichHTML(this.object.system.description, {async: true}), + data: itemData, + limited: this.object.limited, + options: this.options, + owner: this.document.isOwner, + isGM: game.user.isGM + } + + this.options.editable = !(this.object.origin == "embeddedItem"); + console.log("ITEM DATA", formData, this); + return formData; + } + + + /* -------------------------------------------- */ + _getHeaderButtons() { + let buttons = super._getHeaderButtons(); + buttons.unshift({ + class: "post", + icon: "fas fa-comment", + onclick: ev => this.postItem() + }); + return buttons + } + + /* -------------------------------------------- */ + postItem() { + let chatData = duplicate(DarkStarsUtility.data(this.item)); + if (this.actor) { + chatData.actor = { id: this.actor.id }; + } + // Don't post any image for the item (which would leave a large gap) if the default image is used + if (chatData.img.includes("/blank.png")) { + chatData.img = null; + } + // JSON object for easy creation + chatData.jsondata = JSON.stringify( + { + compendium: "postedItem", + payload: chatData, + }); + + renderTemplate('systems/fvtt-dark-stars/templates/post-item.html', chatData).then(html => { + let chatOptions = DarkStarsUtility.chatDataSetup(html); + ChatMessage.create(chatOptions) + }); + } + + /* -------------------------------------------- */ + /** @override */ + activateListeners(html) { + super.activateListeners(html); + + // Everything below here is only needed if the sheet is editable + if (!this.options.editable) return; + + + // Update Inventory Item + html.find('.item-edit').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + const item = this.object.options.actor.getOwnedItem(li.data("item-id")); + item.sheet.render(true); + }); + + html.find('.delete-spec').click(ev => { + this.object.update({ "data.specialisation": [{ name: 'None' }] }); + }); + + html.find('.delete-subitem').click(ev => { + this.deleteSubitem(ev); + }); + + // Update Inventory Item + html.find('.item-delete').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + let itemType = li.data("item-type"); + }); + + html.find('.view-subitem').click(ev => { + this.viewSubitem(ev); + }); + + html.find('.view-spec').click(ev => { + this.manageSpec(); + }); + + } + + + + /* -------------------------------------------- */ + get template() { + let type = this.item.type; + return `systems/fvtt-dark-stars/templates/item-${type}-sheet.html`; + } + + /* -------------------------------------------- */ + /** @override */ + _updateObject(event, formData) { + return this.object.update(formData) + } +} \ No newline at end of file diff --git a/modules/dark-stars-item.js b/modules/dark-stars-item.js new file mode 100644 index 0000000..b824946 --- /dev/null +++ b/modules/dark-stars-item.js @@ -0,0 +1,25 @@ +import { DarkStarsUtility } from "./dark-stars-utility.js"; + +export const defaultItemImg = { + skill: "systems/fvtt-dark-stars/images/icons/icon_skill.webp", + armor: "systems/fvtt-dark-stars/images/icons/icon_armour.webp", + weapon: "systems/fvtt-dark-stars/images/icons/icon_weapon.webp", + equipment: "systems/fvtt-dark-stars/images/icons/icon_equipment.webp", + race: "systems/fvtt-dark-stars/images/icons/icon_race.webp", + money: "systems/fvtt-dark-stars/images/icons/icon_money.webp", +} + +/** + * Extend the basic ItemSheet with some very simple modifications + * @extends {ItemSheet} + */ +export class DarkStarsItem extends Item { + + constructor(data, context) { + if (!data.img) { + data.img = defaultItemImg[data.type]; + } + super(data, context); + } + +} diff --git a/modules/dark-stars-main.js b/modules/dark-stars-main.js new file mode 100644 index 0000000..1238788 --- /dev/null +++ b/modules/dark-stars-main.js @@ -0,0 +1,116 @@ +/** + * DarkStars system + * Author: Uberwald + * Software License: Prop + */ + +/* -------------------------------------------- */ + +/* -------------------------------------------- */ +// Import Modules +import { DarkStarsActor } from "./dark-stars-actor.js"; +import { DarkStarsItemSheet } from "./dark-stars-item-sheet.js"; +import { DarkStarsActorSheet } from "./dark-stars-actor-sheet.js"; +import { DarkStarsNPCSheet } from "./dark-stars-npc-sheet.js"; +import { DarkStarsUtility } from "./dark-stars-utility.js"; +import { DarkStarsCombat } from "./dark-stars-combat.js"; +import { DarkStarsItem } from "./dark-stars-item.js"; +import { DarkStarsHotbar } from "./dark-star-shotbar.js" +import { DarkStarsCommands } from "./dark-stars-commands.js" + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ + +/************************************************************************************/ +Hooks.once("init", async function () { + + console.log(`Initializing DarkStars RPG`); + + game.system.darkstars = { + DarkStarsCommands + } + + /* -------------------------------------------- */ + // preload handlebars templates + DarkStarsUtility.preloadHandlebarsTemplates(); + + /* -------------------------------------------- */ + // Set an initiative formula for the system + CONFIG.Combat.initiative = { + formula: "1d6", + decimals: 1 + }; + + /* -------------------------------------------- */ + game.socket.on("system.fvtt-dark-stars", data => { + DarkStarsUtility.onSocketMesssage(data) + }); + + /* -------------------------------------------- */ + // Define custom Entity classes + CONFIG.Combat.documentClass = DarkStarsCombat + CONFIG.Actor.documentClass = DarkStarsActor + CONFIG.Item.documentClass = DarkStarsItem + + /* -------------------------------------------- */ + // Register sheet application classes + Actors.unregisterSheet("core", ActorSheet); + Actors.registerSheet("fvtt-dark-stars", DarkStarsActorSheet, { types: ["character"], makeDefault: true }); + Actors.registerSheet("fvtt-dark-stars", DarkStarsNPCSheet, { types: ["npc"], makeDefault: false }); + + Items.unregisterSheet("core", ItemSheet); + Items.registerSheet("fvtt-dark-stars", DarkStarsItemSheet, { makeDefault: true }); + + DarkStarsUtility.init() +}); + +/* -------------------------------------------- */ +function welcomeMessage() { + ChatMessage.create({ + user: game.user.id, + whisper: [game.user.id], + content: `
+ Welcome to the DarkStars RPG. + ` }); +} + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ +Hooks.once("ready", function () { + + // User warning + if (!game.user.isGM && game.user.character == undefined) { + ui.notifications.info("Warning ! No character linked to your user !"); + ChatMessage.create({ + content: "WARNING The player " + game.user.name + " is not linked to a character !", + user: game.user._id + }); + } + + // CSS patch for v9 + if (game.version) { + let sidebar = document.getElementById("sidebar"); + sidebar.style.width = "min-content"; + } + + welcomeMessage(); + DarkStarsUtility.ready() + DarkStarsCommands.init() +}) + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ +Hooks.on("chatMessage", (html, content, msg) => { + if (content[0] == '/') { + let regExp = /(\S+)/g; + let commands = content.match(regExp); + if (game.system.cruciblerpg.commands.processChatCommand(commands, content, msg)) { + return false; + } + } + return true; +}); + diff --git a/modules/dark-stars-npc-sheet.js b/modules/dark-stars-npc-sheet.js new file mode 100644 index 0000000..fe219c5 --- /dev/null +++ b/modules/dark-stars-npc-sheet.js @@ -0,0 +1,209 @@ +/** + * Extend the basic ActorSheet with some very simple modifications + * @extends {ActorSheet} + */ + +import { DarkStarsUtility } from "./dark-stars-utility.js"; + +/* -------------------------------------------- */ +export class DarkStarsNPCSheet extends ActorSheet { + + /** @override */ + static get defaultOptions() { + + return mergeObject(super.defaultOptions, { + classes: ["dark-stars-rpg", "sheet", "actor"], + template: "systems/fvtt-dark-stars/templates/npc-sheet.html", + width: 640, + height: 720, + tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "stats" }], + dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }], + editScore: true + }); + } + + /* -------------------------------------------- */ + async getData() { + const objectData = this.object.system + let actorData = duplicate(objectData) + + let formData = { + title: this.title, + id: this.actor.id, + type: this.actor.type, + img: this.actor.img, + name: this.actor.name, + editable: this.isEditable, + cssClass: this.isEditable ? "editable" : "locked", + data: actorData, + limited: this.object.limited, + skills: this.actor.getSkills( ), + weapons: this.actor.checkAndPrepareEquipments( duplicate(this.actor.getWeapons()) ), + armors: this.actor.checkAndPrepareEquipments( duplicate(this.actor.getArmors())), + shields: this.actor.checkAndPrepareEquipments( duplicate(this.actor.getShields())), + spells: this.actor.checkAndPrepareEquipments( duplicate(this.actor.getLore())), + equipments: this.actor.checkAndPrepareEquipments(duplicate(this.actor.getEquipmentsOnly()) ), + equippedWeapons: this.actor.checkAndPrepareEquipments(duplicate(this.actor.getEquippedWeapons()) ), + equippedArmor: this.actor.getEquippedArmor(), + equippedShield: this.actor.getEquippedShield(), + feats: duplicate(this.actor.getFeats()), + subActors: duplicate(this.actor.getSubActors()), + race: duplicate(this.actor.getRace()), + moneys: duplicate(this.actor.getMoneys()), + encCapacity: this.actor.getEncumbranceCapacity(), + saveRolls: this.actor.getSaveRoll(), + conditions: this.actor.getConditions(), + containersTree: this.actor.containersTree, + encCurrent: this.actor.encCurrent, + options: this.options, + owner: this.document.isOwner, + editScore: this.options.editScore, + isGM: game.user.isGM + } + this.formData = formData; + + console.log("PC : ", formData, this.object); + return formData; + } + + /* -------------------------------------------- */ + /** @override */ + activateListeners(html) { + super.activateListeners(html); + + // Everything below here is only needed if the sheet is editable + if (!this.options.editable) return; + + html.bind("keydown", function(e) { // Ignore Enter in actores sheet + if (e.keyCode === 13) return false; + }); + + // Update Inventory Item + html.find('.item-edit').click(ev => { + const li = $(ev.currentTarget).parents(".item") + let itemId = li.data("item-id") + const item = this.actor.items.get( itemId ); + item.sheet.render(true); + }); + // Delete Inventory Item + html.find('.item-delete').click(ev => { + const li = $(ev.currentTarget).parents(".item") + DarkStarsUtility.confirmDelete(this, li) + }) + html.find('.item-add').click(ev => { + let dataType = $(ev.currentTarget).data("type") + this.actor.createEmbeddedDocuments('Item', [{ name: "NewItem", type: dataType }], { renderSheet: true }) + }) + + html.find('.equip-activate').click(ev => { + const li = $(ev.currentTarget).parents(".item") + let itemId = li.data("item-id") + this.actor.equipActivate( itemId) + }); + html.find('.equip-deactivate').click(ev => { + const li = $(ev.currentTarget).parents(".item") + let itemId = li.data("item-id") + this.actor.equipDeactivate( itemId) + }); + + html.find('.subactor-edit').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let actorId = li.data("actor-id"); + let actor = game.actors.get( actorId ); + actor.sheet.render(true); + }); + + html.find('.subactor-delete').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let actorId = li.data("actor-id"); + this.actor.delSubActor(actorId); + }); + html.find('.quantity-minus').click(event => { + const li = $(event.currentTarget).parents(".item"); + this.actor.incDecQuantity( li.data("item-id"), -1 ); + } ); + html.find('.quantity-plus').click(event => { + const li = $(event.currentTarget).parents(".item"); + this.actor.incDecQuantity( li.data("item-id"), +1 ); + } ); + + html.find('.ammo-minus').click(event => { + const li = $(event.currentTarget).parents(".item") + this.actor.incDecAmmo( li.data("item-id"), -1 ); + } ); + html.find('.ammo-plus').click(event => { + const li = $(event.currentTarget).parents(".item") + this.actor.incDecAmmo( li.data("item-id"), +1 ) + } ); + + html.find('.roll-ability').click((event) => { + const abilityKey = $(event.currentTarget).data("ability-key"); + this.actor.rollAbility(abilityKey); + }); + html.find('.roll-skill').click((event) => { + const li = $(event.currentTarget).parents(".item") + const skillId = li.data("item-id") + this.actor.rollSkill(skillId) + }); + + html.find('.roll-weapon').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const skillId = li.data("item-id") + this.actor.rollWeapon(skillId) + }); + html.find('.roll-armor-die').click((event) => { + this.actor.rollArmorDie() + }); + html.find('.roll-shield-die').click((event) => { + this.actor.rollShieldDie() + }); + html.find('.roll-target-die').click((event) => { + this.actor.rollDefenseRanged() + }); + + html.find('.roll-save').click((event) => { + const saveKey = $(event.currentTarget).data("save-key") + this.actor.rollSave(saveKey) + }); + + + html.find('.lock-unlock-sheet').click((event) => { + this.options.editScore = !this.options.editScore; + this.render(true); + }); + html.find('.item-link a').click((event) => { + const itemId = $(event.currentTarget).data("item-id"); + const item = this.actor.getOwnedItem(itemId); + item.sheet.render(true); + }); + html.find('.item-equip').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + this.actor.equipItem( li.data("item-id") ); + this.render(true); + }); + + html.find('.update-field').change(ev => { + const fieldName = $(ev.currentTarget).data("field-name"); + let value = Number(ev.currentTarget.value); + this.actor.update( { [`${fieldName}`]: value } ); + }); + + } + + /* -------------------------------------------- */ + /** @override */ + setPosition(options = {}) { + const position = super.setPosition(options); + const sheetBody = this.element.find(".sheet-body"); + const bodyHeight = position.height - 192; + sheetBody.css("height", bodyHeight); + return position; + } + + /* -------------------------------------------- */ + /** @override */ + _updateObject(event, formData) { + // Update the Actor + return this.object.update(formData); + } +} diff --git a/modules/dark-stars-roll-dialog.js b/modules/dark-stars-roll-dialog.js new file mode 100644 index 0000000..2bab220 --- /dev/null +++ b/modules/dark-stars-roll-dialog.js @@ -0,0 +1,84 @@ +import { DarkStarsUtility } from "./dark-stars-utility.js"; + +export class DarkStarsRollDialog extends Dialog { + + /* -------------------------------------------- */ + static async create(actor, rollData) { + + let options = { classes: ["DarkStarsDialog"], width: 540, height: 340, 'z-index': 99999 }; + let html = await renderTemplate('systems/fvtt-dark-stars/templates/roll-dialog-generic.html', rollData); + + return new DarkStarsRollDialog(actor, rollData, html, options); + } + + /* -------------------------------------------- */ + constructor(actor, rollData, html, options, close = undefined) { + let conf = { + title: (rollData.mode == "skill") ? "Skill" : "Attribute", + content: html, + buttons: { + roll: { + icon: '', + label: "Roll !", + callback: () => { this.roll() } + }, + cancel: { + icon: '', + label: "Cancel", + callback: () => { this.close() } + } + }, + close: close + } + + super(conf, options); + + this.actor = actor; + this.rollData = rollData; + } + + /* -------------------------------------------- */ + roll() { + DarkStarsUtility.rollDarkStars(this.rollData) + } + + /* -------------------------------------------- */ + async refreshDialog() { + const content = await renderTemplate("systems/fvtt-dark-stars/templates/roll-dialog-generic.html", this.rollData) + this.data.content = content + this.render(true) + } + + /* -------------------------------------------- */ + activateListeners(html) { + super.activateListeners(html); + + var dialog = this; + function onLoad() { + } + $(function () { onLoad(); }); + + html.find('#advantage').change((event) => { + this.rollData.advantage = event.currentTarget.value + }) + html.find('#disadvantage').change((event) => { + this.rollData.disadvantage = event.currentTarget.value + }) + html.find('#rollAdvantage').change((event) => { + this.rollData.rollAdvantage = event.currentTarget.value + }) + html.find('#useshield').change((event) => { + this.rollData.useshield = event.currentTarget.checked + }) + html.find('#hasCover').change((event) => { + this.rollData.hasCover = event.currentTarget.value + }) + html.find('#situational').change((event) => { + this.rollData.situational = event.currentTarget.value + }) + html.find('#distanceBonusDice').change((event) => { + this.rollData.distanceBonusDice = Number(event.currentTarget.value) + }) + + } +} \ No newline at end of file diff --git a/modules/dark-stars-token.js b/modules/dark-stars-token.js new file mode 100644 index 0000000..6340ca8 --- /dev/null +++ b/modules/dark-stars-token.js @@ -0,0 +1,6 @@ +import { DarkStarsUtility } from "./dark-stars-utility.js"; + +/* -------------------------------------------- */ +export class DarkStarsToken extends Token { + +} diff --git a/modules/dark-stars-utility.js b/modules/dark-stars-utility.js new file mode 100644 index 0000000..9c963b1 --- /dev/null +++ b/modules/dark-stars-utility.js @@ -0,0 +1,595 @@ +/* -------------------------------------------- */ +import { DarkStarsCombat } from "./dark-stars-combat.js"; +import { DarkStarsCommands } from "./dark-stars-commands.js"; + +/* -------------------------------------------- */ + +/* -------------------------------------------- */ +export class DarkStarsUtility { + + + /* -------------------------------------------- */ + static async init() { + Hooks.on('renderChatLog', (log, html, data) => DarkStarsUtility.chatListeners(html)); + /*Hooks.on("dropCanvasData", (canvas, data) => { + DarkStarsUtility.dropItemOnToken(canvas, data) + });*/ + + DarkStarsCommands.init(); + + Handlebars.registerHelper('count', function (list) { + return list.length; + }) + Handlebars.registerHelper('includes', function (array, val) { + return array.includes(val); + }) + Handlebars.registerHelper('upper', function (text) { + return text.toUpperCase(); + }) + Handlebars.registerHelper('lower', function (text) { + return text.toLowerCase() + }) + Handlebars.registerHelper('upperFirst', function (text) { + if (typeof text !== 'string') return text + return text.charAt(0).toUpperCase() + text.slice(1) + }) + Handlebars.registerHelper('notEmpty', function (list) { + return list.length > 0; + }) + Handlebars.registerHelper('mul', function (a, b) { + return parseInt(a) * parseInt(b); + }) + + this.gameSettings() + + } + + /*-------------------------------------------- */ + static gameSettings() { + /*game.settings.register("fvtt-dark-stars", "dice-color-skill", { + name: "Dice color for skills", + hint: "Set the dice color for skills", + scope: "world", + config: true, + requiresReload: true , + default: "#101010", + type: String + }) + + Hooks.on('renderSettingsConfig', (event) => { + const element = event.element[0].querySelector(`[name='fvtt-dark-stars.dice-color-skill']`) + if (!element) return + // Replace placeholder element + console.log("Element Found !!!!") + }) */ + } + + /*-------------------------------------------- */ + static upperFirst(text) { + if (typeof text !== 'string') return text + return text.charAt(0).toUpperCase() + text.slice(1) + } + + /*-------------------------------------------- */ + static getSkills() { + return duplicate(this.skills) + } + /*-------------------------------------------- */ + static getWeaponSkills() { + return duplicate(this.weaponSkills) + } + /*-------------------------------------------- */ + static getShieldSkills() { + return duplicate(this.shieldSkills) + } + + /* -------------------------------------------- */ + static async ready() { + const skills = await DarkStarsUtility.loadCompendium("fvtt-dark-stars.skills") + this.skills = skills.map(i => i.toObject()) + this.weaponSkills = duplicate(this.skills.filter(item => item.system.isweaponskill)) + this.shieldSkills = duplicate(this.skills.filter(item => item.system.isshieldskill)) + + const rollTables = await DarkStarsUtility.loadCompendium("fvtt-dark-stars.rolltables") + this.rollTables = rollTables.map(i => i.toObject()) + + } + + /* -------------------------------------------- */ + static async loadCompendiumData(compendium) { + const pack = game.packs.get(compendium) + return await pack?.getDocuments() ?? [] + } + + /* -------------------------------------------- */ + static async loadCompendium(compendium, filter = item => true) { + let compendiumData = await DarkStarsUtility.loadCompendiumData(compendium) + return compendiumData.filter(filter) + } + + + /* -------------------------------------------- */ + static async chatListeners(html) { + + html.on("click", '.view-item-from-chat', event => { + game.system.crucible.creator.openItemView(event) + }) + html.on("click", '.roll-defense-melee', event => { + let rollId = $(event.currentTarget).data("roll-id") + let rollData = DarkStarsUtility.getRollData(rollId) + rollData.defenseWeaponId = $(event.currentTarget).data("defense-weapon-id") + let actor = game.canvas.tokens.get(rollData.defenderTokenId).actor + if (actor && (game.user.isGM || actor.isOwner)) { + actor.rollDefenseMelee(rollData) + } + }) + html.on("click", '.roll-defense-ranged', event => { + let rollId = $(event.currentTarget).data("roll-id") + let rollData = DarkStarsUtility.getRollData(rollId) + let defender = game.canvas.tokens.get(rollData.defenderTokenId).actor + if (defender && (game.user.isGM || defender.isOwner)) { + defender.rollDefenseRanged(rollData) + } + }) + + } + + /* -------------------------------------------- */ + static async preloadHandlebarsTemplates() { + + const templatePaths = [ + 'systems/fvtt-dark-stars/templates/partials/editor-notes-gm.html', + 'systems/fvtt-dark-stars/templates/partials/partial-roll-select.html', + 'systems/fvtt-dark-stars/templates/partials/partial-actor-ability-block.html', + 'systems/fvtt-dark-stars/templates/partials/partial-actor-status.html', + 'systems/fvtt-dark-stars/templates/partials/partial-options-abilities.html', + 'systems/fvtt-dark-stars/templates/partials/partial-item-nav.html', + 'systems/fvtt-dark-stars/templates/partials/partial-item-description.html', + 'systems/fvtt-dark-stars/templates/partials/partial-actor-equipment.html' + ] + return loadTemplates(templatePaths); + } + + /* -------------------------------------------- */ + static removeChatMessageId(messageId) { + if (messageId) { + game.messages.get(messageId)?.delete(); + } + } + + static findChatMessageId(current) { + return DarkStarsUtility.getChatMessageId(DarkStarsUtility.findChatMessage(current)); + } + + static getChatMessageId(node) { + return node?.attributes.getNamedItem('data-message-id')?.value; + } + + static findChatMessage(current) { + return DarkStarsUtility.findNodeMatching(current, it => it.classList.contains('chat-message') && it.attributes.getNamedItem('data-message-id')); + } + + static findNodeMatching(current, predicate) { + if (current) { + if (predicate(current)) { + return current; + } + return DarkStarsUtility.findNodeMatching(current.parentElement, predicate); + } + return undefined; + } + + + /* -------------------------------------------- */ + static createDirectOptionList(min, max) { + let options = {}; + for (let i = min; i <= max; i++) { + options[`${i}`] = `${i}`; + } + return options; + } + + /* -------------------------------------------- */ + static buildListOptions(min, max) { + let options = "" + for (let i = min; i <= max; i++) { + options += `` + } + return options; + } + + /* -------------------------------------------- */ + static getTarget() { + if (game.user.targets) { + for (let target of game.user.targets) { + return target + } + } + return undefined + } + + /* -------------------------------------------- */ + static async onSocketMesssage(msg) { + console.log("SOCKET MESSAGE", msg.name) + if (msg.name == "msg_update_roll") { + this.updateRollData(msg.data) + } + if (msg.name == "msg_gm_process_attack_defense") { + this.processSuccessResult(msg.data) + } + if (msg.name == "msg_gm_item_drop" && game.user.isGM) { + let actor = game.actors.get(msg.data.actorId) + let item + if (msg.data.isPack) { + item = await fromUuid("Compendium." + msg.data.isPack + "." + msg.data.itemId) + } else { + item = game.items.get(msg.data.itemId) + } + this.addItemDropToActor(actor, item) + } + } + + /* -------------------------------------------- */ + static chatDataSetup(content, modeOverride, isRoll = false, forceWhisper) { + let chatData = { + user: game.user.id, + rollMode: modeOverride || game.settings.get("core", "rollMode"), + content: content + }; + + if (["gmroll", "blindroll"].includes(chatData.rollMode)) chatData["whisper"] = ChatMessage.getWhisperRecipients("GM").map(u => u.id); + if (chatData.rollMode === "blindroll") chatData["blind"] = true; + else if (chatData.rollMode === "selfroll") chatData["whisper"] = [game.user]; + + if (forceWhisper) { // Final force ! + chatData["speaker"] = ChatMessage.getSpeaker(); + chatData["whisper"] = ChatMessage.getWhisperRecipients(forceWhisper); + } + + return chatData; + } + + /* -------------------------------------------- */ + static async showDiceSoNice(roll, rollMode) { + if (game.modules.get("dice-so-nice")?.active) { + if (game.dice3d) { + let whisper = null; + let blind = false; + rollMode = rollMode ?? game.settings.get("core", "rollMode"); + switch (rollMode) { + case "blindroll": //GM only + blind = true; + case "gmroll": //GM + rolling player + whisper = this.getUsers(user => user.isGM); + break; + case "roll": //everybody + whisper = this.getUsers(user => user.active); + break; + case "selfroll": + whisper = [game.user.id]; + break; + } + await game.dice3d.showForRoll(roll, game.user, true, whisper, blind); + } + } + } + + /* -------------------------------------------- */ + static async rollDarkStars(rollData) { + + let actor = game.actors.get(rollData.actorId) + + // ability/save/size => 0 + let diceFormula + let startFormula = "0d6cs>=5[blue]" + if (rollData.ability) { + startFormula = String(rollData.ability.value) + "d6cs>=5[blue]" + } + if (rollData.save) { + startFormula = String(rollData.save.value) + "d6cs>=5[blue]" + } + if (rollData.sizeDice) { + let nb = rollData.sizeDice.nb + rollData.distanceBonusDice + this.getDiceFromCover(rollData.hasCover) + this.getDiceFromSituational(rollData.situational) + startFormula = String(nb) + String(rollData.sizeDice.dice) + "cs>=5[blue]" + } + diceFormula = startFormula + + // skill => 2 + // feat => 4 + // bonus => 6 + if (rollData.skill) { + let level = rollData.skill.system.level + if (rollData.skill.system.issl2) { + rollData.hasSLBonus = true + level += 2 + if (level > 7) { level = 7 } + } + rollData.skill.system.skilldice = __skillLevel2Dice[level] + diceFormula += "+" + String(rollData.skill.system.skilldice) + "cs>=5[black]" + + if (rollData.skill.system.skilltype == "complex" && rollData.skill.system.level == 0) { + rollData.complexSkillDisadvantage = true + rollData.rollAdvantage = "roll-disadvantage" + } + + if (rollData.skill.system.isfeatdie) { + rollData.hasFeatDie = true + diceFormula += "+ 1d10cs>=5[dark-stars-purple]" + } else { + diceFormula += `+ 0d10cs>=5[dark-stars-purple]` + } + if (rollData.skill.system.bonusdice != "none") { + rollData.hasBonusDice = rollData.skill.system.bonusdice + diceFormula += `+ ${rollData.hasBonusDice}cs>=5[black]` + } else { + diceFormula += `+ 0d6cs>=5[black]` + } + } else { + diceFormula += `+ 0d8cs=>5 + 0d10cs>=5 + 0d6cs>=5` + } + + // advantage => 8 + let advFormula = "+ 0d8cs>=5" + if (rollData.advantage == "advantage1" || rollData.forceAdvantage) { + advFormula = "+ 1d8cs>=5[dark-stars-darkgreen]" + } + if (rollData.advantage == "advantage2") { + advFormula = "+ 2d8cs>=5[dark-stars-darkgreen]" + } + diceFormula += advFormula + + // disadvantage => 10 + let disFormula = "- 0d8cs>=5" + if (rollData.disadvantage == "disadvantage1" || rollData.forceDisadvantage) { + disFormula = "- 1d8cs>=5[red]" + } + if (rollData.disadvantage == "disadvantage2") { + disFormula = "- 2d8cs>=5[red]" + } + diceFormula += disFormula + + // armor => 12 + let skillArmorPenalty = 0 + for (let armor of rollData.armors) { + if (armor.system.equipped) { + skillArmorPenalty += armor.system.skillpenalty + } + } + if (rollData.skill && rollData.skill.system.armorpenalty && skillArmorPenalty > 0) { + rollData.skillArmorPenalty = skillArmorPenalty + diceFormula += `- ${skillArmorPenalty}d8cs>=5` + } else { + diceFormula += `- 0d8cs>=5` + } + + // shield => 14 + if (rollData.useshield && rollData.shield) { + diceFormula += "+ 1" + String(rollData.shield.system.shielddie) + "cs>=5[yellow]" + } else { + diceFormula += " + 0d6cs>=5" + } + + // Performs roll + console.log("Roll formula", diceFormula) + let myRoll = rollData.roll + if (!myRoll) { // New rolls only of no rerolls + myRoll = new Roll(diceFormula).roll({ async: false }) + await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode")) + } + rollData.rollOrder = 0 + rollData.roll = myRoll + rollData.nbSuccess = myRoll.total + + if (rollData.rollAdvantage == "none" && rollData.forceRollAdvantage) { + rollData.rollAdvantage = "roll-advantage" + } + if (rollData.rollAdvantage == "none" && rollData.forceRollDisadvantage) { + rollData.rollAdvantage = "roll-disadvantage" + } + if (rollData.rollAdvantage != "none") { + + rollData.rollOrder = 1 + rollData.rollType = (rollData.rollAdvantage == "roll-advantage") ? "Advantage" : "Disadvantage" + this.createChatWithRollMode(rollData.alias, { + content: await renderTemplate(`systems/fvtt-dark-stars/templates/chat-generic-result.html`, rollData) + }) + + rollData.rollOrder = 2 + let myRoll2 = new Roll(diceFormula).roll({ async: false }) + await this.showDiceSoNice(myRoll2, game.settings.get("core", "rollMode")) + + rollData.roll = myRoll2 // Tmp switch to display the proper results + rollData.nbSuccess = myRoll2.total + this.createChatWithRollMode(rollData.alias, { + content: await renderTemplate(`systems/fvtt-dark-stars/templates/chat-generic-result.html`, rollData) + }) + rollData.roll = myRoll // Revert the tmp switch + rollData.nbSuccess = myRoll.total + + if (rollData.rollAdvantage == "roll-advantage") { + if (myRoll2.total > rollData.nbSuccess) { + hasChanged = true + rollData.roll = myRoll2 + rollData.nbSuccess = myRoll2.total + } + } else { + if (myRoll2.total < rollData.nbSuccess) { + rollData.roll = myRoll2 + rollData.nbSuccess = myRoll2.total + } + } + rollData.rollOrder = 3 + } + rollData.nbSuccess = Math.max(0, rollData.nbSuccess) + + rollData.isFirstRollAdvantage = false + // Manage exp + if (rollData.skill && rollData.skill.system.level > 0) { + let nbSkillSuccess = rollData.roll.terms[2].total + if (nbSkillSuccess == 0 || nbSkillSuccess == rollData.skill.system.level) { + actor.incrementSkillExp(rollData.skill.id, 1) + } + } + + this.saveRollData(rollData) + actor.lastRoll = rollData + + this.createChatWithRollMode(rollData.alias, { + content: await renderTemplate(`systems/fvtt-dark-stars/templates/chat-generic-result.html`, rollData) + }) + console.log("Rolldata result", rollData) + + // Message response + this.displayDefenseMessage(rollData) + + // Manage defense result + this.processAttackDefense(rollData) + } + + /* -------------------------------------------- */ + static sortArrayObjectsByName(myArray) { + myArray.sort((a, b) => { + let fa = a.name.toLowerCase(); + let fb = b.name.toLowerCase(); + if (fa < fb) { + return -1; + } + if (fa > fb) { + return 1; + } + return 0; + }) + } + + /* -------------------------------------------- */ + static getUsers(filter) { + return game.users.filter(filter).map(user => user.id); + } + /* -------------------------------------------- */ + static getWhisperRecipients(rollMode, name) { + switch (rollMode) { + case "blindroll": return this.getUsers(user => user.isGM); + case "gmroll": return this.getWhisperRecipientsAndGMs(name); + case "selfroll": return [game.user.id]; + } + return undefined; + } + /* -------------------------------------------- */ + static getWhisperRecipientsAndGMs(name) { + let recep1 = ChatMessage.getWhisperRecipients(name) || []; + return recep1.concat(ChatMessage.getWhisperRecipients('GM')); + } + + /* -------------------------------------------- */ + static blindMessageToGM(chatOptions) { + let chatGM = duplicate(chatOptions); + chatGM.whisper = this.getUsers(user => user.isGM); + chatGM.content = "Blinde message of " + game.user.name + "
" + chatOptions.content; + console.log("blindMessageToGM", chatGM); + game.socket.emit("system.fvtt-dark-stars", { msg: "msg_gm_chat_message", data: chatGM }); + } + + + /* -------------------------------------------- */ + static async searchItem(dataItem) { + let item + if (dataItem.pack) { + item = await fromUuid("Compendium." + dataItem.pack + "." + dataItem.id) + } else { + item = game.items.get(dataItem.id) + } + return item + } + + /* -------------------------------------------- */ + static split3Columns(data) { + + let array = [[], [], []]; + if (data == undefined) return array; + + let col = 0; + for (let key in data) { + let keyword = data[key]; + keyword.key = key; // Self-reference + array[col].push(keyword); + col++; + if (col == 3) col = 0; + } + return array; + } + + /* -------------------------------------------- */ + static createChatMessage(name, rollMode, chatOptions) { + switch (rollMode) { + case "blindroll": // GM only + if (!game.user.isGM) { + this.blindMessageToGM(chatOptions); + + chatOptions.whisper = [game.user.id]; + chatOptions.content = "Message only to the GM"; + } + else { + chatOptions.whisper = this.getUsers(user => user.isGM); + } + break; + default: + chatOptions.whisper = this.getWhisperRecipients(rollMode, name); + break; + } + chatOptions.alias = chatOptions.alias || name; + ChatMessage.create(chatOptions); + } + + /* -------------------------------------------- */ + static getBasicRollData() { + let rollData = { + rollId: randomID(16), + rollMode: game.settings.get("core", "rollMode"), + advantage: "none" + } + DarkStarsUtility.updateWithTarget(rollData) + return rollData + } + + /* -------------------------------------------- */ + static updateWithTarget(rollData) { + let target = DarkStarsUtility.getTarget() + if (target) { + rollData.defenderTokenId = target.id + } + } + + /* -------------------------------------------- */ + static createChatWithRollMode(name, chatOptions) { + this.createChatMessage(name, game.settings.get("core", "rollMode"), chatOptions) + } + + /* -------------------------------------------- */ + static async confirmDelete(actorSheet, li) { + let itemId = li.data("item-id"); + let msgTxt = "

Are you sure to remove this Item ?"; + let buttons = { + delete: { + icon: '', + label: "Yes, remove it", + callback: () => { + actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]); + li.slideUp(200, () => actorSheet.render(false)); + } + }, + cancel: { + icon: '', + label: "Cancel" + } + } + msgTxt += "

"; + let d = new Dialog({ + title: "Confirm removal", + content: msgTxt, + buttons: buttons, + default: "cancel" + }); + d.render(true); + } + +} \ No newline at end of file diff --git a/styles/simple.css b/styles/simple.css new file mode 100644 index 0000000..90f2697 --- /dev/null +++ b/styles/simple.css @@ -0,0 +1,1497 @@ + /* ==================== (A) Fonts ==================== */ + + :root { + /* =================== 1. ACTOR SHEET FONT STYLES =========== */ + --window-header-title-font-size: 1.3rem; + --window-header-title-font-weight: normal; + --window-header-title-color: #f5f5f5; + + --major-button-font-size: 1.05rem; + --major-button-font-weight: normal; + --major-button-color: #dadada; + + --tab-header-font-size: 1.0rem; + --tab-header-font-weight: 700; + --tab-header-color: #403f3e; + --tab-header-color-active: #4a0404; + + --actor-input-font-size: 0.8rem; + --actor-input-font-weight: 500; + --actor-input-color: black; + + --actor-label-font-size: 0.8rem; + --actor-label-font-weight: 700; + --actor-label-color: #464331c4; + + /* =================== 2. DEBUGGING HIGHLIGHTERS ============ */ + --debug-background-color-red: #ff000054; + --debug-background-color-blue: #1d00ff54; + --debug-background-color-green: #54ff0054; + + --debug-box-shadow-red: inset 0 0 2px red; + --debug-box-shadow-blue: inset 0 0 2px blue; + --debug-box-shadow-green: inset 0 0 2px green; + } + +/*@import url("https://fonts.googleapis.com/css2?family=Martel:wght@400;800&family=Roboto:wght@300;400;500&display=swap");*/ +/* Global styles & Font */ +.window-app { + text-align: justify; + font-size: 16px; + letter-spacing: 1px; +} + +/* Fonts */ +.sheet header.sheet-header h1 input, .window-app .window-header, #actors .directory-list, #navigation #scene-list .scene.nav-item { + font-size: 1.0rem; +} /* For title, sidebar character and scene */ +.sheet nav.sheet-tabs { + font-size: 0.8rem; +} /* For nav and title */ +.window-app input, .foundryvtt-vadentis .item-form, .sheet header.sheet-header .flex-group-center.flex-compteurs, .sheet header.sheet-header .flex-group-center.flex-fatigue, select, button, .item-checkbox, #sidebar, #players, #navigation #nav-toggle { + font-size: 0.8rem; +} + +.window-header{ + background: rgba(0,0,0,0.75); +} + +.window-app.sheet .window-content { + margin: 0; + padding: 0; +} +.strong-text{ + font-weight: bold; +} + +.tabs .item.active, .blessures-list li ul li:first-child:hover, a:hover { + text-shadow: 1px 0px 0px #ff6600; +} + +.rollable:hover, .rollable:focus { + color: #000; + text-shadow: 0 0 10px red; + cursor: pointer; +} +input:disabled { + color:#1c2058; +} +select:disabled { + color:#1c2058; +} +table {border: 1px solid #7a7971;} + +.grid, .grid-2col { + display: grid; + grid-column: span 2 / span 2; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + margin: 10px 0; + padding: 0; +} + +.grid-3col { + grid-column: span 3 / span 3; + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.grid-4col { + grid-column: span 4 / span 4; + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.grid-5col { + grid-column: span 5 / span 5; + grid-template-columns: repeat(5, minmax(0, 1fr)); +} + +.grid-6col { + grid-column: span 5 / span 5; + grid-template-columns: repeat(5, minmax(0, 1fr)); +} + +.grid-7col { + grid-column: span 7 / span 7; + grid-template-columns: repeat(7, minmax(0, 1fr)); +} + +.grid-8col { + grid-column: span 8 / span 8; + grid-template-columns: repeat(8, minmax(0, 1fr)); +} + +.grid-9col { + grid-column: span 9 / span 9; + grid-template-columns: repeat(9, minmax(0, 1fr)); +} + +.grid-10col { + grid-column: span 10 / span 10; + grid-template-columns: repeat(10, minmax(0, 1fr)); +} + +.grid-11col { + grid-column: span 11 / span 11; + grid-template-columns: repeat(11, minmax(0, 1fr)); +} + +.grid-12col { + grid-column: span 12 / span 12; + grid-template-columns: repeat(12, minmax(0, 1fr)); +} + +.flex-group-center, +.flex-group-left, +.flex-group-right { + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + text-align: center; + padding: 5px; +} + +.flex-group-left { + -webkit-box-pack: start; + -ms-flex-pack: start; + justify-content: flex-start; + text-align: left; +} + +.flex-group-right { + -webkit-box-pack: end; + -ms-flex-pack: end; + justify-content: flex-end; + text-align: right; +} + +.flex-center { + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + text-align: center; +} + +.table-create-actor { + font-size: 0.8rem; +} + +.flex-between { + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +.flex-shrink { + flex: 'flex-shrink' ; +} + +/* Styles limited to foundryvtt-vadentis sheets */ + +.fvtt-dark-stars .sheet-header { + -webkit-box-flex: 0; + -ms-flex: 0 0 210px; + flex: 0 0 210px; + overflow: hidden; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -webkit-box-pack: start; + -ms-flex-pack: start; + justify-content: flex-start; + margin-bottom: 10px; +} + +.fvtt-dark-stars .sheet-header .profile-img { + -webkit-box-flex: 0; + -ms-flex: 0 0 128px; + flex: 0 0 128px; + width: 196px; + height: auto; + max-height:260px; + margin-top: 0px; + margin-right: 10px; + object-fit: cover; + object-position: 50% 0; +} + +.button-img { + vertical-align: baseline; + width: 8%; + height: 8%; + max-height: 48px; + border-width: 0; + border: 1px solid rgba(0, 0, 0, 0); +} +.button-img:hover { + color: rgba(255, 255, 128, 0.7); + border: 1px solid rgba(255, 128, 0, 0.8); + cursor: pointer; +} + +.button-effect-img { + vertical-align: baseline; + width: 16px; + max-height: 16px; + height: 16; + border-width: 0; +} + +.small-button-container { + height: 16px; + width: 16px; + border: 0; + vertical-align: bottom; +} + +.fvtt-dark-stars .sheet-header .header-fields { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.fvtt-dark-stars .sheet-header h1.charname { + height: 50px; + padding: 0px; + margin: 5px 0; + border-bottom: 0; +} + +.fvtt-dark-stars .sheet-header h1.charname input { + width: 100%; + height: 100%; + margin: 0; +} + +.fvtt-dark-stars .sheet-tabs { + -webkit-box-flex: 0; + -ms-flex: 0; + flex: 0; +} + +.fvtt-dark-stars .sheet-body, +.fvtt-dark-stars .sheet-body .tab, +.fvtt-dark-stars .sheet-body .tab .editor { + height: 100%; + font-size: 0.8rem; +} + +.editor { + border: 2; + height: 300px; + padding: 0 3px; +} + +.medium-editor { + border: 2; + height: 240px; + padding: 0 3px; +} + +.small-editor { + border: 2; + height: 120px; + padding: 0 3px; +} + +.fvtt-dark-stars .tox .tox-editor-container { + background: #fff; +} + +.fvtt-dark-stars .tox .tox-edit-area { + padding: 0 8px; +} + +.fvtt-dark-stars .resource-label { + font-weight: bold; + text-transform: uppercase; +} + +.fvtt-dark-stars .tabs { + height: 40px; + border-top: 1px solid #AAA; + border-bottom: 1px solid #AAA; + color: #000000; +} + +.fvtt-dark-stars .tabs .item { + line-height: 40px; + font-weight: bold; +} + +.fvtt-dark-stars .tabs .item.active { + text-decoration: underline; + text-shadow: none; +} + +.fvtt-dark-stars .items-list { + list-style: none; + margin: 1px 0; + padding: 0; + overflow-y: auto; +} + +.fvtt-dark-stars .items-list .item-header { + font-weight: bold; +} + +.fvtt-dark-stars .items-list .item { + height: 30px; + line-height: 24px; + padding: 1px 0; + border-bottom: 1px solid #BBB; +} + +.fvtt-dark-stars .items-list .item .item-image { + -webkit-box-flex: 0; + -ms-flex: 0 0 24px; + flex: 0 0 24px; + margin-right: 5px; +} + +.fvtt-dark-stars .items-list .item img { + display: block; +} + +.fvtt-dark-stars .items-list .item-name { + margin: 0; +} + +.fvtt-dark-stars .items-list .item-controls { + -webkit-box-flex: 0; + -ms-flex: 0 0 86px; + flex: 0 0 86px; + text-align: right; +} + + +/* ======================================== */ +/* Sheet */ +.window-app.sheet .window-content .sheet-header{ + background: url("../images/ui/pc_sheet_bg.webp") +} +/* background: #011d33 url("../images/ui/fond1.webp") repeat left top;*/ +/*color: rgba(168, 139, 139, 0.5);*/ + +.window-app.sheet .window-content .sheet-header input[type="text"], .window-app.sheet .window-content .sheet-header input[type="number"], .window-app.sheet .window-content .sheet-header input[type="password"], .window-app.sheet .window-content .sheet-header input[type="date"], .window-app.sheet .window-content .sheet-header input[type="time"] { + color: rgba(36, 37, 37, 0.75); + background: rgba(245, 245, 241, 0.95); + border: 1 none; + margin-bottom: 0.25rem; + margin-left: 2px; +} + +.window-app.sheet .window-content .sheet-body input[type="text"], .window-app.sheet .window-content .sheet-body input[type="number"], .window-app.sheet .window-content .sheet-body input[type="password"], .window-app.sheet .window-content .sheet-body input[type="date"], .window-app.sheet .window-content .sheet-body input[type="time"] { + color: rgba(36, 37, 37, 0.75); + background: rgba(245, 245, 241, 0.95); + border: 1 none; + margin-bottom: 0.25rem; + margin-left: 2px; +} + +.window-app.sheet .window-content .sheet-body select, .window-app.sheet .window-content .sheet-header select { + color: rgba(36, 37, 37, 0.75); + background: rgba(245, 245, 241, 0.95); + border: 1 none; + margin-bottom: 0.25rem; + margin-left: 2px; +} + +.window-app .window-content, .window-app.sheet .window-content .sheet-body{ + font-size: 0.8rem; + background: url("../images/ui/pc_sheet_bg.webp") repeat left top; +} + +/* background: rgba(245,245,240,0.6) url("../images/ui/sheet_background.webp") left top;*/ + +section.sheet-body{padding: 0.25rem 0.5rem;} + +.sheet header.sheet-header .profile-img { + object-fit: cover; + object-position: 50% 0; + margin: 0.5rem 0 0.5rem 0.5rem; + padding: 0; +} + +.sheet nav.sheet-tabs { + font-size: 0.70rem; + font-weight: bold; + height: 3rem; + flex: 0 0 3rem; + margin: 0; + padding: 0 0 0 0.25rem; + text-align: center; + text-transform: uppercase; + line-height: 1.5rem; + border-top: 0 none; + border-bottom: 0 none; + background-color:black; + color:beige; +} + +/* background: rgb(245,245,240) url("../images/ui/fond4.webp") repeat left top;*/ + +nav.sheet-tabs .item { + position: relative; + padding: 0 0.25rem; +} + +nav.sheet-tabs .item:after { + content: ""; + position: absolute; + top: 0; + right: 0; + height: 2rem; + width: 1px; + border-right: 1px dashed rgba(52, 52, 52, 0.25); +} + +.sheet .tab[data-tab] { + padding: 0; +} + +section.sheet-body:after { + content: ""; + display: block; + clear: both; +} + +.sheet header.sheet-header .flex-compteurs {text-align: right;} +.sheet header.sheet-header .resource-content {width: 2rem;} + +.select-diff { + display: inline-block; + text-align: left; + width: 50px; +} + +.window-app.sheet .window-content .tooltip:hover .tooltiptext { + top: 2rem; + left: 2rem; + margin: 0; + padding: 0.25rem; +} + +.window-app.sheet .window-content .carac-value, .window-app.sheet .window-content .competence-xp { + margin: 0.05rem; + flex-basis: 3rem; + text-align: center; +} + +/* ======================================== */ +/* Global UI elements */ + +/* ======================================== */ + +h1, h2, h3, h4 { + font-weight: bold; +} + +ul, ol { + margin: 0; + padding: 0; +} +ul, li { + list-style-type: none; +} + +.sheet li { + margin: 0.010rem; + padding: 0.25rem; +} +.header-fields li { + margin: 0; + padding: 0; +} + +.alterne-list > .list-item:hover { + background: rgba(100, 100, 50, 0.25); +} +.alterne-list > .list-item:nth-child(even) { + background: rgba(80, 60, 0, 0.10); +} +.alterne-list > .list-item:nth-child(odd) { + background: rgb(160, 130, 100, 0.05); +} + +.specialisation-label { + font-size: 0.8rem; +} + +.carac-label, +.attr-label { + font-weight: bold; +} + +.list-item { + margin: 0.125rem; + box-shadow: inset 0px 0px 1px #00000096; + border-radius: 0.25rem; + padding: 0.125rem; + flex: 1 1 5rem; + display: flex !important; +} +.list-item-shadow { + background:rgba(87, 60, 32, 0.35); + flex-grow: 0; + flex-wrap: nowrap; + justify-content: flex-start; +} +.list-item-shadow2 { + background:rgba(87, 60, 32, 0.25); + flex-grow: 0; + flex-wrap: nowrap; + justify-content: flex-start; +} +.item-display-show { + display: block; +} +.item-display-hide { + display: none; +} +.conteneur-type { + background: rgb(200, 10, 100, 0.25); +} +.item-quantite { + margin-left: 0.5rem; +} +.list-item-margin1 { + margin-left: 1rem; +} +.list-item-margin2 { + margin-left: 2rem; +} +.list-item-margin3 { + margin-left: 3rem; +} +.list-item-margin4 { + margin-left: 4rem; +} + +.sheet-competence-img { + width: 24px; + max-width: 24px; + height: 24px; + max-height: 24px; + flex-grow: 0; + margin-right: 0.25rem; +} +.competence-column { + flex-direction: column; + align-content: flex-start; + justify-content: flex-start; + flex-grow: 0; + flex-basis: 1; +} +.competence-header { + align-content: flex-start; + justify-content: flex-start; + font-weight: bold; + flex-grow: 0; +} +.secondaire-label, +.arme-label, +.generic-label, +.competence-label, +.devotion-label, +.sort-label, +.technique-label, +.ability-label, +.arme-label, +.armure-label, +.equipement-label, +.description-label { + flex-grow: 2; + margin-left: 4px; +} +.status-header-label { + margin-left: 2px; +} +.roll-dialog-label { + margin: 4px 0; + min-width: 96px; +} +.short-label { + flex-grow: 1; +} +.keyword-label { + font-size: 0.85rem; +} + +.item-sheet-label { + flex-grow: 1; +} + +.item-text-long-line { + flex-grow: 3; +} + +.score-label { + flex-grow: 2; + align-content: center; +} + +.attribut-value, +.carac-value { + flex-grow: 0; + flex-basis: 64px; + margin-right: 4px; + margin-left: 4px; +} +.sante-value, +.competence-value { + flex-grow: 0; + flex-basis: 2rem; + margin-right: 0.25rem; + margin-left: 0.25rem; +} +.description-value { + flex-grow: 0; + flex-basis: 4rem; + margin-right: 0.25rem; + margin-left: 0.25rem; +} +.competence-xp { + flex-grow: 0; + flex-basis: 2rem; + margin-right: 0.25rem; + margin-left: 0.25rem; +} +.blessures-title { + font-weight: bold; +} +.alchimie-title { + font-weight: bold; +} +.blessure-data { + flex-direction: row; + align-content: flex-start; + justify-content: flex-start; +} +.blessures-soins { + flex-grow: 0; + flex-basis: 32px; + margin-right: 4px; + margin-left: 4px; +} +.blessures-loc { + flex-grow: 0; + flex-basis: 96px; + margin-right: 4px; + margin-left: 4px; +} +.pointsreve-value { + flex-grow: 0; + flex-basis: 64px; + margin-right: 4px; + margin-left: 4px; +} + +.input-sante-header, +.stress-style { + flex-grow: 0; + flex-basis: 64px; + margin-right: 4px; + margin-left: 4px; +} + +.small-label { + margin-top: 5px; +} + +.padd-right { + margin-right: 8px; +} +.padd-left { + margin-left: 8px; +} + +.stack-left { + align-items:center; + flex-shrink: 1; + flex-grow: 0; +} +.npc-ability-label { + flex-grow: 2; +} + +.packed-left { + white-space: nowrap; + flex-grow: 0; +} + +.input-numeric-short { + width: 40px; + max-width: 40px; + flex-grow: 0; + flex-shrink: 0; + flex-basis: 40px; + margin-right: 0.25rem; + margin-left: 0.25rem; +} + +.abilities-table { + align-content: flex-start; +} + +/* ======================================== */ +.tokenhudext { + display: flex; + flex: 0 !important; + font-weight: 600; +} +.tokenhudext.left { + justify-content: flex-start; + flex-direction: column; + position: absolute; + top: 2.75rem; + right: 4rem; +} +.tokenhudext.right { + justify-content: flex-start; + flex-direction: column; + position: absolute; + top: 2.75rem; + left: 4rem; +} +.control-icon.tokenhudicon { + width: fit-content; + height: fit-content; + min-width: 6rem; + flex-basis: auto; + padding: 0; + line-height: 1rem; + margin: 0.25rem; +} +.control-icon.tokenhudicon.right { + margin-left: 8px; +} +#token-hud .status-effects.active{ + z-index: 2; +} +/* ======================================== */ +.item-checkbox { + height: 25px; + border: 1px solid #736953a6; + border-left: none; + font-weight: 500; + font-size: 1rem; + color: black; + padding-top: 5px; + margin-right: 0px; + width: 45px; + position: relative; + left: 0px; + text-align: center; +} + + +.flex-actions-bar { + flex-grow: 2; +} + +/* ======================================== */ +/* Sidebar CSS */ +#sidebar { + font-size: 1rem; + background-position: 100%; + color: rgba(220,220,220,0.75); +} + +/* background: rgb(105,85,65) url("../images/ui/texture_feuille_perso_onglets.webp") no-repeat right bottom;*/ + +#sidebar.collapsed { + height: 470px !important; +} + +#sidebar-tabs > .collapsed, #chat-controls .chat-control-icon { + color: rgba(220,220,220,0.75); + text-shadow: 1px 1px 0 rgba(0,0,0,0.75); +} + +.sidebar-tab .directory-list .entity { + border-top: 1px dashed rgba(0,0,0,0.25); + border-bottom: 0 none; + padding: 0.25rem 0; +} + +.sidebar-tab .directory-list .entity:hover { + background: rgba(0,0,0,0.05); + cursor: pointer; +} +.chat-message-header { + background: rgba(220,220,210,0.5); + font-size: 1.1rem; + height: 48px; + text-align: center; + vertical-align: middle; + display: flex; + align-items: center; +} + +.chat-message .message-header .flavor-text, .chat-message .message-header .whisper-to { + font-size: 0.9rem; +} +.chat-actor-name { + padding: 4px; +} + +.chat-img { + width: 64px; + height: 64px; +} + +.roll-dialog-header { + height: 52px; +} + +.actor-icon { + float: left; + width: 48px; + height: 48px; + padding: 2px 6px 2px 2px; +} + +.padding-dice { + padding-top: .2rem; + padding-bottom: .2rem; +} + +.dice-image { + box-sizing: border-box; + border: none; + border-radius: 0; + max-width: 100%; +} + +.dice-image-reroll { + background-color:rgba(115, 224, 115, 0.25); + border-color: #011d33; + box-sizing: border-box; + border: 1px; + border-radius: 0%; + max-width: 100%; +} + +.chat-dice { + width: 15%; + height: 15%; + font-size: 15px; + padding: 10px; + padding-bottom: 20px; + padding-top: .2rem; + padding-bottom: .2rem; +} + +.div-river-full { + height: 5rem; + align-items: flex-start; +} + +.div-river { + align-content: center; + margin-left: 8px; + align-content:space-around; + justify-content: space-around; +} + +.div-center { + align-self: center; +} + +.chat-message { + background: rgba(220,220,210,0.5); + font-size: 0.9rem; +} + +.chat-message.whisper { + background: rgba(220,220,210,0.75); + border: 2px solid #545469; +} + +.chat-message .chat-icon { + border: 0; + padding: 2px 6px 2px 2px; + float: left; + width: 64px; + height: 64px; +} + +.ability-icon { + border: 0; + padding: 2px 2px 2px 2px; + max-width:32px; + max-height:32px; + width: auto; + height: auto; +} +.small-ability-icon { + border: 0; + padding: 2px 2px 2px 2px; + max-width:16px; + max-height:16px; + width: auto; + height: auto; +} +.combat-icon { + border: 0; + padding: 2px 2px 2px 2px; + max-width:24px; + max-height:24px; + width: auto; + height: auto; +} + +#sidebar-tabs { + flex: 0 0 32px; + box-sizing: border-box; + margin: 0 0 5px; + border-bottom: 1px solid rgba(0,0,0,0); + box-shadow: inset 0 0 2rem rgba(0,0,0,0.5); +} + +#sidebar-tabs > .item.active { + border: 1px solid rgba(114,98,72,1); + background: rgba(30, 25, 20, 0.75); + box-shadow: 0 0 6px inset rgba(114,98,72,1); +} + +#sidebar #sidebar-tabs i{ + width: 25px; + height: 25px; + display: inline-block; + background-position:center; + background-size:cover; + text-shadow: 1px 1px 0 rgba(0,0,0,0.75); + +} + +/*--------------------------------------------------------------------------*/ +/* Control, Tool, hotbar & navigation */ + +#controls .scene-control, #controls .control-tool { + box-shadow: 0 0 3px #000; + margin: 0 0 8px; + border-radius: 0; + background: rgba(30, 25, 20, 1); + background-origin: padding-box; + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; +} + +#controls .scene-control.active, #controls .control-tool.active, #controls .scene-control:hover, #controls .control-tool:hover { + background: rgba(72, 46, 28, 1); + background-origin: padding-box; + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; + box-shadow: 0 0 3px #ff6400; +} + +#hotbar #action-bar #macro-list { + border: 1px solid rgba(72, 46, 28, 1); + box-shadow: 2px 2px 5px #000000; +} + +#hotbar #action-bar .macro { + border-image: url(img/ui/bg_control.jpg) 21 repeat; + border-image-slice: 6 6 6 6 fill; + border-image-width: 6px 6px 6px 6px; + border-image-outset: 0px 0px 0px 0px; + border-radius: 0px; +} + +#hotbar .bar-controls { + background: rgba(30, 25, 20, 1); + border: 1px solid rgba(72, 46, 28, 1); +} + +#players { + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; + background: rgba(30, 25, 20, 1); +} + +#navigation #scene-list .scene.nav-item.active { + background: rgba(72, 46, 28, 1); +} + +#navigation #scene-list .scene.nav-item { + background: rgba(30, 25, 20, 1); + background-origin: padding-box; + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; +} + +#navigation #scene-list .scene.view, #navigation #scene-list .scene.context { + background: rgba(72, 46, 28, 1); + background-origin: padding-box; + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; + box-shadow: 0 0 3px #ff6400; +} + +#navigation #nav-toggle { + background: rgba(30, 25, 20, 1); + background-origin: padding-box; + border-image: url(img/ui/footer-button.png) 10 repeat; + border-image-width: 4px; + border-image-outset: 0px; +} + +/* Tooltip container */ +.tooltip { + position: relative; + display: inline-block; + /*border-bottom: 1px dotted black; /* If you want dots under the hoverable text */ +} + +/* Tooltip text */ +.tooltip .tooltiptext { + text-align: left; + background: rgba(231, 229, 226, 0.9); + width: 150px; + padding: 3px 0; + font-size: 0.9rem; + + /* Position the tooltip text */ + top: 1px; + position: absolute; + z-index: 1; + + /* Fade in tooltip */ + visibility: hidden; + opacity: 0; + transition: opacity 0.3s; +} + +.tooltip .ttt-fatigue{ + width: 360px; + + background: rgba(30, 25, 20, 0.9); + border-image: url(img/ui/bg_control.jpg) 21 repeat; + border-image-slice: 6 6 6 6 fill; + border-image-width: 6px 6px 6px 6px; + border-image-outset: 0px 0px 0px 0px; + border-radius: 0px; + + font-size: 0.8rem; + padding: 3px 0; +} + +.tooltip .ttt-ajustements { + width: 150px; + background: rgba(220,220,210,0.95); + border-radius: 6px; + font-size: 0.9rem; + padding: 3px 0; +} + +.tooltip-nobottom { + border-bottom: unset; /* If you want dots under the hoverable text */ +} +.tooltip .ttt-xp { + width: 250px; + background: rgba(220,220,210,0.95); + border-radius: 6px; + font-size: 0.9rem; + padding: 3px 0; +} + +/* Show the tooltip text when you mouse over the tooltip container */ +.tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; +} + +.river-button { + box-shadow: inset 0px 1px 0px 0px #a6827e; + background: linear-gradient(to bottom, #21374afc 5%, #152833ab 100%); + background-color: #7d5d3b00; + border-radius: 3px; + border: 2px ridge #846109; + display: inline-block; + cursor: pointer; + color: #ffffff; + font-size: 0.8rem; + padding: 2px 4px 0px 4px; + text-decoration: none; + text-shadow: 0px 1px 0px #4d3534; + position: relative; + margin:4px; +} + +.chat-card-button { + box-shadow: inset 0px 1px 0px 0px #a6827e; + background: linear-gradient(to bottom, #21374afc 5%, #152833ab 100%); + background-color: #7d5d3b00; + border-radius: 3px; + border: 2px ridge #846109; + display: inline-block; + cursor: pointer; + color: #ffffff; + font-size: 0.8rem; + padding: 4px 12px 0px 12px; + text-decoration: none; + text-shadow: 0px 1px 0px #4d3534; + position: relative; + margin:2px; +} + +.chat-card-button:hover { + background: linear-gradient(to bottom, #800000 5%, #3e0101 100%); + background-color: red; +} +.chat-card-button:active { + position:relative; + top:1px; +} + +.plus-minus-button { + box-shadow: inset 0px 1px 0px 0px #a6827e; + background: linear-gradient(to bottom, #21374afc 5%, #152833ab 100%); + background-color: #7d5d3b00; + border-radius: 2px; + border: 1px ridge #846109; + display: inline-block; + cursor: pointer; + color: #ffffff; + margin: 2px 2px 2px 2px; + padding: 2px 2px 2px 2px; + text-decoration: none; + text-shadow: 0px 1px 0px #4d3534; + position: relative; + margin:0px; +} + +.river-button:hover, +.plus-minus-button:hover, +.chat-card-button:hover { + background: linear-gradient(to bottom, #800000 5%, #3e0101 100%); + background-color: red; +} + +.plus-minus-button:active, +.chat-card-button:active { + position:relative; + top:1px; +} + +.plus-minus { + font-size: 0.9rem; + font-weight: bold; +} + +.ul-level1 { + padding-left: 2rem; +} + +.drop-equipment-effect, +.drop-power-effect, +.drop-perk-effect, +.drop-ability-effect, +.drop-effect-specaffected, +.drop-effect-spec, +.drop-ability-weapon, +.drop-ability-armor, +.drop-race-perk, +.drop-spec-perk, +.drop-ability-power, +.drop-ability-spec, +.drop-spec-power, +.drop-specialability, +.drop-abilities, +.drop-optionnal-abilities, +.drop-virtue-vice-effect, +.drop-virtue-vice, +.drop-vice-virtue, +.drop-specialperk1, +.drop-perk2, +.drop-spec1 , +.drop-spec2 { + background: linear-gradient(to bottom, #6c95b9fc 5%, #105177ab 100%); + background-color: #7d5d3b00; + border-radius: 3px; + border: 2px ridge #846109; +} + +/*************************************************************/ +#pause +{ + font-size: 2rem; +} +#pause > h3 +{ + color: #CCC +} +#pause > img { + content: url(../images/ui/crucible_pause_logo.jpg); + height: 160px; + width: 160px; + top: -80px; + left: calc(50% - 132px); +} + +#logo { + content : url(../images/ui/crucible_game_logo.png); + width: 100px; + height: 60px; +} + +.dice-cell { + padding-left: 12px; + padding-right: 12px; + width: 60px; + text-align: center; +} + +.dice-formula, +.dice-total { + height: 54px; + position:relative; +} + +/* =================== 1. ACTOR SHEET FONT STYLES =========== *//* +Agility AGI: #02a41d Also Used for Ranged Damage +Mind MND: #a100fe +Social SOC: #fd7100 +Strength STR: #5f3d00 Also Used For Melee Damage +Physique PHY: #990304 Also used For Damage Resistance +Combat COM: 0136ff Also Used for Melee Attack +Defence DEF: #88826a Also used in the Defence on Combat Tab +Stealth STL: #505050 +Perception PER: #f9c801 Also Used for Ranged Damage +Focus FOC: #ff0084 +*/ +.color-class-black { + background-color: black; + background: black; +} +.color-class-agi, +.color-class-range { + background-color: #02a41d; + background: #02a41d; +} +.color-class-pool { + background-color:#c5c3c3; +} +.color-class-mnd { + background-color: #a100fe; +} +.color-class-soc { + background-color: #fd7100; +} +.color-class-str, +.color-class-meleedmg { + background-color: #5f3d00; +} +.color-class-phy, +.color-class-dmgres { + background-color: #990304; +} +.color-class-mr { + background-color: #050505; +} +.color-class-com, +.color-class-melee { + background-color: #0136ff; +} +.color-class-def, +.color-class-defence { + background-color: #88826a; +} +.color-class-stl { + background-color: #505050; +} +.color-class-per, +.color-class-ranged { + background-color: #f9c801; +} +.color-class-foc { + background-color: #ff0084; +} +.color-class-common { + background: rgba(185, 183, 40, 0.45); +} +.status-small-label { + font-size: 0.65rem; +} +.combat-button { + min-height: 26px; + max-height: 26px; + margin-top: 4px; +} +.no-grow { + flex-grow: 1; + max-width: 32px; +} +.status-col-name { + max-width: 72px; +} +.status-block { + max-width: 216px; +} +.momentum-block { + max-width: 128px; + justify-content: flex-start; +} +.ability-item { + flex-grow: 1; + justify-content: flex-start; + margin: 2px; +} +.ability-block { + min-width: 160px; +} +.ability-margin { + margin-left: 4px; + margin-top: 5px; +} +.combat-margin { + margin-left: 4px; + margin-top: 3px; +} +.item-ability-roll { + max-height: 42px; + min-height: 36px; +} +.item-ability-roll select, .item-ability-roll input { + margin-top: 4px; + margin-right: 2px; +} +.table-momentum { + background: none; + border: 0; +} +.img-no-border { + max-width: 48px; + max-height: 48px; + border: 0; +} +.items-title-bg { + margin-top: 6px; + background: black; + color: white; +} +.items-title-text { + margin-left: 4px; +} +.lock-icon { + width:16px; + height: 16px; +} +.item-sheet-img { + width: 64px; + height: auto; +} +.item-name-img { + flex-grow:1; + max-width: 2rem; + min-width: 2rem; +} +.item-name-label-header { + flex-grow:2; + max-width: 12rem; + min-width: 12rem; +} +.item-name-label-header-long { + flex-grow:2; + max-width: 14rem; + min-width: 14rem; +} +.item-name-label-header-long2 { + flex-grow:2; + max-width: 24rem; + min-width: 24rem; +} +.item-name-label { + flex-grow:2; + max-width: 10rem; + min-width: 10rem; +} +.item-name-label-long { + flex-grow:2; + max-width: 12rem; + min-width: 12rem; +} +.item-name-label-long2 { + flex-grow:2; + max-width: 22rem; + min-width: 22rem; +} +.item-name-label-level2 { + flex-grow:2; + max-width: 9rem; + min-width: 9rem; +} +.item-field-label-short { + flex-grow:1; + max-width: 4rem; + min-width: 4rem; +} +.item-field-label-medium { + flex-grow:1; + max-width: 6rem; + min-width: 6rem; +} +.item-field-label-long { + flex-grow:1; + max-width: 8rem; + min-width: 8rem; +} +.item-control-end { + align-self: flex-end; +} +.alternate-list { + margin-top: 4px; + flex-wrap: nowrap; +} +.item-filler { + flex-grow: 6; + flex-shrink: 7; +} +.item-controls-fixed { + min-width:2rem; + max-width: 2rem; +} + +.dice-pool-stack { + flex: 1 1 5rem; + display: flex !important; + flex-grow: 0; + justify-content: flex-start; +} +.dice-pool-label { + margin-left: 4px; +} +.dice-pool-div { + border-left: 4px; + border-radius: 2px; + margin-bottom: 1rem; + background-color: #403f3e40; +} + +.dice-pool-image { + border: 0; + margin-left: 4px; + min-width: 48px; + min-height: 48px; + max-width: 48px; + max-height: 48px; + flex-grow: 0; +} \ No newline at end of file diff --git a/styles/unused.html b/styles/unused.html new file mode 100644 index 0000000..5f8d30b --- /dev/null +++ b/styles/unused.html @@ -0,0 +1,60 @@ +{{!-- Carac Tab --}} +
+ +
+ + +
+ +
    +
  • +

    {{data.momentum.label}}

    + + +
  • +
+ +
+ +
+ +
+ + +
    +
  • + +

    {{data.mr.label}}

    +
    + + +
  • + +
+ + +
  • + {{#each data.secondary as |stat2 key|}} + {{#if stat2.iscombat}} + +

    {{stat2.label}} :

    +
    + Cur +  Max + {{/if}} +{{/each}} +
  • +
  • +

    {{data.momentum.label}}:

    + Cur +  Max +
  • diff --git a/system.json b/system.json new file mode 100644 index 0000000..7bbece4 --- /dev/null +++ b/system.json @@ -0,0 +1,214 @@ +{ + "authors": [ + { + "name": "Uberwald", + "flags": {} + } + ], + "description": "Dark Stars RPG system for FoundryVTT", + "esmodules": [ + "modules/dark-stars-main.js" + ], + "gridDistance": 5, + "gridUnits": "m", + "languages": [ + { + "lang": "en", + "name": "English", + "path": "lang/en.json", + "flags": {} + } + ], + "license": "LICENSE.txt", + "packs": [ + { + "type": "Item", + "label": "Armors", + "name": "armor", + "path": "packs/armor.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Equipments", + "name": "equipment", + "path": "packs/equipment.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Shields", + "name": "shields", + "path": "packs/shields.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Weapons", + "name": "weapons", + "path": "packs/weapons.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Conditions", + "name": "conditions", + "path": "packs/conditions.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Currency", + "name": "currency", + "path": "packs/currency.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Lore - Air", + "name": "lore-air", + "path": "packs/lore-air.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Lore - Earth", + "name": "lore-earth", + "path": "packs/lore-earth.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Lore - Fire", + "name": "lore-fire", + "path": "packs/lore-fire.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Lore - Water", + "name": "lore-water", + "path": "packs/lore-water.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Lore - Shadow", + "name": "lore-shadow", + "path": "packs/lore-shadow.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Skills", + "name": "skills", + "path": "packs/skills.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Feats", + "name": "feats", + "path": "packs/feats.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Poisons", + "name": "poisons", + "path": "packs/poisons.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Powers - Class", + "name": "classpowers", + "path": "packs/classpowers.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Tricks & Traps", + "name": "trickstraps", + "path": "packs/trickstraps.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Action Tokens", + "name": "action-tokens", + "path": "packs/action-tokens.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "Item", + "label": "Powers - Monsters", + "name": "monster-powers", + "path": "packs/monster-powers.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + }, + { + "type": "RollTable", + "label": "Rolltables", + "name": "rolltables", + "path": "packs/rolltables.db", + "system": "fvtt-dark-stars", + "private": false, + "flags": {} + } + ], + "primaryTokenAttribute": "secondary.hp", + "secondaryTokenAttribute": "secondary.effort", + "socket": true, + "styles": [ + "styles/simple.css" + ], + "version": "10.0.0", + "compatibility": { + "minimum": "10", + "verified": "10", + "maximum": "10" + }, + "title": "Dark Stars RPG", + "manifest": "https://www.uberwald.me/gitea/uberwald/fvtt-dark-stars", + "download": "https://www.uberwald.me/gitea/uberwald/fvtt-dark-stars", + "url": "https://www.uberwald.me/gitea/uberwald/", + "background": "images/ui/dark_stars_welcome_page.webp", + "id": "fvtt-dark-stars" +} \ No newline at end of file diff --git a/template.json b/template.json new file mode 100644 index 0000000..88d86a4 --- /dev/null +++ b/template.json @@ -0,0 +1,205 @@ +{ + "Actor": { + "types": ["character", "npc"], + "templates": { + "biodata": { + "biodata": { + "class": "", + "age": 0, + "size": 0, + "weight": "", + "height": "", + "hair": "", + "sex": "", + "eyes": "", + "background": "", + "description": "", + "notes": "", + "gmnotes": "" + } + }, + "core": { + "subactors": [], + "abilities": { + "str":{ + "label": "Strength", + "abbrev": "str", + "col": 1, + "value": 1 + }, + "agi":{ + "label": "Agility", + "abbrev": "agi", + "col": 1, + "value": 1 + }, + "dex":{ + "label": "Dexterity", + "abbrev": "dex", + "col": 1, + "value": 1 + }, + "con":{ + "label": "Constitution", + "abbrev": "con", + "col": 1, + "value": 1 + }, + "int":{ + "label": "Intelligence", + "abbrev": "int", + "col": 2, + "value": 1 + }, + "wit":{ + "label": "Wits", + "abbrev": "wit", + "col": 2, + "value": 1 + }, + "cha":{ + "label": "Charisma", + "abbrev": "cha", + "col": 2, + "value": 1 + } + }, + "secondary": { + "hp": { + "label": "Hitpoint", + "abbrev": "hb", + "value": -1, + "max": 0 + }, + "effort": { + "label": "Effort", + "abbrev": "eff", + "value": -1, + "max": 0 + } + }, + "social": { + "fame": { + "label": "Fame", + "value": 0 + }, + "reputation": { + "label": "Reputation", + "value": 0 + } + } + }, + "npccore": { + "npctype": "", + "description": "" + } + }, + "character": { + "templates": [ "biodata", "core" ] + }, + "npc": { + "templates": [ "biodata", "core" ] + } + }, + "Item": { + "types": [ "race", "skill", "armor", "shield", "equipment", "weapon", "money" , "feat", "spell", "condition", "poison"], + "poison": { + "description": "" + }, + "condition": { + "advantage": false, + "disadvantage": false, + "rolladvantage": false, + "rolldisadvantage": false, + "loosehpround": false, + "loohproundvalue": 0, + "noadvantage": false, + "attackdisadvantage": false, + "defensedisadvantage": false, + "targetadvantage": false, + "noaction": false, + "description": "" + }, + "race": { + "description": "" + }, + "feat": { + "isfeatdie": false, + "issl": false, + "sl": 0, + "description": "" + }, + "skill": { + "ability": "", + "armorpenalty": false, + "isproficient": false, + "isweaponskill": false, + "isshieldskill": false, + "isfeatdie": false, + "issl2": false, + "islore": false, + "skilltype": "", + "isinnate": false, + "bonusdice": "", + "background": 0, + "basic": 0, + "class": 0, + "exp": 0, + "explevel": 0, + "description": "" + }, + "armor": { + "armortype": "", + "absorprionroll": "", + "damagedroll": "", + "isproficient": false, + "minstr": 0, + "skillpenalty": 0, + "equipped": false, + "cost": 0, + "description":"" + }, + "shield": { + "shielddie": "", + "skill": "", + "equipped": false, + "cost": 0, + "description":"" + }, + "equipment": { + "equiptype": "", + "cost": 0, + "quantity": 0, + "equipped": false, + "iscontainer": false, + "containercapacity": 0, + "containerid": "", + "description":"" + }, + "money" : { + "value": 0, + "quantity": 0, + "description": "" + }, + "weapon": { + "isproficient": false, + "skill": "", + "qualities": "", + "flaws": "", + "damage": "", + "isranged": false, + "range": "", + "maxrange": "", + "minstr": 0, + "cost": 0, + "equipped": false, + "description": "" + }, + "spell":{ + "lore": "", + "circle": 1, + "range": "", + "description": "" + } + } +} diff --git a/templates/actors/actor-sheet.hbs b/templates/actors/actor-sheet.hbs new file mode 100644 index 0000000..26edc76 --- /dev/null +++ b/templates/actors/actor-sheet.hbs @@ -0,0 +1,530 @@ +
    + + {{!-- Sheet Header --}} +
    +
    +

    +
    + +
    + +
    +
    +
      + {{#each data.abilities as |ability key|}} + {{#if (eq ability.col 1)}} + {{> systems/fvtt-crucible-rpg/templates/partial-actor-ability-block.html ability=ability key=key}} + {{/if}} + {{/each}} +
    + +
  • + +

    Class

    +
    + +
  • + +
    + +
    +
      + {{#each data.abilities as |ability key|}} + {{#if (eq ability.col 2)}} + {{> systems/fvtt-crucible-rpg/templates/partial-actor-ability-block.html ability=ability key=key}} + {{/if}} + {{/each}} + + {{#if equippedArmor}} +
    • + + +

      {{equippedArmor.name}}

      +
      +
    • + {{/if}} + {{#if equippedShield}} +
    • + + +

      {{equippedShield.name}}

      +
      +
    • + {{/if}} +
    • + + +

      Target Roll

      +
      +
    • + +
    +
    + +
    + {{> systems/fvtt-crucible-rpg/templates/partial-actor-status.html}} +
    + +
    +
    +
    +
    +
    + + {{!-- Sheet Tab Navigation --}} + + + {{!-- Sheet Body --}} +
    + + {{!-- Skills Tab --}} +
    + +
      +
    • + +

      +
      + + + + + + + + + +
    • + {{#each skills as |skill key|}} +
    • + + {{skill.name}} + {{upper skill.system.ability}} + {{skill.system.skilldice}} +  -  +
       
      +
      + +
      +
    • + {{/each}} +
    +
    + + {{!-- Combat Tab --}} +
    +
    + +
    +
      +
    • + +

      +
      + + + + + + +
    • + {{#each equippedWeapons as |weapon key|}} +
    • + + {{weapon.name}} + + {{weapon.system.ability}} + + {{perk.system.range}} + +
       
      +
      + +
      +
    • + {{/each}} +
    +
    + +
    +
      +
    • + +

      +
      + + + + + + + + + +
    • + {{#each feats as |feat key|}} +
    • + + {{feat.name}} + + {{upperFirst feat.system.isfeatdie}} + {{upperFirst feat.system.issl}} + {{feat.system.sl}} + +
       
      +
      + +
      +
    • + {{/each}} +
    +
    + +
    +
      +
    • + +

      +
      +
    • + {{#each conditions as |condition key|}} +
    • + + {{condition.name}} +
       
      +
      + +
      +
    • + {{/each}} +
    +
    + +
    +
    + + + {{!-- Lore Tab --}} +
    + +
    + +
      +
    • + +

      +
      + + + + + + + + + +
    • + + {{#each spells as |spell key|}} +
    • + + + {{spell.name}} + + {{upperFirst spell.system.lore}} + {{upperFirst spell.system.circle}} + {{upperFirst spell.system.range}} +
       
      +
      + +
      +
    • + {{/each}} + +
    + +
    +
    + + {{!-- Equipement Tab --}} +
    + +
    +

    Encumbrance

    + Current : {{encCurrent}} + Capacity : {{encCapacity}} +
    + +
      +
    • + +

      +
      + + + + + + + + + +
       
      +
      + +
      +
    • + {{#each moneys as |money key|}} +
    • + + {{money.name}} + + + + + + + + {{#if money.system.idrDice}} + {{money.system.idrDice}} + {{else}} +  -  + {{/if}} + + +
       
      +
      + +
      +
    • + {{/each}} +
    + +
      +
    • + +

      +
      + + + + + + +
      + +
      +
    • + {{#each weapons as |weapon key|}} +
    • + + {{weapon.name}} + + + +
       
      + +
    • + {{/each}} +
    + +
      +
    • + +

      +
      + + + + + + + +
       
      +
      + +
      +
    • + {{#each armors as |armor key|}} +
    • + + {{armor.name}} + {{upper armor.system.armortype}} + {{armor.system.absorprionroll}} + +
       
      + +
    • + {{/each}} +
    + + + +
      +
    • + +

      +
      + + + +
       
      +
      + +
      + +
    • + {{#each containersTree as |equip key|}} + {{> systems/fvtt-crucible-rpg/templates/partial-actor-equipment.html equip=equip level=1}} +
        + {{#each equip.data.contents as |subgear key|}} + {{> systems/fvtt-crucible-rpg/templates/partial-actor-equipment.html equip=subgear level=2}} + {{/each}} +
      + {{/each}} +
    + +
    + +
    + + {{!-- Biography Tab --}} +
    +
    +
    +
      +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    +
    +
    +
      +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + + +
    • +
    +
    +
    + + +
    +

    Background :

    +
    + {{editor description target="system.biodata.description" button=true owner=owner + editable=editable}} +
    +
    +

    Notes :

    +
    + {{editor notes target="system.biodata.notes" button=true owner=owner editable=editable}} +
    +
    + +
    + +
    +
    \ No newline at end of file diff --git a/templates/actors/npc-sheet.hbs b/templates/actors/npc-sheet.hbs new file mode 100644 index 0000000..208c497 --- /dev/null +++ b/templates/actors/npc-sheet.hbs @@ -0,0 +1,413 @@ +
    + + {{!-- Sheet Header --}} +
    +
    +

    +
    + +
    + +
    +
    +
      + {{#each data.abilities as |ability key|}} + {{#if (eq ability.col 1)}} + {{> systems/fvtt-crucible-rpg/templates/partial-actor-ability-block.html ability=ability key=key}} + {{/if}} + {{/each}} +
    + +
  • + +

    Class

    +
    + +
  • + +
    + +
    +
      + {{#each data.abilities as |ability key|}} + {{#if (eq ability.col 2)}} + {{> systems/fvtt-crucible-rpg/templates/partial-actor-ability-block.html ability=ability key=key}} + {{/if}} + {{/each}} + + {{#if equippedArmor}} +
    • + + +

      {{equippedArmor.name}}

      +
      +
    • + {{/if}} + {{#if equippedShield}} +
    • + + +

      {{equippedShield.name}}

      +
      +
    • + {{/if}} +
    • + + +

      Target Roll

      +
      +
    • + +
    +
    + +
    + {{> systems/fvtt-crucible-rpg/templates/partial-actor-status.html}} +
    + +
    +
    +
    +
    +
    + + {{!-- Sheet Tab Navigation --}} + + + {{!-- Sheet Body --}} +
    + + {{!-- Skills Tab --}} +
    + +
      +
    • + +

      +
      + + + + + + + + + +
    • + {{#each skills as |skill key|}} +
    • + + {{skill.name}} + {{upper skill.system.ability}} + {{skill.system.skilldice}} +  -  +
       
      +
      + +
      +
    • + {{/each}} +
    + +
      +
    • + +

      +
      + + + + + + +
    • + {{#each equippedWeapons as |weapon key|}} +
    • + + {{weapon.name}} + + {{weapon.system.ability}} + + {{perk.system.range}} + +
       
      +
      + +
      +
    • + {{/each}} +
    + +
      +
    • + +

      +
      + + + + + + + + + +
    • + {{#each feats as |feat key|}} +
    • + + {{feat.name}} + + {{upperFirst feat.system.isfeatdie}} + {{upperFirst feat.system.issl}} + {{feat.system.sl}} + +
       
      +
      + +
      +
    • + {{/each}} +
    + +
      +
    • + +

      +
      +
    • + {{#each conditions as |condition key|}} +
    • + + {{condition.name}} + +
       
      +
       
      +
      + +
      +
    • + {{/each}} +
    + +
      +
    • + +

      +
      + + + + + + +
      + +
      +
    • + {{#each weapons as |weapon key|}} +
    • + + {{weapon.name}} + + + +
       
      + +
    • + {{/each}} +
    + +
      +
    • + +

      +
      + + + + + + + +
       
      +
      + +
      +
    • + {{#each armors as |armor key|}} +
    • + + {{armor.name}} + {{upper armor.system.armortype}} + {{armor.system.absorprionroll}} + +
       
      + +
    • + {{/each}} +
    + + + +
      +
    • + +

      +
      + + + +
       
      +
      + +
      + +
    • + {{#each containersTree as |equip key|}} + {{> systems/fvtt-crucible-rpg/templates/partial-actor-equipment.html equip=equip level=1}} +
        + {{#each equip.data.contents as |subgear key|}} + {{> systems/fvtt-crucible-rpg/templates/partial-actor-equipment.html equip=subgear level=2}} + {{/each}} +
      + {{/each}} +
    +
    + +
    + + {{!-- Biography Tab --}} +
    +
    +
    +
      +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    +
    +
    +
      +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + + +
    • +
    +
    +
    + + +
    +

    Background :

    +
    + {{editor data.biodata.description target="system.biodata.description" button=true owner=owner + editable=editable}} +
    +
    +

    Notes :

    +
    + {{editor data.biodata.notes target="system.biodata.notes" button=true owner=owner editable=editable}} +
    +
    + +
    + + + \ No newline at end of file diff --git a/templates/apps/roll-dialog-generic.hbs b/templates/apps/roll-dialog-generic.hbs new file mode 100644 index 0000000..e9d1241 --- /dev/null +++ b/templates/apps/roll-dialog-generic.hbs @@ -0,0 +1,167 @@ +
    +
    + {{#if img}} + + {{/if}} +

    {{title}}

    +
    + +
    + + {{#if sizeDice}} +
    + Size basic dices : + {{sizeDice.nb}}{{sizeDice.dice}} +
    + +
    + Distance bonus dice(s) : + +
    + {{/if}} + + {{#if hasCover}} +
    + Cover : + +
    + {{/if}} + + {{#if situational}} +
    + Situational : + +
    + {{/if}} + + + {{#if save}} +
    + {{save.label}} : + {{save.value}}d6 +
    + {{/if}} + + {{#if ability}} +
    + Ability : + {{ability.value}}d6 +
    + {{/if}} + + {{#if weapon}} +
    + Weapon : + {{weapon.name}} +
    + {{/if}} + + {{#if shield}} +
    + Use shield ? : + +
    +
    + {{shield.name}} : + {{shield.data.shielddie}} +
    + {{/if}} + + {{#if skill}} +
    + Skill : + {{skill.name}} - {{skill.data.skilldice}} +
    +
    + Feature die or SL+2? : + {{#if skill.data.isfeatdie}} Yes {{else}} No {{/if}} +
    + {{/if}} + + {{#if noAdvantage}} +
    + No advantage due to condition : {{noAdvantage.name}} +
    + {{else}} +
    + Advantage : + +
    + {{/if}} + +
    + Disadvantage : + +
    + +
    + Roll with Advantage/Disadvantage : + +
    + + {{#if forceAdvantage}} +
    + 1 Advantage from condition : {{forceAdvantage.name}} + {{#if advantageFromTarget}} (Provided by targetted actor) {{/if}} + +
    + {{/if}} + {{#if forceDisadvantage}} +
    + 1 Disadvantage from condition : {{forceDisadvantage.name}} +
    + {{/if}} + {{#if forceRollAdvantage}} +
    + Roll Advantage from condition : {{forceRollAdvantage.name}} +
    + {{/if}} + {{#if forceRollDisadvantage}} +
    + Roll Disadvantage from condition : {{forceRollDisadvantage.name}} +
    + {{/if}} + + +
    + +
    \ No newline at end of file diff --git a/templates/chat/chat-attack-defense-result.hbs b/templates/chat/chat-attack-defense-result.hbs new file mode 100644 index 0000000..4367f4b --- /dev/null +++ b/templates/chat/chat-attack-defense-result.hbs @@ -0,0 +1,75 @@ +
    + {{#if actorImg}} + {{alias}} + {{/if}} +

    {{alias}}

    +
    + +
    + + {{#if img}} +
    + {{name}} +
    + {{/if}} + +
    +
    + +
    +
      +
    • Fight result !
    • + {{#if successDetails.fumbleDetails}} +
    • Fumble ! : {{successDetails.fumbleDetails.data.text}}
    • + {{/if}} + + + {{#if armorResult}} +
    • Armor initial result : {{armorResult.rawArmor}}
    • + {{#each armorResult.messages as |message idx|}} +
    • {{message}}
    • + {{/each}} + {{/if}} + + {{#if successDetails.hack_vs_shields}} +
    • Hack weapon : check shield !
    • + {{/if}} + + {{#if successDetails.entangle}} +
    • Entangle weapon : attacker can entangle !
    • + {{/if}} + + {{#if successDetails.knockback}} +
    • Knockback weapon : check knockback !
    • + {{/if}} + + {{#if successDetails.hack_armors}} +
    • Hack weapon : check armor damage !
    • + {{/if}} + + {{#if successDetails.penetrating_impale}} +
    • Penetrating weapon : apply the Impale condition !
    • + {{/if}} + + {{#if (or successDetails.critical_1 successDetails.critical_2)}} +
    • Critical {{#if successDetails.critical_1}} 1 {{else}} 2 {{/if}} : {{successDetails.criticalText}}
    • + {{/if}} + + {{#if successDetails.attackerHPLossValue}} +
    • Attacker has lost HP : {{successDetails.attackerHPLossValue}} HP
    • + {{/if}} + + {{#if successDetails.defenderHPLossValue}} +
    • Defender has lost HP : {{successDetails.defenderHPLossValue}} HP
    • + {{/if}} + +
    • Success details : {{successDetails.result}}
    • + +
    • Final successes {{sumSuccess}}
    • + + + +
    +
    + +
    diff --git a/templates/chat/chat-generic-result.hbs b/templates/chat/chat-generic-result.hbs new file mode 100644 index 0000000..e6633de --- /dev/null +++ b/templates/chat/chat-generic-result.hbs @@ -0,0 +1,149 @@ +
    + {{#if actorImg}} + {{alias}} + {{/if}} +

    {{alias}}

    +
    + +
    + + {{#if img}} +
    + {{name}} +
    + {{/if}} + +
    +
    + +
    +
      + {{#if (eq rollOrder 1)}} +
    • Roll with {{rollType}} - Roll 1
    • + {{/if}} + {{#if (eq rollOrder 2)}} +
    • Roll with {{rollType}} - Roll 2
    • + {{/if}} + {{#if (eq rollOrder 3)}} +
    • Roll with {{rollType}} - Final result !
    • + {{/if}} + + {{#if save}} +
    • Save : {{save.label}} - {{save.value}}d6 + ({{#each roll.terms.0.results as |die idx|}} + {{die.result}}  + {{/each}}) +
    • + {{/if}} + + {{#if sizeDice}} +
    • Size/Range/Cover/Situational dices + ({{#each roll.terms.0.results as |die idx|}} + {{die.result}}  + {{/each}}) +
    • + {{/if}} + + {{#if ability}} +
    • Ability : {{ability.label}} - {{ability.value}}d6 + ({{#each roll.terms.0.results as |die idx|}} + {{die.result}}  + {{/each}}) +
    • + {{/if}} + + {{#if skill}} +
    • Skill : {{skill.name}} - {{skill.data.skilldice}} + {{#if featSL}} + - with Feat SL +{{featSL}} + {{/if}} +  ({{#each roll.terms.2.results as |die idx|}} + {{die.result}}  + {{/each}}) +
    • + {{/if}} + + {{#if noAdvantage}} +
    • No advantage due to condition : {{noAdvantage.name}}
    • + {{else}} + {{#if (or (eq advantage "advantage1") forceAdvantage)}} +
    • 1 Advantage Die ! +  ({{#each roll.terms.8.results as |die idx|}} + {{die.result}}  + {{/each}}) +
    • + {{/if}} + {{#if (eq advantage "advantage2") }} +
    • 2 Advantage Dice ! +  ({{#each roll.terms.8.results as |die idx|}} + {{die.result}}  + {{/each}}) +
    • + {{/if}} + {{/if}} + + {{#if (or (eq disadvantage "disadvantage1") forceDisadvantage)}} +
    • 1 Disadvantage Die ! +  ({{#each roll.terms.10.results as |die idx|}} + {{die.result}}  + {{/each}}) +
    • + {{/if}} + {{#if (eq disadvantage "disadvantage2")}} +
    • 2 Disadvantage Dice ! +  ({{#each roll.terms.10.results as |die idx|}} + {{die.result}}  + {{/each}}) +
    • + {{/if}} + {{#if (eq rollAdvantage "roll-advantage")}} +
    • Roll with Advantage !
    • + {{/if}} + {{#if (eq rollAdvantage "roll-disadvantage")}} +
    • Roll with Disadvantage !
    • + {{/if}} + + {{#if skillArmorPenalty}} +
    • Armor Penalty : {{skillArmorPenalty}} Disadvantage Dice +  ({{#each roll.terms.12.results as |die idx|}} + {{die.result}}  + {{/each}}) +
    • + {{/if}} + + {{#if hasBonusDice}} +
    • Skill bonus dice : {{hasBonusDice}} +  ({{#each roll.terms.6.results as |die idx|}} + {{die.result}}  + {{/each}}) +
    • + {{/if}} + + {{#if complexSkillDisadvantage}} +
    • Roll with Disadvantage because of Complex Skill at SL 0 !
    • + {{/if}} + + {{#if hasFeatDie}} +
    • Feat Die : d10 +  ({{#each roll.terms.4.results as |die idx|}} + {{die.result}}  + {{/each}}) +
    • + {{/if}} + + {{#if useshield}} +
    • Shield : {{shield.name}} - {{shield.data.shielddie}} + ({{#each roll.terms.14.results as |die idx|}} + {{die.result}}  + {{/each}}) +
    • + {{/if}} + +
    • Number of successes {{nbSuccess}}
    • + + + +
    +
    + +
    diff --git a/templates/chat/chat-opposed-fail.hbs b/templates/chat/chat-opposed-fail.hbs new file mode 100644 index 0000000..4e71d14 --- /dev/null +++ b/templates/chat/chat-opposed-fail.hbs @@ -0,0 +1,11 @@ +
    + {{alias}} +

    {{defenderName}}

    +
    + +
    +
    + {{defenderName}} wins the opposition against {{attackerName}} ! +
    + + diff --git a/templates/chat/chat-request-defense.hbs b/templates/chat/chat-request-defense.hbs new file mode 100644 index 0000000..f5bda4d --- /dev/null +++ b/templates/chat/chat-request-defense.hbs @@ -0,0 +1,46 @@ +
    + {{#if actorImg}} + {{alias}} + {{/if}} +

    {{alias}}

    +
    + +
    + +{{#if img}} +
    + {{name}} +
    +{{/if}} + +
    +
    + +
    + + {{#if isRangedAttack}} +
    {{defender.name}} is under Ranged attack. He must roll a Target Roll to defend himself.
    + {{else}} +
    {{defender.name}} is under Melee attack. He must roll a Defense Roll to defend himself.
    + {{/if}} + +
      + {{#if isRangedAttack}} +
    • + +
    • + {{else}} +
    • + {{#each defenderWeapons as |weapon idx|}} + + {{/each}} +
    • + {{/if}} +
    + + + +
    + + \ No newline at end of file diff --git a/templates/items/item-armor-sheet.hbs b/templates/items/item-armor-sheet.hbs new file mode 100644 index 0000000..f4ebe36 --- /dev/null +++ b/templates/items/item-armor-sheet.hbs @@ -0,0 +1,92 @@ +
    +
    + +
    +

    +
    +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-nav.html}} + + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-description.html}} + +
    + +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    +
    +
    + +
    +
    diff --git a/templates/items/item-condition-sheet.hbs b/templates/items/item-condition-sheet.hbs new file mode 100644 index 0000000..a84066a --- /dev/null +++ b/templates/items/item-condition-sheet.hbs @@ -0,0 +1,64 @@ +
    +
    + +
    +

    +
    +
    + {{> systems/fvtt-crucible-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + +
    + + +
    + {{editor description target="system.description" button=true owner=owner editable=editable}} +
    +
    + +
    +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • + {{#if data.loosehpround}} +
    • + +
    • + {{/if}} +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • + +
    + +
    + +
    +
    \ No newline at end of file diff --git a/templates/items/item-equipment-sheet.hbs b/templates/items/item-equipment-sheet.hbs new file mode 100644 index 0000000..68b2708 --- /dev/null +++ b/templates/items/item-equipment-sheet.hbs @@ -0,0 +1,31 @@ +
    +
    + +
    +

    +
    +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-description.html}} + +
    +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    + +
    +
    diff --git a/templates/items/item-feat-sheet.hbs b/templates/items/item-feat-sheet.hbs new file mode 100644 index 0000000..0751e74 --- /dev/null +++ b/templates/items/item-feat-sheet.hbs @@ -0,0 +1,35 @@ +
    +
    + +
    +

    +
    +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-description.html}} + +
    +
      +
    • + +
    • + +
    • + +
    • + + {{#if data.issl}} +
    • + +
    • + {{/if}} + +
    +
    +
    +
    diff --git a/templates/items/item-money-sheet.hbs b/templates/items/item-money-sheet.hbs new file mode 100644 index 0000000..e2c11d1 --- /dev/null +++ b/templates/items/item-money-sheet.hbs @@ -0,0 +1,28 @@ +
    +
    + +
    +

    +
    +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-description.html}} + +
    +
      +
    • + +
    • +
    • + +
    • +
    +
    + +
    +
    diff --git a/templates/items/item-poison-sheet.hbs b/templates/items/item-poison-sheet.hbs new file mode 100644 index 0000000..9f587ae --- /dev/null +++ b/templates/items/item-poison-sheet.hbs @@ -0,0 +1,28 @@ +
    +
    + +
    +

    +
    +
    + {{> systems/fvtt-crucible-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + +
    + + +
    + {{editor description target="data.description" button=true owner=owner editable=editable}} +
    +
    + +
    +
      +
    + +
    + +
    +
    \ No newline at end of file diff --git a/templates/items/item-race-sheet.hbs b/templates/items/item-race-sheet.hbs new file mode 100644 index 0000000..9f587ae --- /dev/null +++ b/templates/items/item-race-sheet.hbs @@ -0,0 +1,28 @@ +
    +
    + +
    +

    +
    +
    + {{> systems/fvtt-crucible-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + +
    + + +
    + {{editor description target="data.description" button=true owner=owner editable=editable}} +
    +
    + +
    +
      +
    + +
    + +
    +
    \ No newline at end of file diff --git a/templates/items/item-shield-sheet.hbs b/templates/items/item-shield-sheet.hbs new file mode 100644 index 0000000..985f820 --- /dev/null +++ b/templates/items/item-shield-sheet.hbs @@ -0,0 +1,55 @@ +
    +
    + +
    +

    +
    +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-nav.html}} + + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-description.html}} + +
    + +
    +
      +
    • + +
    • + +
    • + +
    • + + +
    • + +
    • + +
    • + +
    • + +
    +
    +
    + +
    +
    diff --git a/templates/items/item-skill-sheet.hbs b/templates/items/item-skill-sheet.hbs new file mode 100644 index 0000000..5f7c68e --- /dev/null +++ b/templates/items/item-skill-sheet.hbs @@ -0,0 +1,105 @@ +
    +
    + +
    +

    +
    +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-description.html}} + +
    +
      +
    • + +
    • + +
    • + +
    • +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    +
    +
    +
    diff --git a/templates/items/item-spell-sheet.hbs b/templates/items/item-spell-sheet.hbs new file mode 100644 index 0000000..61f5949 --- /dev/null +++ b/templates/items/item-spell-sheet.hbs @@ -0,0 +1,49 @@ +
    +
    + +
    +

    +
    +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-description.html}} + +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    +
    +
    +
    diff --git a/templates/items/item-weapon-sheet.hbs b/templates/items/item-weapon-sheet.hbs new file mode 100644 index 0000000..dcb6071 --- /dev/null +++ b/templates/items/item-weapon-sheet.hbs @@ -0,0 +1,84 @@ +
    +
    + +
    +

    +
    +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-nav.html}} + + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-crucible-rpg/templates/partial-item-description.html}} + +
    + +
    +
      + + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + {{#if data.isranged}} +
    • + +
    • +
    • + +
    • + {{/if}} + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    +
    +
    + +
    +
    diff --git a/templates/items/post-item.hbs b/templates/items/post-item.hbs new file mode 100644 index 0000000..1bad9c8 --- /dev/null +++ b/templates/items/post-item.hbs @@ -0,0 +1,8 @@ +
    +

    {{name}}

    + {{#if img}} + + {{/if}} +

    Description :

    +

    {{{data.description}}}

    +
    diff --git a/templates/partials/editor-notes-gm.hbs b/templates/partials/editor-notes-gm.hbs new file mode 100644 index 0000000..4c17392 --- /dev/null +++ b/templates/partials/editor-notes-gm.hbs @@ -0,0 +1,6 @@ +{{#if data.isGM}} +

    GM Notes :

    +
    + {{editor data.gmnotes target="system.gmnotes" button=true owner=owner editable=editable}} +
    +{{/if}} diff --git a/templates/partials/partial-actor-ability-block.hbs b/templates/partials/partial-actor-ability-block.hbs new file mode 100644 index 0000000..3e05740 --- /dev/null +++ b/templates/partials/partial-actor-ability-block.hbs @@ -0,0 +1,19 @@ +
  • + +

    {{ability.label}}

    +
    + +
  • \ No newline at end of file diff --git a/templates/partials/partial-actor-equipment.hbs b/templates/partials/partial-actor-equipment.hbs new file mode 100644 index 0000000..51b1f41 --- /dev/null +++ b/templates/partials/partial-actor-equipment.hbs @@ -0,0 +1,37 @@ +
  • + + {{#if (eq level 1)}} + {{equip.name}} + {{else}} + {{equip.name}} + {{/if}} + + + + + +  -  + + + {{#if equip.system.iscontainer}} + {{equip.system.contentsEnc}} + {{else}} + {{mul equip.system.weight equip.system.quantity}} + {{/if}} + + +  -  + + +
     
    +
    + {{#if (eq level 1)}} + {{#if equip.system.equipped}}{{else}}{{/if}} + {{/if}} + +
    +
  • diff --git a/templates/partials/partial-actor-status.hbs b/templates/partials/partial-actor-status.hbs new file mode 100644 index 0000000..09985af --- /dev/null +++ b/templates/partials/partial-actor-status.hbs @@ -0,0 +1,39 @@ +
      +
    • + +

      HP

      +
      + + + {{#if (eq type "character")}} + / {{data.secondary.hp.max}} + {{else}} + / + {{/if}} +
    • +
    • + +

      Effort

      +
      + + + {{#if (eq type "character")}} + / {{data.secondary.effort.max}} + {{else}} + / + {{/if}} +
    • + +
    •  
    • + + {{#each saveRolls as |save key|}} +
    • + + +

      + {{save.label}} {{save.value}}

      +
      +
    • + {{/each}} +
        + diff --git a/templates/partials/partial-item-description.hbs b/templates/partials/partial-item-description.hbs new file mode 100644 index 0000000..26c9623 --- /dev/null +++ b/templates/partials/partial-item-description.hbs @@ -0,0 +1,8 @@ +
        +
        + +
        + {{editor description target="system.description" button=true owner=owner editable=editable}} +
        +
        +
        diff --git a/templates/partials/partial-item-nav.hbs b/templates/partials/partial-item-nav.hbs new file mode 100644 index 0000000..95b52cd --- /dev/null +++ b/templates/partials/partial-item-nav.hbs @@ -0,0 +1,5 @@ +{{!-- Sheet Tab Navigation --}} + diff --git a/templates/partials/partial-options-abilities.hbs b/templates/partials/partial-options-abilities.hbs new file mode 100644 index 0000000..0efa008 --- /dev/null +++ b/templates/partials/partial-options-abilities.hbs @@ -0,0 +1,7 @@ + + + + + + + diff --git a/templates/partials/partial-options-equipment-types.hbs b/templates/partials/partial-options-equipment-types.hbs new file mode 100644 index 0000000..ab60653 --- /dev/null +++ b/templates/partials/partial-options-equipment-types.hbs @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/templates/partials/partial-options-level.hbs b/templates/partials/partial-options-level.hbs new file mode 100644 index 0000000..0fb86c0 --- /dev/null +++ b/templates/partials/partial-options-level.hbs @@ -0,0 +1,14 @@ +{{#if notapplicable}} + +{{/if}} + + + + + + + + + + + diff --git a/templates/partials/partial-options-range.hbs b/templates/partials/partial-options-range.hbs new file mode 100644 index 0000000..e08a1e5 --- /dev/null +++ b/templates/partials/partial-options-range.hbs @@ -0,0 +1,15 @@ +{{#if notapplicable}} + +{{/if}} + + + + + + + + + + + + diff --git a/templates/partials/partial-roll-select.hbs b/templates/partials/partial-roll-select.hbs new file mode 100644 index 0000000..7c9e85f --- /dev/null +++ b/templates/partials/partial-roll-select.hbs @@ -0,0 +1,4 @@ + +
          +
        +