From 6d1360cbd1655100052c03f6c8d33a98b0c9c0e5 Mon Sep 17 00:00:00 2001 From: sladecraven Date: Sun, 13 Mar 2022 16:17:04 +0100 Subject: [PATCH] Initiali import --- README.md | 2 +- fonts/middlesaxonytext.ttf | Bin 0 -> 67040 bytes fonts/zag_bold.otf | Bin 0 -> 35968 bytes fonts/zag_regular.otf | Bin 0 -> 35976 bytes 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_2.png | Bin 0 -> 2145 bytes images/dice/d6_3.png | Bin 0 -> 2825 bytes images/dice/d6_4.png | Bin 0 -> 3139 bytes images/dice/d6_5.png | Bin 0 -> 3793 bytes images/dice/d6_6.png | Bin 0 -> 4196 bytes images/dice/perspective-dice-five.webp | Bin 0 -> 10440 bytes images/icons/.directory | 6 + images/icons/locked.svg | 60 + lang/en.json | 3 + modules/imperium5-actor-sheet.js | 354 ++++ modules/imperium5-actor.js | 1303 +++++++++++++++ modules/imperium5-combat.js | 38 + modules/imperium5-commands.js | 123 ++ modules/imperium5-item-sheet.js | 509 ++++++ modules/imperium5-item.js | 32 + modules/imperium5-main.js | 128 ++ modules/imperium5-roll-dialog.js | 239 +++ modules/imperium5-utility.js | 741 +++++++++ styles/simple.css | 1447 +++++++++++++++++ styles/unused.html | 60 + system.json | 38 + template.json | 424 +++++ templates/actor-sheet.html | 879 ++++++++++ templates/chat-create-actor.html | 143 ++ templates/chat-effect-used.html | 7 + templates/chat-generic-result.html | 54 + templates/chat-opposed-damage.html | 15 + templates/chat-opposed-fail.html | 11 + templates/chat-perk-ready.html | 7 + templates/editor-notes-gm.html | 6 + templates/item-ability-sheet.html | 152 ++ templates/item-armor-sheet.html | 63 + templates/item-effect-sheet.html | 149 ++ templates/item-equipment-sheet.html | 86 + templates/item-money-sheet.html | 33 + templates/item-perk-sheet.html | 112 ++ templates/item-power-sheet.html | 136 ++ templates/item-race-sheet.html | 134 ++ templates/item-role-sheet.html | 114 ++ templates/item-shield-sheet.html | 55 + templates/item-specialisation-sheet.html | 62 + templates/item-weapon-sheet.html | 145 ++ templates/npc-sheet.html | 184 +++ templates/partial-actor-equipment.html | 49 + templates/partial-actor-stat-block.html | 16 + templates/partial-actor-status.html | 70 + templates/partial-equipment-effects.html | 16 + templates/partial-item-description.html | 8 + templates/partial-item-nav.html | 5 + .../partial-options-equipment-types.html | 13 + templates/partial-options-level.html | 14 + templates/partial-options-range.html | 15 + templates/partial-options-statistics.html | 22 + templates/partial-roll-select-effects.html | 102 ++ templates/post-item.html | 8 + templates/roll-dialog-generic.html | 88 + 64 files changed, 8481 insertions(+), 1 deletion(-) create mode 100755 fonts/middlesaxonytext.ttf create mode 100755 fonts/zag_bold.otf create mode 100755 fonts/zag_regular.otf 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_2.png create mode 100644 images/dice/d6_3.png create mode 100644 images/dice/d6_4.png create mode 100644 images/dice/d6_5.png create mode 100644 images/dice/d6_6.png create mode 100644 images/dice/perspective-dice-five.webp create mode 100644 images/icons/.directory create mode 100644 images/icons/locked.svg create mode 100644 lang/en.json create mode 100644 modules/imperium5-actor-sheet.js create mode 100644 modules/imperium5-actor.js create mode 100644 modules/imperium5-combat.js create mode 100644 modules/imperium5-commands.js create mode 100644 modules/imperium5-item-sheet.js create mode 100644 modules/imperium5-item.js create mode 100644 modules/imperium5-main.js create mode 100644 modules/imperium5-roll-dialog.js create mode 100644 modules/imperium5-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/actor-sheet.html create mode 100644 templates/chat-create-actor.html create mode 100644 templates/chat-effect-used.html create mode 100644 templates/chat-generic-result.html create mode 100644 templates/chat-opposed-damage.html create mode 100644 templates/chat-opposed-fail.html create mode 100644 templates/chat-perk-ready.html create mode 100644 templates/editor-notes-gm.html create mode 100644 templates/item-ability-sheet.html create mode 100644 templates/item-armor-sheet.html create mode 100644 templates/item-effect-sheet.html create mode 100644 templates/item-equipment-sheet.html create mode 100644 templates/item-money-sheet.html create mode 100644 templates/item-perk-sheet.html create mode 100644 templates/item-power-sheet.html create mode 100644 templates/item-race-sheet.html create mode 100644 templates/item-role-sheet.html create mode 100644 templates/item-shield-sheet.html create mode 100644 templates/item-specialisation-sheet.html create mode 100644 templates/item-weapon-sheet.html create mode 100644 templates/npc-sheet.html create mode 100644 templates/partial-actor-equipment.html create mode 100644 templates/partial-actor-stat-block.html create mode 100644 templates/partial-actor-status.html create mode 100644 templates/partial-equipment-effects.html create mode 100644 templates/partial-item-description.html create mode 100644 templates/partial-item-nav.html create mode 100644 templates/partial-options-equipment-types.html create mode 100644 templates/partial-options-level.html create mode 100644 templates/partial-options-range.html create mode 100644 templates/partial-options-statistics.html create mode 100644 templates/partial-roll-select-effects.html create mode 100644 templates/post-item.html create mode 100644 templates/roll-dialog-generic.html diff --git a/README.md b/README.md index debd6fc..0c21108 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# fvtt-imperium5 +# fvtt-pegasus-rpg 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 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 Wa%oF@`rc-_RAKRgE)|vt`1&6$fA2B? literal 0 HcmV?d00001 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 zcmaKSWl$YWx9)5i`y$Ue#JZ z2v?j;TulDG4*2y5V`&KhSET@erV9W#GXQ|B|6h4~)%~Lf3YP#t<26@`lK_zM4FKq@ zUSk{lFY`uxb@+dI`~TYf-~5Rys>q#UyjJ_SSc3fz9J{{*vJr?u@4tN#3>)iolrW_k zb$*jydhB<9&an@|vJ#Vm(i^`JEI=;<9Q@3U5=CljBK7}D7&!YWak$!^lLSSQJlUxt zI(X5Iz_P}B2Pv}p(@LpSnAYyClV>_4q;KmN5(QcwK}{P!1vFw}iyf#%)~^#O>8e_+=h>QLlhs24XrX;=0NM$^Dl^Tl82Cc<&J?rjFK zNT?|*DdW4%7MEM0(3x%Npmbdn!!6x)HQ{$Of8FS%T z;wu{h(`=t|o1VY)6ik=T$~Xu- zb#O_gRGW%tC=9@a=qclI*4Ty+A8Qih-NqU`5?8r?5>$V_D~jBr^E&6g>%pI=Oz~Lz zvL>_IbP6B_QPPGVon1#YLfeXp8kYeFLPMbD6?3?V@;?kf{?k#oMCR%J* zafs*P&k#WR%9DQ@3kxRzN|Az{hnm=JMv<2rAx31~)SRB0n#x&GUtfRZfUSxbsEuWr zNH$Qw6h&0e5q(CY$~|O|8e!3{qQyP&EuzvM$EGA*SUu^b_im7IsjSp4)-C;)UPg5j z?d(rjZI;7NnN(y-P8&(pO=$OaL6vE4NAP(r#HMmqb&U$mLlK1~9{M~?9@1|gH0=iO zHwm#8lGB*Clm~7*j^qz_r0fm{E6t5iFH?iHMsk7($Hgnnl*P05@>1(cDHmO4DwfqT z9r>-LuM9C=r^b7ErPt^3nZ>_T-3?oetDGTri&&#wW!D8gw*ne?l{$=HzL-)eqmhxt z6z>NA(n1@zd1t*&$SfZpkzuOZ7!n;*S7pGcWvZi>ET=j2%LPrHi>0=hu-rIO`pu0w z3zulWWK^hDR4n3GJsPfzy5+2Xi6}U2xCvrxO z#{vbXWr<>zCmF)@(ARy1$5itE%yP`S7$aZ1(pY;?6Iu&$#LAK&?ux+fH2g57KDOBj zSPwnon~fY5n&Zu zVB9{bWK=+iX%SRwT9Jy7CsaZgF&#F_6Ng}<&V=X~gJgvg&Q%z4L7ncB=p?s;g*F_f z``d}BkW<2Kup?6jykd-8z+IA4NEJ_O%YmcVrNhDcMpedX@Wz&-7M;fIY2TS9mbML^ zuIp`VA0kP*9VS83#XI;#;Ub|Bl+62jkh$H?+W-pbBsi+efp-SJ@dGF@S0Ns6A2TXX z%W%gxBNT)XGa^CVfA?ytqburOh`W_fLPjw*@sA{odlMc`*CM;2vh7UXX3zaM)#Q|M zPSUfua-60Qs~4(MODRMY`vm*}0`kNMu`My6Yo!a_F6yDuLsy`dqGLxRvhYB80dpbv zG4XMmct5gM;-!eHHntt~3;vuzw=W^j8$U$r2-uZzNucjz`jSlDA<>!Va1oP88&l9u z-Lr~zBvT(vU%+&0I-t`+P);CZD2Hou^OX{9WF^|O3{gJJf``2fom>?aWurrAs1kdi zEP3jyJN0Mk&n*b1ulLu9uWms}7(z14zXoQO2R~PBw;^lo7;yGEqVKVJ4Q5Ncvvq__cG*z18bybGguoEJgkRnds{8LaA`(tyK@zwu zaW@sTFmYIfRPhej!l$8cLN0-LOt=k&8~J8EY6wq{vIObN;vYoSC)!Ju8EENva4Ew3 zjSrqeq|XUKX=J;<0ITMs{GM&qAX)T_8x?QbOQ$aotCI<7RuIi+f$iEl1P$a3Fk3Fj z$-ROc{eFJL@Iv>^yvTb}9cS!^=x+14ViS0lV*+%FfNadpv}181JN|dfmTO3v1ZI@t zN?~4n1&cPIYhb8SXKp-0%l|Hki<~ve`>M3Yn4Dc{h#Ps7;vSBuKXYR6S{QlZd5_@0 zGo;+7D_0eGm|O7d%GHS^ZNHX+%%Yg{&6OisT$kpK6Xw*hMhosLk9sz+kL3Eud*nP; zK1uv4-e$Th*7P`3=gP7}jSzf5HS^pgIzfMqd`o_cdX*-Uztfog^LdoslW#6N|EC+@ z&>~B(C%bd4P}UjR=QPpY{>x2ck7v%li#Fc*4SEZq6_Q`<{v-LUJ@2@$f47^uE~@Zk zouPj&r6BhAzPHlu5FFVUwrDwJfmRXbx+Apf-yLJSr9I2iqLm6Ngxj`DuJ2*kTe%Jk z9*dUOkllF2{d!&(g9dlBDYU=A(Dr-3ad~!+QRc(gBsibivNm;fjtp%Rv};-J5FA=E zqV{MIwC4Lu`%1aBKpJ=vs|RyW(1>dOCOzzQ_U`!pTtD2L**-xziTeYOF>Ivb(Z=h> z_W;vA?p?^I?lB=4ydASabsDq#{ozNp$xb7Cndk7oZ=!k zK^_wf`iy>pVIhkfGtFKOT8*FBF#Gu-wy~^Jshh} zP{|I{^si-(E;o6OIxhi+9r#FRnu~!U@4!j%$EHtzolZhYhIk)%X3omd5w~(-zVc>CKS9GBl6pq%Z4mRvfTbiR;1htGHN&30^?=ql8ZpRrcTZNiYc zo*kmTt6xqvrfeSw%&{&SwR1Y>kI~3u4RnyHnm>H|%~p7=WI%K86WSB|NI7YEB*oj} z!*BBrR*hch%~DIeMM%}fV^rXLc}Wl1!IQ*98c98&WA;Ui{*FkjsgAQ5W7xt6{?Jpf zFSlvV#Zycm?@_S#Jll;D@$RWy4dL@*B#Y(UiCIWGflw~zj64NH=n2@D9oDzCf);+W z48yI88BzJh0!aA%(hu<(jO7}ghK~?q9CDbu2mra4uBnIFrC2}Tqv^DIuo5Y~Wt1rJrm zk7pOWeO_%R%%YL%$?uJ=kCv>0fm-Xe8toX9FePkm7BGwd*h4u@mcl8|Wz_(`J0TJt zDAN_96)><87|3HJSUV!8)9HLOkSWYDFTY9SKCwwoSj7)^+2!i6iupQ0l3pN#dVa@G z8X;&sb1&-zB|vRy4VD{7>hbMQnIz*IX69Ze+0Pmv%fn?>yH9(|$nX&CQlzt;i#ykZ zBqpB~Wf~L}c?{bVYbu4t{dOYD946pXhz6%*ez~|cJVfGWeqTBjCtZy?@NGzjWZycy zYYOC|VsL;tp~~q$%MWN3sG%imv;hJ1AQ5#bH8aH|=h%s4;6&zUUZ%-cA3Wo4a5Hf_ zq-b+ALl$E~Jyvm&{6yEd24xfA1lc(8zjWzk5H$;G;6T@rT@;lY&wxc4F3+m7@tGqe z$D9qwcGGt!-mh@u=l)u_jHc>^+*H(+w29g*Xl7KB>?k!IHpzH6uMhFYVWAz50cLdV zoXod2w>Zu6QG!R)fg%k%9SyzyBDg;e{V)mG#{oL}{T-+KGSeyM46}-LuBl}WR7Ivk z%ROUKlQ61W+*S3c=cO!)6=&&6($1kmXeG@T>K0mymgo$1^~BY4C7^`0`=zQefH~kU zo&jVGhg#L5)2lJIA*IfdVR~#CbEskXZZM{zI%5i{hj}PnbLZAH6Fpq2v^@7Rp*kJoP7|&B);gu< zYb<_TIjJMawfL68zG*eFuKK~6dm%2SaZ)z3*xkI!QDhah@r_yRb>YMZFT5&O{_2d@ zsi?vIwf9tY*Rgo??Z2JowR`JJy$i*3zL;Dd>jtTO=!4(YS4dl$C0XOhW;N7Npyx3Y z&~GdH`oV70O|L6m-m1TAcQ%Htj{$G+ZesH4hN(EpP`*N|L^EQgR{zJhpXM{gb;P z(1%AO>9fx-X}5Dv>@Ui<-J|b4<30KbL`JXmz_)9#r{Qq_s+nNf1X5d zyC4+H{+ZA#Xy{qt-(hGC6dDk}j$w?k58dnD^CpAFK?ncFKhVF%$Tit&guaN5_(5O5 zP@%uyp|01^zg9B{&yLW)yZ$|*TmH~LM1MigbI{>K(Fv$lcYq(@7vPWlf_8q#q3O)|W^P#_xtzyOfl(T&w z&Ly71X)>88jk(Kh%3lo=;Xb2fk#*kCjTE(8TC3!@Ah~e!du7>ls&}DN>9paYI5sff z6+gPL+HrW{Ai)hz3aAku+giu)@bFok|0(bzZHgH5MkSX{5#fLln65HE4RkYp5r2{W za~`8z+q;{~x1=9Dp{(lselKTYEkRk#A8CHomlec<2hIL^h&H*1sFb3tU#B-T_Wjq` z$*QwrdgZ43GCTgA41A>=Ir*Mh!VFkt&sWy$r5s;TvF-2i*HyN)N$2bgjk0%D+99*V zvX;dhGId363l77-@len}e7F{0J3wONuynOePR|AQx?ah*+bI*DoSv~1&&JEh;UC~S%vWrgi4Uu}!<4U=Jx5y)s z`;DF5x85H-wMK4+Oac*ydGK_g`dYd|7H0mfzoGiQ*Wiv0^*J zeg2fx{cQe5P+)ul&OAs5)r4A0EVGk`YCO!hk7VAKSpvrJ+O4wh##AG6rXj97HcnB* z`)-_dh7$LxX62`g^Y`n&!7|KK;16HM-OK zA+^to7F9MH?7BG_c>tgV3-?7n*GA7kcp;?{M73n3I6pzK`2RU0l9xB&oObG;1D?5f#JLU`Su z`V#B9I|1bgi3aIz5AGZm{3Kp+Qp1`s2nG`W!5Fb3%$s{T{BO|W`i3Zd^oT+Ee9l2IfQ%2RaVsZsOi3FT$WvPRc`GJ*Zb z(3OS>{T*}>kejd)#z|OfyS;`qL-7^e@yF;II{Nf|L4HxwlM~w)j2!cbOI#!rJOLzq zC8A3zE$6(a>lu{!Jgsn~#vRcQPc^n_s`N14b{}E2f4VdMMH~LWvYjd%*u1c1Z|B6& zz8A*#jzc<0Hx%Q$!$!~`^6FP*rZnz(vNj5FaeqPbFgGHY4D@K958D<}rCe~EVLQWm z1g0ZwPEs2o*Nan*NTxji=p!%m7LWDbm}9s&^RghURbA0ic`a5k~$AI4CG%ki}`vB_ktoF;w=l!@xw zc;yy1ChEwF$u)RQ`7>c~N5@s_3AN^2ord89h5b!9d7@S{yTWa@hX-wqW8{UROelN% zh(0gTCNO{_@n|YbAkomnc?k7`tEDTNR5ennFeQVMzBcsASRX?kMzB*gUR6i+IW8kO z8_}wmT$+o;SeX)1SlwE4s4CsXwO(MgnKK!=XI=XvtabH{fF)yEl2<Wq)h1ExQK=YF82WwB6qkWOOQu$gur3lZ-ic*8{1uDt6Jrd49b-dV z-+Lvkav53XYaBTInVA{JlAA)zj|_%2#}tT7zNsjU7Z}X|@jqE}nquH~8|@-tyyRN6KC6Hzthb6i#wo)wIs&?8h9lhbhAjPFaJbluZ} zH5(1`j-0;Pw^<#B2nsBDQlT)L@W`5)PoG2DV6UiytrH&Dt(zcL-pyIae^|w{&#)01YDh~>ZWQ!p_~TT;csJ{L#>#;RSH}4!Vji{ zl%}Z=>XYP_QNH1lRb{Q!iN~+IFaL;$LMu@o^_Ff13vP5kv|B%+c??6}$O$B@x0SJl ztO>&Wr097WobvI()}wX3aE$)+LSsoEO~DmxRh|QVh?si**j*G6ie|1Ug1#kl;NPO} zP}-GC1RfxMtJG5-E!A+1haojtw=&w%B>8w!NbfY{fcSV>7LLu_N_9qv!K8je15Nts zM^Pr0AUThz^gEvN8ZAI&?&%Skjm)r3%x(hd%W~s-OhnGtraviuSaZ*-{_T~cWscn> zA{^LJOzWnmrXSOd^f5GhJn7A5o_V~;4@w0sQ396dleR3Bb};T5Sz^TN`ryu*u-q|{ z6%T{?-OD-hINMTn&$~v8zYNJ+1bys61w}rMfho?KS|kGDg$d5qqfeib5Om_FVPC(b|utLDvB+40wucHgDVkZmJF!%CR!AE#&lW$C{ye=CBnXydY2jY2nveOW@{( zezo+SVQeTl&NN80WOtj)%%Cn{Ibix&dY#J1$VitK^hHnXMhIV5chy*I@`nZcmWjCBm@5Ox~$QtwPJfziR}{sX#o=LcyS9r(%}#z-*UUP9oqO zEPeRSlE}n4JH7A{4?0ZJeDsar!)S8X+}5g|mLi50MYxu&&uaJh`(?_$pq z%UdxdO%L$3%FU9D7~V*^mlCDBUMMv~Rj&?L)qyU-!KT$zwC8Fp-p$fHx-sbKs*kZ` zHVnoWdwS8IuZUuPG+`4r2}OyvYrfKO>K1*St+4TdJ5|IR>Y^NYU! z9Xt_Us9DBXj7dvR2y3#W)Sxlq3#7aA1dPW{Anj8dWm8X(b)~Hygsi0Lm=xc#!yGk& z9Q2itgNS-_HD0sX=m%XQ1S3HnfBuaC;_D$YM$R_w3j4E|E#A8zd;DG!F*!TX{C@ow zC7zs;yi=6@gZ~1{QLudKD7AvU%qR>;c=J4EUvQ?Kf;)X{;&Cd2J0EiB=?~MD1&hiy z3RvdyVsj7Je4WOr0X@&_dU!NKr;N?F0v}h+5@1#W2Pahckb<2?#1SrfCaSDTN=gz6 z9CF3LCxNFj()}3vOC>%tJmP)z`5^VR6N%Ef2{as-T$`dk6E~ZHwg+~r&Ve7ZAu1G_ z)T>zHZKn)+5Y#~yTb5d%{C9QY z(MXQ<-bt-w!&Ljt0`vmOM8jPKABLsFy*a>=qjk(Zh)tOkF`$WM7l@0kD1f*Co1Vn> ziA{(3eptea2f4kjY5Co_+6}zPq~!v?=0v$+7IlifBUf(yM;loN=c%&V1q`mC)Hhcj zr@bSsiV^cfDx@2zgWcO!?4Je#I_wnP2N`|r1HCMfLNQTrLhxAqmB7xOmVrB*O5htNppn07+!EWpZ z)zvPMMd1ixRgq0n47_c;W#Uwr94!}Tk4~~Ri_xq%=Pz?)IsDTDS?zN{RU?L+AgO;G z2|bi2sH>&n^HE3I!X*!QEi85eHVisQGV1@>K=_Acz>Y6i>sFhlUDj%f_AM z21ZwLQ2A=g6us0KNJX{pxaeg7r~D|C?tib(2Ly{}xjs&3obQ>F3!e3R-S$v+=}5m_ zL7DPmF7Jh-t7smTU3Xpf-*$fRL?uK`bhMYPSgTn{mb+k_c73zNq;jIx%#V7(+OD|Y zSr5niwO$>BwfPtu&v}jV1&JCR?0f(I%=MJ^xLE7p_?yAfZ^bCzP?5LZ;_!KE^cwD1 zgWhao(4U_&k+$l4?7QAo;#-mKA9_ryD|_924>bkbv(6k1GZ$dis>P~)EoiHBtm#!k z_WgyHqOeIv^7-t0EQhN+|9MFBkKi0+-1Gpag4l*xg?wFULeqc_%h9Z`~}>8|o(8Y>mxuyI{8qN$(ZwO-09#F9;om88$MOWEA?E+0l=4vbY?{ z^O#xw`pO}zoRpY(?J36_ZqsgF16ks|cb7@(T2BpEbZeVhvy&^;)Jmzom4vtPVPe)F zOswT#=$jO8ZTX=VySNXNB_~I59_q{AMwtWI3(*s zF|kRUi|9gX^v1MR4iYCE50ojciRpI(yGi^?M(%HRF3L7Vm-b?|>h6#7GLzwn>G-4~ z94r10%663jlZ#^rc~`|gq&wwcIhuYn#JHS~4VC`qgYXph2ad@|(AM$^CLz>|A+m8F zj~?v1z|pkbw*?arorVNEX+#6>tXyfaD9md?lkFCOO1CGfR+X`%@y~**V$lVEAu;W^ zS(H!OQ}RzuF0$@s!a}lpk_Z>O9wxN#_G!@ZHJ>783$VNXaf>v$fUMD`~XBSWF8n1^Xe^Ko!A3J2;gB zVWSeS*x!lWGZU}>A`qgl-Av4(o#mvOCq6udT<*5`tXPS{x#Gr?hsuRJ4Qb-n9j}En zKVQU+K}i%JJnl8<9d@>qy&zlGV|@H46CU@pgtVH6qb4bAum_kMbuWdrj2KFxu!`0A zj=vpWg7?JpHqhT+a;*_CS@za$?*X~4)>fYfu zJ>o(z?GxN~3Un|62xP%zp5l*(P^cQETAq&~Zx4<>dsq7Qta3RS-{M0u_LquG6k7XD zMS1eiV9^!dLBAl`^}V4+Ms3*EVBGO)4mXO$Dm{uU6x|rkQ1bU*BK~~%*z&ChM+~Cz zNmKv%P=$#*)nLR-%H7--C)HMZ@#6j9l2UH+0*eFQ@V-{55nNk31<;LGQ@oN2$7;bA)E ztPf6xz8k*$Ok?%>T{X=~Cy!R>6ELgK_Hw*JLA?XncgqGRcTAxrX`s9y(agcfpEtk- zrM{)V4Xe5(bNfehK&_USHf9_ybVXk&zUgVlxCA^3lOV;ikl z9&kt|l;6faIgBwQN${OXd4z83UU4k(S0GHdXbn^h4||+X@NqCET;QoIziFq0qS(O1 z1KOs#Skp+~*glK16Gj}fouz)S4EmmzZP5mv{_YU#H(r=)CEyA@l`XfYKUM+3z5eBj3-B9*n&p zOkIs4-OYFrUQz{@qD#o-p5D`z>F@e(yTH>=D}Hf=LK6TSX*cw5)@K_GoWb{U6rFg2 zVPz4Rexmb&0OI6qhO0Ue^H$>DWt^*x#qDe z_IqPEIAV|XJs$j_C(mG(1GBg&{y;-GT=2RkuoY*u!+r{Tr&LB)l$V!xA5d;4$s4mk zrdjP_Z#v2{TEAwiQqCWI|G6=lA \ 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 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 K*wmwZDE|d4VG2b6 literal 0 HcmV?d00001 diff --git a/images/dice/d6_3.png b/images/dice/d6_3.png new file mode 100644 index 0000000000000000000000000000000000000000..66a0fa610e6e2261a49816bb8c161b3cb0f0710c GIT binary patch literal 2825 zcmV+k3-mqPSc6K(lY}xYbK6yzyAw;?pLZmw(Bi_gngu!Gop}Dyk zy}i9qtJP4e)ttSdCej6>v9S^P`S~a;EJSs6HF|n_hWuV8lfl>57ZDK=(CKv0YPC=* zm8|8{|GgQFMw-AK6%`e9cz8HH{CT%@b8~Z})6>)G?(S}@GqbX?hR;W&^FecSGre~0 zTAHFLtA+pf=H=x@U$}6=s_fy?$s0Kyc>er3LPA2I*Xu#k_I=gy>C-1{-MSU=@$oR5 z%{I+Jq;rhHV8Eh9i}2yY2Uh33a^(slA|lY)*=hB*L^_95RaGG&Aps_niIe#sJ$i)o z>(`^dzu&6uh;)uscnS1u^!QkK^qp?If%M1<99Ffj41qB6yWj}rT6irP{7RM3kEK^umC|EX4(DPweJze5(pkja-5r5}fr8esbm>xyV~BK?(dl%8Wh)d4 zEMLCd;y5CmWrUQj(P*GjsVt5o(phHCoH>Yyh!7;3>BSU8ItOHAWbnT7SEJD&GBT3U zSR$PR=FXjqgoFg%^5k+kPMkQwcs!BL5ogby#jIJgc*)Ah$Usm~5aaPgI!Cy>yQ8F} zg!8j>iHV6wNlCG48zP-U{Qdn=Qc}X%nY#G+cw}W|S-l;R&N0Ek!KkXLLU3>}YjaW* zg#!l;ATKWuqeqXndV3wE2`TkeZK^y$-CtAR)t2p1O@L`6j* zDk|#N&uE&4$z-zo5MT+idv_oxisD>2K%_e*PPcofX&P;9ZD?4a_4#) zB>!l8dwWq@T8jMqeB8Tt53Q}OL)LRs6a}?fjRgx9K&R6oCML!qu65%YN%ov+Z*Qk} z?%YW$l}f96y2xZQdfmEpbbWoj&<}w%B;ROgU|@hgcI+5EcI;TY8u};{3OYGC*`{tz zj)tVA_w@A8I-Slg*~4!E0Ri;;_wR*%Xm%ml?ouB=endz}2FCp$u^>uPSdnamkP0g!C>J1kBv=4E4`wkLa-;ErfC!v6$z5f2BMWN zWJ4c7*e+7`B3kJ}pDcO&__3h*_8?m6Licy+?(W8yFJA<$!B&WLk=I}gqLkj(*XIyt z#yUDWgf77*L@8aNP~e|`{t>zi(OzsJO6h=jn))~*qLt31p$?aLczCe-f719ww9@Te z_c0FauR zDoAc(Vj{+k8z)FM8;Dx^mMvRYUD(G2S65dYIBVWosc`e=O>E!3oz;26!opBqUXB?v zX0ST1kVr;4@Z%MqnVE^JSFd97NpkJF^d8aw7(IG4Qc_Z|W5*6uS63rHKOgt+-$zqZ z6Ixqa@$K6;$mMdV)oS?q`un$sfjIt zJy?}qTU!ggUXSA9;y!5w-6W@h@_+>n9XK;${{es zX6ZFGH3$t2MQLfN-9_~G_v8He^9T(M#fJ|c?4FPCcWjbwFc=UT8j6n}KeAfP^XJbI z5)y*i+FDlUB@D%C>6Mk0NJvP)*RNl>s0S@AEr^MU!RODPIhmherS#U;R;*gJit}S{ zV`C%Y;^OfA`*%)roE592CnqQK*8Sn(!-qJ1`ZO-Zx^qtWIQKZM`INIL1{BrO<8uc@iwx{kl{?AbFx^QDGG>7AXOf}ax?YeOGL z#iDdac)}f_1}R}tdUtm>A9ef{lgUKh!Lx`(>1wr_k2?N}r>CbRuRj+Gi_%FKC+T5P zx>BhWd|8j!*MmD6j6SkywOX$0aOixg!AQE$ot###Tq$V2)L}N3 zn`ie5RKidgNq2X5$F^QJQCnM!vuDpDARvJ8b~2d^VPRn?Dk{Rgd-t$l!2)|rAuxoM z?<*Aw1(K7Kk(`{2hK2?d6cnJcvhr6ar_Rn!sMTsrm@ol;etytsG>D0bfv2Y@8}S4y zVf{<$sZ*y4)`y+I4AOJP2_e#*5F*_P8F5TcO-&`AlO;x7UEPrT6n-?HAvh6a_wIxc b=}ySMK~%?)jGK}q00000NkvXXu0mjfCunFA 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_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*leTCuDF;;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/images/icons/.directory b/images/icons/.directory new file mode 100644 index 0000000..b68f2ff --- /dev/null +++ b/images/icons/.directory @@ -0,0 +1,6 @@ +[Dolphin] +SortRole=creationtime +Timestamp=2021,4,13,9,23,48.267 +Version=4 +ViewMode=1 +VisibleRoles=Details_text,Details_size,Details_modificationtime,Details_creationtime,CustomizedDetails diff --git a/images/icons/locked.svg b/images/icons/locked.svg new file mode 100644 index 0000000..6033b6c --- /dev/null +++ b/images/icons/locked.svg @@ -0,0 +1,60 @@ + + + + + + image/svg+xml + + + + + + + + + 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/imperium5-actor-sheet.js b/modules/imperium5-actor-sheet.js new file mode 100644 index 0000000..e3f8c6c --- /dev/null +++ b/modules/imperium5-actor-sheet.js @@ -0,0 +1,354 @@ +/** + * Extend the basic ActorSheet with some very simple modifications + * @extends {ActorSheet} + */ + +import { Imperium5Utility } from "./imperium5-utility.js"; +import { Imperium5RollDialog } from "./imperium5-roll-dialog.js"; + +/* -------------------------------------------- */ +export class PegasusActorSheet extends ActorSheet { + + /** @override */ + static get defaultOptions() { + + return mergeObject(super.defaultOptions, { + classes: ["fvtt-pegasus-rpg", "sheet", "actor"], + template: "systems/fvtt-pegasus-rpg/templates/actor-sheet.html", + width: 920, + height: 720, + tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "combat" }], + dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }], + editScore: true + }); + } + + /* -------------------------------------------- */ + async getData() { + const objectData = PegasusUtility.data(this.object); + + let actorData = duplicate(PegasusUtility.templateData(this.object)); + + let formData = { + title: this.title, + id: objectData.id, + type: objectData.type, + img: objectData.img, + name: objectData.name, + editable: this.isEditable, + cssClass: this.isEditable ? "editable" : "locked", + data: actorData, + effects: this.object.effects.map(e => foundry.utils.deepClone(e.data)), + limited: this.object.limited, + specs: this.actor.getSpecs( ), + optionsDiceList: PegasusUtility.getOptionsDiceList(), + optionsLevel: PegasusUtility.getOptionsLevel(), + weapons: this.actor.checkAndPrepareEquipments( duplicate(this.actor.getWeapons()) ), + armors: this.actor.checkAndPrepareEquipments( duplicate(this.actor.getArmors())), + shields: this.actor.checkAndPrepareEquipments( duplicate(this.actor.getShields()) ), + equipments: this.actor.checkAndPrepareEquipments(duplicate(this.actor.getEquipmentsOnly()) ), + perks: duplicate(this.actor.getPerks()), + abilities: duplicate(this.actor.getAbilities()), + activePerks: duplicate(this.actor.getActivePerks()), + powers: duplicate(this.actor.getPowers()), + subActors: duplicate(this.actor.getSubActors()), + race: duplicate(this.actor.getRace()), + role: duplicate(this.actor.getRole()), + effects: duplicate(this.actor.getEffects()), + moneys: duplicate(this.actor.getMoneys()), + encCapacity: this.actor.getEncumbranceCapacity(), + containersTree: this.actor.containersTree, + encCurrent: this.actor.encCurrent, + encHindrance: this.actor.encHindrance, + 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; + } + + /* -------------------------------------------- */ + async openGenericRoll() { + let rollData = PegasusUtility.getBasicRollData() + rollData.alias = "Dice Pool Roll", + rollData.mode = "generic" + rollData.title = `Dice Pool Roll` + rollData.img = "icons/dice/d12black.svg" + + let rollDialog = await PegasusRollDialog.create( this.actor, rollData); + rollDialog.render( true ); + } + + /* -------------------------------------------- */ + async rollIDR( itemId, diceValue) { + let item = this.actor.data.items.get( itemId) ?? {name: "Unknown"} + let myRoll = new Roll(diceValue+"x").roll({ async: false }) + await PegasusUtility.showDiceSoNice(myRoll, game.settings.get("core", "rollMode")) + let chatData = { + user: game.user.id, + rollMode: game.settings.get("core", "rollMode"), + whisper: [game.user.id].concat(ChatMessage.getWhisperRecipients('GM')), + content: `${this.actor.name} has roll IDR for ${item.name} : ${myRoll.total}` + } + ChatMessage.create(chatData) + } + + /* -------------------------------------------- */ + /** @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"); + PegasusUtility.confirmDelete(this, li); + }); + + html.find('.spec-group-activate').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + this.actor.specPowerActivate( itemId) + }); + html.find('.spec-group-deactivate').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + this.actor.specPowerDeactivate( itemId) + }); + + 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('.effect-used').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + this.actor.perkEffectUsed( itemId) + }); + + html.find('.perk-status').change(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + this.actor.updatePerkStatus( itemId, ev.currentTarget.value) + }); + + html.find('.power-cost-spent').change(ev => { + const li = $(ev.currentTarget).parents(".item"); + let itemId = li.data("item-id"); + this.actor.updatePowerSpentCost( itemId, ev.currentTarget.value) + }); + + html.find('.power-dmg-roll').click(ev => { + const li = $(ev.currentTarget).parents(".item") + let itemId = li.data("item-id") + this.actor.powerDmgRoll( itemId ) + }) + + html.find('.perk-used').change(ev => { + const li = $(ev.currentTarget).parents(".item") + let itemId = li.data("item-id") + let index = Number($(ev.currentTarget).data("use-index") ) + this.actor.updatePerkUsed( itemId, index, ev.currentTarget.checked ) + }); + + 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('.momentum-minus').click(event => { + this.actor.modifyMomentum( -1 ) + } ) + html.find('.momentum-plus').click(event => { + this.actor.modifyMomentum( 1 ) + } ) + + html.find('.unarmed-attack').click((event) => { + this.actor.rollUnarmedAttack(); + }); + html.find('.generic-pool-roll').click((event) => { + this.openGenericRoll() + } ); + html.find('.attack-melee').click((event) => { + this.actor.rollPool( 'com'); + }); + html.find('.attack-ranged').click((event) => { + this.actor.rollPool( 'agi'); + }); + html.find('.defense-roll').click((event) => { + this.actor.rollPool( 'def', true); + }); + html.find('.damage-melee').click((event) => { + this.actor.rollPool( 'str'); + }); + html.find('.damage-ranged').click((event) => { + this.actor.rollPool( 'per'); + }); + html.find('.damage-resistance').click((event) => { + this.actor.rollPool( 'phy'); + }); + + html.find('.roll-stat').click((event) => { + const statId = $(event.currentTarget).data("stat-key"); + this.actor.rollStat(statId); + }); + html.find('.roll-mr').click((event) => { + this.actor.rollMR(); + }); + html.find('.roll-idr').click((event) => { + const diceValue = $(event.currentTarget).data("dice-value") + const li = $(event.currentTarget).parents(".item") + this.rollIDR( li.data("item-id"), diceValue) + }) + + html.find('.roll-spec').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const specId = li.data("item-id"); + this.actor.rollSpec(specId); + }); + html.find('.power-roll').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const powerId = li.data("item-id"); + this.actor.rollPower(powerId); + }); + html.find('.weapon-roll').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const weaponId = li.data("item-id"); + this.actor.rollWeapon(weaponId); + }); + html.find('.armor-roll').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const armorId = li.data("item-id"); + this.actor.rollArmor(armorId); + }); + + html.find('.weapon-damage-roll').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const weaponId = li.data("item-id"); + this.actor.rollWeapon(weaponId, true); + }); + + html.find('.weapon-damage').click((event) => { + const li = $(event.currentTarget).parents(".item"); + const weapon = this.actor.getOwnedItem(li.data("item-id")); + this.actor.rollDamage(weapon, 'damage'); + }); + + 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('.power-activate').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + this.actor.activatePower( li.data("item-id") ); + this.render(true); + }); + + html.find('.change-worstfear').change(ev => { + this.actor.manageWorstFear( ev.currentTarget.checked ) + }); + html.find('.change-desires').change(ev => { + this.actor.manageDesires( ev.currentTarget.checked ) + }); + + html.find('.update-field').change(ev => { + const fieldName = $(ev.currentTarget).data("field-name"); + let value = Number(ev.currentTarget.value); + this.actor.update( { [`${fieldName}`]: value } ); + }); + html.find('.perk-active').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + this.actor.activatePerk( li.data("item-id") ); + this.render(true); + }); + + } + + /* -------------------------------------------- */ + /** @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!!!!") + let item = await PegasusUtility.searchItem( dragData) + if (item == undefined) { + item = this.actor.items.get( dragData.data._id ) + } + this.actor.preprocessItem( event, item, true ) + super._onDropItem(event, dragData) + } + + /* -------------------------------------------- */ + /** @override */ + _updateObject(event, formData) { + // Update the Actor + return this.object.update(formData); + } +} diff --git a/modules/imperium5-actor.js b/modules/imperium5-actor.js new file mode 100644 index 0000000..1a20422 --- /dev/null +++ b/modules/imperium5-actor.js @@ -0,0 +1,1303 @@ +/* -------------------------------------------- */ +import { Imperium5Utility } from "./imperium5-utility.js"; +import { Imperium5RollDialog } from "./imperium5-roll-dialog.js"; + +/* -------------------------------------------- */ +const coverBonusTable = { "nocover": 0, "lightcover": 2, "heavycover": 4, "entrenchedcover": 6 }; +const statThreatLevel = [ "agi", "str", "phy", "com", "def", "per" ] +/* -------------------------------------------- */ +/* -------------------------------------------- */ +/** + * Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system. + * @extends {Actor} + */ +export class PegasusActor 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 PegasusUtility.loadCompendium("fvtt-weapons-of-the-gods.skills"); + data.items = skills.map(i => i.toObject()); + } + if (data.type == 'npc') { + } + + return super.create(data, options); + } + + /* -------------------------------------------- */ + prepareBaseData() { + } + + /* -------------------------------------------- */ + async prepareData() { + super.prepareData(); + } + + /* -------------------------------------------- */ + prepareDerivedData() { + + if (this.type == 'character') { + this.computeNRGHealth(); + this.data.data.encCapacity = this.getEncumbranceCapacity() + this.buildContainerTree() + } + + super.prepareDerivedData(); + } + + /* -------------------------------------------- */ + _preUpdate(changed, options, user) { + + super._preUpdate(changed, options, user); + } + + /* -------------------------------------------- */ + getEncumbranceCapacity() { + return this.data.data.statistics.str.value * 25 + } + + /* -------------------------------------------- */ + getActivePerks() { + let perks = this.data.items.filter(item => item.type == 'perk' && item.data.data.active); + return perks; + } + /* -------------------------------------------- */ + getAbilities() { + let ab = this.data.items.filter(item => item.type == 'ability'); + return ab; + } + /* -------------------------------------------- */ + getPerks() { + let comp = this.data.items.filter(item => item.type == 'perk'); + return comp; + } + /* -------------------------------------------- */ + getEffects() { + let comp = this.data.items.filter(item => item.type == 'effect'); + return comp; + } + /* -------------------------------------------- */ + getPowers() { + let comp = this.data.items.filter(item => item.type == 'power'); + return comp; + } + /* -------------------------------------------- */ + getMoneys() { + let comp = this.data.items.filter(item => item.type == 'money'); + return comp; + } + /* -------------------------------------------- */ + getArmors() { + let comp = duplicate(this.data.items.filter(item => item.type == 'armor') || []); + return comp; + } + /* -------------------------------------------- */ + getShields() { + let comp = this.data.items.filter(item => item.type == 'shield') + return comp; + } + getRace() { + let race = this.data.items.filter(item => item.type == 'race') + return race[0] ?? []; + } + getRole() { + let role = this.data.items.filter(item => item.type == 'role') + return role[0] ?? []; + } + /* -------------------------------------------- */ + checkAndPrepareEquipment(item) { + if ( item.data.resistance ) { + item.data.resistanceDice = PegasusUtility.getDiceFromLevel(item.data.resistance) + } + if ( item.data.idr ) { + item.data.idrDice = PegasusUtility.getDiceFromLevel(item.data.idr) + } + if ( item.data.damage) { + item.data.damageDice = PegasusUtility.getDiceFromLevel(item.data.damage) + } + if( item.data.level) { + item.data.levelDice = PegasusUtility.getDiceFromLevel(item.data.level) + } + } + + /* -------------------------------------------- */ + checkAndPrepareEquipments(listItem) { + for (let item of listItem) { + this.checkAndPrepareEquipment(item) + } + return listItem + } + + /* -------------------------------------------- */ + getWeapons() { + let comp = duplicate(this.data.items.filter(item => item.type == 'weapon') || []); + return comp; + } + /* -------------------------------------------- */ + getItemById(id) { + let item = this.data.items.find(item => item.id == id); + if (item) { + item = duplicate(item) + if (item.type == 'specialisation') { + item.data.dice = PegasusUtility.getDiceFromLevel(item.data.level); + } + } + return item; + } + + /* -------------------------------------------- */ + getSpecs() { + let comp = duplicate(this.data.items.filter(item => item.type == 'specialisation') || []); + for (let c of comp) { + c.data.dice = PegasusUtility.getDiceFromLevel(c.data.level); + } + return comp; + } + + /* -------------------------------------------- */ + async manageWorstFear(flag) { + if (flag) { + let effect = await PegasusUtility.getEffectFromCompendium("Worst Fear") + effect.data.worstfear = true + this.createEmbeddedDocuments('Item', [effect]) + } else { + let effect = this.data.items.find(item => item.type == "effect" && item.data.data.worstfear) + if (effect) { + this.deleteEmbeddedDocuments('Item', [effect.id]) + } + } + } + /* -------------------------------------------- */ + async manageDesires(flag) { + if (flag) { + let effect = await PegasusUtility.getEffectFromCompendium("Desires") + effect.data.desires = true + this.createEmbeddedDocuments('Item', [effect]) + } else { + let effect = this.data.items.find(item => item.type == "effect" && item.data.data.desires) + if (effect) { + this.deleteEmbeddedDocuments('Item', [effect.id]) + } + } + } + + /* -------------------------------------------- */ + getRelevantSpec(statKey) { + let comp = duplicate(this.data.items.filter(item => item.type == 'specialisation' && item.data.data.statistic == statKey) || []); + for (let c of comp) { + c.data.dice = PegasusUtility.getDiceFromLevel(c.data.level); + } + return comp; + } + + /* -------------------------------------------- */ + async activatePerk(perkId) { + let item = this.data.items.find(item => item.id == perkId); + if (item && item.data.data) { + let update = { _id: item.id, "data.active": !item.data.data.active }; + await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity + } + } + + /* -------------------------------------------- */ + async activatePower(itemId) { + let item = this.data.items.find(item => item.id == itemId) + if (item && item.data.data) { + + let nrg = duplicate(this.data.data.nrg) + if (!item.data.data.activated) { // Current value + + if (item.data.data.costspent > nrg.value || item.data.data.costspent > nrg.max) { + return ui.notifications.warn("Not enough NRG to activate the Power " + item.name) + } + nrg.activated += item.data.data.costspent + nrg.value -= item.data.data.costspent + nrg.max -= item.data.data.costspent + await this.update({ 'data.nrg': nrg }) + + let effects = [] + for (let effect of item.data.data.effectsgained) { + effect.data.powerId = itemId // Link to the perk, in order to dynamically remove them + effects.push(effect) + } + if (effects.length) { + await this.createEmbeddedDocuments('Item', effects) + } + } else { + nrg.activated -= item.data.data.costspent + nrg.max += item.data.data.costspent + await this.update({ 'data.nrg': nrg }) + + let toRem = [] + for (let item of this.data.items) { + if (item.type == 'effect' && item.data.data.powerId == itemId) { + toRem.push(item.id) + } + } + if (toRem.length) { + await this.deleteEmbeddedDocuments('Item', toRem) + } + } + let update = { _id: item.id, "data.activated": !item.data.data.activated } + await this.updateEmbeddedDocuments('Item', [update]) // Updates one EmbeddedEntity + } + } + + /* -------------------------------------------- */ + async equipItem(itemId) { + let item = this.data.items.find(item => item.id == itemId); + if (item && item.data.data) { + let update = { _id: item.id, "data.equipped": !item.data.data.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.data.items.filter(item => item.type == 'shield' || item.type == 'armor' || item.type == "weapon" || item.type == "equipment"); + } + /* ------------------------------------------- */ + getEquipmentsOnly() { + return duplicate(this.data.items.filter(item => item.type == "equipment") || []) + } + + /* ------------------------------------------- */ + computeThreatLevel() { + let tl = 0 + for(let key of statThreatLevel) { // Init with concerned stats + tl += PegasusUtility.getDiceValue( this.data.data.statistics[key].value ) + } + let powers = duplicate( this.getPowers() || []) + if ( powers.length > 0 ) { // Then add some mental ones of powers + tl += PegasusUtility.getDiceValue( this.data.data.statistics.foc.value ) + tl += PegasusUtility.getDiceValue( this.data.data.statistics.mnd.value ) + } + tl += PegasusUtility.getDiceValue( this.data.data.mr.value ) + let specThreat = this.data.items.filter( it => it.type == "specialisation" && it.data.data.isthreatlevel) || [] + for (let spec of specThreat) { + tl += PegasusUtility.getDiceValue( spec.data.data.level ) + } + tl += this.data.data.nrg.absolutemax + this.data.data.secondary.health.max + this.data.data.secondary.delirium.max + tl += this.getPerks().length * 5 + + let weapons = this.getWeapons() + for(let weapon of weapons) { + tl += PegasusUtility.getDiceValue(weapon.data.damage) + } + let armors = this.getArmors() + for(let armor of armors) { + tl += PegasusUtility.getDiceValue(armor.data.resistance) + } + let shields = this.getShields() + for(let shield of shields) { + tl += PegasusUtility.getDiceValue(shield.data.level) + } + let abilities = duplicate(this.getAbilities()) + for (let ability of abilities) { + tl += ability.data.threatlevel + } + let equipments = this.getEquipmentsOnly() + for (let equip of equipments) { + tl += equip.data.threatlevel + } + if ( tl != this.data.data.biodata.threatlevel) { + this.update( {'data.biodata.threatlevel': tl} ) + } + } + + /* ------------------------------------------- */ + async buildContainerTree() { + let equipments = duplicate(this.data.items.filter(item => item.type == "equipment") || []) + for (let equip1 of equipments) { + if (equip1.data.iscontainer) { + equip1.data.contents = [] + equip1.data.contentsEnc = 0 + for (let equip2 of equipments) { + if (equip1._id != equip2._id && equip2.data.containerid == equip1._id) { + equip1.data.contents.push(equip2) + let q = equip2.data.quantity ?? 1 + equip1.data.contentsEnc += q *equip2.data.weight + } + } + } + } + + // Compute whole enc + let enc = 0 + for (let item of equipments) { + item.data.idrDice = PegasusUtility.getDiceFromLevel( Number(item.data.idr)) + if (item.data.equipped) { + if (item.data.iscontainer) { + enc += item.data.contentsEnc + } else if (item.data.containerid == "") { + let q = item.data.quantity ?? 1 + enc += q * item.data.weight + } + } + } + for (let item of this.data.items) { // Process items/shields/armors + if ((item.type == "weapon" || item.type == "shield" || item.type == "armor") && item.data.data.equipped) { + let q = item.data.data.quantity ?? 1 + enc += q * item.data.data.weight + } + } + + // Store local values + this.encCurrent = enc + this.containersTree = equipments.filter(item => item.data.containerid == "") // Returns the root of equipements without container + + // Manages slow effect + let overCapacity = Math.floor(this.encCurrent / this.getEncumbranceCapacity()) + this.encHindrance = Math.floor(this.encCurrent / this.getEncumbranceCapacity()) + + //console.log("Capacity", overCapacity, this.encCurrent / this.getEncumbranceCapacity() ) + let effect = this.data.items.find(item => item.type == "effect" && item.data.data.slow) + if (overCapacity >= 4) { + if (!effect) { + effect = await PegasusUtility.getEffectFromCompendium("Slowed") + effect.data.slow = true + this.createEmbeddedDocuments('Item', [effect]) + } + } else { + if (effect) { + this.deleteEmbeddedDocuments('Item', [effect.id]) + } + } + } + + /* -------------------------------------------- */ + modifyMomentum(incDec) { + let momentum = duplicate(this.data.data.momentum) + momentum.value += incDec + if (momentum.value >= 0 ) { + this.update({ 'data.momentum': momentum }) + let chatData = { + user: game.user.id, + rollMode: game.settings.get("core", "rollMode"), + whisper: [game.user.id].concat(ChatMessage.getWhisperRecipients('GM')) + } + if (incDec > 0) { + chatData.content = `

