Merge pull request 'v13.0.9 - Le combat d'Illysis' (#772) from VincentVk/foundryvtt-reve-de-dragon:v13 into v13

Reviewed-on: public/foundryvtt-reve-de-dragon#772
This commit is contained in:
2025-09-28 17:39:55 +02:00
58 changed files with 1364 additions and 482 deletions

126
assets/ui/chance.svg Normal file
View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="80.64064mm"
height="87.145065mm"
viewBox="0 0 80.64064 87.145065"
version="1.1"
id="svg857"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
sodipodi:docname="chance.svg">
<defs
id="defs851">
<linearGradient
inkscape:collect="always"
id="linearGradient2797">
<stop
style="stop-color:#800000;stop-opacity:1;"
offset="0"
id="stop2793" />
<stop
style="stop-color:#800000;stop-opacity:0;"
offset="1"
id="stop2795" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient845"
osb:paint="gradient">
<stop
style="stop-color:#800000;stop-opacity:1;"
offset="0"
id="stop841" />
<stop
style="stop-color:#800000;stop-opacity:0;"
offset="1"
id="stop843" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2797"
id="radialGradient2799"
cx="150.29712"
cy="162.02298"
fx="150.29712"
fy="162.02298"
r="148.85666"
gradientTransform="matrix(1,0,0,1.0817371,0,-13.243291)"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8510546"
inkscape:cx="440.18041"
inkscape:cy="219.34219"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:window-width="3840"
inkscape:window-height="2054"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1" />
<metadata
id="metadata854">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-65.513013,-104.59413)">
<g
id="g7-6"
transform="matrix(0.26458333,0,0,0.26458333,68.622737,106.83336)"
style="opacity:1;mix-blend-mode:normal;fill:#000000;fill-opacity:0.604288;stroke:none;stroke-width:10.56;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.4;image-rendering:auto">
<title
id="title4-3">Layer 1</title>
<path
fill="#ffffff"
fill-rule="evenodd"
stroke="#000000"
stroke-width="8"
id="path2-1"
d="m 206.5,1.5 q 8.5,0 15,3 6.5,3 12.25,8.25 5.75,5.25 12.5,15.5 Q 253,38.5 259.5,43 266,47.5 273.75,52.25 281.5,57 285.5,63 q 4,6 5.75,11.5 1.75,5.5 0,18 -1.75,12.5 -5.75,20 -4,7.5 -12.75,14.25 -8.75,6.75 -17.25,10.75 -8.5,4 -15,6 -6.5,2 -28.75,4.25 -22.25,2.25 -23,3 l -0.75,0.75 0.5,1.25 q 0.5,1.25 32,0.5 31.5,-0.75 43.5,2.75 12,3.5 18.25,8.25 6.25,4.75 9.75,10.25 3.5,5.5 5.5,10.5 2,5 0.5,19.5 -1.5,14.5 -4.75,19.75 -3.25,5.25 -3.75,6.5 l -0.5,1.25 -4.25,2.5 q -4.25,2.5 -10.25,10.5 -6,8 -10,16.5 -4,8.5 -6.75,12.25 -2.75,3.75 -5.25,5.5 -2.5,1.75 -4,4 -1.5,2.25 -9.5,6.25 -8,4 -18.5,2.5 Q 210,290.5 202.75,285.25 195.5,280 190.5,275 q -5,-5 -10.25,-12.25 -5.25,-7.25 -7,-10.5 Q 171.5,249 169,237 q -2.5,-12 -5.5,-21 l -3,-9 -1.5,6.5 q -1.5,6.5 0,28.5 1.5,22 6.5,35 5,13 9.25,18.25 4.25,5.25 10,10.5 5.75,5.25 4.75,10.75 -1,5.5 -1.25,5.75 L 188,322.5 181,321 q -7,-1.5 -13.75,-7.25 Q 160.5,308 156,301.5 q -4.5,-6.5 -8.5,-17 -4,-10.5 -5.5,-20.5 -1.5,-10 -0.25,-36.25 l 1.25,-26.25 -3.25,0.75 q -3.25,0.75 -4.75,5.25 -1.5,4.5 -5.5,29.5 -4,25 -8,32.5 -4,7.5 -7.25,10.75 -3.25,3.25 -8.25,6.75 -5,3.5 -11,5.5 -6,2 -19,0.5 Q 63,291.5 57,288 51,284.5 43.25,274.75 35.5,265 31.75,257 L 28,249 26.5,247.75 Q 25,246.5 20.75,242.75 16.5,239 10,230.5 3.5,222 2.5,219 1.5,216 3,202.5 4.5,189 9.5,181 14.5,173 21.75,167.25 29,161.5 37,158.5 q 8,-3 36.5,-3.5 28.5,-0.5 30.75,-2.75 l 2.25,-2.25 -1.25,-1.25 Q 104,147.5 74,145 44,142.5 30.5,136.5 17,130.5 11.75,125.25 6.5,120 4.5,116.5 2.5,113 2,103.5 1.5,94 5.25,84.75 9,75.5 21.5,65 34,54.5 35,54.25 L 36,54 37.5,51.25 Q 39,48.5 46.25,39.25 53.5,30 57.25,25.75 61,21.5 68,16 75,10.5 83,8 91,5.5 97,6 q 6,0.5 14,4 8,3.5 14.25,9.25 6.25,5.75 9.25,12.25 3,6.5 4.5,15.5 1.5,9 4.25,34 2.75,25 3.75,25 h 1 l 0.75,-2.5 Q 149.5,101 151.5,72 153.5,43 155,39.5 156.5,36 162.25,27.75 168,19.5 176,13.5 q 8,-6 15,-9 7,-3 15.5,-3 z"
style="fill:#000000;fill-opacity:0.604288;stroke:none;stroke-width:10.56;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.4" />
</g>
<g
id="g7"
transform="matrix(0.26458333,0,0,0.26458333,66.057598,105.25559)"
style="opacity:1;mix-blend-mode:normal;fill:#217821;stroke:none;stroke-width:10.56;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.4;image-rendering:auto">
<title
id="title4"
style="stroke:url(#radialGradient2799)">Layer 1</title>
<path
fill="#ffffff"
fill-rule="evenodd"
stroke="#000000"
stroke-width="8"
id="path2"
d="m 206.5,1.5 q 8.5,0 15,3 6.5,3 12.25,8.25 5.75,5.25 12.5,15.5 Q 253,38.5 259.5,43 266,47.5 273.75,52.25 281.5,57 285.5,63 q 4,6 5.75,11.5 1.75,5.5 0,18 -1.75,12.5 -5.75,20 -4,7.5 -12.75,14.25 -8.75,6.75 -17.25,10.75 -8.5,4 -15,6 -6.5,2 -28.75,4.25 -22.25,2.25 -23,3 l -0.75,0.75 0.5,1.25 q 0.5,1.25 32,0.5 31.5,-0.75 43.5,2.75 12,3.5 18.25,8.25 6.25,4.75 9.75,10.25 3.5,5.5 5.5,10.5 2,5 0.5,19.5 -1.5,14.5 -4.75,19.75 -3.25,5.25 -3.75,6.5 l -0.5,1.25 -4.25,2.5 q -4.25,2.5 -10.25,10.5 -6,8 -10,16.5 -4,8.5 -6.75,12.25 -2.75,3.75 -5.25,5.5 -2.5,1.75 -4,4 -1.5,2.25 -9.5,6.25 -8,4 -18.5,2.5 Q 210,290.5 202.75,285.25 195.5,280 190.5,275 q -5,-5 -10.25,-12.25 -5.25,-7.25 -7,-10.5 Q 171.5,249 169,237 q -2.5,-12 -5.5,-21 l -3,-9 -1.5,6.5 q -1.5,6.5 0,28.5 1.5,22 6.5,35 5,13 9.25,18.25 4.25,5.25 10,10.5 5.75,5.25 4.75,10.75 -1,5.5 -1.25,5.75 L 188,322.5 181,321 q -7,-1.5 -13.75,-7.25 Q 160.5,308 156,301.5 q -4.5,-6.5 -8.5,-17 -4,-10.5 -5.5,-20.5 -1.5,-10 -0.25,-36.25 l 1.25,-26.25 -3.25,0.75 q -3.25,0.75 -4.75,5.25 -1.5,4.5 -5.5,29.5 -4,25 -8,32.5 -4,7.5 -7.25,10.75 -3.25,3.25 -8.25,6.75 -5,3.5 -11,5.5 -6,2 -19,0.5 Q 63,291.5 57,288 51,284.5 43.25,274.75 35.5,265 31.75,257 L 28,249 26.5,247.75 Q 25,246.5 20.75,242.75 16.5,239 10,230.5 3.5,222 2.5,219 1.5,216 3,202.5 4.5,189 9.5,181 14.5,173 21.75,167.25 29,161.5 37,158.5 q 8,-3 36.5,-3.5 28.5,-0.5 30.75,-2.75 l 2.25,-2.25 -1.25,-1.25 Q 104,147.5 74,145 44,142.5 30.5,136.5 17,130.5 11.75,125.25 6.5,120 4.5,116.5 2.5,113 2,103.5 1.5,94 5.25,84.75 9,75.5 21.5,65 34,54.5 35,54.25 L 36,54 37.5,51.25 Q 39,48.5 46.25,39.25 53.5,30 57.25,25.75 61,21.5 68,16 75,10.5 83,8 91,5.5 97,6 q 6,0.5 14,4 8,3.5 14.25,9.25 6.25,5.75 9.25,12.25 3,6.5 4.5,15.5 1.5,9 4.25,34 2.75,25 3.75,25 h 1 l 0.75,-2.5 Q 149.5,101 151.5,72 153.5,43 155,39.5 156.5,36 162.25,27.75 168,19.5 176,13.5 q 8,-6 15,-9 7,-3 15.5,-3 z"
style="fill:#217821;stroke:none;stroke-width:10.56;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.4" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

90
assets/ui/destinee.svg Normal file
View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 437.49716 437.38699"
version="1.1"
id="svg13"
sodipodi:docname="destinee.svg"
width="437.49716"
height="437.38699"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata17">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1776"
inkscape:window-height="1711"
id="namedview15"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.4355469"
inkscape:cx="217.3254"
inkscape:cy="228.937"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg13" />
<defs
id="defs7">
<radialGradient
id="lorc-aura-gradient-1"
gradientTransform="scale(1.0001259,0.9998741)"
cx="257.54889"
cy="253.53915"
fx="257.54889"
fy="253.53915"
r="243.02338"
gradientUnits="userSpaceOnUse">
<stop
offset="0%"
stop-color="#f8e71c"
stop-opacity="1"
id="stop2" />
<stop
offset="100%"
stop-color="#f5a623"
stop-opacity="0.53"
id="stop4" />
</radialGradient>
</defs>
<g
class=""
transform="translate(-38.6746,-35.063004)"
id="g11">
<path
d="m 320.938,13.28 c -16.646,34.584 -38.466,60.157 -63.094,60.157 -24.522,0 -47.035,-25.275 -63.656,-59.593 0.366,39.358 -9.71,90.884 -30.938,105.125 -21.228,14.24 -49.64,-12.002 -78.844,-32.126 17.455,34.04 42.095,67.5 29.78,92.28 -12.21,24.576 -59.172,35.96 -92.874,35.626 29.338,19.29 78.842,45.803 78.844,74.188 0.002,28.384 -49.504,53.71 -78.844,73 33.702,-0.333 80.663,11.612 92.876,36.187 12.227,24.61 -9.03,56.31 -33.75,85.563 44.826,-15.413 65.142,-5.735 85.374,10.812 h 31.75 C 154.822,459.086 125.5,386.671 125.5,302.936 125.498,184.316 184.42,88.03 256.906,88.03 c 72.488,0 131.406,96.29 131.406,214.907 0,83.74 -29.317,156.153 -72.062,191.563 h 27.313 c 19.847,-14.62 39.796,-25.65 89.687,-9.28 -26.233,-30.264 -42.2,-62.484 -29.97,-87.095 12.257,-24.665 56.658,-36.612 90.533,-36.188 -29.4,-19.297 -75.344,-44.584 -75.344,-73 0,-28.415 45.943,-54.89 75.342,-74.187 -33.874,0.424 -78.273,-10.962 -90.53,-35.625 -12.315,-24.78 9.982,-58.24 27.437,-92.28 -29.202,20.12 -57.583,46.385 -78.845,32.124 -21.262,-14.263 -31.382,-66.13 -30.938,-105.69 z m -68.97,93.75 c -19.56,2.543 -37.343,25.564 -37.343,55.407 0,16.447 5.67,30.986 14,41.032 l 10.156,12.218 -15.593,2.937 c -10.815,2.035 -18.743,7.737 -25.53,17.063 -6.79,9.325 -11.984,22.344 -15.626,37.343 -6.585,27.128 -8.078,60.24 -8.31,89.47 h 36.093 l 0.656,8.656 9.124,122.563 h 76.187 l 8.095,-122.5 0.563,-8.72 h 34.375 c -0.026,-29.592 -0.44,-63.166 -6.407,-90.5 -3.295,-15.095 -8.287,-28.096 -15.156,-37.313 -6.87,-9.216 -15.133,-14.897 -27.28,-16.78 l -15.94,-2.47 10.064,-12.593 c 7.97,-9.996 13.375,-24.36 13.375,-40.406 -0.002,-31.817 -19.884,-55.313 -41.44,-55.313 -2.54,0 -3.96,-0.103 -4.03,-0.094 h -0.03 z"
fill="url(#lorc-aura-gradient-1)"
stroke="#d03d02"
stroke-opacity="1"
stroke-width="4"
transform="matrix(0.9,0,0,0.9,25.6,25.6)"
id="path9"
style="fill:url(#lorc-aura-gradient-1)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

66
assets/ui/encaisser.svg Normal file
View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 410.39459 390.34079"
version="1.1"
id="svg6"
sodipodi:docname="encaisser.svg"
width="410.39459"
height="390.34079"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2284"
inkscape:window-height="1517"
id="namedview8"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.4355469"
inkscape:cx="206.1036"
inkscape:cy="215.2477"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg6" />
<g
class=""
transform="translate(-49.8964,-68.7523)"
id="g4">
<path
d="m 26.996,47.947 c 11.726,44.806 56.176,129.96 67.496,242.934 -6.597,76.494 -22.66,98.81 -22.66,152.74 0,27.602 11.33,38.038 23.254,38.038 11.662,0 23.72,-11.823 23.72,-40.896 0,-56.606 -16.937,-73.84 -23.283,-151.65 6.472,-83.65 59.715,-45.933 59.715,2.765 0,-112.652 101.99,-85.16 116.024,-34.77 -5.164,35.11 -15.028,45.947 -15.028,75.368 0,16.633 8.51,28.86 16.74,28.86 8.416,0 16.41,-11.433 16.41,-27.226 0,-27.953 -9.303,-41.066 -14.515,-75.825 15.447,-37.68 115.544,-34.583 115.845,-1.754 -3.41,26.414 -12.764,32.13 -12.764,51.16 0,9.714 6.58,16.855 12.943,16.855 6.506,0 12.685,-6.677 12.685,-15.9 0,-18.435 -9.164,-25.838 -12.596,-52.854 14.138,-49.16 86.57,-19.867 92.008,-73.298 -51.22,45.91 -357.175,26.76 -455.994,-134.545 z m 128.85,266.22 c -4.676,31.802 -17.635,40.28 -17.635,61.724 0,10.642 8.592,18.346 17.636,18.346 8.844,0 17.988,-8.24 17.988,-19.45 0,-22.338 -13.464,-28.757 -17.988,-60.62 z"
fill="#ffffff"
transform="matrix(0.9,0,0,0.9,25.6,25.6)"
fill-opacity="1"
id="path2" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

66
assets/ui/recul.svg Normal file
View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 433.39499 347.36221"
version="1.1"
id="svg6"
sodipodi:docname="recul.svg"
width="433.39499"
height="347.36221"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1053"
inkscape:window-height="498"
id="namedview8"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.4355469"
inkscape:cx="216.6975"
inkscape:cy="201.682"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg6" />
<g
class=""
transform="translate(-39.3025,-82.318)"
id="g4">
<path
d="M 274.663,63.02 90.792,80.26 244.985,99.533 c 5.063,-13.339 12.952,-24.341 22.541,-31.828 a 52.072,52.072 0 0 1 7.137,-4.683 z m 19.832,12.803 c -5.092,0.166 -10.492,2.296 -15.879,6.502 -7.835,6.118 -15.009,16.575 -18.83,29.688 -3.821,13.112 -3.477,26.099 -0.289,35.927 3.188,9.829 8.73,16.071 15.633,18.395 6.903,2.324 14.766,0.596 22.601,-5.522 7.835,-6.117 15.01,-16.574 18.83,-29.687 3.822,-13.113 3.48,-26.1 0.292,-35.928 -3.189,-9.828 -8.73,-16.07 -15.633,-18.394 a 19.017,19.017 0 0 0 -6.725,-0.98 z m 166.85,9.485 c -24.113,13.949 -46.193,20.298 -87.233,17.252 L 340.48,228.452 c -0.675,2.682 -0.318,6 1.922,10.87 2.243,4.876 6.355,10.89 11.836,17.607 9.99,12.242 24.527,27.16 39.573,44.238 14.56,-5.5 28.23,-12.828 38.972,-20.19 11.841,-8.113 20.234,-16.95 21.965,-19.939 l 42.027,-118.22 c -16.748,-14.613 -29.471,-33.974 -35.43,-57.51 z m -288.07,51.261 -149.623,21.762 89.309,12.988 2.158,-5.052 z m 286.265,2.325 16.941,6.078 -39.123,109.037 -37.212,19.181 -8.247,-15.998 30.913,-15.933 z m -259.842,4.394 -70.586,36.043 -29.222,68.422 19.218,8.809 24.905,-57.764 59.299,-22.973 -14.702,75.955 -0.963,1.477 c -32.725,50.18 -71.654,93.41 -118.464,134.28 l -26.461,45.443 17.021,7.245 31.875,-43.989 1.38,-0.906 c 45.476,-29.872 75.93,-62.333 112.255,-94.492 l 4.533,-4.012 5.426,2.686 c 23.365,11.571 42.934,24.117 62.107,37.705 l 8.924,6.324 -69.006,65.643 24.649,39.794 17.67,-10.308 -20.078,-28.477 8.224,-5.004 c 29.884,-18.186 49.986,-39.43 71.938,-66.039 -23.653,-35.6 -42.006,-49.433 -71.592,-71.267 l 9.908,-7.227 c 34.703,-25.312 38.132,-54.476 41.61,-79.449 -9.203,4.441 -19.498,5.772 -29.473,2.414 -13.488,-4.54 -22.924,-16.472 -27.465,-30.473 -0.17,-0.522 -0.321,-1.054 -0.479,-1.584 z m 116.62,45.04 c -1.355,7.027 -3.324,14.17 -6.092,21.349 l 14.056,9.666 5.938,-22.223 z m -174.243,97.476 -126.85,17.953 99.67,14.105 a 598.987,598.987 0 0 0 27.18,-32.058 z m 91.781,82.73 -95.892,21.432 59.406,13.277 z"
fill="#ffffff"
fill-opacity="1"
transform="matrix(0.9,0,0,0.9,25.6,25.6)"
id="path2" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -20,6 +20,10 @@
- affichage du statut de surprise du défenseur - affichage du statut de surprise du défenseur
- prise en compte des significatives (demi-surprises, armes disparates, - prise en compte des significatives (demi-surprises, armes disparates,
particulière en finesse) particulière en finesse)
- gestion de l'appel à la chance
- gestion de l'utilisation de la destinée
- gestion du recul depuis le messages
- gestion de l'encaissement depuis le messages
- impossible de faire un jet "actif" en surprise totale (attaque, parade, ...) - impossible de faire un jet "actif" en surprise totale (attaque, parade, ...)
## 13.0.8 - Le renouveau d'Illysis ## 13.0.8 - Le renouveau d'Illysis

