Using sqlite database, mt proctor animation.

logins
Thomas Hintz 5 years ago
parent b34a66f697
commit 6ff6387fef

@ -35,7 +35,7 @@ install:
npm install
interactive:
cd dist/ && csi -include-path $(assets) -s farm.scm
cd dist/ && csi -include-path $(assets) -include-path ../src/server -s farm.scm
src/server/farm: src/server/farm.scm
cd src/server/ && csc -include-path ../../$(assets) -O3 farm.scm

@ -47,7 +47,7 @@
"20 Cows on Peridier Ridge"))))
(define *ff-text*
'(((p "Natural Disaster--The Solar Winds break through the atmosphere. You are luckily shielded by Mt Proctor. Your hay survives and jumps in price. " (b "COLLECT $500 per Hay acre") ". To see if they escaped, other players must roll. Odd: escaped, Even: hit. " (b "Wind hit players must clean up all acres at $100 per acre.")))
'(((p (img (@ (src "53c3a93b0867eee67b9b9f6ebc4c1f4a.gif") (style "float: left;"))) "Natural Disaster--The Solar Winds break through the atmosphere. You are luckily shielded by Mt Proctor. Your hay survives and jumps in price. " (b "COLLECT $500 per Hay acre") ". To see if they escaped, other players must roll. Odd: escaped, Even: hit. " (b "Wind hit players must clean up all acres at $100 per acre.")))
((p "Planetary Disaster Fund comes through." (p (b "COLLECT $100 per Grain acre."))))
((p "Another high wind spring and your wheat didn't get sprayed. Weeds take over and cut your harvest in half. Hold this card through Wheat Harvest for this year."))
((p "Kept back some of your cows and Proxima B steak goes viral.") (p (b "COLLECT $2,000 if you have cows.")))

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 MiB

@ -0,0 +1,73 @@
<?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"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 83.521004 90"
enable-background="new 0 0 100 100"
xml:space="preserve"
id="svg4827"
sodipodi:docname="volcano.svg"
width="83.521004"
height="90"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><metadata
id="metadata4833"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs4831" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1375"
id="namedview4829"
showgrid="false"
inkscape:zoom="16.931965"
inkscape:cx="34.840436"
inkscape:cy="58.282217"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer2" /><g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="background"
style="display:inline"><path
d="m 22.724,90 h 3.742 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.552 -0.448,-1 -1,-1 h -3.742 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 z"
id="path4848"
inkscape:connector-curvature="0" /><path
d="m 31.709,48.246 c -0.5,0 -0.92,0.373 -0.991,0.868 C 26.776,76.3 7.844,86.928 5.8,88 H 1 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 h 17.541 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.552 -0.448,-1 -1,-1 H 9.625 C 15.767,83.846 29.057,72.489 32.57,50.247 H 46.913 C 50.775,72.775 66.307,83.893 73.711,88 H 63.602 C 55.695,83.379 50.214,73.507 50.159,73.407 c -0.267,-0.485 -0.875,-0.662 -1.357,-0.396 -0.484,0.266 -0.662,0.873 -0.396,1.357 0.21,0.383 4.67,8.425 11.601,13.632 H 33.441 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 h 49.08 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.553 -0.447,-1 -1,-1 H 78.222 C 76.161,87.159 53.146,77.158 48.752,49.104 c -0.077,-0.49 -0.493,-0.858 -0.99,-0.858"
id="path4821"
inkscape:connector-curvature="0" /></g><g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="smoke"
style="display:inline"><path
d="m 29.271,25.283 c 0.179,0.522 -0.101,1.091 -0.623,1.27 -0.107,0.037 -0.216,0.054 -0.323,0.054 -0.416,0 -0.804,-0.262 -0.946,-0.677 -0.976,-2.858 0.208,-6.044 2.816,-7.576 2.525,-1.483 5.727,-1.065 7.784,1.015 0.389,0.393 0.385,1.026 -0.008,1.414 -0.393,0.389 -1.026,0.385 -1.414,-0.008 -1.414,-1.43 -3.614,-1.716 -5.349,-0.697 -1.793,1.053 -2.607,3.242 -1.937,5.205 z"
id="path4856"
inkscape:connector-curvature="0" /><path
d="m 59.002,9.435 c 4.279,0.188 8.044,3.055 9.37,7.136 0.137,0.422 0.529,0.691 0.951,0.691 0.102,0 0.207,-0.016 0.309,-0.049 0.525,-0.171 0.813,-0.735 0.642,-1.26 C 68.691,11.083 64.197,7.661 59.09,7.437 58.553,7.435 58.071,7.84 58.047,8.392 58.023,8.944 58.45,9.41 59.002,9.435 Z"
id="path4854"
inkscape:connector-curvature="0" /><path
d="m 15.571,32.367 h 0.85 c 0.47,2.492 2.663,4.383 5.29,4.383 h 5.372 c 2.044,0 3.708,1.663 3.708,3.708 v 4.135 h 2 v -4.135 c 0,-3.147 -2.561,-5.708 -5.708,-5.708 h -5.372 c -1.865,0 -3.383,-1.518 -3.383,-3.383 0,-0.552 -0.448,-1 -1,-1 h -1.757 c -3.271,0 -5.933,-2.662 -5.933,-5.933 0,-3.271 2.662,-5.933 5.933,-5.933 h 3.212 c 0.552,0 1,-0.448 1,-1 v -1.073 c 0,-3.677 2.992,-6.668 6.668,-6.668 0.552,0 1,-0.448 1,-1 V 8.377 C 27.452,4.861 30.313,2 33.829,2 c 3.516,0 6.377,2.861 6.377,6.377 V 8.76 c 0,0.552 0.448,1 1,1 h 3.454 c 2.576,0 4.671,2.095 4.671,4.67 0,0.552 0.448,1 1,1 h 3.058 c 5.435,0 9.856,4.421 9.856,9.855 0,0.552 0.448,1 1,1 h 2.791 c 1.905,0 3.455,1.55 3.455,3.456 0,1.906 -1.55,3.455 -3.455,3.455 H 52.499 c -3.147,0 -5.708,2.561 -5.708,5.708 v 6.034 h 2 v -6.034 c 0,-2.044 1.664,-3.708 3.708,-3.708 h 14.536 c 3.008,0 5.455,-2.447 5.455,-5.455 0,-3.008 -2.447,-5.456 -5.455,-5.456 H 65.202 C 64.693,18.214 59.589,13.43 53.388,13.43 H 51.255 C 50.771,10.225 47.997,7.76 44.659,7.76 H 42.183 C 41.866,3.428 38.24,0 33.829,0 29.398,0 25.76,3.457 25.471,7.815 21.152,8.304 17.784,11.98 17.784,16.428 v 0.073 h -2.213 c -4.375,0 -7.933,3.559 -7.933,7.933 0,4.374 3.559,7.933 7.933,7.933 z"
id="path4852"
inkscape:connector-curvature="0" /><path
d="M 43.531,44.77 V 30.819 c 0,-0.552 -0.448,-1 -1,-1 v 0 c -0.552,0 -1,0.448 -1,1 V 44.77 Z"
id="path4850"
inkscape:connector-curvature="0" /><path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 36.754563,48.297963 V 38.85399"
id="path4844"
inkscape:connector-curvature="0" /></g></svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

@ -0,0 +1,80 @@
<?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"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 83.521004 90"
enable-background="new 0 0 100 100"
xml:space="preserve"
id="svg4827"
sodipodi:docname="volcano0.svg"
width="83.521004"
height="90"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
inkscape:export-filename="/home/tjhintz/tmp/volcano/volcano0.png"
inkscape:export-xdpi="106.654"
inkscape:export-ydpi="106.654"><metadata
id="metadata4833"><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 /></cc:Work></rdf:RDF></metadata><defs
id="defs4831" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1375"
id="namedview4829"
showgrid="false"
inkscape:zoom="11.972707"
inkscape:cx="3.3491555"
inkscape:cy="44.177135"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer2" /><g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="background"
style="display:inline"><path
d="m 22.724,90 h 3.742 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.552 -0.448,-1 -1,-1 h -3.742 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 z"
id="path4848"
inkscape:connector-curvature="0" /><path
d="m 31.709,48.246 c -0.5,0 -0.92,0.373 -0.991,0.868 C 26.776,76.3 7.844,86.928 5.8,88 H 1 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 h 17.541 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.552 -0.448,-1 -1,-1 H 9.625 C 15.767,83.846 29.057,72.489 32.57,50.247 H 46.913 C 50.775,72.775 66.307,83.893 73.711,88 H 63.602 C 55.695,83.379 50.214,73.507 50.159,73.407 c -0.267,-0.485 -0.875,-0.662 -1.357,-0.396 -0.484,0.266 -0.662,0.873 -0.396,1.357 0.21,0.383 4.67,8.425 11.601,13.632 H 33.441 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 h 49.08 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.553 -0.447,-1 -1,-1 H 78.222 C 76.161,87.159 53.146,77.158 48.752,49.104 c -0.077,-0.49 -0.493,-0.858 -0.99,-0.858"
id="path4821"
inkscape:connector-curvature="0" /></g><g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="smoke"
style="display:inline"><path
d="m 38.275981,46.275652 c 0.0179,0.0522 -0.0101,0.1091 -0.0623,0.127 -0.0107,0.0037 -0.0216,0.0054 -0.0323,0.0054 -0.0416,0 -0.0804,-0.0262 -0.0946,-0.0677 -0.0976,-0.2858 0.0208,-0.6044 0.2816,-0.7576 0.2525,-0.1483 0.5727,-0.1065 0.7784,0.1015 0.0389,0.0393 0.0385,0.1026 -8e-4,0.1414 -0.0393,0.0389 -0.1026,0.0385 -0.1414,-8e-4 -0.1414,-0.143 -0.3614,-0.1716 -0.5349,-0.0697 -0.1793,0.1053 -0.2607,0.3242 -0.1937,0.5205 z"
id="path4856"
inkscape:connector-curvature="0"
style="stroke-width:0.1" /><path
d="m 41.249081,44.690852 c 0.4279,0.0188 0.8044,0.3055 0.937,0.7136 0.0137,0.0422 0.0529,0.0691 0.0951,0.0691 0.0102,0 0.0207,-0.0016 0.0309,-0.0049 0.0525,-0.0171 0.0813,-0.0735 0.0642,-0.126 -0.1583,-0.487 -0.6077,-0.8292 -1.1184,-0.8516 -0.0537,-2e-4 -0.1019,0.0403 -0.1043,0.0955 -0.0024,0.0552 0.0403,0.1018 0.0955,0.1043 z"
id="path4854"
inkscape:connector-curvature="0"
style="stroke-width:0.1" /><path
d="m 36.905981,46.984052 h 0.085 c 0.047,0.2492 0.2663,0.4383 0.529,0.4383 h 0.5372 c 0.2044,0 0.3708,0.1663 0.3708,0.3708 v 0.4135 h 0.2 v -0.4135 c 0,-0.3147 -0.2561,-0.5708 -0.5708,-0.5708 h -0.5372 c -0.1865,0 -0.3383,-0.1518 -0.3383,-0.3383 0,-0.0552 -0.0448,-0.1 -0.1,-0.1 h -0.1757 c -0.3271,0 -0.5933,-0.2662 -0.5933,-0.5933 0,-0.3271 0.2662,-0.5933 0.5933,-0.5933 h 0.3212 c 0.0552,0 0.1,-0.0448 0.1,-0.1 v -0.1073 c 0,-0.3677 0.2992,-0.6668 0.6668,-0.6668 0.0552,0 0.1,-0.0448 0.1,-0.1 v -0.0383 c 1e-4,-0.3516 0.2862,-0.6377 0.6378,-0.6377 0.3516,0 0.6377,0.2861 0.6377,0.6377 v 0.0383 c 0,0.0552 0.0448,0.1 0.1,0.1 h 0.3454 c 0.2576,0 0.4671,0.2095 0.4671,0.467 0,0.0552 0.0448,0.1 0.1,0.1 h 0.3058 c 0.5435,0 0.9856,0.4421 0.9856,0.9855 0,0.0552 0.0448,0.1 0.1,0.1 h 0.2791 c 0.1905,0 0.3455,0.155 0.3455,0.3456 0,0.1906 -0.155,0.3455 -0.3455,0.3455 h -1.4537 c -0.3147,0 -0.5708,0.2561 -0.5708,0.5708 v 0.6034 h 0.2 v -0.6034 c 0,-0.2044 0.1664,-0.3708 0.3708,-0.3708 h 1.4536 c 0.3008,0 0.5455,-0.2447 0.5455,-0.5455 0,-0.3008 -0.2447,-0.5456 -0.5455,-0.5456 h -0.1833 c -0.0509,-0.6071 -0.5613,-1.0855 -1.1814,-1.0855 h -0.2133 c -0.0484,-0.3205 -0.3258,-0.567 -0.6596,-0.567 h -0.2476 c -0.0317,-0.4332 -0.3943,-0.776 -0.8354,-0.776 -0.4431,0 -0.8069,0.3457 -0.8358,0.7815 -0.4319,0.0489 -0.7687,0.4165 -0.7687,0.8613 v 0.0073 h -0.2213 c -0.4375,0 -0.7933,0.3559 -0.7933,0.7933 0,0.4374 0.3559,0.7933 0.7933,0.7933 z"
id="path4852"
inkscape:connector-curvature="0"
style="stroke-width:0.1" /><path
d="m 39.701981,48.224352 v -1.3951 c 0,-0.0552 -0.0448,-0.1 -0.1,-0.1 v 0 c -0.0552,0 -0.1,0.0448 -0.1,0.1 v 1.3951 z"
id="path4850"
inkscape:connector-curvature="0"
style="stroke-width:0.1" /><path
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 39.024337,48.577149 V 47.632751"
id="path4844"
inkscape:connector-curvature="0" /></g></svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

@ -0,0 +1,82 @@
<?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"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 83.521004 90"
enable-background="new 0 0 100 100"
xml:space="preserve"
id="svg4827"
sodipodi:docname="volcano1.svg"
width="83.521004"
height="90"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
inkscape:export-filename="/home/tjhintz/tmp/volcano/volcano1.png"
inkscape:export-xdpi="106.654"
inkscape:export-ydpi="106.654"><metadata
id="metadata4833"><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 /></cc:Work></rdf:RDF></metadata><defs
id="defs4831" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1375"
id="namedview4829"
showgrid="false"
inkscape:zoom="16"
inkscape:cx="19.364384"
inkscape:cy="35.768813"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer2" /><g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="background"
style="display:inline"><path
d="m 22.724,90 h 3.742 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.552 -0.448,-1 -1,-1 h -3.742 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 z"
id="path4848"
inkscape:connector-curvature="0" /><path
d="m 31.709,48.246 c -0.5,0 -0.92,0.373 -0.991,0.868 C 26.776,76.3 7.844,86.928 5.8,88 H 1 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 h 17.541 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.552 -0.448,-1 -1,-1 H 9.625 C 15.767,83.846 29.057,72.489 32.57,50.247 H 46.913 C 50.775,72.775 66.307,83.893 73.711,88 H 63.602 C 55.695,83.379 50.214,73.507 50.159,73.407 c -0.267,-0.485 -0.875,-0.662 -1.357,-0.396 -0.484,0.266 -0.662,0.873 -0.396,1.357 0.21,0.383 4.67,8.425 11.601,13.632 H 33.441 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 h 49.08 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.553 -0.447,-1 -1,-1 H 78.222 C 76.161,87.159 53.146,77.158 48.752,49.104 c -0.077,-0.49 -0.493,-0.858 -0.99,-0.858"
id="path4821"
inkscape:connector-curvature="0" /></g><g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="smoke"
style="display:inline"><g
id="g4911"
transform="matrix(2,0,0,2,-39.355281,-48.923119)"><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4856"
d="m 38.275981,46.275652 c 0.0179,0.0522 -0.0101,0.1091 -0.0623,0.127 -0.0107,0.0037 -0.0216,0.0054 -0.0323,0.0054 -0.0416,0 -0.0804,-0.0262 -0.0946,-0.0677 -0.0976,-0.2858 0.0208,-0.6044 0.2816,-0.7576 0.2525,-0.1483 0.5727,-0.1065 0.7784,0.1015 0.0389,0.0393 0.0385,0.1026 -8e-4,0.1414 -0.0393,0.0389 -0.1026,0.0385 -0.1414,-8e-4 -0.1414,-0.143 -0.3614,-0.1716 -0.5349,-0.0697 -0.1793,0.1053 -0.2607,0.3242 -0.1937,0.5205 z" /><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4854"
d="m 41.249081,44.690852 c 0.4279,0.0188 0.8044,0.3055 0.937,0.7136 0.0137,0.0422 0.0529,0.0691 0.0951,0.0691 0.0102,0 0.0207,-0.0016 0.0309,-0.0049 0.0525,-0.0171 0.0813,-0.0735 0.0642,-0.126 -0.1583,-0.487 -0.6077,-0.8292 -1.1184,-0.8516 -0.0537,-2e-4 -0.1019,0.0403 -0.1043,0.0955 -0.0024,0.0552 0.0403,0.1018 0.0955,0.1043 z" /><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4852"
d="m 36.905981,46.984052 h 0.085 c 0.047,0.2492 0.2663,0.4383 0.529,0.4383 h 0.5372 c 0.2044,0 0.3708,0.1663 0.3708,0.3708 v 0.4135 h 0.2 v -0.4135 c 0,-0.3147 -0.2561,-0.5708 -0.5708,-0.5708 h -0.5372 c -0.1865,0 -0.3383,-0.1518 -0.3383,-0.3383 0,-0.0552 -0.0448,-0.1 -0.1,-0.1 h -0.1757 c -0.3271,0 -0.5933,-0.2662 -0.5933,-0.5933 0,-0.3271 0.2662,-0.5933 0.5933,-0.5933 h 0.3212 c 0.0552,0 0.1,-0.0448 0.1,-0.1 v -0.1073 c 0,-0.3677 0.2992,-0.6668 0.6668,-0.6668 0.0552,0 0.1,-0.0448 0.1,-0.1 v -0.0383 c 1e-4,-0.3516 0.2862,-0.6377 0.6378,-0.6377 0.3516,0 0.6377,0.2861 0.6377,0.6377 v 0.0383 c 0,0.0552 0.0448,0.1 0.1,0.1 h 0.3454 c 0.2576,0 0.4671,0.2095 0.4671,0.467 0,0.0552 0.0448,0.1 0.1,0.1 h 0.3058 c 0.5435,0 0.9856,0.4421 0.9856,0.9855 0,0.0552 0.0448,0.1 0.1,0.1 h 0.2791 c 0.1905,0 0.3455,0.155 0.3455,0.3456 0,0.1906 -0.155,0.3455 -0.3455,0.3455 h -1.4537 c -0.3147,0 -0.5708,0.2561 -0.5708,0.5708 v 0.6034 h 0.2 v -0.6034 c 0,-0.2044 0.1664,-0.3708 0.3708,-0.3708 h 1.4536 c 0.3008,0 0.5455,-0.2447 0.5455,-0.5455 0,-0.3008 -0.2447,-0.5456 -0.5455,-0.5456 h -0.1833 c -0.0509,-0.6071 -0.5613,-1.0855 -1.1814,-1.0855 h -0.2133 c -0.0484,-0.3205 -0.3258,-0.567 -0.6596,-0.567 h -0.2476 c -0.0317,-0.4332 -0.3943,-0.776 -0.8354,-0.776 -0.4431,0 -0.8069,0.3457 -0.8358,0.7815 -0.4319,0.0489 -0.7687,0.4165 -0.7687,0.8613 v 0.0073 h -0.2213 c -0.4375,0 -0.7933,0.3559 -0.7933,0.7933 0,0.4374 0.3559,0.7933 0.7933,0.7933 z" /><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4850"
d="m 39.701981,48.224352 v -1.3951 c 0,-0.0552 -0.0448,-0.1 -0.1,-0.1 v 0 c -0.0552,0 -0.1,0.0448 -0.1,0.1 v 1.3951 z" /><path
inkscape:connector-curvature="0"
id="path4844"
d="M 39.024337,48.577149 V 47.632751"
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /></g></g></svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -0,0 +1,82 @@
<?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"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 83.521004 90"
enable-background="new 0 0 100 100"
xml:space="preserve"
id="svg4827"
sodipodi:docname="volcano2.svg"
width="83.521004"
height="90"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
inkscape:export-filename="/home/tjhintz/tmp/volcano/volcano2.png"
inkscape:export-xdpi="106.654"
inkscape:export-ydpi="106.654"><metadata
id="metadata4833"><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 /></cc:Work></rdf:RDF></metadata><defs
id="defs4831" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1375"
id="namedview4829"
showgrid="false"
inkscape:zoom="11.313709"
inkscape:cx="-8.2897213"
inkscape:cy="46.522973"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer2" /><g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="background"
style="display:inline"><path
d="m 22.724,90 h 3.742 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.552 -0.448,-1 -1,-1 h -3.742 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 z"
id="path4848"
inkscape:connector-curvature="0" /><path
d="m 31.709,48.246 c -0.5,0 -0.92,0.373 -0.991,0.868 C 26.776,76.3 7.844,86.928 5.8,88 H 1 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 h 17.541 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.552 -0.448,-1 -1,-1 H 9.625 C 15.767,83.846 29.057,72.489 32.57,50.247 H 46.913 C 50.775,72.775 66.307,83.893 73.711,88 H 63.602 C 55.695,83.379 50.214,73.507 50.159,73.407 c -0.267,-0.485 -0.875,-0.662 -1.357,-0.396 -0.484,0.266 -0.662,0.873 -0.396,1.357 0.21,0.383 4.67,8.425 11.601,13.632 H 33.441 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 h 49.08 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.553 -0.447,-1 -1,-1 H 78.222 C 76.161,87.159 53.146,77.158 48.752,49.104 c -0.077,-0.49 -0.493,-0.858 -0.99,-0.858"
id="path4821"
inkscape:connector-curvature="0" /></g><g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="smoke"
style="display:inline"><g
id="g4911"
transform="matrix(4,0,0,4,-118.06584,-146.02873)"><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4856"
d="m 38.275981,46.275652 c 0.0179,0.0522 -0.0101,0.1091 -0.0623,0.127 -0.0107,0.0037 -0.0216,0.0054 -0.0323,0.0054 -0.0416,0 -0.0804,-0.0262 -0.0946,-0.0677 -0.0976,-0.2858 0.0208,-0.6044 0.2816,-0.7576 0.2525,-0.1483 0.5727,-0.1065 0.7784,0.1015 0.0389,0.0393 0.0385,0.1026 -8e-4,0.1414 -0.0393,0.0389 -0.1026,0.0385 -0.1414,-8e-4 -0.1414,-0.143 -0.3614,-0.1716 -0.5349,-0.0697 -0.1793,0.1053 -0.2607,0.3242 -0.1937,0.5205 z" /><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4854"
d="m 41.249081,44.690852 c 0.4279,0.0188 0.8044,0.3055 0.937,0.7136 0.0137,0.0422 0.0529,0.0691 0.0951,0.0691 0.0102,0 0.0207,-0.0016 0.0309,-0.0049 0.0525,-0.0171 0.0813,-0.0735 0.0642,-0.126 -0.1583,-0.487 -0.6077,-0.8292 -1.1184,-0.8516 -0.0537,-2e-4 -0.1019,0.0403 -0.1043,0.0955 -0.0024,0.0552 0.0403,0.1018 0.0955,0.1043 z" /><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4852"
d="m 36.905981,46.984052 h 0.085 c 0.047,0.2492 0.2663,0.4383 0.529,0.4383 h 0.5372 c 0.2044,0 0.3708,0.1663 0.3708,0.3708 v 0.4135 h 0.2 v -0.4135 c 0,-0.3147 -0.2561,-0.5708 -0.5708,-0.5708 h -0.5372 c -0.1865,0 -0.3383,-0.1518 -0.3383,-0.3383 0,-0.0552 -0.0448,-0.1 -0.1,-0.1 h -0.1757 c -0.3271,0 -0.5933,-0.2662 -0.5933,-0.5933 0,-0.3271 0.2662,-0.5933 0.5933,-0.5933 h 0.3212 c 0.0552,0 0.1,-0.0448 0.1,-0.1 v -0.1073 c 0,-0.3677 0.2992,-0.6668 0.6668,-0.6668 0.0552,0 0.1,-0.0448 0.1,-0.1 v -0.0383 c 1e-4,-0.3516 0.2862,-0.6377 0.6378,-0.6377 0.3516,0 0.6377,0.2861 0.6377,0.6377 v 0.0383 c 0,0.0552 0.0448,0.1 0.1,0.1 h 0.3454 c 0.2576,0 0.4671,0.2095 0.4671,0.467 0,0.0552 0.0448,0.1 0.1,0.1 h 0.3058 c 0.5435,0 0.9856,0.4421 0.9856,0.9855 0,0.0552 0.0448,0.1 0.1,0.1 h 0.2791 c 0.1905,0 0.3455,0.155 0.3455,0.3456 0,0.1906 -0.155,0.3455 -0.3455,0.3455 h -1.4537 c -0.3147,0 -0.5708,0.2561 -0.5708,0.5708 v 0.6034 h 0.2 v -0.6034 c 0,-0.2044 0.1664,-0.3708 0.3708,-0.3708 h 1.4536 c 0.3008,0 0.5455,-0.2447 0.5455,-0.5455 0,-0.3008 -0.2447,-0.5456 -0.5455,-0.5456 h -0.1833 c -0.0509,-0.6071 -0.5613,-1.0855 -1.1814,-1.0855 h -0.2133 c -0.0484,-0.3205 -0.3258,-0.567 -0.6596,-0.567 h -0.2476 c -0.0317,-0.4332 -0.3943,-0.776 -0.8354,-0.776 -0.4431,0 -0.8069,0.3457 -0.8358,0.7815 -0.4319,0.0489 -0.7687,0.4165 -0.7687,0.8613 v 0.0073 h -0.2213 c -0.4375,0 -0.7933,0.3559 -0.7933,0.7933 0,0.4374 0.3559,0.7933 0.7933,0.7933 z" /><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4850"
d="m 39.701981,48.224352 v -1.3951 c 0,-0.0552 -0.0448,-0.1 -0.1,-0.1 v 0 c -0.0552,0 -0.1,0.0448 -0.1,0.1 v 1.3951 z" /><path
inkscape:connector-curvature="0"
id="path4844"
d="M 39.024337,48.577149 V 47.632751"
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /></g></g></svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

@ -0,0 +1,82 @@
<?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"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 83.521004 90"
enable-background="new 0 0 100 100"
xml:space="preserve"
id="svg4827"
sodipodi:docname="volcano3.svg"
width="83.521004"
height="90"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
inkscape:export-filename="/home/tjhintz/tmp/volcano/volcano3.png"
inkscape:export-xdpi="106.66666"
inkscape:export-ydpi="106.66666"><metadata
id="metadata4833"><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 /></cc:Work></rdf:RDF></metadata><defs
id="defs4831" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1375"
id="namedview4829"
showgrid="false"
inkscape:zoom="8"
inkscape:cx="-43.021575"
inkscape:cy="60.241354"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer2" /><g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="background"
style="display:inline"><path
d="m 22.724,90 h 3.742 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.552 -0.448,-1 -1,-1 h -3.742 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 z"
id="path4848"
inkscape:connector-curvature="0" /><path
d="m 31.709,48.246 c -0.5,0 -0.92,0.373 -0.991,0.868 C 26.776,76.3 7.844,86.928 5.8,88 H 1 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 h 17.541 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.552 -0.448,-1 -1,-1 H 9.625 C 15.767,83.846 29.057,72.489 32.57,50.247 H 46.913 C 50.775,72.775 66.307,83.893 73.711,88 H 63.602 C 55.695,83.379 50.214,73.507 50.159,73.407 c -0.267,-0.485 -0.875,-0.662 -1.357,-0.396 -0.484,0.266 -0.662,0.873 -0.396,1.357 0.21,0.383 4.67,8.425 11.601,13.632 H 33.441 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 h 49.08 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.553 -0.447,-1 -1,-1 H 78.222 C 76.161,87.159 53.146,77.158 48.752,49.104 c -0.077,-0.49 -0.493,-0.858 -0.99,-0.858"
id="path4821"
inkscape:connector-curvature="0" /></g><g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="smoke"
style="display:inline"><g
id="g4911"
transform="matrix(8,0,0,8,-275.48696,-340.41183)"><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4856"
d="m 38.275981,46.275652 c 0.0179,0.0522 -0.0101,0.1091 -0.0623,0.127 -0.0107,0.0037 -0.0216,0.0054 -0.0323,0.0054 -0.0416,0 -0.0804,-0.0262 -0.0946,-0.0677 -0.0976,-0.2858 0.0208,-0.6044 0.2816,-0.7576 0.2525,-0.1483 0.5727,-0.1065 0.7784,0.1015 0.0389,0.0393 0.0385,0.1026 -8e-4,0.1414 -0.0393,0.0389 -0.1026,0.0385 -0.1414,-8e-4 -0.1414,-0.143 -0.3614,-0.1716 -0.5349,-0.0697 -0.1793,0.1053 -0.2607,0.3242 -0.1937,0.5205 z" /><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4854"
d="m 41.249081,44.690852 c 0.4279,0.0188 0.8044,0.3055 0.937,0.7136 0.0137,0.0422 0.0529,0.0691 0.0951,0.0691 0.0102,0 0.0207,-0.0016 0.0309,-0.0049 0.0525,-0.0171 0.0813,-0.0735 0.0642,-0.126 -0.1583,-0.487 -0.6077,-0.8292 -1.1184,-0.8516 -0.0537,-2e-4 -0.1019,0.0403 -0.1043,0.0955 -0.0024,0.0552 0.0403,0.1018 0.0955,0.1043 z" /><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4852"
d="m 36.905981,46.984052 h 0.085 c 0.047,0.2492 0.2663,0.4383 0.529,0.4383 h 0.5372 c 0.2044,0 0.3708,0.1663 0.3708,0.3708 v 0.4135 h 0.2 v -0.4135 c 0,-0.3147 -0.2561,-0.5708 -0.5708,-0.5708 h -0.5372 c -0.1865,0 -0.3383,-0.1518 -0.3383,-0.3383 0,-0.0552 -0.0448,-0.1 -0.1,-0.1 h -0.1757 c -0.3271,0 -0.5933,-0.2662 -0.5933,-0.5933 0,-0.3271 0.2662,-0.5933 0.5933,-0.5933 h 0.3212 c 0.0552,0 0.1,-0.0448 0.1,-0.1 v -0.1073 c 0,-0.3677 0.2992,-0.6668 0.6668,-0.6668 0.0552,0 0.1,-0.0448 0.1,-0.1 v -0.0383 c 1e-4,-0.3516 0.2862,-0.6377 0.6378,-0.6377 0.3516,0 0.6377,0.2861 0.6377,0.6377 v 0.0383 c 0,0.0552 0.0448,0.1 0.1,0.1 h 0.3454 c 0.2576,0 0.4671,0.2095 0.4671,0.467 0,0.0552 0.0448,0.1 0.1,0.1 h 0.3058 c 0.5435,0 0.9856,0.4421 0.9856,0.9855 0,0.0552 0.0448,0.1 0.1,0.1 h 0.2791 c 0.1905,0 0.3455,0.155 0.3455,0.3456 0,0.1906 -0.155,0.3455 -0.3455,0.3455 h -1.4537 c -0.3147,0 -0.5708,0.2561 -0.5708,0.5708 v 0.6034 h 0.2 v -0.6034 c 0,-0.2044 0.1664,-0.3708 0.3708,-0.3708 h 1.4536 c 0.3008,0 0.5455,-0.2447 0.5455,-0.5455 0,-0.3008 -0.2447,-0.5456 -0.5455,-0.5456 h -0.1833 c -0.0509,-0.6071 -0.5613,-1.0855 -1.1814,-1.0855 h -0.2133 c -0.0484,-0.3205 -0.3258,-0.567 -0.6596,-0.567 h -0.2476 c -0.0317,-0.4332 -0.3943,-0.776 -0.8354,-0.776 -0.4431,0 -0.8069,0.3457 -0.8358,0.7815 -0.4319,0.0489 -0.7687,0.4165 -0.7687,0.8613 v 0.0073 h -0.2213 c -0.4375,0 -0.7933,0.3559 -0.7933,0.7933 0,0.4374 0.3559,0.7933 0.7933,0.7933 z" /><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4850"
d="m 39.701981,48.224352 v -1.3951 c 0,-0.0552 -0.0448,-0.1 -0.1,-0.1 v 0 c -0.0552,0 -0.1,0.0448 -0.1,0.1 v 1.3951 z" /><path
inkscape:connector-curvature="0"
id="path4844"
d="M 39.024337,48.577149 V 47.632751"
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /></g></g></svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

@ -0,0 +1,82 @@
<?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"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 83.521004 90"
enable-background="new 0 0 100 100"
xml:space="preserve"
id="svg4827"
sodipodi:docname="volcano4.svg"
width="83.521004"
height="90"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
inkscape:export-filename="/home/tjhintz/tmp/volcano/volcano4.png"
inkscape:export-xdpi="106.66666"
inkscape:export-ydpi="106.66666"><metadata
id="metadata4833"><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="defs4831" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1375"
id="namedview4829"
showgrid="false"
inkscape:zoom="8"
inkscape:cx="-14.5812"
inkscape:cy="44.273261"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer2" /><g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="background"
style="display:inline"><path
d="m 22.724,90 h 3.742 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.552 -0.448,-1 -1,-1 h -3.742 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 z"
id="path4848"
inkscape:connector-curvature="0" /><path
d="m 31.709,48.246 c -0.5,0 -0.92,0.373 -0.991,0.868 C 26.776,76.3 7.844,86.928 5.8,88 H 1 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 h 17.541 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.552 -0.448,-1 -1,-1 H 9.625 C 15.767,83.846 29.057,72.489 32.57,50.247 H 46.913 C 50.775,72.775 66.307,83.893 73.711,88 H 63.602 C 55.695,83.379 50.214,73.507 50.159,73.407 c -0.267,-0.485 -0.875,-0.662 -1.357,-0.396 -0.484,0.266 -0.662,0.873 -0.396,1.357 0.21,0.383 4.67,8.425 11.601,13.632 H 33.441 c -0.552,0 -1,0.448 -1,1 v 0 c 0,0.552 0.448,1 1,1 h 49.08 c 0.552,0 1,-0.448 1,-1 v 0 c 0,-0.553 -0.447,-1 -1,-1 H 78.222 C 76.161,87.159 53.146,77.158 48.752,49.104 c -0.077,-0.49 -0.493,-0.858 -0.99,-0.858"
id="path4821"
inkscape:connector-curvature="0" /></g><g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="smoke"
style="display:inline"><g
id="g4911"
transform="matrix(9.9676375,0,0,9.9676375,-351.91862,-436.06862)"><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4856"
d="m 38.275981,46.275652 c 0.0179,0.0522 -0.0101,0.1091 -0.0623,0.127 -0.0107,0.0037 -0.0216,0.0054 -0.0323,0.0054 -0.0416,0 -0.0804,-0.0262 -0.0946,-0.0677 -0.0976,-0.2858 0.0208,-0.6044 0.2816,-0.7576 0.2525,-0.1483 0.5727,-0.1065 0.7784,0.1015 0.0389,0.0393 0.0385,0.1026 -8e-4,0.1414 -0.0393,0.0389 -0.1026,0.0385 -0.1414,-8e-4 -0.1414,-0.143 -0.3614,-0.1716 -0.5349,-0.0697 -0.1793,0.1053 -0.2607,0.3242 -0.1937,0.5205 z" /><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4854"
d="m 41.249081,44.690852 c 0.4279,0.0188 0.8044,0.3055 0.937,0.7136 0.0137,0.0422 0.0529,0.0691 0.0951,0.0691 0.0102,0 0.0207,-0.0016 0.0309,-0.0049 0.0525,-0.0171 0.0813,-0.0735 0.0642,-0.126 -0.1583,-0.487 -0.6077,-0.8292 -1.1184,-0.8516 -0.0537,-2e-4 -0.1019,0.0403 -0.1043,0.0955 -0.0024,0.0552 0.0403,0.1018 0.0955,0.1043 z" /><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4852"
d="m 36.905981,46.984052 h 0.085 c 0.047,0.2492 0.2663,0.4383 0.529,0.4383 h 0.5372 c 0.2044,0 0.3708,0.1663 0.3708,0.3708 v 0.4135 h 0.2 v -0.4135 c 0,-0.3147 -0.2561,-0.5708 -0.5708,-0.5708 h -0.5372 c -0.1865,0 -0.3383,-0.1518 -0.3383,-0.3383 0,-0.0552 -0.0448,-0.1 -0.1,-0.1 h -0.1757 c -0.3271,0 -0.5933,-0.2662 -0.5933,-0.5933 0,-0.3271 0.2662,-0.5933 0.5933,-0.5933 h 0.3212 c 0.0552,0 0.1,-0.0448 0.1,-0.1 v -0.1073 c 0,-0.3677 0.2992,-0.6668 0.6668,-0.6668 0.0552,0 0.1,-0.0448 0.1,-0.1 v -0.0383 c 1e-4,-0.3516 0.2862,-0.6377 0.6378,-0.6377 0.3516,0 0.6377,0.2861 0.6377,0.6377 v 0.0383 c 0,0.0552 0.0448,0.1 0.1,0.1 h 0.3454 c 0.2576,0 0.4671,0.2095 0.4671,0.467 0,0.0552 0.0448,0.1 0.1,0.1 h 0.3058 c 0.5435,0 0.9856,0.4421 0.9856,0.9855 0,0.0552 0.0448,0.1 0.1,0.1 h 0.2791 c 0.1905,0 0.3455,0.155 0.3455,0.3456 0,0.1906 -0.155,0.3455 -0.3455,0.3455 h -1.4537 c -0.3147,0 -0.5708,0.2561 -0.5708,0.5708 v 0.6034 h 0.2 v -0.6034 c 0,-0.2044 0.1664,-0.3708 0.3708,-0.3708 h 1.4536 c 0.3008,0 0.5455,-0.2447 0.5455,-0.5455 0,-0.3008 -0.2447,-0.5456 -0.5455,-0.5456 h -0.1833 c -0.0509,-0.6071 -0.5613,-1.0855 -1.1814,-1.0855 h -0.2133 c -0.0484,-0.3205 -0.3258,-0.567 -0.6596,-0.567 h -0.2476 c -0.0317,-0.4332 -0.3943,-0.776 -0.8354,-0.776 -0.4431,0 -0.8069,0.3457 -0.8358,0.7815 -0.4319,0.0489 -0.7687,0.4165 -0.7687,0.8613 v 0.0073 h -0.2213 c -0.4375,0 -0.7933,0.3559 -0.7933,0.7933 0,0.4374 0.3559,0.7933 0.7933,0.7933 z" /><path
style="stroke-width:0.1"
inkscape:connector-curvature="0"
id="path4850"
d="m 39.701981,48.224352 v -1.3951 c 0,-0.0552 -0.0448,-0.1 -0.1,-0.1 v 0 c -0.0552,0 -0.1,0.0448 -0.1,0.1 v 1.3951 z" /><path
inkscape:connector-curvature="0"
id="path4844"
d="M 39.024337,48.577149 V 47.632751"
style="fill:none;stroke:#000000;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /></g></g></svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

7
package-lock.json generated

@ -1,5 +1,5 @@
{
"name": "webpack-test",
"name": "farm",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
@ -3462,6 +3462,11 @@
"safe-buffer": "~5.1.1"
}
},
"cookies-js": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/cookies-js/-/cookies-js-1.2.3.tgz",
"integrity": "sha1-AzFQSefFK+4/cxhqaRZ+qw3bLTE="
},
"copy-concurrently": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",

