Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 945011c052 | |||
| c764b63d10 | |||
| a4c41785ce | |||
| 526c4ae365 | |||
| 9fdbcc34f9 | |||
| 17723fc52c | |||
| 1b9e94ba1f | |||
| 191a6081b7 | |||
| 3a97c128b6 | |||
| b2d0dc3268 | |||
| a38c55e877 | |||
| 6219e65e66 | |||
| 92eb1d9653 | |||
| dd644387f1 | |||
| 4cbfe0b394 | |||
| 5609720876 | |||
| cb983ae1c9 | |||
| 99b60b5aba | |||
| ce0070ed7e | |||
| 9160fdc92a | |||
| 802a0efcbb | |||
| 856ab034d5 | |||
| 11361fd814 | |||
| b22aea2507 | |||
| 70ed9465b8 | |||
| 1f5b1e1eae | |||
| 48b178327b | |||
| fbc8706893 | |||
| db47e5e6e9 | |||
| 250fdb9ca7 | |||
| d6692bc64d | |||
| ae8d0d8193 | |||
| bea133bee4 | |||
| c34fc7dcbe | |||
| 2599c6fa33 | |||
| 90f4e959cd | |||
| 54ec20ab6b | |||
| 2f770d2d63 | |||
| a5c13d40eb | |||
| f43d9e5d19 | |||
| 80399ac7b1 | |||
| a066a507ef | |||
| 8893a50596 | |||
| 2a6a0b038e | |||
| 7ba6f19133 | |||
| 70d8b50ac3 | |||
| b06d1b4513 | |||
| ced8faf6f6 | |||
| afd548ae0b | |||
| 775ec7ed70 | |||
| 333bd70d1a | |||
| 197e4ffc5d |
3
.babelrc
3
.babelrc
@@ -21,5 +21,6 @@
|
||||
"@babel/preset-react"
|
||||
],
|
||||
"plugins": [["@babel/plugin-proposal-decorators", { "legacy": true }],
|
||||
"@babel/plugin-proposal-class-properties"]
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"emotion"]
|
||||
}
|
||||
|
||||
5
.dockerignore
Normal file
5
.dockerignore
Normal file
@@ -0,0 +1,5 @@
|
||||
/node_modules/
|
||||
/node_modules_bak/
|
||||
/dist/
|
||||
*~
|
||||
/src/server/farm
|
||||
58
Dockerfile
Normal file
58
Dockerfile
Normal file
@@ -0,0 +1,58 @@
|
||||
FROM alpine as chicken
|
||||
|
||||
ENV CHICKEN_VERSION 4.11.0
|
||||
ENV PLATFORM linux
|
||||
|
||||
RUN set -eux; \
|
||||
apk update; \
|
||||
apk --no-cache --update add build-base; \
|
||||
wget -qO- https://code.call-cc.org/releases/${CHICKEN_VERSION}/chicken-${CHICKEN_VERSION}.tar.gz | tar xzv; \
|
||||
cd /chicken-${CHICKEN_VERSION}; \
|
||||
make PLATFORM=${PLATFORM}; \
|
||||
make PLATFORM=${PLATFORM} install; \
|
||||
make PLATFORM=${PLATFORM} check; \
|
||||
cd /; \
|
||||
rm -rf /chicken-${CHICKEN_VERSION}
|
||||
|
||||
FROM chicken as deps
|
||||
RUN chicken-install http-session srfi-69 coops uri-common srfi-18 medea numbers spiffy spiffy-cookies sql-de-lite crypt intarweb sxml-transforms websockets miscmacros
|
||||
|
||||
FROM deps as deps2
|
||||
RUN chicken-install -r pll; \
|
||||
cd pll; \
|
||||
sed -i '1s/^/(import scheme)\n/' amb.scm; \
|
||||
chicken-install
|
||||
|
||||
FROM deps2 as compat
|
||||
RUN apk --no-cache --update add libc6-compat
|
||||
|
||||
FROM node:16 as node
|
||||
WORKDIR /farm
|
||||
# COPY ./ /farm
|
||||
COPY package.json package.json
|
||||
COPY package-lock.json package-lock.json
|
||||
RUN npm install
|
||||
|
||||
FROM node as buildfe
|
||||
WORKDIR /farm
|
||||
COPY ./ /farm
|
||||
RUN make prodfe
|
||||
|
||||
FROM compat as buildfarm
|
||||
WORKDIR /farm
|
||||
COPY ./ /farm
|
||||
RUN make farm
|
||||
|
||||
FROM buildfarm as farm
|
||||
WORKDIR /farm
|
||||
RUN mkdir dist
|
||||
COPY --from=buildfe /farm/dist /farm/dist
|
||||
RUN cp src/server/farm dist/; \
|
||||
chmod +x dist/farm
|
||||
|
||||
FROM farm as run
|
||||
WORKDIR /farm/dist
|
||||
ENTRYPOINT ["./farm"]
|
||||
CMD ["-:a50"]
|
||||
# CMD ./farm
|
||||
|
||||
24
Makefile
24
Makefile
@@ -17,38 +17,54 @@
|
||||
# along with the Alpha Centauri Farming project. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
|
||||
.PHONY: clean install interactive
|
||||
.PHONY: clean install interactive cypress
|
||||
|
||||
assets := assets/game/acf/
|
||||
|
||||
docker:
|
||||
sudo docker build --network=host . -t farm
|
||||
|
||||
dev:
|
||||
npx webpack --config webpack.dev.js --env.assets ./$(assets)
|
||||
|
||||
rundev:
|
||||
webpack-dev-server --open --config webpack.dev.js --env.assets ./$(assets)
|
||||
# make interactive
|
||||
|
||||
rundevserver:
|
||||
make interactive
|
||||
|
||||
prod: src/server/farm
|
||||
npx webpack --config webpack.prod.js --env.assets ./$(assets)
|
||||
|
||||
prodfe:
|
||||
npx webpack --config webpack.prod.js --env.assets ./$(assets)
|
||||
|
||||
install:
|
||||
npm init -y
|
||||
npm install
|
||||
|
||||
interactive:
|
||||
cd dist/ && csi -include-path $(assets) -include-path ../src/server -s farm.scm
|
||||
csi -include-path $(assets) -include-path src/server -s src/server/farm.scm
|
||||
|
||||
src/server/farm: src/server/farm.scm
|
||||
interactiveserver:
|
||||
cd dist ; csi -include-path $(assets) -include-path ../src/server -s farm.scm
|
||||
|
||||
src/server/farm: src/server/farm.scm src/server/db.scm
|
||||
cd src/server/ && csc -include-path ../../$(assets) -O3 farm.scm
|
||||
|
||||
farm:
|
||||
make src/server/farm
|
||||
|
||||
runprod:
|
||||
cd dist/ && chmod +x farm && ./farm
|
||||
cd dist/ && chmod +x farm && ./farm -:a50
|
||||
|
||||
upload:
|
||||
rsync -rtvz dist/ $(SERVER):~/farm
|
||||
|
||||
cypress:
|
||||
npm run cypress:open
|
||||
|
||||
clean:
|
||||
rm -f *~ res/js/app.js
|
||||
|
||||
|
||||
BIN
assets/font/IndieFlower-Regular.ttf
Normal file
BIN
assets/font/IndieFlower-Regular.ttf
Normal file
Binary file not shown.
BIN
assets/font/IndieFlower-Regular.woff
Normal file
BIN
assets/font/IndieFlower-Regular.woff
Normal file
Binary file not shown.
BIN
assets/font/IndieFlower-Regular.woff2
Normal file
BIN
assets/font/IndieFlower-Regular.woff2
Normal file
Binary file not shown.
BIN
assets/font/LibreBaskerville-Regular.ttf
Normal file
BIN
assets/font/LibreBaskerville-Regular.ttf
Normal file
Binary file not shown.
BIN
assets/font/LibreBaskerville-Regular.woff
Normal file
BIN
assets/font/LibreBaskerville-Regular.woff
Normal file
Binary file not shown.
BIN
assets/font/LibreBaskerville-Regular.woff2
Normal file
BIN
assets/font/LibreBaskerville-Regular.woff2
Normal file
Binary file not shown.
BIN
assets/font/LibreBaskerville-Regular.zip
Normal file
BIN
assets/font/LibreBaskerville-Regular.zip
Normal file
Binary file not shown.
BIN
assets/font/Libre_Baskerville.zip
Normal file
BIN
assets/font/Libre_Baskerville.zip
Normal file
Binary file not shown.
BIN
assets/font/Libre_Baskerville/LibreBaskerville-Bold.ttf
Normal file
BIN
assets/font/Libre_Baskerville/LibreBaskerville-Bold.ttf
Normal file
Binary file not shown.
BIN
assets/font/Libre_Baskerville/LibreBaskerville-Italic.ttf
Normal file
BIN
assets/font/Libre_Baskerville/LibreBaskerville-Italic.ttf
Normal file
Binary file not shown.
BIN
assets/font/Libre_Baskerville/LibreBaskerville-Regular.ttf
Normal file
BIN
assets/font/Libre_Baskerville/LibreBaskerville-Regular.ttf
Normal file
Binary file not shown.
94
assets/font/Libre_Baskerville/OFL.txt
Normal file
94
assets/font/Libre_Baskerville/OFL.txt
Normal file
@@ -0,0 +1,94 @@
|
||||
Copyright (c) 2012, Pablo Impallari (www.impallari.com|impallari@gmail.com),
|
||||
Copyright (c) 2012, Rodrigo Fuenzalida (www.rfuenzalida.com|hello<6C>rfuenzalida.com), with Reserved Font Name Libre Baskerville.
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
93
assets/font/OFL.txt
Normal file
93
assets/font/OFL.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
Copyright (c) 2010, Kimberly Geswein (kimberlygeswein.com)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
BIN
assets/font/fonts/LibreBaskerville-Regular.woff
Normal file
BIN
assets/font/fonts/LibreBaskerville-Regular.woff
Normal file
Binary file not shown.
BIN
assets/font/fonts/LibreBaskerville-Regular.woff2
Normal file
BIN
assets/font/fonts/LibreBaskerville-Regular.woff2
Normal file
Binary file not shown.
@@ -47,7 +47,7 @@
|
||||
"20 Cows on Peridier Ridge"))))
|
||||
|
||||
(define *ff-text*
|
||||
'(((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 (img (@ (src "./assets/img/volcano2.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.")))
|
||||
|
||||
45
assets/img/cake.svg
Normal file
45
assets/img/cake.svg
Normal file
@@ -0,0 +1,45 @@
|
||||
<?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 80.125 79.983627"
|
||||
xml:space="preserve"
|
||||
id="svg4786"
|
||||
sodipodi:docname="cake.svg"
|
||||
width="80.125"
|
||||
height="79.983627"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><metadata
|
||||
id="metadata4792"><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="defs4790" /><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="namedview4788"
|
||||
showgrid="false"
|
||||
inkscape:zoom="9.424"
|
||||
inkscape:cx="40.23"
|
||||
inkscape:cy="27.714"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4786" /><path
|
||||
d="M 79.125,21.572625 H 62.26 v -7.523 c 2.469,-0.565 4.319,-2.775 4.319,-5.4129999 0,-2.758 -4.007,-7.411 -4.811,-8.31599999 -0.379,-0.428 -1.116,-0.427 -1.496,0 -0.803,0.90499999 -4.81,5.55899999 -4.81,8.31599999 0,2.8059999 2.092,5.1269999 4.798,5.4989999 v 7.437 H 48.247 v -7.523 c 2.469,-0.565 4.319,-2.775 4.319,-5.4129999 0,-2.758 -4.007,-7.411 -4.811,-8.31599999 -0.38,-0.428 -1.116,-0.427 -1.496,0 -0.803,0.90499999 -4.81,5.55899999 -4.81,8.31599999 0,2.8059999 2.092,5.1269999 4.798,5.4989999 v 7.437 H 34.233 v -7.523 c 2.469,-0.565 4.319,-2.775 4.319,-5.4129999 0,-2.758 -4.007,-7.411 -4.811,-8.31599999 -0.379,-0.428 -1.116,-0.427 -1.496,0 -0.803,0.90499999 -4.81,5.55899999 -4.81,8.31599999 0,2.8059999 2.092,5.1269999 4.798,5.4989999 v 7.437 H 20.219 v -7.523 c 2.469,-0.565 4.319,-2.775 4.319,-5.4129999 0,-2.758 -4.007,-7.411 -4.811,-8.31599999 -0.38,-0.428 -1.116,-0.427 -1.496,0 -0.803,0.90499999 -4.81,5.55899999 -4.81,8.31599999 0,2.8059999 2.092,5.1269999 4.798,5.4989999 v 7.437 H 1.129 c -0.552,0 -1,0.447 -1,1 v 8.891 c 0,0.062 0.006,0.123 0.017,0.182 0.062,3.042 1.68,5.705 4.084,7.237 v 26.175 H 3.543 c -1.954,0 -3.543,1.59 -3.543,3.543 v 2.023 c 0,1.683 1.181,3.089 2.757,3.449 0.007,0.027 0.002,0.054 0.012,0.081 l 1.717,4.771 c 0.254,0.702 1.133,1.059 2.613,1.059 h 65.803 c 1.48,0 2.359,-0.356 2.613,-1.06 l 1.717,-4.771 c 0.01,-0.027 0.005,-0.054 0.012,-0.081 1.575,-0.36 2.756,-1.766 2.756,-3.449 v -2.023 c 0,-1.953 -1.589,-3.543 -3.543,-3.543 H 75.77 v -26.013 c 2.601,-1.53 4.355,-4.351 4.355,-7.581 v -8.891 c 0,-0.552 -0.448,-0.999 -1,-0.999 z M 57.462,8.6356251 c 0,-1.337 1.953,-4.153 3.558,-6.11 1.668,2.035 3.559,4.833 3.559,6.11 0,1.9619999 -1.596,3.5579999 -3.559,3.5579999 -1.961,0 -3.558,-1.596 -3.558,-3.5579999 z m -14.013,0 c 0,-1.337 1.953,-4.153 3.558,-6.11 1.668,2.035 3.559,4.833 3.559,6.11 0,1.9619999 -1.596,3.5579999 -3.559,3.5579999 -1.962,0 -3.558,-1.596 -3.558,-3.5579999 z m -14.014,0 c 0,-1.337 1.953,-4.153 3.558,-6.11 1.668,2.035 3.559,4.833 3.559,6.11 0,1.9619999 -1.596,3.5579999 -3.559,3.5579999 -1.962,0 -3.558,-1.596 -3.558,-3.5579999 z m -14.014,0 c 0,-1.337 1.953,-4.153 3.558,-6.11 1.668,2.035 3.559,4.833 3.559,6.11 0,1.9619999 -1.596,3.5579999 -3.559,3.5579999 -1.961,0 -3.558,-1.596 -3.558,-3.5579999 z M 72.901,77.983625 H 7.099 c -0.38,0 -0.672,-0.038 -0.854,-0.077 l -1.346,-3.739 h 70.202 l -1.346,3.739 c -0.181,0.039 -0.474,0.077 -0.854,0.077 z m 5.099,-9.384 v 2.023 c 0,0.852 -0.692,1.544 -1.543,1.544 H 3.543 c -0.851,0 -1.543,-0.692 -1.543,-1.544 v -2.023 c 0,-0.851 0.692,-1.543 1.543,-1.543 h 72.914 c 0.851,0 1.543,0.692 1.543,1.543 z m -4.23,-3.543 H 6.23 v -25.229 c 0.855,0.278 1.765,0.433 2.712,0.433 3.383,0 6.327,-1.919 7.798,-4.727 1.472,2.808 4.415,4.727 7.798,4.727 3.383,0 6.327,-1.919 7.798,-4.727 1.472,2.808 4.415,4.727 7.798,4.727 3.383,0 6.327,-1.919 7.798,-4.727 1.472,2.808 4.415,4.727 7.798,4.727 3.383,0 6.326,-1.919 7.798,-4.727 1.472,2.808 4.415,4.727 7.798,4.727 0.848,0 1.666,-0.127 2.442,-0.352 v 25.148 z m 4.355,-33.594 c 0,3.748 -3.049,6.798 -6.798,6.798 -3.749,0 -6.798,-3.05 -6.798,-6.798 0,-0.553 -0.448,-1 -1,-1 -0.552,0 -1,0.447 -1,1 0,3.748 -3.049,6.798 -6.798,6.798 -3.749,0 -6.798,-3.05 -6.798,-6.798 0,-0.553 -0.448,-1 -1,-1 -0.552,0 -1,0.447 -1,1 0,3.748 -3.05,6.798 -6.798,6.798 -3.748,0 -6.798,-3.05 -6.798,-6.798 0,-0.553 -0.448,-1 -1,-1 -0.552,0 -1,0.447 -1,1 0,3.748 -3.05,6.798 -6.798,6.798 -3.748,0 -6.798,-3.05 -6.798,-6.798 0,-0.553 -0.448,-1 -1,-1 -0.552,0 -1,0.447 -1,1 0,3.748 -3.05,6.798 -6.798,6.798 -3.748,0 -6.798,-3.05 -6.798,-6.798 0,-0.059 -0.005,-0.116 -0.015,-0.172 v -7.719 h 75.996 v 7.891 z"
|
||||
id="path4780"
|
||||
inkscape:connector-curvature="0" /></svg>
|
||||
|
After Width: | Height: | Size: 5.1 KiB |
BIN
assets/img/mars-texture-clipped.png
Normal file
BIN
assets/img/mars-texture-clipped.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
3
cypress.json
Normal file
3
cypress.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:8080"
|
||||
}
|
||||
5
cypress/fixtures/example.json
Normal file
5
cypress/fixtures/example.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
25
cypress/integration/sample_spec.js
Normal file
25
cypress/integration/sample_spec.js
Normal file
@@ -0,0 +1,25 @@
|
||||
describe('My First Test', () => {
|
||||
it('Visits the Kitchen Sink', () => {
|
||||
cy.visit('/')
|
||||
|
||||
cy.contains('Begin').click()
|
||||
|
||||
cy.contains('New Game').click()
|
||||
|
||||
cy.contains('Login').click()
|
||||
|
||||
cy.get('form [name="username"]').type('test')
|
||||
cy.get('form [name="password"]').type('food')
|
||||
cy.get('button[type="submit"]').click()
|
||||
|
||||
cy.get('form [name="gameName"]').type('test')
|
||||
cy.get('button[type="submit"]').click()
|
||||
|
||||
cy.get('span[class="add-ai"]').click()
|
||||
|
||||
cy.contains('Ready to start').click()
|
||||
cy.contains('Start Game').click()
|
||||
|
||||
cy.get('.show .action-item').first().click()
|
||||
})
|
||||
})
|
||||
21
cypress/plugins/index.js
Normal file
21
cypress/plugins/index.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/// <reference types="cypress" />
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
}
|
||||
25
cypress/support/commands.js
Normal file
25
cypress/support/commands.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
20
cypress/support/index.js
Normal file
20
cypress/support/index.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
22411
package-lock.json
generated
22411
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@@ -2,6 +2,8 @@
|
||||
"name": "farm",
|
||||
"sideEffects": [
|
||||
"*.css",
|
||||
"*.scss",
|
||||
"*.ttf",
|
||||
"*.woff",
|
||||
"*.woff2"
|
||||
],
|
||||
@@ -11,7 +13,8 @@
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "webpack --config webpack.dev.js",
|
||||
"prod": "webpack --config webpack.prod.js"
|
||||
"prod": "webpack --config webpack.prod.js",
|
||||
"cypress:open": "cypress open"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
@@ -22,35 +25,39 @@
|
||||
"@babel/plugin-proposal-decorators": "^7.8.3",
|
||||
"@babel/preset-env": "^7.8.3",
|
||||
"@babel/preset-react": "^7.8.3",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.1",
|
||||
"autoprefixer": "^9.7.4",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-plugin-transform-decorators-legacy": "^1.3.5",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"copy-webpack-plugin": "^5.1.1",
|
||||
"css-loader": "^3.4.2",
|
||||
"favicons-webpack-plugin": "^2.1.0",
|
||||
"css-url-relative-plugin": "^1.0.0",
|
||||
"cypress": "^4.4.1",
|
||||
"file-loader": "^4.3.0",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"mini-css-extract-plugin": "^0.8.2",
|
||||
"node-sass": "^4.13.1",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.3",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"react-refresh": "^0.8.3",
|
||||
"sass-loader": "^8.0.2",
|
||||
"style-loader": "^1.1.3",
|
||||
"terser-webpack-plugin": "^2.3.5",
|
||||
"webpack": "^4.41.5",
|
||||
"webpack-cli": "^3.3.10",
|
||||
"webpack-dev-server": "^3.11.0",
|
||||
"webpack-merge": "^4.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/core": "^10.0.35",
|
||||
"@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",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-redux": "^7.1.3",
|
||||
"redux": "^4.0.5"
|
||||
"redux": "^4.0.5",
|
||||
"sass": "^1.69.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,29 +28,25 @@ import Welcome from '../welcome/Welcome.jsx'
|
||||
import Tractor from '../tractor/Tractor.jsx'
|
||||
|
||||
import { SCREENS, messagePanelId } from '../../constants.js'
|
||||
import { play } from './actions.js'
|
||||
|
||||
class Chrome extends React.Component {
|
||||
render() {
|
||||
const Chrome = ({ children, spikes, tractorClass }) => {
|
||||
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} />
|
||||
{children}
|
||||
<Tractor spikes={spikes} className={tractorClass} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
const App = ({ screen, logout, createAccount, login, errors }) => {
|
||||
let view;
|
||||
switch (this.props.screen) {
|
||||
switch (screen) {
|
||||
case SCREENS.intro:
|
||||
view = (<Chrome spikes={true} tractorClass='intro'><Welcome /></Chrome>);
|
||||
break;
|
||||
case SCREENS.start:
|
||||
view = (<Chrome><CreateOrJoin signOut={this.props.logout} /></Chrome>);
|
||||
view = (<Chrome><CreateOrJoin signOut={logout} /></Chrome>);
|
||||
break;
|
||||
case SCREENS.newGame:
|
||||
view = (<Chrome>
|
||||
@@ -60,9 +56,9 @@ class App extends React.Component {
|
||||
title={'New Game'}
|
||||
type={'new-game'}
|
||||
showGameName={true}
|
||||
createAccount={this.props.createAccount}
|
||||
login={this.props.login}
|
||||
errors={this.props.errors}
|
||||
createAccount={createAccount}
|
||||
login={login}
|
||||
errors={errors}
|
||||
/>
|
||||
</div>
|
||||
</Chrome>);
|
||||
@@ -71,9 +67,9 @@ class App extends React.Component {
|
||||
view = (
|
||||
<Chrome>
|
||||
<div className='view-container'>
|
||||
<JoinGame createAccount={this.props.createAccount}
|
||||
login={this.props.login}
|
||||
errors={this.props.errors}
|
||||
<JoinGame createAccount={createAccount}
|
||||
login={login}
|
||||
errors={errors}
|
||||
/>
|
||||
</div>
|
||||
</Chrome>);
|
||||
@@ -89,7 +85,6 @@ class App extends React.Component {
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
state => state.app,
|
||||
|
||||
@@ -61,14 +61,6 @@ export default class CreateAccount extends React.Component {
|
||||
</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>
|
||||
|
||||
@@ -24,33 +24,32 @@ 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) => {
|
||||
const CreateOrJoin = ({ signOut, showNewGame, start, showJoinGame }) => {
|
||||
const handleSignOut = (e) => {
|
||||
e.preventDefault();
|
||||
this.props.signOut();
|
||||
signOut();
|
||||
Cookies.expire('awful-cookie');
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Fragment>
|
||||
<Button size='large' className='shadow' onClick={this.props.showNewGame}>
|
||||
<div className="font-preloader">text</div>
|
||||
<Button size='large' className='shadow action-item' onClick={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}>
|
||||
{(start.start.games.length > 0) || (start.start.openGames.length > 0) ? (
|
||||
<Button size='large' className='shadow' onClick={showJoinGame}>
|
||||
Join Game
|
||||
</Button>
|
||||
) : (<Fragment />)}
|
||||
{this.props.start.start.user ? (
|
||||
<Button size='large' className='shadow sign-out-button' onClick={this.signOut}>
|
||||
{start.start.user ? (
|
||||
<Button size='large' className='shadow sign-out-button' onClick={handleSignOut}>
|
||||
Sign Out
|
||||
</Button>
|
||||
) : (<></>)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
state => state,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,13 +21,10 @@ import { connect } from 'react-redux'
|
||||
|
||||
import SpaceNode from './SpaceNode.jsx'
|
||||
|
||||
import { setMessagePanelSpace, mpMouse } from './actions.js'
|
||||
|
||||
class MessagePanel extends React.Component {
|
||||
render () {
|
||||
if (this.props.space !== null) {
|
||||
const MessagePanel = (props) => {
|
||||
if (props.space !== null) {
|
||||
const panel = document.getElementById('message-panel'),
|
||||
mpDims = this.props.mpDims;
|
||||
mpDims = props.mpDims;
|
||||
panel.style.top =
|
||||
(Math.min(Math.max(mpDims.mouseY, mpDims.minHeight + mpDims.padding),
|
||||
mpDims.maxHeight)) + 'px';
|
||||
@@ -35,14 +32,13 @@ class MessagePanel extends React.Component {
|
||||
(Math.min(Math.max(mpDims.mouseX, mpDims.minWidth + mpDims.padding),
|
||||
mpDims.maxWidth)) + 'px';
|
||||
return (
|
||||
<SpaceNode space={this.props.space} height='210px'
|
||||
<SpaceNode space={props.space} height='210px'
|
||||
showtitle={true} orientation={''} />
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
state => state.farm,
|
||||
|
||||
@@ -22,8 +22,9 @@ export default class PlayerIcon extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<center>
|
||||
{this.props.colors
|
||||
.map(c => (<div key={c} className={'player player-' + c}></div>))}
|
||||
{this.props.colors.map(c => (
|
||||
<div key={c} className={'player player-' + c + (c === this.props.current ? ' player-current' : '')} />
|
||||
))}
|
||||
</center>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ class SpaceNode extends React.Component {
|
||||
{title}
|
||||
</div>)
|
||||
: (null)}
|
||||
{ this.props.space.players.length ? <PlayerIcon colors={this.props.space.players} /> : ''}
|
||||
{ this.props.space.players.length ? <PlayerIcon colors={this.props.space.players} current={this.props.current} /> : ''}
|
||||
<div className='space-description'>
|
||||
{this.props.space.description}
|
||||
</div>
|
||||
|
||||
@@ -39,3 +39,5 @@ export const AUTO_SKIP = 'auto-skip'
|
||||
export const MESSAGE = 'message'
|
||||
export const SET_HARVEST_TABLE = 'set-harvest-table'
|
||||
export const SET_MOVING_SKIP = 'set-moving-skip'
|
||||
export const SERVER_ERROR = 'server-error'
|
||||
export const REMOVE_PLAYER = 'remove-player'
|
||||
|
||||
@@ -21,13 +21,14 @@ import { UPDATE_GAME, UPDATE_PLAYER, GAME_STATE, SET_SELECTED_CARD, SET_CARDS,
|
||||
MP_MOUSE, SET_MP_DIMS, MARK_ACTION_CHANGE_HANDLED, SET_NEXT_ACTION,
|
||||
MOVE_PLAYER, NEXT_UI_ACTION, NEXT_UI_ACTION_SILENT, ALERT, ALERT_HANDLED,
|
||||
AUTO_SKIP, MESSAGE, SET_HARVEST_TABLE, SET_CARD_ERROR,
|
||||
SET_MOVING_SKIP } from './actionTypes.js'
|
||||
SET_MOVING_SKIP, SERVER_ERROR, REMOVE_PLAYER } from './actionTypes.js'
|
||||
|
||||
export { updateGame, updatePlayer, gameState, setSelectedCard, setCards,
|
||||
spacePushPlayer, spaceClearPlayers, setOldMessages, setMessagePanelSpace,
|
||||
mpMouse, setMPDims, movePlayer, setNextAction, nextUIAction,
|
||||
markActionChangeHandled, nextUIActionSilent, alert, alertHandled,
|
||||
autoSkip, message, setHarvestTable, setCardError, setMovingSkip }
|
||||
autoSkip, message, setHarvestTable, setCardError, setMovingSkip,
|
||||
serverError, removePlayer }
|
||||
|
||||
function updateGame(update) {
|
||||
return { type: UPDATE_GAME,
|
||||
@@ -95,6 +96,11 @@ function movePlayer(newSpace, oldSpace, player) {
|
||||
newSpace, oldSpace, player };
|
||||
}
|
||||
|
||||
function removePlayer(color) {
|
||||
return { type: REMOVE_PLAYER,
|
||||
color };
|
||||
}
|
||||
|
||||
function nextUIAction() {
|
||||
return { type: NEXT_UI_ACTION };
|
||||
}
|
||||
@@ -135,3 +141,7 @@ function setHarvestTable(table) {
|
||||
function setMovingSkip(skip) {
|
||||
return { type: SET_MOVING_SKIP, skip };
|
||||
}
|
||||
|
||||
function serverError(exn) {
|
||||
return { type: SERVER_ERROR, exn };
|
||||
}
|
||||
|
||||
@@ -25,13 +25,14 @@ import { updateGame, updatePlayer, gameState, setSelectedCard, setCards,
|
||||
movePlayer, setOldMessages, markActionChangeHandled,
|
||||
mpMouse, rolled, setNextAction, nextUIAction, nextUIActionSilent, alert,
|
||||
autoSkip, message, alertHandled, setHarvestTable,
|
||||
setCardError, setMovingSkip } from './actions.js'
|
||||
setCardError, setMovingSkip, serverError, removePlayer } from './actions.js'
|
||||
import { itemCard, fateCard } from 'game.js'
|
||||
|
||||
export { initialize, buy, roll, endTurn, loan, trade, submitTradeAccept,
|
||||
submitTradeDeny, submitTradeCancel, audit, handleMessage,
|
||||
nextAction, buyUncleBert, actionsFinished, skip, endAiTurn,
|
||||
startGame }
|
||||
startGame, readyToStart, leaveGame, kickPlayer, toggleRevealForTrade,
|
||||
addAIPlayer, birthdayBonusPlayer }
|
||||
|
||||
let store;
|
||||
let movingTimer = 0;
|
||||
@@ -42,9 +43,16 @@ function handleMessage(evt) {
|
||||
|
||||
if (data.event === 'error') {
|
||||
console.log('error:' + data.exn);
|
||||
store.dispatch(serverError(data.exn));
|
||||
return;
|
||||
}
|
||||
batch(() => {
|
||||
if (data.event === 'left-game') {
|
||||
window.location.href = window.location.pathname;
|
||||
}
|
||||
if (data.event === 'player-left-game') {
|
||||
store.dispatch(removePlayer(data.color));
|
||||
}
|
||||
if (data.game.state === GAME_STATES.preGame) {
|
||||
store.dispatch(alert(ALERTS.preGame, '', 'pre-game'));
|
||||
}
|
||||
@@ -79,7 +87,7 @@ function handleMessage(evt) {
|
||||
store.dispatch(movePlayer(data.player.space, -1, data.player.color));
|
||||
store.dispatch(setHarvestTable(data.harvestTable));
|
||||
}
|
||||
// new player(s) added to game, put them on the board
|
||||
// player(s) added or removed from game, put them on the board
|
||||
if (data.game.otherPlayers.length !== store.getState().farm.game.otherPlayers.length) {
|
||||
const otherPlayers = store.getState().farm.game.otherPlayers;
|
||||
const newPlayers = data.game.otherPlayers.filter(
|
||||
@@ -119,7 +127,7 @@ function handleMessage(evt) {
|
||||
store.dispatch(autoSkip(data.component));
|
||||
}
|
||||
if (data.event === 'end-of-game') {
|
||||
store.dispatch(alert(ALERTS.endOfGame, data.results, 'endOfGame' + data.game.turn));
|
||||
store.dispatch(alert(ALERTS.endOfGame, { results: data.results, stats: data.stats }, 'endOfGame' + data.game.turn));
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -198,6 +206,30 @@ function startGame() {
|
||||
sendCommand({ type: 'start-game' });
|
||||
}
|
||||
|
||||
function readyToStart() {
|
||||
sendCommand({ type: 'ready-to-start' });
|
||||
}
|
||||
|
||||
function leaveGame() {
|
||||
sendCommand({ type: 'leave-game' });
|
||||
}
|
||||
|
||||
function kickPlayer(name) {
|
||||
sendCommand({ type: 'kick-player', name });
|
||||
}
|
||||
|
||||
function birthdayBonusPlayer(name) {
|
||||
sendCommand({ type: 'birthday-bonus-player', name });
|
||||
}
|
||||
|
||||
function addAIPlayer() {
|
||||
sendCommand({ type: 'add-ai-player' });
|
||||
}
|
||||
|
||||
function toggleRevealForTrade(id) {
|
||||
sendCommand({ type: 'toggle-reveal-for-trading', id });
|
||||
}
|
||||
|
||||
// TODO share with Board.jsx
|
||||
// http://stackoverflow.com/questions/149055
|
||||
function formatMoney(n) {
|
||||
|
||||
@@ -22,7 +22,7 @@ import { UPDATE_GAME, UPDATE_PLAYER, GAME_STATE, SET_SELECTED_CARD, SET_CARDS,
|
||||
SET_MP_DIMS, MOVE_PLAYER, SET_NEXT_ACTION, NEXT_UI_ACTION,
|
||||
MARK_ACTION_CHANGE_HANDLED, NEXT_UI_ACTION_SILENT, ALERT, ALERT_HANDLED,
|
||||
AUTO_SKIP, MESSAGE, SET_HARVEST_TABLE, SET_CARD_ERROR,
|
||||
SET_MOVING_SKIP } from './actionTypes.js'
|
||||
SET_MOVING_SKIP, SERVER_ERROR, REMOVE_PLAYER } from './actionTypes.js'
|
||||
import { GAME_STATES } from '../../constants.js'
|
||||
import { spaceContent, corners } from 'game.js'
|
||||
|
||||
@@ -88,9 +88,11 @@ const initialState = {
|
||||
debt: 5000,
|
||||
spaces,
|
||||
state: GAME_STATES.turnEnded,
|
||||
assets: { hay: 10, grain: 10, fruit: 0, cows: 0, harvester: 0, tractor: 0 },
|
||||
assets: { hay: 10, grain: 10, fruit: 0, cows: 0, harvester: 0, tractor: 0, birthday: 0 },
|
||||
color: '',
|
||||
name: '',
|
||||
cards: [],
|
||||
revealedCards: [],
|
||||
ridges: { ridge1: 0, ridge2: 0, ridge3: 0, ridge4: 0 },
|
||||
space: 0,
|
||||
hayDoubled: false,
|
||||
@@ -105,18 +107,26 @@ const initialState = {
|
||||
state: GAME_STATES.preTurn,
|
||||
turn: 0,
|
||||
oldMessages: [],
|
||||
name: '',
|
||||
host: '',
|
||||
settings: { downPayment: 0.2,
|
||||
loanInterest: 0.2,
|
||||
maxDebt: 50000,
|
||||
auditThreshold: 250000 }
|
||||
auditThreshold: 250000,
|
||||
startingOtbs: 2,
|
||||
startingCash: 5000,
|
||||
startingDebt: 5000 },
|
||||
readyToStart: false
|
||||
},
|
||||
ui: { card: { type: 'no-card', contents: '', total: 0 },
|
||||
cards: [],
|
||||
cardError: false,
|
||||
action: false,
|
||||
actionValue: null,
|
||||
actionId: -1,
|
||||
nextAction: false,
|
||||
nextActionValue: null,
|
||||
nextActionId: -1,
|
||||
actionChangeHandled: true,
|
||||
message: '',
|
||||
alerts: {},
|
||||
@@ -124,7 +134,8 @@ const initialState = {
|
||||
autoSkip: false,
|
||||
playerSpaces: {},
|
||||
movingSkip: false,
|
||||
harvestTable: false },
|
||||
harvestTable: false,
|
||||
exn: false },
|
||||
spaces: spaces,
|
||||
space: null,
|
||||
// message panel dimenions
|
||||
@@ -135,6 +146,8 @@ const initialState = {
|
||||
profileTurns: 500
|
||||
}
|
||||
|
||||
let lastActionId = -1;
|
||||
|
||||
export default function(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case UPDATE_GAME:
|
||||
@@ -170,6 +183,21 @@ export default function(state = initialState, action) {
|
||||
[action.player]: action.newSpace }}
|
||||
};
|
||||
}
|
||||
case REMOVE_PLAYER:
|
||||
const playerSpace = state.ui.playerSpaces[action.color];
|
||||
return { ...state,
|
||||
spaces: state.spaces.map((item, index) => {
|
||||
if (index === playerSpace) {
|
||||
return { ...item,
|
||||
players: item.players
|
||||
.filter(x => x !== action.color) };
|
||||
}
|
||||
return item;
|
||||
}),
|
||||
ui: { ...state.ui,
|
||||
playerSpaces: { ...state.ui.playerSpaces,
|
||||
[action.player]: -1 }}
|
||||
};
|
||||
case SET_OLD_MESSAGES:
|
||||
return { ...state, oldMessages: action.messages };
|
||||
case MESSAGE_PANEL_SPACE:
|
||||
@@ -185,15 +213,18 @@ export default function(state = initialState, action) {
|
||||
maxHeight: action.maxHeight }};
|
||||
case SET_NEXT_ACTION:
|
||||
return { ...state, ui: { ...state.ui, nextAction: action.action,
|
||||
nextActionValue: action.value }};
|
||||
nextActionValue: action.value,
|
||||
nextActionId: ++lastActionId }};
|
||||
case NEXT_UI_ACTION:
|
||||
return { ...state, ui: { ...state.ui, action: state.ui.nextAction,
|
||||
actionValue: state.ui.nextActionValue,
|
||||
actionId: state.ui.nextActionId,
|
||||
autoSkip: false,
|
||||
actionChangeHandled: !state.ui.nextAction }};
|
||||
case NEXT_UI_ACTION_SILENT: // don't set actionChangeHandled
|
||||
return { ...state, ui: { ...state.ui, action: state.ui.nextAction,
|
||||
actionValue: state.ui.nextActionValue }};
|
||||
actionValue: state.ui.nextActionValue,
|
||||
actionId: state.ui.nextActionId }};
|
||||
case MARK_ACTION_CHANGE_HANDLED:
|
||||
return { ...state, ui: { ...state.ui, actionChangeHandled: true }};
|
||||
case ALERT:
|
||||
@@ -228,6 +259,8 @@ export default function(state = initialState, action) {
|
||||
return { ...state, ui: { ...state.ui, harvestTable: action.table }};
|
||||
case SET_MOVING_SKIP:
|
||||
return { ...state, ui: { ...state.ui, movingSkip: action.skip }};
|
||||
case SERVER_ERROR:
|
||||
return { ...state, ui: { ...state.ui, exn: action.exn }};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -26,19 +26,23 @@ 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'
|
||||
import { faArrowCircleLeft, faCaretDown } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
import NewGame from '../new-game/NewGame.jsx'
|
||||
|
||||
const JoinGameScreens = { list: 'list', details: 'details' };
|
||||
|
||||
const defaultMaxGames = 5;
|
||||
|
||||
class JoinGame extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
screen: JoinGameScreens.list,
|
||||
game: null,
|
||||
showSignIn: false
|
||||
showSignIn: false,
|
||||
maxGames: defaultMaxGames,
|
||||
maxOpenGames: defaultMaxGames
|
||||
};
|
||||
}
|
||||
|
||||
@@ -67,11 +71,22 @@ class JoinGame extends React.Component {
|
||||
this.setState(state => { return { showSignIn: !state.showSignIn }; });
|
||||
}
|
||||
|
||||
showAllGames = (e) => {
|
||||
e.preventDefault();
|
||||
this.setState({ maxGames: Number.MAX_SAFE_INTEGER });
|
||||
}
|
||||
|
||||
showAllOpenGames = (e) => {
|
||||
e.preventDefault();
|
||||
this.setState({ maxOpenGames: Number.MAX_SAFE_INTEGER });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { games, openGames } = this.props;
|
||||
return (
|
||||
<GroupBox title={(
|
||||
<Fragment>
|
||||
<a href="#" onClick={this.handleBack}>
|
||||
<a onClick={this.handleBack}>
|
||||
<FontAwesomeIcon icon={faArrowCircleLeft} />
|
||||
</a>
|
||||
Join Game
|
||||
@@ -97,21 +112,39 @@ class JoinGame extends React.Component {
|
||||
</>
|
||||
) : (<></>)}
|
||||
<ul>
|
||||
{this.props.games
|
||||
{games.slice(0, this.state.maxGames)
|
||||
.map((g, i) =>
|
||||
(<li key={i}>
|
||||
(
|
||||
<li key={i}>
|
||||
<a onClick={(e) => this.handleJoinAsExisting(e, g.id)}>{g.name}</a>
|
||||
</li>))}
|
||||
</li>
|
||||
))}
|
||||
{games.length > this.state.maxGames ? (
|
||||
<li>
|
||||
<center>
|
||||
<a onClick={this.showAllGames}><FontAwesomeIcon icon={faCaretDown} /> Show all</a>
|
||||
</center>
|
||||
</li>
|
||||
) : (<></>)}
|
||||
</ul>
|
||||
<h3>Open Games</h3>
|
||||
<ul>
|
||||
{this.props.openGames
|
||||
{openGames.slice(0, this.state.maxOpenGames)
|
||||
.map((g, i) =>
|
||||
(<li key={i}>
|
||||
(
|
||||
<li key={i}>
|
||||
<a onClick={e => {
|
||||
e.preventDefault();
|
||||
this.handleClickGame(g); }}>{g.name}</a>
|
||||
</li>))}
|
||||
</li>
|
||||
))}
|
||||
{openGames.length > this.state.maxOpenGames ? (
|
||||
<li>
|
||||
<center>
|
||||
<a onClick={this.showAllOpenGames}><FontAwesomeIcon icon={faCaretDown} /> Show all</a>
|
||||
</center>
|
||||
</li>
|
||||
) : (<></>)}
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -42,7 +42,7 @@ export default class LoginOrCreateAccount extends React.Component {
|
||||
createAccount={this.props.createAccount} />
|
||||
)}
|
||||
<div className="center">
|
||||
<a onClick={this.toggleLogin}>{this.state.showLogin ? 'Create Account' : 'Login'}</a>
|
||||
<a className="action-item" onClick={this.toggleLogin}>{this.state.showLogin ? 'Create Account' : 'Login'}</a>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -25,6 +25,8 @@ import LoginOrCreateAccount from '../login-or-create-account/LoginOrCreateAccoun
|
||||
import { startOrJoinGame } from '../start/actions.js'
|
||||
import { start } from '../app/actions.js'
|
||||
|
||||
import { itemCardShort } from 'game.js'
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faArrowCircleLeft, faCog } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
@@ -63,6 +65,7 @@ class NewGame extends React.Component {
|
||||
auditThreshold: 250000,
|
||||
startingCash: 5000,
|
||||
startingDebt: 5000,
|
||||
startingOtbs: 2,
|
||||
trade: true,
|
||||
showLogin: false
|
||||
};
|
||||
@@ -180,6 +183,13 @@ class NewGame extends React.Component {
|
||||
step={1000}
|
||||
value={this.state.startingDebt}
|
||||
onChange={this.handleInputChange} />
|
||||
<InputRow label={'Number of Starting ' + itemCardShort}
|
||||
name='startingOtbs'
|
||||
min={0}
|
||||
max={8}
|
||||
step={1}
|
||||
value={this.state.startingOtbs}
|
||||
onChange={this.handleInputChange} />
|
||||
<Row>
|
||||
<Col width='12'>
|
||||
<label>
|
||||
|
||||
@@ -19,12 +19,11 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
|
||||
import { GroupBox, Row, Col, Button } from '../widgets.jsx'
|
||||
import { Button } from '../widgets.jsx'
|
||||
import { start } from '../app/actions.js'
|
||||
|
||||
|
||||
class Welcome extends React.Component {
|
||||
render() {
|
||||
const Welcome = (props) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<div className='intro-text'>
|
||||
@@ -32,13 +31,12 @@ class Welcome extends React.Component {
|
||||
Your ancestors were farmers on one of the first transports to Alpha Centuari{`'`}s Proxima b. The growing season is short and harsh but the colonists depend on you for their food. Are you up to the challenge?
|
||||
</div>
|
||||
</div>
|
||||
<Button size='large' className='shadow intro' onClick={this.props.start}>
|
||||
<Button size='large' className='shadow intro action-item' onClick={props.start}>
|
||||
Begin
|
||||
</Button>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
state => state,
|
||||
|
||||
@@ -20,51 +20,43 @@ import React, { Fragment } from 'react'
|
||||
|
||||
export { GroupBox, Row, Col, Button }
|
||||
|
||||
class GroupBox extends React.Component {
|
||||
render() {
|
||||
const GroupBox = (props) => {
|
||||
return (
|
||||
<div className='panel card'>
|
||||
{this.props.title ?
|
||||
{props.title ?
|
||||
(<div className='card-divider'>
|
||||
{this.props.title}
|
||||
{props.title}
|
||||
</div>) : (<Fragment />)}
|
||||
<div className={'card-section ' + this.props.className ? this.props.className : ''}>
|
||||
{this.props.children}
|
||||
<div className={'card-section ' + props.className ? props.className : ''}>
|
||||
{props.children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Row extends React.Component {
|
||||
render() {
|
||||
const Row = (props) => {
|
||||
return (<div className={'grid-x full-width ' +
|
||||
(this.props.collapse ? 'collapse' : '') + ' ' +
|
||||
(this.props.className ? this.props.className : '')} >
|
||||
{this.props.children}</div>);
|
||||
}
|
||||
(props.collapse ? 'collapse' : '') + ' ' +
|
||||
(props.className ? props.className : '')} >
|
||||
{props.children}</div>);
|
||||
}
|
||||
|
||||
class Col extends React.Component {
|
||||
render() {
|
||||
const Col = (props) => {
|
||||
return (
|
||||
<div className={'cell small-' + this.props.width + ' ' + (this.props.className ? this.props.className : '')}>
|
||||
{this.props.children}
|
||||
<div className={'cell small-' + props.width + ' ' + (props.className ? props.className : '')}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Button extends React.Component {
|
||||
render() {
|
||||
const Button = (props) => {
|
||||
return (
|
||||
<button className={'button ' + (this.props.size ? this.props.size : '') +
|
||||
' ' + (this.props.className ? this.props.className : '')}
|
||||
type={this.props.type || 'button'}
|
||||
disabled={this.props.disabled}
|
||||
onClick={this.props.onClick} >
|
||||
{this.props.children}
|
||||
<button className={'button ' + (props.size ? props.size : '') +
|
||||
' ' + (props.className ? props.className : '')}
|
||||
type={props.type || 'button'}
|
||||
disabled={props.disabled}
|
||||
onClick={props.onClick} >
|
||||
{props.children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ const unsubscribeNewOrJoinGame = store.subscribe(() => {
|
||||
});
|
||||
|
||||
let autostart = new URL(window.location.href).searchParams.get('autostart');
|
||||
let rejoin = new URL(window.location.href).searchParams.get('reload-game');
|
||||
|
||||
function handleMessage(evt) {
|
||||
const data = JSON.parse(evt.data),
|
||||
@@ -86,6 +87,8 @@ function handleMessage(evt) {
|
||||
if (data.event === 'error') {
|
||||
console.log('error:' + data.exn);
|
||||
} else if (data.event === 'new-game-started') {
|
||||
history.replaceState({}, document.title, '/');
|
||||
console.log('clearing auto-reload');
|
||||
initialize(store, Ws.sendCommand);
|
||||
Ws.setMainOnMessage(handleMessageFarm);
|
||||
Ws.openSecondary('push-web-socket');
|
||||
@@ -111,6 +114,10 @@ function handleMessage(evt) {
|
||||
gameName: data.games.games[0].name }));
|
||||
}
|
||||
}
|
||||
if (rejoin) {
|
||||
store.dispatch(startOrJoinGame({ type: 'join-as-existing',
|
||||
gameId: false }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(use sql-de-lite crypt)
|
||||
|
||||
(define *db* "/home/tjhintz/db")
|
||||
;; (define *db* "/home/tjhintz/db")
|
||||
(define *db* "/farmdb/db")
|
||||
|
||||
(define-syntax with-db
|
||||
(syntax-rules ()
|
||||
@@ -10,12 +11,17 @@
|
||||
body ...)))))
|
||||
|
||||
(define (create-tables)
|
||||
(when (not (file-exists? *db*))
|
||||
(call-with-output-file *db*
|
||||
(lambda (output-port)
|
||||
""
|
||||
)))
|
||||
(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);"))))
|
||||
(exec (sql db "create table if not exists users(id INTEGER PRIMARY KEY, username TEXT, email TEXT, password TEXT, salt TEXT);"))
|
||||
(exec (sql db "create table if not exists sessions(bindings TEXT, session_id TEXT PRIMARY KEY);"))
|
||||
(exec (sql db "create table if not exists games(id INTEGER PRIMARY KEY, status TEXT, object TEXT, updated INTEGER);"))
|
||||
(exec (sql db "create table if not exists players(id INTEGER PRIMARY KEY, object TEXT);"))
|
||||
(exec (sql db "create table if not exists user_games(user_id INTEGER, game_id INTEGER);"))))
|
||||
|
||||
(define (db-session-set! sid bindings)
|
||||
(with-db (db)
|
||||
@@ -62,6 +68,12 @@
|
||||
(string=? (crypt password (alist-ref 'salt user))
|
||||
(alist-ref 'password user))))
|
||||
|
||||
(define (unsecure-set-password username password)
|
||||
(let ((salt (crypt-gensalt)))
|
||||
(with-db (db)
|
||||
(exec (sql db "update users set password=?, salt=? where username=?;")
|
||||
(crypt password salt) salt username))))
|
||||
|
||||
(define (alist->string alist)
|
||||
(with-output-to-string (lambda () (write alist))))
|
||||
|
||||
@@ -70,14 +82,14 @@
|
||||
|
||||
(define (db-add-game status object)
|
||||
(with-db (db)
|
||||
(exec (sql db "insert into games(status, object) values (?, ?);")
|
||||
status (alist->string object))
|
||||
(exec (sql db "insert into games(status, object, updated) values (?, ?, ?);")
|
||||
status (alist->string object) (current-seconds))
|
||||
(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))))
|
||||
(exec (sql db "replace into games(id, status, object, updated) values (?, ?, ?, ?);")
|
||||
id status (alist->string object) (current-seconds))))
|
||||
|
||||
(define (db-fetch-game id)
|
||||
(string->alist
|
||||
@@ -91,7 +103,7 @@
|
||||
string->alist
|
||||
(with-db (db)
|
||||
(query fetch-column
|
||||
(sql db "select object from games where status=?;")
|
||||
(sql db "select object from games where status=? order by updated desc;")
|
||||
"pre-game"))))
|
||||
|
||||
(define (db-fetch-game-row id)
|
||||
@@ -127,8 +139,13 @@
|
||||
(exec (sql db "insert into user_games(user_id, game_id) values (?, ?);")
|
||||
user-id game-id)))
|
||||
|
||||
(define (db-remove-user-game user-id game-id)
|
||||
(with-db (db)
|
||||
(exec (sql db "delete from user_games where user_id=? and game_id=?;")
|
||||
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)))
|
||||
(sql db "select game_id from user_games join games on user_games.game_id=games.id where user_games.user_id=? and not games.status=? order by updated desc;")
|
||||
user-id "finished")))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
214
src/style.scss
214
src/style.scss
@@ -16,6 +16,20 @@
|
||||
// along with the Alpha Centauri Farming project. If not, see
|
||||
// <https://www.gnu.org/licenses/>.
|
||||
|
||||
@font-face {
|
||||
font-family: 'IndieFlower-Regular';
|
||||
src: url('../assets/font/IndieFlower-Regular.woff2') format('woff2'),
|
||||
url('../assets/font/IndieFlower-Regular.woff') format('woff'),
|
||||
url('../assets/font/IndieFlower-Regular.ttf') format('truetype');
|
||||
}
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'LibreBaskerville-Regular';
|
||||
// src: url('../assets/font/LibreBaskerville-Regular.woff2') format('woff2'),
|
||||
// url('../assets/font/LibreBaskerville-Regular.woff') format('woff'),
|
||||
// url('../assets/font/LibreBaskerville-Regular.ttf') format('truetype');
|
||||
// }
|
||||
|
||||
@import './foundation/foundation';
|
||||
|
||||
// Global styles
|
||||
@@ -159,6 +173,8 @@ $tab-margin: 0.3rem;
|
||||
width: 100%; }
|
||||
|
||||
.space {
|
||||
font-family: 'IndieFlower-Regular';
|
||||
// font-family: 'LibreBaskerville-Regular';
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
padding: 3px;
|
||||
@@ -169,6 +185,7 @@ $tab-margin: 0.3rem;
|
||||
font-size: 4px; }
|
||||
@include breakpoint(large) {
|
||||
font-size: 14px; }
|
||||
text-shadow: 0px 0px 2px black;
|
||||
}
|
||||
|
||||
.space-description {
|
||||
@@ -244,6 +261,23 @@ $tab-margin: 0.3rem;
|
||||
margin-top: 4px;
|
||||
margin-left: 5px; }
|
||||
|
||||
.player-current {
|
||||
position: relative;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.15);
|
||||
transition: all 0.3s ease-in-out; }
|
||||
|
||||
.player-current::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 12px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
box-shadow: 0 0px 5px $primary-color;
|
||||
animation: die-pulse 2s ease-in-out infinite; }
|
||||
|
||||
.player-selectable {
|
||||
border: 4px solid #ff816e;
|
||||
cursor: pointer;
|
||||
@@ -271,7 +305,7 @@ $tab-margin: 0.3rem;
|
||||
.player-black {
|
||||
background-color: black; }
|
||||
.player-none {
|
||||
background-color: #f2f2f2; }
|
||||
background-color: #f2f2f2; } //
|
||||
|
||||
.tab .player {
|
||||
height: 27px;
|
||||
@@ -437,21 +471,107 @@ $trade-margin: 3rem;
|
||||
.space-type-hay {
|
||||
background-color: hsla(120, 100%, 25%, 0.19); }
|
||||
|
||||
.space-type-hay::after {
|
||||
background: url('../assets/img/hay.svg') repeat;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.2;
|
||||
z-index: -1;
|
||||
position: absolute;
|
||||
filter: invert(42%) sepia(93%) saturate(1352%) hue-rotate(87deg) brightness(119%) contrast(119%);
|
||||
background-size: auto 100%;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.space-type-cherry {
|
||||
background-color: hsla(0, 100%, 40%, 0.28); }
|
||||
|
||||
.space-type-cherry::after {
|
||||
background: url('../assets/img/fruit.svg') repeat;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.1;
|
||||
z-index: -1;
|
||||
position: absolute;
|
||||
filter: invert(13%) sepia(54%) saturate(6280%) hue-rotate(358deg) brightness(98%) contrast(123%);
|
||||
background-size: auto 50%;
|
||||
background-position: right;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.space-type-apple {
|
||||
background-color: hsla(0, 100%, 40%, 0.28); }
|
||||
|
||||
.space-type-apple::after {
|
||||
background: url('../assets/img/fruit.svg') repeat;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.1;
|
||||
z-index: -1;
|
||||
position: absolute;
|
||||
filter: invert(13%) sepia(54%) saturate(6280%) hue-rotate(358deg) brightness(98%) contrast(123%);
|
||||
background-size: auto 50%;
|
||||
background-position: right;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.space-type-wheat {
|
||||
background-color: hsla(50, 97%, 48%, 0.22); }
|
||||
|
||||
.space-type-wheat::after {
|
||||
background: url('../assets/img/wheat.svg') repeat;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.2;
|
||||
z-index: -1;
|
||||
position: absolute;
|
||||
filter: invert(68%) sepia(58%) saturate(747%) hue-rotate(8deg) brightness(102%) contrast(106%);
|
||||
background-size: auto 110%;
|
||||
background-position: right;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.space-type-corn {
|
||||
background-color: hsla(50, 97%, 48%, 0.22); }
|
||||
|
||||
.space-type-corn::after {
|
||||
background: url('../assets/img/wheat.svg') repeat;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.2;
|
||||
z-index: -1;
|
||||
position: absolute;
|
||||
filter: invert(68%) sepia(58%) saturate(747%) hue-rotate(8deg) brightness(102%) contrast(106%);
|
||||
background-size: auto 110%;
|
||||
background-position: right;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.space-type-cows {
|
||||
background-color: hsla(0, 25%, 43%, 0.49); }
|
||||
|
||||
.space-type-cows::after {
|
||||
background: url('../assets/img/cow.svg') repeat;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.2;
|
||||
z-index: -1;
|
||||
position: absolute;
|
||||
filter: invert(43%) sepia(5%) saturate(4943%) hue-rotate(314deg) brightness(76%) contrast(75%);
|
||||
background-size: auto 35%;
|
||||
background-position: right;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.space-type-buy {
|
||||
background-color: hsla(240, 100%, 85%, 0.15); }
|
||||
|
||||
.space-title {
|
||||
text-align: center;
|
||||
font-style: italic; }
|
||||
@@ -686,6 +806,14 @@ $trade-margin: 3rem;
|
||||
padding: 0.5rem;
|
||||
display: none; }
|
||||
|
||||
.tab.border-top {
|
||||
$tab-border: 0.3rem solid $primary-color;
|
||||
border-top: $tab-border;
|
||||
border-bottom: none;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.tab.show {
|
||||
display: block; }
|
||||
|
||||
@@ -752,6 +880,7 @@ $trade-margin: 3rem;
|
||||
margin-top: 1rem; }
|
||||
|
||||
.clear-background {
|
||||
position: relative;
|
||||
background: white; }
|
||||
|
||||
.harvest-card {
|
||||
@@ -805,6 +934,10 @@ $trade-margin: 3rem;
|
||||
justify-content: center;
|
||||
align-items: center; }
|
||||
|
||||
.alert-container {
|
||||
max-height: 90vh;
|
||||
overflow: auto; }
|
||||
|
||||
.moving {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -926,70 +1059,27 @@ $intro-time: 6s;
|
||||
position: absolute;
|
||||
bottom: 1rem; }
|
||||
|
||||
/* -------- MENU ------- */
|
||||
/* Position and sizing of burger button */
|
||||
.bm-burger-button {
|
||||
position: fixed;
|
||||
width: 36px;
|
||||
height: 30px;
|
||||
left: 36px;
|
||||
top: 36px;
|
||||
}
|
||||
.lobby-icon {
|
||||
cursor: pointer;
|
||||
margin-left: 0.2rem; }
|
||||
|
||||
/* Color/shape of burger icon bars */
|
||||
.bm-burger-bars {
|
||||
background: #373a47;
|
||||
}
|
||||
.kick-player {
|
||||
color: red; }
|
||||
|
||||
/* Color/shape of burger icon bars on hover*/
|
||||
.bm-burger-bars-hover {
|
||||
background: #a90000;
|
||||
}
|
||||
.birthday-selected {
|
||||
color: blue; }
|
||||
|
||||
/* Position and sizing of clickable cross button */
|
||||
.bm-cross-button {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
ul {
|
||||
margin-left: 0;
|
||||
list-style-type: none; }
|
||||
|
||||
/* Color/shape of close button cross */
|
||||
.bm-cross {
|
||||
background: #bdc3c7;
|
||||
}
|
||||
.font-preloader {
|
||||
font-family: 'IndieFlower-Regular';
|
||||
width: 0;
|
||||
height: 0; }
|
||||
|
||||
/*
|
||||
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);
|
||||
.game-over p {
|
||||
margin-bottom: 0.2rem;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -19,31 +19,39 @@
|
||||
const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
const FaviconsWebpackPlugin = require('favicons-webpack-plugin')
|
||||
// const FaviconsWebpackPlugin = require('favicons-webpack-plugin')
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const CopyPlugin = require('copy-webpack-plugin');
|
||||
const webpack = require("webpack");
|
||||
const autoprefixer = require("autoprefixer");
|
||||
const CssUrlRelativePlugin = require('css-url-relative-plugin')
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
app: './src/main.jsx',
|
||||
app: './src/index.jsx',
|
||||
},
|
||||
output: {
|
||||
filename: '[name].bundle.js',
|
||||
filename: './assets/[name].[hash].js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
},
|
||||
optimization: {
|
||||
runtimeChunk: 'single',
|
||||
splitChunks: {
|
||||
cacheGroups: {
|
||||
vendor: {
|
||||
test: /[\\/]node_modules[\\/]/,
|
||||
name: 'vendors',
|
||||
chunks: 'all',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
new CleanWebpackPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
title: 'Alpha Centauri Farming',
|
||||
filename: 'main.html',
|
||||
meta: {viewport: 'width=device-width, initial-scale=1'},
|
||||
}),
|
||||
new FaviconsWebpackPlugin('./assets/img/tractor.svg'),
|
||||
// new FaviconsWebpackPlugin('./assets/img/tractor.svg'),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: '[name].css',
|
||||
chunkFilename: '[id].css',
|
||||
filename: './assets/[name].[hash].css',
|
||||
chunkFilename: './assets/[id].[hash].css',
|
||||
}),
|
||||
new CopyPlugin([
|
||||
{ from: './src/server/farm.scm', to: './[name].[ext]' },
|
||||
@@ -57,6 +65,7 @@ module.exports = {
|
||||
]
|
||||
}
|
||||
}),
|
||||
new CssUrlRelativePlugin()
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
@@ -71,11 +80,26 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
},
|
||||
// {
|
||||
// test: /mars-texture.png$/,
|
||||
// loader: 'file-loader',
|
||||
// options: {
|
||||
// name: './assets/img/[name].[contenthash].[ext]',
|
||||
// },
|
||||
// },
|
||||
{
|
||||
test: /\.(woff|woff2|eot|ttf|otf|svg|png|gif)$/,
|
||||
use: [
|
||||
'file-loader',
|
||||
],
|
||||
test: /\.(svg|png|gif)$/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: './assets/img/[name].[hash].[ext]',
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.(woff|woff2|eot|ttf|otf)$/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: './assets/font/[name].[hash].[ext]',
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.s[ac]ss$/i,
|
||||
|
||||
@@ -19,15 +19,54 @@
|
||||
const merge = require('webpack-merge');
|
||||
const common = require('./webpack.common.js');
|
||||
const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
||||
const webpack = require('webpack');
|
||||
|
||||
module.exports = function(env) {
|
||||
return merge(common, {
|
||||
mode: 'development',
|
||||
devtool: 'inline-source-map',
|
||||
devServer: {
|
||||
port: 9000,
|
||||
contentBase: './dist',
|
||||
hot: true,
|
||||
proxy: {
|
||||
'/websocket': {
|
||||
target: 'ws://localhost:8080',
|
||||
ws: true
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
modules: [path.resolve(__dirname, 'src'),
|
||||
path.resolve(__dirname, env.assets),
|
||||
'node_modules']
|
||||
},
|
||||
plugins: [
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new ReactRefreshWebpackPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
title: 'Alpha Centauri Farming',
|
||||
filename: 'index.html',
|
||||
meta: {viewport: 'width=device-width, initial-scale=1'},
|
||||
}),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
plugins: [require.resolve('react-refresh/babel')],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ const common = require('./webpack.common.js');
|
||||
const path = require('path');
|
||||
|
||||
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
module.exports = function(env) {
|
||||
return merge(common, {
|
||||
@@ -31,8 +33,16 @@ module.exports = function(env) {
|
||||
path.resolve(__dirname, env.assets),
|
||||
'node_modules']
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
title: 'Alpha Centauri Farming',
|
||||
filename: 'main.html',
|
||||
meta: {viewport: 'width=device-width, initial-scale=1'},
|
||||
}),
|
||||
],
|
||||
optimization: {
|
||||
minimizer: [new OptimizeCssAssetsPlugin({})],
|
||||
minimize: true,
|
||||
minimizer: [new OptimizeCssAssetsPlugin({}), new TerserPlugin()],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user