View File

@@ -424,7 +424,7 @@ select,
.system-foundryvtt-reve-de-dragon .roll-dialog { .system-foundryvtt-reve-de-dragon .roll-dialog {
font-family: CaslonAntique; font-family: CaslonAntique;
display: grid; display: grid;
grid-template-areas: "header header header header header header header" "action action action action action action action" "type separation separation separation separation separation separation" "type carac carac carac comp comp resume" "type choix choix choix choix choix modifiers" "type resolution resolution resolution resolution resolution modifiers" "type chances chances chances chances chances buttons" "footer footer footer footer footer footer footer"; grid-template-areas: "header header header header header header header" "action action action action action action action" "type separation separation separation separation separation separation" "type carac carac carac comp comp resume" "type choix choix choix choix choix conditions" "type resolution resolution resolution resolution resolution conditions" "type chances chances chances chances chances buttons" "footer footer footer footer footer footer footer";
grid-template-columns: 2rem 1rem 1fr 1fr 2fr 2fr 3fr; grid-template-columns: 2rem 1rem 1fr 1fr 2fr 2fr 3fr;
gap: 0.2rem; gap: 0.2rem;
} }
@@ -450,7 +450,7 @@ select,
grid-area: resolution; grid-area: resolution;
} }
.system-foundryvtt-reve-de-dragon .roll-dialog roll-conditions { .system-foundryvtt-reve-de-dragon .roll-dialog roll-conditions {
grid-area: modifiers; grid-area: conditions;
} }
.system-foundryvtt-reve-de-dragon .roll-dialog roll-chances { .system-foundryvtt-reve-de-dragon .roll-dialog roll-chances {
grid-area: chances; grid-area: chances;
@@ -655,6 +655,80 @@ select,
width: 1.5rem; width: 1.5rem;
text-align: center; text-align: center;
} }
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat {
font-family: CaslonAntique;
display: grid;
grid-template-areas: "img header buttons" "img resume buttons" "details details details" "actions actions actions";
grid-template-columns: 3rem 1fr 1.4rem;
grid-template-rows: max-content max-content max-content max-content;
gap: 0 0.5rem;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-img {
grid-area: img;
display: flex;
flex-direction: column;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-img img {
border: 0;
max-height: 3rem;
max-width: 3rem;
object-fit: contain;
height: 100%;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-header {
grid-area: header;
font-weight: bold;
font-size: 0.9rem;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-resume {
grid-area: resume;
text-align: justify;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-details {
grid-area: details;
text-align: justify;
display: flex;
flex-direction: column;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-actions {
grid-area: actions;
display: flex;
flex-direction: column;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-actions a {
display: flex;
flex-direction: row;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-actions a img {
margin-right: 0.5rem;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons {
grid-area: buttons;
display: flex;
flex-direction: column;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a {
border-radius: 0.2rem;
cursor: pointer;
padding: 0.2rem;
position: relative;
box-shadow: inset 1x 1px #a6827e;
color: var(--color-controls);
border: 1px ridge #846109;
display: inline-block;
align-items: center;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a img {
max-width: 1rem;
max-height: 1rem;
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a:hover {
background: var(--background-custom-button-hover);
}
.system-foundryvtt-reve-de-dragon .chat-message div.roll-chat div.chat-buttons a:active {
position: relative;
top: 1px;
}
.system-foundryvtt-reve-de-dragon .window-header { .system-foundryvtt-reve-de-dragon .window-header {
background: rgba(0, 0, 0, 0.75); background: rgba(0, 0, 0, 0.75);
} }
@@ -943,16 +1017,18 @@ select,
} }
.system-foundryvtt-reve-de-dragon .flex-grow, .system-foundryvtt-reve-de-dragon .flex-grow,
.system-foundryvtt-reve-de-dragon .flex-grow-3 { .system-foundryvtt-reve-de-dragon .flex-grow-3 {
display: flex;
flex-grow: 3; flex-grow: 3;
} }
.system-foundryvtt-reve-de-dragon .flex-grow-2 { .system-foundryvtt-reve-de-dragon .flex-grow-0-5 {
flex-grow: 2; flex-grow: 0.5;
} }
.system-foundryvtt-reve-de-dragon .flex-grow-1 { .system-foundryvtt-reve-de-dragon .flex-grow-1 {
flex-grow: 1; flex-grow: 1;
} }
.system-foundryvtt-reve-de-dragon .flex-grow-0-5 { .system-foundryvtt-reve-de-dragon .flex-grow-2 {
flex-grow: 0.5; display: flex;
flex-grow: 2;
} }
.system-foundryvtt-reve-de-dragon .voyage-liste-survies { .system-foundryvtt-reve-de-dragon .voyage-liste-survies {
max-width: 12rem; max-width: 12rem;
@@ -995,14 +1071,16 @@ select,
justify-content: center; justify-content: center;
text-align: center; text-align: center;
} }
.system-foundryvtt-reve-de-dragon .item-actions-controls, .system-foundryvtt-reve-de-dragon :is(.item-actions-controls, .equipement-actions) {
.system-foundryvtt-reve-de-dragon .equipement-actions {
margin: 0; margin: 0;
flex-grow: 2; flex-grow: 1.2;
align-items: end; align-items: end;
justify-content: flex-end; justify-content: flex-end;
text-align: right; text-align: right;
} }
.system-foundryvtt-reve-de-dragon .liste-equipement :is(.equipement-actions, .item-actions-controls) {
flex-grow: 2;
}
.system-foundryvtt-reve-de-dragon .blessure-control { .system-foundryvtt-reve-de-dragon .blessure-control {
flex-grow: 1; flex-grow: 1;
flex-direction: row; flex-direction: row;
@@ -1236,15 +1314,6 @@ select,
margin-right: 0.2rem; margin-right: 0.2rem;
margin-left: 0.2rem; margin-left: 0.2rem;
} }
.system-foundryvtt-reve-de-dragon .flex-grow-1 {
flex-grow: 1;
}
.system-foundryvtt-reve-de-dragon .flex-grow-2 {
flex-grow: 2;
}
.system-foundryvtt-reve-de-dragon .flex-grow-3 {
flex-grow: 3;
}
.system-foundryvtt-reve-de-dragon fieldset { .system-foundryvtt-reve-de-dragon fieldset {
border-style: groove; border-style: groove;
border-width: 0.1rem; border-width: 0.1rem;
@@ -1991,11 +2060,13 @@ select,
flex-grow: 2; flex-grow: 2;
} }
.system-foundryvtt-reve-de-dragon #sidebar { .system-foundryvtt-reve-de-dragon #sidebar {
font-size: 1rem;
background: #695541 url(../assets/ui/bg_sid_dark.webp) no-repeat right bottom; background: #695541 url(../assets/ui/bg_sid_dark.webp) no-repeat right bottom;
background-position: 100%; background-position: 100%;
color: rgba(220, 220, 220, 0.75); color: rgba(220, 220, 220, 0.75);
} }
.system-foundryvtt-reve-de-dragon #sidebar .chat-message {
font-size: 1rem;
}
.system-foundryvtt-reve-de-dragon #sidebar-tabs > .collapsed, .system-foundryvtt-reve-de-dragon #sidebar-tabs > .collapsed,
.system-foundryvtt-reve-de-dragon #chat-controls .chat-control-icon { .system-foundryvtt-reve-de-dragon #chat-controls .chat-control-icon {
color: rgba(220, 220, 220, 0.75); color: rgba(220, 220, 220, 0.75);
@@ -2606,6 +2677,22 @@ select,
border: 2px ridge #846109; border: 2px ridge #846109;
display: inline-block; display: inline-block;
} }
.system-foundryvtt-reve-de-dragon .chat-card-button img,
.system-foundryvtt-reve-de-dragon .chat-card-button-pushed img {
max-width: 1rem;
max-height: 1rem;
}
.system-foundryvtt-reve-de-dragon .chat-card-info {
font-size: 1.1rem;
display: flex;
flex-direction: row;
}
.system-foundryvtt-reve-de-dragon .chat-card-info img {
margin: 0 0.5rem;
max-width: 1rem;
max-height: 1rem;
filter: invert(0.8);
}
.system-foundryvtt-reve-de-dragon .chat-card-button { .system-foundryvtt-reve-de-dragon .chat-card-button {
text-shadow: 1px 1px #4d3534; text-shadow: 1px 1px #4d3534;
box-shadow: inset 1x 1px #a6827e; box-shadow: inset 1x 1px #a6827e;

View File

@@ -7,6 +7,7 @@
@import "item/munition.less"; @import "item/munition.less";
@import "item/tarot.less"; @import "item/tarot.less";
@import "roll-dialog.less"; @import "roll-dialog.less";
@import "roll-chat.less";
.window-header{ .window-header{
background: rgba(0,0,0,0.75); background: rgba(0,0,0,0.75);
} }
@@ -333,16 +334,18 @@
flex-shrink: 0; flex-shrink: 0;
} }
.flex-grow, .flex-grow-3 { .flex-grow, .flex-grow-3 {
display: flex;
flex-grow: 3; flex-grow: 3;
} }
.flex-grow-2 { .flex-grow-0-5 {
flex-grow: 2; flex-grow: 0.5;
} }
.flex-grow-1 { .flex-grow-1 {
flex-grow: 1; flex-grow: 1;
} }
.flex-grow-0-5 { .flex-grow-2 {
flex-grow: 0.5; display: flex;
flex-grow: 2;
} }
.voyage-liste-survies { .voyage-liste-survies {
max-width: 12rem; max-width: 12rem;
@@ -386,14 +389,16 @@
justify-content: center; justify-content: center;
text-align: center; text-align: center;
} }
.item-actions-controls, :is(.item-actions-controls, .equipement-actions) {
.equipement-actions {
margin: 0; margin: 0;
flex-grow: 2; flex-grow: 1.2;
align-items: end; align-items: end;
justify-content: flex-end; justify-content: flex-end;
text-align: right; text-align: right;
} }
.liste-equipement :is(.equipement-actions, .item-actions-controls) {
flex-grow: 2;
}
.blessure-control { .blessure-control {
flex-grow: 1; flex-grow: 1;
@@ -622,16 +627,6 @@
} }
} }
} }
.flex-grow-1 {
flex-grow: 1;
}
.flex-grow-2 {
flex-grow: 2;
}
.flex-grow-3 {
flex-grow: 3;
}
fieldset { fieldset {
border-style: groove; border-style: groove;
border-width: 0.1rem; border-width: 0.1rem;
@@ -1451,7 +1446,9 @@
/* ======================================== */ /* ======================================== */
/* Sidebar CSS */ /* Sidebar CSS */
#sidebar { #sidebar {
font-size: 1rem; .chat-message{
font-size: 1rem;
}
background: rgb(105,85,65) url(../assets/ui/bg_sid_dark.webp) no-repeat right bottom; background: rgb(105,85,65) url(../assets/ui/bg_sid_dark.webp) no-repeat right bottom;
background-position: 100%; background-position: 100%;
color: rgba(220,220,220,0.75); color: rgba(220,220,220,0.75);
@@ -1961,7 +1958,23 @@
border: 2px ridge #846109; border: 2px ridge #846109;
display: inline-block; display: inline-block;
img {
max-width: 1rem;
max-height: 1rem;
}
} }
.chat-card-info{
font-size: 1.1rem;
display: flex;
flex-direction: row;
img {
margin: 0 0.5rem;
max-width: 1rem;
max-height: 1rem;
filter: invert(0.8);
}
}
.chat-card-button{ .chat-card-button{
text-shadow: 1px 1px #4d3534; text-shadow: 1px 1px #4d3534;

87
less/roll-chat.less Normal file
View File

@@ -0,0 +1,87 @@
.chat-message {
div.roll-chat {
font-family: CaslonAntique;
display: grid;
grid-template-areas:
"img header buttons"
"img resume buttons"
"details details details"
"actions actions actions";
grid-template-columns: 3rem 1fr 1.4rem;
grid-template-rows: max-content max-content max-content max-content;
gap: 0 0.5rem;
div.chat-img {
grid-area: img;
display: flex;
flex-direction: column;
img {
border: 0;
max-height: 3rem;
max-width: 3rem;
object-fit: contain;
height: 100%;
}
}
div.chat-header {
grid-area: header;
font-weight: bold;
font-size: 0.9rem;
}
div.chat-resume {
grid-area: resume;
text-align: justify;
}
div.chat-details {
grid-area: details;
text-align: justify;
display: flex;
flex-direction: column;
}
div.chat-actions {
grid-area: actions;
display: flex;
flex-direction: column;
a {
display: flex;
flex-direction: row;
img {
margin-right: 0.5rem;
}
}
}
div.chat-buttons {
grid-area: buttons;
display: flex;
flex-direction: column;
a {
border-radius: 0.2rem;
cursor: pointer;
padding: 0.2rem;
position: relative;
box-shadow: inset 1x 1px #a6827e;
color: var(--color-controls);
border: 1px ridge #846109;
display: inline-block;
align-items: center;
img {
max-width: 1rem;
max-height: 1rem;
}
}
a:hover {
background: var(--background-custom-button-hover);
}
a:active{
position:relative;
top:1px;
}
}
}
}

View File

@@ -6,8 +6,8 @@
"action action action action action action action" "action action action action action action action"
"type separation separation separation separation separation separation" "type separation separation separation separation separation separation"
"type carac carac carac comp comp resume" "type carac carac carac comp comp resume"
"type choix choix choix choix choix modifiers" "type choix choix choix choix choix conditions"
"type resolution resolution resolution resolution resolution modifiers" "type resolution resolution resolution resolution resolution conditions"
"type chances chances chances chances chances buttons" "type chances chances chances chances chances buttons"
"footer footer footer footer footer footer footer"; "footer footer footer footer footer footer footer";
grid-template-columns: 2rem 1rem 1fr 1fr 2fr 2fr 3fr; grid-template-columns: 2rem 1rem 1fr 1fr 2fr 2fr 3fr;
@@ -22,7 +22,7 @@
roll-choix { grid-area: choix; } roll-choix { grid-area: choix; }
roll-table { grid-area: resolution; } roll-table { grid-area: resolution; }
roll-conditions { grid-area: modifiers; } roll-conditions { grid-area: conditions; }
roll-chances { grid-area: chances; } roll-chances { grid-area: chances; }
roll-resume { grid-area: resume; } roll-resume { grid-area: resume; }
roll-buttons { grid-area: buttons; } roll-buttons { grid-area: buttons; }

View File

@@ -46,7 +46,7 @@ import { PAS_DE_DRACONIC, POSSESSION_SANS_DRACONIC } from "./item/base-items.js"
import { RdDRollResult } from "./rdd-roll-result.js"; import { RdDRollResult } from "./rdd-roll-result.js";
import { RdDInitiative } from "./initiative.mjs"; import { RdDInitiative } from "./initiative.mjs";
import RollDialog from "./roll/roll-dialog.mjs"; import RollDialog from "./roll/roll-dialog.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2 } from "./settings/options-avancees.js"; import { OptionsAvancees, ROLL_DIALOG_V2, ROLL_DIALOG_V2_TEST } from "./settings/options-avancees.js";
export const MAINS_DIRECTRICES = ['Droitier', 'Gaucher', 'Ambidextre'] export const MAINS_DIRECTRICES = ['Droitier', 'Gaucher', 'Ambidextre']
@@ -506,7 +506,7 @@ export class RdDActor extends RdDBaseActorSang {
'system.sante.fatigue.value': 0, 'system.sante.fatigue.value': 0,
'system.compteurs.ethylisme': { value: 1, nb_doses: 0, jet_moral: false } 'system.compteurs.ethylisme': { value: 1, nb_doses: 0, jet_moral: false }
}) })
await this.removeEffects(e => e.id != STATUSES.StatusDemiReve); await this.removeEffects(e => !e.statuses?.has(STATUSES.StatusDemiReve));
await this.supprimerBlessures(it => true); await this.supprimerBlessures(it => true);
await ChatMessage.create({ await ChatMessage.create({
whisper: ChatUtility.getOwners(this), whisper: ChatUtility.getOwners(this),
@@ -2513,29 +2513,27 @@ export class RdDActor extends RdDBaseActorSang {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async computeArmure(attackerRoll) { async computeArmure(dmg) {
let dmg = (attackerRoll.dmg.dmgArme ?? 0) + (attackerRoll.dmg.dmgActor ?? 0); let baseDmg = (dmg.dmgArme ?? 0) + (dmg.dmgActor ?? 0);
let armeData = attackerRoll.arme;
let protection = 0; let protection = 0;
const armures = this.items.filter(it => it.type == "armure" && it.system.equipe); if (dmg.encaisserSpecial != "noarmure") {
for (const armure of armures) { const armures = this.items.filter(it => it.type == "armure" && it.system.equipe)
protection += await RdDDice.rollTotal(armure.system.protection.toString());
if (dmg > 0 && attackerRoll.dmg.encaisserSpecial != "noarmure") { for (const armure of armures) {
await armure.deteriorerArmure(dmg) protection += await RdDDice.rollTotal(armure.system.protection.toString());
dmg = 0; if (baseDmg > 0 && dmg.encaisserSpecial != "noarmure") {
await armure.deteriorerArmure(baseDmg)
baseDmg = 0;
}
}
protection -= Math.min(dmg.penetration, protection)
protection += this.getProtectionNaturelle();
// Gestion des cas particuliers sur la fenêtre d'encaissement
if (dmg.encaisserSpecial == "chute") {
protection = Math.min(protection, 2);
} }
} }
const penetration = Misc.toInt(armeData?.system.penetration ?? 0); console.log("Final protect", protection, dmg)
protection = Math.max(protection - penetration, 0);
protection += this.getProtectionNaturelle();
// Gestion des cas particuliers sur la fenêtre d'encaissement
if (attackerRoll.dmg.encaisserSpecial == "noarmure") {
protection = 0;
}
if (attackerRoll.dmg.encaisserSpecial == "chute") {
protection = Math.min(protection, 2);
}
console.log("Final protect", protection, attackerRoll);
return protection; return protection;
} }
@@ -3049,7 +3047,7 @@ export class RdDActor extends RdDBaseActorSang {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _rollArtV2(oeuvreId, callbackAction = async (actor, rd) => await actor._resultArtV2(rd)) { async _rollArtV2(oeuvreId) {
const oeuvre = this.items.get(oeuvreId) const oeuvre = this.items.get(oeuvreId)
const rollData = { const rollData = {
title: `Interpretation de ${oeuvre.name} par ${this.name}`, title: `Interpretation de ${oeuvre.name} par ${this.name}`,
@@ -3066,23 +3064,13 @@ export class RdDActor extends RdDBaseActorSang {
} }
await RollDialog.create(rollData, { await RollDialog.create(rollData, {
onRollDone: (dialog) => { onRollDone: (dialog) => {
this._onCloseRollDialog() if (!OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST))
dialog.close() dialog.close()
}, }
customChatMessage: true,
callbacks: [callbackAction]
}) })
} }
async _resultArtV2(artData) {
const niveau = artData.oeuvre.system.niveau ?? 0;
const baseQualite = (artData.rolled.isSuccess ? niveau : artData.competence.system.niveau);
artData.qualiteFinale = Math.min(baseQualite, niveau) + artData.rolled.ptQualite;
await RdDRollResult.displayRollData(artData, this.name, `chat-resultat-${artData.art}.hbs`);
}
/* -------------------------------------------- */ /* -------------------------------------------- */
async rollOeuvre(id) { async rollOeuvre(id) {
if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) { if (OptionsAvancees.isUsing(ROLL_DIALOG_V2)) {
return await this._rollArtV2(id) return await this._rollArtV2(id)
@@ -3168,7 +3156,7 @@ export class RdDActor extends RdDBaseActorSang {
async _resultArt(artData) { async _resultArt(artData) {
const niveau = artData.oeuvre.system.niveau ?? 0; const niveau = artData.oeuvre.system.niveau ?? 0;
const baseQualite = (artData.rolled.isSuccess ? niveau : artData.competence.system.niveau); const baseQualite = (artData.rolled.isSuccess ? niveau : artData.competence.system.niveau);
artData.qualiteFinale = Math.min(baseQualite, niveau) + artData.rolled.ptQualite; artData.qualiteFinale = Math.min(baseQualite, niveau) + artData.rolled.ptQualite
await RdDRollResult.displayRollData(artData, this.name, `chat-resultat-${artData.art}.hbs`); await RdDRollResult.displayRollData(artData, this.name, `chat-resultat-${artData.art}.hbs`);
} }
@@ -3189,7 +3177,7 @@ export class RdDActor extends RdDBaseActorSang {
async _resultRecetteCuisine(cuisine) { async _resultRecetteCuisine(cuisine) {
const niveauRecette = cuisine.oeuvre.system.niveau ?? 0; const niveauRecette = cuisine.oeuvre.system.niveau ?? 0;
const baseQualite = (cuisine.rolled.isSuccess ? niveauRecette : cuisine.competence.system.niveau); const baseQualite = (cuisine.rolled.isSuccess ? niveauRecette : cuisine.competence.system.niveau);
cuisine.qualiteFinale = Math.min(baseQualite, niveauRecette) + cuisine.rolled.ptQualite; cuisine.qualiteFinale = Math.min(baseQualite, niveauRecette) + cuisine.rolled.ptQualite
cuisine.exotismeFinal = Math.min(Math.min(cuisine.qualiteFinale, cuisine.oeuvre.system.exotisme ?? 0), 0); cuisine.exotismeFinal = Math.min(Math.min(cuisine.qualiteFinale, cuisine.oeuvre.system.exotisme ?? 0), 0);
cuisine.sust = cuisine.oeuvre.system.sust * Math.min(cuisine.proportions, cuisine.proportionsMax ?? cuisine.proportions) cuisine.sust = cuisine.oeuvre.system.sust * Math.min(cuisine.proportions, cuisine.proportionsMax ?? cuisine.proportions)
const platCuisine = { const platCuisine = {

View File

@@ -118,8 +118,7 @@ export class RdDBaseActorReve extends RdDBaseActor {
.filter(it => it != undefined); .filter(it => it != undefined);
} }
async computeArmure(dmg) { return this.getProtectionNaturelle() }
async computeArmure(attackerRoll) { return this.getProtectionNaturelle() }
async remiseANeuf() { } async remiseANeuf() { }
async appliquerAjoutExperience(rollData, hideChatMessage = 'show') { } async appliquerAjoutExperience(rollData, hideChatMessage = 'show') { }
@@ -227,12 +226,12 @@ export class RdDBaseActorReve extends RdDBaseActor {
isEffectAllowed(effectId) { return false } isEffectAllowed(effectId) { return false }
getEffects(filter = e => true, forceRequise = undefined) { getEffects(filter = e => true, forceRequise = undefined) {
const effects = this.getEmbeddedCollection("ActiveEffect").filter(filter) const effects = this.getEmbeddedCollection("ActiveEffect")
const selected = effects.filter(filter)
if (forceRequise && this.isForceInsuffisante(forceRequise)) { if (forceRequise && this.isForceInsuffisante(forceRequise)) {
/// TODO selected.push(StatusEffects.prepareActiveEffect(STATUSES.StatusForceWeak))
effects.push(StatusEffects.prepareActiveEffect(STATUSES.StatusForceWeak))
} }
return effects return selected
} }
getEffectByStatus(statusId) { getEffectByStatus(statusId) {
@@ -257,7 +256,8 @@ export class RdDBaseActorReve extends RdDBaseActor {
async removeEffects(filter = e => true) { async removeEffects(filter = e => true) {
if (game.user.isGM) { if (game.user.isGM) {
const ids = this.getEffects(filter).map(it => it.id); const effectsToRemove = this.getEffects(filter);
const ids = effectsToRemove.map(it => it.id);
await this.deleteEmbeddedDocuments('ActiveEffect', ids); await this.deleteEmbeddedDocuments('ActiveEffect', ids);
} }
} }
@@ -495,29 +495,37 @@ export class RdDBaseActorReve extends RdDBaseActor {
/* -------------------------------------------- */ /* -------------------------------------------- */
async encaisser() { await RdDEncaisser.encaisser(this) } async encaisser() { await RdDEncaisser.encaisser(this) }
async encaisserDommages(rollData, attacker = undefined, show = undefined, attackerToken = undefined, defenderToken = undefined) { async encaisserDommages(dmg, attacker = undefined, show = undefined, attackerToken = undefined, defenderToken = undefined) {
if (attacker && !await attacker.accorder(this, 'avant-encaissement')) { if (attacker && !await attacker.accorder(this, 'avant-encaissement')) {
return; return
} }
const armure = await this.computeArmure(rollData); if (!Misc.isOwnerPlayer(this)) {
return RdDBaseActor.remoteActorCall({
tokenId: attackerToken?.id ?? this.token?.id,
actorId: this.id,
method: 'encaisserDommages', args: [dmg, attacker, show, attackerToken, defenderToken]
})
}
const armure = await this.computeArmure(dmg)
if (ReglesOptionnelles.isUsing('validation-encaissement-gr')) { if (ReglesOptionnelles.isUsing('validation-encaissement-gr')) {
await this.encaisserDommagesValidationGR(rollData, armure, show, attackerToken, defenderToken); await this.encaisserDommagesValidationGR(dmg, armure, show, attackerToken, defenderToken);
} }
else { else {
const jet = await RdDUtility.jetEncaissement(this, rollData, armure, { showDice: SHOW_DICE }); const jet = await RdDUtility.jetEncaissement(this, dmg, armure, { showDice: SHOW_DICE });
await this.$onEncaissement(jet, show, attackerToken, defenderToken) await this.$onEncaissement(jet, show, attackerToken, defenderToken)
} }
} }
async encaisserDommagesValidationGR(rollData, armure, show, attackerToken, defenderToken) { async encaisserDommagesValidationGR(dmg, armure, show, attackerToken, defenderToken) {
if (!game.user.isGM) { if (!game.user.isGM) {
RdDBaseActor.remoteActorCall({ RdDBaseActor.remoteActorCall({
tokenId: this.token?.id, tokenId: this.token?.id,
actorId: this.id, actorId: this.id,
method: 'encaisserDommagesValidationGR', args: [rollData, armure, show, attackerToken, defenderToken] method: 'encaisserDommagesValidationGR', args: [dmg, armure, show, attackerToken, defenderToken]
}) })
} else { } else {
DialogValidationEncaissement.validerEncaissement(this, rollData, armure, DialogValidationEncaissement.validerEncaissement(this, dmg, armure,
jet => this.$onEncaissement(jet, show, attackerToken, defenderToken)); jet => this.$onEncaissement(jet, show, attackerToken, defenderToken));
} }
} }
@@ -554,15 +562,37 @@ export class RdDBaseActorReve extends RdDBaseActor {
} }
} }
async encaisserRecul(force, dmgArme = 0) {
const diffRecul = this.getTaille() - force - dmgArme
const rolled = await RdDResolutionTable.roll(10, diffRecul)
if (rolled.isSuccess) {
return 'encaisse'
}
if (rolled.isETotal || (await this.rollEquilibre(diffRecul)).isEchec) {
await this.setEffect(STATUSES.StatusProne, true)
return 'chute'
}
return 'recul'
}
/* -------------------------------------------- */
async rollEquilibre(diff) {
// TODO: accrobatie optionnelle sur jet d'équilibre?
if (ReglesOptionnelles.isSet('acrobatie-pour-recul')){
diff += Math.max(0, this.getCompetence('acrobatie')?.system.niveau ?? 0)
}
return await RdDResolutionTable.roll(this.getAgilite(), diff);
}
/* -------------------------------------------- */ /* -------------------------------------------- */
async accorder(entite, when = 'avant-encaissement') { async accorder(entite, when = 'avant-encaissement') {
if (when != game.settings.get(SYSTEM_RDD, "accorder-entite-cauchemar") if (when != game.settings.get(SYSTEM_RDD, "accorder-entite-cauchemar")
|| entite == undefined || entite == undefined
|| !entite.isEntite([ENTITE_INCARNE]) || !entite.isEntite([ENTITE_INCARNE])
|| entite.isEntiteAccordee(this)) { || entite.isEntiteAccordee(this)) {
return true; return true
} }
const rolled = await RdDResolutionTable.roll(this.getReveActuel(), - Number(entite.getNiveau())); const rolled = await RdDResolutionTable.roll(this.getReveActuel(), - Number(entite.getNiveau()))
const rollData = { const rollData = {
alias: this.getAlias(), alias: this.getAlias(),
rolled: rolled, rolled: rolled,
@@ -571,11 +601,11 @@ export class RdDBaseActorReve extends RdDBaseActor {
}; };
if (rolled.isSuccess) { if (rolled.isSuccess) {
await entite.setEntiteReveAccordee(this); await entite.setEntiteReveAccordee(this)
} }
await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-accorder-cauchemar.hbs'); await RdDRollResult.displayRollData(rollData, this, 'chat-resultat-accorder-cauchemar.hbs')
await this.appliquerAjoutExperience(rollData, true); await this.appliquerAjoutExperience(rollData, true)
return rolled.isSuccess; return rolled.isSuccess;
} }

View File

@@ -1,6 +1,4 @@
import { Grammar } from "../grammar.js";
import { ITEM_TYPES } from "../constants.js"; import { ITEM_TYPES } from "../constants.js";
import { LIST_CARAC_AUTRES } from "../rdd-carac.js";
import { RdDBaseActorSang } from "./base-actor-sang.js"; import { RdDBaseActorSang } from "./base-actor-sang.js";
export class RdDCreature extends RdDBaseActorSang { export class RdDCreature extends RdDBaseActorSang {
@@ -45,5 +43,4 @@ export class RdDCreature extends RdDBaseActorSang {
} }
return undefined return undefined
} }
} }

View File

@@ -197,8 +197,12 @@ export class ChatUtility {
static async onCreateChatMessage(chatMessage, options, id) { static async onCreateChatMessage(chatMessage, options, id) {
if (chatMessage.isAuthor) { if (chatMessage.isAuthor) {
await chatMessage.setFlag(SYSTEM_RDD, 'rdd-timestamp', game.system.rdd.calendrier.getTimestamp()); await ChatUtility.setTimestamp(chatMessage)
await chatMessage.update({ content: await RdDTextEditor.enrichHTML(chatMessage.content, undefined, { showLink: false }) }) await chatMessage.update({ content: await RdDTextEditor.enrichHTML(chatMessage.content, undefined, { showLink: false }) })
} }
} }
static async setTimestamp(chatMessage) {
await chatMessage.setFlag(SYSTEM_RDD, 'rdd-timestamp', game.system.rdd.calendrier.getTimestamp());
}
} }

View File

@@ -7,18 +7,17 @@ import { RdDUtility } from "./rdd-utility.js";
*/ */
export class DialogValidationEncaissement extends Dialog { export class DialogValidationEncaissement extends Dialog {
static async validerEncaissement(actor, rollData, armure, onEncaisser) { static async validerEncaissement(actor, dmg, armure, onEncaisser) {
const encaissement = await RdDUtility.jetEncaissement(actor, rollData, armure, { showDice: HIDE_DICE }); const encaissement = await RdDUtility.jetEncaissement(actor, dmg, armure, { showDice: HIDE_DICE });
const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-validation-encaissement.hbs', { const html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-validation-encaissement.hbs', {
actor: actor, actor: actor,
rollData: rollData,
encaissement: encaissement encaissement: encaissement
}); });
new DialogValidationEncaissement(html, actor, rollData, armure, encaissement, onEncaisser).render(true); new DialogValidationEncaissement(html, actor, dmg, armure, encaissement, onEncaisser).render(true);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
constructor(html, actor, rollData, armure, encaissement, onEncaisser) { constructor(html, actor, dmg, armure, encaissement, onEncaisser) {
// Common conf // Common conf
let buttons = { let buttons = {
"valider": { label: "Valider", callback: html => this.onValider() }, "valider": { label: "Valider", callback: html => this.onValider() },
@@ -42,11 +41,11 @@ export class DialogValidationEncaissement extends Dialog {
super(dialogConf, dialogOptions); super(dialogConf, dialogOptions);
this.actor = actor this.actor = actor
this.rollData = rollData; this.dmg = dmg
this.armure = armure; this.armure = armure
this.encaissement = encaissement; this.encaissement = encaissement
this.onEncaisser = onEncaisser; this.onEncaisser = onEncaisser
this.forceDiceResult = {total: encaissement.roll.result }; this.forceDiceResult = {total: encaissement.roll.result }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -55,14 +54,14 @@ export class DialogValidationEncaissement extends Dialog {
this.html = html; this.html = html;
this.html.find('input.encaissement-roll-result').keyup(async event => { this.html.find('input.encaissement-roll-result').keyup(async event => {
this.forceDiceResult.total = event.currentTarget.value; this.forceDiceResult.total = event.currentTarget.value;
this.encaissement = await RdDUtility.jetEncaissement(this.actor, this.rollData, this.armure, { showDice: HIDE_DICE, forceDiceResult: this.forceDiceResult}); this.encaissement = await RdDUtility.jetEncaissement(this.actor, this.dmg, this.armure, { showDice: HIDE_DICE, forceDiceResult: this.forceDiceResult});
this.html.find('label.encaissement-total').text(this.encaissement.total); this.html.find('label.encaissement-total').text(this.encaissement.total);
this.html.find('label.encaissement-blessure').text(this.encaissement.blessures) this.html.find('label.encaissement-blessure').text(this.encaissement.blessures)
}); });
} }
async onValider() { async onValider() {
this.encaissement = await RdDUtility.jetEncaissement(this.actor, this.rollData, this.armure, { showDice: SHOW_DICE, forceDiceResult: this.forceDiceResult}); this.encaissement = await RdDUtility.jetEncaissement(this.actor, this.dmg, this.armure, { showDice: SHOW_DICE, forceDiceResult: this.forceDiceResult});
this.onEncaisser(this.encaissement) this.onEncaisser(this.encaissement)
} }
} }

View File

@@ -34,6 +34,9 @@ export class Misc {
return ((n % m) + m) % m; return ((n % m) + m) % m;
} }
static inRange(value, min,max){
return Math.max(min, Math.min(value, max))
}
static sum() { static sum() {
return (a, b) => Number(a) + Number(b); return (a, b) => Number(a) + Number(b);
} }

View File

@@ -1,5 +1,6 @@
import { RdDItemArme } from "./item/arme.js"; import { RdDItemArme } from "./item/arme.js";
import { RdDPossession } from "./rdd-possession.js"; import { RdDPossession } from "./rdd-possession.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
const conditionsTactiques = [ const conditionsTactiques = [
{ key: '', label: '', dmg: 0, attaque: 0, parade: 0, esquive: true, isTactique: false }, { key: '', label: '', dmg: 0, attaque: 0, parade: 0, esquive: true, isTactique: false },
@@ -35,11 +36,14 @@ export class RdDBonus {
/* -------------------------------------------- */ /* -------------------------------------------- */
static dmg(rollData, actor, isEntiteIncarnee = false) { static dmg(rollData, actor, isEntiteIncarnee = false) {
const diff = rollData.diffLibre;
const dmgArme = RdDBonus.dmgArme(rollData.arme, rollData.arme?.system.dommagesReels) const dmgArme = RdDBonus.dmgArme(rollData.arme, rollData.arme?.system.dommagesReels)
const forceRequise = RdDItemArme.valeurMain(rollData.arme?.system.force ?? 0, RdDItemArme.getMainAttaque(rollData.competence)) const forceRequise = rollData.arme ? RdDItemArme.valeurMain(rollData.arme.system.force ?? 0, RdDItemArme.getMainAttaque(rollData.competence)) : 0
let dmg = { let dmg = {
total: 0, total: 0,
dmgArme: dmgArme, dmgArme: dmgArme,
diff: diff,
dmgDiffLibre: ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(diff ?? 0) : 0,
penetration: RdDBonus._peneration(rollData), penetration: RdDBonus._peneration(rollData),
dmgTactique: RdDBonus.dmgBonus(rollData.tactique), dmgTactique: RdDBonus.dmgBonus(rollData.tactique),
dmgParticuliere: RdDBonus._dmgParticuliere(rollData), dmgParticuliere: RdDBonus._dmgParticuliere(rollData),
@@ -51,6 +55,28 @@ export class RdDBonus {
dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere + dmg.dmgForceInsuffisante dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere + dmg.dmgForceInsuffisante
return dmg; return dmg;
} }
static dmgRollV2(rollData, current) {
const actor = rollData.active.actor
const attaque = current.attaque
const arme = attaque.arme
const dmgArme = RdDBonus.dmgArme(arme, attaque.dommagesArme)
const dmg = {
total: 0,
dmgArme: dmgArme,
penetration: arme.penetration(),
diff: attaque.diff,
dmgTactique: current.tactique?.dmg ?? 0,
dmgParticuliere: 0, // TODO RdDBonus._dmgParticuliere(rollData),
dmgSurprise: rollData.opponent?.surprise?.dmg ?? 0,
mortalite: RdDBonus.mortalite(current.dmg?.mortalite, arme.system.mortalite, rollData.opponent?.actor?.isEntite()),
dmgActor: RdDBonus.bonusDmg(actor, attaque.carac.key, dmgArme, attaque.forceRequise),
dmgForceInsuffisante: Math.min(0, actor.getForce() - attaque.forceRequise),
dmgDiffLibre: ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(attaque.diff ?? 0) : 0
}
dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere + dmg.dmgForceInsuffisante + dmg.dmgDiffLibre
return dmg
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static description(condition) { static description(condition) {

View File

@@ -17,9 +17,9 @@ import { RdDItemCompetenceCreature } from "./item-competencecreature.js";
import { RdDInitiative } from "./initiative.mjs"; import { RdDInitiative } from "./initiative.mjs";
import RollDialog from "./roll/roll-dialog.mjs"; import RollDialog from "./roll/roll-dialog.mjs";
import { PART_DEFENSE } from "./roll/roll-part-defense.mjs"; import { PART_DEFENSE } from "./roll/roll-part-defense.mjs";
import { PART_ATTAQUE } from "./roll/roll-part-attaque.mjs";
import { RollDialogAdapter } from "./roll/roll-dialog-adapter.mjs"; import { RollDialogAdapter } from "./roll/roll-dialog-adapter.mjs";
import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll/roll-constants.mjs"; import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll/roll-constants.mjs";
import { OptionsAvancees, ROLL_DIALOG_V2_TEST } from "./settings/options-avancees.js";
/* -------------------------------------------- */ /* -------------------------------------------- */
const premierRoundInit = [ const premierRoundInit = [
@@ -373,7 +373,7 @@ export class RdDCombat {
if (Misc.isOwnerPlayer(defender)) { if (Misc.isOwnerPlayer(defender)) {
let attackerRoll = msg.attackerRoll; let attackerRoll = msg.attackerRoll;
let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : undefined; let attacker = msg.attackerId ? game.actors.get(msg.attackerId) : undefined;
defender.encaisserDommages(attackerRoll, attacker, msg.attackerToken); defender.encaisserDommages(attackerRoll.dmg, attacker, msg.attackerToken);
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.attackerToken.id, msg.defenderToken.id); const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(msg.attackerId, msg.attackerToken.id, msg.defenderToken.id);
rddCombat?.removeChatMessageActionsPasseArme(attackerRoll.passeArme); rddCombat?.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
} }
@@ -403,16 +403,19 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
static registerChatCallbacks(html) { static registerChatCallbacks(html) {
for (let button of [ for (let button of [
'.defense-button', '.button-defense',
'.parer-button', '.button-parade',
'.esquiver-button', '.button-esquive',
'.button-encaisser',
'.particuliere-attaque', '.particuliere-attaque',
'.encaisser-button',
'.appel-chance-defense', '.appel-chance-defense',
'.appel-destinee-defense', '.appel-destinee-defense',
'.appel-chance-attaque', '.appel-chance-attaque',
'.appel-destinee-attaque', '.appel-destinee-attaque',
'.echec-total-attaque', '.echec-total-attaque',
// '.appel-chance',
// '.chat-encaissement',
// '.resister-recul',
]) { ]) {
$(html).on("click", button, event => { $(html).on("click", button, event => {
const rddCombat = RdDCombat.rddCombatForAttackerAndDefender( const rddCombat = RdDCombat.rddCombatForAttackerAndDefender(
@@ -472,11 +475,11 @@ export class RdDCombat {
switch (button) { switch (button) {
case '.particuliere-attaque': return await this.choixParticuliere(attackerRoll, event.currentTarget.attributes['data-mode'].value); case '.particuliere-attaque': return await this.choixParticuliere(attackerRoll, event.currentTarget.attributes['data-mode'].value);
case '.defense-button': return this.defenseV2(attackerRoll); case '.button-defense': return this.defenseV2(attackerRoll);
case '.parer-button': return this.parade(attackerRoll, armeParadeId); case '.button-parade': return this.parade(attackerRoll, armeParadeId);
case '.esquiver-button': return this.esquive(attackerRoll, compId, competence); case '.button-esquive': return this.esquive(attackerRoll, compId, competence);
case '.encaisser-button': return this.encaisser(attackerRoll, defenderRoll); case '.button-encaisser': return this.encaisser(attackerRoll, defenderRoll);
case '.echec-total-attaque': return this._onEchecTotal(attackerRoll); case '.echec-total-attaque': return this._onEchecTotal(attackerRoll);
case '.appel-chance-attaque': return this.attacker.rollAppelChance( case '.appel-chance-attaque': return this.attacker.rollAppelChance(
@@ -486,11 +489,9 @@ export class RdDCombat {
() => this.defenseChanceuse(attackerRoll, defenderRoll), () => this.defenseChanceuse(attackerRoll, defenderRoll),
() => this.afficherOptionsDefense(attackerRoll, defenderRoll, { defenseChance: true })); () => this.afficherOptionsDefense(attackerRoll, defenderRoll, { defenseChance: true }));
case '.appel-destinee-attaque': return this.attacker.appelDestinee( case '.appel-destinee-attaque': return this.attacker.appelDestinee(
() => this.attaqueSignificative(attackerRoll), () => this.attaqueSignificative(attackerRoll));
() => { });
case '.appel-destinee-defense': return this.defender.appelDestinee( case '.appel-destinee-defense': return this.defender.appelDestinee(
() => this.defenseDestinee(defenderRoll), () => this.defenseDestinee(defenderRoll));
() => { });
} }
} }
@@ -807,8 +808,6 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async _sendMessageDefense(attackerRoll, defenderRoll, essaisPrecedents = undefined) { async _sendMessageDefense(attackerRoll, defenderRoll, essaisPrecedents = undefined) {
console.log("RdDCombat._sendMessageDefense", attackerRoll, defenderRoll, essaisPrecedents, " / ", this.attacker, this.target, this.attackerId, attackerRoll.competence.system.categorie);
this.removeChatMessageActionsPasseArme(attackerRoll.passeArme); this.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
if (essaisPrecedents) { if (essaisPrecedents) {
foundry.utils.mergeObject(attackerRoll.essais, essaisPrecedents, { overwrite: true }); foundry.utils.mergeObject(attackerRoll.essais, essaisPrecedents, { overwrite: true });
@@ -969,10 +968,8 @@ export class RdDCombat {
} }
async defenseV2(attackerRoll) { async defenseV2(attackerRoll) {
// this._prepareParade(attackerRoll, arme, competence); // this._prepareParade(attackerRoll, arme, competence);
const rollData = await this.doRollDefense({
{
ids: { ids: {
actorId: this.defender.id, actorId: this.defender.id,
actorTokenId: this.defenderTokenId, actorTokenId: this.defenderTokenId,
@@ -980,23 +977,31 @@ export class RdDCombat {
opponentId: this.attackerId, opponentId: this.attackerId,
}, },
type: { allowed: ['defense'], current: 'defense' }, type: { allowed: ['defense'], current: 'defense' },
attaque: RollDialogAdapter.mapActionAttaque(attackerRoll), attackerRoll: RollDialogAdapter.mapActionAttaque(attackerRoll),
passeArme: attackerRoll.passeArme, passeArme: attackerRoll.passeArme,
}
await RollDialog.create(rollData, {
onRollDone: (dialog) => { dialog.close() },
customChatMessage: true,
callbacks: [async (roll) => {
this.removeChatMessageActionsPasseArme(roll.passeArme)
// defense: esquive / arme de parade / competence de défense
if (!RdDCombat.isParticuliere(roll))
await roll.active.actor.incDecItemUse(roll.current[PART_DEFENSE].defense?.id,)
await this._onDefense(roll)
}]
}) })
} }
async doRollDefense(rollData, callbacks = []) {
await RollDialog.create(rollData, {
onRollDone: (dialog) => {
if (!OptionsAvancees.isUsing(ROLL_DIALOG_V2_TEST))
dialog.close()
},
customChatMessage: true,
callbacks: [
async (roll) => {
this.removeChatMessageActionsPasseArme(roll.passeArme);
// defense: esquive / arme de parade / competence de défense
if (!RdDCombat.isParticuliere(roll)) {
await roll.active.actor.incDecItemUse(roll.current[PART_DEFENSE].defense?.id);
}
await this._onDefense(roll);
},
...callbacks
]
})
}
/* -------------------------------------------- */ /* -------------------------------------------- */
_prepareParade(attackerRoll, armeParade, competenceParade) { _prepareParade(attackerRoll, armeParade, competenceParade) {
@@ -1025,57 +1030,42 @@ export class RdDCombat {
} }
async _onDefense(rollData) { async _onDefense(rollData) {
const isEsquive = rollData.current[PART_DEFENSE].isEsquive
const isParade = !isEsquive
if (RdDCombat.isReussite(rollData)) { if (RdDCombat.isReussite(rollData)) {
await this._onDefenseNormale(rollData) if (isParade) {
await this.computeDeteriorationArme(rollData)
}
if (RdDCombat.isParticuliere(rollData)) { if (RdDCombat.isParticuliere(rollData)) {
await this._onDefenseParticuliere(rollData) await this._onDefenseParticuliere(rollData, isEsquive)
} }
} }
else { this.removeChatMessageActionsPasseArme(rollData.passeArme)
await this._onDefenseEchec(dialog, rollData)
}
} }
async _onDefenseParticuliere(rollData) { async _onDefenseParticuliere(rollData, isEsquive) {
console.log("RdDCombat._onDefenseParticuliere >>>", rollData); if (isEsquive) {
if (/*TODO: parade?*/!rollData.attackerRoll?.isPart) { ChatUtility.createChatWithRollMode(
{ content: "<strong>Vous pouvez esquiver une deuxième fois!</strong>" },
this.defender)
}
else if (/*TODO: parade?*/!rollData.attackerRoll?.particuliere) {
// TODO: attaquant doit jouer résistance et peut être désarmé p132 // TODO: attaquant doit jouer résistance et peut être désarmé p132
ChatUtility.createChatWithRollMode( ChatUtility.createChatWithRollMode(
{ content: `(à gérer) L'attaquant doit jouer résistance et peut être désarmé (p132)` }, { content: `(à gérer) L'attaquant doit jouer résistance et peut être désarmé (p132)` },
this.defender) this.defender)
} }
ChatUtility.createChatWithRollMode(
{ content: "<strong>Vous pouvez esquiver une deuxième fois!</strong>" },
this.defender);
} }
async _onDefenseNormale(rollData) { async _onDefenseNormale(rollData) {
console.log("RdDCombat._onDefenseNormale >>>", rollData); console.log("RdDCombat._onDefenseNormale >>>", rollData);
// console.log("RdDCombat._onEsquiveNormal >>>", rollData);
// await RdDRollResult.displayRollData(rollData, this.defender, 'chat-resultat-esquive.hbs');
// this.removeChatMessageActionsPasseArme(rollData.passeArme);
//TODO
await this.computeRecul(rollData); await this.computeRecul(rollData);
await this.computeDeteriorationArme(rollData); await this.computeDeteriorationArme(rollData);
await RdDRollResult.displayRollData(rollData, this.defender, 'chat-resultat-parade.hbs'); await RdDRollResult.displayRollData(rollData, this.defender, 'chat-resultat-parade.hbs');
this.removeChatMessageActionsPasseArme(rollData.passeArme); this.removeChatMessageActionsPasseArme(rollData.passeArme);
} }
async _onDefenseEchec(rollData) {
console.log("RdDCombat._onDefenseEchec >>>", rollData);
// console.log("RdDCombat._onEsquiveEchec >>>", rollData);
// await RdDRollResult.displayRollData(rollData, this.defender, 'chat-resultat-esquive.hbs');
// this.removeChatMessageActionsPasseArme(rollData.passeArme);
// this._sendMessageDefense(rollData.attackerRoll, rollData, { defense: true })
await RdDRollResult.displayRollData(rollData, this.defender, 'chat-resultat-parade.hbs');
this.removeChatMessageActionsPasseArme(rollData.passeArme);
this._sendMessageDefense(rollData.attackerRoll, rollData, { defense: true });
}
@@ -1214,7 +1204,7 @@ export class RdDCombat {
// Est-ce une parade normale? // Est-ce une parade normale?
if (defenderRoll.arme && attackerRoll && !defenderRoll.rolled.isPart) { if (defenderRoll.arme && attackerRoll && !defenderRoll.rolled.isPart) {
// Est-ce que l'attaque est une particulière en force ou une charge // Est-ce que l'attaque est une particulière en force ou une charge
if (defenderRoll.needResist || this._isForceOuCharge(attackerRoll)) { if (defenderRoll.needResist || this._isForceOuCharge(attackerRoll, defenderRoll.v2)) {
defenderRoll.show = defenderRoll.show || {} defenderRoll.show = defenderRoll.show || {}
@@ -1262,7 +1252,7 @@ export class RdDCombat {
finalLevel: Misc.toInt(defenderRoll.competence.system.niveau) - dmg, finalLevel: Misc.toInt(defenderRoll.competence.system.niveau) - dmg,
showDice: HIDE_DICE showDice: HIDE_DICE
}); });
defenderRoll.show.desarme = desarme.rolled.isEchec; defenderRoll.show.desarme = desarme.rolled.isEchec
} }
} }
} }
@@ -1270,41 +1260,19 @@ export class RdDCombat {
/* -------------------------------------------- */ /* -------------------------------------------- */
async computeRecul(defenderRoll) { // Calcul du recul (p. 132) async computeRecul(defenderRoll) { // Calcul du recul (p. 132)
if (!ReglesOptionnelles.isUsing('recul')) {
return
}
const attackerRoll = defenderRoll.attackerRoll; const attackerRoll = defenderRoll.attackerRoll;
if (ReglesOptionnelles.isUsing('recul') && this._isForceOuCharge(attackerRoll)) { if (this._isForceOuCharge(attackerRoll, defenderRoll.v2)) {
const impact = this._computeImpactRecul(attackerRoll); defenderRoll.show.recul = this.defender.encaisserRecul(this.attacker.getForce(), attackerRoll.dmg.dmgArme)
const rollRecul = await RdDResolutionTable.rollData({ caracValue: 10, finalLevel: impact });
if (rollRecul.rolled.isSuccess) {
defenderRoll.show.recul = 'encaisse';
} else if (rollRecul.rolled.isETotal || this._isReculCauseChute(impact)) {
defenderRoll.show.recul = 'chute';
await this.defender.setEffect(STATUSES.StatusProne, true);
}
else {
defenderRoll.show.recul = 'recul';
}
} }
} }
/* -------------------------------------------- */ _isForceOuCharge(attaque, isRollV2 = false /* TODO: delete roll V1 */) {
async _isReculCauseChute(impact) { return attaque.particuliere == 'force' || 'charge' == (isRollV2 ? attaque.tactique?.key : attaque.tactique)
const agilite = this.defender.getAgilite();
const chute = await RdDResolutionTable.rollData({ caracValue: agilite, finalLevel: impact });
return chute.rolled.isEchec;
} }
/* -------------------------------------------- */
_isForceOuCharge(attaque) {
return attaque.particuliere == 'force' || attaque.tactique == 'charge';
}
/* -------------------------------------------- */
_computeImpactRecul(attaque) {
const taille = this.defender.getTaille();
const force = this.attacker.getForce();
const dommages = attaque.arme.system.dommagesReels ?? attaque.arme.system.dommages;
return taille - (force + dommages);
}
/* -------------------------------------------- */ /* -------------------------------------------- */
async encaisser(attackerRoll, defenderRoll) { async encaisser(attackerRoll, defenderRoll) {
@@ -1314,12 +1282,16 @@ export class RdDCombat {
this._onEchecTotal(defenderRoll); this._onEchecTotal(defenderRoll);
} }
await this.doRollEncaissement(attackerRoll, defenderRoll);
this.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
}
async doRollEncaissement(attackerRoll, defenderRoll) {
if (Misc.isOwnerPlayer(this.defender)) { if (Misc.isOwnerPlayer(this.defender)) {
attackerRoll.attackerId = this.attackerId; attackerRoll.attackerId = this.attackerId;
attackerRoll.defenderTokenId = this.defenderToken.id; attackerRoll.defenderTokenId = this.defenderToken.id;
await this.computeRecul(defenderRoll); await this.computeRecul(defenderRoll);
await this.defender.encaisserDommages(attackerRoll, this.attacker, defenderRoll?.show, this.attackerToken, this.defenderToken); await this.defender.encaisserDommages(attackerRoll.dmg, this.attacker, defenderRoll?.show, this.attackerToken, this.defenderToken);
} }
else { // envoi à un GM: les joueurs n'ont pas le droit de modifier les personnages qu'ils ne possèdent pas else { // envoi à un GM: les joueurs n'ont pas le droit de modifier les personnages qu'ils ne possèdent pas
game.socket.emit(SYSTEM_SOCKET_ID, { game.socket.emit(SYSTEM_SOCKET_ID, {
@@ -1330,9 +1302,8 @@ export class RdDCombat {
attackerToken: this.attackerToken, attackerToken: this.attackerToken,
defenderToken: this.defenderToken defenderToken: this.defenderToken
} }
}); })
} }
this.removeChatMessageActionsPasseArme(attackerRoll.passeArme);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -1366,6 +1337,3 @@ export class RdDCombat {
} }
} }
function newFunction(attackerRoll) {
return attackerRoll.diffLibre;
}

View File

@@ -86,6 +86,7 @@ import { RdDCombatManager, RdDCombat } from "./rdd-combat.js"
import { Migrations } from './migrations.js' import { Migrations } from './migrations.js'
import RollDialog from "./roll/roll-dialog.mjs" import RollDialog from "./roll/roll-dialog.mjs"
import ChatRollResult from "./roll/chat-roll-result.mjs"
/** /**
* RdD system * RdD system
@@ -292,6 +293,7 @@ export class SystemReveDeDragon {
TMRRencontres.init() TMRRencontres.init()
ExportScriptarium.init() ExportScriptarium.init()
RollDialog.init() RollDialog.init()
ChatRollResult.init()
} }
initSettings() { initSettings() {
@@ -346,18 +348,7 @@ export class SystemReveDeDragon {
}) })
} }
static async setupAccueil() {
let exists = game.scenes.find(j => j.name == "Accueil RdD");
if (!exists) {
const scenes = await SystemCompendiums.loadCompendium("foundryvtt-reve-de-dragon.scenes-rdd")
let newDocuments = scenes.filter(i => i.name == "Accueil RdD");
await game.scenes.documentClass.create(newDocuments);
game.scenes.find(i => i.name == "Accueil RdD").activate();
}
}
async onReady() { async onReady() {
/* -------------------------------------------- */ /* -------------------------------------------- */
/* Foundry VTT Initialization */ /* Foundry VTT Initialization */
/* -------------------------------------------- */ /* -------------------------------------------- */
@@ -376,7 +367,8 @@ export class SystemReveDeDragon {
StatusEffects.onReady() StatusEffects.onReady()
RdDDice.onReady() RdDDice.onReady()
RollDialog.onReady() RollDialog.onReady()
RdDStatBlockParser.parseStatBlock() ChatRollResult.onReady()
/* -------------------------------------------- */ /* -------------------------------------------- */
/* Affiche/Init le calendrier */ /* Affiche/Init le calendrier */
game.system.rdd.calendrier.display() game.system.rdd.calendrier.display()
@@ -389,7 +381,17 @@ export class SystemReveDeDragon {
}) })
} }
SystemReveDeDragon.setupAccueil() this.setupAccueil()
}
async setupAccueil() {
let exists = game.scenes.find(j => j.name == "Accueil RdD");
if (!exists) {
const scenes = await SystemCompendiums.loadCompendium("foundryvtt-reve-de-dragon.scenes-rdd")
let newDocuments = scenes.filter(i => i.name == "Accueil RdD");
await game.scenes.documentClass.create(newDocuments);
game.scenes.find(i => i.name == "Accueil RdD").activate();
}
} }
/* -------------------------------------------- */ /* -------------------------------------------- */

View File

@@ -157,7 +157,7 @@ export class RdDResolutionTable {
/* -------------------------------------------- */ /* -------------------------------------------- */
static significativeRequise(chances) { static significativeRequise(chances) {
chances.roll = Math.floor(chances.score / 2); chances.roll = Math.min(chances.part + 1, chances.sign)
foundry.utils.mergeObject(chances, reussites.find(x => x.code == 'sign'), { overwrite: true }); foundry.utils.mergeObject(chances, reussites.find(x => x.code == 'sign'), { overwrite: true });
} }
@@ -188,7 +188,7 @@ export class RdDResolutionTable {
static computeReussite(chances, roll, diviseur) { static computeReussite(chances, roll, diviseur) {
const reussite = reussites.find(x => x.condition(chances, roll)) const reussite = reussites.find(x => x.condition(chances, roll))
if (diviseur > 1 && reussite.isSuccess) { if (diviseur > 1 && reussite.isSuccess) {
if (chances > roll * diviseur){ if (chances > roll * diviseur) {
return reussiteInsuffisante return reussiteInsuffisante
} }
} }

View File

@@ -66,12 +66,11 @@ export class RdDEncaisser extends Dialog {
/* -------------------------------------------- */ /* -------------------------------------------- */
performEncaisser(mortalite) { performEncaisser(mortalite) {
this.actor.encaisserDommages({ this.actor.encaisserDommages({
dmg: { total: Number(this.modifier),
total: Number(this.modifier), ajustement: Number(this.modifier),
ajustement: Number(this.modifier), encaisserSpecial: this.encaisserSpecial,
encaisserSpecial: this.encaisserSpecial, mortalite: mortalite,
mortalite: mortalite penetration: 0
}
}) })
} }
} }

View File

@@ -9,6 +9,7 @@ import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { ReglesOptionnelles } from "./settings/regles-optionnelles.js"; import { ReglesOptionnelles } from "./settings/regles-optionnelles.js";
import { Grammar } from "./grammar.js"; import { Grammar } from "./grammar.js";
import { ACTOR_TYPES } from "./constants.js"; import { ACTOR_TYPES } from "./constants.js";
import { RdDUtility } from "./rdd-utility.js";
/** /**
* Extend the base Dialog entity to select roll parameters * Extend the base Dialog entity to select roll parameters

View File

@@ -610,35 +610,33 @@ export class RdDUtility {
/* -------------------------------------------- */ /* -------------------------------------------- */
static async getLocalisation(type = 'personnage') { static async getLocalisation(type = 'personnage') {
let result = await RdDDice.rollTotal("1d20"); const loc = { result: await RdDDice.rollTotal("1d20")};
let txt = ""
if (type == 'personnage') { if (type == 'personnage') {
if (result <= 3) txt = "Jambe, genou, pied, jarret"; if (loc.result <= 3) loc.txt = "Jambe, genou, pied, jarret";
else if (result <= 7) txt = "Hanche, cuisse, fesse"; else if (loc.result <= 7) loc.txt = "Hanche, cuisse, fesse";
else if (result <= 9) txt = "Ventre, reins"; else if (loc.result <= 9) loc.txt = "Ventre, reins";
else if (result <= 12) txt = "Poitrine, dos"; else if (loc.result <= 12) loc.txt = "Poitrine, dos";
else if (result <= 14) txt = "Avant-bras, main, coude"; else if (loc.result <= 14) loc.txt = "Avant-bras, main, coude";
else if (result <= 18) txt = "Epaule, bras, omoplate"; else if (loc.result <= 18) loc.txt = "Epaule, bras, omoplate";
else if (result == 19) txt = "Tête"; else if (loc.result == 19) loc.txt = "Tête";
else if (result == 20) txt = "Tête (visage)"; else if (loc.result == 20) loc.txt = "Tête (visage)";
} else { } else {
if (result <= 7) txt = "Jambes/Pattes"; if (loc.result <= 7) loc.txt = "Jambes/Pattes";
else if (result <= 18) txt = "Corps"; else if (loc.result <= 18) loc.txt = "Corps";
else if (result <= 20) txt = "Tête"; else if (loc.result <= 20) loc.txt = "Tête";
} }
return loc
return { result: result, label: txt };
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async jetEncaissement(actor, rollData, armure, options = { showDice: HIDE_DICE }) { static async jetEncaissement(actor, dmg, armure, options = { showDice: HIDE_DICE }) {
const diff = Math.abs(rollData.diffLibre); const diff = Math.abs(dmg.diff)
let formula = RdDUtility.formuleEncaissement(diff, options) const formula = RdDUtility.formuleEncaissement(diff, options)
const roll = await RdDDice.roll(formula, options); const roll = await RdDDice.roll(formula, options);
RdDUtility.remplaceDeMinParDifficulte(roll, diff, options); RdDUtility.remplaceDeMinParDifficulte(roll, diff, options);
return await RdDUtility.prepareEncaissement(actor, rollData, roll, armure); return await RdDUtility.prepareEncaissement(actor, dmg, roll, armure);
} }
static remplaceDeMinParDifficulte(roll, diff, options) { static remplaceDeMinParDifficulte(roll, diff, options) {
@@ -661,7 +659,7 @@ export class RdDUtility {
} }
} }
static formuleEncaissement(diff, options) { static formuleEncaissement(diff) {
// Chaque dé fait au minimum la difficulté libre // Chaque dé fait au minimum la difficulté libre
if (ReglesOptionnelles.isUsing('degat-minimum-malus-libre')) { if (ReglesOptionnelles.isUsing('degat-minimum-malus-libre')) {
return `2d10min${diff}` return `2d10min${diff}`
@@ -670,25 +668,22 @@ export class RdDUtility {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static async prepareEncaissement(actor, rollData, roll, armure) { static async prepareEncaissement(actor, dmg, roll, armure) {
// La difficulté d'ataque s'ajoute aux dégâts const jetTotal = roll.total + dmg.total - armure
const bonusDegatsDiffLibre = ReglesOptionnelles.isUsing('degat-ajout-malus-libre') ? Math.abs(rollData.diffLibre ?? 0) : 0 const encaissement = RdDUtility._selectEncaissement(jetTotal, dmg.mortalite);
const jetTotal = roll.total + rollData.dmg.total - armure + bonusDegatsDiffLibre
const encaissement = RdDUtility._selectEncaissement(jetTotal, rollData.dmg.mortalite);
const over20 = Math.max(jetTotal - 20, 0); const over20 = Math.max(jetTotal - 20, 0);
encaissement.dmg = rollData.dmg encaissement.dmg = dmg
if (ReglesOptionnelles.isUsing('localisation-aleatoire')) { if (ReglesOptionnelles.isUsing('localisation-aleatoire')) {
encaissement.dmg.loc = rollData.dmg.loc ?? await RdDUtility.getLocalisation(actor.type) encaissement.dmg.loc = dmg.loc ?? await RdDUtility.getLocalisation(actor.type)
encaissement.dmg.loc.label = encaissement.dmg.loc.label ?? 'Corps;' encaissement.dmg.loc.label = encaissement.dmg.loc.label ?? 'Corps;'
} }
else { else {
encaissement.dmg.loc = { label: '' } encaissement.dmg.loc = { label: '' }
} }
encaissement.dmg.bonusDegatsDiffLibre = bonusDegatsDiffLibre encaissement.roll = roll
encaissement.roll = roll; encaissement.armure = armure
encaissement.armure = armure; encaissement.penetration = dmg.penetration
encaissement.penetration = rollData.arme?.system.penetration ?? 0; encaissement.total = jetTotal
encaissement.total = jetTotal;
encaissement.vie = await RdDUtility._evaluatePerte(encaissement.vie, over20); encaissement.vie = await RdDUtility._evaluatePerte(encaissement.vie, over20);
encaissement.endurance = await RdDUtility._evaluatePerte(encaissement.endurance, over20); encaissement.endurance = await RdDUtility._evaluatePerte(encaissement.endurance, over20);
return encaissement; return encaissement;

View File

@@ -0,0 +1,180 @@
import { ChatUtility } from "../chat-utility.js"
import RollDialog from "./roll-dialog.mjs"
import { RdDCarac } from "../rdd-carac.js"
import { RdDCombat } from "../rdd-combat.js"
import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs"
import { RdDResolutionTable } from "../rdd-resolution-table.js"
export default class ChatRollResult {
static init() {
ChatRollResult.instance = new ChatRollResult()
Hooks.on('renderChatLog', (log, html, chatLog) => ChatRollResult.instance.chatListeners(html))
}
static onReady() {
foundry.applications.handlebars.loadTemplates({
'partial-appel-chance': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-appel-chance.hbs',
'partial-encaissement': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-encaissement.hbs',
'partial-recul-choc': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-recul-choc.hbs',
'partial-info-appel-moral': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-info-appel-moral.hbs',
})
}
async display(roll) {
this.prepareDisplay(roll)
const chatMessage = await ChatUtility.createChatWithRollMode(
{
content: await this.buildRollHtml(roll)
},
roll.active.actor,
roll.current?.rollmode?.key
)
const save = RollDialog.saveParts(roll)
ChatUtility.setMessageData(chatMessage, 'rollData', save)
return chatMessage
}
prepareDisplay(roll) {
roll.done = roll.done || {}
roll.show = roll.show || {}
roll.show.chance = this.isAppelChancePossible(roll)
roll.show.encaissement = this.isShowEncaissement(roll)
roll.show.recul = this.getReculChoc(roll)
}
isAppelChancePossible(roll) {
return roll.active.actor.isPersonnage() &&
roll.rolled.isEchec &&
RdDCarac.isActionPhysique(roll.current.carac?.key)
}
isShowEncaissement(roll) {
return roll.rolled.isEchec &&
roll.attackerRoll?.dmg.mortalite != 'empoignade'
}
getReculChoc(roll, defender = roll.active.actor, attacker = roll.opponent.actor) {
const attaque = roll.attackerRoll
if (attaque &&
(roll.rolled.isEchec || !roll.current.defense.isEsquive) &&
(attaque.particuliere == 'force' || 'charge' == attaque.tactique?.key)) {
const taille = defender.system.carac.taille.value
const impact = attacker.system.carac.force.value + roll.attackerRoll?.dmg.dmgArme
return {
raison: 'charge' == attaque.tactique?.key ? 'charge' : 'particulière en force',
taille: taille,
impact: impact,
chances: RdDResolutionTable.computeChances(10, taille-impact).norm,
diff: taille - impact
}
}
return undefined
}
async buildRollHtml(roll) {
const template = `systems/foundryvtt-reve-de-dragon/templates/roll/result/chat-${roll.type.current}.hbs`
return await foundry.applications.handlebars.renderTemplate(template, roll)
}
async chatListeners(html) {
$(html).on("click", '.appel-chance', event => this.onClickAppelChance(event))
$(html).on("click", '.appel-destinee', event => this.onClickAppelDestinee(event))
$(html).on("click", '.encaissement', event => this.onClickEncaissement(event))
$(html).on("click", '.resister-recul', event => this.onClickRecul(event))
}
getCombat(roll) {
switch (roll.type.current) {
case ROLL_TYPE_DEFENSE:
return RdDCombat.rddCombatForAttackerAndDefender(roll.ids.opponentId, roll.ids.opponentTokenId, roll.ids.actorTokenId)
case ROLL_TYPE_ATTAQUE:
return RdDCombat.rddCombatForAttackerAndDefender(roll.ids.actorId, roll.ids.actorTokenId, roll.ids.opponentId)
}
return undefined
}
async updateChatMessage(chatMessage, savedRoll) {
ChatUtility.setMessageData(chatMessage, 'rollData', savedRoll)
const copy = foundry.utils.duplicate(savedRoll)
RollDialog.loadRollData(copy)
this.prepareDisplay(copy)
chatMessage.update({ content: await this.buildRollHtml(copy) })
chatMessage.render(true)
}
onClickAppelChance(event) {
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData')
const actor = game.actors.get(savedRoll.ids.actorId)
actor.rollAppelChance(
() => this.onAppelChanceSuccess(savedRoll, chatMessage),
() => this.onAppelChanceEchec(savedRoll, chatMessage))
event.preventDefault()
}
onAppelChanceSuccess(savedRoll, chatMessage) {
const reRoll = foundry.utils.duplicate(savedRoll)
reRoll.type.retry = true
const callbacks = [r => ChatUtility.removeChatMessageId(chatMessage.id)]
// TODO: annuler les effets
switch (reRoll.type.current) {
case ROLL_TYPE_DEFENSE:
this.getCombat(reRoll)?.doRollDefense(reRoll, callbacks)
break
case ROLL_TYPE_ATTAQUE:
// TODO
this.getCombat(reRoll)?.doRollAttaque(reRoll, callbacks)
break
default: {
RollDialog.create(reRoll, { callbacks: callbacks })
}
}
}
async onAppelChanceEchec(savedRoll, chatMessage) {
savedRoll.type.retry = true
await this.updateChatMessage(chatMessage, savedRoll)
}
onClickAppelDestinee(event) {
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData')
const actor = game.actors.get(savedRoll.ids.actorId)
actor.appelDestinee(async () => {
const reRoll = foundry.utils.duplicate(savedRoll)
reRoll.type.retry = true
RdDResolutionTable.significativeRequise(reRoll.rolled)
await this.updateChatMessage(chatMessage, reRoll)
})
}
async onClickEncaissement(event) {
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData')
const attaque = savedRoll.attackerRoll
const defender = game.actors.get(savedRoll.ids.actorId)
const attacker = game.actors.get(savedRoll.ids.opponentId)
const defenderToken = savedRoll.ids.actorTokenId ? canvas.tokens.get(savedRoll.ids.actorTokenId) : undefined
const attackerToken = savedRoll.ids.opponentTokenId ? canvas.tokens.get(savedRoll.ids.opponentTokenId) : undefined
await defender?.encaisserDommages(attaque.dmg, attacker, undefined, attackerToken, defenderToken)
savedRoll.done.encaissement = true
await this.updateChatMessage(chatMessage, savedRoll)
}
async onClickRecul(event) {
const chatMessage = ChatUtility.getChatMessage(event)
const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData')
const defender = game.actors.get(savedRoll.ids.actorId)
const attacker = game.actors.get(savedRoll.ids.opponentId)
savedRoll.done.recul = await defender.encaisserRecul(attacker.getForce(), savedRoll.attackerRoll.dmg.dmgArme)
// const reculChoc = this.getReculChoc(savedRoll, defender, attacker)
await this.updateChatMessage(chatMessage, savedRoll)
}
}

View File

@@ -41,9 +41,7 @@ export class RollBasicParts {
initFrom(rollData) { initFrom(rollData) {
return { return {
selected: {}, selected: {},
type: { type: rollData.type,
current: rollData.type.current
},
ids: { ids: {
sceneId: rollData.ids.sceneId, sceneId: rollData.ids.sceneId,
actorId: rollData.active.id, actorId: rollData.active.id,

View File

@@ -1,15 +0,0 @@
export class RollChatResult {
constructor(rollType) {
this.rollType = rollType
}
async display(rollData) {
const template = this.rollType.chatResultTemplate()
const chatContent = await renderTemplate(template, rollData)
ChatMessage.create({
whisper: ChatUtility.getOwners(this),
content: chatContent
})
}
}

View File

@@ -6,13 +6,12 @@ import { ReglesOptionnelles } from "../settings/regles-optionnelles.js";
import { PART_OEUVRE } from "./roll-part-oeuvre.mjs"; import { PART_OEUVRE } from "./roll-part-oeuvre.mjs";
import { RdDItemArme } from "../item/arme.js"; import { RdDItemArme } from "../item/arme.js";
import { RdDBonus } from "../rdd-bonus.js"; import { RdDBonus } from "../rdd-bonus.js";
import { ROLL_TYPE_ATTAQUE } from "./roll-constants.mjs";
/* -------------------------------------------- */ /* -------------------------------------------- */
export class RollDialogAdapter { export class RollDialogAdapter {
async rollDice(rollData, rollTitle) { static async rollDice(rollData, rollTitle) {
const chances = this.computeChances({ const chances = RollDialogAdapter.computeChances({
carac: rollData.current.carac.value, carac: rollData.current.carac.value,
diff: rollData.current.totaldiff, diff: rollData.current.totaldiff,
bonus: rollData.current.bonus, bonus: rollData.current.bonus,
@@ -21,13 +20,13 @@ export class RollDialogAdapter {
rollMode: rollData.current.rollmode.key rollMode: rollData.current.rollmode.key
}) })
const rolled = await this.rollChances(rollData, chances) const rolled = await RollDialogAdapter.rollChances(rollData, chances)
this.adjustRollDataForV1(rollData, rolled, rollTitle) RollDialogAdapter.adjustRollDataForV1(rollData, rolled, rollTitle)
return rolled return rolled
} }
computeChances({ carac, diff, bonus, sign, showDice, rollMode }) { static computeChances({ carac, diff, bonus, sign, showDice, rollMode }) {
const chances = foundry.utils.duplicate(RdDResolutionTable.computeChances(carac, diff)) const chances = foundry.utils.duplicate(RdDResolutionTable.computeChances(carac, diff))
RdDResolutionTable._updateChancesWithBonus(chances, bonus, diff) RdDResolutionTable._updateChancesWithBonus(chances, bonus, diff)
RdDResolutionTable._updateChancesFactor(chances, sign) RdDResolutionTable._updateChancesFactor(chances, sign)
@@ -36,7 +35,7 @@ export class RollDialogAdapter {
return chances return chances
} }
async rollChances(rollData, chances) { static async rollChances(rollData, chances) {
const rolled = await RdDResolutionTable.rollChances(chances, const rolled = await RdDResolutionTable.rollChances(chances,
rollData.current.sign, rollData.current.sign,
rollData.current.resultat) rollData.current.resultat)
@@ -47,7 +46,7 @@ export class RollDialogAdapter {
return rolled return rolled
} }
adjustRollDataForV1(rollData, rolled, rollTitle) { static adjustRollDataForV1(rollData, rolled, rollTitle) {
// temporaire pour être homogène roll v1 // temporaire pour être homogène roll v1
rollData.alias = rollData.active.actor.getAlias() rollData.alias = rollData.active.actor.getAlias()
// pour experience // pour experience

View File

@@ -25,7 +25,7 @@ import { RollPartMeditation } from "./roll-part-meditation.mjs";
import { RollPartMoral } from "./roll-part-moral.mjs"; import { RollPartMoral } from "./roll-part-moral.mjs";
import { RollPartOpponent } from "./roll-part-opponent.mjs"; import { RollPartOpponent } from "./roll-part-opponent.mjs";
import { RollPartSurEnc } from "./roll-part-surenc.mjs"; import { RollPartSurEnc } from "./roll-part-surenc.mjs";
import { RollPartTricher } from "./roll-part-tricher.mjs"; import { PART_TRICHER, RollPartTricher } from "./roll-part-tricher.mjs";
import { RollPartTache } from "./roll-part-tache.mjs"; import { RollPartTache } from "./roll-part-tache.mjs";
import { RollPartOeuvre } from "./roll-part-oeuvre.mjs"; import { RollPartOeuvre } from "./roll-part-oeuvre.mjs";
import { RollPartSort } from "./roll-part-sort.mjs"; import { RollPartSort } from "./roll-part-sort.mjs";
@@ -38,7 +38,8 @@ import { RollPartDefense } from "./roll-part-defense.mjs";
import { RollDialogAdapter } from "./roll-dialog-adapter.mjs"; import { RollDialogAdapter } from "./roll-dialog-adapter.mjs";
import { ROLLDIALOG_SECTION } from "./roll-part.mjs"; import { ROLLDIALOG_SECTION } from "./roll-part.mjs";
import { ROLL_TYPE_COMP } from "./roll-constants.mjs"; import { ROLL_TYPE_COMP } from "./roll-constants.mjs";
import { RollChatResult } from "./roll-chat-result.mjs"; import ChatRollResult from "./chat-roll-result.mjs";
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api
const doNothing = (dialog) => { } const doNothing = (dialog) => { }
@@ -48,11 +49,12 @@ const ALL_ROLL_TYPES = [
new RollTypeTache(), new RollTypeTache(),
new RollTypeAttaque(), new RollTypeAttaque(),
new RollTypeDefense(), new RollTypeDefense(),
// new RollTypeResistance ??
new RollTypeSort(), new RollTypeSort(),
new RollTypeMeditation(), new RollTypeMeditation(),
new RollTypeOeuvre(), new RollTypeOeuvre(),
new RollTypeJeu(), new RollTypeJeu(),
// new RollTypeResistance ??
// new RollTypeFixedCarac ??
] ]
const BASIC_PARTS = new RollBasicParts() const BASIC_PARTS = new RollBasicParts()
@@ -182,6 +184,8 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
'roll-button': 'systems/foundryvtt-reve-de-dragon/templates/roll/roll-button.hbs', 'roll-button': 'systems/foundryvtt-reve-de-dragon/templates/roll/roll-button.hbs',
}) })
ChatRollResult.onReady()
foundry.applications.handlebars.loadTemplates(ALL_ROLL_TYPES.map(m => m.template)) foundry.applications.handlebars.loadTemplates(ALL_ROLL_TYPES.map(m => m.template))
foundry.applications.handlebars.loadTemplates(ROLL_PARTS.map(p => p.template)) foundry.applications.handlebars.loadTemplates(ROLL_PARTS.map(p => p.template))
ROLL_PARTS.forEach(p => p.onReady()) ROLL_PARTS.forEach(p => p.onReady())
@@ -246,9 +250,52 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
// rien pour l'instant // rien pour l'instant
} }
/** pre-configure les paramètres des différentes parties de la fenêtre (par exemple, prépare les listes de caractéristiques/compétences */
static $prepareRollData(rollData) {
rollData.current = rollData.current ?? {}
rollData.selected = rollData.selected ?? {}
rollData.type = rollData.type ?? {}
rollData.type.retry = rollData.type.retry ?? false
BASIC_PARTS.restore(rollData)
const potential = ALL_ROLL_TYPES.find(m => m.code == rollData.type.current)?.code
const allowed = rollData.type.retry && potential
? [potential]
: (rollData.type.allowed ?? ALL_ROLL_TYPES.filter(m => m.isAllowed(rollData) && m.visible(rollData)).map(m => m.code))
const rollType = allowed.find(c => c == rollData.type.current) ?? (allowed.length > 0 ? allowed[0].code : ROLL_TYPE_COMP);
rollData.type.allowed = allowed
rollData.type.current = rollType
ALL_ROLL_TYPES.find(m => m.code == rollType).setRollDataType(rollData)
rollData.refs = foundry.utils.mergeObject(rollData.refs ?? {}, Object.fromEntries(ROLL_PARTS.map(p => [p.code, {}])));
rollData.options = rollData.options ?? { rollMode: game.settings.get("core", "rollMode") }
ROLL_PARTS.forEach(p => p.initialize(rollData))
ROLL_PARTS.forEach(p => p.restore(rollData))
ROLL_PARTS.filter(p => p.isValid(rollData))
.forEach(p => {
p.loadRefs(rollData)
p.prepareContext(rollData)
})
return rollData
}
static saveParts(rollData) {
const target = BASIC_PARTS.initFrom(rollData)
ROLL_PARTS.filter(p => p.isActive(rollData))
.forEach(p => p.storeClean(rollData, target))
target.attackerRoll = rollData.attackerRoll
target.rolled = rollData.rolled
target.result = rollData.result
target.done = target.done ?? {}
return target
}
constructor(rollData, rollOptions) { constructor(rollData, rollOptions) {
super() super()
this.rollData = rollData
this.rollData = RollDialog.$prepareRollData(rollData)
this.rollOptions = { this.rollOptions = {
callbacks: [ callbacks: [
async r => await r.active.actor.appliquerAjoutExperience(r), async r => await r.active.actor.appliquerAjoutExperience(r),
@@ -258,37 +305,8 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
customChatMessage: rollOptions.customChatMessage, customChatMessage: rollOptions.customChatMessage,
onRollDone: rollOptions.onRollDone ?? doNothing onRollDone: rollOptions.onRollDone ?? doNothing
} }
this.$loadParts() this.chatRollResult = new ChatRollResult();
} this.selectType()
/** pre-configure les paramètres des différentes parties de la fenêtre (par exemple, prépare les listes de caractéristiques/compétences */
$loadParts() {
const rollData = this.rollData;
rollData.current = rollData.current ?? {}
rollData.selected = rollData.selected ?? {}
rollData.type = rollData.type ?? {}
rollData.type.retry = rollData.type.retry ?? false
BASIC_PARTS.restore(rollData)
const loadedType = ALL_ROLL_TYPES.find(m => m.code == rollData.type?.current)?.code
const allowedTypes = ALL_ROLL_TYPES.filter(m => m.isAllowed(rollData) && m.visible(rollData)).map(m => m.code)
rollData.type.allowed = rollData.type.retry ? [loadedType] : rollData.type.allowed ?? ALL_ROLL_TYPES.map(m => m.code)
rollData.type.current = allowedTypes.find(m => m == rollData.type?.current) ?? (allowedTypes.length > 0 ? allowedTypes[0] : ROLL_TYPE_COMP)
this.getSelectedType().setRollDataType(rollData)
rollData.refs = this.$prepareRefs(rollData)
rollData.options = rollData.options ?? { showDice: true, rollMode: game.settings.get("core", "rollMode") }
ROLL_PARTS.forEach(p => p.initialize(rollData))
ROLL_PARTS.forEach(p => p.restore(rollData))
ROLL_PARTS.filter(p => p.isValid(rollData))
.forEach(p => {
p.loadRefs(rollData)
p.prepareContext(rollData)
})
this.selectType();
} }
selectType() { selectType() {
@@ -301,23 +319,21 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
ROLL_PARTS.find(it => it.code == PART_COMP).filterComps(this.rollData) ROLL_PARTS.find(it => it.code == PART_COMP).filterComps(this.rollData)
} }
$prepareRefs(rollData) {
return foundry.utils.mergeObject(rollData.refs ?? {}, Object.fromEntries(ROLL_PARTS.map(p => [p.code, {}]))); static getActiveParts(rollData) {
return ROLL_PARTS.filter(p => p.isActive(rollData))
} }
$saveParts() { // get title() {
const target = BASIC_PARTS.initFrom(this.rollData) // return this.rollData.title ?? `Jet de dés de ${this.rollData.active.actor.name}`
ROLL_PARTS.filter(p => p.isActive(this.rollData)) // }
.forEach(p => p.store(this.rollData, target))
return target
}
getActiveParts() { rollTitle(rollData) {
return ROLL_PARTS.filter(p => p.isActive(this.rollData)) return rollData.label ?? ROLL_PARTS
} .filter(it => it.section == ROLLDIALOG_SECTION.ACTION)
.filter(it => it.isActive(rollData))
get title() { .map(it => it.title(rollData))
return this.rollData.title ?? `Jet de dés de ${this.rollData.active.actor.name}` .reduce(Misc.joining(' '))
} }
async _onRender(context, options) { async _onRender(context, options) {
@@ -340,13 +356,13 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
)) ))
Promise.all( Promise.all(
this.getActiveParts().map(async p => await p._onRender(this, context, options)) RollDialog.getActiveParts(this.rollData).map(async p => await p._onRender(this, context, options))
) )
} }
getAjustements() { static getAjustements(rollData) {
return this.getActiveParts() return RollDialog.getActiveParts(rollData)
.map(p => p.getAjustements(this.rollData)) .map(p => p.getAjustements(rollData))
.reduce((a, b) => a.concat(b)) .reduce((a, b) => a.concat(b))
.sort((a, b) => a.diff == undefined ? 1 : b.diff == undefined ? -1 : 0) .sort((a, b) => a.diff == undefined ? 1 : b.diff == undefined ? -1 : 0)
} }
@@ -360,19 +376,19 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
const types = ALL_ROLL_TYPES.filter(m => m.isAllowed(rollData) && m.visible(rollData)) const types = ALL_ROLL_TYPES.filter(m => m.isAllowed(rollData) && m.visible(rollData))
.map(m => m.toTypeData(rollData)) .map(m => m.toTypeData(rollData))
BASIC_PARTS.loadSurprises(this.rollData, this.getSelectedType().code) BASIC_PARTS.loadSurprises(rollData, this.getSelectedType().code)
this.rollData.type.label = this.getSelectedType()?.title(this.rollData) rollData.type.label = this.getSelectedType()?.title(rollData)
//TOCHECK: set type.label ? //TOCHECK: set type.label ?
const visibleRollParts = this.getActiveParts() const visibleRollParts = RollDialog.getActiveParts(rollData)
visibleRollParts.forEach(p => p.applyExternalImpacts(visibleRollParts, rollData)) visibleRollParts.forEach(p => p.applyExternalImpacts(visibleRollParts, rollData))
this.setSpecialComp(visibleRollParts); this.setSpecialComp(visibleRollParts);
visibleRollParts.forEach(p => p.prepareContext(rollData)) visibleRollParts.forEach(p => p.prepareContext(rollData))
this.calculAjustements() RollDialog.calculAjustements(rollData)
const templates = this.getActiveParts().map(p => p.toTemplateData()) const templates = RollDialog.getActiveParts(rollData).map(p => p.toTemplateData())
const context = await super._prepareContext() const context = await super._prepareContext()
return foundry.utils.mergeObject( return foundry.utils.mergeObject(
{ {
@@ -386,16 +402,16 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
const specialComp = visibleRollParts.map(p => p.getSpecialComp(this.rollData)) const specialComp = visibleRollParts.map(p => p.getSpecialComp(this.rollData))
.reduce((a, b) => a.concat(b)) .reduce((a, b) => a.concat(b))
if (specialComp.length > 0) { if (specialComp.length > 0) {
const rollPartComp = this.getActiveParts() const rollPartComp = RollDialog.getActiveParts(this.rollData)
.find(it => it.code == PART_COMP); .find(it => it.code == PART_COMP);
rollPartComp?.setSpecialComp(this.rollData, specialComp) rollPartComp?.setSpecialComp(this.rollData, specialComp)
} }
} }
calculAjustements() { static calculAjustements(rollData) {
this.rollData.ajustements = this.getAjustements() rollData.ajustements = RollDialog.getAjustements(rollData)
this.rollData.ajustements.forEach(it => it.isDiff = it.diff != undefined) rollData.ajustements.forEach(it => it.isDiff = it.diff != undefined)
this.rollData.current.totaldiff = this.rollData.ajustements rollData.current.totaldiff = rollData.ajustements
.map(adj => adj.diff) .map(adj => adj.diff)
.filter(d => d != undefined) .filter(d => d != undefined)
.reduce(Misc.sum(), 0) .reduce(Misc.sum(), 0)
@@ -406,20 +422,26 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
} }
async roll() { async roll() {
this.calculAjustements() // ROLL_PARTS.filter(p => p.isActive(this.rollData))
const rollData = this.rollData // .forEach(p => p.validate(this.rollData))
console.info('Roll parts:', this.$saveParts())
const rolled = await this.$rollDice(rollData) const roll = RollDialog.saveParts(this.rollData)
rollData.rolled = rolled RollDialog.loadRollData(roll)
Promise.all(this.rollOptions.callbacks.map(async callback => await callback(rollData))) roll.current.resultat = this.rollData.current[PART_TRICHER]?.resultat ?? -1
if (!this.rollOptions.customChatMessage) { roll.rolled = await this.$rollDice(roll)
const rollChatResult = new RollChatResult(this.getSelectedType()) roll.result = this.getSelectedType(roll).getResult(roll)
await rollChatResult.display(this.rollData) console.info('RollDialog.roll:', roll)
rollData.active.actor.$onRollCompetence(this.rollData) await Promise.all(this.rollOptions.callbacks.map(async callback => await callback(roll)))
} await this.chatRollResult.display(roll)
this.rollOptions.onRollDone(this) this.rollOptions.onRollDone(this)
} }
static loadRollData(roll) {
RollDialog.$prepareRollData(roll)
RollDialog.calculAjustements(roll)
roll.v2 = true
}
async defaultCallback(rollData, rolled) { async defaultCallback(rollData, rolled) {
await rollData.active.actor.appliquerAjoutExperience(rollData) await rollData.active.actor.appliquerAjoutExperience(rollData)
@@ -427,15 +449,6 @@ export default class RollDialog extends HandlebarsApplicationMixin(ApplicationV2
} }
async $rollDice(rollData) { async $rollDice(rollData) {
const adapter = new RollDialogAdapter(ROLL_PARTS); return await RollDialogAdapter.rollDice(rollData, this.rollTitle(rollData))
return await adapter.rollDice(rollData, this.rollTitle(rollData));
}
rollTitle(rollData) {
return ROLL_PARTS
.filter(it => it.section == ROLLDIALOG_SECTION.ACTION)
.filter(it => it.isActive(rollData))
.map(it => it.title(rollData))
.reduce(Misc.joining(' '))
} }
} }

View File

@@ -1,10 +1,9 @@
import { RdDBonus } from "../rdd-bonus.js" import { RdDBonus } from "../rdd-bonus.js"
import { StatusEffects } from "../settings/status-effects.js" import { ReglesOptionnelles } from "../settings/regles-optionnelles.js"
import { ROLL_TYPE_ATTAQUE } from "./roll-constants.mjs" import { ROLL_TYPE_ATTAQUE } from "./roll-constants.mjs"
import { PART_CARAC } from "./roll-part-carac.mjs" import { PART_CARAC } from "./roll-part-carac.mjs"
import { PART_COMP } from "./roll-part-comp.mjs" import { PART_COMP } from "./roll-part-comp.mjs"
import { RollPartSelect } from "./roll-part-select.mjs" import { RollPartSelect } from "./roll-part-select.mjs"
import { PART_SIGN } from "./roll-part-sign.mjs"
import { ROLLDIALOG_SECTION } from "./roll-part.mjs" import { ROLLDIALOG_SECTION } from "./roll-part.mjs"
export const PART_ATTAQUE = 'attaque' export const PART_ATTAQUE = 'attaque'
@@ -28,6 +27,16 @@ export class RollPartAttaque extends RollPartSelect {
} }
} }
store(rollData, targetData) {
super.store(rollData, targetData)
this.getSaved(targetData).dmg = this.getCurrent(rollData).dmg
}
restore(rollData) {
super.restore(rollData)
this.getCurrent(rollData).dmg = this.getSaved(rollData).dmg
}
choices(refs) { return refs.attaques } choices(refs) { return refs.attaques }
static $extractAttaque(attaque, actor) { static $extractAttaque(attaque, actor) {
@@ -41,28 +50,7 @@ export class RollPartAttaque extends RollPartSelect {
prepareContext(rollData) { prepareContext(rollData) {
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
current.dmg = this.$dmgRollV2(rollData, current) current.dmg = RdDBonus.dmgRollV2(rollData, current)
}
$dmgRollV2(rollData, current) {
const actor = rollData.active.actor
const attaque = current.attaque
const arme = attaque.arme
const dmgArme = RdDBonus.dmgArme(arme, attaque.dommagesArme)
const dmg = {
total: 0,
dmgArme: dmgArme,
penetration: arme.penetration(),
dmgTactique: current.tactique?.dmg ?? 0,
dmgParticuliere: 0, // TODO RdDBonus._dmgParticuliere(rollData),
dmgSurprise: rollData.opponent?.surprise?.dmg ?? 0,
mortalite: RdDBonus.mortalite(current.dmg?.mortalite, arme.system.mortalite, rollData.opponent?.actor?.isEntite()),
dmgActor: RdDBonus.bonusDmg(actor, attaque.carac.key, dmgArme, attaque.forceRequise),
dmgForceInsuffisante: Math.min(0, actor.getForce() - attaque.forceRequise)
}
dmg.total = dmg.dmgSurprise + dmg.dmgTactique + dmg.dmgArme + dmg.dmgActor + dmg.dmgParticuliere + dmg.dmgForceInsuffisante
return dmg
} }
getAjustements(rollData) { getAjustements(rollData) {

View File

@@ -43,6 +43,7 @@ export class RollPartCarac extends RollPartSelect {
// ? refs.all.filter(it => allowed.includes(Grammar.toLowerCaseNoAccent(it.key))) // ? refs.all.filter(it => allowed.includes(Grammar.toLowerCaseNoAccent(it.key)))
? refs.all.filter(it => allowed.includes(it.key)) ? refs.all.filter(it => allowed.includes(it.key))
: refs.all : refs.all
this.$selectCarac(rollData)
} }
prepareContext(rollData) { prepareContext(rollData) {

View File

@@ -52,6 +52,7 @@ export class RollPartComp extends RollPartSelect {
// ? refs.all.filter(it => allowed.includes(Grammar.toLowerCaseNoAccent(it.label))) // ? refs.all.filter(it => allowed.includes(Grammar.toLowerCaseNoAccent(it.label)))
? refs.all.filter(it => allowed.includes(it.label)) ? refs.all.filter(it => allowed.includes(it.label))
: refs.all : refs.all
this.$selectComp(rollData)
} }
prepareContext(rollData) { prepareContext(rollData) {

View File

@@ -1,4 +1,5 @@
import { SYSTEM_RDD } from "../constants.js"; import { SYSTEM_RDD } from "../constants.js";
import { Misc } from "../misc.js";
import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs"; import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs";
const CONDITIONS = "conditions" const CONDITIONS = "conditions"
@@ -50,7 +51,7 @@ export class RollPartConditions extends RollPart {
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
current.min = game.settings.get(SYSTEM_RDD, this.settingMin()) current.min = game.settings.get(SYSTEM_RDD, this.settingMin())
current.max = game.settings.get(SYSTEM_RDD, this.settingMax()) current.max = game.settings.get(SYSTEM_RDD, this.settingMax())
current.value = current.value ?? 0 current.value = Misc.inRange(current.value ?? 0, current.min, current.max)
} }
getAjustements(rollData) { getAjustements(rollData) {
@@ -63,12 +64,13 @@ export class RollPartConditions extends RollPart {
async _onRender(rollDialog, context, options) { async _onRender(rollDialog, context, options) {
const input = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="${this.code}"]`) const input = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="${this.code}"]`)
input?.addEventListener("change", e => { input?.addEventListener("input", e => this.onInputChange(e, rollDialog))
const current = this.getCurrent(rollDialog.rollData) }
current.value = parseInt(e.currentTarget.value)
rollDialog.render() onInputChange(event, rollDialog) {
}) this.getCurrent(rollDialog.rollData).value = parseInt(event.currentTarget.value)
rollDialog.render()
} }
} }

View File

@@ -27,14 +27,14 @@ export class RollPartDefense extends RollPartSelect {
loadRefs(rollData) { loadRefs(rollData) {
const refs = this.getRefs(rollData) const refs = this.getRefs(rollData)
const attaque = rollData.attaque const attackerRoll = rollData.attackerRoll
const defenseur = rollData.active.actor const defenseur = rollData.active.actor
refs.isDistance = [ATTAQUE_TYPE.TIR, ATTAQUE_TYPE.LANCER].find(it => it == attaque?.main) refs.isDistance = [ATTAQUE_TYPE.TIR, ATTAQUE_TYPE.LANCER].find(it => it == attackerRoll?.main)
const esquives = refs.isDistance == ATTAQUE_TYPE.TIR ? [] : defenseur.getCompetencesEsquive() const esquives = refs.isDistance == ATTAQUE_TYPE.TIR ? [] : defenseur.getCompetencesEsquive()
.map(it => RollPartDefense.$extractEsquive(it, defenseur)) .map(it => RollPartDefense.$extractEsquive(it, defenseur))
const parades = defenseur.items.filter(it => it.isParade() && (!refs.isDistance || it.isBouclier())) const parades = defenseur.items.filter(it => it.isParade() && (!refs.isDistance || it.isBouclier()))
.map(it => RollPartDefense.$extractParade(it, attaque?.attaque.arme, defenseur)) .map(it => RollPartDefense.$extractParade(it, attackerRoll?.attaque.arme, defenseur))
refs.defenses = [...esquives, ...parades].filter(it => it != undefined) refs.defenses = [...esquives, ...parades].filter(it => it != undefined)
this.$selectDefense(rollData) this.$selectDefense(rollData)
@@ -48,7 +48,8 @@ export class RollPartDefense extends RollPartSelect {
// TODO: carac pour créatures // TODO: carac pour créatures
carac: defenseur.isPersonnage() ? CARACS.DEROBEE : esquive.name, carac: defenseur.isPersonnage() ? CARACS.DEROBEE : esquive.name,
verb: "esquive", verb: "esquive",
comp: esquive comp: esquive,
isEsquive: true
} }
} }
@@ -66,7 +67,8 @@ export class RollPartDefense extends RollPartSelect {
comp: comp, comp: comp,
arme: armeDefense, arme: armeDefense,
forceRequise: armeDefense ? RdDItemArme.valeurMain(armeDefense.system.force ?? 0, RdDItemArme.getMainAttaque(comp)) : 0, forceRequise: armeDefense ? RdDItemArme.valeurMain(armeDefense.system.force ?? 0, RdDItemArme.getMainAttaque(comp)) : 0,
typeParade: armeAttaque ? RdDItemArme.defenseArmeParade(armeDefense, armeAttaque) : 'norm' typeParade: armeAttaque ? RdDItemArme.defenseArmeParade(armeDefense, armeAttaque) : 'norm',
isEsquive: false
} }
} }
@@ -111,7 +113,7 @@ export class RollPartDefense extends RollPartSelect {
isArmeDisparate(rollData) { isArmeDisparate(rollData) {
const armeDefense = this.getCurrent(rollData).arme const armeDefense = this.getCurrent(rollData).arme
if (armeDefense) { if (armeDefense) {
const armeAttaque = rollData.attaque?.attaque.arme const armeAttaque = rollData.attackerRoll?.attaque.arme
return RdDItemArme.defenseArmeParade(armeAttaque, armeDefense) == 'sign' return RdDItemArme.defenseArmeParade(armeAttaque, armeDefense) == 'sign'
} }
return false return false
@@ -120,12 +122,12 @@ export class RollPartDefense extends RollPartSelect {
getDiffDefense(rollData) { getDiffDefense(rollData) {
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
const refs = this.getRefs(rollData) const refs = this.getRefs(rollData)
if (refs.isDistance || !rollData.attaque) { if (refs.isDistance || !rollData.attackerRoll) {
// Déterminer la difficulté de parade // Déterminer la difficulté de parade
return { diff: 0, type: DIFF.LIBRE } return { diff: 0, type: DIFF.LIBRE }
} }
else { else {
return { diff: rollData.attaque.diff ?? 0, type: DIFF.DEFENSE } return { diff: rollData.attackerRoll.diff ?? 0, type: DIFF.DEFENSE }
} }
} }
} }

View File

@@ -1,5 +1,6 @@
import { DIFF, DIFFS, ROLL_TYPE_MEDITATION, ROLL_TYPE_OEUVRE, ROLL_TYPE_SORT, ROLL_TYPE_TACHE } from "./roll-constants.mjs"; import { DIFF, DIFFS, ROLL_TYPE_MEDITATION, ROLL_TYPE_OEUVRE, ROLL_TYPE_SORT, ROLL_TYPE_TACHE } from "./roll-constants.mjs";
import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs"; import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs";
import { Misc } from "../misc.js";
export const PART_DIFF = "diff" export const PART_DIFF = "diff"
@@ -41,8 +42,8 @@ export class RollPartDiff extends RollPart {
{ {
type: diffType.key, type: diffType.key,
label: diffType?.label ?? '', label: diffType?.label ?? '',
value: current.value ?? 0,
disabled: !diffType.libre, disabled: !diffType.libre,
value: Misc.inRange(current.value ?? 0, -10, diffType.max),
min: -10, min: -10,
max: diffType.max max: diffType.max
}, },
@@ -67,11 +68,12 @@ export class RollPartDiff extends RollPart {
async _onRender(rollDialog, context, options) { async _onRender(rollDialog, context, options) {
const input = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="${this.code}"]`) const input = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="${this.code}"]`)
input?.addEventListener("change", e => { input?.addEventListener("input", e => this.onInputChange(e, rollDialog))
this.getCurrent(rollDialog.rollData).value = parseInt(e.currentTarget.value)
rollDialog.render()
})
} }
onInputChange(event, rollDialog) {
this.getCurrent(rollDialog.rollData).value = parseInt(event.currentTarget.value)
rollDialog.render()
}
} }

View File

@@ -1,5 +1,4 @@
import { ITEM_TYPES } from "../constants.js" import { ITEM_TYPES } from "../constants.js"
import { Grammar } from "../grammar.js"
import { Misc } from "../misc.js" import { Misc } from "../misc.js"
import { CARACS } from "../rdd-carac.js" import { CARACS } from "../rdd-carac.js"
import { ROLL_TYPE_OEUVRE } from "./roll-constants.mjs" import { ROLL_TYPE_OEUVRE } from "./roll-constants.mjs"
@@ -59,6 +58,7 @@ export class RollPartOeuvre extends RollPartSelect {
label: oeuvre.name, label: oeuvre.name,
art: art, art: art,
caracs: art.caracs(oeuvre), caracs: art.caracs(oeuvre),
qualite: oeuvre.system.niveau,
value: -oeuvre.system.niveau, value: -oeuvre.system.niveau,
oeuvre: oeuvre, oeuvre: oeuvre,
comp: actor.getCompetence(art.competence(oeuvre)) comp: actor.getCompetence(art.competence(oeuvre))

View File

@@ -1,6 +1,7 @@
import { Misc } from "../misc.js"
import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs" import { ROLLDIALOG_SECTION, RollPart } from "./roll-part.mjs"
const PART_TRICHER = "tricher" export const PART_TRICHER = "tricher"
export class RollPartTricher extends RollPart { export class RollPartTricher extends RollPart {
@@ -11,21 +12,20 @@ export class RollPartTricher extends RollPart {
prepareContext(rollData) { prepareContext(rollData) {
const current = this.getCurrent(rollData) const current = this.getCurrent(rollData)
if (current.resultat == undefined) { current.resultat = Misc.inRange(current.resultat == undefined ? -1 : current.resultat, -1, 100)
current.resultat = -1
}
} }
getAjustements(rollData) { getAjustements(rollData) {
rollData.current.resultat = this.getCurrent(rollData).resultat
return [] return []
} }
async _onRender(rollDialog, context, options) { async _onRender(rollDialog, context, options) {
const input = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="${this.code}"]`) const input = rollDialog.element.querySelector(`roll-section[name="${this.code}"] input[name="${this.code}"]`)
input?.addEventListener("change", e => { input?.addEventListener("input", e => this.onInputChange(e, rollDialog))
this.getCurrent(rollDialog.rollData).resultat = parseInt(e.currentTarget.value) }
})
onInputChange(event, rollDialog) {
this.getCurrent(rollDialog.rollData).resultat = parseInt(event.currentTarget.value)
} }
} }

View File

@@ -1,5 +1,3 @@
import { Misc } from "../misc.js"
export const ROLLDIALOG_SECTION = { export const ROLLDIALOG_SECTION = {
ACTION: 'action', ACTION: 'action',
CARAC: 'carac', CARAC: 'carac',
@@ -53,7 +51,23 @@ export class RollPart {
} }
restore(rollData) { } restore(rollData) { }
store(rollData, targetData) { }
storeClean(rollData, targetData) {
this.store(rollData, targetData)
if (rollData.selected[this.code]) {
const toDelete = Object.entries(rollData.selected[this.code])
.map(([k, v]) => v == undefined ? k : undefined)
.filter(k => k != undefined)
toDelete.forEach(k => delete rollData.selected[this.code][k])
if (Object.keys(rollData.selected[this.code]).length == 0) {
delete rollData.selected[this.code]
}
}
}
store(rollData, targetData) {
this.setSaved(targetData, { key: this.getCurrent(rollData).key })
}
/** /**
* le texte à ajouter dans la barre de titre * le texte à ajouter dans la barre de titre
@@ -70,6 +84,9 @@ export class RollPart {
loadRefs(rollData) { } loadRefs(rollData) { }
prepareContext(rollData) { } prepareContext(rollData) { }
/** permet de sauvegarder dans rollData les informations (cas des champs edit) */
validate(rollData) {}
/** ---- cross roll-part filtering ---- */ /** ---- cross roll-part filtering ---- */
applyImpact(rollData, filter) { } applyImpact(rollData, filter) { }

View File

@@ -15,5 +15,14 @@ export class RollTypeOeuvre extends RollType {
onSelect(rollData) { onSelect(rollData) {
this.setDiffType(rollData, DIFF.AUCUN) this.setDiffType(rollData, DIFF.AUCUN)
} }
getResult(rollData){
const current = rollData.current[PART_OEUVRE]
const qualite = rollData.rolled.isSuccess ? current.qualite : Math.min(current.qualite, current.comp.system.niveau)
return {
qualite: qualite + rollData.rolled.ptQualite
}
}
} }

View File

@@ -52,4 +52,8 @@ export class RollType {
rollData.current[PART_DIFF].type = type rollData.current[PART_DIFF].type = type
this.setRollDataType(rollData) this.setRollDataType(rollData)
} }
getResult(rollData){
return undefined
}
} }

View File

@@ -3,9 +3,11 @@ import { Misc } from "../misc.js"
export const EXPORT_CSV_SCRIPTARIUM = 'export-csv-scriptarium' export const EXPORT_CSV_SCRIPTARIUM = 'export-csv-scriptarium'
export const ROLL_DIALOG_V2 = 'roll-dialog-v2' export const ROLL_DIALOG_V2 = 'roll-dialog-v2'
export const ROLL_DIALOG_V2_TEST = 'roll-dialog-v2-test'
const OPTIONS_AVANCEES = [ const OPTIONS_AVANCEES = [
{ group: 'Fenêtres', name: ROLL_DIALOG_V2, descr: "Utiliser les nouvelles fenêtres de jet", default: false }, { group: 'Fenêtres', name: ROLL_DIALOG_V2, descr: "Utiliser les nouvelles fenêtres de jet", default: false },
{ group: 'Fenêtres', name: ROLL_DIALOG_V2_TEST, descr: "Mode de test des nouvelles fenêtres", default: false },
{ group: 'Menus', name: EXPORT_CSV_SCRIPTARIUM, descr: "Proposer le menu d'export csv Scriptarium", default: false }, { group: 'Menus', name: EXPORT_CSV_SCRIPTARIUM, descr: "Proposer le menu d'export csv Scriptarium", default: false },
] ]
@@ -56,7 +58,7 @@ export class OptionsAvancees extends FormApplication {
return formData return formData
} }
static getSettingKey(name){ static getSettingKey(name) {
return `${SYSTEM_RDD}.${this._getId(name)}` return `${SYSTEM_RDD}.${this._getId(name)}`
} }

View File

@@ -13,6 +13,7 @@ const listeReglesOptionnelles = [
{ group: 'Règles de combat', name: 'localisation-aleatoire', descr: "Proposer une localisation aléatoire des blessures" }, { group: 'Règles de combat', name: 'localisation-aleatoire', descr: "Proposer une localisation aléatoire des blessures" },
{ group: 'Règles de combat', name: 'recul', descr: "Appliquer le recul en cas de particulière en force ou de charge" }, { group: 'Règles de combat', name: 'recul', descr: "Appliquer le recul en cas de particulière en force ou de charge" },
{ group: 'Règles de combat', name: 'acrobatie-pour-recul', descr: "L'acrobatie aide à ne pas chuter en cas de recul" , default: false },
{ group: 'Règles de combat', name: 'resistanceArmeParade', descr: "Faire le jet de résistance des armes lors de parades pouvant les endommager" }, { group: 'Règles de combat', name: 'resistanceArmeParade', descr: "Faire le jet de résistance des armes lors de parades pouvant les endommager" },
{ group: 'Règles de combat', name: 'deteriorationArmure', descr: "Tenir compte de la détérioration des armures" }, { group: 'Règles de combat', name: 'deteriorationArmure', descr: "Tenir compte de la détérioration des armures" },
{ group: 'Règles de combat', name: 'defenseurDesarme', descr: "Le défenseur peut être désarmé en parant une particulière en force ou une charge avec une arme autre qu'un bouclier" }, { group: 'Règles de combat', name: 'defenseurDesarme', descr: "Le défenseur peut être désarmé en parant une particulière en force ou une charge avec une arme autre qu'un bouclier" },

View File

@@ -16,7 +16,7 @@
{{/if}} {{/if}}
{{/unless}} {{/unless}}
</span> </span>
<ul class="item-list alterne-list"> <ul class="item-list alterne-list liste-equipement">
<li class="competence-header flexrow"> <li class="competence-header flexrow">
<span class="equipement-nom">Nom</span> <span class="equipement-nom">Nom</span>
{{#unless system.illimite}} {{#unless system.illimite}}

View File

@@ -15,7 +15,7 @@
&hyphen; Valeur: {{numberFormat calc.prixTotalEquipement decimals=2}} Sols &hyphen; Valeur: {{numberFormat calc.prixTotalEquipement decimals=2}} Sols
{{/if}} {{/if}}
</span> </span>
<ul class="item-list alterne-list"> <ul class="item-list alterne-list liste-equipement">
<li class="competence-header flexrow"> <li class="competence-header flexrow">
<span class="equipement-nom">Nom</span> <span class="equipement-nom">Nom</span>
<span class="equipement-detail-buttons">Q.</span> <span class="equipement-detail-buttons">Q.</span>

View File

@@ -36,7 +36,7 @@
{{/unless}} {{/unless}}
{{else}} {{else}}
{{#if (settings-get 'rdd-advanced-roll-dialog-v2')}} {{#if (settings-get 'rdd-advanced-roll-dialog-v2')}}
<a class='chat-card-button defense-button' <a class='chat-card-button button-defense'
data-attackerId='{{attackerId}}' data-attackerId='{{attackerId}}'
data-attackerTokenId='{{attackerToken.id}}' data-attackerTokenId='{{attackerToken.id}}'
data-defenderTokenId='{{defenderToken.id}}' data-defenderTokenId='{{defenderToken.id}}'
@@ -51,7 +51,7 @@
<br> <br>
{{else}} {{else}}
{{#each armes as |arme key|}} {{#each armes as |arme key|}}
<a class='chat-card-button parer-button' <a class='chat-card-button button-parade'
data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderToken.id}}' data-attackerTokenId='{{../attackerToken.id}}' data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderToken.id}}' data-attackerTokenId='{{../attackerToken.id}}'
data-armeid='{{arme._id}}'> data-armeid='{{arme._id}}'>
Parer avec {{arme.name}} Parer avec {{arme.name}}
@@ -67,7 +67,7 @@
<br> <br>
{{/each}} {{/each}}
{{#if mainsNues}} {{#if mainsNues}}
<a class='chat-card-button parer-button' <a class='chat-card-button button-parade'
data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}' data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'
data-armeid='{{arme._id}}' data-competence='{{arme.system.competence}}'> data-armeid='{{arme._id}}' data-competence='{{arme.system.competence}}'>
Parer à mains nues à {{diffLibre}}{{#if arme.nbUsage}} (Utilisations : {{arme.nbUsage}}){{/if}} Parer à mains nues à {{diffLibre}}{{#if arme.nbUsage}} (Utilisations : {{arme.nbUsage}}){{/if}}
@@ -76,7 +76,7 @@
{{/if}} {{/if}}
{{#if (ne attaqueCategorie 'tir')}} {{#if (ne attaqueCategorie 'tir')}}
{{#each esquives as |esquive key|}} {{#each esquives as |esquive key|}}
<a class='chat-card-button esquiver-button' <a class='chat-card-button button-esquive'
data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderToken.id}}' data-attackerTokenId='{{../attackerToken.id}}' data-attackerId='{{../attackerId}}' data-defenderTokenId='{{../defenderToken.id}}' data-attackerTokenId='{{../attackerToken.id}}'
data-compid='{{esquive._id}}' data-competence='{{esquive.name}}'> data-compid='{{esquive._id}}' data-competence='{{esquive.name}}'>
{{esquive.name}} {{esquive.name}}
@@ -92,7 +92,7 @@
{{/if}} {{/if}}
{{/if}} {{/if}}
{{/unless}} {{/unless}}
<a class='chat-card-button encaisser-button' <a class='chat-card-button button-encaisser'
data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'> data-attackerId='{{attackerId}}' data-defenderTokenId='{{defenderToken.id}}' data-attackerTokenId='{{attackerToken.id}}'>
Encaisser à {{plusMoins dmg.total}} Encaisser à {{plusMoins dmg.total}}
{{#if (eq dmg.mortalite 'non-mortel')~}} {{#if (eq dmg.mortalite 'non-mortel')~}}

View File

@@ -37,8 +37,8 @@
{{#if encaissement.dmg.dmgSurprise}} {{#if encaissement.dmg.dmgSurprise}}
<div>+dom surprise: {{plusMoins encaissement.dmg.dmgSurprise}}</div> <div>+dom surprise: {{plusMoins encaissement.dmg.dmgSurprise}}</div>
{{/if}} {{/if}}
{{#if encaissement.dmg.bonusDegatsDiffLibre}} {{#if encaissement.dmg.dmgDiffLibre}}
<div>+dom attaque: {{plusMoins encaissement.dmg.bonusDegatsDiffLibre}}</div> <div>+dom attaque: {{plusMoins encaissement.dmg.dmgDiffLibre}}</div>
{{/if}} {{/if}}
</div> </div>
</span> </span>

View File

@@ -15,7 +15,7 @@
{{>"systems/foundryvtt-reve-de-dragon/templates/item/partial-inventaire.hbs"}} {{>"systems/foundryvtt-reve-de-dragon/templates/item/partial-inventaire.hbs"}}
{{#if options.isOwned}} {{#if options.isOwned}}
<div class="flexcol"> <div class="flexcol">
<ul class="item-list alterne-list"> <ul class="item-list alterne-list liste-equipement">
<li class="competence-header flexrow"> <li class="competence-header flexrow">
<span class="equipement-nom">Nom</span> <span class="equipement-nom">Nom</span>
<span class="equipement-detail">Q.</span> <span class="equipement-detail">Q.</span>

View File

@@ -1,31 +1,53 @@
<h4>
{{log this}}
<img class="chat-icon" src="{{competence.img}}" data-tooltip="{{competence.name}}" />
{{alias}} esquive une attaque à {{diffLibre}}</h4>
<p>{{selectedCarac.label}}{{#unless (eq selectedCarac.label competence.name)}} / {{competence.name}}{{/unless}}</p>
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.hbs"}}
<hr>
<p>
{{#if rolled.isSuccess}}
<span>Attaque esquivée!</span>
{{#if rolled.isPart}}
<!-- TODO: cas de parade à mains nues, texte à modifier -->
<span><strong>{{alias}} peut faire une deuxième esquive!</strong></span>
{{/if}}
{{else}}
<span>L'esquive de {{alias}} a échoué!</span>
{{/if}}
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-info-appel-au-moral.hbs"}}
</p>
{{#if attackerRoll.tactique}} {{log this}}
<p> <div class="roll-chat">
{{#if (eq attackerRoll.tactique 'charge')}} <div class="chat-img">
<img class="chat-icon" src="icons/svg/thrust.svg" data-tooltip="charge" height="32" width="32" /> <img src="{{active.img}}" data-tooltip="{{active.name}}" />
C'était une charge, les parades de l'adversaire de {{alias}} auront un -4 et il ne pourra pas esquiver! <img src="{{competence.img}}" data-tooltip="{{competence.name}}" />
{{ else if (eq attackerRoll.tactique 'feinte')}} </div>
<img class="chat-icon" src="systems/foundryvtt-reve-de-dragon/icons/heures/hd06.svg" data-tooltip="feinte" height="32" width="32" />
C'était une feinte! <div class="chat-header">
{{/if}} {{active.name}} {{#if current.defense.isEsquive}}esquive{{else}}pare{{/if}} une attaque {{grammar-apostrophe 'de' opponent.name}}
</p> </div>
{{/if}}
<div class="chat-resume">
{{current.carac.label}} / {{current.comp.label}} à {{current.diff.value}}
<br>{{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.hbs"}}
</div>
<div class="chat-details">
<hr>
{{#if rolled.isSuccess}}
<p>
Attaque {{#if current.defense.isEsquive}}esquivée{{else}}parée{{/if}}
{{#if rolled.isPart}}, {{active.name}} peut
<strong>{{#if current.defense.isEsquive}}faire une deuxième esquive{{else}}réutiliser son arme{{/if}}</strong>
{{/if}}
</p>
{{else}}
<p>{{#if current.defense.isEsquive}}Esquive{{else}}parade{{/if}} échouée!</p>
{{/if}}
{{!-- {{else}}
<!-- TODO: cas de parade à mains nues, texte à modifier -->
{{/if}} --}}
{{#if attackerRoll.tactique}}
<p>
{{#if (eq attackerRoll.tactique.key 'charge')}}
<img class="chat-icon" src="icons/svg/thrust.svg" data-tooltip="charge" height="32" width="32" />
C'était une charge, les parades de {{opponent.name}} auront un -4 et il ne pourra pas esquiver!
{{else if (eq attackerRoll.tactique.key 'feinte')}}
<img class="chat-icon" src="systems/foundryvtt-reve-de-dragon/icons/heures/hd06.svg" data-tooltip="feinte" height="32" width="32" />
C'était une feinte!
{{/if}}
</p>
{{/if}}
{{> 'partial-info-appel-moral'}}
</div>
<div class="chat-actions">
{{> 'partial-recul-choc'}}
{{> 'partial-encaissement'}}
</div>
<div class="chat-buttons">
{{> 'partial-appel-chance'}}
</div>
</div>

View File

@@ -0,0 +1,30 @@
{{log this}}
<div class="roll-chat">
<div class="chat-img">
<img src="{{active.img}}" data-tooltip="{{active.name}}" />
<img src="{{competence.img}}" data-tooltip="{{competence.name}}" />
</div>
<div class="chat-header">
{{active.name}} {{current.oeuvre.art.action}}: {{current.oeuvre.label}} (de niveau {{current.oeuvre.oeuvre.system.niveau}})
</div>
<div class="chat-resume">
{{current.carac.label}} / {{current.comp.label}} à {{current.diff.value}}
<br>{{> "systems/foundryvtt-reve-de-dragon/templates/chat-infojet.hbs"}}
</div>
<div class="chat-details">
<p>
{{active.name}}
{{#if rolled.isSuccess}}réussit son interprétation avec
{{else}}manque d'inspiration, son interprétation a
{{/if}}
une qualité de {{result.qualite}}.
</p>
{{> 'partial-info-appel-moral'}}
{{> "systems/foundryvtt-reve-de-dragon/templates/chat-description.hbs" current.oeuvre.oeuvre.system}}
</div>
<div class="chat-buttons">
{{> 'partial-appel-chance'}}
</div>
</div>

View File

@@ -0,0 +1,22 @@
{{#unless type.retry}}
{{#if show.chance}}
<a class='appel-chance'
data-actorId='{{ids.actorId}}'
data-actorTokenId='{{ids.actorTokenId}}'
data-opponentId='{{ids.opponentId}}'
data-opponentTokenId='{{ids.opponentTokenId}}'
data-tooltip="Appel à la chance">
<img src="systems/foundryvtt-reve-de-dragon/assets/ui/chance.svg"/>
</a>
{{#if (gt active.actor.system.compteurs.destinee.value 0)}}
<a class='appel-destinee'
data-actorId='{{ids.actorId}}'
data-actorTokenId='{{ids.actorTokenId}}'
data-opponentId='{{ids.opponentId}}'
data-opponentTokenId='{{ids.opponentTokenId}}'
data-tooltip="Utiliser la destinée">
<img src="systems/foundryvtt-reve-de-dragon/assets/ui/destinee.svg"/>
</a>
{{/if}}
{{/if}}
{{/unless}}

View File

@@ -0,0 +1,15 @@
{{#if show.encaissement}}
{{#if done.encaissement}}
<span class='chat-card-info'>
<img src="systems/foundryvtt-reve-de-dragon/assets/ui/encaisser.svg"/>
{{active.name}} a encaissé
</span>
{{else}}
<a class='chat-card-button encaissement'
data-tooltip="Encaisser à {{plusMoins attackerRoll.dmg.total}} {{#if (eq attackerRoll.dmg.mortalite 'non-mortel')~}}(non-mortel){{/if}}"
>
<img src="systems/foundryvtt-reve-de-dragon/assets/ui/encaisser.svg"/> Encaisser à {{plusMoins attackerRoll.dmg.total}}
{{#if (eq attackerRoll.dmg.mortalite 'non-mortel')~}}(non-mortel){{/if}}
</a>
{{/if}}
{{/if}}

View File

@@ -0,0 +1,15 @@
{{#if current.appelmoral.checked}}
<div>
{{{current.appelmoral.icon}}}
Vous avez fait appel {{#if (gt refs.appelmoral.moral 0)}}au moral{{else}}à l'énergie du désespoir{{/if}}
{{#if rolled.isSuccess}}
et réussi, votre moral reste de {{refs.appelmoral.moral}}.
{{else}}
et échoué,
{{#if (eq refs.appelmoral.moral -3)}}
vous marquez un point de dissolution!
{{else}}votre moral baisse de 1.
{{/if}}
{{/if}}
<div>
{{/if}}

View File

@@ -0,0 +1,18 @@
{{#if show.recul}}
{{#if (eq done.recul 'recul')}}
<span class='chat-card-info'>
<img src="systems/foundryvtt-reve-de-dragon/assets/ui/recul.svg"/>
{{active.name}} recule sous l'impact
</span>
{{else if (eq done.recul 'chute')}}
<span class='chat-card-info'>
<img src="icons/svg/falling.svg"/>
{{active.name}} recule et chute sous l'impact
</span>
{{else}}
<a class='chat-card-button resister-recul' data-tooltip="Résister au recul ({{show.recul.raison}}, taille {{show.recul.taille}}, impact {{show.recul.impact}})">
<img src="systems/foundryvtt-reve-de-dragon/assets/ui/recul.svg"/> Résister au recul
10 à {{plusMoins show.recul.diff}} = {{show.recul.chances}}%
</a>
{{/if}}
{{/if}}

View File

@@ -6,7 +6,6 @@
<roll-comp>{{#each templates as |template|}}{{> 'roll-section' rollData=@root.rollData currentsection='comp'}}{{/each}}</roll-comp> <roll-comp>{{#each templates as |template|}}{{> 'roll-section' rollData=@root.rollData currentsection='comp'}}{{/each}}</roll-comp>
<roll-choix>{{#each templates as |template|}}{{> 'roll-section' rollData=@root.rollData currentsection='choix'}}{{/each}}</roll-choix> <roll-choix>{{#each templates as |template|}}{{> 'roll-section' rollData=@root.rollData currentsection='choix'}}{{/each}}</roll-choix>
<roll-conditions>{{#each templates as |template|}}{{> 'roll-section' rollData=@root.rollData currentsection='conditions'}}{{/each}}</roll-conditions> <roll-conditions>{{#each templates as |template|}}{{> 'roll-section' rollData=@root.rollData currentsection='conditions'}}{{/each}}</roll-conditions>
{{log rollData.current rollData.current.carac rollData.current.totaldiff}}</roll-table>
<roll-table>{{> 'roll-table' carac=rollData.current.carac.value diff=rollData.current.totaldiff}}</roll-table> <roll-table>{{> 'roll-table' carac=rollData.current.carac.value diff=rollData.current.totaldiff}}</roll-table>
<roll-chances>{{> 'roll-chances' carac=rollData.current.carac.value diff=rollData.current.totaldiff}}</roll-chances> <roll-chances>{{> 'roll-chances' carac=rollData.current.carac.value diff=rollData.current.totaldiff}}</roll-chances>
<roll-resume>{{> 'roll-ajustements' }} <roll-resume>{{> 'roll-ajustements' }}

View File

@@ -1,4 +1,6 @@
<input name="{{code}}" type="checkbox" {{#if current.checked}}checked{{/if}}/> <input name="{{code}}" type="checkbox"
{{#if current.checked}}checked{{/if}}
{{#if rollData.type.retry}}disabled{{/if}}/>
<label for="{{code}}" data-tooltip="Moral: {{plusMoins refs.moral}}"> <label for="{{code}}" data-tooltip="Moral: {{plusMoins refs.moral}}">
{{#if current.icon}}{{{current.icon}}}{{/if}}{{refs.label}} {{#if current.icon}}{{{current.icon}}}{{/if}}{{refs.label}}
</label> </label>

View File

@@ -1,2 +1,4 @@
<input name="{{code}}" type="checkbox" {{#if current.checked}}checked{{/if}}/> <input name="{{code}}" type="checkbox"
{{#if current.checked}}checked{{/if}}
{{#if rollData.type.retry}}disabled{{/if}}/>
<label for="{{code}}">{{refs.label}}</label> <label for="{{code}}">{{refs.label}}</label>

View File

@@ -1,3 +1,5 @@
<input name="{{code}}" type="checkbox" {{#if current.checked}}checked{{/if}}/> <input name="{{code}}" type="checkbox"
{{#if current.checked}}checked{{/if}}
{{#if rollData.type.retry}}disabled{{/if}}/>
{{#if current.icon}}{{{current.icon}}}{{/if}} {{#if current.icon}}{{{current.icon}}}{{/if}}
<label for="{{code}}">{{either current.label refs.label}} : {{plusMoins current.value}}</label> <label for="{{code}}">{{either current.label refs.label}} : {{plusMoins current.value}}</label>

View File

@@ -1,4 +1,6 @@
<input name="{{code}}" type="checkbox" {{#if current.checked}}checked{{/if}}/> <input name="{{code}}" type="checkbox"
{{#if current.checked}}checked{{/if}}
{{#if rollData.type.retry}}disabled{{/if}}/>
<label for="{{code}}"> <label for="{{code}}">
{{#if current.icon}}{{{current.icon}}}{{/if}} {{#if current.icon}}{{{current.icon}}}{{/if}}
{{current.label}} {{current.label}}

View File

@@ -1 +0,0 @@
<span>: {{current.item.name}}</span>

View File

@@ -1,4 +1,3 @@
{{log 'roll-table' carac diff this}}
<table class='table-resolution'> <table class='table-resolution'>
<tr> <tr>
<th></th> <th></th>