@ -45,6 +45,7 @@
"@fortawesome/fontawesome-svg-core": "^1.2.26",
"@fortawesome/free-solid-svg-icons": "^5.12.0",
"@fortawesome/react-fontawesome": "^0.1.8",
"cookies-js": "^1.2.3",
"mobx": "^5.15.3",
"mobx-react": "^6.1.4",
"react": "^16.12.0",

@ -34,9 +34,9 @@ class Chrome extends React.Component {
render() {
return (
<div className='flex-fullcenter'>
<div className='background-heading'><h1>Alpha Centauri Farming</h1></div>
{this.props.children}
<Tractor spikes={this.props.spikes} className={this.props.tractorClass} />
<div className='background-heading'><h1>Alpha Centauri Farming</h1></div>
{this.props.children}
<Tractor spikes={this.props.spikes} className={this.props.tractorClass} />
</div>
);
}
@ -49,31 +49,43 @@ class App extends React.Component {
case SCREENS.intro:
view = (<Chrome spikes={true} tractorClass='intro'><Welcome /></Chrome>);
break;
case SCREENS.start:
view = (<Chrome><CreateOrJoin /></Chrome>);
break;
case SCREENS.newGame:
view = (<Chrome>
case SCREENS.start:
view = (<Chrome><CreateOrJoin signOut={this.props.logout} /></Chrome>);
break;
case SCREENS.newGame:
view = (<Chrome>
<div className='view-container'>
<NewGame colors={['green', 'red', 'blue', 'yellow', 'black']}
button={'Start'}
title={'New Game'}
type={'new-game'}
showGameName={true} />
<NewGame colors={['green', 'red', 'blue', 'yellow', 'black']}
button={'Start'}
title={'New Game'}
type={'new-game'}
showGameName={true}
createAccount={this.props.createAccount}
login={this.props.login}
errors={this.props.errors}
/>
</div>
</Chrome>);
break;
case SCREENS.joinGame:
view = (
<Chrome>
<div className='view-container'>
<JoinGame createAccount={this.props.createAccount}
login={this.props.login}
errors={this.props.errors}
/>
</div>
</Chrome>);
break;
case SCREENS.joinGame:
view = (<Chrome><div className='view-container'><JoinGame /></div></Chrome>);
break;
case SCREENS.play:
view = (<Board />);
break;
}
return (
<Fragment>
{view}
<div id={messagePanelId}><MessagePanel /></div>
{view}
<div id={messagePanelId}><MessagePanel /></div>
</Fragment>
);
}

@ -0,0 +1,108 @@
// Copyright 2020 Thomas Hintz
//
// This file is part of the Alpha Centauri Farming project.
//
// The Alpha Centauri Farming project is free software: you can
// redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// The Alpha Centauri Farming project is distributed in the hope that
// it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with the Alpha Centauri Farming project. If not, see
// <https://www.gnu.org/licenses/>.
import React from 'react'
import { GroupBox, Row, Col, Button } from '../widgets.jsx'
export default class CreateAccount extends React.Component {
state = {
username: '',
email: '',
password: '',
confirmPassword: '',
loading: false
}
onChange = (e) => {
const target = e.target,
value = target.value,
name = target.name;
this.setState({
[name]: value
});
}
onSubmit = (e) => {
e.preventDefault();
this.setState({ loading: true });
this.props.createAccount(this.state);
}
componentDidUpdate(prevProps) {
if (this.state.loading && prevProps.errors !== this.props.errors) {
this.setState({ loading: false });
}
}
render() {
return (
<GroupBox title="Create Account">
<form onSubmit={this.onSubmit}>
<Row>
<Col width="12">
<label>
Username
<input onChange={this.onChange} required name="username" type="text" />
</label>
</Col>
</Row>
<Row>
<Col width="12">
<label>
Email (optional)
<input onChange={this.onChange} name="email" type="email" />
</label>
</Col>
</Row>
<Row>
<Col width="12">
<label>
Password
<input onChange={this.onChange} required name="password" type="password" />
</label>
</Col>
</Row>
<Row>
<Col width="12">
<label>
Confirm Password
<input onChange={this.onChange} required name="confirmPassword" type="password" />
</label>
</Col>
</Row>
<Row>
<Col width="12">
{!this.state.loading ? (
<>
{this.props.errors.map((err, i) => (
<p key={i}>
Error: {err}
</p>
))}
<Button type="submit">Create Account</Button>
</>
) : (
<span>Creating Account...</span>
)}
</Col>
</Row>
</form>
</GroupBox>
);
}
}

@ -19,21 +19,34 @@
import React, { Fragment } from 'react'
import { connect } from 'react-redux'
import Cookies from 'cookies-js'
import { GroupBox, Row, Col, Button } from '../widgets.jsx'
import { showNewGame, showJoinGame } from '../app/actions.js'
class CreateOrJoin extends React.Component {
signOut = (e) => {
e.preventDefault();
this.props.signOut();
Cookies.expire('awful-cookie');
}
render() {
return (
<Fragment>
<Button size='large' className='shadow' onClick={this.props.showNewGame}>
New Game
</Button>
{this.props.start.start.games.length > 0 ? (
<Button size='large' className='shadow' onClick={this.props.showJoinGame}>
Join Game
</Button>
) : (<Fragment />)}
<Button size='large' className='shadow' onClick={this.props.showNewGame}>
New Game
</Button>
{(this.props.start.start.games.length > 0) || (this.props.start.start.openGames.length > 0) ? (
<Button size='large' className='shadow' onClick={this.props.showJoinGame}>
Join Game
</Button>
) : (<Fragment />)}
{this.props.start.start.user ? (
<Button size='large' className='shadow sign-out-button' onClick={this.signOut}>
Sign Out
</Button>
) : (<></>)}
</Fragment>
);
}

@ -24,6 +24,7 @@ import WheatImg from './../../../assets/img/wheat.svg'
import TractorImg from './../../../assets/img/tractor-icon.svg'
import TractorFullImg from './../../../assets/img/tractor-with-spikes.svg'
import HarvesterImg from './../../../assets/img/harvester.svg'
import VolcanoImg from './../../../assets/img/volcano2.gif'
import React, { Fragment } from 'react'
import ReactDOM from 'react-dom'
@ -45,21 +46,21 @@ import { setSelectedCard, setMessagePanelSpace, setMPDims, movePlayer,
setMovingSkip } from './actions.js'
import { buy, roll, endTurn, loan, trade, submitTradeAccept,
submitTradeDeny, submitTradeCancel, audit,
buyUncleBert, skip, endAiTurn } from './interface.js'
buyUncleBert, skip, endAiTurn, startGame } from './interface.js'
function netWorth(player) {
return ((player.assets.hay + player.assets.grain) * 2000) +
(player.assets.fruit * 5000) +
(player.assets.cows * 500) +
((player.assets.harvester + player.assets.tractor) * 10000) +
(player.assets.fruit * 5000) +
(player.assets.cows * 500) +
((player.assets.harvester + player.assets.tractor) * 10000) +
player.displayCash - player.debt;
}
function assetsValue(player) {
return ((player.assets.hay + player.assets.grain) * 2000) +
(player.assets.fruit * 5000) +
(player.assets.cows * 500) +
((player.assets.harvester + player.assets.tractor) * 10000);
(player.assets.fruit * 5000) +
(player.assets.cows * 500) +
((player.assets.harvester + player.assets.tractor) * 10000);
}
function getElementValue(id) {
@ -464,20 +465,20 @@ class FarmsContainer extends React.Component {
return (
<GroupBox title='Farms'>
<b>Ridges</b>:
{ridgeNames.map((ridge, idx) => (
<div key={idx} className={"farms-ridge player-" + ridges['ridge' + (idx + 1)]}>
<span>{ridge}</span>
<span className='num-cows'>{'(' + ((idx + 2) * 10) + ' cows)'}</span>
</div>
))}
<br />
{this.props.otherPlayers
.map(p => (
<div key={p.player.name}>
<PlayerColorIcon color={p.player.color} />{'\u00A0'}
<b>{p.player.name}</b> <PlayerResources player={p.player} />
<br /> <br />
</div>))}
{ridgeNames.map((ridge, idx) => (
<div key={idx} className={"farms-ridge player-" + ridges['ridge' + (idx + 1)]}>
<span>{ridge}</span>
<span className='num-cows'>{'(' + ((idx + 2) * 10) + ' cows)'}</span>
</div>
))}
<br />
{this.props.otherPlayers
.map(p => (
<div key={p.player.name}>
<PlayerColorIcon color={p.player.color} />{'\u00A0'}
<b>{p.player.name}</b> <PlayerResources player={p.player} />
<br /> <br />
</div>))}
</GroupBox>
);
}
@ -521,11 +522,11 @@ const makeDefaultToTrade = () => {
class TradeContainer2 extends React.Component {
resources = [{ img: HayImg,
h: '120',
s: '100',
label: 'acres of Hay',
key: 'hay',
amount: 10
h: '120',
s: '100',
label: 'acres of Hay',
key: 'hay',
amount: 10
}, {
img: WheatImg,
h: '41',
@ -859,7 +860,7 @@ class CCBY extends React.Component {
render() {
return (
<Fragment>
License Creative Commons <a href='https://creativecommons.org/licenses/by/3.0/us/legalcode'>CCBY</a>
License Creative Commons <a href={`https://creativecommons.org/licenses/by/${this.props.version ? this.props.version : 3}.0/us/legalcode`}>CCBY</a>
</Fragment>
);
}
@ -880,6 +881,9 @@ class Misc extends React.Component {
<li>
<img src={TractorFullImg} /> <img src={TractorImg} /> Copyright Nick Roach with modifications by Thomas Hintz - License <a href='GPL http://www.gnu.org/copyleft/gpl.html'>GPL</a>
</li>
<li>
<img src={VolcanoImg} /> Copyright <a href="https://thenounproject.com/Maludk/">Laymik</a> - <CCBY />
</li>
<li>
<img src={CornImg} /> <img src={FruitImg} /> Copyright <a href='https://madexmade.com/'>Made</a> - <CCBY />
</li>
@ -1090,6 +1094,7 @@ class Die extends React.Component {
trigger = 0;
decayFactorPct = 0.4;
showScreenTimerId = false;
rollIndex = 0;
constructor(props) {
super(props);
@ -1107,12 +1112,16 @@ class Die extends React.Component {
}
roll() {
let roll = random(6);
while (roll === this.lastRoll) {
roll = random(6);
if (!this.props.rolls) {
let roll = random(6);
while (roll === this.lastRoll) {
roll = random(6);
}
this.lastRoll = roll;
return roll;
} else {
return this.props.rolls[++this.rollIndex];
}
this.lastRoll = roll;
return roll;
}
tick = () => {
@ -1272,6 +1281,7 @@ class Rolling extends React.Component {
return (
<GroupBox title={this.props.name + ' is rolling!'}>
<Die decay={true} num={this.props.num} ms={2000} roll={true}
rolls={this.props.rolls}
showScreen={this.props.showScreen}
skip={this.props.skip}
autoSkip={this.props.autoSkip}
@ -1354,6 +1364,7 @@ class Harvest extends React.Component {
break;
case 'roll':
view = (<Die decay={true} num={this.props.rolled} ms={2000} roll={true}
rolls={this.props.rolls}
showScreen={() => this.nextView('income')}
skip={this.props.player.name === this.props.game.currentPlayer}
autoSkip={this.props.autoSkip === 'die'}
@ -1569,6 +1580,7 @@ class Action extends React.Component {
break;
case 'harvest':
view = (<Harvest rolled={this.props.ui.actionValue.rolled}
rolls={this.props.ui.actionValue.rolls}
player={this.props.player}
currentPlayer={currentPlayer}
harvestMult={this.props.ui.actionValue.harvestMult}
@ -1635,6 +1647,7 @@ class Action extends React.Component {
(this.props.ui.actionValue.to < this.props.ui.actionValue.from ?
this.props.ui.actionValue.from - 49 : this.props.ui.actionValue.from);
view = (<Rolling num={roll}
rolls={this.props.ui.actionValue.rolls}
name={this.props.game.currentPlayer}
showScreen={(this.props.player.name === this.props.game.currentPlayer ||
currentPlayer.ai)
@ -1764,14 +1777,16 @@ class AlertOverlay extends React.Component {
render() {
return (
<div className={'alert-overlay' + (this.state.visible ? '' : ' hidden') }>
<div onClick={this.hide} className='alert-overlay-hide'>
<FontAwesomeIcon icon={faTimes} />
</div>
{!this.props.preventHiding ? (
<div onClick={this.hide} className='alert-overlay-hide'>
<FontAwesomeIcon icon={faTimes} />
</div>
) : (<></>)}
<div className='alert-overlay-contents'>
{this.props.children}
<br />
<Button onClick={this.buttonClick}>{this.props.buttonText}</Button>
<a onClick={this.hide}>close</a>
{!this.props.preventHiding ? (<a onClick={this.hide}>close</a>) : (<></>)}
</div>
</div>
);
@ -2015,6 +2030,30 @@ class BoardApp extends React.Component {
</Fragment>
</AlertOverlay>
);
} else if (alert && alert.type === ALERTS.preGame) {
alertOverlay = (
<AlertOverlay visible={true}
id={alert.id}
alertHandled={this.props.alertHandled}
buttonText='Start Game'
hideHandler={() => 'nothing'}
preventHiding={true}
handler={startGame}>
<Fragment>
<h1>Pre Game</h1>
<p>When all players have joined click 'Start Game'!</p>
<h3>Players</h3>
<ul>
<li>{this.props.player.name}</li>
{this.props.game.otherPlayers.map((p, i) => (
<li key={i}>
{p.player.name}
</li>
))}
</ul>
</Fragment>
</AlertOverlay>
);
} else if (alert && alert.type === ALERTS.proposedTrade) {
alertOverlay = (
<AlertOverlay visible={true}

@ -30,7 +30,8 @@ import { itemCard, fateCard } from 'game.js'
export { initialize, buy, roll, endTurn, loan, trade, submitTradeAccept,
submitTradeDeny, submitTradeCancel, audit, handleMessage,
nextAction, buyUncleBert, actionsFinished, skip, endAiTurn }
nextAction, buyUncleBert, actionsFinished, skip, endAiTurn,
startGame }
let store;
let movingTimer = 0;
@ -44,6 +45,13 @@ function handleMessage(evt) {
return;
}
batch(() => {
if (data.game.state === GAME_STATES.preGame) {
store.dispatch(alert(ALERTS.preGame, '', 'pre-game'));
}
if (store.getState().farm.game.state === GAME_STATES.preGame &&
data.game.state === GAME_STATES.preTurn) {
store.dispatch(alertHandled('pre-game'));
}
if (data.player.state === GAME_STATES.preTurn &&
data.game.otherPlayers.length > 0 &&
store.getState().farm.player.state !== GAME_STATES.preTurn) {
@ -186,6 +194,10 @@ function skip(component) {
sendCommand({ type: 'skip', component });
}
function startGame() {
sendCommand({ type: 'start-game' });
}
// TODO share with Board.jsx
// http://stackoverflow.com/questions/149055
function formatMoney(n) {

@ -23,6 +23,7 @@ import { GroupBox, Row, Col, Button } from '../widgets.jsx'
import { startOrJoinGame } from '../start/actions.js'
import { start } from '../app/actions.js'
import LoginOrCreateAccount from '../login-or-create-account/LoginOrCreateAccount.jsx';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faArrowCircleLeft } from '@fortawesome/free-solid-svg-icons'
@ -36,14 +37,15 @@ class JoinGame extends React.Component {
super(props);
this.state = {
screen: JoinGameScreens.list,
game: null
game: null,
showSignIn: false
};
}
handleClickGame = game => {
this.setState({ screen: JoinGameScreens.details,
game: game
});
});
}
handleBack = e => {
@ -54,59 +56,82 @@ class JoinGame extends React.Component {
}
}
handleJoinAsExisting = e => {
handleJoinAsExisting = (e, id) => {
e.preventDefault();
this.props.startOrJoinGame({ type: 'join-as-existing',
playerName: e.target.text,
gameId: this.state.game.id,
gameName: this.state.game.name });
gameId: id });
}
showSignIn = (e) => {
e.preventDefault();
this.setState(state => { return { showSignIn: !state.showSignIn }; });
}
render() {
return (
<GroupBox title={(
<Fragment>
<a href="#" onClick={this.handleBack}>
<FontAwesomeIcon icon={faArrowCircleLeft} />
</a>
Join Game
<a href="#" onClick={this.handleBack}>
<FontAwesomeIcon icon={faArrowCircleLeft} />
</a>
Join Game
</Fragment>
)}>
<Row>
<Row>
<Col width='12'>
{this.state.screen === JoinGameScreens.list ?
(<ul>
{this.state.screen === JoinGameScreens.list ?
(<>
<h3>My Games</h3>
{(!this.props.user && !this.state.showSignIn) ? (
<a onClick={this.showSignIn}>Sign In to see your games</a>
) : (<></>)}
{(!this.props.user && this.state.showSignIn) ? (
<>
<hr />
<LoginOrCreateAccount login={this.props.login}
createAccount={this.props.createAccount}
errors={this.props.errors}
showLogin={true}
/>
<hr />
</>
) : (<></>)}
<ul>
{this.props.games
.map((g, i) =>
(<li key={i}>
<a onClick={e => {
e.preventDefault();
this.handleClickGame(g); }}>{g.name}</a>
</li>))}
</ul>)
: (
<Fragment>
<h3><b>Game:</b> {this.state.game.name}</h3>
<h4>Join as existing player:</h4>
<ul>
{this.state.game.players.map((p, i) =>
(<li key={i}>
<a onClick={this.handleJoinAsExisting}>
{p}
</a>
</li>))}
</ul>
<NewGame colors={this.state.game.colors}
button={'Join'}
showGameName={false}
gameName={this.state.game.name}
gameId={this.state.game.id}
type={'join-game'}
hideBack={true}
title={'Join as New Player'} />
</Fragment>)}
</Col>
</Row>
.map((g, i) =>
(<li key={i}>
<a onClick={(e) => this.handleJoinAsExisting(e, g.id)}>{g.name}</a>
</li>))}
</ul>
<h3>Open Games</h3>
<ul>
{this.props.openGames
.map((g, i) =>
(<li key={i}>
<a onClick={e => {
e.preventDefault();
this.handleClickGame(g); }}>{g.name}</a>
</li>))}
</ul>
</>
)
: (
<Fragment>
<h3><b>Game:</b> {this.state.game.name}</h3>
<NewGame colors={this.state.game.colors}
button={'Join'}
showGameName={false}
gameName={this.state.game.name}
gameId={this.state.game.id}
type={'join-game'}
hideBack={true}
createAccount={this.props.createAccount}
login={this.props.login}
errors={this.props.errors}
title={'Join as New Player'} />
</Fragment>)}
</Col>
</Row>
</GroupBox>
);
}

@ -0,0 +1,50 @@
// Copyright 2020 Thomas Hintz
//
// This file is part of the Alpha Centauri Farming project.
//
// The Alpha Centauri Farming project is free software: you can
// redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// The Alpha Centauri Farming project is distributed in the hope that
// it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with the Alpha Centauri Farming project. If not, see
// <https://www.gnu.org/licenses/>.
import React from 'react'
import { GroupBox, Row, Col, Button } from '../widgets.jsx'
import CreateAccount from '../create-account/CreateAccount.jsx';
import Login from '../login/Login.jsx';
export default class LoginOrCreateAccount extends React.Component {
state = {
showLogin: !!this.props.showLogin
}
toggleLogin = (e) => {
e.preventDefault();
this.setState(state => { return { showLogin: !state.showLogin }; });
}
render() {
return (
<>
{this.state.showLogin ? (
<Login errors={this.props.errors} login={this.props.login} />
) : (
<CreateAccount errors={this.props.errors}
createAccount={this.props.createAccount} />
)}
<div className="center">
<a onClick={this.toggleLogin}>{this.state.showLogin ? 'Create Account' : 'Login'}</a>
</div>
</>
);
}
}

@ -0,0 +1,90 @@
// Copyright 2020 Thomas Hintz
//
// This file is part of the Alpha Centauri Farming project.
//
// The Alpha Centauri Farming project is free software: you can
// redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// The Alpha Centauri Farming project is distributed in the hope that
// it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with the Alpha Centauri Farming project. If not, see
// <https://www.gnu.org/licenses/>.
import React from 'react'
import { GroupBox, Row, Col, Button } from '../widgets.jsx'
export default class Login extends React.Component {
state = {
username: '',
password: '',
loading: false
}
onChange = (e) => {
const target = e.target,
value = target.value,
name = target.name;
this.setState({
[name]: value
});
}
onSubmit = (e) => {
e.preventDefault();
this.setState({ loading: true });
this.props.login(this.state);
}
componentDidUpdate(prevProps) {
if (this.state.loading && prevProps.errors !== this.props.errors) {
this.setState({ loading: false });
}
}
render() {
return (
<GroupBox title="Login">
<form onSubmit={this.onSubmit}>
<Row>
<Col width="12">
<label>
Username
<input onChange={this.onChange} required name="username" type="text" />
</label>
</Col>
</Row>
<Row>
<Col width="12">
<label>
Password
<input onChange={this.onChange} required name="password" type="password" />
</label>
</Col>
</Row>
<Row>
<Col width="12">
{!this.state.loading ? (
<>
{this.props.errors.map((err, i) => (
<p key={i}>
Error: {err}
</p>
))}
<Button type="submit">Login</Button>
</>
) : (
<span>Logging in...</span>
)}
</Col>
</Row>
</form>
</GroupBox>
);
}
}

@ -20,6 +20,7 @@ import React, { Fragment } from 'react'
import { connect } from 'react-redux'
import { GroupBox, Row, Col, Button } from '../widgets.jsx'
import LoginOrCreateAccount from '../login-or-create-account/LoginOrCreateAccount.jsx';
import { startOrJoinGame } from '../start/actions.js'
import { start } from '../app/actions.js'
@ -53,7 +54,6 @@ class NewGame extends React.Component {
super(props);
this.state = {
showSettings: false,
playerName: '',
checkedColor: props.colors[0],
gameId: typeof props.gameId === 'undefined' ? -1 : props.gameId,
gameName: props.gameName || '',
@ -63,7 +63,8 @@ class NewGame extends React.Component {
auditThreshold: 250000,
startingCash: 5000,
startingDebt: 5000,
trade: true
trade: true,
showLogin: false
};
}
@ -96,8 +97,7 @@ class NewGame extends React.Component {
}
render() {
let playerNameInput,
titleBar = !this.props.hideBack ? (
let titleBar = !this.props.hideBack ? (
<Fragment>
<a onClick={this.handleBack}>
<FontAwesomeIcon icon={faArrowCircleLeft} />
@ -129,97 +129,97 @@ class NewGame extends React.Component {
mainScreenClass = !this.state.showSettings ? '' : 'hidden';
return (
<GroupBox title={titleBar}>
<form onSubmit={this.handleSubmit}>
<div className={mainScreenClass}>
<Row>
<Col width='12'>
<label>Your Name
<input type='text' name='playerName'
required
value={this.state.playerName}
onChange={this.handleInputChange} />
</label>
</Col>
</Row>
<Row>
<Col width='12'>
<label>Your Color</label>
{colors}
<br /><br />
</Col>
</Row>
{gameName}
</div>
<div className={settingsClass}>
<InputRow label='Down Payment'
name='downPayment'
min={0}
max={1}
step={0.1}
value={this.state.downPayment}
onChange={this.handleInputChange} />
<InputRow label='Loan Interest'
name='loanInterest'
min={0}
max={1}
step={0.1}
value={this.state.loanInterest}
onChange={this.handleInputChange} />
<InputRow label='Maximum Debt'
name='maxDebt'
min={0}
step={5000}
value={this.state.maxDebt}
onChange={this.handleInputChange} />
<InputRow label='Audit Threshold'
name='auditThreshold'
min={0}
step={25000}
value={this.state.auditThreshold}
onChange={this.handleInputChange} />
<InputRow label='Starting Cash'
name='startingCash'
min={0}
step={1000}
value={this.state.startingCash}
onChange={this.handleInputChange} />
<InputRow label='Starting Debt'
name='startingDebt'
min={0}
step={1000}
value={this.state.startingDebt}
onChange={this.handleInputChange} />
<Row>
<Col width='12'>
<label>
<input type='checkbox'
checked={this.state.trade}
onChange={this.handleInputChange}
name='trade' />
Enable Trading
</label>
</Col>
</Row>
</div>
<Row>
<Col width='12'>
<div className='new-game-submit-container'>
<Button type='submit'>{this.props.button} Game</Button>
{this.props.showGameName ? (
<a onClick={this.toggleSettings}>
<FontAwesomeIcon icon={faCog} size='lg' />
</a>
) : (<Fragment />)}
</div>
</Col>
</Row>
</form>
{this.props.user ? (
<form onSubmit={this.handleSubmit}>
<div className={mainScreenClass}>
<Row>
<Col width='12'>
<label>Your Color</label>
{colors}
<br /><br />
</Col>
</Row>
{gameName}
</div>
<div className={settingsClass}>
<InputRow label='Down Payment'
name='downPayment'
min={0}
max={1}
step={0.1}
value={this.state.downPayment}
onChange={this.handleInputChange} />
<InputRow label='Loan Interest'
name='loanInterest'
min={0}
max={1}
step={0.1}
value={this.state.loanInterest}
onChange={this.handleInputChange} />
<InputRow label='Maximum Debt'
name='maxDebt'
min={0}
step={5000}
value={this.state.maxDebt}
onChange={this.handleInputChange} />
<InputRow label='Audit Threshold'
name='auditThreshold'
min={0}
step={25000}
value={this.state.auditThreshold}
onChange={this.handleInputChange} />
<InputRow label='Starting Cash'
name='startingCash'
min={0}
step={1000}
value={this.state.startingCash}
onChange={this.handleInputChange} />
<InputRow label='Starting Debt'
name='startingDebt'
min={0}
step={1000}
value={this.state.startingDebt}
onChange={this.handleInputChange} />
<Row>
<Col width='12'>
<label>
<input type='checkbox'
checked={this.state.trade}
onChange={this.handleInputChange}
name='trade' />
Enable Trading
</label>
</Col>
</Row>
</div>
<Row>
<Col width='12'>
<div className='new-game-submit-container'>
<Button type='submit'>{this.props.button} Game</Button>
{this.props.showGameName ? (
<a onClick={this.toggleSettings}>
<FontAwesomeIcon icon={faCog} size='lg' />
</a>
) : (<Fragment />)}
</div>
</Col>
</Row>
</form>
) : (
<>
<span>Sign in or create account to continue</span>
<LoginOrCreateAccount login={this.props.login}
createAccount={this.props.createAccount}
errors={this.props.errors}
/>
</>
)}
</GroupBox>
);
}
}
export default connect(
null,
state => state.start.start,
{ startOrJoinGame, start }
)(NewGame)

@ -17,4 +17,7 @@
// <https://www.gnu.org/licenses/>.
export const SET_START_GAMES = 'set-start-games';
export const SET_OPEN_GAMES = 'set-open-games';
export const SET_USER = 'set-user';
export const SET_ERRORS = 'set-errors';
export const START_OR_JOIN_GAME = 'start-or-join-game';

@ -16,16 +16,32 @@
// along with the Alpha Centauri Farming project. If not, see
// <https://www.gnu.org/licenses/>.
import { SET_START_GAMES, START_OR_JOIN_GAME } from './actionTypes.js'
import { SET_START_GAMES, START_OR_JOIN_GAME, SET_USER, SET_OPEN_GAMES,
SET_ERRORS } from './actionTypes.js'
export { setStartGames, startOrJoinGame }
export { setStartGames, startOrJoinGame, setUser, setOpenGames, setErrors }
function setStartGames(games) {
return { type: SET_START_GAMES,
games };
}
function setOpenGames(games) {
return { type: SET_OPEN_GAMES,
games };
}
function setUser(user) {
return { type: SET_USER,
user };
}
function startOrJoinGame(msg) {
return { type: START_OR_JOIN_GAME,
msg };
}
function setErrors(errors) {
return { type: SET_ERRORS,
errors };
}

@ -16,11 +16,15 @@
// along with the Alpha Centauri Farming project. If not, see
// <https://www.gnu.org/licenses/>.
import { SET_START_GAMES, START_OR_JOIN_GAME } from './actionTypes.js'
import { SET_START_GAMES, START_OR_JOIN_GAME, SET_USER, SET_OPEN_GAMES,
SET_ERRORS } from './actionTypes.js'
import { SCREENS } from '../../constants.js'
const initialState = {
start: { games: [] },
start: { games: [],
openGames: [],
errors: [],
user: false },
msg: null
};
@ -28,6 +32,12 @@ export default function(state = initialState, action) {
switch (action.type) {
case SET_START_GAMES:
return { ...state, start: { ...state.start, games: action.games }};
case SET_OPEN_GAMES:
return { ...state, start: { ...state.start, openGames: action.games }};
case SET_USER:
return { ...state, start: { ...state.start, user: action.user }};
case SET_ERRORS:
return { ...state, start: { ...state.start, errors: action.errors }};
case START_OR_JOIN_GAME:
return { ...state, msg: action.msg };
default:

@ -26,7 +26,8 @@ export const SCREENS = {
export const GAME_STATES = { preTurn: 'pre-turn',
midTurn: 'mid-turn',
turnEnded: 'turn-ended' };
turnEnded: 'turn-ended',
preGame: 'pre-game' };
export const rootId = 'initial-element';
export const messagePanelId = 'message-panel';
export const ALERTS = { beginTurn: 'begin-turn',
@ -34,4 +35,5 @@ export const ALERTS = { beginTurn: 'begin-turn',
endOfGame: 'end-of-game',
auditCalled: 'audit-called',
raiseMoney: 'raise-money',
proposedTrade: 'proposed-trade' }
proposedTrade: 'proposed-trade',
preGame: 'pre-game' }

@ -28,7 +28,8 @@ import { rootId } from './constants.js'
import App from './components/app/App.jsx'
import { initialize, handleMessage as handleMessageFarm } from './components/farm/interface.js'
import { setStartGames, startOrJoinGame } from './components/start/actions.js'
import { setStartGames, startOrJoinGame, setUser, setOpenGames,
setErrors } from './components/start/actions.js'
import { play } from './components/app/actions.js'
import { createStore } from 'redux'
import rootReducer from './rootReducers.js'
@ -46,9 +47,24 @@ document.body.appendChild(makeDiv(rootId));
window.store = store;
const createAccount = (msg) => {
Ws.sendCommand({ type: 'create-account', ...msg});
}
const login = (msg) => {
Ws.sendCommand({ type: 'login', ...msg});
}
const logout = () => {
Ws.sendCommand({ type: 'logout'});
}
ReactDOM.render(
<Provider store={store}>
<App />
<App createAccount={createAccount}
login={login}
logout={logout}
/>
</Provider>,
document.getElementById(rootId)
);
@ -78,6 +94,9 @@ function handleMessage(evt) {
store.dispatch(play());
} else if (data.event === 'start-init') {
store.dispatch(setStartGames(data.games.games));
store.dispatch(setOpenGames(data.openGames.games));
store.dispatch(setUser(data.user));
store.dispatch(setErrors(data.errors));
if (autostart) {
if (data.games.games.length === 0) {
store.dispatch(startOrJoinGame({ type: 'new-game',

@ -0,0 +1,134 @@
(use sql-de-lite crypt)
(define *db* "/home/tjhintz/db")
(define-syntax with-db
(syntax-rules ()
((_ (var) body ...)
(call-with-database *db*
(lambda (var)
body ...)))))
(define (create-tables)
(with-db (db)
(exec (sql db "create table users(id INTEGER PRIMARY KEY, username TEXT, email TEXT, password TEXT, salt TEXT);"))
(exec (sql db "create table sessions(bindings TEXT, session_id TEXT PRIMARY KEY);"))
(exec (sql db "create table games(id INTEGER PRIMARY KEY, status TEXT, object TEXT);"))
(exec (sql db "create table players(id INTEGER PRIMARY KEY, object TEXT);"))
(exec (sql db "create table user_games(user_id INTEGER, game_id INTEGER);"))))
(define (db-session-set! sid bindings)
(with-db (db)
(exec (sql db "insert or replace into sessions(bindings, session_id) values (?, ?);")
(with-output-to-string (lambda () (write bindings)))
sid)))
(define (db-session-ref sid)
(with-input-from-string
(or (alist-ref
'bindings
(with-db (db)
(query fetch-alist
(sql db "select * from sessions where session_id=?;")
sid)))
"#f")
read))
(define (add-user username email password)
(let ((salt (crypt-gensalt)))
(with-db (db)
(exec (sql db "insert into users(username, password, salt, email) values(?, ?, ?, ?);")
username (crypt password salt) salt email)
(last-insert-rowid db))))
(define (fetch-user username)
(with-db (db)
(query fetch-alist
(sql db "select * from users where username=?;")
username)))
(define (fetch-user-by-id id)
(with-db (db)
(query fetch-alist
(sql db "select * from users where id=?;")
id)))
(define (valid-password? username password)
(and-let* ((user (fetch-user username))
(_ (if (null? user)
(begin (crypt password "$2a$12$OW1wyLclJvq.PIxgoHCjdu")
#f)
#t)))
(string=? (crypt password (alist-ref 'salt user))
(alist-ref 'password user))))
(define (alist->string alist)
(with-output-to-string (lambda () (write alist))))
(define (string->alist s)
(with-input-from-string s read))
(define (db-add-game status object)
(with-db (db)
(exec (sql db "insert into games(status, object) values (?, ?);")
status (alist->string object))
(last-insert-rowid db)))
(define (db-update-game id status object)
(with-db (db)
(exec (sql db "replace into games(id, status, object) values (?, ?, ?);")
id status (alist->string object))))
(define (db-fetch-game id)
(string->alist
(with-db (db)
(query fetch-value
(sql db "select object from games where id=?;")
id))))
(define (db-fetch-open-games)
(map
string->alist
(with-db (db)
(query fetch-column
(sql db "select object from games where status=?;")
"pre-game"))))
(define (db-fetch-game-row id)
(let ((res
(with-db (db)
(query fetch-alist
(sql db "select * from games where id=?;")
id))))
`((id . ,(alist-ref 'id res))
(status . ,(alist-ref 'status res))
(object . ,(string->alist (alist-ref 'object res))))))
(define (db-add-player object)
(with-db (db)
(exec (sql db "insert into players(object) values (?);")
(alist->string object))
(last-insert-rowid db)))
(define (db-update-player id object)
(with-db (db)
(exec (sql db "replace into players(id, object) values (?, ?);")
id (alist->string object))))
(define (db-fetch-player id)
(string->alist
(with-db (db)
(query fetch-value
(sql db "select object from players where id=?;")
id))))
(define (db-add-user-game user-id game-id)
(with-db (db)
(exec (sql db "insert into user_games(user_id, game_id) values (?, ?);")
user-id game-id)))
(define (db-fetch-user-games user-id)
(with-db (db)
(query fetch-column
(sql db "select game_id from user_games where user_id=?;")
user-id)))

@ -29,6 +29,33 @@
(else
(include "game")))
(include "db.scm")
(session-storage-initialize
(lambda ()
'no-op))
(session-storage-set!
(lambda (sid session-item)
(db-session-set! sid (session-item-bindings session-item))))
(define (expiration)
(+ (current-milliseconds)
(inexact->exact (floor (* (session-lifetime) 1000)))))
(session-storage-ref
(lambda (sid)
(let ((data (db-session-ref sid)))
(if data
(make-session-item (expiration) (remote-address) data #f)
(error "session not found")))
;; (make-session-item (+ (current-milliseconds) 100000000) (remote-address) `((user-id . ,(db-session-ref sid))) #f)
))
(session-storage-delete!
(lambda (sid)
(error "session storage delete not handled")))
(root-path "./")
(define (neq? a b) (not (eq? a b)))
@ -55,6 +82,7 @@
(SRV:send-reply (pre-post-order* sxml rules)))))))
(define *game* (make-parameter #f))
(define *player* (make-parameter #f))
(define-syntax safe-set!
(ir-macro-transformer
@ -90,6 +118,7 @@
(next-year-rules initform: '() accessor: player-next-year-rules)
(color initform: #f accessor: player-color)
(name initform: "PLAYER X" accessor: player-name)
(user-id initform: #f accessor: player-user-id)
(trade initform: '() accessor: player-trade)
(last-updated initform: 0 accessor: player-last-updated)
(last-cash initform: 5000 accessor: player-last-cash)
@ -114,7 +143,7 @@
(colors initform: '() accessor: game-colors)
(last-updated initform: 0 accessor: game-last-updated)
(called-audit initform: #f accessor: game-called-audit)
(state initform: 'playing accessor: game-state)
(state initform: 'pre-game accessor: game-state)
(name initform: "game" accessor: game-name)
(turn initform: 1 accessor: game-turn)
(current-player initform: #f accessor: game-current-player)
@ -154,6 +183,7 @@
(next-year-rules . ,(player-next-year-rules player))
(color . ,(player-color player))
(name . ,(player-name player))
(user-id . ,(player-user-id player))
(trade . ())
(last-updated . 0)
(last-cash . ,(player-cash player))
@ -196,11 +226,11 @@
*operating-expense-cards*)))
'called-audit (if (alist-ref 'called-audit x)
(find (lambda (p)
(string=? (player-name p) (alist-ref 'called-audit x)))
(equal? (player-name p) (alist-ref 'called-audit x)))
players)
#f)
'current-player (find (lambda (p)
(string=? (player-name p) (alist-ref 'current-player x)))
(equal? (player-name p) (alist-ref 'current-player x)))
players)
(fold (lambda (k r) (cons k (cons (alist-ref k x) r)))
'()
@ -221,6 +251,10 @@
(lambda ()
(write (app->sexp *app*)))))
(define (save-game game)
(db-update-game (game-id game) (symbol->string (game-state game))
(game->sexp game)))
(define (load-app)
(with-input-from-file "/home/tjhintz/app.scm"
(lambda ()
@ -236,7 +270,7 @@
(fold (lambda (k r) (cons k (cons (alist-ref k x) r)))
'()
'(cash debt space previous-space state assets ridges
harvest-mult otbs
harvest-mult otbs user-id
year-rules next-year-rules hay-doubled corn-doubled
color name trade last-updated last-cash))))
@ -310,13 +344,14 @@
(safe-set! (game-colors game) (cdr (game-colors game)))
color))
(define (add-player-to-game game color name)
(define (add-player-to-game game color name user-id)
(let ((player (make <player>
'cash (game-setting 'starting-cash game)
'display-cash (game-setting 'starting-cash game)
'debt (game-setting 'starting-debt game)
'color color
'name name
'user-id user-id
'state (if (= (length (game-players game)) 0)
'pre-turn 'turn-ended))))
(safe-set! (game-players game) (append (game-players game) (list player)))
@ -441,6 +476,7 @@
(player-otbs p))))
(color . ,(symbol->string (player-color p)))
(name . ,(player-name p))
(user-id . ,(player-user-id p))
(trade . ,(player-trade p))
(lastCash . ,(player-last-cash p))
(hayDoubled . ,(player-hay-doubled p))
@ -459,6 +495,7 @@
(player-otbs p))))
(color . ,(symbol->string (player-color p)))
(name . ,(player-name p))
(user-id . ,(player-user-id p))
(trade . ,(player-trade p))
(lastCash . ,(player-last-cash p))
(hayDoubled . ,(player-hay-doubled p))
@ -535,13 +572,23 @@
(define (finish-year player #!optional (collect-wages #t))
(let ((game (*game*)))
(when collect-wages
(safe-set! (player-cash player)
(+ (player-cash player) 5000))
(safe-set! (player-display-cash player) (player-cash player))
(safe-set! (game-actions game)
(cons '((?action . info)
(?value . "You earned $5,000 from your city job!"))
(game-actions game))))
(let* ((richest (car (sort (game-players game)
(lambda (p1 p2)
(> (player-net-worth p1)
(player-net-worth p2))))))
(bonus (max (farming-round
(* (- (player-net-worth richest)
(player-net-worth player))
0.2))
2500)))
(safe-set! (player-cash player)
;; (+ (player-cash player) 5000)
(+ (player-cash player) bonus))
(safe-set! (player-display-cash player) (player-cash player))
(safe-set! (game-actions game)
(cons `((?action . info)
(?value . ,(conc "You earned $" bonus " from your city job!")))
(game-actions game)))))
(when (game-called-audit game)
(safe-set! (game-actions game)
(append (game-actions game)
@ -810,7 +857,7 @@
(player->list player)
(game->list (*game*) player)))
(define (create-start-response event)
(define (create-start-response event #!key (errors '()))
`((event . ,event)
(games . ((games . ,(list->vector
(map (lambda (game)
@ -820,7 +867,24 @@
(map symbol->string (game-colors game))))
(players . ,(list->vector
(map player-name (game-players game))))))
(app-games *app*))))))))
(map (lambda (gid)
(sexp->game (db-fetch-game gid)))
(db-fetch-user-games (session-ref (sid) 'user-id -1))))))))
(openGames . ((games . ,(list->vector
(map (lambda (game)
`((name . ,(game-name game))
(id . ,(game-id game))
(colors . ,(list->vector
(map symbol->string (game-colors game))))
(players . ,(list->vector
(map player-name (game-players game))))))
(map sexp->game (db-fetch-open-games)))))))
(user . ,(let ((id (session-ref (sid) 'user-id #f)))
(if (and id (not (equal? id -1)))
id
#f)))
(errors . ,(list->vector errors))))
(define (message-players! game player message #!key (type "action"))
(for-each (lambda (p)
(when (not (eq? p player))
@ -855,13 +919,35 @@
(safe-set! (player-display-cash player) (player-cash player)))
(game-players game))))
(define (find-game id)
(let ((game-in-memory (find (lambda (g) (= (game-id g) id))
(app-games *app*))))
(if game-in-memory
game-in-memory
(let ((db-game (sexp->game (db-fetch-game id))))
(push! db-game (app-games *app*))
db-game))))
(define (next-roll last-roll)
(let ((roll (+ (random 6) 1)))
(if (= roll last-roll)
(next-roll last-roll)
roll)))
(define (make-rolls n)
(define (_make-rolls n i rolls)
(if (<= i n)
(_make-rolls n (+ i 1) (cons (next-roll (car rolls)) rolls))
rolls))
(_make-rolls n 1 (list (next-roll -1))))
(define (process-message player game type msg)
(when game
(safe-set! (game-messages game) '())
(when player
(safe-set! (player-last-cash player) (player-cash player)))
(print "message type: " type)
(cond ((string=? type "roll")
(let ((num (+ (random 6) 1)))
(let ((num (+ (random 6) 1))
(rolls (make-rolls 22)))
(when *next-roll* (set! num *next-roll*))
(safe-set! (player-previous-space player)
(player-space player))
@ -876,7 +962,8 @@
(finish-year player))
(safe-set! (player-harvest-mult player) 1)
(let ((resp `((from . ,(player-previous-space player))
(to . ,(player-space player)))))
(to . ,(player-space player))
(rolls . ,(list->vector rolls)))))
(safe-set! (game-actions game)
(append (game-actions game)
`(((?action . move) (?value . ,resp))
@ -1169,7 +1256,7 @@
(print exn)
(print-error-message exn)
(print "error saving app"))
(save-app))
(save-game game))
(if (eq? (game-state game) 'finished)
(do-end-of-game game)
(message-players! game player '() type: "update"))
@ -1180,6 +1267,7 @@
(create-start-response "start-init"))
((string=? type "new-game")
(let* ((color (string->symbol (alist-ref 'checkedColor msg)))
(user (fetch-user-by-id (session-ref (sid) 'user-id)))
(game (make <game> 'colors (filter (cut neq? <> color)
'(green red blue yellow black))
'name (alist-ref 'gameName msg)
@ -1200,49 +1288,81 @@
(trade . ,(or (alist-ref 'trade msg) #t)))))
(player (add-player-to-game game
color
(alist-ref 'playerName msg)))
(alist-ref 'username user)
(alist-ref 'id user)))
;; (ai-player (add-ai-to-game game 'red "AI Player 1"))
)
(push! game (app-games *app*))
(session-set! (sid) 'player player)
(session-set! (sid) 'game game)
(let ((gid (db-add-game "pre-game" (game->sexp game))))
(safe-set! (game-id game) gid)
(db-update-game gid "pre-game" (game->sexp game))
(db-add-user-game (alist-ref 'id user) (game-id game))
(session-set! (sid) 'game-id (game-id game)))
(*game* game)
(*player* player)
(set-startup-otbs game player 2)
;; (set-startup-otbs game ai-player 2)
;; (thread-start! (make-ai-push-receiver game ai-player))
(create-start-response "new-game-started")))
((string=? type "join-game")
(let* ((name (alist-ref 'gameName msg))
(let* ((user (fetch-user-by-id (session-ref (sid) 'user-id)))
(name (alist-ref 'username user))
(id (alist-ref 'gameId msg))
(game (find (lambda (g) (= (game-id g) id))
(app-games *app*)))
(game (find-game id))
(color-raw (string->symbol (alist-ref 'checkedColor msg)))
(color (if (not (member color-raw (game-colors game)))
(car (game-colors game))
color-raw))
(player (add-player-to-game game
color
(alist-ref 'playerName msg))))
(alist-ref 'username user)
(alist-ref 'id user))))
(safe-set! (game-colors game) (filter (cut neq? <> color) (game-colors game)))
(session-set! (sid) 'player player)
(session-set! (sid) 'game game)
(session-set! (sid) 'game-id (game-id game))
(db-add-user-game (alist-ref 'id user) (game-id game))
(*game* game)
(*player* player)
(set-startup-otbs game player 2)
(message-players! game player '() type: "update")
(create-start-response "new-game-started")))
((string=? type "join-as-existing")
(let* ((name (alist-ref 'gameName msg))
(pname (alist-ref 'playerName msg))
(id (alist-ref 'gameId msg))
(game (find (lambda (g) (= (game-id g) id))
(app-games *app*)))
(player (find (lambda (p) (string=? (player-name p) pname))
(let* ((id (alist-ref 'gameId msg))
(user-id (session-ref (sid) 'user-id))
(game (find-game id))
(player (find (lambda (p) (equal? (player-user-id p) user-id))
(game-players game))))
(session-set! (sid) 'player player)
(session-set! (sid) 'game game)
(*game* game)
(*player* player)
(create-start-response "new-game-started")))
))
((string=? type "create-account")
(let ((username (alist-ref 'username msg))
(email (alist-ref 'email msg))
(password (alist-ref 'password msg))
(confirm-password (alist-ref 'confirmPassword msg)))
(if (string=? password confirm-password)
(if (null? (fetch-user username))
(let ((id (add-user username email password)))
(session-set! (sid) 'user-id id)
(create-start-response "start-init"))
(create-start-response "start-init" errors: '("Account already exists")))
(create-start-response "start-init" errors: '("Passwords don't match")))))
((string=? type "login")
(let ((username (alist-ref 'username msg))
(password (alist-ref 'password msg)))
(if (valid-password? username password)
(begin (session-set! (sid) 'user-id (alist-ref 'id (fetch-user username)))
(create-start-response "start-init"))
(create-start-response "start-init" errors: '("Invalid password or account doesn't exist")))))
((string=? type "logout")
(session-set! (sid) 'game-id #f)
(session-set! (sid) 'user-id #f)
(create-start-response "start-init"))
((string=? type "start-game")
(safe-set! (game-state (*game*)) 'pre-turn)
(db-update-game (game-id (*game*)) (symbol->string (game-state (*game*)))
(game->sexp (*game*)))
(message-players! (*game*) (*player*) '() type: "update")
(create-ws-response (*player*) "update" '()))))
(define (process-ai-push-message player game msg)
(print (player-name player))
@ -1297,6 +1417,18 @@
(process-ai-push-message player game msg)
(loop (mailbox-receive! (player-mailbox player))))))
(define (session-game)
(let ((user-id (session-ref (sid) 'user-id)))
(if (and (not (*game*)) (session-ref (sid) 'game-id #f))
(let ((possible-game (find-game (session-ref (sid) 'game-id))))
(when possible-game
(*game* possible-game)
(*player* (find (lambda (p)
(equal? (player-user-id p) user-id))
(game-players (*game*))))
(*game*)))
(and (*game*)))))
(define (websocket-page)
(sid (read-cookie (session-cookie-name)))
;; TODO some kind of error handling if (sid) #f
@ -1321,16 +1453,28 @@
(print-call-chain)
(print-error-message exn))))
(event . "error"))
(let* ((game (session-ref (sid) 'game #f))
(player (session-ref (sid) 'player #f))
(res (process-message player
(session-game)
(let* ((game (*game*))
(res (process-message (*player*)
game
(alist-ref 'type msg)
msg)))
(when game
(safe-set! (game-last-updated game) (+ (game-last-updated game) 1))
(safe-set! (player-last-updated player) (game-last-updated game)))
res)))))
(when (*player*)
(safe-set! (player-last-updated (*player*)) (game-last-updated game))))
res)
;; (let* ((game (session-ref (sid) 'game #f))
;; (player (session-ref (sid) 'player #f))
;; (res (process-message player
;; game
;; (alist-ref 'type msg)
;; msg)))
;; (when game
;; (safe-set! (game-last-updated game) (+ (game-last-updated game) 1))
;; (safe-set! (player-last-updated player) (game-last-updated game)))
;; res)
))))
(loop (read-json (receive-message)))))))
(define (push-websocket-page)
@ -1338,39 +1482,33 @@
;; TODO some kind of error handling if (sid) #f
(with-concurrent-websocket
(lambda ()
(let ((game (session-ref (sid) 'game))
(player (session-ref (sid) 'player)))
(*game* game)
(let loop ((msg (mailbox-receive! (player-mailbox player))))
(print msg)
(when (not game)
(set! game (session-ref (sid) 'game)))
(when (not player)
(set! player (session-ref (sid) 'player)))
;; when (< (player-last-updated player)
;; (game-last-updated game))
(handle-exceptions
exn
(send-message
(json->string
(session-game)
(let loop ((msg (mailbox-receive! (player-mailbox (*player*)))))
(session-game)
;; when (< (player-last-updated player)
;; (game-last-updated game))
(handle-exceptions
exn
(send-message
(json->string
`((exn . ,(with-output-to-string
(lambda ()
(print-call-chain)
(print-error-message exn)))))))
(send-message
(json->string
(handle-exceptions
exn
`((exn . ,(with-output-to-string
(lambda ()
(print-call-chain)
(print-error-message exn)))))))
(send-message
(json->string
(handle-exceptions
exn
`((exn . ,(with-output-to-string
(lambda ()
(print-call-chain)
(print-error-message exn))))
(event . "error"))
(create-ws-response player
(alist-ref 'type msg)
(alist-ref 'value msg))
))))
(loop (mailbox-receive! (player-mailbox player))))))))
(print-error-message exn))))
(event . "error"))
(create-ws-response (*player*)
(alist-ref 'type msg)
(alist-ref 'value msg))
))))
(loop (mailbox-receive! (player-mailbox (*player*))))))))
(define (otb-spec->otb-cards spec id)
`((contents . ,(sxml->html* (list-ref spec 5)))
@ -2034,6 +2172,7 @@
(game-players game)))))
((alist-ref 'action operating-expense) player)
`((rolled . ,rolled)
(rolls . ,(list->vector (make-rolls 22)))
(income . ,income)
(harvestMult . ,harvest-mult)
(operatingExpense . ,(alist-ref 'contents operating-expense))

@ -141,6 +141,8 @@ $tab-margin: 0.3rem;
align-items: center; }
.view-container {
max-height: 90vh;
overflow: auto;
border: 0.3rem solid $dark-color;
background: linear-gradient(180deg, $light-color 0%, $orange-color 100%);
padding: 1rem;
@ -919,3 +921,75 @@ $intro-time: 6s;
.pad-right {
padding-right: 0.3rem; }
.sign-out-button {
position: absolute;
bottom: 1rem; }
/* -------- MENU ------- */
/* Position and sizing of burger button */
.bm-burger-button {
position: fixed;
width: 36px;
height: 30px;
left: 36px;
top: 36px;
}
/* Color/shape of burger icon bars */
.bm-burger-bars {
background: #373a47;
}
/* Color/shape of burger icon bars on hover*/
.bm-burger-bars-hover {
background: #a90000;
}
/* Position and sizing of clickable cross button */
.bm-cross-button {
height: 24px;
width: 24px;
}
/* Color/shape of close button cross */
.bm-cross {
background: #bdc3c7;
}
/*
Sidebar wrapper styles
Note: Beware of modifying this element as it can break the animations - you should not need to touch it in most cases
*/
.bm-menu-wrap {
position: fixed;
height: 100%;
}
/* General sidebar styles */
.bm-menu {
background: #373a47;
padding: 2.5em 1.5em 0;
font-size: 1.15em;
}
/* Morph shape necessary with bubble or elastic */
.bm-morph-shape {
fill: #373a47;
}
/* Wrapper for item list */
.bm-item-list {
color: #b8b7ad;
padding: 0.8em;
}
/* Individual item */
.bm-item {
display: inline-block;
}
/* Styling of overlay */
.bm-overlay {
background: rgba(0, 0, 0, 0.3);
}

@ -22,7 +22,9 @@ export { sendCommand, setMainOnMessage, setSecondaryOnMessage,
class WebSocketConnection {
location = '';
fullPath = '';
reopen = true;
onmessageProc = () => 'nothing'
ws = null;
constructor(location) {
@ -35,6 +37,7 @@ class WebSocketConnection {
uri = "ws:";
}
uri += "//" + loc.host;
this.fullPath = uri + '/websocket/' + location;
this.ws = new WebSocket(uri + '/websocket/' + location);
}
@ -47,8 +50,17 @@ class WebSocketConnection {
this.ws.send(JSON.stringify(cmd));
}
reestablish = () => {
this.ws = new WebSocket(this.fullPath);
this.ws.onmessage = this.onmessageProc;
}
onmessage(proc) {
this.onmessageProc = proc;
this.ws.onmessage = proc;
// this.ws.onclose = () => {
// setTimeout(this.reestablish, 500);
// }
}
onopen(proc) {

@ -72,7 +72,7 @@ module.exports = {
}
},
{
test: /\.(woff|woff2|eot|ttf|otf|svg|png)$/,
test: /\.(woff|woff2|eot|ttf|otf|svg|png|gif)$/,
use: [
'file-loader',
],

Loading…
Cancel
Save