${this.name} has gained a Momentum${this.name} has used a Momentum true) { + let array = Array.from(this.getEmbeddedCollection("ActiveEffect").values()); + return Array.from(this.getEmbeddedCollection("ActiveEffect").values()).filter(it => matching(it)); + } + /* -------------------------------------------- */ + getEffectByLabel(label) { + return this.getActiveEffects().find(it => it.data.label == label); + } + /* -------------------------------------------- */ + getEffectById(id) { + return this.getActiveEffects().find(it => it.id == id); + } + + /* -------------------------------------------- */ + getAttribute(attrKey) { + return this.data.data.attributes[attrKey]; + } + + /* -------------------------------------------- */ + async addObjectToContainer(itemId, containerId) { + let container = this.data.items.find(item => item.id == containerId && item.data.data.iscontainer) + let object = this.data.items.find(item => item.id == itemId) + console.log("Found", container, object) + if (container) { + if (object.data.data.iscontainer) { + ui.notifications.warn("Only 1 level of container allowed") + return + } + let alreadyInside = this.data.items.filter(item => item.data.data.containerid && item.data.data.containerid == containerId); + if (alreadyInside.length >= container.data.data.containercapacity) { + ui.notifications.warn("Container is already full !") + return + } else { + await this.updateEmbeddedDocuments("Item", [{ _id: object.id, 'data.containerid': containerId }]) + } + } else if (object && object.data.data.containerid) { // remove from container + console.log("Removeing: ", object) + await this.updateEmbeddedDocuments("Item", [{ _id: object.id, 'data.containerid': "" }]); + } + } + + /* -------------------------------------------- */ + async preprocessItem(event, item, onDrop = false) { + console.log("Pre-process !!!", item) + if (item.data.type == 'race') { + this.applyRace(item.data) + } else if (item.data.type == 'ability') { + this.applyAbility(item.data, [], true) + if (!onDrop) { + await this.createEmbeddedDocuments('Item', [item.data]) + return + } + } else { + if (!onDrop) { + await this.createEmbeddedDocuments('Item', [item.data]) + return + } + } + + 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) + } + + /* -------------------------------------------- */ + async equipGear(equipmentId) { + let item = this.data.items.find(item => item.id == equipmentId); + if (item && item.data.data) { + let update = { _id: item.id, "data.equipped": !item.data.data.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.data.data.subactors) { + subActors.push(duplicate(game.actors.get(id))) + } + return subActors; + } + /* -------------------------------------------- */ + async addSubActor(subActorId) { + let subActors = duplicate(this.data.data.subactors); + subActors.push(subActorId); + await this.update({ 'data.subactors': subActors }); + } + /* -------------------------------------------- */ + async delSubActor(subActorId) { + let newArray = []; + for (let id of this.data.data.subactors) { + if (id != subActorId) { + newArray.push(id); + } + } + await this.update({ 'data.subactors': newArray }); + } + + /* -------------------------------------------- */ + syncRoll(rollData) { + let linkedRollId = PegasusUtility.getDefenseState(this.id); + if (linkedRollId) { + rollData.linkedRollId = linkedRollId; + } + this.lastRollId = rollData.rollId; + PegasusUtility.saveRollData(rollData); + } + + /* -------------------------------------------- */ + getStat(statKey) { + let stat + if (statKey == 'mr') { + stat = duplicate(this.data.data.mr); + } else { + stat = duplicate(this.data.data.statistics[statKey]); + } + stat.dice = PegasusUtility.getDiceFromLevel(stat.value); + return stat; + } + + /* -------------------------------------------- */ + getOneSpec(specId) { + let spec = this.data.items.find(item => item.type == 'specialisation' && item.id == specId) + if (spec) { + spec = duplicate(spec); + spec.data.dice = PegasusUtility.getDiceFromLevel(spec.data.level); + } + return spec; + } + + /* -------------------------------------------- */ + specPowerActivate(specId) { + let spec = this.getOneSpec(specId) + if (spec) { + let powers = [] + for (let power of spec.data.powers) { + power.data.specId = specId + powers.push(power) + } + if (powers.length > 0) { + this.createEmbeddedDocuments('Item', powers) + } + this.updateEmbeddedDocuments('Item', [{ _id: specId, 'data.powersactivated': true }]) + } + } + + /* -------------------------------------------- */ + specPowerDeactivate(specId) { + let toRem = [] + for (let power of this.data.items) { + if (power.type == "power" && power.data.data.specId && power.data.data.specId == specId) { + toRem.push(power.id) + } + } + if (toRem.length > 0) { + this.deleteEmbeddedDocuments('Item', toRem) + } + this.updateEmbeddedDocuments('Item', [{ _id: specId, 'data.powersactivated': false }]) + } + + /* -------------------------------------------- */ + equipActivate(itemId) { + let item = this.items.get(itemId) + if (item) { + let effects = [] + for (let effect of item.data.data.effects) { + effect.data.itemId = itemId // Keep link + effects.push(effect) + } + if (effects.length > 0) { + this.createEmbeddedDocuments('Item', effects) + } + this.updateEmbeddedDocuments('Item', [{ _id: itemId, 'data.activated': true }]) + } + } + + /* -------------------------------------------- */ + equipDeactivate(itemId) { + let toRem = [] + for (let item of this.data.items) { + if (item.data.data.itemId && item.data.data.itemId == itemId) { + toRem.push(item.id) + } + } + if (toRem.length > 0) { + this.deleteEmbeddedDocuments('Item', toRem) + } + this.updateEmbeddedDocuments('Item', [{ _id: itemId, 'data.activated': false }]) + } + + /* -------------------------------------------- */ + async perkEffectUsed(itemId) { + let effect = this.items.get(itemId) + if (effect) { + PegasusUtility.createChatWithRollMode(effect.name, { + content: await renderTemplate(`systems/fvtt-pegasus-rpg/templates/chat-effect-used.html`, effect.data) + }); + + this.deleteEmbeddedDocuments('Item', [effect.id]) + } + } + + /* -------------------------------------------- */ + disableWeaverPerk(perk) { + if (perk.data.data.isweaver) { + for (let spec of this.data.items) { + if (spec.type == 'specialisation' && spec.data.data.ispowergroup) { + this.specPowerDeactivate(spec.id) + } + } + } + } + + /* -------------------------------------------- */ + enableWeaverPerk(perk) { + if (perk.data.data.isweaver) { + for (let spec of this.data.items) { + if (spec.type == 'specialisation' && spec.data.data.ispowergroup) { + this.specPowerActivate(spec.id) + } + } + } + } + + /* -------------------------------------------- */ + async cleanPerkEffects(itemId) { + let effects = [] + for (let item of this.data.items) { + if (item.type == "effect" && item.data.data.perkId == itemId) { + effects.push(item.id) + } + } + if (effects.length > 0) { + console.log("DELET!!!!", effects, this) + await this.deleteEmbeddedDocuments('Item', effects) + } + } + + /* -------------------------------------------- */ + async updatePerkUsed(itemId, index, checked) { + let item = this.items.get(itemId) + if (item && index) { + let key = "data.used" + index + await this.updateEmbeddedDocuments('Item', [{ _id: itemId, [`${key}`]: checked }]) + item = this.items.get(itemId) // Refresh + if (item.data.data.nbuse == "next1action" && item.data.data.used1) { + this.cleanPerkEffects(itemId) + } + if (item.data.data.nbuse == "next2action" && item.data.data.used1 && item.data.data.used2) { + this.cleanPerkEffects(itemId) + } + if (item.data.data.nbuse == "next3action" && item.data.data.used1 && item.data.data.used2 && item.data.data.used3) { + this.cleanPerkEffects(itemId) + } + } + } + + /* -------------------------------------------- */ + async updatePowerSpentCost(itemId, value) { + let item = this.items.get(itemId) + if (item && value) { + value = Number(value) || 0 + await this.updateEmbeddedDocuments('Item', [{ _id: itemId, 'data.costspent': value }]) + } + } + + /* -------------------------------------------- */ + async updatePerkStatus(itemId, status) { + let item = this.items.get(itemId) + if (item) { + + if (item.data.data.status == status) return;// Ensure we are really changing the status + + let updateOK = true + if (status == "ready") { + await this.cleanPerkEffects(itemId) + await this.updateEmbeddedDocuments('Item', [{ _id: itemId, 'data.used1': false, 'data.used2': false, 'data.used3': false }]) + if (item.data.data.features.nrgcost.flag) { + let nrg = duplicate(this.data.data.nrg) + nrg.activated -= item.data.data.features.nrgcost.value + nrg.max += item.data.data.features.nrgcost.value + await this.update({ 'data.nrg': nrg }) + } + if (item.data.data.features.bonushealth.flag) { + let health = duplicate(this.data.data.secondary.health) + health.value -= Number(item.data.data.features.bonushealth.value) || 0 + health.max -= Number(item.data.data.features.bonushealth.value) || 0 + await this.update({ 'data.secondary.health': health }) + } + if (item.data.data.features.bonusdelirium.flag) { + let delirium = duplicate(this.data.data.secondary.delirium) + delirium.value -= Number(item.data.data.features.bonusdelirium.value) || 0 + delirium.max -= Number(item.data.data.features.bonusdelirium.value) || 0 + await this.update({ 'data.secondary.delirium': delirium }) + } + if (item.data.data.features.bonusnrg.flag) { + let nrg = duplicate(this.data.data.nrg) + nrg.value -= Number(item.data.data.features.bonusnrg.value) || 0 + nrg.max -= Number(item.data.data.features.bonusnrg.value) || 0 + await this.update({ 'data.nrg': nrg }) + } + this.disableWeaverPerk(item) + PegasusUtility.createChatWithRollMode(item.name, { + content: await renderTemplate(`systems/fvtt-pegasus-rpg/templates/chat-perk-ready.html`, { name: this.name, perk: item }) + }); + + } + if (status == "activated") { + // Add effects linked to the perk + let effects = [] + for (let effect of item.data.data.effectsgained) { + effect.data.perkId = itemId // Link to the perk, in order to dynamically remove them + effect.data.isUsed = false // Flag to indicate removal when used in a roll window + effects.push(effect) + } + if (effects.length) { + await this.createEmbeddedDocuments('Item', effects) + } + // Manage additional flags + if (item.data.data.features.nrgcost.flag) { + if ((this.data.data.nrg.value >= item.data.data.features.nrgcost.value) && (this.data.data.nrg.max >= item.data.data.features.nrgcost.value)) { + let nrg = duplicate(this.data.data.nrg) + nrg.activated += item.data.data.features.nrgcost.value + nrg.value -= item.data.data.features.nrgcost.value + nrg.max -= item.data.data.features.nrgcost.value + await this.update({ 'data.nrg': nrg }) + } else { + updateOK = false + ui.notifications.warn("Not enough NRG to activate the Perk " + item.name) + } + } + if (item.data.data.features.bonushealth.flag) { + let health = duplicate(this.data.data.secondary.health) + health.value += Number(item.data.data.features.bonushealth.value) || 0 + health.max += Number(item.data.data.features.bonushealth.value) || 0 + await this.update({ 'data.secondary.health': health }) + } + if (item.data.data.features.bonusdelirium.flag) { + let delirium = duplicate(this.data.data.secondary.delirium) + delirium.value += Number(item.data.data.features.bonusdelirium.value) || 0 + delirium.max += Number(item.data.data.features.bonusdelirium.value) || 0 + await this.update({ 'data.secondary.delirium': delirium }) + } + if (item.data.data.features.bonusnrg.flag) { + let nrg = duplicate(this.data.data.nrg) + nrg.value += Number(item.data.data.features.bonusnrg.value) || 0 + nrg.max += Number(item.data.data.features.bonusnrg.value) || 0 + await this.update({ 'data.nrg': nrg }) + } + this.enableWeaverPerk(item) + } + if (updateOK) { + await this.updateEmbeddedDocuments('Item', [{ _id: item.id, 'data.status': status }]) + } + } + } + + /* -------------------------------------------- */ + async deleteAllItemsByType(itemType) { + let items = this.data.items.filter(item => item.type == itemType); + await this.deleteEmbeddedDocuments('Item', items); + } + + /* -------------------------------------------- */ + async addItemWithoutDuplicate(newItem) { + let item = this.data.items.find(item => item.type == newItem.type && item.name.toLowerCase() == newItem.name.toLowerCase()) + if (!item) { + await this.createEmbeddedDocuments('Item', [newItem]); + } + } + + /* -------------------------------------------- */ + async computeNRGHealth() { + if (this.isToken) return + + if (this.isOwner || game.user.isGM) { + let updates = {} + let phyDiceValue = PegasusUtility.getDiceValue(this.data.data.statistics.phy.value) + this.data.data.secondary.health.bonus + this.data.data.statistics.phy.mod; + if (phyDiceValue != this.data.data.secondary.health.max) { + updates['data.secondary.health.max'] = phyDiceValue + } + if (this.computeValue) { + updates['data.secondary.health.value'] = phyDiceValue + } + let mndDiceValue = PegasusUtility.getDiceValue(this.data.data.statistics.mnd.value) + this.data.data.secondary.delirium.bonus + this.data.data.statistics.mnd.mod; + if (mndDiceValue != this.data.data.secondary.delirium.max) { + updates['data.secondary.delirium.max'] = mndDiceValue + } + if (this.computeValue) { + updates['data.secondary.delirium.value'] = mndDiceValue + } + let stlDiceValue = PegasusUtility.getDiceValue(this.data.data.statistics.stl.value) + this.data.data.secondary.stealthhealth.bonus + this.data.data.statistics.stl.mod; + if (stlDiceValue != this.data.data.secondary.stealthhealth.max) { + updates['data.secondary.stealthhealth.max'] = stlDiceValue + } + if (this.computeValue) { + updates['data.secondary.stealthhealth.value'] = stlDiceValue + } + + let socDiceValue = PegasusUtility.getDiceValue(this.data.data.statistics.soc.value) + this.data.data.secondary.socialhealth.bonus + this.data.data.statistics.soc.mod; + if (socDiceValue != this.data.data.secondary.socialhealth.max) { + updates['data.secondary.socialhealth.max'] = socDiceValue + } + if (this.computeValue) { + updates['data.secondary.socialhealth.value'] = socDiceValue + } + + let nrgValue = PegasusUtility.getDiceValue(this.data.data.statistics.foc.value) + this.data.data.nrg.mod + this.data.data.statistics.foc.mod + if (nrgValue != this.data.data.nrg.absolutemax) { + updates['data.nrg.absolutemax'] = nrgValue + } + if (this.computeValue) { + updates['data.nrg.max'] = nrgValue + updates['data.nrg.value'] = nrgValue + } + + nrgValue = PegasusUtility.getDiceValue(this.data.data.statistics.mnd.value) + this.data.data.statistics.mnd.mod; + if (nrgValue != this.data.data.combat.stunthreshold) { + updates['data.combat.stunthreshold'] = nrgValue + } + + let momentum = this.data.data.statistics.foc.value + this.data.data.statistics.foc.mod + if (momentum != this.data.data.momentum.max) { + updates['data.momentum.value'] = 0 + updates['data.momentum.max'] = momentum + } + + let mrLevel = (this.data.data.statistics.agi.value + this.data.data.statistics.str.value) - this.data.data.statistics.phy.value + mrLevel = (mrLevel < 1) ? 1 : mrLevel; + if (mrLevel != this.data.data.mr.value) { + updates['data.mr.value'] = mrLevel + } + + let race = this.getRace() + if (race && race.name && (race.name != this.data.data.biodata.racename)) { + updates['data.biodata.racename'] = race.name + } + let role = this.getRole() + if (role && role.name && (role.name != this.data.data.biodata.rolename)) { + updates['data.biodata.rolename'] = role.name + } + //console.log("UPD", updates, this.data.data.biodata) + await this.update(updates) + + this.computeThreatLevel() + } + + if (this.isOwner || game.user.isGM) { + // Update current hindrance level + let hindrance = this.data.data.combat.hindrancedice + if (this.data.data.secondary.health.value < 0) { + hindrance += Math.abs(this.data.data.secondary.health.value) + } + if (this.data.data.secondary.delirium.value < 0) { + hindrance += Math.abs(this.data.data.secondary.delirium.value) + } + this.data.data.combat.hindrancedice = hindrance + } + } + + /* -------------------------------------------- */ + async modStat(key, inc = 1) { + let stat = duplicate(this.data.data.statistics[key]) + stat.mod += parseInt(inc) + await this.update({ [`data.statistics.${key}`]: stat }) + } + + /* -------------------------------------------- */ + async valueStat(key, inc = 1) { + key = key.toLowerCase() + let stat = duplicate(this.data.data.statistics[key]) + stat.value += parseInt(inc) + await this.update({ [`data.statistics.${key}`]: stat }) + } + + /* -------------------------------------------- */ + async addIncSpec(spec, inc = 1) { + console.log("Using spec : ", spec, inc) + let specExist = this.data.items.find(item => item.type == 'specialisation' && item.name.toLowerCase() == spec.name.toLowerCase()) + if (specExist) { + specExist = duplicate(specExist) + specExist.data.level += inc; + let update = { _id: specExist._id, "data.level": specExist.data.level }; + await this.updateEmbeddedDocuments('Item', [update]); + } else { + spec.data.level += inc; + await this.createEmbeddedDocuments('Item', [spec]); + } + } + + /* -------------------------------------------- */ + async incDecQuantity(objetId, incDec = 0) { + let objetQ = this.data.items.get(objetId) + if (objetQ) { + let newQ = objetQ.data.data.quantity + incDec + if (newQ >= 0) { + const updated = await this.updateEmbeddedDocuments('Item', [{ _id: objetQ.id, 'data.quantity': newQ }]) // pdates one EmbeddedEntity + } + } + } + /* -------------------------------------------- */ + async incDecAmmo(objetId, incDec = 0) { + let objetQ = this.data.items.get(objetId) + if (objetQ) { + let newQ = objetQ.data.data.ammocurrent + incDec; + if ( newQ >= 0 && newQ <= objetQ.data.data.ammomax) { + const updated = await this.updateEmbeddedDocuments('Item', [{ _id: objetQ.id, 'data.ammocurrent': newQ }]); // pdates one EmbeddedEntity + } + } + } + + /* -------------------------------------------- */ + async applyAbility(ability, updates = [], directUpdate = false) { + // manage stat bonus + if (ability.data.affectedstat != "notapplicable") { + let stat = duplicate(this.data.data.statistics[ability.data.affectedstat]) + stat.mod += Number(ability.data.statmodifier) + updates[`data.statistics.${ability.data.affectedstat}`] = stat + } + // manage status bonus + if (ability.data.statusaffected != "notapplicable") { + if (ability.data.statusaffected == 'nrg') { + let nrg = duplicate(this.data.data.nrg) + nrg.mod += Number(ability.data.statusmodifier) + updates[`data.nrg`] = nrg + } + if (ability.data.statusaffected == 'health') { + let health = duplicate(this.data.data.secondary.health) + health.bonus += Number(ability.data.statusmodifier) + updates[`data.secondary.health`] = health + } + if (ability.data.statusaffected == 'delirium') { + let delirium = duplicate(this.data.data.secondary.delirium) + delirium.bonus += Number(ability.data.statusmodifier) + updates[`data.secondary.delirium`] = delirium + } + } + if (directUpdate) { + await this.update(updates) + } + let newItems = [] + if (ability.data.effectsgained) { + for (let effect of ability.data.effectsgained) { + newItems.push(effect); + } + } + if (ability.data.powersgained) { + for (let power of ability.data.powersgained) { + newItems.push(power); + } + } + if (ability.data.specialisations) { + for (let spec of ability.data.specialisations) { + newItems.push(spec); + } + } + if (ability.data.attackgained) { + for (let weapon of ability.data.attackgained) { + newItems.push(weapon); + } + } + if (ability.data.armorgained) { + for (let armor of ability.data.armorgained) { + newItems.push(armor); + } + } + await this.createEmbeddedDocuments('Item', newItems) + } + + /* -------------------------------------------- */ + async applyRace(race) { + let updates = { 'data.biodata.racename': race.name } + let newItems = [] + await this.deleteAllItemsByType('race') + newItems.push(race); + + for (let ability of race.data.abilities) { + newItems.push(ability) + this.applyAbility(ability, updates) + } + if (race.data.perksgained) { + for (let power of race.data.perks) { + newItems.push(power); + } + } + + await this.update(updates) + await this.createEmbeddedDocuments('Item', newItems) + console.log("Updates", updates, newItems) + console.log("Updated actor", this) + } + + /* -------------------------------------------- */ + getIncreaseStatValue(updates, statKey) { + let stat = duplicate(this.data.data.statistics[statKey]) + stat.value += 1; + updates[`data.statistics.${statKey}`] = stat + } + + /* -------------------------------------------- */ + async applyRole(role) { + console.log("ROLE", role) + + let updates = { 'data.biodata.rolename': role.name } + let newItems = [] + await this.deleteAllItemsByType('role') + newItems.push(role); + + this.getIncreaseStatValue(updates, role.data.statincrease1) + this.getIncreaseStatValue(updates, role.data.statincrease2) + + //newItems = newItems.concat(duplicate(role.data.specialisationsplus1)) + newItems = newItems.concat(duplicate(role.data.specialperk)) + + await this.update(updates) + await this.createEmbeddedDocuments('Item', newItems) + } + + + /* -------------------------------------------- */ + addHindrancesList(effectsList) { + if (this.data.data.combat.stunlevel > 0) { + effectsList.push({ label: "Stun Hindrance", type: "hindrance", applied: false, value: this.data.data.combat.stunlevel }) + } + if (this.data.data.combat.hindrancedice > 0) { + effectsList.push({ label: "Health/Delirium Hindrance", type: "hindrance", applied: false, value: this.data.data.combat.hindrancedice }) + } + let overCapacity = Math.floor(this.encCurrent / this.getEncumbranceCapacity()) + if (overCapacity > 0) { + effectsList.push({ label: "Encumbrance Hindrance", type: "hindrance", applied: false, value: overCapacity }) + } + let effects = this.data.items.filter(item => item.type == 'effect') + for (let effect of effects) { + effect = duplicate(effect) + if (effect.data.hindrance) { + effectsList.push({ label: effect.name, type: "effect", applied: false, effect: effect, value: effect.data.effectlevel }) + } + } + } + + /* -------------------------------------------- */ + /* ROLL SECTION + /* -------------------------------------------- */ + + /* -------------------------------------------- */ + addEffects(rollData) { + let effects = this.data.items.filter(item => item.type == 'effect') + for (let effect of effects) { + effect = duplicate(effect) + if (!effect.data.hindrance + && (effect.data.stataffected != "notapplicable" || effect.data.specaffected.length > 0) + && effect.data.stataffected != "special") { + if (effect.data.effectstatlevel) { + effect.data.effectlevel = this.data.data.statistics[effect.data.effectstat].value + } + rollData.effectsList.push({ label: effect.name, type: "effect", applied: false, effect: effect, value: effect.data.effectlevel }) + } + } + } + + /* -------------------------------------------- */ + addArmorsShields(rollData, statKey = "none", useShield = false) { + if (statKey == 'phy') { + let armors = this.getArmors() + for (let armor of armors) { + rollData.armorsList.push({ label: `Armor ${armor.name}`, type: "other", applied: false, value: armor.data.resistance }) + } + } + if (useShield) { + let shields = this.data.items.filter(item => item.type == "shield" && item.data.data.equipped) + for (let sh of shields) { + rollData.armorsList.push({ label: `Shield ${sh.name}`, type: "other", applied: false, value: sh.data.data.level }) + } + } + } + addWeapons(rollData, statKey) { + let weapons = this.getWeapons() + for (let weapon of weapons) { + if (weapon.data.equipped && weapon.data.statistic == statKey) { + rollData.weaponsList.push({ label: `Attack ${weapon.name}`, type: "attack", applied: false, weapon: weapon, value: 0 }) + } + if (weapon.data.equipped && weapon.data.enhanced && weapon.data.enhancedstat == statKey) { + rollData.weaponsList.push({ label: `Enhanced Attack ${weapon.name}`, type: "enhanced", applied: false, weapon: weapon, value: weapon.data.enhancedlevel }) + } + if (weapon.data.equipped && weapon.data.damagestatistic == statKey) { + rollData.weaponsList.push({ label: `Damage ${weapon.name}`, type: "damage", applied: false, weapon: weapon, value: weapon.data.damage }) + } + } + } + addEquipments(rollData, statKey) { + let equipments = this.getEquipmentsOnly() + for (let equip of equipments) { + if (equip.data.equipped && equip.data.stataffected == statKey) { + rollData.equipmentsList.push({ label: `Item ${equip.name}`, type: "item", applied: false, equip: equip, value: equip.data.level }) + } + } + } + + /* -------------------------------------------- */ + getCommonRollData(statKey = undefined, useShield = false) { + let rollData = PegasusUtility.getBasicRollData() + rollData.alias = this.name + rollData.actorImg = this.img + rollData.actorId = this.id + rollData.img = this.img + rollData.activePerks = duplicate(this.getActivePerks()) + + if (statKey) { + rollData.statKey = statKey + rollData.stat = this.getStat(statKey) + rollData.statDicesLevel = rollData.stat.value + rollData.statMod = rollData.stat.mod + rollData.specList = this.getRelevantSpec(statKey) + rollData.selectedSpec = "0" + if (statKey.toLowerCase() == "mr") { + rollData.img = "systems/fvtt-pegasus-rpg/images/icons/MR.webp" + } else { + rollData.img = `systems/fvtt-pegasus-rpg/images/icons/${rollData.stat.abbrev}.webp` + } + } + + this.addEffects(rollData) + this.addArmorsShields(rollData, statKey, useShield) + this.addWeapons(rollData, statKey, useShield) + this.addEquipments(rollData, statKey) + + return rollData + } + + /* -------------------------------------------- */ + async startRoll(rollData) { + this.syncRoll(rollData); + //console.log("ROLL DATA", rollData) + let rollDialog = await PegasusRollDialog.create(this, rollData); + console.log(rollDialog); + rollDialog.render(true); + + } + + /* -------------------------------------------- */ + powerDmgRoll(itemId) { + let power = this.data.items.get(itemId) + if (power) { + power = duplicate(power) + this.rollPool(power.data.dmgstatistic) + } + } + + /* -------------------------------------------- */ + rollPool(statKey, useShield = false) { + let stat = this.getStat(statKey) + if (stat) { + let rollData = this.getCommonRollData(statKey, useShield) + rollData.mode = "stat" + rollData.title = `Roll : ${stat.label} ` + rollData.img = "icons/dice/d12black.svg" + + this.startRoll(rollData) + } else { + ui.notifications.warn("Statistic not found !"); + } + } + + /* -------------------------------------------- */ + rollUnarmedAttack() { + let stat = this.getStat('com') + if (stat) { + let rollData = this.getCommonRollData(statKey) + rollData.mode = "stat" + rollData.title = `Unarmed Attack`; + rollData.damages = this.getStat('str'); + + this.startRoll(rollData); + } else { + ui.notifications.warn("Statistic not found !"); + } + } + + /*-------------------------------------------- */ + rollStat(statKey) { + let stat = this.getStat(statKey); + if (stat) { + let rollData = this.getCommonRollData(statKey) + rollData.mode = "stat" + rollData.title = `Stat ${stat.label}`; + + this.startRoll(rollData) + } else { + ui.notifications.warn("Statistic not found !"); + } + } + + /* -------------------------------------------- */ + async rollSpec(specId) { + let spec = this.getOneSpec(specId) + if (spec) { + let rollData = this.getCommonRollData(spec.data.statistic) + rollData.mode = "spec" + rollData.title = `Spec. : ${spec.name} ` + rollData.specList = [spec] + rollData.selectedSpec = spec._id + rollData.specName = spec.name + rollData.img = spec.img + rollData.specDicesLevel = spec.data.level + this.startRoll(rollData) + } else { + ui.notifications.warn("Specialisation not found !"); + } + } + + /* -------------------------------------------- */ + async rollMR(isInit = false, combatId = 0, combatantId = 0) { + let mr = duplicate(this.data.data.mr) + if (mr) { + mr.dice = PegasusUtility.getDiceFromLevel(mr.value); + + let rollData = this.getCommonRollData("mr") + rollData.mode = "MR" + rollData.img = "systems/fvtt-pegasus-rpg/images/icons/MR.webp" + rollData.isInit = isInit + rollData.combatId = combatId + rollData.combatantId = combatantId + console.log("MR ROLL", rollData) + this.startRoll(rollData); + } else { + ui.notifications.warn("MR not found !"); + } + } + + /* -------------------------------------------- */ + async rollArmor(armorId) { + let armor = this.data.items.get(armorId) + + if (armor) { + let rollData = this.getCommonRollData(armor.data.statistic) + + armor = duplicate(armor); + this.checkAndPrepareEquipment(armor); + + rollData.mode = "armor" + rollData.armor = armor + rollData.title = `Armor : ${armor.name}` + rollData.isResistance = true; + rollData.img = armor.img + rollData.otherDicesLevel = armor.data.resistance + + this.startRoll(rollData); + } else { + ui.notifications.warn("Armor not found !", weaponId); + } + } + + /* -------------------------------------------- */ + async rollPower(powerId) { + let power = this.data.items.get(powerId) + + if (power) { + power = duplicate(power) + let rollData = this.getCommonRollData(power.data.statistic) + + rollData.mode = "power" + rollData.power = power + rollData.title = `Power : ${power.name}` + rollData.img = power.img + + this.startRoll(rollData); + } else { + ui.notifications.warn("Power not found !", powerId); + } + } +} diff --git a/modules/imperium5-combat.js b/modules/imperium5-combat.js new file mode 100644 index 0000000..352736f --- /dev/null +++ b/modules/imperium5-combat.js @@ -0,0 +1,38 @@ +import { Imperium5Utility } from "./imperium5pegasus-utility.js"; + +/* -------------------------------------------- */ +export class PegasusCombat 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() + } + } + + /* -------------------------------------------- */ + static async decInitBy10( combatantId, value) { + const combatant = game.combat.combatants.get(combatantId) + let initValue = combatant.initiative + value + await game.combat.setInitiative(combatantId, initValue) + setTimeout( this.checkTurnPosition, 400) // The setInitiative is no more blocking for unknown reason + } + +} diff --git a/modules/imperium5-commands.js b/modules/imperium5-commands.js new file mode 100644 index 0000000..f332e11 --- /dev/null +++ b/modules/imperium5-commands.js @@ -0,0 +1,123 @@ +/* -------------------------------------------- */ + +import { Imperium5Utility } from "./imperium5-utility.js"; +import { Imperium5RollDialog } from "./imperium5-roll-dialog.js"; + +/* -------------------------------------------- */ +export class PegasusCommands { + + static init() { + if (!game.system.pegasus.commands) { + const pegasusCommands = new PegasusCommands(); + pegasusCommands.registerCommand({ path: ["/char"], func: (content, msg, params) => pegasusCommands.createChar(msg), descr: "Create a new character" }); + pegasusCommands.registerCommand({ path: ["/pool"], func: (content, msg, params) => pegasusCommands.poolRoll(msg), descr: "Generic Roll Window" }); + game.system.pegasus.commands = pegasusCommands; + } + } + 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("pegasusCommands._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) { + RdDCommands._chatAnswer(msg, command.descr); + } + return true; + } + return false; + } + + /* -------------------------------------------- */ + async createChar(msg) { + game.system.pegasus.creator = new PegasusActorCreate(); + game.system.pegasus.creator.start(); + } + + /* -------------------------------------------- */ + static _chatAnswer(msg, content) { + msg.whisper = [game.user.id]; + msg.content = content; + ChatMessage.create(msg); + } + + /* -------------------------------------------- */ + async poolRoll( msg) { + let rollData = PegasusUtility.getBasicRollData() + rollData.alias = "Dice Pool Roll", + rollData.mode = "generic" + rollData.title = `Dice Pool Roll`; + + let rollDialog = await PegasusRollDialog.create( this, rollData); + rollDialog.render( true ); + } + +} \ No newline at end of file diff --git a/modules/imperium5-item-sheet.js b/modules/imperium5-item-sheet.js new file mode 100644 index 0000000..014076d --- /dev/null +++ b/modules/imperium5-item-sheet.js @@ -0,0 +1,509 @@ +import { Imperium5Utility } from "./imperium5-utility.js"; + +/** + * Extend the basic ItemSheet with some very simple modifications + * @extends {ItemSheet} + */ +export class PegasusItemSheet extends ItemSheet { + + /** @override */ + static get defaultOptions() { + + return mergeObject(super.defaultOptions, { + classes: ["fvtt-pegasus-rpg", "sheet", "item"], + template: "systems/fvtt-pegasus-rpg/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() { + const objectData = PegasusUtility.data(this.object); + + let itemData = foundry.utils.deepClone(PegasusUtility.templateData(this.object)); + let formData = { + title: this.title, + id: this.id, + type: objectData.type, + img: objectData.img, + name: objectData.name, + editable: this.isEditable, + cssClass: this.isEditable ? "editable" : "locked", + optionsDiceList: PegasusUtility.getOptionsDiceList(), + optionsStatusList: PegasusUtility.getOptionsStatusList(), + data: itemData, + limited: this.object.limited, + options: this.options, + owner: this.document.isOwner, + mr: (this.object.type == 'specialisation'), + isGM: game.user.isGM + } + + this.options.editable = !(this.object.data.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(PegasusUtility.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-pegasus-rpg/templates/post-item.html', chatData).then(html => { + let chatOptions = PegasusUtility.chatDataSetup(html); + ChatMessage.create(chatOptions) + }); + } + + /* -------------------------------------------- */ + async viewSubitem(ev) { + let field = $(ev.currentTarget).data('type'); + let idx = Number($(ev.currentTarget).data('index')); + let itemData = this.object.data.data[field][idx]; + if (itemData.name != 'None') { + let spec = await Item.create(itemData, { temporary: true }); + spec.data.origin = "embeddedItem"; + new PegasusItemSheet(spec).render(true); + } + } + + /* -------------------------------------------- */ + async deleteSubitem(ev) { + let field = $(ev.currentTarget).data('type'); + let idx = Number($(ev.currentTarget).data('index')); + let oldArray = this.object.data.data[field]; + let itemData = this.object.data.data[field][idx]; + if (itemData.name != 'None') { + let newArray = []; + for (var i = 0; i < oldArray.length; i++) { + if (i != idx) { + newArray.push(oldArray[i]); + } + } + this.object.update({ [`data.${field}`]: newArray }); + } + } + + /* -------------------------------------------- */ + async manageSpec() { + let itemData = this.object.data.data.specialisation[0]; + if (itemData.name != 'None') { + let spec = await Item.create(itemData, { temporary: true }); + spec.data.origin = "embeddedItem"; + new PegasusItemSheet(spec).render(true); + } + } + + /* -------------------------------------------- */ + /** @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); + }); + + html.find('.stat-choice-flag').click(ev => { + let idx = $(ev.currentTarget).data("stat-idx"); + let array = duplicate(this.object.data.data.statincreasechoice); + array[Number(idx)].flag = !array[Number(idx)].flag; + this.object.update({ "data.statincreasechoice": array }); + }); + + // 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(); + }); + + } + + /* -------------------------------------------- */ + async addAbility(event, item, dataItem) { + let newItem = duplicate(item.data); + newItem._id = randomID(dataItem.id.length); + console.log("ABB", event, item, dataItem) + if (event.toElement.className == 'drop-abilities') { + let abilityArray = duplicate(this.object.data.data.abilities); + abilityArray.push(newItem); + await this.object.update({ 'data.abilities': abilityArray }); + } + if (event.toElement.className == 'drop-optionnal-abilities') { + let abilityArray = duplicate(this.object.data.data.optionnalabilities); + abilityArray.push(newItem); + await this.object.update({ 'data.optionnalabilities': abilityArray }); + } + } + + /* -------------------------------------------- */ + async addRacePerk(event, item, dataItem) { + let newItem = duplicate(item.data); + newItem._id = randomID(dataItem.id.length); + if (event.toElement.className == 'drop-race-perk') { + let perkArray = duplicate(this.object.data.data.perks); + perkArray.push(newItem); + await this.object.update({ 'data.perks': perkArray }); + } + } + + /* -------------------------------------------- */ + async addSpecialisation(item, dataItem) { + let newItem = duplicate(item.data); + newItem._id = randomID(dataItem.id.length); + let specArray = [newItem]; + await this.object.update({ 'data.specialisation': specArray }); + } + + /* -------------------------------------------- */ + async addRoleSpecialisation(event, item, dataItem) { + let newItem = duplicate(item.data); + newItem._id = randomID(dataItem.id.length); + console.log("Add spec", event, newItem); + if (event.toElement.className == 'drop-spec1') { + let specArray = duplicate(this.object.data.data.specialisationsplus1); + specArray.push(newItem); + await this.object.update({ 'data.specialisationsplus1': specArray }); + } + if (event.toElement.className == 'drop-spec2') { + let specArray = duplicate(this.object.data.data.specincrease); + specArray.push(newItem); + await this.object.update({ 'data.specincrease': specArray }); + } + } + + /* -------------------------------------------- */ + async addRolePerk(event, item, dataItem) { + let newItem = duplicate(item.data); + newItem._id = randomID(dataItem.id.length); + console.log("Add spec", event, newItem); + if (event.toElement.className == 'drop-perk2') { + let perkArray = duplicate(this.object.data.data.perks); + perkArray.push(newItem); + await this.object.update({ 'data.perks': perkArray }); + } + if (event.toElement.className == 'drop-specialperk1') { + let perkArray = duplicate(this.object.data.data.specialperk); + perkArray.push(newItem); + await this.object.update({ 'data.specialperk': perkArray }); + } + } + + /* -------------------------------------------- */ + async addPower(event, item, dataItem) { + let newItem = duplicate(item.data); + newItem._id = randomID(dataItem.id.length); + if (event.toElement.className == 'drop-spec-power') { + let powArray = duplicate(this.object.data.data.powers); + powArray.push(newItem); + await this.object.update({ 'data.powers': powArray }); + } + } + + /* -------------------------------------------- */ + async addAbilityPower(event, item, dataItem) { + let newItem = duplicate(item.data); + newItem._id = randomID(dataItem.id.length); + if (event.toElement.className == 'drop-ability-power') { + let powArray = duplicate(this.object.data.data.powersgained); + powArray.push(newItem); + await this.object.update({ 'data.powersgained': powArray }); + } + } + /* -------------------------------------------- */ + async addAbilityEffect(event, item, dataItem) { + let newItem = duplicate(item.data); + newItem._id = randomID(dataItem.id.length); + if (event.toElement.className == 'drop-ability-effect') { + let powArray = duplicate(this.object.data.data.effectsgained); + powArray.push(newItem); + await this.object.update({ 'data.effectsgained': powArray }); + } + } + + /* -------------------------------------------- */ + async addAbilitySpec(event, item, dataItem) { + let newItem = duplicate(item.data); + newItem._id = randomID(dataItem.id.length); + if (event.toElement.className == 'drop-ability-spec') { + let powArray = duplicate(this.object.data.data.specialisations); + powArray.push(newItem); + await this.object.update({ 'data.specialisations': powArray }); + } + } + /* -------------------------------------------- */ + async addAbilityWeaponArmor(event, item, dataItem) { + let newItem = duplicate(item.data); + newItem._id = randomID(dataItem.id.length); + if (event.toElement.className == 'drop-ability-weapon') { + let weaponArray = duplicate(this.object.data.data.attackgained); + weaponArray.push(newItem); + await this.object.update({ 'data.attackgained': weaponArray }); + } + if (event.toElement.className == 'drop-ability-armor') { + let armorArray = duplicate(this.object.data.data.armorgained); + armorArray.push(newItem); + await this.object.update({ 'data.armorgained': armorArray }); + } + } + + /* -------------------------------------------- */ + async addPerkSpecialisation(event, item, dataItem) { + let newItem = duplicate(item.data); + if (event.toElement.className == 'drop-spec-perk') { + //console.log("PER SPEC", event) + let key = event.toElement.dataset["key"]; + if (key == 'affectedspec') { + await this.object.update({ 'data.features.affectedspec.value': newItem.name }); + } else { + await this.object.update({ 'data.features.gainspecdice.value': newItem.name }); + } + } + } + + /* -------------------------------------------- */ + async addPerkEffect(event, item, dataItem) { + let newItem = duplicate(item.data) + if (event.toElement.className == 'drop-perk-effect') { + let effectArray = duplicate(this.object.data.data.effectsgained) + effectArray.push(newItem) + await this.object.update({ 'data.effectsgained': effectArray }) + } + } + + /* -------------------------------------------- */ + async addEffectPower(event, item, dataItem) { + let newItem = duplicate(item.data) + if (event.toElement.className == 'drop-power-effect') { + let effectArray = duplicate(this.object.data.data.effectsgained) + effectArray.push(newItem); + await this.object.update({ 'data.effectsgained': effectArray }) + } + } + + /* -------------------------------------------- */ + async addEffectSpec(event, item, dataItem) { + let newItem = duplicate(item.data); + if (event.toElement.className == 'drop-effect-spec') { + let specArray = duplicate(this.object.data.data.recoveryrollspec); + specArray.push(newItem); + await this.object.update({ 'data.recoveryrollspec': specArray }); + } + if (event.toElement.className == 'drop-effect-specaffected') { + let specArray = duplicate(this.object.data.data.specaffected); + specArray.push(newItem); + await this.object.update({ 'data.specaffected': specArray }); + } + } + + /* -------------------------------------------- */ + async addEffectItem(event, item, dataItem) { + let newItem = duplicate(item.data); + if (event.toElement.className == 'drop-equipment-effect') { + let effectArray = duplicate(this.object.data.data.effects); + effectArray.push(newItem); + await this.object.update({ 'data.effects': effectArray }); + } + } + + /* -------------------------------------------- */ + async _onDrop(event) { + + if (this.object.type == 'weapon' || this.object.type == 'armor' || this.object.type == 'shield' + || this.object.type == 'equipment') { + let data = event.dataTransfer.getData('text/plain'); + if (data) { + let dataItem = JSON.parse(data); + let item = await PegasusUtility.searchItem(dataItem); + if (item.data.type == 'effect') { + return this.addEffectItem(event, item, dataItem); + } + } + } + + if (this.object.type == 'power') { + let data = event.dataTransfer.getData('text/plain'); + if (data) { + let dataItem = JSON.parse(data); + let item = await PegasusUtility.searchItem(dataItem); + if (item.data.type == 'effect') { + return this.addEffectPower(event, item, dataItem); + } + } + } + + if (this.object.type == 'effect') { + let data = event.dataTransfer.getData('text/plain'); + if (data) { + let dataItem = JSON.parse(data); + let item = await PegasusUtility.searchItem(dataItem); + if (item.data.type == 'specialisation') { + return this.addEffectSpec(event, item, dataItem); + } + } + } + + if (this.object.type == 'race') { + let data = event.dataTransfer.getData('text/plain'); + if (data) { + let dataItem = JSON.parse(data); + let item = await PegasusUtility.searchItem(dataItem); + if (item.data.type == 'ability') { + return this.addAbility(event, item, dataItem); + } + if (item.data.type == 'perk') { + return this.addRacePerk(event, item, dataItem); + } + } + } + + if (this.object.type == 'perk') { + let data = event.dataTransfer.getData('text/plain') + if (data) { + let dataItem = JSON.parse(data); + let item = await PegasusUtility.searchItem(dataItem) + if (item.data.type == 'specialisation') { + return this.addPerkSpecialisation(event, item, dataItem) + } + if (item.data.type == 'effect') { + return this.addPerkEffect(event, item, dataItem); + } + } + } + + if (this.object.type == 'specialisation') { + let data = event.dataTransfer.getData('text/plain'); + if (data) { + let dataItem = JSON.parse(data); + let item = await PegasusUtility.searchItem(dataItem); + if (item.data.type == 'power') { + return this.addPower(event, item, dataItem); + } + } + } + if (this.object.type == 'ability') { + let data = event.dataTransfer.getData('text/plain'); + if (data) { + let dataItem = JSON.parse(data); + let item = await PegasusUtility.searchItem(dataItem); + if (item.data.type == 'effect') { + return this.addAbilityEffect(event, item, dataItem); + } + if (item.data.type == 'power') { + return this.addAbilityPower(event, item, dataItem); + } + if (item.data.type == 'specialisation') { + return this.addAbilitySpec(event, item, dataItem); + } + if (item.data.type == 'weapon' || item.data.type == 'armor') { + return this.addAbilityWeaponArmor(event, item, dataItem); + } + } + } + + if (this.object.type == 'role') { + let data = event.dataTransfer.getData('text/plain'); + if (data) { + let dataItem = JSON.parse(data); + let item = await PegasusUtility.searchItem(dataItem); + if (item.data.type == 'specialisation') { + return this.addRoleSpecialisation(event, item, dataItem); + } + if (item.data.type == 'perk') { + return this.addRolePerk(event, item, dataItem); + } + } + } + + ui.notifications.warn("This item can not be dropped over another item"); + } + + /* -------------------------------------------- */ + get template() { + let type = this.item.type; + return `systems/fvtt-pegasus-rpg/templates/item-${type}-sheet.html`; + } + + /* -------------------------------------------- */ + /** @override */ + _updateObject(event, formData) { + return this.object.update(formData); + } +} diff --git a/modules/imperium5-item.js b/modules/imperium5-item.js new file mode 100644 index 0000000..ffdcd82 --- /dev/null +++ b/modules/imperium5-item.js @@ -0,0 +1,32 @@ + +export const defaultItemImg = { + specialisation: "systems/fvtt-pegasus-rpg/images/icons/icon_spec.webp", + perk: "systems/fvtt-pegasus-rpg/images/icons/icon_perk.webp", + ability: "systems/fvtt-pegasus-rpg/images/icons/icon_raceability.webp", + armor: "systems/fvtt-pegasus-rpg/images/icons/icon_armour.webp", + weapon: "systems/fvtt-pegasus-rpg/images/icons/icon_weapon.webp", + equipment: "systems/fvtt-pegasus-rpg/images/icons/icon_equipment.webp", + effect: "systems/fvtt-pegasus-rpg/images/icons/icon_effect.webp", + race: "systems/fvtt-pegasus-rpg/images/icons/icon_race.webp", + power: "systems/fvtt-pegasus-rpg/images/icons/icon_power.webp", + armour: "systems/fvtt-pegasus-rpg/images/icons/icon_armour.webp", + equipment: "systems/fvtt-pegasus-rpg/images/icons/icon_equipment.webp", + weapon: "systems/fvtt-pegasus-rpg/images/icons/icon_meleeweapon.webp", + shield: "systems/fvtt-pegasus-rpg/images/icons/icon_shield.webp", + money: "systems/fvtt-pegasus-rpg/images/icons/icon_money.webp", +} + +/** + * Extend the basic ItemSheet with some very simple modifications + * @extends {ItemSheet} + */ +export class PegasusItem extends Item { + + constructor(data, context) { + if (!data.img) { + data.img = defaultItemImg[data.type]; + } + super(data, context); + } + +} diff --git a/modules/imperium5-main.js b/modules/imperium5-main.js new file mode 100644 index 0000000..e76dac8 --- /dev/null +++ b/modules/imperium5-main.js @@ -0,0 +1,128 @@ +/** + * imperium5 system + * Author: Uberwald + * Software License: Prop + */ + +/* -------------------------------------------- */ + +/* -------------------------------------------- */ +// Import Modules +import { Imperium5Actor } from "./imperium5-actor.js"; +import { Imperium5ItemSheet } from "./imperium5-item-sheet.js"; +import { Imperium5ActorSheet } from "./imperium5-actor-sheet.js"; +import { Imperium5Utility } from "./imperium5-utility.js"; +import { Imperium5Combat } from "./imperium5-combat.js"; +import { Imperium5Item } from "./imperium5-item.js"; + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ + +/************************************************************************************/ +Hooks.once("init", async function () { + console.log(`Initializing Pegasus RPG`); + + /* -------------------------------------------- */ + // preload handlebars templates + PegasusUtility.preloadHandlebarsTemplates(); + + /* -------------------------------------------- */ + game.settings.register("fvtt-pegasus-rpg", "dice-max-level", { + name: "Maximum level value for dices lists", + hint: "Se the maximum level value for dices lists", + scope: "world", + config: true, + default: 20, + type: Number + }); + + /* -------------------------------------------- */ + // Set an initiative formula for the system + CONFIG.Combat.initiative = { + formula: "1d6", + decimals: 1 + }; + + /* -------------------------------------------- */ + game.socket.on("system.fvtt-pegasus-rpg", data => { + PegasusUtility.onSocketMesssage(data) + }); + + /* -------------------------------------------- */ + // Define custom Entity classes + CONFIG.Combat.documentClass = PegasusCombat + CONFIG.Actor.documentClass = PegasusActor + CONFIG.Item.documentClass = PegasusItem + //CONFIG.Token.objectClass = PegasusToken + game.system.pegasus = { }; + + /* -------------------------------------------- */ + // Register sheet application classes + Actors.unregisterSheet("core", ActorSheet); + Actors.registerSheet("fvtt-pegasus", PegasusActorSheet, { types: ["character"], makeDefault: true }); + Actors.registerSheet("fvtt-pegasus", PegasusNPCSheet, { types: ["npc"], makeDefault: false }); + + Items.unregisterSheet("core", ItemSheet); + Items.registerSheet("fvtt-pegasus", PegasusItemSheet, { makeDefault: true }); + + PegasusUtility.init(); + +}); + +/* -------------------------------------------- */ +function welcomeMessage() { + ChatMessage.create({ + user: game.user.id, + whisper: [game.user.id], + content: `
+ Welcome to the Pegasus Engine CORE RPG. +
Created by GMD Online +

The Pegasus Engine is a available for free on our website. It is also available as a PDF and in Print format at an affordable price.

+

This project has been made possible thanks to all the Official GMD Members and Patreon Members that have supported me and as a result made it possible to supply this interface for free.

+

In return I have made available a fully detailed Compendium for FREE for all members, which can be obtained from the Members page on my website.

+

You too can become a supporter for future projects and enjoy amazing rewards. +
Sign up Here : https://www.gmdonline.co.uk/gmdmemberspage/

+

GMD Online, GMD CORE RPG logo are © 2018 CORE Worlds and Game Rules © 2001. Interface © 2021 All rights reserved.

+

Enjoy and become the hero you were born to be!

+ ` }); +} + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ +Hooks.once("ready", function () { + + PegasusUtility.ready(); + // 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(); +}); + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ +Hooks.on("chatMessage", (html, content, msg) => { + if (content[0] == '/') { + let regExp = /(\S+)/g; + let commands = content.match(regExp); + if (game.system.pegasus.commands.processChatCommand(commands, content, msg)) { + return false; + } + } + return true; +}); + diff --git a/modules/imperium5-roll-dialog.js b/modules/imperium5-roll-dialog.js new file mode 100644 index 0000000..a963c69 --- /dev/null +++ b/modules/imperium5-roll-dialog.js @@ -0,0 +1,239 @@ +import { Imperium5Utility } from "./imperium5-utility.js"; + +export class PegasusRollDialog extends Dialog { + + /* -------------------------------------------- */ + static async create(actor, rollData ) { + + let options = { classes: ["PegasusDialog"], width: 620, height: 380, 'z-index': 99999 }; + let html = await renderTemplate('systems/fvtt-pegasus-rpg/templates/roll-dialog-generic.html', rollData); + + return new PegasusRollDialog(actor, rollData, html, options ); + } + + /* -------------------------------------------- */ + constructor(actor, rollData, html, options, close = undefined) { + let conf = { + title: (rollData.mode == "skill") ? "Skill" : "Roll", + 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 () { + PegasusUtility.rollPegasus( this.rollData ) + } + + + /* -------------------------------------------- */ + manageEffects( effectIdx, toggled) { + let effect = this.rollData.effectsList[effectIdx] + if (effect) { + effect.applied = toggled + + let level, genre, idVal + if (effect.type == 'hindrance' ) { + level = effect.value + genre = 'positive' + idVal = "#hindranceDicesLevel" + } + if (effect.type == 'effect' ) { + let effectData = effect.effect + level = effectData.data.effectlevel + genre = effectData.data.genre + effectData.data.isUsed = toggled + if (effectData.data.bonusdice) { + idVal = "#bonusDicesLevel" + } + if (effectData.data.reducedicevalue || effectData.data.statdice) { + idVal = "#statDicesLevel" + } + if (effectData.data.otherdice) { + idVal = "#otherDicesLevel" + } + if (effectData.data.hindrance) { + idVal = "#hindranceDicesLevel" + genre = 'positive' // Dynamic fix + } + } + // Now process the dice level update + let newLevel = Number($(idVal).val()) + console.log("Ongoing", newLevel, toggled, idVal ) + if (toggled) { + if ( genre == 'positive') { + newLevel += Number(level) + }else { + newLevel -= Number(level) + } + }else { + if ( genre == 'positive') { + newLevel -= Number(level) + }else { + newLevel += Number(level) + } + } + newLevel = (newLevel<0) ? 0 : newLevel + $(idVal).val(newLevel) + } + //console.log("Effect", effect, toggled) + this.rollData.statDicesLevel = Number($('#statDicesLevel').val()) + this.rollData.specDicesLevel = Number($('#specDicesLevel').val()) + this.rollData.bonusDicesLevel = Number($('#bonusDicesLevel').val()) + this.rollData.hindranceDicesLevel = Number($('#hindranceDicesLevel').val()) + this.rollData.otherDicesLevel = Number($('#otherDicesLevel').val()) + } + + /* -------------------------------------------- */ + manageArmors( armorIdx, toggled) { + let armor = this.rollData.armorsList[armorIdx] + if (armor) { + armor.applied = toggled + if (armor.type == 'other' ) { + if (toggled) { + this.rollData.otherDicesLevel += Number(armor.value) + } else { + this.rollData.otherDicesLevel -= Number(armor.value) + this.rollData.otherDicesLevel = (this.rollData.otherDicesLevel<0) ? 0 : this.rollData.otherDicesLevel + } + $("#otherDicesLevel").val(this.rollData.otherDicesLevel) + } + } + console.log("Armor", armorIdx, toggled) + } + + /* -------------------------------------------- */ + manageWeapons( weaponIdx, toggled) { + let weapon = this.rollData.weaponsList[weaponIdx] + if (weapon) { + if (toggled) { + this.rollData.weaponName = weapon.weapon.name + } else { + this.rollData.weaponName = undefined + } + weapon.applied = toggled + if (weapon.type == 'damage' || weapon.type == 'enhanced' ) { + if (toggled) { + this.rollData.otherDicesLevel += Number(weapon.value) + } else { + this.rollData.weaponName = undefined + this.rollData.otherDicesLevel -= Number(weapon.value) + this.rollData.otherDicesLevel = (this.rollData.otherDicesLevel<0) ? 0 : this.rollData.otherDicesLevel + } + $("#otherDicesLevel").val(this.rollData.otherDicesLevel) + } + } + console.log("Weapon", weaponIdx, toggled, this.rollData.otherDicesLevel, weapon) + } + + /* -------------------------------------------- */ + manageEquip( equipIdx, toggled) { + let equip = this.rollData.equipmentsList[equipIdx] + if (equip) { + equip.applied = toggled + let idVal = "#otherDicesLevel" // Default + if (equip.equip.data.bonusdice) { + idVal = "#bonusDicesLevel" + } + if (equip.equip.data.statdice) { + idVal = "#statDicesLevel" + } + if (equip.equip.data.otherdice) { + idVal = "#otherDicesLevel" + } + let newLevel = Number($(idVal).val()) + if (toggled) { + newLevel += Number(equip.value) + } else { + newLevel -= Number(equip.value) + } + newLevel = (newLevel <0) ? 0 : newLevel + $(idVal).val(newLevel) + // Then refresh + this.rollData.statDicesLevel = Number($('#statDicesLevel').val()) + this.rollData.specDicesLevel = Number($('#specDicesLevel').val()) + this.rollData.bonusDicesLevel = Number($('#bonusDicesLevel').val()) + this.rollData.hindranceDicesLevel = Number($('#hindranceDicesLevel').val()) + this.rollData.otherDicesLevel = Number($('#otherDicesLevel').val()) + } + } + + /* -------------------------------------------- */ + activateListeners(html) { + super.activateListeners(html); + + var dialog = this; + function onLoad() { + } + $(function () { onLoad(); }); + + html.find('#specList').change(async (event) => { + this.rollData.selectedSpec = event.currentTarget.value + let spec = this.rollData.specList.find(item => item._id == this.rollData.selectedSpec) + if ( spec) { + this.rollData.specDiceLevel = spec.data.level + this.rollData.specName = spec.name + $('#specDicesLevel').val(this.rollData.specDiceLevel) + } else { + this.rollData.specName = undefined + $('#specDicesLevel').val(0) + } + const content = await renderTemplate("systems/fvtt-pegasus-rpg/templates/roll-dialog-generic.html", this.rollData) + this.data.content = content + this.render(true) + }); + html.find('#statDicesLevel').change((event) => { + this.rollData.statDicesLevel = Number(event.currentTarget.value) + }); + html.find('#specDicesLevel').change((event) => { + this.rollData.specDicesLevel = Number(event.currentTarget.value) + }); + html.find('#bonusDicesLevel').change((event) => { + this.rollData.bonusDicesLevel = Number(event.currentTarget.value) + }); + html.find('#hindranceDicesLevel').change((event) => { + this.rollData.hindranceDicesLevel = Number(event.currentTarget.value) + }); + html.find('#otherDicesLevel').change((event) => { + this.rollData.otherDicesLevel = Number(event.currentTarget.value) + }); + html.find('.effect-clicked').change((event) => { + let toggled = event.currentTarget.checked + let effectIdx = $(event.currentTarget).data("effect-idx") + this.manageEffects( effectIdx, toggled) + }); + html.find('.armor-clicked').change((event) => { + let toggled = event.currentTarget.checked + let armorIdx = $(event.currentTarget).data("armor-idx") + this.manageArmors( armorIdx, toggled) + }); + html.find('.weapon-clicked').change((event) => { + let toggled = event.currentTarget.checked + let weaponIdx = $(event.currentTarget).data("weapon-idx") + this.manageWeapons( weaponIdx, toggled) + }); + html.find('.equip-clicked').change((event) => { + let toggled = event.currentTarget.checked + let equipIdx = $(event.currentTarget).data("equip-idx") + this.manageEquip( equipIdx, toggled) + }); + + + } +} \ No newline at end of file diff --git a/modules/imperium5-utility.js b/modules/imperium5-utility.js new file mode 100644 index 0000000..c7e49aa --- /dev/null +++ b/modules/imperium5-utility.js @@ -0,0 +1,741 @@ +/* -------------------------------------------- */ +import { Imperium5Combat } from "./imperium5-combat.js"; +import { Imperium5Commands } from "./imperium5-commands.js"; + +/* -------------------------------------------- */ +const __level2Dice = ["d0", "d4", "d6", "d8", "d10", "d12"]; +const __name2DiceValue = { "0": 0, "d0": 0, "d4": 4, "d6": 6, "d8": 8, "d10": 10, "d12": 12 } + +/* -------------------------------------------- */ +export class Imperium5Utility { + + + /* -------------------------------------------- */ + static async init() { + Hooks.on('renderChatLog', (log, html, data) => PegasusUtility.chatListeners(html)); + Hooks.on("getCombatTrackerEntryContext", (html, options) => { + PegasusUtility.pushInitiativeOptions(html, options); + }); + Hooks.on("dropCanvasData", (canvas, data) => { + PegasusUtility.dropItemOnToken(canvas, data) + }); + + this.rollDataStore = {} + this.defenderStore = {} + this.diceList = []; + this.diceFoundryList = []; + this.optionsDiceList = ""; + this.buildDiceLists(); + PegasusCommands.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); + }) + } + + /* -------------------------------------------- */ + static pushInitiativeOptions(html, options) { + console.log('Option pushed....') + options.push({ name: "Apply -10", condition: true, icon: '', callback: target => { PegasusCombat.decInitBy10(target.data('combatant-id'), -10); } }) + } + + /* -------------------------------------------- */ + static getSpecs() { + return this.specs; + } + + /* -------------------------------------------- */ + static async ready() { + const specs = await PegasusUtility.loadCompendium("fvtt-pegasus-rpg.specialisations"); + this.specs = specs.map(i => i.toObject()); + } + + /* -------------------------------------------- */ + static async addItemDropToActor(actor, item) { + actor.preprocessItem("none", item, false) + let chatData = { + user: game.user.id, + rollMode: game.settings.get("core", "rollMode"), + whisper: [game.user.id].concat(ChatMessage.getWhisperRecipients('GM')), + content: `
The item ${item.name} has been dropped on the actor ${actor.name}= token.x && x <= (token.x + token.width) + && y >= token.y && y <= (token.y + token.height)) { + let item = await this.searchItem(data) + if (game.user.isGM || token.actor.isOwner) { + this.addItemDropToActor(token.actor, item) + } else { + game.socket.emit("system.fvtt-pegasus-rpg", { name: "msg_gm_item_drop", data: { actorId: token.actor.id, itemId: item.id, isPack: item.pack } }) + } + return + } + } + } + + /* -------------------------------------------- */ + static async loadCompendiumData(compendium) { + const pack = game.packs.get(compendium); + return await pack?.getDocuments() ?? []; + } + + /* -------------------------------------------- */ + static async loadCompendium(compendium, filter = item => true) { + let compendiumData = await PegasusUtility.loadCompendiumData(compendium); + return compendiumData.filter(filter); + } + + /* -------------------------------------------- */ + static buildDiceLists() { + let maxLevel = game.settings.get("fvtt-pegasus-rpg", "dice-max-level"); + let diceList = ["0"]; + let diceValues = [0]; + let diceFoundryList = ["d0"]; + let diceLevel = 1; + let concat = ""; + let concatFoundry = ""; + let optionsDiceList = ''; + let optionsLevel = ''; + for (let i = 1; i <= maxLevel; i++) { + let currentDices = concat + __level2Dice[diceLevel]; + diceList.push(currentDices); + diceFoundryList.push(concatFoundry + __level2Dice[diceLevel] + "x"); + if (__level2Dice[diceLevel] == "d12") { + concat = concat + "d12 "; + concatFoundry = concatFoundry + "d12x, "; + diceLevel = 1; + } else { + diceLevel++; + } + optionsDiceList += ``; + optionsLevel += ``; + } + this.diceList = diceList; + this.diceFoundryList = diceFoundryList; + this.optionsDiceList = optionsDiceList; + this.optionsLevel = optionsLevel; + + this.optionsStatusList = ''; + + } + + /* -------------------------------------------- */ + static getOptionsStatusList() { + return this.optionsStatusList; + } + /* -------------------------------------------- */ + static getOptionsDiceList() { + return this.optionsDiceList; + } + /* -------------------------------------------- */ + static getOptionsLevel() { + return this.optionsLevel; + } + + /* -------------------------------------------- */ + static computeAttackDefense(defenseRollId) { + let defenseRollData = this.getRollData(defenseRollId); + let attackRollData = this.getRollData(defenseRollData.linkedRollId); + let defender = game.actors.get(defenseRollData.actorId); + defender.processDefenseResult(defenseRollData, attackRollData); + } + + /* -------------------------------------------- */ + static applyDamage(defenseRollId) { + let defenseRollData = this.getRollData(defenseRollId); + let defender = game.actors.get(defenseRollData.actorId); + defender.applyDamageLoss(defenseRollData.finalDamage); + } + + /* -------------------------------------------- */ + static applyNoDefense(actorId, attackRollId) { + let attackRollData = this.getRollData(attackRollId); + let defender = game.actors.get(actorId); + defender.processNoDefense(attackRollData); + } + + /* -------------------------------------------- */ + static async chatListeners(html) { + + html.on("click", '.chat-create-actor', event => { + game.system.pegasus.creator.processChatEvent(event); + }); + html.on("click", '.view-item-from-chat', event => { + game.system.pegasus.creator.openItemView(event) + }); + } + + /* -------------------------------------------- */ + static async preloadHandlebarsTemplates() { + + const templatePaths = [ + 'systems/fvtt-pegasus-rpg/templates/editor-notes-gm.html', + 'systems/fvtt-pegasus-rpg/templates/partial-roll-select-effects.html', + 'systems/fvtt-pegasus-rpg/templates/partial-options-statistics.html', + 'systems/fvtt-pegasus-rpg/templates/partial-options-level.html', + 'systems/fvtt-pegasus-rpg/templates/partial-options-range.html', + 'systems/fvtt-pegasus-rpg/templates/partial-options-equipment-types.html', + 'systems/fvtt-pegasus-rpg/templates/partial-equipment-effects.html', + 'systems/fvtt-pegasus-rpg/templates/partial-actor-stat-block.html', + 'systems/fvtt-pegasus-rpg/templates/partial-actor-status.html', + 'systems/fvtt-pegasus-rpg/templates/partial-item-nav.html', + 'systems/fvtt-pegasus-rpg/templates/partial-item-description.html', + 'systems/fvtt-pegasus-rpg/templates/partial-actor-equipment.html' + ] + return loadTemplates(templatePaths); + } + + /* -------------------------------------------- */ + static async getEffectFromCompendium(effectName) { + effectName = effectName.toLowerCase() + let effect = game.items.contents.find(item => item.type == 'effect' && item.name.toLowerCase() == effectName) + if (!effect) { + let effects = await this.loadCompendium('fvtt-pegasus.effect', item => item.name.toLowerCase() == effectName) + let objs = effects.map(i => i.toObject()) + effect = objs[0] + } else { + effect = duplicate(effect); + } + + console.log("Effect", effect) + return effect + } + + /* -------------------------------------------- */ + static removeChatMessageId(messageId) { + if (messageId) { + game.messages.get(messageId)?.delete(); + } + } + + static findChatMessageId(current) { + return PegasusUtility.getChatMessageId(PegasusUtility.findChatMessage(current)); + } + + static getChatMessageId(node) { + return node?.attributes.getNamedItem('data-message-id')?.value; + } + + static findChatMessage(current) { + return PegasusUtility.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 PegasusUtility.findNodeMatching(current.parentElement, predicate); + } + return undefined; + } + + /* -------------------------------------------- */ + static templateData(it) { + return PegasusUtility.data(it)?.data ?? {} + } + + /* -------------------------------------------- */ + static data(it) { + if (it instanceof Actor || it instanceof Item || it instanceof Combatant) { + return it.data; + } + return it; + } + + /* -------------------------------------------- */ + static getDiceValue(level = 0) { + let diceString = this.diceList[level] + let diceTab = diceString.split(" ") + let diceValue = 0 + for (let dice of diceTab) { + diceValue += __name2DiceValue[dice] + } + return diceValue + } + + /* -------------------------------------------- */ + static getDiceFromLevel(level = 0) { + level = Number(level) + return this.diceList[level]; + } + /* -------------------------------------------- */ + static getFoundryDiceFromLevel(level = 0) { + level = Number(level) + //console.log(this.diceFoundryList); + return this.diceFoundryList[level]; + } + + /* -------------------------------------------- */ + 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 && game.user.targets.size == 1) { + for (let target of game.user.targets) { + return target; + } + } + return undefined; + } + + /* -------------------------------------------- */ + static getDefenseState(actorId) { + return this.defenderStore[actorId]; + } + + /* -------------------------------------------- */ + static async updateDefenseState(defenderId, rollId) { + this.defenderStore[defenderId] = rollId; + if (game.user.character && game.user.character.id == defenderId) { + let defender = game.actors.get(defenderId); + let chatData = { + user: game.user.id, + alias: defender.name, + rollMode: game.settings.get("core", "rollMode"), + whisper: [game.user.id].concat(ChatMessage.getWhisperRecipients('GM')), + content: `
${defender.name} is under attack. He must roll a skill/weapon/technique to defend himself or suffer damages (button below). + 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 loadCompendiumData(compendium) { + const pack = game.packs.get(compendium); + return await pack?.getDocuments() ?? []; + } + + /* -------------------------------------------- */ + static async loadCompendium(compendium, filter = item => true) { + let compendiumData = await this.loadCompendiumData(compendium); + //console.log("Compendium", compendiumData); + return compendiumData.filter(filter); + } + + /* -------------------------------------------- */ + 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 removeUsedPerkEffects(rollData) { + // De-actived used effects from perks + let toRem = [] + for (let effect of rollData.effectsList) { + if (effect.effect.data.perkId && effect.effect.data.isUsed) { + toRem.push(effect.effect._id) + } + } + if (toRem.length > 0) { + let actor = game.actors.get(rollData.actorId) + actor.deleteEmbeddedDocuments('Item', toRem) + } + } + + /* -------------------------------------------- */ + static async rollPegasus(rollData) { + + let dicePool = [{ name: "stat", level: 0, statmod: 0 }, { name: "spec", level: 0 }, { name: "bonus", level: 0 }, { name: "hindrance", level: 0 }, { name: "other", level: 0 }]; + if (rollData.stat) { + dicePool[0].level += Number(rollData.stat.value); + dicePool[0].statmod = Number(rollData.stat.mod); + } + if (rollData.statDicesLevel) { + dicePool[0].level = rollData.statDicesLevel; + } + if (rollData.selectedSpec && rollData.selectedSpec != "0") { + rollData.spec = rollData.specList.find(item => item._id == rollData.selectedSpec); + rollData.spec.data.dice = PegasusUtility.getDiceFromLevel(rollData.spec.data.level); + } + if (rollData.spec) { + dicePool[1].level += Number(rollData.spec.data.level); + } + if (rollData.specDicesLevel) { + dicePool[1].level = rollData.specDicesLevel; + } + if (rollData.bonusDicesLevel) { + dicePool[2].level += Number(rollData.bonusDicesLevel); + } + if (rollData.hindranceDicesLevel) { + dicePool[3].level += Number(rollData.hindranceDicesLevel); + } + if (rollData.otherDicesLevel) { + dicePool[4].level += Number(rollData.otherDicesLevel); + } + + let diceFormulaTab = []; + for (let diceGroup of dicePool) { + diceFormulaTab.push(this.getFoundryDiceFromLevel(diceGroup.level)) + } + let diceFormula = '{' + diceFormulaTab.join(', ') + '}kh'; + + // Performs roll + let myRoll = rollData.roll; + if (!myRoll) { // New rolls only of no rerolls + myRoll = new Roll(diceFormula).roll({ async: false }); + console.log("ROLL : ", diceFormula) + await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode")); + rollData.roll = myRoll + } + + // Final score and keep data + rollData.finalScore = myRoll.total + dicePool[0].statmod; + + if (rollData.damages) { + let dmgFormula = this.getFoundryDiceFromLevel(rollData.damages.value) + let dmgRoll = new Roll(dmgFormula).roll({ async: false }); + await this.showDiceSoNice(dmgRoll, game.settings.get("core", "rollMode")); + rollData.dmgResult = dmgRoll.total; + } + + this.createChatWithRollMode(rollData.alias, { + content: await renderTemplate(`systems/fvtt-pegasus-rpg/templates/chat-generic-result.html`, rollData) + }); + + // Init stuf + if (rollData.isInit) { + let combat = game.combats.get(rollData.combatId) + combat.updateEmbeddedDocuments("Combatant", [{ _id: rollData.combatantId, initiative: rollData.finalScore }]); + } + + //this.removeUsedPerkEffects( rollData) // Unused for now + + // And save the roll + this.saveRollData(rollData); + } + + /* -------------------------------------------- */ + static getDamageDice(result) { + if (result < 0) return 0; + return Math.floor(result / 5) + 1; + } + + /* ------------------------- ------------------- */ + static async updateRoll(rollData) { + + let diceResults = rollData.diceResults; + let sortedRoll = []; + for (let i = 0; i < 10; i++) { + sortedRoll[i] = 0; + } + for (let dice of diceResults) { + sortedRoll[dice.result]++; + } + let index = 0; + let bestRoll = 0; + for (let i = 0; i < 10; i++) { + if (sortedRoll[i] > bestRoll) { + bestRoll = sortedRoll[i]; + index = i; + } + } + let bestScore = (bestRoll * 10) + index + rollData.bestScore = bestScore + rollData.finalScore = bestScore + rollData.negativeModifier + rollData.positiveModifier + + this.saveRollData(rollData) + + this.createChatWithRollMode(rollData.alias, { + content: await renderTemplate(`systems/fvtt-weapons-of-the-gods/templates/chat-generic-result.html`, rollData) + }); + } + + /* ------------------------- ------------------- */ + static async rerollDice(actorId, diceIndex = -1) { + let actor = game.actors.get(actorId); + let rollData = actor.getRollData(); + + if (diceIndex == -1) { + rollData.hasWillpower = actor.decrementWillpower(); + rollData.roll = undefined; + } else { + let myRoll = new Roll("1d6").roll({ async: false }); + await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode")); + console.log("Result: ", myRoll); + + rollData.roll.dice[0].results[diceIndex].result = myRoll.total; // Patch + rollData.nbStrongHitUsed++; + } + this.rollFraggedKingdom(rollData); + } + + /* -------------------------------------------- */ + static getUsers(filter) { + return game.users.filter(filter).map(user => user.data._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-pegasus-rgp", { 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"), + bonusDicesLevel: 0, + hindranceDicesLevel: 0, + otherDicesLevel: 0, + statDicesLevel: 0, + specDicesLevel: 0, + effectsList: [], + armorsList: [], + weaponsList: [], + equipmentsList: [], + optionsDiceList: PegasusUtility.getOptionsDiceList() + } + PegasusUtility.updateWithTarget(rollData) + return rollData + } + + /* -------------------------------------------- */ + static updateWithTarget(rollData) { + let objectDefender + let target = PegasusUtility.getTarget(); + if (target) { + let defenderActor = game.actors.get(target.data.actorId) + objectDefender = PegasusUtility.data(defenderActor) + objectDefender = mergeObject(objectDefender, target.data.actorData) + rollData.defender = objectDefender + rollData.attackerId = this.id + rollData.defenderId = objectDefender._id + defenderActor.addHindrancesList(rollData.effectsList) + } + } + + /* -------------------------------------------- */ + 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..c562fa5 --- /dev/null +++ b/styles/simple.css @@ -0,0 +1,1447 @@ + /* ==================== (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-pegasus-rpg .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-pegasus-rpg .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-pegasus-rpg .sheet-header .header-fields { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.fvtt-pegasus-rpg .sheet-header h1.charname { + height: 50px; + padding: 0px; + margin: 5px 0; + border-bottom: 0; +} + +.fvtt-pegasus-rpg .sheet-header h1.charname input { + width: 100%; + height: 100%; + margin: 0; +} + +.fvtt-pegasus-rpg .sheet-tabs { + -webkit-box-flex: 0; + -ms-flex: 0; + flex: 0; +} + +.fvtt-pegasus-rpg .sheet-body, +.fvtt-pegasus-rpg .sheet-body .tab, +.fvtt-pegasus-rpg .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-pegasus-rpg .tox .tox-editor-container { + background: #fff; +} + +.fvtt-pegasus-rpg .tox .tox-edit-area { + padding: 0 8px; +} + +.fvtt-pegasus-rpg .resource-label { + font-weight: bold; + text-transform: uppercase; +} + +.fvtt-pegasus-rpg .tabs { + height: 40px; + border-top: 1px solid #AAA; + border-bottom: 1px solid #AAA; + color: #000000; +} + +.fvtt-pegasus-rpg .tabs .item { + line-height: 40px; + font-weight: bold; +} + +.fvtt-pegasus-rpg .tabs .item.active { + text-decoration: underline; + text-shadow: none; +} + +.fvtt-pegasus-rpg .items-list { + list-style: none; + margin: 1px 0; + padding: 0; + overflow-y: auto; +} + +.fvtt-pegasus-rpg .items-list .item-header { + font-weight: bold; +} + +.fvtt-pegasus-rpg .items-list .item { + height: 30px; + line-height: 24px; + padding: 1px 0; + border-bottom: 1px solid #BBB; +} + +.fvtt-pegasus-rpg .items-list .item .item-image { + -webkit-box-flex: 0; + -ms-flex: 0 0 24px; + flex: 0 0 24px; + margin-right: 5px; +} + +.fvtt-pegasus-rpg .items-list .item img { + display: block; +} + +.fvtt-pegasus-rpg .items-list .item-name { + margin: 0; +} + +.fvtt-pegasus-rpg .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; + 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, +.stat-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-stat-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; +} + +.stats-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; +} + +.stat-icon { + border: 0; + padding: 2px 2px 2px 2px; + max-width:32px; + max-height:32px; + width: auto; + height: auto; +} +.small-stat-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: 3px; + border: 2px ridge #846109; + display: inline-block; + cursor: pointer; + color: #ffffff; + padding: 0px 6px 0px 6px; + 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-abilities, +.drop-optionnal-abilities, +.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/pegasus_logo_v1.webp); + height: 160px; + width: 256px; + top: -80px; + left: calc(50% - 132px); +} + +#logo { + content : url(../images/ui/pegasus_logo_v1.webp); + 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; +} +.stat-item { + flex-grow: 1; + justify-content: flex-start; + margin: 2px; +} +.stat-block { + min-width: 160px; +} +.stat-margin { + margin-left: 4px; + margin-top: 5px; +} +.combat-margin { + margin-left: 4px; + margin-top: 3px; +} +.stat-text-white { + color: white; +} +.item-stat-roll { + max-height: 42px; + min-height: 36px; +} +.item-stat-roll select, .item-stat-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 { + flex-grow:2; + max-width: 10rem; + min-width: 10rem; +} +.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; +} \ 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 --}} +
+ + + + + +
  • + {{#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..842fdf2 --- /dev/null +++ b/system.json @@ -0,0 +1,38 @@ +{ + "author": "Uberwald", + "compatibleCoreVersion": "9", + "description": "Imperium 5 RPG system for FoundryVTT", + "download": "https://www.uberwald.me/data/files/fvtt-imperium5/fvtt-imperium5.zip", + "esmodules": [ + "modules/imperium5-main.js" + ], + "gridDistance": 5, + "gridUnits": "m", + "languages": [ + { + "lang": "en", + "name": "English", + "path": "lang/en.json" + } + ], + "library": false, + "license": "LICENSE.txt", + "manifest": "https://www.uberwald.me/data/files/fvtt-imperium5/system.json", + "manifestPlusVersion": "1.0.0", + "media": [], + "minimumCoreVersion": "0.8.0", + "name": "fvtt-imperium5", + "packs": [ + ], + "primaryTokenAttribute": "secondary.health", + "secondaryTokenAttribute": "secondary.delirium", + "socket": true, + "styles": [ + "styles/simple.css" + ], + "templateVersion": 1, + "title": "Imperium5 RPG", + "url": "https://www.uberwald.me/data/files/fvtt-imperium5", + "version": "0.0.1", + "background" : "./images/ui/imperium5_welcome_page.webp" +} diff --git a/template.json b/template.json new file mode 100644 index 0000000..2557e41 --- /dev/null +++ b/template.json @@ -0,0 +1,424 @@ +{ + "Actor": { + "types": ["character", "npc"], + "templates": { + "biodata": { + "biodata": { + "name": "", + "age": 0, + "size": "", + "weight": "", + "hair": "", + "sex": "", + "eyes": "", + "description": "", + "worstfear": "", + "worstfearactive": false, + "desires": "", + "desiresactive": false, + "preferredhand": "", + "catchphrase": "", + "catchphraseused": false, + "catchphrasetrigger": "", + "charactervalue": 0, + "level": 0, + "threatlevel": 0, + "cdp": 0, + "cdpused": 0, + "notes": "", + "gmnotes": "", + "racename": "", + "rolename": "" + } + }, + "core": { + "race": [], + "abilities": [], + "subactors": [], + "statistics": { + "agi":{ + "label": "Agility", + "abbrev": "AGI", + "level": 1, + "value": 1, + "mod": 0, + "col": 1 + }, + "mnd":{ + "label": "Mind", + "abbrev": "MND", + "level": 1, + "value": 1, + "col": 1, + "mod": 0 + }, + "soc":{ + "label": "Social", + "abbrev": "SOC", + "level": 1, + "value": 1, + "col": 1, + "mod": 0 + }, + "str":{ + "label": "Strength", + "abbrev": "STR", + "level": 1, + "value": 1, + "col": 1, + "mod": 0 + }, + "phy":{ + "label": "Physique", + "abbrev": "PHY", + "level": 1, + "value": 1, + "col": 1, + "mod": 0 + }, + "com":{ + "label": "Combat", + "abbrev": "COM", + "level": 1, + "value": 1, + "col": 2, + "mod": 0 + }, + "def":{ + "label": "Defence", + "abbrev": "DEF", + "level": 1, + "value": 1, + "col": 2, + "mod": 0 + }, + "stl":{ + "label": "Stealth", + "abbrev": "STL", + "level": 1, + "value": 1, + "col": 2, + "mod": 0 + }, + "per":{ + "label": "Perception", + "abbrev": "PER", + "level": 1, + "value": 1, + "col": 2, + "mod": 0 + }, + "foc":{ + "label": "Focus", + "abbrev": "FOC", + "level": 1, + "value": 1, + "col": 2, + "mod": 0 + } + }, + "nrg": { + "label": "NRG", + "type": "value", + "absolutemax": 0, + "value": 0, + "max": 0, + "mod": 0, + "activated": 0 + }, + "mr": { + "label": "MR (Initiative)", + "type": "dice", + "value": 0, + "mod": 0 + }, + "momentum": { + "label": "Momentum", + "type": "value", + "value": 0, + "max": 0 + }, + "secondary": { + "health": { + "label": "Health", + "value": 0, + "type": "value", + "ismax": true, + "iscombat": true, + "bonus": 0, + "max": 0 + }, + "delirium": { + "label": "Delirium", + "value": 0, + "type": "value", + "ismax": true, + "iscombat": true, + "bonus": 0, + "max": 0 + }, + "stealthhealth": { + "label": "STL Health", + "type": "value", + "value": 0, + "ismax": true, + "bonus": 0, + "max": 0 + }, + "socialhealth": { + "label": "SOC Health", + "type": "value", + "value": 0, + "ismax": true, + "bonus": 0, + "max": 0 + } + }, + "combat": { + "bonusdice": 0, + "otherdice": 0, + "hindrancedice": 0, + "stunlevel": 0, + "stunthreshold": 0 + } + }, + "npccore": { + "npctype": "", + "description": "" + } + }, + "character": { + "templates": [ "biodata", "core" ] + }, + "npc": { + "templates": [ "npccore" ] + } + }, + "Item": { + "types": [ "race", "role", "ability", "specialisation", "perk", "power" , "armor", "shield", "equipment", "weapon", "effect", "money"], + "effect": { + "type": "", + "genre": "", + "effectlevel": 0, + "reducedicevalue": false, + "stataffected": "", + "specaffected": [], + "statdice": false, + "bonusdice": false, + "otherdice": false, + "hindrance" : false, + "resistedby": "", + "recoveryroll": false, + "recoveryrollstat": "", + "recoveryrollspec": [], + "effectstatlevel": false, + "effectstat": "", + "description": "" + }, + "race": { + "description": "", + "environment": "", + "society_culture": "", + "outlook": "", + "selectablestats": false, + "statsonlyonce": false, + "numberstats": 0, + "abilities": [], + "optionnalabilities": [], + "nboptionnal": 0, + "perksgained": false, + "perksall": false, + "perksnumber": 0, + "perks": [], + "statistics": "" + }, + "role": { + "statincrease1": "", + "statincrease2": "", + "messagespecplus1": "", + "specialisationsplus1": [], + "powers1": [], + "MR": 0, + "specialperk": [], + "specincrease": [], + "perks": [], + "description": "" + }, + "ability": { + "affectedstat": "str", + "statmodifier": 1, + "statlevelincrease": 0, + "statusaffected": "", + "statusmodifier": 0, + "powersgained": [], + "specialisations": [], + "effectsgained": [], + "aoe": "", + "affectedcircumstances": "", + "affectedspecialisations": "", + "nrgcost": 0, + "opponenthindrance": 0, + "attackgained": [], + "armorgained": [], + "threatlevel": 0, + "description": "" + }, + "specialisation": { + "statistic": "", + "level": 1, + "ispowergroup": false, + "powersource": "", + "powersactivated": false, + "powers": [], + "isthreatlevel": false, + "description": "" + }, + "perk": { + "level": 1, + "active": false, + "duration": "", + "isweaver": false, + "effectsgained": [], + "features": { + "nrgcost": { + "label": "NRG cost to use", + "flag": false, + "type": "number", + "isvalid": true, + "value": 0 + }, + "range": { + "label": "Range", + "flag": false, + "type": "range", + "isvalid": true, + "value": "" + }, + "nbtargets": { + "label": "# Targets", + "flag": false, + "type": "string", + "isvalid": true, + "value": "" + }, + "bonushealth": { + "label": "Bonus to Health", + "flag": false, + "type": "string", + "isvalid": true, + "value": "" + }, + "bonusnrg": { + "label": "Bonus to NRG", + "flag": false, + "type": "string", + "isvalid": true, + "value": "" + }, + "bonusdelirium": { + "label": "Bonus to Delirium", + "flag": false, + "type": "string", + "isvalid": true, + "value": "" + } + }, + "status": "", + "nbuse": "", + "used1":false, + "used2":false, + "used3":false + }, + "power": { + "rollneeded": false, + "statistic": "", + "cost": 0, + "costspent": 0, + "range": "", + "action": "", + "type": "", + "powersource": "", + "effects": "", + "activated": false, + "purchasedeffects": "", + "effectsgained": [], + "dmgroll": false, + "dmgstatistic": "", + "description": "" + }, + "armor": { + "statistic": "", + "resistance": "", + "weight": 0, + "cost": 0, + "idr": "", + "equipped": false, + "locationprotected": "", + "effects": [], + "activated": false, + "description":"" + }, + "shield": { + "deftype": "", + "level": "", + "weight": 0, + "cost": 0, + "idr": "", + "equipped": false, + "effects": [], + "activated": false, + "description":"" + }, + "equipment": { + "type": "", + "cost": 0, + "weight": 0, + "idr": "", + "quantity": 0, + "equipped": false, + "stataffected": "", + "level": 0, + "statdice": false, + "bonusdice": false, + "otherdice": false, + "effects": [], + "activated": false, + "iscontainer": false, + "containercapacity": 0, + "containerid": "", + "threatlevel": 0, + "description":"" + }, + "money" : { + "value": 0, + "quantity": 0, + "weight": 0, + "idr":"", + "description": "" + }, + "weapon": { + "statistic": "", + "damagestatistic": "", + "damage": "", + "cost": 0, + "weight": 0, + "idr": "", + "range": "", + "maxrange": "", + "ao": "", + "enhanced": false, + "enhancedstat": "", + "enhancedlevel": 0, + "damagetype": "", + "damagetypelevel": 0, + "vehicledamagetype": "", + "vehicledamagetypelevel": 0, + "ammocurrent": 0, + "ammomax": 0, + "equipped": false, + "effects": [], + "activated": false, + "description": "" + } + } +} diff --git a/templates/actor-sheet.html b/templates/actor-sheet.html new file mode 100644 index 0000000..efa57a7 --- /dev/null +++ b/templates/actor-sheet.html @@ -0,0 +1,879 @@ +
    + + {{!-- Sheet Header --}} +
    +
    +

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

      Dice Pool

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

      MR

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

    Stun

    +
    +
      +
    • + Current + + Threshold + +
    • +
    +
    +
    + +
    +
      +
    • + +

      +
      + + + + + + + + + + + + + + + + + + + + + + + + +
    • + {{#each perks as |perk key|}} +
    • + + {{perk.name}} + + {{perk.data.level}} + + {{#if perk.data.features.range.flag}} + {{perk.data.features.range.value}} + {{else}} +  -  + + {{/if}} + {{#if perk.data.features.nbtargets.flag}} + {{perk.data.features.nbtargets.value}} + {{else}} +  -  + {{/if}} + + + + + + {{#if (ne perk.data.status "ready")}} + {{#if (eq perk.data.nbuse "next1action")}} + +   +   + {{/if}} + {{#if (eq perk.data.nbuse "next2action")}} + + +   + {{/if}} + {{#if (eq perk.data.nbuse "next3action")}} + + + + {{/if}} + {{else}} + + + + {{/if}} +
       
      +
      + +
      +
    • + {{/each}} +
    +
    + +
      +
    • + +

      +
      + + + + + + + + + + + + +
    • + + {{#each effects as |effect key|}} +
    • + + {{effect.name}} + {{effect.data.effectlevel}} + {{upperFirst effect.data.type}} + {{upperFirst effect.data.genre}} + {{upper effect.data.stataffected}} +
       
      +
      + +
      +
    • + {{/each}} +
    +
    +
    + + {{!-- Other Tab --}} +
    + +
      +
    • + +

      +
      + + + + + + + + + +
    • + {{#each specs as |spec key|}} +
    • + + {{spec.name}} + {{upper spec.data.statistic}} + {{spec.data.dice}} + {{#if spec.data.ispowergroup}} + {{#if spec.data.powersactivated}} + Deactivate + {{else}} + Activate + {{/if}} + {{else}} +  -  + {{/if}} +
       
      +
      + +
      +
    • + {{/each}} +
    +
    + +
    + +
    + + {{!-- Powers Tab --}} +
    + +
    + +
      +
    • +

      {{data.nrg.label}}

      + Activated +  Current +  Mod +  Max + / {{data.nrg.absolutemax}} +
    • +
    + + + +
      +
    • + +

      +
      + + + + + + + + + + + + +
    • + {{#each abilities as |ability key|}} +
    • + + {{ability.name}} + + {{upper ability.data.affectedstat}} + {{ability.data.statmodifier}} + {{upperFirst ability.data.statusaffected}} + {{ability.data.statusmodifier}} + +
       
      +
      + +
      +
    • + {{/each}} +
    + +
    +
    + + {{!-- Equipement Tab --}} +
    + +
    +

    Encumbrance

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

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

      +
      + + + + + + + + + + + + + + + + + + + + + +
    • + {{#each weapons as |weapon key|}} +
    • + + {{weapon.name}} + + + + + {{#if (gt weapon.data.ammomax 0)}} + + {{else}} + + + {{/if}} + + + {{#if (count weapon.data.effects)}} + {{#if weapon.data.activated}} + Deactivate + {{else}} + Activate + {{/if}} + {{else}} +  -  + {{/if}} + + + + + {{#if weapon.data.idrDice}} + {{weapon.data.idrDice}} + {{else}} +  -  + {{/if}} + + +
       
      + +
    • + {{/each}} +
    + +
      +
    • + +

      +
      + + + + + + + + + + + + + + + + + + +
    • + {{#each armors as |armor key|}} +
    • + + {{armor.name}} + {{upper armor.data.statistic}} + {{armor.data.resistanceDice}} + {{armor.data.locationprotected}} + + + {{#if (count armor.data.effects)}} + {{#if armor.data.activated}} + Deactivate + {{else}} + Activate + {{/if}} + {{else}} +  -  + {{/if}} + + + {{armor.data.weight}} + + {{#if armor.data.idrDice}} + {{armor.data.idrDice}} + {{else}} +  -  + {{/if}} + + +
       
      + +
    • + {{/each}} +
    + +
      +
    • + +

      +
      + + + + + + + + + + + + +
    • + {{#each shields as |shield key|}} +
    • + + {{shield.name}} + {{shield.data.levelDice}} + + + {{#if (count shield.data.effects)}} + {{#if shield.data.activated}} + Deactivate + {{else}} + Activate + {{/if}} + {{else}} +  -  + {{/if}} + + + {{shield.data.weight}} + + {{#if shield.data.idrDice}} + {{shield.data.idrDice}} + {{else}} +  -  + {{/if}} + +
       
      + +
    • + {{/each}} +
    + +
      +
    • + +

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

    Psychology :

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

    Catchphrase :

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

    Development :

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

    Background :

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

    Notes :

    +
    + {{editor content=data.biodata.notes target="data.biodata.notes" button=true owner=owner editable=editable}} +
    +
    + +
    + + + \ No newline at end of file diff --git a/templates/chat-create-actor.html b/templates/chat-create-actor.html new file mode 100644 index 0000000..3b2a9af --- /dev/null +++ b/templates/chat-create-actor.html @@ -0,0 +1,143 @@ +
    + {{alias}} +

    {{name}}

    +
    + +
    +
    + + {{#if (eq step "select-race")}} +
    Select a race from the list below
    + + {{#each races as |race index|}} + + + + + {{/each}} +
    {{race.name}}Select it !
    + {{/if}} + + {{#if (eq step "select-race-optionnal")}} +
    The selected Race has some optionnal abilities. Select one ability by clicking the relevant button below
    + Remaining abilities to select : {{nboptionnal}} +
    + + {{#each optionnalabilities as |ability index|}} + + + + + {{/each}} +
    {{ability.name}}Select it !
    + {{/if}} + + {{#if (eq step "select-race-stats")}} +
    Select Stats to gain +1 modifier :
    + Remaining stats to select : {{numberstats}} +
    + + {{#each stats as |stat key|}} + {{#if @root.statsonlyonce}} + {{#if stat.used}} + {{else}} + + + + + {{/if}} + {{else}} + + + + + {{/if}} + {{/each}} +
    {{stat.label}} ({{stat.abbrev}})Select it !
    {{stat.label}} ({{stat.abbrev}})Select it !
    + {{/if}} + + {{#if (eq step "select-race-perks")}} +
    Now select {{nbraceperks}} Perk(s) for your character +
    + + {{#each raceperks as |perk index|}} + + + + + {{/each}} +
    {{perk.name}}Select it !
    + {{/if}} + + {{#if (eq step "select-role")}} +
    Now select a Role for your character. +
    + + {{#each roles as |role index|}} + + + + + {{/each}} +
    {{role.name}}Select it !
    + {{/if}} + + {{#if (eq step "select-role-start-spec")}} +
    Choose 1 Specialisation at +1DT : +
    + + {{#each rolestartspec as |spec index|}} + + + + + {{/each}} +
    {{spec.name}}Select it !
    + {{/if}} + + {{#if (eq step "select-role-stat")}} +
    Choose 1 Stat at +1DT : +
    + + {{#each rolestats as |rolestat key|}} + + + + + {{/each}} +
    {{rolestat.label}}Select it !
    + {{/if}} + + {{#if (eq step "select-role-spec")}} +
    Now select a specialisation at +{{dt}}DT. +
    + + {{#each rolespec as |spec index|}} + + + + + {{/each}} +
    {{spec.name}}Select it !
    + {{/if}} + + {{#if (eq step "select-role-perk")}} +
    Now select a Perk. +
    + + {{#each roleperks as |perk index|}} + + + + + {{/each}} +
    {{perk.name}}Select it !
    + {{/if}} + + {{#if (eq step "character-end")}} +
    Follow the next steps from the rulebook page 50 !. You can now spend 150 CDPs to customise your character. +
    + {{/if}} + + +
    diff --git a/templates/chat-effect-used.html b/templates/chat-effect-used.html new file mode 100644 index 0000000..f7c166e --- /dev/null +++ b/templates/chat-effect-used.html @@ -0,0 +1,7 @@ +
    +

    {{name}}

    + {{#if img}} + + {{/if}} +
    Effect {{name}} has ben used and removed from the available effects.
    +
    diff --git a/templates/chat-generic-result.html b/templates/chat-generic-result.html new file mode 100644 index 0000000..658ae08 --- /dev/null +++ b/templates/chat-generic-result.html @@ -0,0 +1,54 @@ +
    + {{#if actorImg}} + {{alias}} + {{/if}} +

    {{alias}}

    +
    + +
    + + {{#if img}} +
    + {{name}} +
    + {{/if}} + +
    +
    + +
    +
      + {{#if power}} +
    • Power : {{power.name}}
    • + {{/if}} + {{#if isDamage}} +
    • Weapon Damage Dice : {{weapon.data.damageDice}}
    • + {{/if}} + {{#if isResistance}} +
    • Armor Resistance Dice : {{armor.data.resistanceDice}}
    • + {{/if}} + {{#if stat}} +
    • Statistic : {{stat.label}}
    • + {{/if}} + {{#if spec}} +
    • Specialisation : {{spec.name}}
    • + {{/if}} + + {{#if weaponName}} +
    • Weapon : {{weaponName}}
    • + {{/if}} + + {{#if isResistance}} +
    • Defense Result : {{finalScore}} + {{else}} + {{#if isDamage}} +
    • Damages : {{finalScore}} + {{else}} +
    • Final Result : {{finalScore}} + {{/if}} + {{/if}} + +
    +
    + +
    diff --git a/templates/chat-opposed-damage.html b/templates/chat-opposed-damage.html new file mode 100644 index 0000000..e6f27a6 --- /dev/null +++ b/templates/chat-opposed-damage.html @@ -0,0 +1,15 @@ +
    + {{alias}} +

    {{defenderName}}

    +
    + +
    + +
    +
      +
    • {{defenderName}} suffer damages from {{attackerName}} !
    • +
    • Damages (inc. armor+weapon) : {{finalDamage}}
    • +
    • +
    • +
    +
    diff --git a/templates/chat-opposed-fail.html b/templates/chat-opposed-fail.html new file mode 100644 index 0000000..4e71d14 --- /dev/null +++ b/templates/chat-opposed-fail.html @@ -0,0 +1,11 @@ +
    + {{alias}} +

    {{defenderName}}

    +
    + +
    +
    + {{defenderName}} wins the opposition against {{attackerName}} ! +
    + +
    diff --git a/templates/chat-perk-ready.html b/templates/chat-perk-ready.html new file mode 100644 index 0000000..8925b2a --- /dev/null +++ b/templates/chat-perk-ready.html @@ -0,0 +1,7 @@ +
    +

    {{name}}

    + {{#if img}} + + {{/if}} +
    {{name}} has Just Deactivated the Perk: {{perk.name}}, make sure to manually delete all Effects provided by this Perk from Targets.
    +
    diff --git a/templates/editor-notes-gm.html b/templates/editor-notes-gm.html new file mode 100644 index 0000000..f3b3218 --- /dev/null +++ b/templates/editor-notes-gm.html @@ -0,0 +1,6 @@ +{{#if data.isGM}} +

    GM Notes :

    +
    + {{editor content=data.gmnotes target="data.gmnotes" button=true owner=owner editable=editable}} +
    +{{/if}} diff --git a/templates/item-ability-sheet.html b/templates/item-ability-sheet.html new file mode 100644 index 0000000..ef9da6c --- /dev/null +++ b/templates/item-ability-sheet.html @@ -0,0 +1,152 @@ +
    +
    + +
    +

    +
    +
    + + {{> systems/fvtt-pegasus-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-pegasus-rpg/templates/partial-item-description.html}} + +
    +
      +
    • + +
    • +
    • + +
    • + +
    • + +
    • +
    • + +
    • + +
    • +
    • +
    • +
        +
      • +
      • + {{#each data.effectsgained as |effect idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      +
    • + +
    • +
    • +
    • +
        +
      • +
      • + {{#each data.powersgained as |power idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      +
    • + +
    • +
    • +
        +
      • +
      • + {{#each data.specialisations as |spec idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      +
    • + + + + +
    • + +
    • + +
    • +
    • +
        +
      • +
      • + {{#each data.attackgained as |weapon idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      +
    • + + +
    • +
    • +
        +
      • +
      • + {{#each data.armorgained as |armor idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      +
    • + + +
    +
    + +
    +
    diff --git a/templates/item-armor-sheet.html b/templates/item-armor-sheet.html new file mode 100644 index 0000000..b6fd3c2 --- /dev/null +++ b/templates/item-armor-sheet.html @@ -0,0 +1,63 @@ +
    +
    + +
    +

    +
    +
    + + {{> systems/fvtt-pegasus-rpg/templates/partial-item-nav.html}} + + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-pegasus-rpg/templates/partial-item-description.html}} + +
    + +
    +
      +
    • + +
    • + +
    • + +
    • +
    • + +
    • + + {{> systems/fvtt-pegasus-rpg/templates/partial-equipment-effects.html}} + +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    +
    + +
    +
    diff --git a/templates/item-effect-sheet.html b/templates/item-effect-sheet.html new file mode 100644 index 0000000..48fe87f --- /dev/null +++ b/templates/item-effect-sheet.html @@ -0,0 +1,149 @@ +
    +
    + +
    +

    + +

    +
    +
    + {{> systems/fvtt-pegasus-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-pegasus-rpg/templates/partial-item-description.html}} + +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + {{#if data.effectstatlevel}} +
    • + +
    • + {{else}} +
    • + +
    • + {{/if}} + +
    • +
    • +
        +
      • +
      • + {{#each data.specaffected as |spec idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      +
    • + +
    • + +
    • + + {{#if (eq data.genre "positive")}} + +
    • +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + {{else}} + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + {{#if data.recoveryroll}} +
    • + +
    • +
    • +
        +
      • + +
      • +
      • +
      • + {{#each data.recoveryrollspec as |spec idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      +
    • + {{/if}} + + {{/if}} + +
    +
    + +
    +
    diff --git a/templates/item-equipment-sheet.html b/templates/item-equipment-sheet.html new file mode 100644 index 0000000..0d66d2d --- /dev/null +++ b/templates/item-equipment-sheet.html @@ -0,0 +1,86 @@ +
    +
    + +
    +

    +
    +
    + + {{> systems/fvtt-pegasus-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-pegasus-rpg/templates/partial-item-description.html}} + +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + {{> systems/fvtt-pegasus-rpg/templates/partial-equipment-effects.html}} + +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • + {{#if data.iscontainer}} +
    • + +
    • + {{/if}} +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    + +
    +
    diff --git a/templates/item-money-sheet.html b/templates/item-money-sheet.html new file mode 100644 index 0000000..da8430c --- /dev/null +++ b/templates/item-money-sheet.html @@ -0,0 +1,33 @@ +
    +
    + +
    +

    +
    +
    + {{> systems/fvtt-pegasus-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + {{> systems/fvtt-pegasus-rpg/templates/partial-item-description.html}} + +
    +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    + +
    +
    diff --git a/templates/item-perk-sheet.html b/templates/item-perk-sheet.html new file mode 100644 index 0000000..d353c99 --- /dev/null +++ b/templates/item-perk-sheet.html @@ -0,0 +1,112 @@ +
    +
    + +
    +

    +
    +
    + {{> systems/fvtt-pegasus-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + {{> systems/fvtt-pegasus-rpg/templates/partial-item-description.html}} + +
    +
      +
    • + +
    • + +
    • + +
    • +
    • + +
    • +
    • + +
    • + +
    • +
    • +
    • +
        +
      • +
      • + {{#each data.effectsgained as |effect idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      +
    • + + {{#each data.features as |feature key|}} + {{#if feature.isvalid}} +
    • + + +
    • + {{#if feature.flag}} +
        +
      • + {{#if (eq feature.type "statdice")}} + + {{/if}} + {{#if (eq feature.type "range")}} + + {{/if}} + {{#if (eq feature.type "number")}} + + {{/if}} + {{#if (eq feature.type "string")}} + + {{/if}} + {{#if (eq feature.type "dropspec")}} +
          +
        • +
        • + +
        + {{/if}} + {{#if (eq feature.type "text")}} +
        + {{editor content=features.value target="data.features.{{key}}.value" button=true owner=owner editable=editable}} +
        + {{/if}} +
      • +
      + {{/if}} + {{/if}} + {{/each}} +
    + +
    + +
    +
    diff --git a/templates/item-power-sheet.html b/templates/item-power-sheet.html new file mode 100644 index 0000000..abb84fb --- /dev/null +++ b/templates/item-power-sheet.html @@ -0,0 +1,136 @@ +
    +
    + +
    +

    +
    +
    + + {{> systems/fvtt-pegasus-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + +
    +
    + + {{editor content=data.description target="data.description" button=true owner=owner editable=editable}} +
    +
    + + {{editor content=data.effects target="data.effects" button=true owner=owner editable=editable}} +
    +
    + + {{editor content=data.purchasedeffects target="data.purchasedeffects" button=true owner=owner + editable=editable}} +
    +
    + +
    +
      +
    • + +
    • + {{#if data.rollneeded}} +
    • + +
    • + {{/if}} + +
    • + + +
    • + +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • + +
    • + + +
    • + {{#if data.dmgroll}} +
    • + +
    • + {{/if}} + +
    • +
    • +
    • +
        +
      • +
        +
      • + {{#each data.effectsgained as |effect idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      +
    • + +
    +
    + +
    +
    \ No newline at end of file diff --git a/templates/item-race-sheet.html b/templates/item-race-sheet.html new file mode 100644 index 0000000..ec9bb16 --- /dev/null +++ b/templates/item-race-sheet.html @@ -0,0 +1,134 @@ +
    +
    + +
    +

    +
    +
    + {{> systems/fvtt-pegasus-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + +
    + + +
    + {{editor content=data.description target="data.description" button=true owner=owner editable=editable}} +
    + +
    + {{editor content=data.environment target="data.environment" button=true owner=owner editable=editable}} +
    + +
    + {{editor content=data.society_culture target="data.society_culture" button=true owner=owner editable=editable}} +
    + +
    + {{editor content=data.outlook target="data.outlook" button=true owner=owner editable=editable}} +
    +
    + +
    +
      +
    • +
    • +
        +
      • +
        +
      • + {{#each data.abilities as |ability idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      +
    + +
      +
    • + +
    • + {{#if data.selectablestats}} +
    • + +
    • +
    • + +
    • + {{/if}} +
    + +
      +
    • + +
    • + +
    • +
    • +
        +
      • +
        +
      • + {{#each data.optionnalabilities as |ability idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      +
    + +
      +
    • + +
    • + {{#if data.perksgained}} +
    • + +
    • +
    • + +
    • + {{#if data.perksall}} + {{else}} +
        +
      • +
        +
      • + {{#each data.perks as |perk idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      + {{/if}} + {{/if}} +
    + +
    + +
    +
    \ No newline at end of file diff --git a/templates/item-role-sheet.html b/templates/item-role-sheet.html new file mode 100644 index 0000000..b0aa6eb --- /dev/null +++ b/templates/item-role-sheet.html @@ -0,0 +1,114 @@ +
    +
    + +
    +

    + +

    +
    +
    + {{> systems/fvtt-pegasus-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-pegasus-rpg/templates/partial-item-description.html}} + +
    +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
        +
      • + +
      • +
      • + +
      • +
      • +
      • + {{#each data.specialisationsplus1 as |spec idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      +
    • + +
    • +
        +
      • +
      • + {{#each data.specialperk as |perk idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      + +
    • +
    • +
        +
      • + {{#each data.statincreasechoice as |stat idx|}} + + + {{/each}} +
      • +
      +
    • +
    • +
        +
      • +
      • + {{#each data.specincrease as |spec idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      + +
    • +
    • +
        +
      • +
      • + {{#each data.perks as |perk idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      + +
    +
    + +
    +
    diff --git a/templates/item-shield-sheet.html b/templates/item-shield-sheet.html new file mode 100644 index 0000000..5614e11 --- /dev/null +++ b/templates/item-shield-sheet.html @@ -0,0 +1,55 @@ +
    +
    + +
    +

    +
    +
    + {{> systems/fvtt-pegasus-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-pegasus-rpg/templates/partial-item-description.html}} + +
    +
      +
    • + +
    • +
    • + +
    • + + {{> systems/fvtt-pegasus-rpg/templates/partial-equipment-effects.html}} + +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    + +
    +
    diff --git a/templates/item-specialisation-sheet.html b/templates/item-specialisation-sheet.html new file mode 100644 index 0000000..c5c7c1c --- /dev/null +++ b/templates/item-specialisation-sheet.html @@ -0,0 +1,62 @@ +
    +
    + +
    +

    +
    +
    + + {{> systems/fvtt-pegasus-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-pegasus-rpg/templates/partial-item-description.html}} + +
    +
      +
    • + +
    • + +
    • + +
    • +
    • + +
    • + {{#if data.ispowergroup}} +
    • + +
    • +
    • +
    • +
        +
      • +
      • + {{#each data.powers as |power idx|}} +
      • + +
        + +
        +
      • + {{/each}} +
      + + {{/if}} +
    • + +
    • +
    +
    +
    +
    diff --git a/templates/item-weapon-sheet.html b/templates/item-weapon-sheet.html new file mode 100644 index 0000000..3341e46 --- /dev/null +++ b/templates/item-weapon-sheet.html @@ -0,0 +1,145 @@ +
    +
    + +
    +

    +
    +
    + {{> systems/fvtt-pegasus-rpg/templates/partial-item-nav.html}} + + {{!-- Sheet Body --}} +
    + + {{> systems/fvtt-pegasus-rpg/templates/partial-item-description.html}} + +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • +
    • + +
    • +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + + {{> systems/fvtt-pegasus-rpg/templates/partial-equipment-effects.html}} + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + {{#if data.enhanced}} +
    • + +
    • +
    • + +
    • + {{/if}} + +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    + +
    +
    diff --git a/templates/npc-sheet.html b/templates/npc-sheet.html new file mode 100644 index 0000000..6bfa5f1 --- /dev/null +++ b/templates/npc-sheet.html @@ -0,0 +1,184 @@ +
    + + {{!-- Sheet Header --}} +
    +
    +
    + +

    +
    +
    +
    + + {{!-- Sheet Tab Navigation --}} + + + {{!-- Sheet Body --}} +
    + + {{!-- Carac Tab --}} +
    + Unlocked/Locked{{#if editScore}}Unlocked{{else}}Locked{{/if}} + +
    +
    + +

    Type

    +
    + + + +
    +

    Traits List

    +
      + {{#each traits as |trait key|}} +
    • + + {{trait.name}} + {{trait.data.data.type}} +
      + + +
      +
    • + {{/each}} +
    +
    + +
    + +
    +

    Stats & Numbers

    +
      + {{#each data.spec as |spec key|}} +
    • + {{spec.label}} + +
    • + {{/each}} +
    +
    + +
    +
    + + + {{!-- Defence Tab --}} +
    +
    + +
    + {{#each data.fight as |fight key|}} +
      +
    • + {{fight.label}} + +
    • + {{#each fight.derivated as |derivated keydev|}} +
    • + {{derivated.label}} + +
    • + {{/each}} +
    + {{/each}} +
    + +

    Weapons

    + + +
    +
    + + {{!-- Traits Tab --}} +
    + + +
    + + {{!-- Features Tab --}} +
    +
    + +

    Equipment

    + + +
    +
    + + {{!-- Notes Tab --}} +
    +
    +

    Description :

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

    Notes :

    +
    + {{editor content=data.notes target="data.notes" button=true owner=owner editable=editable}} +
    +
    + {{>"systems/fvtt-fragged-kingdom/templates/editor-notes-gm.html"}} +
    +
    + +
    +
    + diff --git a/templates/partial-actor-equipment.html b/templates/partial-actor-equipment.html new file mode 100644 index 0000000..01158d3 --- /dev/null +++ b/templates/partial-actor-equipment.html @@ -0,0 +1,49 @@ +
  • + + {{#if (eq level 1)}} + {{equip.name}} + {{else}} + {{equip.name}} + {{/if}} + + + + + + {{#if (count equip.data.effects)}} + {{#if equip.data.activated}} + Deactivate + {{else}} + Activate + {{/if}} + {{else}} +  -  + {{/if}} + + + {{#if equip.data.iscontainer}} + {{equip.data.contentsEnc}} + {{else}} + {{mul equip.data.weight equip.data.quantity}} + {{/if}} + + + {{#if equip.data.idrDice}} + {{equip.data.idrDice}} + {{else}} +  -  + {{/if}} + + +
     
    +
    + {{#if (eq level 1)}} + {{#if equip.data.equipped}}{{else}}{{/if}} + {{/if}} + +
    +
  • diff --git a/templates/partial-actor-stat-block.html b/templates/partial-actor-stat-block.html new file mode 100644 index 0000000..d2cdb82 --- /dev/null +++ b/templates/partial-actor-stat-block.html @@ -0,0 +1,16 @@ +
  • + + + + +

    {{stat.abbrev}}

    +
    + + +
  • \ No newline at end of file diff --git a/templates/partial-actor-status.html b/templates/partial-actor-status.html new file mode 100644 index 0000000..7fd3734 --- /dev/null +++ b/templates/partial-actor-status.html @@ -0,0 +1,70 @@ +
      +
    • + + + + +
    • + {{#each data.secondary as |stat2 key|}} +
    • + + + + + + +
    • + {{/each}} +
    • + + + + + + + /{{data.nrg.absolutemax}} +
    • +
    • + + + + + + + +
    • +
    • + + + + + + + + + - + +
    • + + + +
    + + \ No newline at end of file diff --git a/templates/partial-equipment-effects.html b/templates/partial-equipment-effects.html new file mode 100644 index 0000000..1736d89 --- /dev/null +++ b/templates/partial-equipment-effects.html @@ -0,0 +1,16 @@ +
  • +
  • +
  • +
      +
    • +
    • + {{#each data.effects as |effect idx|}} +
    • + +
      + +
      +
    • + {{/each}} +
    +
  • diff --git a/templates/partial-item-description.html b/templates/partial-item-description.html new file mode 100644 index 0000000..a78040b --- /dev/null +++ b/templates/partial-item-description.html @@ -0,0 +1,8 @@ +
    +
    + +
    + {{editor content=data.description target="data.description" button=true owner=owner editable=editable}} +
    +
    +
    diff --git a/templates/partial-item-nav.html b/templates/partial-item-nav.html new file mode 100644 index 0000000..95b52cd --- /dev/null +++ b/templates/partial-item-nav.html @@ -0,0 +1,5 @@ +{{!-- Sheet Tab Navigation --}} + diff --git a/templates/partial-options-equipment-types.html b/templates/partial-options-equipment-types.html new file mode 100644 index 0000000..ab60653 --- /dev/null +++ b/templates/partial-options-equipment-types.html @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/templates/partial-options-level.html b/templates/partial-options-level.html new file mode 100644 index 0000000..0fb86c0 --- /dev/null +++ b/templates/partial-options-level.html @@ -0,0 +1,14 @@ +{{#if notapplicable}} + +{{/if}} + + + + + + + + + + + diff --git a/templates/partial-options-range.html b/templates/partial-options-range.html new file mode 100644 index 0000000..e08a1e5 --- /dev/null +++ b/templates/partial-options-range.html @@ -0,0 +1,15 @@ +{{#if notapplicable}} + +{{/if}} + + + + + + + + + + + + diff --git a/templates/partial-options-statistics.html b/templates/partial-options-statistics.html new file mode 100644 index 0000000..a187abf --- /dev/null +++ b/templates/partial-options-statistics.html @@ -0,0 +1,22 @@ +{{#if notapplicable}} + +{{/if}} +{{#if all}} + +{{/if}} +{{#if all}} + +{{/if}} + + + + + + + + + + +{{#if mr}} + +{{/if}} diff --git a/templates/partial-roll-select-effects.html b/templates/partial-roll-select-effects.html new file mode 100644 index 0000000..b324929 --- /dev/null +++ b/templates/partial-roll-select-effects.html @@ -0,0 +1,102 @@ + +{{#if (notEmpty effectsList)}} + +
      + {{#each effectsList as |effect idx|}} + + {{#if effect.effect.data.hindrance}} +
    • + + +
    • + {{else}} + {{#if (eq type "hindrance")}} +
    • + + +
    • + {{else}} + {{#if (count effect.effect.data.specaffected)}} + {{#each effect.effect.data.specaffected as |spec idx|}} + {{#if (eq @root.specName spec.name)}} +
    • + + {{#if effect.effect}} + + {{else}} + + {{/if}} +
    • + {{/if}} + {{/each}} + {{else}} + {{#if (eq @root.statKey effect.effect.data.stataffected)}} +
    • + + {{#if effect.effect}} + + {{else}} + + {{/if}} +
    • + {{else}} + {{#if (eq effect.effect.data.stataffected "all")}} +
    • + + {{#if effect.effect}} + + {{else}} + + {{/if}} +
    • + {{/if}} + {{/if}} + {{/if}} + {{/if}} + {{/if}} + + {{/each}} +
    +{{/if}} + +{{#if (notEmpty armorsList)}} + +
      + + {{#each armorsList as |armor idx|}} +
    • + + +
    • + {{/each}} + +
    +{{/if}} + +{{#if (notEmpty weaponsList)}} + +
      + + {{#each weaponsList as |weapon idx|}} +
    • + + +
    • + {{/each}} + +
    +{{/if}} + +{{#if (notEmpty equipmentsList)}} + +
      + + {{#each equipmentsList as |equip idx|}} +
    • + + +
    • + {{/each}} + +
    +{{/if}} diff --git a/templates/post-item.html b/templates/post-item.html new file mode 100644 index 0000000..1bad9c8 --- /dev/null +++ b/templates/post-item.html @@ -0,0 +1,8 @@ +
    +

    {{name}}

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

    Description :

    +

    {{{data.description}}}

    +
    diff --git a/templates/roll-dialog-generic.html b/templates/roll-dialog-generic.html new file mode 100644 index 0000000..75c582d --- /dev/null +++ b/templates/roll-dialog-generic.html @@ -0,0 +1,88 @@ +
    +
    + {{#if img}} + + {{/if}} +

    {{title}}

    +
    + +
    + +
    + +
    + Stat Dice : + +  + {{statMod}} +
    + + {{#if specList}} +
    + Spec : + +   +
    + {{/if}} + +
    + Spec Dice : + +   +
    + +
    + Bonus Dice : + +   +
    + +
    + Hindrance Dice : + +   +
    + +
    + Other Dice : + +   +
    + +
    + +
    + {{> systems/fvtt-pegasus-rpg/templates/partial-roll-select-effects.html}} +
    + +
    + +