Initial commit of Mesos Java project
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
target/
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
logs/
|
||||||
316
mvnw
vendored
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Maven Start Up Batch script
|
||||||
|
#
|
||||||
|
# Required ENV vars:
|
||||||
|
# ------------------
|
||||||
|
# JAVA_HOME - location of a JDK home dir
|
||||||
|
#
|
||||||
|
# Optional ENV vars
|
||||||
|
# -----------------
|
||||||
|
# M2_HOME - location of maven2's installed home dir
|
||||||
|
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||||
|
# e.g. to debug Maven itself, use
|
||||||
|
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||||
|
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||||
|
|
||||||
|
if [ -f /usr/local/etc/mavenrc ] ; then
|
||||||
|
. /usr/local/etc/mavenrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f /etc/mavenrc ] ; then
|
||||||
|
. /etc/mavenrc
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$HOME/.mavenrc" ] ; then
|
||||||
|
. "$HOME/.mavenrc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
# OS specific support. $var _must_ be set to either true or false.
|
||||||
|
cygwin=false;
|
||||||
|
darwin=false;
|
||||||
|
mingw=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN*) cygwin=true ;;
|
||||||
|
MINGW*) mingw=true;;
|
||||||
|
Darwin*) darwin=true
|
||||||
|
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||||
|
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
if [ -x "/usr/libexec/java_home" ]; then
|
||||||
|
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||||
|
else
|
||||||
|
export JAVA_HOME="/Library/Java/Home"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
if [ -r /etc/gentoo-release ] ; then
|
||||||
|
JAVA_HOME=`java-config --jre-home`
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$M2_HOME" ] ; then
|
||||||
|
## resolve links - $0 may be a link to maven's home
|
||||||
|
PRG="$0"
|
||||||
|
|
||||||
|
# need this for relative symlinks
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG="`dirname "$PRG"`/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
saveddir=`pwd`
|
||||||
|
|
||||||
|
M2_HOME=`dirname "$PRG"`/..
|
||||||
|
|
||||||
|
# make it fully qualified
|
||||||
|
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||||
|
|
||||||
|
cd "$saveddir"
|
||||||
|
# echo Using m2 at $M2_HOME
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $cygwin ; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||||
|
if $mingw ; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ]; then
|
||||||
|
javaExecutable="`which javac`"
|
||||||
|
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||||
|
# readlink(1) is not available as standard on Solaris 10.
|
||||||
|
readLink=`which readlink`
|
||||||
|
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||||
|
if $darwin ; then
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||||
|
else
|
||||||
|
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||||
|
fi
|
||||||
|
javaHome="`dirname \"$javaExecutable\"`"
|
||||||
|
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||||
|
JAVA_HOME="$javaHome"
|
||||||
|
export JAVA_HOME
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVACMD" ] ; then
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="`\\unset -f command; \\command -v java`"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||||
|
echo " We cannot execute $JAVACMD" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$JAVA_HOME" ] ; then
|
||||||
|
echo "Warning: JAVA_HOME environment variable is not set."
|
||||||
|
fi
|
||||||
|
|
||||||
|
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||||
|
|
||||||
|
# traverses directory structure from process work directory to filesystem root
|
||||||
|
# first directory with .mvn subdirectory is considered project base directory
|
||||||
|
find_maven_basedir() {
|
||||||
|
|
||||||
|
if [ -z "$1" ]
|
||||||
|
then
|
||||||
|
echo "Path not specified to find_maven_basedir"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
basedir="$1"
|
||||||
|
wdir="$1"
|
||||||
|
while [ "$wdir" != '/' ] ; do
|
||||||
|
if [ -d "$wdir"/.mvn ] ; then
|
||||||
|
basedir=$wdir
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||||
|
if [ -d "${wdir}" ]; then
|
||||||
|
wdir=`cd "$wdir/.."; pwd`
|
||||||
|
fi
|
||||||
|
# end of workaround
|
||||||
|
done
|
||||||
|
echo "${basedir}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# concatenates all lines of a file
|
||||||
|
concat_lines() {
|
||||||
|
if [ -f "$1" ]; then
|
||||||
|
echo "$(tr -s '\n' ' ' < "$1")"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||||
|
if [ -z "$BASE_DIR" ]; then
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
##########################################################################################
|
||||||
|
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||||
|
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||||
|
##########################################################################################
|
||||||
|
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found .mvn/wrapper/maven-wrapper.jar"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
|
||||||
|
fi
|
||||||
|
if [ -n "$MVNW_REPOURL" ]; then
|
||||||
|
jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||||
|
else
|
||||||
|
jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||||
|
fi
|
||||||
|
while IFS="=" read key value; do
|
||||||
|
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
|
||||||
|
esac
|
||||||
|
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Downloading from: $jarUrl"
|
||||||
|
fi
|
||||||
|
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
|
||||||
|
if $cygwin; then
|
||||||
|
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v wget > /dev/null; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found wget ... using wget"
|
||||||
|
fi
|
||||||
|
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||||
|
wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||||
|
else
|
||||||
|
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||||
|
fi
|
||||||
|
elif command -v curl > /dev/null; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Found curl ... using curl"
|
||||||
|
fi
|
||||||
|
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||||
|
curl -o "$wrapperJarPath" "$jarUrl" -f
|
||||||
|
else
|
||||||
|
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo "Falling back to using Java to download"
|
||||||
|
fi
|
||||||
|
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||||
|
# For Cygwin, switch paths to Windows format before running javac
|
||||||
|
if $cygwin; then
|
||||||
|
javaClass=`cygpath --path --windows "$javaClass"`
|
||||||
|
fi
|
||||||
|
if [ -e "$javaClass" ]; then
|
||||||
|
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo " - Compiling MavenWrapperDownloader.java ..."
|
||||||
|
fi
|
||||||
|
# Compiling the Java class
|
||||||
|
("$JAVA_HOME/bin/javac" "$javaClass")
|
||||||
|
fi
|
||||||
|
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||||
|
# Running the downloader
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo " - Running MavenWrapperDownloader.java ..."
|
||||||
|
fi
|
||||||
|
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
##########################################################################################
|
||||||
|
# End of extension
|
||||||
|
##########################################################################################
|
||||||
|
|
||||||
|
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||||
|
if [ "$MVNW_VERBOSE" = true ]; then
|
||||||
|
echo $MAVEN_PROJECTBASEDIR
|
||||||
|
fi
|
||||||
|
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin; then
|
||||||
|
[ -n "$M2_HOME" ] &&
|
||||||
|
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||||
|
[ -n "$JAVA_HOME" ] &&
|
||||||
|
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||||
|
[ -n "$CLASSPATH" ] &&
|
||||||
|
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||||
|
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||||
|
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Provide a "standardized" way to retrieve the CLI args that will
|
||||||
|
# work with both Windows and non-Windows executions.
|
||||||
|
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
|
||||||
|
export MAVEN_CMD_LINE_ARGS
|
||||||
|
|
||||||
|
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||||
|
|
||||||
|
exec "$JAVACMD" \
|
||||||
|
$MAVEN_OPTS \
|
||||||
|
$MAVEN_DEBUG_OPTS \
|
||||||
|
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||||
|
"-Dmaven.home=${M2_HOME}" \
|
||||||
|
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||||
|
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
||||||
188
mvnw.cmd
vendored
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
@REM or more contributor license agreements. See the NOTICE file
|
||||||
|
@REM distributed with this work for additional information
|
||||||
|
@REM regarding copyright ownership. The ASF licenses this file
|
||||||
|
@REM to you under the Apache License, Version 2.0 (the
|
||||||
|
@REM "License"); you may not use this file except in compliance
|
||||||
|
@REM with the License. You may obtain a copy of the License at
|
||||||
|
@REM
|
||||||
|
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@REM
|
||||||
|
@REM Unless required by applicable law or agreed to in writing,
|
||||||
|
@REM software distributed under the License is distributed on an
|
||||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
@REM KIND, either express or implied. See the License for the
|
||||||
|
@REM specific language governing permissions and limitations
|
||||||
|
@REM under the License.
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
@REM Maven Start Up Batch script
|
||||||
|
@REM
|
||||||
|
@REM Required ENV vars:
|
||||||
|
@REM JAVA_HOME - location of a JDK home dir
|
||||||
|
@REM
|
||||||
|
@REM Optional ENV vars
|
||||||
|
@REM M2_HOME - location of maven2's installed home dir
|
||||||
|
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||||
|
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||||
|
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||||
|
@REM e.g. to debug Maven itself, use
|
||||||
|
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||||
|
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||||
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||||
|
@echo off
|
||||||
|
@REM set title of command window
|
||||||
|
title %0
|
||||||
|
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||||
|
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||||
|
|
||||||
|
@REM set %HOME% to equivalent of $HOME
|
||||||
|
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||||
|
|
||||||
|
@REM Execute a user defined script before this one
|
||||||
|
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||||
|
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||||
|
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
|
||||||
|
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
|
||||||
|
:skipRcPre
|
||||||
|
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
set ERROR_CODE=0
|
||||||
|
|
||||||
|
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||||
|
@setlocal
|
||||||
|
|
||||||
|
@REM ==== START VALIDATION ====
|
||||||
|
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME not found in your environment. >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
:OkJHome
|
||||||
|
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||||
|
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||||
|
echo location of your Java installation. >&2
|
||||||
|
echo.
|
||||||
|
goto error
|
||||||
|
|
||||||
|
@REM ==== END VALIDATION ====
|
||||||
|
|
||||||
|
:init
|
||||||
|
|
||||||
|
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||||
|
@REM Fallback to current working directory if not found.
|
||||||
|
|
||||||
|
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||||
|
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||||
|
|
||||||
|
set EXEC_DIR=%CD%
|
||||||
|
set WDIR=%EXEC_DIR%
|
||||||
|
:findBaseDir
|
||||||
|
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||||
|
cd ..
|
||||||
|
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||||
|
set WDIR=%CD%
|
||||||
|
goto findBaseDir
|
||||||
|
|
||||||
|
:baseDirFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
goto endDetectBaseDir
|
||||||
|
|
||||||
|
:baseDirNotFound
|
||||||
|
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||||
|
cd "%EXEC_DIR%"
|
||||||
|
|
||||||
|
:endDetectBaseDir
|
||||||
|
|
||||||
|
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||||
|
|
||||||
|
@setlocal EnableExtensions EnableDelayedExpansion
|
||||||
|
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||||
|
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||||
|
|
||||||
|
:endReadAdditionalConfig
|
||||||
|
|
||||||
|
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||||
|
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||||
|
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||||
|
|
||||||
|
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||||
|
|
||||||
|
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||||
|
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
|
||||||
|
)
|
||||||
|
|
||||||
|
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||||
|
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||||
|
if exist %WRAPPER_JAR% (
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Found %WRAPPER_JAR%
|
||||||
|
)
|
||||||
|
) else (
|
||||||
|
if not "%MVNW_REPOURL%" == "" (
|
||||||
|
SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
|
||||||
|
)
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||||
|
echo Downloading from: %DOWNLOAD_URL%
|
||||||
|
)
|
||||||
|
|
||||||
|
powershell -Command "&{"^
|
||||||
|
"$webclient = new-object System.Net.WebClient;"^
|
||||||
|
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||||
|
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||||
|
"}"^
|
||||||
|
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
|
||||||
|
"}"
|
||||||
|
if "%MVNW_VERBOSE%" == "true" (
|
||||||
|
echo Finished downloading %WRAPPER_JAR%
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@REM End of extension
|
||||||
|
|
||||||
|
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||||
|
@REM work with both Windows and non-Windows executions.
|
||||||
|
set MAVEN_CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
%MAVEN_JAVA_EXE% ^
|
||||||
|
%JVM_CONFIG_MAVEN_PROPS% ^
|
||||||
|
%MAVEN_OPTS% ^
|
||||||
|
%MAVEN_DEBUG_OPTS% ^
|
||||||
|
-classpath %WRAPPER_JAR% ^
|
||||||
|
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
|
||||||
|
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||||
|
if ERRORLEVEL 1 goto error
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:error
|
||||||
|
set ERROR_CODE=1
|
||||||
|
|
||||||
|
:end
|
||||||
|
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||||
|
|
||||||
|
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
|
||||||
|
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||||
|
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
|
||||||
|
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
|
||||||
|
:skipRcPost
|
||||||
|
|
||||||
|
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||||
|
if "%MAVEN_BATCH_PAUSE%"=="on" pause
|
||||||
|
|
||||||
|
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
|
||||||
|
|
||||||
|
cmd /C exit /B %ERROR_CODE%
|
||||||
112
pom.xml
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.example</groupId>
|
||||||
|
<artifactId>Mesos2</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<name>MesosLL07</name>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<junit.version>5.12.1</junit.version>
|
||||||
|
<javafx.version>21.0.6</javafx.version>
|
||||||
|
<log4j.version>2.23.1</log4j.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.controlsfx</groupId>
|
||||||
|
<artifactId>controlsfx</artifactId>
|
||||||
|
<version>11.2.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
|
<version>${junit.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<version>${junit.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- JavaFX -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-controls</artifactId>
|
||||||
|
<version>${javafx.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-swing</artifactId>
|
||||||
|
<version>${javafx.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-fxml</artifactId>
|
||||||
|
<version>${javafx.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Apache PDFBox per leggere il PDF -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.pdfbox</groupId>
|
||||||
|
<artifactId>pdfbox</artifactId>
|
||||||
|
<version>2.0.29</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-api</artifactId>
|
||||||
|
<version>${log4j.version}</version> <!-- Controlla se ci sono versioni più recenti, ma questa è ottima e sicura -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Log4j 2 Core -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-core</artifactId>
|
||||||
|
<version>${log4j.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.13.0</version>
|
||||||
|
<configuration>
|
||||||
|
<source>25</source>
|
||||||
|
<target>25</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-maven-plugin</artifactId>
|
||||||
|
<version>0.0.8</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<!-- Default configuration for running with: mvn clean javafx:run -->
|
||||||
|
<id>default-cli</id>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>org.example.mesosll07/org.example.mesosll07.HelloApplication</mainClass>
|
||||||
|
<launcher>app</launcher>
|
||||||
|
<jlinkZipName>app</jlinkZipName>
|
||||||
|
<jlinkImageName>app</jlinkImageName>
|
||||||
|
<noManPages>true</noManPages>
|
||||||
|
<stripDebug>true</stripDebug>
|
||||||
|
<noHeaderFiles>true</noHeaderFiles>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
22
src/main/java/Server/Automaton/ActionResult.java
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package Server.Automaton;
|
||||||
|
|
||||||
|
public class ActionResult {
|
||||||
|
private final boolean success;
|
||||||
|
private final String errorMessage; // null se success
|
||||||
|
|
||||||
|
private ActionResult(boolean success, String errorMessage) {
|
||||||
|
this.success = success;
|
||||||
|
this.errorMessage = errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ActionResult ok() {
|
||||||
|
return new ActionResult(true, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ActionResult failure(String message) {
|
||||||
|
return new ActionResult(false, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuccess() { return success; }
|
||||||
|
public String getErrorMessage(){ return errorMessage; }
|
||||||
|
}
|
||||||
53
src/main/java/Server/Automaton/Automaton.java
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package Server.Automaton;
|
||||||
|
|
||||||
|
public class Automaton {
|
||||||
|
|
||||||
|
private GameState state = GameState.SETUP;
|
||||||
|
|
||||||
|
public GameState getState(){
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean canEvolve(int val){
|
||||||
|
int currOrd = state.ordinal();
|
||||||
|
int setupOrd = GameState.SETUP.ordinal();
|
||||||
|
|
||||||
|
if (currOrd == setupOrd && val > 5) {
|
||||||
|
System.out.println("Invalid number of player");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Boolean evolve(int i) {
|
||||||
|
if (!canEvolve(i))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int currOrd = state.ordinal();
|
||||||
|
int lastOrd = GameState.GAME_OVER.ordinal();
|
||||||
|
|
||||||
|
if (currOrd < lastOrd) {
|
||||||
|
state = state.next(i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Boolean evolveTo(GameState toState, int i){
|
||||||
|
if (!canEvolve(i))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int toOrd = toState.ordinal();
|
||||||
|
int currOrd = state.ordinal();
|
||||||
|
|
||||||
|
if (toOrd>currOrd) {
|
||||||
|
state = toState;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
789
src/main/java/Server/Automaton/Game.java
Normal file
@@ -0,0 +1,789 @@
|
|||||||
|
package Server.Automaton;
|
||||||
|
|
||||||
|
import Server.*;
|
||||||
|
import Server.Cards.*;
|
||||||
|
import Server.Utils.LoadingCardsException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Central game controller for MESOS.
|
||||||
|
*
|
||||||
|
* Owns the full game lifecycle:
|
||||||
|
*
|
||||||
|
* SETUP → newGame()
|
||||||
|
* TOTEM_PLACEMENT → placeTotem() — one call per player, in TurnTile order
|
||||||
|
* ACTION_RESOLUTION → resolveCardAction() — one call per pending action
|
||||||
|
* EXTRA_DRAW → extraDrawAction() / skipExtraDraw() [only if a player owns the building]
|
||||||
|
* EVENT_RESOLUTION → handled automatically at end of round
|
||||||
|
* GAME_OVER → endGame()
|
||||||
|
*
|
||||||
|
* All public methods that advance state return a boolean:
|
||||||
|
* true = action accepted, state may have changed
|
||||||
|
* false = action rejected, state unchanged (caller should log / notify client)
|
||||||
|
*
|
||||||
|
* NOTE: GameState must have EXTRA_DRAW added to the enum for this class to compile:
|
||||||
|
* UNKNOWN, SETUP, TOTEM_PLACEMENT, ACTION_RESOLUTION, EXTRA_DRAW, EVENT_RESOLUTION, GAME_OVER
|
||||||
|
*
|
||||||
|
* NOTE: Tribe.endPoints() already computes end-game building bonuses internally via
|
||||||
|
* buildingAbilitiesEndPoints(). Do NOT also call BuildingManager.endgameForSix() etc.
|
||||||
|
* in endGame() — that would count those bonuses twice.
|
||||||
|
*/
|
||||||
|
public class Game {
|
||||||
|
private static final Logger logger = LogManager.getLogger(Game.class);
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Constants
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private static final int TOTAL_ROUNDS = 10;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Core state
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private final List<Player> players;
|
||||||
|
private GameBoard gameBoard;
|
||||||
|
private int round;
|
||||||
|
private GameState state;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Phase tracking
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** How many players have placed their totem this round. */
|
||||||
|
private int totemPlacedCount;
|
||||||
|
|
||||||
|
/** Index of the offering tile (central row) currently being resolved (left to right). */
|
||||||
|
private int currentOfferingTileIndex;
|
||||||
|
|
||||||
|
/** Actions the current player still has to resolve. */
|
||||||
|
private List<Symbol> pendingActions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue of players who own the EXTRA_DRAW building and still need to
|
||||||
|
* make their extra-draw decision this round. Ordered by turn order.
|
||||||
|
*/
|
||||||
|
//TODO to check it should be a single player
|
||||||
|
private final List<Player> extraDrawQueue;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Constructor
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public Game(List<Player> players) {
|
||||||
|
this.players = new ArrayList<>(players);
|
||||||
|
this.round = 0;
|
||||||
|
this.state = GameState.SETUP;
|
||||||
|
this.pendingActions = new ArrayList<>();
|
||||||
|
this.extraDrawQueue = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// SETUP
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises a new game: loads the deck, builds the board, deals initial
|
||||||
|
* food, and randomises the first turn order.
|
||||||
|
*
|
||||||
|
* Initial food (rulebook setup step 10):
|
||||||
|
* Slot 1 -> 2 food, slots 2-3 -> 3 food, slots 4-5 -> 4 food.
|
||||||
|
*/
|
||||||
|
public void newGame(String cardsFilePath) {
|
||||||
|
try {
|
||||||
|
logger.info("STARTING NEW GAME : ", cardsFilePath);
|
||||||
|
|
||||||
|
CardDeck deck = new CardDeck();
|
||||||
|
deck.setForNPlayer(cardsFilePath, players.size());
|
||||||
|
|
||||||
|
gameBoard = new GameBoard(Era.I, deck, players.size());
|
||||||
|
gameBoard.initOfferingTiles(players.size());
|
||||||
|
gameBoard.setupInitialRows(players.size());
|
||||||
|
|
||||||
|
gameBoard.getTurnTile().setInitialOrder(players);
|
||||||
|
dealInitialFood();
|
||||||
|
|
||||||
|
round = 1;
|
||||||
|
totemPlacedCount = 0;
|
||||||
|
state = GameState.TOTEM_PLACEMENT;
|
||||||
|
|
||||||
|
logger.info("Game started! Round 1.");
|
||||||
|
logTurnOrder();
|
||||||
|
|
||||||
|
} catch (LoadingCardsException e) {
|
||||||
|
System.err.println("Fatal: could not load cards — " + e.getMessage());
|
||||||
|
state = GameState.GAME_OVER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dealInitialFood() {
|
||||||
|
Player[] order = gameBoard.getTurnTile().getPositions();
|
||||||
|
for (int i = 0; i < order.length; i++) {
|
||||||
|
int food = (i == 0) ? 2 : (i <= 2) ? 3 : 4;
|
||||||
|
order[i].addFood(food);
|
||||||
|
logger.info(" " + order[i].getNickname() + " starts with " + food + " food.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// PHASE 1 — TOTEM PLACEMENT
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places the player's totem on an offering tile.
|
||||||
|
*
|
||||||
|
* Players must place in the order defined by the TurnTile (top to bottom).
|
||||||
|
* If the wrong player calls this, it is rejected.
|
||||||
|
*
|
||||||
|
* @param player the player attempting to place
|
||||||
|
* @param tileIndex 0-based index into the offering tile list
|
||||||
|
* @return true if placement was accepted
|
||||||
|
*/
|
||||||
|
public boolean placeTotem(Player player, int tileIndex) {
|
||||||
|
if (state != GameState.TOTEM_PLACEMENT) {
|
||||||
|
logger.info("Error: not the totem-placement phase.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce turn order
|
||||||
|
Player expected = gameBoard.getTurnTile().getPositions()[totemPlacedCount];
|
||||||
|
if (expected != player) {
|
||||||
|
logger.info("Error: it is " + expected.getNickname()
|
||||||
|
+ "'s turn to place, not " + player.getNickname() + ".");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<OfferingTile> tiles = gameBoard.getOfferingTiles();
|
||||||
|
if (tileIndex < 0 || tileIndex >= tiles.size()) {
|
||||||
|
logger.info("Error: tile index " + tileIndex + " out of range.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
OfferingTile chosen = tiles.get(tileIndex);
|
||||||
|
if (!gameBoard.placeTotem(player, chosen, gameBoard.getTurnTile())) {
|
||||||
|
logger.info("Tile " + chosen.getLetter() + " is already occupied.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
totemPlacedCount++;
|
||||||
|
logger.info("player {} TOTEM PLACED ON TILE {}", player.getNickname() , chosen.getLetter());
|
||||||
|
|
||||||
|
// + " (" + totemPlacedCount + "/" + players.size() + ").");
|
||||||
|
|
||||||
|
if (totemPlacedCount == players.size()) {
|
||||||
|
logger.info("START ACTION RESOLUTION");
|
||||||
|
startActionResolution();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// PHASE 2 — ACTION RESOLUTION
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
private void startActionResolution() {
|
||||||
|
state = GameState.ACTION_RESOLUTION;
|
||||||
|
currentOfferingTileIndex = 0;
|
||||||
|
logger.info("All totems placed — resolving actions left to right.");
|
||||||
|
loadNextPlayerActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans offering tiles left to right for the next occupied one, loads that
|
||||||
|
* player's actions, and either resolves them automatically (food tile) or
|
||||||
|
* waits for player input (card tiles).
|
||||||
|
*
|
||||||
|
* Calls onAllActionsResolved() when every tile has been processed.
|
||||||
|
*/
|
||||||
|
private void loadNextPlayerActions() {
|
||||||
|
List<OfferingTile> tiles = gameBoard.getOfferingTiles();
|
||||||
|
|
||||||
|
while (currentOfferingTileIndex < tiles.size()) {
|
||||||
|
OfferingTile tile = tiles.get(currentOfferingTileIndex);
|
||||||
|
|
||||||
|
if (tile.isEmpty()) {
|
||||||
|
currentOfferingTileIndex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player player = tile.getOccupant();
|
||||||
|
pendingActions = new ArrayList<>(tile.getActions());
|
||||||
|
logger.info(" PLAYER {} PENDING_ACTIONS {} ", player.getNickname() , pendingActions );
|
||||||
|
// --- Food tile: resolve entirely without player input ---
|
||||||
|
if (pendingActions.get(0) == Symbol.FOOD) {
|
||||||
|
|
||||||
|
int food = pendingActions.size();
|
||||||
|
player.addFood(food);
|
||||||
|
BuildingManager.bonusEndTurn(player, food); // BONUS_FOOD_ENDTURN building effect
|
||||||
|
logger.info(player.getNickname() + " receives " + food
|
||||||
|
+ " food from tile " + tile.getLetter() + ".");
|
||||||
|
pendingActions.clear();
|
||||||
|
finishPlayerTurn(player);
|
||||||
|
currentOfferingTileIndex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Card tile: strip impossible actions first ---
|
||||||
|
filterImpossibleActions(player);
|
||||||
|
|
||||||
|
if (pendingActions.isEmpty()) {
|
||||||
|
// All actions were impossible (both rows empty)
|
||||||
|
finishPlayerTurn(player);
|
||||||
|
currentOfferingTileIndex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the player to call resolveCardAction()
|
||||||
|
logger.info("player {} pending ACTIONS {}" , player.getNickname() , pendingActions);
|
||||||
|
// logger.info("It is " + player.getNickname() + "'s turn — actions: " + pendingActions);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Every tile has been processed
|
||||||
|
onAllActionsResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strips actions that cannot be executed because the required row is empty.
|
||||||
|
* Pure filter — no side effects beyond mutating pendingActions.
|
||||||
|
*/
|
||||||
|
private void filterImpossibleActions(Player player) {
|
||||||
|
|
||||||
|
Card ctop = gameBoard.getTopRow().stream().filter(s -> s instanceof CharacterCard || s instanceof BuildingCard).findFirst().orElse(null);
|
||||||
|
Card cdown = gameBoard.getBottomRow().stream().filter(s -> s instanceof CharacterCard || s instanceof BuildingCard).findFirst().orElse(null);
|
||||||
|
|
||||||
|
boolean topEmpty = ctop == null;
|
||||||
|
boolean bottomEmpty = cdown == null;
|
||||||
|
|
||||||
|
if (topEmpty) {
|
||||||
|
long removed = pendingActions.stream().filter(s -> s == Symbol.UP).count();
|
||||||
|
pendingActions.removeIf(s -> s == Symbol.UP);
|
||||||
|
if (removed > 0)
|
||||||
|
logger.info("{} LOSES ACTION UP" ,player.getNickname(), removed);
|
||||||
|
|
||||||
|
}
|
||||||
|
if (bottomEmpty) {
|
||||||
|
long removed = pendingActions.stream().filter(s -> s == Symbol.DOWN).count();
|
||||||
|
pendingActions.removeIf(s -> s == Symbol.DOWN);
|
||||||
|
if (removed > 0)
|
||||||
|
logger.info("{} LOSES ACTION DOWN" ,player.getNickname(), removed);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves one card-pick action for the current player.
|
||||||
|
* Works for both CharacterCards and BuildingCards.
|
||||||
|
*
|
||||||
|
* For BuildingCards: builder discount is applied and the player must have
|
||||||
|
* enough food. If not, the card is put back and the call is rejected — the
|
||||||
|
* player must choose a different card.
|
||||||
|
*
|
||||||
|
* @param player the acting player (must match the current tile occupant)
|
||||||
|
* @param fromTop true = pick from the top row; false = from the bottom row
|
||||||
|
* @param cardId the ID of the card to take
|
||||||
|
* @return true if the action was accepted and consumed
|
||||||
|
*/
|
||||||
|
public ActionResult resolveCardAction(Player player, boolean fromTop, int cardId) {
|
||||||
|
if (state != GameState.ACTION_RESOLUTION) {
|
||||||
|
logger.info("Error: not in ACTION_RESOLUTION phase.");
|
||||||
|
return ActionResult.failure("Error: not in ACTION_RESOLUTION phase.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
OfferingTile currentTile = gameBoard.getOfferingTiles().get(currentOfferingTileIndex);
|
||||||
|
if (currentTile.getOccupant() != player) {
|
||||||
|
logger.info("Error: it is not " + player.getNickname() + "'s turn to act.");
|
||||||
|
return ActionResult.failure("Error: it is not " + player.getNickname() + "'s turn to act.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Symbol required = fromTop ? Symbol.UP : Symbol.DOWN;
|
||||||
|
if (!pendingActions.contains(required)) {
|
||||||
|
logger.info("Error: no " + required + " action available for "
|
||||||
|
+ player.getNickname() + ".");
|
||||||
|
return ActionResult.failure("Error: no " + required + " action available for "
|
||||||
|
+ player.getNickname() + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the card in the correct row
|
||||||
|
Card card = fromTop
|
||||||
|
? gameBoard.takeFromTopRow(cardId)
|
||||||
|
: gameBoard.takeFromBottomRow(cardId);
|
||||||
|
|
||||||
|
if (card == null) {
|
||||||
|
logger.info("Error: card " + cardId + " not found in the "
|
||||||
|
+ (fromTop ? "top" : "bottom") + " row.");
|
||||||
|
return ActionResult.failure("Error: card " + cardId + " not found in the "
|
||||||
|
+ (fromTop ? "top" : "bottom") + " row.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event cards can never be taken by players
|
||||||
|
if (card instanceof EventCard) {
|
||||||
|
putCardBack(card, fromTop);
|
||||||
|
logger.info("Error: Event cards cannot be taken.");
|
||||||
|
return ActionResult.failure("Error: Event cards cannot be taken.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the card
|
||||||
|
if (card instanceof BuildingCard) {
|
||||||
|
ActionResult result =resolveBuildingCard(player, (BuildingCard) card);
|
||||||
|
if (!result.isSuccess()) {
|
||||||
|
putCardBack(card, fromTop);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resolveCharacterCard(player, (CharacterCard) card);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume the action
|
||||||
|
pendingActions.remove(required);
|
||||||
|
logger.info("player {} TOOK CARD {} --> PEDNING ACTIONS {}",player.getNickname(), cardId, pendingActions);
|
||||||
|
|
||||||
|
|
||||||
|
// A row may have become empty after this draw — re-check impossible actions
|
||||||
|
if (!pendingActions.isEmpty()) {
|
||||||
|
filterImpossibleActions(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no more actions remain, finish this player's turn
|
||||||
|
if (pendingActions.isEmpty()) {
|
||||||
|
finishPlayerTurn(player);
|
||||||
|
currentOfferingTileIndex++;
|
||||||
|
loadNextPlayerActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionResult.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles building card acquisition.
|
||||||
|
* Applies builder discount, checks affordability, deducts food, and
|
||||||
|
* registers the building with BuildingManager.
|
||||||
|
*
|
||||||
|
* @return true if the acquisition succeeded
|
||||||
|
*/
|
||||||
|
private ActionResult resolveBuildingCard(Player player, BuildingCard building) {
|
||||||
|
int discount = player.getPlayerTribe().buildersDiscount();
|
||||||
|
int actualCost = Math.max(0, building.getCost() - discount);
|
||||||
|
|
||||||
|
if (player.getFoodTokens() < actualCost) {
|
||||||
|
logger.info(player.getNickname() + " cannot afford building "
|
||||||
|
+ building.getCardId() + " (cost after discount: " + actualCost
|
||||||
|
+ ", has: " + player.getFoodTokens() + " food).");
|
||||||
|
return ActionResult.failure(player.getNickname() + " cannot afford building "
|
||||||
|
+ building.getCardId() + " (cost after discount: " + actualCost
|
||||||
|
+ ", has: " + player.getFoodTokens() + " food).");
|
||||||
|
}
|
||||||
|
|
||||||
|
player.removeFood(actualCost);
|
||||||
|
player.getPlayerTribe().addBuilding(building);
|
||||||
|
gameBoard.getBuildingManager().addActiveBuilding(building, player);
|
||||||
|
|
||||||
|
logger.info(player.getNickname() + " bought building "
|
||||||
|
+ building.getCardId() + " for " + actualCost + " food.");
|
||||||
|
return ActionResult.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles character card acquisition and triggers all immediate effects:
|
||||||
|
*
|
||||||
|
* Hunter with leg icon (iconValue > 0):
|
||||||
|
* Take 1 food per Hunter currently in tribe (including this one).
|
||||||
|
*
|
||||||
|
* FOOD_FOR_SIX building:
|
||||||
|
* Take 6 food if this draw completes a full set of all 6 character types.
|
||||||
|
*
|
||||||
|
* FOOD_PER_INVENTORS building:
|
||||||
|
* Take 3 food if this Inventor forms a matching pair.
|
||||||
|
*
|
||||||
|
* The card is added to the tribe BEFORE effects fire so all counts include it.
|
||||||
|
*/
|
||||||
|
private void resolveCharacterCard(Player player, CharacterCard character) {
|
||||||
|
player.getPlayerTribe().addCharacter(character);
|
||||||
|
|
||||||
|
// Hunter with leg icon: immediate food reward
|
||||||
|
if (character.getCharacterType() == CharacterType.HUNTER
|
||||||
|
&& character.getIconValue() > 0) {
|
||||||
|
int food = player.getPlayerTribe().huntersNumber() * character.getIconValue();
|
||||||
|
player.addFood(food);
|
||||||
|
logger.info(player.getNickname() + "'s hunter (leg icon) grants +"
|
||||||
|
+ food + " food.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Building-triggered effects on character draw
|
||||||
|
gameBoard.getBuildingManager().foodForSix(player, character);
|
||||||
|
if (character.getCharacterType() == CharacterType.INVENTOR) {
|
||||||
|
gameBoard.getBuildingManager().foodPerInventors(player, character);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(player.getNickname() + " drew "
|
||||||
|
+ character.getCharacterType() + " (id " + character.getCardId() + ").");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the current player as done with their tile actions.
|
||||||
|
* Calls TurnTile.returnTotem(), which places them in the next available
|
||||||
|
* slot and immediately applies the position food reward or penalty.
|
||||||
|
*/
|
||||||
|
private void finishPlayerTurn(Player player) {
|
||||||
|
int slot = gameBoard.getTurnTile().returnTotem(player);
|
||||||
|
OfferingTile off = gameBoard.getOfferingTile(player);
|
||||||
|
logger.info("player {} leaving offering {} " ,player.getNickname(), off);
|
||||||
|
if(off!=null) off.setOccupant(null);
|
||||||
|
logger.info("player {} left offering {} slot {} " ,player.getNickname(), off, slot);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// PHASE 2b — EXTRA DRAW (EXTRA_DRAW building effect)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when every offering tile has been resolved.
|
||||||
|
* Checks if any player owns the EXTRA_DRAW building and, if so, enters
|
||||||
|
* the EXTRA_DRAW phase. Otherwise proceeds directly to end of round.
|
||||||
|
*
|
||||||
|
* Rulebook appendix: "After resolving all actions (once all totems are back
|
||||||
|
* on the TurnTile) and before the end-of-round phase, you may take 1
|
||||||
|
* Character or Building card (paying its cost) from the top row."
|
||||||
|
*/
|
||||||
|
private void onAllActionsResolved() {
|
||||||
|
logger.info("All actions resolved.");
|
||||||
|
|
||||||
|
extraDrawQueue.clear();
|
||||||
|
// Iterate in turn order so extra draws happen in a consistent sequence
|
||||||
|
for (Player p : gameBoard.getTurnTile().getPositions()) {
|
||||||
|
if (p != null && gameBoard.getBuildingManager().extraDraw(p)) {
|
||||||
|
extraDrawQueue.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!extraDrawQueue.isEmpty()) {
|
||||||
|
state = GameState.EXTRA_DRAW;
|
||||||
|
System.out.println("Extra draw phase: "
|
||||||
|
+ extraDrawQueue.get(0).getNickname() + " may draw first.");
|
||||||
|
} else {
|
||||||
|
endRound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The player uses their EXTRA_DRAW building to take one card from the top row.
|
||||||
|
* Building cards may also be taken, paying the cost after builder discounts.
|
||||||
|
*
|
||||||
|
* @param player the player taking the extra draw
|
||||||
|
* @param cardId the ID of the card they want from the top row
|
||||||
|
* @return true if the action was accepted
|
||||||
|
*/
|
||||||
|
public ActionResult extraDrawAction(Player player, int cardId) {
|
||||||
|
if (state != GameState.EXTRA_DRAW) {
|
||||||
|
System.out.println("Error: not in EXTRA_DRAW phase.");
|
||||||
|
return ActionResult.failure("Error: not in EXTRA_DRAW phase.");
|
||||||
|
}
|
||||||
|
if (extraDrawQueue.isEmpty() || extraDrawQueue.get(0) != player) {
|
||||||
|
System.out.println("Error: it is not " + player.getNickname()
|
||||||
|
+ "'s extra draw turn.");
|
||||||
|
return ActionResult.failure("Error: it is not " + player.getNickname()
|
||||||
|
+ "'s extra draw turn.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Card card = gameBoard.takeFromTopRow(cardId);
|
||||||
|
if (card == null) {
|
||||||
|
System.out.println("Error: card " + cardId + " not found in top row.");
|
||||||
|
return ActionResult.failure("Error: card " + cardId + " not found in top row.");
|
||||||
|
}
|
||||||
|
if (card instanceof EventCard) {
|
||||||
|
gameBoard.getTopRow().add(card);
|
||||||
|
System.out.println("Error: cannot take Event cards.");
|
||||||
|
return ActionResult.failure("Error: cannot take Event cards.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card instanceof BuildingCard) {
|
||||||
|
ActionResult result = resolveBuildingCard(player, (BuildingCard) card);
|
||||||
|
if (!result.isSuccess()) {
|
||||||
|
gameBoard.getTopRow().add(card);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resolveCharacterCard(player, (CharacterCard) card);
|
||||||
|
}
|
||||||
|
|
||||||
|
advanceExtraDrawQueue();
|
||||||
|
return ActionResult.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The player declines to use their EXTRA_DRAW building this round.
|
||||||
|
*
|
||||||
|
* @return true if accepted
|
||||||
|
*/
|
||||||
|
public boolean skipExtraDraw(Player player) {
|
||||||
|
if (state != GameState.EXTRA_DRAW) return false;
|
||||||
|
if (extraDrawQueue.isEmpty() || extraDrawQueue.get(0) != player) return false;
|
||||||
|
|
||||||
|
System.out.println(player.getNickname() + " skips extra draw.");
|
||||||
|
advanceExtraDrawQueue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void advanceExtraDrawQueue() {
|
||||||
|
extraDrawQueue.remove(0);
|
||||||
|
if (extraDrawQueue.isEmpty()) {
|
||||||
|
endRound();
|
||||||
|
} else {
|
||||||
|
System.out.println("Next extra draw: " + extraDrawQueue.get(0).getNickname());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// END OF ROUND
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs all end-of-round steps in rulebook order:
|
||||||
|
*
|
||||||
|
* Step 1 — Resolve Event cards.
|
||||||
|
* Normal rounds: only bottom row events.
|
||||||
|
* Round 10: events from BOTH rows; then GAME_OVER.
|
||||||
|
* Step 2 — Advance rows: discard bottom non-buildings, shift top
|
||||||
|
* non-buildings down, draw (numPlayers + 4) new cards.
|
||||||
|
* Step 3 — If a new Era card was revealed in step 2, trigger
|
||||||
|
* the Era transition (building row swap).
|
||||||
|
* Step 4 — Start the next round.
|
||||||
|
*/
|
||||||
|
private void endRound() {
|
||||||
|
state = GameState.EVENT_RESOLUTION;
|
||||||
|
//notify( GameState.EVENT_RESOLUTION);
|
||||||
|
|
||||||
|
logger.info("--- End of round " + round + " ---");
|
||||||
|
|
||||||
|
boolean isFinalRound = (round == TOTAL_ROUNDS);
|
||||||
|
|
||||||
|
// Step 1: resolve events
|
||||||
|
resolveVisibleEvents(isFinalRound);
|
||||||
|
|
||||||
|
if (isFinalRound) {
|
||||||
|
state = GameState.GAME_OVER;
|
||||||
|
notify( GameState.GAME_OVER, endGame());
|
||||||
|
logger.info("Round 10 complete — game over!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: advance rows; returns true if a new Era card was revealed
|
||||||
|
boolean eraChangeTriggered = gameBoard.advanceRows(players.size());
|
||||||
|
|
||||||
|
// Step 3: handle Era transition if needed
|
||||||
|
if (eraChangeTriggered) {
|
||||||
|
gameBoard.triggerEraChange(players.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: start next round
|
||||||
|
startNewRound();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves all visible Event cards in the correct order.
|
||||||
|
*
|
||||||
|
* Rules:
|
||||||
|
* - Multiple events of different types: any order, Sustainment always last.
|
||||||
|
* - Two events of the same type in the same round: resolve in Era order.
|
||||||
|
* - Round 10: events from both rows must be resolved.
|
||||||
|
*
|
||||||
|
* @param bothRows true means events from both rows are included (round 10)
|
||||||
|
*/
|
||||||
|
private void resolveVisibleEvents(boolean bothRows) {
|
||||||
|
List<EventCard> events = bothRows
|
||||||
|
? gameBoard.getAllVisibleEvents() // already sorted by Era ordinal
|
||||||
|
: gameBoard.getVisibleEvents(); // already sorted by Era ordinal
|
||||||
|
|
||||||
|
if (events.isEmpty()) {
|
||||||
|
logger.info("No events to resolve this round.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sustainment must always be resolved last
|
||||||
|
List<EventCard> others = new ArrayList<>();
|
||||||
|
List<EventCard> sustainments = new ArrayList<>();
|
||||||
|
for (EventCard e : events) {
|
||||||
|
if (e.getEvent() == Event.SUSTAINMENT) sustainments.add(e);
|
||||||
|
else others.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (EventCard e : others) resolveOneEvent(e);
|
||||||
|
for (EventCard e : sustainments) resolveOneEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resolveOneEvent(EventCard event) {
|
||||||
|
notify( GameState.EVENT_RESOLUTION, event.toString());
|
||||||
|
logger.info("EVENT Resolving: " + event.getEvent() + " (Era " + event.getEra() + ")");
|
||||||
|
EventsSolver.solveEvents(Collections.singletonList(event), players);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startNewRound() {
|
||||||
|
|
||||||
|
round++;
|
||||||
|
|
||||||
|
notify( GameState.NEXT_ROUND, "Round="+ round);
|
||||||
|
logger.info("startNewRound: {}", round );
|
||||||
|
totemPlacedCount = 0;
|
||||||
|
currentOfferingTileIndex = 0;
|
||||||
|
pendingActions.clear();
|
||||||
|
|
||||||
|
gameBoard.clearOfferingTiles();
|
||||||
|
gameBoard.getTurnTile().resetTrack();
|
||||||
|
|
||||||
|
// notify( GameState.TOTEM_PLACEMENT);
|
||||||
|
state = GameState.TOTEM_PLACEMENT;
|
||||||
|
logger.info("=== NEW Round " + round + " ===");
|
||||||
|
logTurnOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// END GAME
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the final leaderboard and returns it as a formatted string.
|
||||||
|
*
|
||||||
|
* Final score = prestige points accumulated during play
|
||||||
|
* + Tribe.endPoints() which covers: builder PP, inventor PP,
|
||||||
|
* artist PP, building base PP, and building end-game abilities.
|
||||||
|
*
|
||||||
|
* IMPORTANT: do NOT call BuildingManager.endgameForSix() /
|
||||||
|
* endgameBonusCharacter() / endgameBonusPoints() here.
|
||||||
|
* Tribe.endPoints() already computes those bonuses. Calling both would
|
||||||
|
* count them twice.
|
||||||
|
*
|
||||||
|
* Tie-breaking rule: most food wins. Further ties share the victory.
|
||||||
|
*/
|
||||||
|
public String endGame() {
|
||||||
|
state = GameState.GAME_OVER;
|
||||||
|
|
||||||
|
List<Player> ranked = new ArrayList<>(players);
|
||||||
|
ranked.sort((a, b) -> {
|
||||||
|
int diff = totalScore(b) - totalScore(a);
|
||||||
|
if (diff != 0) return diff;
|
||||||
|
return b.getFoodTokens() - a.getFoodTokens();
|
||||||
|
});
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder("=== FINAL LEADERBOARD ===\n");
|
||||||
|
for (int i = 0; i < ranked.size(); i++) {
|
||||||
|
Player p = ranked.get(i);
|
||||||
|
sb.append(i + 1).append(". ")
|
||||||
|
.append(p.getNickname())
|
||||||
|
.append(" — ").append(totalScore(p)).append(" PP")
|
||||||
|
.append(" (food: ").append(p.getFoodTokens()).append(")\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
Player winner = ranked.get(0);
|
||||||
|
sb.append("\nWINNER: ")
|
||||||
|
.append(winner.getNickname().toUpperCase())
|
||||||
|
.append(" with ").append(totalScore(winner)).append(" PP!");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Prestige points accumulated during play + final scoring from the Tribe engine. */
|
||||||
|
private int totalScore(Player p) {
|
||||||
|
return p.getPrestigePoints() + p.getPlayerTribe().endPoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// HELPERS
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
private void putCardBack(Card card, boolean wasTopRow) {
|
||||||
|
if (wasTopRow) gameBoard.getTopRow().add(card);
|
||||||
|
else gameBoard.getBottomRow().add(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logTurnOrder() {
|
||||||
|
Player[] order = gameBoard.getTurnTile().getPositions();
|
||||||
|
StringBuilder sb = new StringBuilder("Turn order: ");
|
||||||
|
for (int i = 0; i < order.length; i++) {
|
||||||
|
if (order[i] != null)
|
||||||
|
sb.append(i + 1).append(". ").append(order[i].getNickname()).append(" ");
|
||||||
|
}
|
||||||
|
logger.info("logTurnOrder: " + sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// GETTERS
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
public List<Player> getPlayers() { return Collections.unmodifiableList(players); }
|
||||||
|
public GameBoard getGameBoard() { return gameBoard; }
|
||||||
|
public int getRound() { return round; }
|
||||||
|
public GameState getState() { return state; }
|
||||||
|
public void setState(GameState s) { this.state = s; }
|
||||||
|
public List<Symbol> getPendingActions() { return Collections.unmodifiableList(pendingActions); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the player currently expected to act:
|
||||||
|
* TOTEM_PLACEMENT — the next player who should place their totem
|
||||||
|
* ACTION_RESOLUTION — the player currently resolving tile actions
|
||||||
|
* EXTRA_DRAW — the next player to make their extra-draw decision
|
||||||
|
*/
|
||||||
|
public Player getCurrentPlayer() {
|
||||||
|
switch (state) {
|
||||||
|
case TOTEM_PLACEMENT:
|
||||||
|
if (totemPlacedCount < players.size())
|
||||||
|
return gameBoard.getTurnTile().getPositions()[totemPlacedCount];
|
||||||
|
return null;
|
||||||
|
case ACTION_RESOLUTION:
|
||||||
|
List<OfferingTile> tiles = gameBoard.getOfferingTiles();
|
||||||
|
if (currentOfferingTileIndex < tiles.size())
|
||||||
|
return tiles.get(currentOfferingTileIndex).getOccupant();
|
||||||
|
return null;
|
||||||
|
case EXTRA_DRAW:
|
||||||
|
return extraDrawQueue.isEmpty() ? null : extraDrawQueue.get(0);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Game{" +
|
||||||
|
" state=" + state +
|
||||||
|
",\n round=" + round +
|
||||||
|
",\n totemPlacedCount=" + totemPlacedCount +
|
||||||
|
",\n currentOfferingTileIndex=" + currentOfferingTileIndex +
|
||||||
|
",\n pendingActions=" + pendingActions +
|
||||||
|
",\n extraDrawQueue=" + extraDrawQueue +
|
||||||
|
" \n GameBoard=" + gameBoard +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public record GameEventNotification(GameState event, String message) {}
|
||||||
|
|
||||||
|
public interface GameEventListener {
|
||||||
|
void onEvent(GameEventNotification notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Campo privato
|
||||||
|
private GameEventListener eventListener;
|
||||||
|
|
||||||
|
// Setter per il controller FX
|
||||||
|
public void setEventListener(GameEventListener listener) {
|
||||||
|
this.eventListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metodi privati di notifica
|
||||||
|
private void notify(GameState event, String message) {
|
||||||
|
if (eventListener != null)
|
||||||
|
eventListener.onEvent(new GameEventNotification(event, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notify(GameState event) {
|
||||||
|
notify(event, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
68
src/main/java/Server/Automaton/GameState.java
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package Server.Automaton;
|
||||||
|
|
||||||
|
public enum GameState implements Comparable<GameState> {
|
||||||
|
UNKNOWN,
|
||||||
|
SETUP,
|
||||||
|
NEXT_ROUND,
|
||||||
|
TOTEM_PLACEMENT,
|
||||||
|
ACTION_RESOLUTION,
|
||||||
|
END_ACTION,
|
||||||
|
EXTRA_DRAW,
|
||||||
|
EVENT_RESOLUTION,
|
||||||
|
GAME_OVER;
|
||||||
|
|
||||||
|
GameState next(int turn){
|
||||||
|
switch (this) {
|
||||||
|
case SETUP:
|
||||||
|
return SETUP;
|
||||||
|
case NEXT_ROUND:
|
||||||
|
return NEXT_ROUND;
|
||||||
|
case TOTEM_PLACEMENT:
|
||||||
|
return ACTION_RESOLUTION;
|
||||||
|
case ACTION_RESOLUTION:
|
||||||
|
return END_ACTION;
|
||||||
|
case EXTRA_DRAW:
|
||||||
|
return EXTRA_DRAW;
|
||||||
|
case END_ACTION:
|
||||||
|
if(turn != 10) return GAME_OVER;
|
||||||
|
return TOTEM_PLACEMENT;
|
||||||
|
default: return UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GameState fromString(String s){
|
||||||
|
return switch (s.toUpperCase().charAt(0)) {
|
||||||
|
case 'S' -> SETUP;
|
||||||
|
case 'T' -> TOTEM_PLACEMENT;
|
||||||
|
case 'A' -> ACTION_RESOLUTION;
|
||||||
|
case 'E' -> END_ACTION;
|
||||||
|
case 'X' -> EXTRA_DRAW;
|
||||||
|
case 'G' -> GAME_OVER;
|
||||||
|
case 'N' -> NEXT_ROUND;
|
||||||
|
default -> UNKNOWN;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
switch (this) {
|
||||||
|
case SETUP:
|
||||||
|
return "SETUP";
|
||||||
|
case TOTEM_PLACEMENT:
|
||||||
|
return "TOTEM_PLACEMENT";
|
||||||
|
case ACTION_RESOLUTION:
|
||||||
|
return "ACTION_RESOLUTION";
|
||||||
|
case END_ACTION:
|
||||||
|
return "END_ACTION";
|
||||||
|
case EVENT_RESOLUTION:
|
||||||
|
return "EVENT_RESOLUTION";
|
||||||
|
case NEXT_ROUND:
|
||||||
|
return "NEXT_ROUND";
|
||||||
|
case EXTRA_DRAW:
|
||||||
|
return "EXTRA_DRAW";
|
||||||
|
case GAME_OVER:
|
||||||
|
return "GAME_OVER";
|
||||||
|
default: return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
214
src/main/java/Server/BuildingManager.java
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import Server.Cards.BuildingCard;
|
||||||
|
import Server.Cards.CharacterCard;
|
||||||
|
import Server.Cards.CharacterType;
|
||||||
|
import Server.Cards.Trigger;
|
||||||
|
import Server.Utils.EventsManagerException;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BuildingManager {
|
||||||
|
private static List<BuildingCard> activeBuildings;
|
||||||
|
|
||||||
|
public BuildingManager() {
|
||||||
|
activeBuildings = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addActiveBuilding (BuildingCard buildingCard, Player player) {
|
||||||
|
buildingCard.setOwner(player);
|
||||||
|
activeBuildings.add(buildingCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BuildingCard playerHasBuilding(Player player, Trigger trigger) {
|
||||||
|
|
||||||
|
for (BuildingCard buildingCard : activeBuildings) {
|
||||||
|
if(buildingCard.getAbilityTrigger() == trigger && buildingCard.getOwner() == player) {
|
||||||
|
return buildingCard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int numberOfTargets(Player player, List<BuildingCard> buildingCards) {
|
||||||
|
int numCard = 0;
|
||||||
|
for (BuildingCard buildingCard : buildingCards) {
|
||||||
|
|
||||||
|
List<CharacterCard> playerCharacters = player.getPlayerTribe().getCharacters();
|
||||||
|
|
||||||
|
for (CharacterCard card : playerCharacters) {
|
||||||
|
if (card.getCharacterType() == buildingCard.getEffectTarget()) {
|
||||||
|
numCard++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return numCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<BuildingCard> playerHasVariousBuildings(Player player, Trigger trigger) {
|
||||||
|
|
||||||
|
List<BuildingCard> buildingCards = new ArrayList<>();
|
||||||
|
|
||||||
|
for (BuildingCard buildingCard : activeBuildings) {
|
||||||
|
if(buildingCard.getAbilityTrigger() == trigger && buildingCard.getOwner() == player) {
|
||||||
|
buildingCards.add(buildingCard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buildingCards;
|
||||||
|
}
|
||||||
|
|
||||||
|
//When a player draw a characterCard, if said card complete a set of 6
|
||||||
|
//different character type, give the player 6 food
|
||||||
|
|
||||||
|
public static void foodForSix(Player player, CharacterCard characterCard) {
|
||||||
|
if(playerHasBuilding(player, Trigger.FOOD_FOR_SIX) == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CharacterCard> playerTribe = player.getPlayerTribe().getCharacters();
|
||||||
|
int [] preDraw = new int[CharacterType.values().length];
|
||||||
|
int [] postDraw = new int[CharacterType.values().length];
|
||||||
|
|
||||||
|
for(CharacterCard card: playerTribe){
|
||||||
|
preDraw[card.getCharacterType().ordinal()]++;
|
||||||
|
postDraw[card.getCharacterType().ordinal()]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
postDraw[characterCard.getCharacterType().ordinal()]++;
|
||||||
|
|
||||||
|
int minPre = Arrays.stream(preDraw).min().getAsInt();
|
||||||
|
int minPost = Arrays.stream(postDraw).min().getAsInt();
|
||||||
|
|
||||||
|
if(minPre < minPost){
|
||||||
|
player.addFood(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//the method return the discount received for the player based on the cards he owns
|
||||||
|
//the card providing the discount are indicated on the BuildingCard
|
||||||
|
public static int sustainDiscount(Player player) {
|
||||||
|
List<BuildingCard> buildingCards = playerHasVariousBuildings(player, Trigger.SUSTAIN_DISCOUNT);
|
||||||
|
if(buildingCards.isEmpty()){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return numberOfTargets(player, buildingCards);
|
||||||
|
}
|
||||||
|
|
||||||
|
//if the player is going to lose during the shamanicRitual, the loss are nullif
|
||||||
|
public static boolean shamanNoLoss(Player player) {
|
||||||
|
return playerHasBuilding(player, Trigger.SHAMAN_NO_LOSS) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if the player has a SHAMAN_DOUBLE_POINTS card
|
||||||
|
public static boolean shamanDoublePoints(Player player) {
|
||||||
|
return playerHasBuilding(player, Trigger.SHAMAN_DOUBLE_POINTS) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if the player has a BONUS_END_TURN buildingCard the player gain additional food
|
||||||
|
//after placing his totem on a tile whit food
|
||||||
|
public static int bonusEndTurn(Player player, int positionReward) {
|
||||||
|
if (playerHasBuilding(player, Trigger.BONUS_FOOD_ENDTURN) == null) return -1;
|
||||||
|
|
||||||
|
if (positionReward >= 1){
|
||||||
|
player.addFood(1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if the player has a BuildingCard whit FOOD_PER_INVENTORS, and he
|
||||||
|
//draws a copy of an inventor that is already in his tribe he gains 3 food
|
||||||
|
public static void foodPerInventors(Player player, CharacterCard characterCard) {
|
||||||
|
if(playerHasBuilding(player, Trigger.FOOD_PER_INVENTORS) == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(characterCard.getCharacterType()!= CharacterType.INVENTOR){
|
||||||
|
throw new EventsManagerException("Invalid character type");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CharacterCard> playerTribe = player.getPlayerTribe().getCharacters();
|
||||||
|
int numberOfInventors = 0;
|
||||||
|
|
||||||
|
for(CharacterCard card: playerTribe){
|
||||||
|
if(card.getIconValue() == characterCard.getIconValue() && card.getCharacterType() == CharacterType.INVENTOR){
|
||||||
|
numberOfInventors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(numberOfInventors%2 == 1)
|
||||||
|
player.addFood(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
//if the player has a SHAMAN_BONUS building the function return 3, else return 0
|
||||||
|
public static int shamanBonus(Player player) {
|
||||||
|
if (BuildingManager.playerHasBuilding(player, Trigger.SHAMAN_BONUS) == null) return 0;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
//return if HUNT_BONUS is active for the player
|
||||||
|
public static boolean hunterBonus(Player player) {
|
||||||
|
return playerHasBuilding(player, Trigger.HUNT_BONUS) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//return if ENDGAME_BUILDER_BONUS is active for the player
|
||||||
|
public static boolean endgameBuilderBonus(Player player) {
|
||||||
|
return playerHasBuilding(player, Trigger.ENDGAME_BUILDER_BONUS) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//return the amount of food gained if the player has a BuildingCard with
|
||||||
|
//PAINTING_FOOD_BONUS, a unit for each ARTIST in his tribe
|
||||||
|
public static void paintingFoodBonus(Player player) {
|
||||||
|
if(playerHasBuilding(player, Trigger.PAINTING_FOOD_BONUS) == null) return;
|
||||||
|
|
||||||
|
player.addFood(player.getPlayerTribe().artistsNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
//return the amount of buildingPoint if the player has a BuildingCard with
|
||||||
|
//ENDGAME_FOR_SIX, a unit for each set of 6 character of different Type
|
||||||
|
public static void endgameForSix(Player player) {
|
||||||
|
if(playerHasBuilding(player, Trigger.ENDGAME_FOR_SIX) == null) return;
|
||||||
|
|
||||||
|
List<CharacterCard> playerTribe = player.getPlayerTribe().getCharacters();
|
||||||
|
|
||||||
|
int [] typeDuplicates = new int[CharacterType.values().length];
|
||||||
|
|
||||||
|
for(CharacterCard card: playerTribe){
|
||||||
|
typeDuplicates[card.getCharacterType().ordinal()]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int minCharacterType = Arrays.stream(typeDuplicates).min().getAsInt();
|
||||||
|
|
||||||
|
player.addPrestigePoints(minCharacterType*6);
|
||||||
|
}
|
||||||
|
|
||||||
|
//add the bonus prestigePoints due to ENDGAME_BONUS_CHARACTER, the amount is based
|
||||||
|
//on the CharacterType indicated on the BuildingCards, 3 for each character of the same type
|
||||||
|
//A plater can own different BuildingCards with the same effect
|
||||||
|
public static void endgameBonusCharacter(Player player) {
|
||||||
|
List<BuildingCard> buildingCards = playerHasVariousBuildings(player, Trigger.ENDGAME_BONUS_CHARACTER);
|
||||||
|
if(buildingCards.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.addPrestigePoints(numberOfTargets(player, buildingCards)* 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
//return if EXTRA_DRAW is active for the player
|
||||||
|
public static boolean extraDraw(Player player) {
|
||||||
|
return playerHasBuilding(player, Trigger.EXTRA_DRAW) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//return if the player has a BuildingCard with
|
||||||
|
//ENDGAME_BONUS_POINTS, then add 25 points if he has
|
||||||
|
public static void endgameBonusPoints(Player player) {
|
||||||
|
if(playerHasBuilding(player, Trigger.ENDGAME_BONUS_POINTS) == null) return;
|
||||||
|
|
||||||
|
player.addPrestigePoints(25);
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/main/java/Server/BuildingRules.java
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class BuildingRules {
|
||||||
|
private static final Map<Integer, Map<Era, Integer>> RULES = new HashMap<>();
|
||||||
|
static {
|
||||||
|
// 2 players
|
||||||
|
Map<Era, Integer> p2 = new EnumMap<>(Era.class);
|
||||||
|
p2.put(Era.I, 1);
|
||||||
|
p2.put(Era.II, 2);
|
||||||
|
p2.put(Era.III, 3);
|
||||||
|
|
||||||
|
// 3 players
|
||||||
|
Map<Era, Integer> p3 = new EnumMap<>(Era.class);
|
||||||
|
p3.put(Era.I, 2);
|
||||||
|
p3.put(Era.II, 2);
|
||||||
|
p3.put(Era.III, 4);
|
||||||
|
|
||||||
|
// 4 players
|
||||||
|
Map<Era, Integer> p4 = new EnumMap<>(Era.class);
|
||||||
|
p4.put(Era.I, 2);
|
||||||
|
p4.put(Era.II, 3);
|
||||||
|
p4.put(Era.III, 4);
|
||||||
|
|
||||||
|
// 5 players
|
||||||
|
Map<Era, Integer> p5 = new EnumMap<>(Era.class);
|
||||||
|
p4.put(Era.I, 2);
|
||||||
|
p4.put(Era.II, 3);
|
||||||
|
p4.put(Era.III, 5);
|
||||||
|
|
||||||
|
// insert into main map
|
||||||
|
RULES.put(2, p2);
|
||||||
|
RULES.put(3, p3);
|
||||||
|
RULES.put(4, p4);
|
||||||
|
RULES.put(5, p5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getBuildingCards(int players, Era era) {
|
||||||
|
Map<Era, Integer> eraMap = RULES.get(players);
|
||||||
|
if (eraMap!=null){
|
||||||
|
Integer value = eraMap.get(era);
|
||||||
|
if (value!=null) return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
84
src/main/java/Server/Cards/BuildingCard.java
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
package Server.Cards;
|
||||||
|
|
||||||
|
import Server.Era;
|
||||||
|
import Server.Player;
|
||||||
|
|
||||||
|
public class BuildingCard extends Card{
|
||||||
|
private final int cost;
|
||||||
|
private final int endPP;
|
||||||
|
private final Trigger abilityTrigger;
|
||||||
|
private final CharacterType effectTarget;
|
||||||
|
private Player owner;
|
||||||
|
|
||||||
|
public BuildingCard(int cardId, int forMinPlayer, Era era, int cost, int endPP, Trigger abilityTrigger,CharacterType characterType, Player owner) {
|
||||||
|
super(cardId, forMinPlayer, era);
|
||||||
|
this.cost = cost;
|
||||||
|
this.endPP = endPP;
|
||||||
|
this.abilityTrigger = abilityTrigger;
|
||||||
|
this.effectTarget = characterType;
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BuildingCard(int cardId, int forMinPlayer, Era era, int cost, int endPP, Trigger abilityTrigger, CharacterType characterType) {
|
||||||
|
super(cardId, forMinPlayer, era);
|
||||||
|
this.cost = cost;
|
||||||
|
this.endPP = endPP;
|
||||||
|
this.abilityTrigger = abilityTrigger;
|
||||||
|
this.effectTarget = characterType;
|
||||||
|
this.owner = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BuildingCard(int cardId, int forMinPlayer, Era era, int cost, int endPP, Trigger abilityTrigger) {
|
||||||
|
super(cardId, forMinPlayer, era);
|
||||||
|
this.cost = cost;
|
||||||
|
this.endPP = endPP;
|
||||||
|
this.abilityTrigger = abilityTrigger;
|
||||||
|
this.effectTarget = null;
|
||||||
|
this.owner = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOwner(Player owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCost() {
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEndPP() {
|
||||||
|
return endPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Trigger getAbilityTrigger() {
|
||||||
|
return abilityTrigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player getOwner() {
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharacterType getEffectTarget() {
|
||||||
|
return effectTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BuildingCard parsRow(String row) {
|
||||||
|
String cleanRow = row.trim();
|
||||||
|
String[] values = cleanRow.split(";");
|
||||||
|
|
||||||
|
int cardId = Integer.parseInt(values[1]);
|
||||||
|
int forMinPlayer = Integer.parseInt(values[2]);
|
||||||
|
Era era = Era.valueOf(values[3]);
|
||||||
|
int cost = Integer.parseInt(values[4]);
|
||||||
|
int endPP = Integer.parseInt(values[5]);
|
||||||
|
Trigger abilityTrigger = Trigger.valueOf(values[6]);
|
||||||
|
/* MANCA LA COLONNA 8 nel CSV
|
||||||
|
if(abilityTrigger == Trigger.SUSTAIN_DISCOUNT || abilityTrigger == Trigger.ENDGAME_BONUS_CHARACTER){
|
||||||
|
CharacterType effectTarget = CharacterType.valueOf(values[7]);
|
||||||
|
return new BuildingCard(cardId, forMinPlayer, era, cost, endPP, abilityTrigger, effectTarget);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
return new BuildingCard(cardId, forMinPlayer, era, cost, endPP, abilityTrigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/main/java/Server/Cards/Card.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package Server.Cards;
|
||||||
|
|
||||||
|
import Server.Era;
|
||||||
|
|
||||||
|
public abstract class Card {
|
||||||
|
private final int cardId;
|
||||||
|
private final int forMinPlayer;
|
||||||
|
private final Era era;
|
||||||
|
|
||||||
|
public Card(int cardId, int forMinPlayer, Era era) {
|
||||||
|
this.cardId = cardId;
|
||||||
|
this.forMinPlayer = forMinPlayer;
|
||||||
|
this.era = era;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCardId() {
|
||||||
|
return cardId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Era getEra() {
|
||||||
|
return era;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getForMinPlayer() {
|
||||||
|
return forMinPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Card{" +
|
||||||
|
"cardId=" + cardId +
|
||||||
|
", era=" + era +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
131
src/main/java/Server/Cards/CardDeck.java
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
package Server.Cards;
|
||||||
|
|
||||||
|
import Server.Automaton.Game;
|
||||||
|
import Server.Era;
|
||||||
|
import Server.Utils.LoadingCardsException;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class CardDeck {
|
||||||
|
private List<Card> tribeDeck;
|
||||||
|
private Map<Era, List<Card>> buildingDeck;
|
||||||
|
private static final Logger logger = LogManager.getLogger(CardDeck.class);
|
||||||
|
|
||||||
|
public List<Card> getTribeDeck() {
|
||||||
|
return tribeDeck;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Card> getBuildingDeck(Era era) {
|
||||||
|
return buildingDeck.get(era);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setForNPlayer(String path, int n) throws LoadingCardsException {
|
||||||
|
|
||||||
|
List<Card> tribe = new ArrayList<>();
|
||||||
|
List<Card> building = new ArrayList<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<String> rows = Files.readAllLines(Path.of(path));
|
||||||
|
int p=0;
|
||||||
|
for (String row : rows) {
|
||||||
|
String cleanRow = row.trim();
|
||||||
|
String[] fields = cleanRow.split(";");
|
||||||
|
logger.info((p++) + " ROW " +row);
|
||||||
|
switch (fields[0]) {
|
||||||
|
case "C":
|
||||||
|
tribe.add(CharacterCard.parsRow(row));
|
||||||
|
break;
|
||||||
|
case "E":
|
||||||
|
tribe.add(EventCard.parsRow(row));
|
||||||
|
break;
|
||||||
|
case "B":
|
||||||
|
building.add(BuildingCard.parsRow(row));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new LoadingCardsException("Content not supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error(e);
|
||||||
|
throw new LoadingCardsException("file not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
building = CardDeck.shuffle(building);
|
||||||
|
|
||||||
|
this.tribeDeck = new ArrayList<>();
|
||||||
|
|
||||||
|
for(Card card : tribe) {
|
||||||
|
if (card.getForMinPlayer() <= n)
|
||||||
|
this.tribeDeck.add(card);
|
||||||
|
}
|
||||||
|
this.tribeDeck = CardDeck.shuffle(this.tribeDeck);
|
||||||
|
|
||||||
|
// groups the building cards by era
|
||||||
|
this.buildingDeck = building.stream().collect(Collectors.groupingBy(Card::getEra));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Card> drawTribe(int n) {
|
||||||
|
List<Card> cards = new ArrayList<>();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if(tribeDeck.isEmpty()) break;
|
||||||
|
cards.add(tribeDeck.getFirst());
|
||||||
|
tribeDeck.remove(tribeDeck.getFirst());
|
||||||
|
}
|
||||||
|
return cards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Card> drawBuilding(int n, Era era) {
|
||||||
|
List<Card> cards = new ArrayList<>();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if(buildingDeck.isEmpty()) break;
|
||||||
|
Card card = buildingDeck.get(era).getFirst();
|
||||||
|
cards.add(card);
|
||||||
|
buildingDeck.remove(card);
|
||||||
|
}
|
||||||
|
return cards;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static List<Card> shuffle(List<Card> cards) {
|
||||||
|
List<Card> shuffled = new ArrayList<>();
|
||||||
|
|
||||||
|
for(Era e: Era.values()) {
|
||||||
|
List<Card> cardsToShuffle = new ArrayList<>();
|
||||||
|
for(Card card : cards) {
|
||||||
|
if(card.getEra() == e){
|
||||||
|
cardsToShuffle.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.shuffle(cardsToShuffle);
|
||||||
|
shuffled.addAll(cardsToShuffle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shuffled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Card getFirstCardForCover() {
|
||||||
|
if(tribeDeck.isEmpty()) return null;
|
||||||
|
Card card = tribeDeck.getFirst();
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Card drawTribeOne() {
|
||||||
|
if(tribeDeck.isEmpty()) return null;
|
||||||
|
Card card = tribeDeck.getFirst();
|
||||||
|
tribeDeck.remove(card);
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Card drawBuildingOne(Era era) {
|
||||||
|
if(buildingDeck.isEmpty()) return null;
|
||||||
|
Card card = buildingDeck.get(era).getFirst();
|
||||||
|
buildingDeck.get(era).remove(card);
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/main/java/Server/Cards/CharacterCard.java
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package Server.Cards;
|
||||||
|
|
||||||
|
import Server.Era;
|
||||||
|
import Server.Utils.LoadingCardsException;
|
||||||
|
|
||||||
|
public class CharacterCard extends Card{
|
||||||
|
private final CharacterType characterType;
|
||||||
|
private final int iconValue;
|
||||||
|
private final int prestigePoints;
|
||||||
|
|
||||||
|
public CharacterCard(int cardId, int forMinPlayer, Era era, CharacterType characterType, int iconValue, int prestigePoints) {
|
||||||
|
super(cardId, forMinPlayer, era);
|
||||||
|
this.characterType = characterType;
|
||||||
|
this.iconValue = iconValue;
|
||||||
|
this.prestigePoints = prestigePoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharacterType getCharacterType() {
|
||||||
|
return characterType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIconValue() {
|
||||||
|
return iconValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPrestigePoints() {
|
||||||
|
return prestigePoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CharacterCard parsRow(String row){
|
||||||
|
|
||||||
|
String cleanRow = row.trim();
|
||||||
|
String[] values = cleanRow.split(";");
|
||||||
|
|
||||||
|
if(!values[0].equals("C")){
|
||||||
|
throw new LoadingCardsException("Not a character card");
|
||||||
|
}
|
||||||
|
|
||||||
|
int cardId = Integer.parseInt(values[1]);
|
||||||
|
int forMinPlayer = Integer.parseInt(values[2]);
|
||||||
|
Era era = Era.valueOf(values[3]);
|
||||||
|
CharacterType characterType = CharacterType.valueOf(values[4]);
|
||||||
|
int iconValue = Integer.parseInt(values[5]);
|
||||||
|
int prestigePoints = Integer.parseInt(values[6]);
|
||||||
|
|
||||||
|
return new CharacterCard(cardId, forMinPlayer, era, characterType, iconValue, prestigePoints);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "CharacterCard{" +
|
||||||
|
"characterType=" + characterType +
|
||||||
|
", value=" + iconValue +
|
||||||
|
", points=" + prestigePoints +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/main/java/Server/Cards/CharacterType.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package Server.Cards;
|
||||||
|
|
||||||
|
public enum CharacterType {
|
||||||
|
INVENTOR,
|
||||||
|
HUNTER,
|
||||||
|
GATHERER,
|
||||||
|
SHAMAN,
|
||||||
|
ARTIST,
|
||||||
|
BUILDER
|
||||||
|
}
|
||||||
8
src/main/java/Server/Cards/Event.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package Server.Cards;
|
||||||
|
|
||||||
|
public enum Event {
|
||||||
|
SUSTAINMENT,
|
||||||
|
HUNT,
|
||||||
|
SHAMANIC_RITUAL,
|
||||||
|
CAVE_PAINTINGS
|
||||||
|
}
|
||||||
55
src/main/java/Server/Cards/EventCard.java
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package Server.Cards;
|
||||||
|
|
||||||
|
import Server.Era;
|
||||||
|
import Server.Utils.LoadingCardsException;
|
||||||
|
|
||||||
|
public class EventCard extends Card {
|
||||||
|
private final Event event;
|
||||||
|
private final int firstValue;
|
||||||
|
private final int secondValue;
|
||||||
|
|
||||||
|
public EventCard(int cardId, int forMinPlayer, Era era, Event event, int firstValue, int secondValue) {
|
||||||
|
super(cardId, forMinPlayer, era);
|
||||||
|
this.event = event;
|
||||||
|
this.firstValue = firstValue;
|
||||||
|
this.secondValue = secondValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Event getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
public int getFirstValue() {
|
||||||
|
return firstValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSecondValue() {
|
||||||
|
return secondValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EventCard parsRow(String row){
|
||||||
|
String cleanRow = row.trim();
|
||||||
|
String[] values = cleanRow.split(";");
|
||||||
|
|
||||||
|
if (!values[0].equals("E")) {
|
||||||
|
throw new LoadingCardsException("Not an EventCard");
|
||||||
|
}
|
||||||
|
|
||||||
|
int cardId = Integer.parseInt(values[1]);
|
||||||
|
int forMinPlayer = Integer.parseInt(values[2]);
|
||||||
|
Era era = Era.valueOf(values[3]);
|
||||||
|
Event event = Event.valueOf(values[4]);
|
||||||
|
int firstValue = Integer.parseInt(values[5]);
|
||||||
|
int secondValue = Integer.parseInt(values[6]);
|
||||||
|
|
||||||
|
return new EventCard(cardId, forMinPlayer, era, event, firstValue, secondValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "EventCard{" +
|
||||||
|
"event=" + event +
|
||||||
|
", firstValue=" + firstValue +
|
||||||
|
", secondValue=" + secondValue +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/main/java/Server/Cards/Trigger.java
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package Server.Cards;
|
||||||
|
|
||||||
|
public enum Trigger {
|
||||||
|
FOOD_FOR_SIX,
|
||||||
|
SUSTAIN_DISCOUNT,
|
||||||
|
SHAMAN_NO_LOSS,
|
||||||
|
BONUS_FOOD_ENDTURN,
|
||||||
|
FOOD_PER_INVENTORS,
|
||||||
|
SHAMAN_BONUS,
|
||||||
|
SHAMAN_DOUBLE_POINTS,
|
||||||
|
HUNT_BONUS,
|
||||||
|
ENDGAME_BUILDER_BONUS,
|
||||||
|
PAINTING_FOOD_BONUS,
|
||||||
|
ENDGAME_FOR_SIX,
|
||||||
|
ENDGAME_BONUS_CHARACTER,
|
||||||
|
EXTRA_DRAW,
|
||||||
|
ENDGAME_BONUS_POINTS
|
||||||
|
}
|
||||||
684
src/main/java/Server/DeckGridAppFX.java
Normal file
@@ -0,0 +1,684 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import Server.Automaton.ActionResult;
|
||||||
|
import Server.Automaton.Game;
|
||||||
|
import Server.Cards.*;
|
||||||
|
import Server.Utils.GameException;
|
||||||
|
import javafx.animation.*;
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.effect.DropShadow;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.*;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.shape.Rectangle;
|
||||||
|
import javafx.scene.text.Font;
|
||||||
|
import javafx.scene.text.FontWeight;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class DeckGridAppFX extends Application {
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger(DeckGridAppFX.class);
|
||||||
|
private static final int IMG_HEIGHT=200;
|
||||||
|
private PDDocument documentFront;
|
||||||
|
private PDDocument documentBack;
|
||||||
|
private PDFRenderer pdfRendererFront;
|
||||||
|
private PDFRenderer pdfRendererBack;
|
||||||
|
private int totalCards = 0;
|
||||||
|
|
||||||
|
// Contenitori Layout
|
||||||
|
private HBox topMenu;
|
||||||
|
private HBox topRow;
|
||||||
|
private HBox centerRow;
|
||||||
|
private HBox bottomRow;
|
||||||
|
private HBox playersArea; // NUOVO: Area per i giocatori
|
||||||
|
private Button btnRefresh;
|
||||||
|
private Button btnTop;
|
||||||
|
private Button btnBottom;
|
||||||
|
private Button btnChooseOffering;
|
||||||
|
|
||||||
|
private Button btnMsg;
|
||||||
|
// --------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Stage primaryStage) {
|
||||||
|
|
||||||
|
List<Player> players = new ArrayList<>();
|
||||||
|
//players.add(new Player("Yellow", TotemColor.YELLOW));
|
||||||
|
players.add(new Player("Blue", TotemColor.BLUE));
|
||||||
|
//players.add(new Player("Purple", TotemColor.PURPLE));
|
||||||
|
players.add(new Player("Red", TotemColor.RED));
|
||||||
|
|
||||||
|
//players.add(new Player("Green", TotemColor.GREEN));
|
||||||
|
|
||||||
|
Game game = new Game(players);
|
||||||
|
String fileCards="/home/lorenzo/dev/Mesos2/src/main/resources/files/cards.csv";
|
||||||
|
String fileCardsImgFront="/home/lorenzo/dev/Mesos2/src/main/resources/files/Cards_total_front_PROMO.pdf";
|
||||||
|
String fileCardsImgCover="/home/lorenzo/dev/Mesos2/src/main/resources/files/Cards_total_back_PROMO.pdf";
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
File fileFront = new File(fileCardsImgFront);
|
||||||
|
File fileBack = new File(fileCardsImgCover);
|
||||||
|
|
||||||
|
documentFront = PDDocument.load(fileFront);
|
||||||
|
pdfRendererFront = new PDFRenderer(documentFront);
|
||||||
|
documentBack = PDDocument.load(fileBack);
|
||||||
|
pdfRendererBack = new PDFRenderer(documentBack);
|
||||||
|
|
||||||
|
} catch ( IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
game.newGame(fileCards);
|
||||||
|
|
||||||
|
game.setEventListener(notification -> {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
drawGameState(game);
|
||||||
|
switch (notification.event()) {
|
||||||
|
case TOTEM_PLACEMENT-> showPopup("TOTEM PLACEMENT\n" + (notification.message()!=null?notification.message():""));
|
||||||
|
case SETUP -> showPopup("SETUP \n" + (notification.message()!=null?notification.message():""));
|
||||||
|
case ACTION_RESOLUTION -> showPopup("ACTION_RESOLUTION \n" + (notification.message()!=null?notification.message():"" ));
|
||||||
|
case END_ACTION -> showPopup("END_ACTION \n" + (notification.message()!=null?notification.message():"" ));
|
||||||
|
case EVENT_RESOLUTION -> showPopup("EVENT_RESOLUTION \n" + (notification.message()!=null?notification.message():"" ));
|
||||||
|
case EXTRA_DRAW -> showPopup("EXTRA_DRAW \n" + (notification.message()!=null?notification.message():"" ));
|
||||||
|
case GAME_OVER -> showPopup("GAME OVER");
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
primaryStage.setTitle("Tavolo da Gioco - Board & Players");
|
||||||
|
|
||||||
|
btnTop = new Button("Pick Top");
|
||||||
|
btnBottom = new Button("Pick Bottom");
|
||||||
|
btnMsg = new Button("Game State");
|
||||||
|
btnRefresh= new Button("Refresh");
|
||||||
|
btnChooseOffering= new Button("Choose Offering");
|
||||||
|
//btnNext.setDisable(true);
|
||||||
|
topMenu = new HBox(15, btnRefresh, btnMsg);
|
||||||
|
topMenu.setAlignment(Pos.CENTER);
|
||||||
|
topMenu.setPadding(new Insets(10));
|
||||||
|
|
||||||
|
Label stateLabel = new Label("Game State = " + game.getState() );
|
||||||
|
stateLabel.setFont(Font.font("System", FontWeight.BOLD, 16));
|
||||||
|
stateLabel.setTextFill(Color.DARKBLUE);
|
||||||
|
|
||||||
|
//showPopup(topMenu, "STARTING GAME");
|
||||||
|
|
||||||
|
btnMsg.setOnAction(e -> {
|
||||||
|
new Alert(Alert.AlertType.INFORMATION,
|
||||||
|
game.toString()
|
||||||
|
).showAndWait();
|
||||||
|
});
|
||||||
|
btnRefresh.setOnAction(e -> {
|
||||||
|
drawGameState(game);
|
||||||
|
});
|
||||||
|
|
||||||
|
topRow = createRowContainer();
|
||||||
|
centerRow = createRowContainer();
|
||||||
|
bottomRow = createRowContainer();
|
||||||
|
|
||||||
|
// NUOVO: Inizializza l'area giocatori
|
||||||
|
playersArea = new HBox(30);
|
||||||
|
playersArea.setAlignment(Pos.CENTER);
|
||||||
|
playersArea.setPadding(new Insets(20));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
VBox tableArea = new VBox(20,
|
||||||
|
new Label(" "), topRow,
|
||||||
|
new Label(" "), centerRow,
|
||||||
|
new Label(" "), bottomRow,
|
||||||
|
new Label("--- ASSET PLAYERS ---"), playersArea
|
||||||
|
);
|
||||||
|
tableArea.setAlignment(Pos.CENTER);
|
||||||
|
tableArea.setPadding(new Insets(20));
|
||||||
|
|
||||||
|
ScrollPane scrollPane = new ScrollPane(tableArea);
|
||||||
|
scrollPane.setFitToWidth(true);
|
||||||
|
|
||||||
|
BorderPane root = new BorderPane();
|
||||||
|
root.setTop(topMenu);
|
||||||
|
root.setCenter(scrollPane);
|
||||||
|
|
||||||
|
Scene scene = new Scene(root, 1300, 900);
|
||||||
|
primaryStage.setScene(scene);
|
||||||
|
primaryStage.show();
|
||||||
|
|
||||||
|
drawGameState(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HBox createRowContainer() {
|
||||||
|
HBox row = new HBox(10);
|
||||||
|
row.setAlignment(Pos.CENTER);
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void drawGameState(Game game) {
|
||||||
|
if (documentFront == null) return;
|
||||||
|
|
||||||
|
|
||||||
|
topMenu.getChildren().clear();
|
||||||
|
topRow.getChildren().clear();
|
||||||
|
centerRow.getChildren().clear();
|
||||||
|
bottomRow.getChildren().clear();
|
||||||
|
playersArea.getChildren().clear();
|
||||||
|
|
||||||
|
drawTopMenu(topMenu, game);
|
||||||
|
drawTopRow(topRow, game);
|
||||||
|
drawTurnTileAndOffering(centerRow, game.getPlayers().size(), game);
|
||||||
|
drawBottomRow(bottomRow, game );
|
||||||
|
|
||||||
|
|
||||||
|
List<Player> players =game.getPlayers();
|
||||||
|
renderPlayers(players, game);
|
||||||
|
logger.info(game.getCurrentPlayer());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pickTopCardAction(Player player, GameBoard board, int cardid){
|
||||||
|
Card card = board.getTopRow().stream()
|
||||||
|
.filter(CharacterCard.class::isInstance)
|
||||||
|
.map(CharacterCard.class::cast)
|
||||||
|
.filter(c -> c.getCardId() == cardid)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
if (card != null) {
|
||||||
|
board.getTopRow().remove(card);
|
||||||
|
CharacterCard charCard = (CharacterCard)card;
|
||||||
|
player.addCharacterToTribe((CharacterCard) card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pickBottomCardAction(Player player, GameBoard board, int cardid){
|
||||||
|
Card card = board.getBottomRow().stream()
|
||||||
|
.filter(CharacterCard.class::isInstance)
|
||||||
|
.map(CharacterCard.class::cast)
|
||||||
|
.filter(c -> c.getCardId() == cardid)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
if (card != null) {
|
||||||
|
board.getBottomRow().remove(card);
|
||||||
|
player.addCharacterToTribe((CharacterCard) card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void drawBottomRow(HBox row, Game game) {
|
||||||
|
for (Card c : game.getGameBoard().getBottomRow()) {
|
||||||
|
ImageView cardImage = createCardImageFromPdf(c.getCardId(), IMG_HEIGHT, true);
|
||||||
|
|
||||||
|
if (cardImage != null) {
|
||||||
|
// 1. Create a Label for your debugging text
|
||||||
|
Label debugLabel = new Label("ID: " + c.getCardId());
|
||||||
|
|
||||||
|
// 2. Style the label so it's readable over any card background
|
||||||
|
// (White text with a semi-transparent black background)
|
||||||
|
debugLabel.setStyle("-fx-background-color: rgba(0, 0, 0, 0.7); " +
|
||||||
|
"-fx-text-fill: white; " +
|
||||||
|
"-fx-padding: 3px; " +
|
||||||
|
"-fx-font-weight: bold;");
|
||||||
|
|
||||||
|
// 3. Create a StackPane and add both the image and the text
|
||||||
|
StackPane cardContainer = new StackPane();
|
||||||
|
cardContainer.getChildren().addAll(cardImage, debugLabel);
|
||||||
|
|
||||||
|
// 4. Align the text wherever you want (e.g., Top-Left corner of the card)
|
||||||
|
StackPane.setAlignment(debugLabel, Pos.TOP_LEFT);
|
||||||
|
// You can also use Pos.CENTER, Pos.BOTTOM_RIGHT, etc.
|
||||||
|
cardImage.setOnMouseClicked(event -> {
|
||||||
|
logger.info("Bottom Card clicked");
|
||||||
|
Player p = game.getCurrentPlayer();
|
||||||
|
//pickBottomCardAction(p, game.getGameBoard(), c.getCardId());
|
||||||
|
ActionResult result = game.resolveCardAction(p, false, c.getCardId());
|
||||||
|
if (!result.isSuccess()) {
|
||||||
|
new Alert(Alert.AlertType.ERROR,
|
||||||
|
result.getErrorMessage()
|
||||||
|
).showAndWait();
|
||||||
|
}
|
||||||
|
drawGameState(game);
|
||||||
|
});
|
||||||
|
// 5. Add the StackPane (which now holds both) to the row
|
||||||
|
row.getChildren().add(cardContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void drawTopMenu(HBox row, Game game) {
|
||||||
|
//topMenu = new HBox(15, btnChooseOffering, btnAction, btnTop, btnBottom, btnMsg);
|
||||||
|
//topMenu.setAlignment(Pos.CENTER);
|
||||||
|
//topMenu.setPadding(new Insets(10));
|
||||||
|
|
||||||
|
Label stateLabel = new Label("Game State = " + game.getState() );
|
||||||
|
stateLabel.setFont(Font.font("System", FontWeight.BOLD, 16));
|
||||||
|
stateLabel.setTextFill(Color.DARKBLUE);
|
||||||
|
row.getChildren().add(stateLabel);
|
||||||
|
row.getChildren().add(btnRefresh);
|
||||||
|
row.getChildren().add(btnMsg);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
private void drawTopRow(HBox row, Game game) {
|
||||||
|
for (Card c : game.getGameBoard().getTopRow()) {
|
||||||
|
ImageView cardImage = createCardImageFromPdf(c.getCardId(), IMG_HEIGHT, true);
|
||||||
|
|
||||||
|
if (cardImage != null) {
|
||||||
|
// 1. Create a Label for your debugging text
|
||||||
|
Label debugLabel = new Label("ID: " + c.getCardId());
|
||||||
|
|
||||||
|
// 2. Style the label so it's readable over any card background
|
||||||
|
// (White text with a semi-transparent black background)
|
||||||
|
debugLabel.setStyle("-fx-background-color: rgba(0, 0, 0, 0.7); " +
|
||||||
|
"-fx-text-fill: white; " +
|
||||||
|
"-fx-padding: 3px; " +
|
||||||
|
"-fx-font-weight: bold;");
|
||||||
|
|
||||||
|
// 3. Create a StackPane and add both the image and the text
|
||||||
|
StackPane cardContainer = new StackPane();
|
||||||
|
cardContainer.getChildren().addAll(cardImage, debugLabel);
|
||||||
|
|
||||||
|
// 4. Align the text wherever you want (e.g., Top-Left corner of the card)
|
||||||
|
StackPane.setAlignment(debugLabel, Pos.TOP_LEFT);
|
||||||
|
// You can also use Pos.CENTER, Pos.BOTTOM_RIGHT, etc.
|
||||||
|
cardImage.setOnMouseClicked(event -> {
|
||||||
|
logger.info("Card clicked");
|
||||||
|
Player p = game.getCurrentPlayer();
|
||||||
|
//pickTopCardAction(p, game.getGameBoard(), c.getCardId());
|
||||||
|
//game.resolveCardAction(p, true, c.getCardId());
|
||||||
|
|
||||||
|
ActionResult result = game.resolveCardAction(p, true, c.getCardId());
|
||||||
|
if (!result.isSuccess()) {
|
||||||
|
new Alert(Alert.AlertType.ERROR,
|
||||||
|
result.getErrorMessage()
|
||||||
|
).showAndWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawGameState(game);
|
||||||
|
});
|
||||||
|
// 5. Add the StackPane (which now holds both) to the row
|
||||||
|
row.getChildren().add(cardContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private StackPane drawTurnTileCardImageWithPlayers(String imagePath, int height, Player[] players) {
|
||||||
|
try {
|
||||||
|
Image fxImage = new Image("file:" + imagePath);
|
||||||
|
|
||||||
|
ImageView imageView = new ImageView(fxImage);
|
||||||
|
imageView.setFitHeight(height);
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
|
||||||
|
|
||||||
|
StackPane stack = new StackPane(imageView);
|
||||||
|
|
||||||
|
VBox playerBox = new VBox(5);
|
||||||
|
playerBox.setPadding(new Insets(5));
|
||||||
|
playerBox.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
|
// 🔥 DEBUG background (to SEE the box)
|
||||||
|
playerBox.setStyle("-fx-background-color: rgba(255,0,0,0.3);");
|
||||||
|
|
||||||
|
for (Player p : players) {
|
||||||
|
if (p==null) continue;
|
||||||
|
Rectangle rect = new Rectangle(15,15);
|
||||||
|
rect.setFill(p.getTotemColor().getFxColor());
|
||||||
|
rect.setStroke(Color.WHITE);
|
||||||
|
rect.setStrokeWidth(1);
|
||||||
|
|
||||||
|
playerBox.getChildren().add(rect);
|
||||||
|
}
|
||||||
|
playerBox.setPadding(new Insets(22));
|
||||||
|
// 🔥 IMPORTANT: bring to front
|
||||||
|
playerBox.toFront();
|
||||||
|
|
||||||
|
// position it clearly
|
||||||
|
StackPane.setAlignment(playerBox, Pos.CENTER);
|
||||||
|
|
||||||
|
stack.getChildren().add(playerBox);
|
||||||
|
|
||||||
|
return stack;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Errore caricamento immagine {}", imagePath, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void drawTurnTileAndOffering(HBox row, int n, Game game) {
|
||||||
|
|
||||||
|
// Clear row if needed (optional but recommended)
|
||||||
|
row.getChildren().clear();
|
||||||
|
|
||||||
|
// --- LEFT BLOCK (Turn + initial card) ---
|
||||||
|
VBox turnBox = new VBox(5);
|
||||||
|
turnBox.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
// Turn label
|
||||||
|
int round = game.getRound(); // adjust if different
|
||||||
|
Label turnLabel = new Label("Round = " + round);
|
||||||
|
turnLabel.setFont(Font.font("System", FontWeight.BOLD, 16));
|
||||||
|
turnLabel.setTextFill(Color.DARKBLUE);
|
||||||
|
|
||||||
|
Card first = game.getGameBoard().getCardDeck().getFirstCardForCover();
|
||||||
|
if (first!=null){
|
||||||
|
ImageView imgCover = createCardImageFromPdf(first.getCardId(), 120, false);
|
||||||
|
turnBox.getChildren().addAll(turnLabel, imgCover);
|
||||||
|
} else turnBox.getChildren().addAll(turnLabel);
|
||||||
|
|
||||||
|
// Add FIRST
|
||||||
|
row.getChildren().add(turnBox);
|
||||||
|
|
||||||
|
// --- TURN TILE ---
|
||||||
|
StackPane pane = drawTurnTileCardImageWithPlayers(
|
||||||
|
"/home/lorenzo/dev/Mesos2/src/main/resources/files/Start_" + n + "P.png",
|
||||||
|
IMG_HEIGHT,
|
||||||
|
game.getGameBoard().getTurnTile().getPositions()
|
||||||
|
);
|
||||||
|
|
||||||
|
row.getChildren().add(pane);
|
||||||
|
|
||||||
|
// --- OFFERINGS ---
|
||||||
|
for (OfferingTile offering : game.getGameBoard().getOfferingTiles()) {
|
||||||
|
Player occupant = offering.getOccupant();
|
||||||
|
StackPane offPane =drawOfferingCardImageWithTotem(
|
||||||
|
"/home/lorenzo/dev/Mesos2/src/main/resources/files/offering" + offering.getLetter() + ".png",
|
||||||
|
IMG_HEIGHT,
|
||||||
|
occupant, game, offering);
|
||||||
|
|
||||||
|
if (offPane != null) {
|
||||||
|
row.getChildren().add(offPane);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- NUOVO: RENDERIZZAZIONE GIOCATORI E RAGGRUPPAMENTO (JAVA 8) ---
|
||||||
|
private void renderPlayers(List<Player> players, Game game) {
|
||||||
|
playersArea.getChildren().clear();
|
||||||
|
for (Player player : players) {
|
||||||
|
boolean active = player.equals(game.getCurrentPlayer());
|
||||||
|
|
||||||
|
VBox playerBox = new VBox(10);
|
||||||
|
playerBox.setPadding(new Insets(15));
|
||||||
|
playerBox.setStyle("-fx-border-color: #555; -fx-border-width: 2; -fx-border-radius: 10; -fx-background-color: #f9f9f9; -fx-background-radius: 10;");
|
||||||
|
|
||||||
|
if (active) {
|
||||||
|
// Strong highlight (gold border + soft background)
|
||||||
|
playerBox.setStyle(
|
||||||
|
"-fx-border-color: gold;" +
|
||||||
|
"-fx-border-width: 3;" +
|
||||||
|
"-fx-border-radius: 10;" +
|
||||||
|
"-fx-background-color: linear-gradient(to bottom, #fffbe6, #f9f9f9);" +
|
||||||
|
"-fx-background-radius: 10;"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Slight scale-up
|
||||||
|
playerBox.setScaleX(1.05);
|
||||||
|
playerBox.setScaleY(1.05);
|
||||||
|
|
||||||
|
// Glow effect
|
||||||
|
DropShadow glow = new DropShadow();
|
||||||
|
glow.setColor(Color.GOLD);
|
||||||
|
glow.setRadius(20);
|
||||||
|
playerBox.setEffect(glow);
|
||||||
|
|
||||||
|
// Smooth pulse animation (NOT blinking)
|
||||||
|
Timeline pulse = new Timeline(
|
||||||
|
new KeyFrame(Duration.ZERO,
|
||||||
|
new KeyValue(glow.radiusProperty(), 10)
|
||||||
|
),
|
||||||
|
new KeyFrame(Duration.seconds(1.2),
|
||||||
|
new KeyValue(glow.radiusProperty(), 25)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
pulse.setAutoReverse(true);
|
||||||
|
pulse.setCycleCount(Animation.INDEFINITE);
|
||||||
|
pulse.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
HBox nameRow = new HBox(8);
|
||||||
|
nameRow.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
Rectangle rect = new Rectangle(12, 12);
|
||||||
|
|
||||||
|
TotemColor color = player.getTotemColor();
|
||||||
|
rect.setFill(color.getFxColor());
|
||||||
|
|
||||||
|
|
||||||
|
rect.setStroke(Color.BLACK);
|
||||||
|
rect.setStrokeWidth(1);
|
||||||
|
|
||||||
|
Label nameLbl = new Label(player.getNickname());
|
||||||
|
nameLbl.setFont(Font.font("System", FontWeight.BOLD, 16));
|
||||||
|
|
||||||
|
if (active) {
|
||||||
|
Label turnLbl = new Label(" ▶ Your Turn");
|
||||||
|
turnLbl.setTextFill(Color.GOLDENROD);
|
||||||
|
turnLbl.setFont(Font.font("System", FontWeight.BOLD, 14));
|
||||||
|
nameRow.getChildren().addAll(rect, nameLbl, turnLbl);
|
||||||
|
} else {
|
||||||
|
nameRow.getChildren().addAll(rect, nameLbl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Risorse Cibo e Soldi
|
||||||
|
Label statsLbl = new Label("🍖 Food: " + player.getFoodTokens() + " | 💰 Points: " + player.getPrestigePoints());
|
||||||
|
statsLbl.setTextFill(Color.DARKRED);
|
||||||
|
statsLbl.setFont(Font.font("System", FontWeight.BOLD, 14));
|
||||||
|
|
||||||
|
playerBox.getChildren().addAll(nameRow, statsLbl);
|
||||||
|
|
||||||
|
Map<CharacterType, List<CharacterCard>> groupedCards = player.getPlayerTribe().getCharacters().stream()
|
||||||
|
.collect(Collectors.groupingBy(CharacterCard::getCharacterType));
|
||||||
|
|
||||||
|
// Itera sui gruppi creati e genera la grafica
|
||||||
|
groupedCards.forEach((type, cardsOfType) -> {
|
||||||
|
Label typeLbl = new Label("Tipo: " + type.name() + " (" + cardsOfType.size() + ")");
|
||||||
|
typeLbl.setFont(Font.font("System", FontWeight.NORMAL, 12));
|
||||||
|
|
||||||
|
HBox cardImagesRow = new HBox(5);
|
||||||
|
for (Card c : cardsOfType) {
|
||||||
|
// Carte più piccole (90px) per l'area giocatore
|
||||||
|
ImageView img = createCardImageFromPdf(c.getCardId(), 90, true);
|
||||||
|
if (img != null) cardImagesRow.getChildren().add(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
playerBox.getChildren().addAll(typeLbl, cardImagesRow);
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<Era, List<BuildingCard>> groupedBuildCards = player.getPlayerTribe().getBuildingCard().stream()
|
||||||
|
.collect(Collectors.groupingBy(BuildingCard::getEra));
|
||||||
|
|
||||||
|
groupedBuildCards.forEach((type, cardsOfType) -> {
|
||||||
|
Label typeLbl = new Label("Build Tipo: " + type.name() + " (" + cardsOfType.size() + ")");
|
||||||
|
typeLbl.setFont(Font.font("System", FontWeight.NORMAL, 12));
|
||||||
|
|
||||||
|
HBox cardImagesRow = new HBox(5);
|
||||||
|
for (Card c : cardsOfType) {
|
||||||
|
// Carte più piccole (90px) per l'area giocatore
|
||||||
|
ImageView img = createCardImageFromPdf(c.getCardId(), 90, true);
|
||||||
|
if (img != null) cardImagesRow.getChildren().add(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
playerBox.getChildren().addAll(typeLbl, cardImagesRow);
|
||||||
|
//playerBox.getChildren().addAll(nameRow, statsLbl);
|
||||||
|
});
|
||||||
|
playersArea.getChildren().add(playerBox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImageView createCardImageFromPdf(int pageIndex, int height, boolean front) {
|
||||||
|
try {
|
||||||
|
BufferedImage bim =null;
|
||||||
|
if (front) bim = pdfRendererFront.renderImageWithDPI(pageIndex-1, 100);
|
||||||
|
else bim = pdfRendererBack.renderImageWithDPI(pageIndex-1, 100);
|
||||||
|
|
||||||
|
Image fxImage = SwingFXUtils.toFXImage(bim, null);
|
||||||
|
ImageView imageView = new ImageView(fxImage);
|
||||||
|
imageView.setFitHeight(height);
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
|
||||||
|
return imageView;
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("Errore rendering pagina {}", pageIndex, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private StackPane drawOfferingCardImageWithTotem(String imagePath, int height, Player occupant, Game game, OfferingTile offering) {
|
||||||
|
try {
|
||||||
|
Image fxImage = new Image("file:" + imagePath);
|
||||||
|
|
||||||
|
ImageView imageView = new ImageView(fxImage);
|
||||||
|
imageView.setFitHeight(height);
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
|
||||||
|
|
||||||
|
imageView.setOnMouseClicked(event -> {
|
||||||
|
int idx = game.getGameBoard().getOfferingTiles().indexOf(offering);
|
||||||
|
game.placeTotem(game.getCurrentPlayer(), idx);
|
||||||
|
logger.info(" PLAYER {} choose {} ", game.getCurrentPlayer().getNickname() , offering );
|
||||||
|
drawGameState(game);
|
||||||
|
});
|
||||||
|
|
||||||
|
StackPane stack = new StackPane(imageView);
|
||||||
|
if(occupant!=null){
|
||||||
|
Rectangle rect = new Rectangle(25, 25);
|
||||||
|
rect.setFill(occupant.getTotemColor().getFxColor());
|
||||||
|
rect.setStroke(Color.WHITE);
|
||||||
|
rect.setStrokeWidth(1);
|
||||||
|
|
||||||
|
StackPane.setAlignment(rect, Pos.TOP_CENTER);
|
||||||
|
rect.setTranslateY(20);
|
||||||
|
StackPane.setMargin(rect, new Insets(5));
|
||||||
|
|
||||||
|
stack.getChildren().add(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stack;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Errore caricamento immagine {}", imagePath, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private ImageView createCardImage(String imagePath, int height) {
|
||||||
|
try {
|
||||||
|
Image fxImage = new Image("file:" + imagePath);
|
||||||
|
|
||||||
|
ImageView imageView = new ImageView(fxImage);
|
||||||
|
imageView.setFitHeight(height);
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
|
||||||
|
|
||||||
|
return imageView;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Errore caricamento immagine {}", imagePath, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void showPopup(String message) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
|
||||||
|
Label label = new Label("⚡ " + message); // icon improves readability
|
||||||
|
label.setWrapText(true);
|
||||||
|
label.setMaxWidth(400);
|
||||||
|
|
||||||
|
label.setStyle(
|
||||||
|
"-fx-background-color: rgba(20,20,20,0.9);" +
|
||||||
|
"-fx-text-fill: white;" +
|
||||||
|
"-fx-font-size: 20px;" +
|
||||||
|
"-fx-font-weight: bold;" +
|
||||||
|
"-fx-padding: 15 25 15 25;" +
|
||||||
|
"-fx-background-radius: 12;" +
|
||||||
|
"-fx-border-radius: 12;" +
|
||||||
|
"-fx-border-color: #00c3ff;"
|
||||||
|
);
|
||||||
|
|
||||||
|
label.setOpacity(0); // start invisible
|
||||||
|
|
||||||
|
topMenu.getChildren().add(label);
|
||||||
|
|
||||||
|
// Fade IN
|
||||||
|
FadeTransition fadeIn = new FadeTransition(Duration.millis(250), label);
|
||||||
|
fadeIn.setFromValue(0);
|
||||||
|
fadeIn.setToValue(1);
|
||||||
|
|
||||||
|
// Stay visible
|
||||||
|
PauseTransition pause = new PauseTransition(Duration.seconds(4));
|
||||||
|
|
||||||
|
// Fade OUT
|
||||||
|
FadeTransition fadeOut = new FadeTransition(Duration.millis(400), label);
|
||||||
|
fadeOut.setFromValue(1.0);
|
||||||
|
fadeOut.setToValue(0.0);
|
||||||
|
|
||||||
|
fadeOut.setOnFinished(f -> topMenu.getChildren().remove(label));
|
||||||
|
|
||||||
|
fadeIn.play();
|
||||||
|
fadeIn.setOnFinished(e -> pause.play());
|
||||||
|
pause.setOnFinished(e -> fadeOut.play());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
private void showPopupNo(String message) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
Label label = new Label(message);
|
||||||
|
label.setStyle(
|
||||||
|
"-fx-background-color: rgba(0,0,0,0.75);" +
|
||||||
|
"-fx-text-fill: white;" +
|
||||||
|
"-fx-font-size: 28px;" +
|
||||||
|
"-fx-font-weight: bold;" +
|
||||||
|
"-fx-padding: 20 40 20 40;" +
|
||||||
|
"-fx-background-radius: 12;"
|
||||||
|
);
|
||||||
|
label.setMouseTransparent(true);
|
||||||
|
|
||||||
|
topMenu.getChildren().add(label);
|
||||||
|
|
||||||
|
PauseTransition pause = new PauseTransition(Duration.seconds(5));
|
||||||
|
pause.setOnFinished(e -> {
|
||||||
|
FadeTransition fade = new FadeTransition(Duration.millis(400), label);
|
||||||
|
fade.setFromValue(1.0);
|
||||||
|
fade.setToValue(0.0);
|
||||||
|
fade.setOnFinished(f -> topMenu.getChildren().remove(label));
|
||||||
|
fade.play();
|
||||||
|
});
|
||||||
|
pause.play();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void stop() throws Exception {
|
||||||
|
if (documentFront != null) documentFront.close();
|
||||||
|
super.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
launch(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/main/java/Server/Era.java
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
public enum Era {
|
||||||
|
I,
|
||||||
|
II,
|
||||||
|
III,
|
||||||
|
FINAL;
|
||||||
|
|
||||||
|
|
||||||
|
public Era next() {
|
||||||
|
Era[] eras = values();
|
||||||
|
// Calcola l'indice del prossimo elemento
|
||||||
|
int nextOrdinal = this.ordinal() + 1;
|
||||||
|
if (nextOrdinal < eras.length) {
|
||||||
|
return eras[nextOrdinal];
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
136
src/main/java/Server/EventsSolver.java
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import Server.Cards.Event;
|
||||||
|
import Server.Cards.EventCard;
|
||||||
|
import Server.Cards.Trigger;
|
||||||
|
import Server.Utils.EventsManagerException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class EventsSolver {
|
||||||
|
|
||||||
|
public static boolean solveEvents(List<EventCard> events, List<Player> players){
|
||||||
|
|
||||||
|
for(EventCard event: events){
|
||||||
|
switch (event.getEvent()){
|
||||||
|
case Event.SUSTAINMENT:
|
||||||
|
EventsSolver.sustainment(event, players);
|
||||||
|
break;
|
||||||
|
case Event.HUNT:
|
||||||
|
EventsSolver.hunt(event, players);
|
||||||
|
break;
|
||||||
|
case Event.SHAMANIC_RITUAL:
|
||||||
|
EventsSolver.shamanicRitual(event, players);
|
||||||
|
break;
|
||||||
|
case Event.CAVE_PAINTINGS:
|
||||||
|
EventsSolver.cavePaintings(event, players);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new EventsManagerException("Unknown event type");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> sustainment(EventCard event, List<Player> players){
|
||||||
|
|
||||||
|
if(event.getEvent() != Event.SUSTAINMENT){throw new EventsManagerException("Not a sustainment card");}
|
||||||
|
|
||||||
|
List<Integer> result = new ArrayList<>();
|
||||||
|
for(Player p: players){
|
||||||
|
int subpoints = p.getPlayerTribe().getCharacters().size();
|
||||||
|
int discount = p.getPlayerTribe().gathererDiscount() + BuildingManager.sustainDiscount(p);
|
||||||
|
|
||||||
|
if(subpoints <= discount){subpoints = 0;}else{
|
||||||
|
subpoints -= discount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(p.getFoodTokens() >= subpoints){
|
||||||
|
p.removeFood(subpoints);
|
||||||
|
}else{
|
||||||
|
subpoints -= p.getFoodTokens();
|
||||||
|
p.removeFood(p.getFoodTokens());
|
||||||
|
p.removePrestigePoints(subpoints*event.getFirstValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(p.getFoodTokens());
|
||||||
|
result.add(p.getPrestigePoints());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> hunt(EventCard event, List<Player> players){
|
||||||
|
if(event.getEvent() != Event.HUNT){throw new EventsManagerException("Not a hunt card");}
|
||||||
|
|
||||||
|
List<Integer> result = new ArrayList<>();
|
||||||
|
|
||||||
|
for(Player p: players){
|
||||||
|
p.addFood(p.getPlayerTribe().huntersNumber());
|
||||||
|
p.addPrestigePoints(p.getPlayerTribe().huntersNumber()*event.getFirstValue());
|
||||||
|
|
||||||
|
if(BuildingManager.hunterBonus(p)){
|
||||||
|
p.addFood(p.getPlayerTribe().huntersNumber());
|
||||||
|
p.addPrestigePoints(p.getPlayerTribe().huntersNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(p.getFoodTokens());
|
||||||
|
result.add(p.getPrestigePoints());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> shamanicRitual(EventCard event, List<Player> players){
|
||||||
|
if(event.getEvent() != Event.SHAMANIC_RITUAL){throw new EventsManagerException("Not a shamanic ritual card");}
|
||||||
|
List<Integer> result = new ArrayList<>();
|
||||||
|
int maxSymbols = 0;
|
||||||
|
int minSymbols = 999;
|
||||||
|
|
||||||
|
for(Player p: players){
|
||||||
|
int symbols = p.getPlayerTribe().shamansIcons() + BuildingManager.shamanBonus(p);
|
||||||
|
if (symbols > maxSymbols){maxSymbols = p.getPlayerTribe().shamansIcons();}
|
||||||
|
if (symbols < minSymbols){minSymbols = p.getPlayerTribe().shamansIcons();}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Player p: players){
|
||||||
|
if(p.getPlayerTribe().shamansIcons() == maxSymbols){
|
||||||
|
p.addPrestigePoints(event.getFirstValue());
|
||||||
|
|
||||||
|
//activating building shamanDoublePoints card effect
|
||||||
|
if(BuildingManager.shamanDoublePoints(p))
|
||||||
|
p.addPrestigePoints(event.getFirstValue());
|
||||||
|
}
|
||||||
|
if(p.getPlayerTribe().shamansIcons() == minSymbols){
|
||||||
|
|
||||||
|
//activating building shamanNoLoss card effect
|
||||||
|
if(!BuildingManager.shamanNoLoss(p))
|
||||||
|
p.removePrestigePoints(event.getSecondValue());
|
||||||
|
}
|
||||||
|
result.add(p.getPrestigePoints());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> cavePaintings(EventCard event, List<Player> players){
|
||||||
|
|
||||||
|
if(event.getEvent() != Event.CAVE_PAINTINGS){throw new EventsManagerException("Not a cave painting card");}
|
||||||
|
List<Integer> result = new ArrayList<>();
|
||||||
|
for(Player p: players){
|
||||||
|
BuildingManager.paintingFoodBonus(p);
|
||||||
|
if(p.getPlayerTribe().artistsNumber() >= event.getFirstValue()){
|
||||||
|
p.addPrestigePoints(p.getPlayerTribe().artistsNumber()*event.getSecondValue());
|
||||||
|
}else{
|
||||||
|
p.removePrestigePoints(event.getSecondValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(p.getPrestigePoints());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
395
src/main/java/Server/GameBoard.java
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import Server.Automaton.Game;
|
||||||
|
import Server.Cards.*;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the physical game board: the two card rows, the offering tiles,
|
||||||
|
* the turn-order tile, and the card deck.
|
||||||
|
*
|
||||||
|
* Responsibilities:
|
||||||
|
* - Owning and mutating the top and bottom card rows.
|
||||||
|
* - Owning the OfferingTiles and the TurnTile.
|
||||||
|
* - Performing end-of-round row transitions and era-change building swaps.
|
||||||
|
* - Exposing visible EventCards to Game for resolution.
|
||||||
|
*
|
||||||
|
* NOT responsible for:
|
||||||
|
* - Resolving events (that is EventsSolver's job, called by Game).
|
||||||
|
* - Applying player rewards/penalties (that is Game's job).
|
||||||
|
* - Holding a reference to the player list.
|
||||||
|
*/
|
||||||
|
public class GameBoard {
|
||||||
|
private static final Logger logger = LogManager.getLogger(GameBoard.class);
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Fields
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private Era era;
|
||||||
|
|
||||||
|
private final CardDeck cardDeck;
|
||||||
|
private final List<Card> topRow;
|
||||||
|
private final List<Card> bottomRow;
|
||||||
|
private final List<OfferingTile> offeringTiles;
|
||||||
|
private final TurnTile turnTile;
|
||||||
|
private final BuildingManager buildingManager;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Constructor
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the board for a new game.
|
||||||
|
* Call {@link #setupInitialRows(int)} and {@link #initOfferingTiles(int)}
|
||||||
|
* separately after construction (mirrors the rulebook setup steps).
|
||||||
|
*/
|
||||||
|
public GameBoard(Era startingEra, CardDeck cardDeck, int numPlayers) {
|
||||||
|
this.era = startingEra;
|
||||||
|
this.cardDeck = cardDeck;
|
||||||
|
this.topRow = new ArrayList<>();
|
||||||
|
this.bottomRow = new ArrayList<>();
|
||||||
|
this.offeringTiles = new ArrayList<>();
|
||||||
|
this.turnTile = new TurnTile(numPlayers);
|
||||||
|
this.buildingManager = new BuildingManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Setup
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and places the correct OfferingTiles for the given player count.
|
||||||
|
*
|
||||||
|
* Tile layout (from the rulebook):
|
||||||
|
* orderId 0 → tile A: 3 food (5-player only)
|
||||||
|
* orderId 1 → tile B: 1 DOWN (all)
|
||||||
|
* orderId 2 → tile C: 1 UP (all)
|
||||||
|
* orderId 3 → tile D: 2 DOWN (3+ players)
|
||||||
|
* orderId 4 → tile E: 1 DOWN 1 UP (all)
|
||||||
|
* orderId 5 → tile F: 2 UP (all)
|
||||||
|
* orderId 6 → tile G: 1 DOWN 2 UP (4+ players)
|
||||||
|
*
|
||||||
|
* With n players, exactly n tiles are used, starting from tile A if
|
||||||
|
* 5 players, otherwise starting from tile B.
|
||||||
|
*/
|
||||||
|
public void initOfferingTiles(int numPlayers) {
|
||||||
|
offeringTiles.clear();
|
||||||
|
switch (numPlayers) {
|
||||||
|
case 2:
|
||||||
|
offeringTiles.add(new OfferingTile(1));
|
||||||
|
offeringTiles.add(new OfferingTile(2));
|
||||||
|
offeringTiles.add(new OfferingTile(4));
|
||||||
|
offeringTiles.add(new OfferingTile(5));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
offeringTiles.add(new OfferingTile(1));
|
||||||
|
offeringTiles.add(new OfferingTile(2));
|
||||||
|
offeringTiles.add(new OfferingTile(3));
|
||||||
|
offeringTiles.add(new OfferingTile(4));
|
||||||
|
offeringTiles.add(new OfferingTile(5));
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
offeringTiles.add(new OfferingTile(1));
|
||||||
|
offeringTiles.add(new OfferingTile(2));
|
||||||
|
offeringTiles.add(new OfferingTile(3));
|
||||||
|
offeringTiles.add(new OfferingTile(4));
|
||||||
|
offeringTiles.add(new OfferingTile(5));
|
||||||
|
offeringTiles.add(new OfferingTile(6));
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
offeringTiles.add(new OfferingTile(0));
|
||||||
|
offeringTiles.add(new OfferingTile(1));
|
||||||
|
offeringTiles.add(new OfferingTile(2));
|
||||||
|
offeringTiles.add(new OfferingTile(3));
|
||||||
|
offeringTiles.add(new OfferingTile(4));
|
||||||
|
offeringTiles.add(new OfferingTile(5));
|
||||||
|
offeringTiles.add(new OfferingTile(6));
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the oddertingTile selected by Player
|
||||||
|
public OfferingTile getOfferingTile(Player p){
|
||||||
|
OfferingTile offering = offeringTiles.stream().filter(o -> p.equals(o.getOccupant())).findFirst().orElse(null);
|
||||||
|
return offering;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Draws the initial two rows of cards according to rulebook setup steps 4-5:
|
||||||
|
*
|
||||||
|
* Bottom row: draw cards one at a time until (numPlayers + 1) Character cards
|
||||||
|
* have been placed. Any Event card drawn is placed in the top row instead.
|
||||||
|
* Top row: fill to (numPlayers + 4) with additional draws (accounting for
|
||||||
|
* any events already placed there from the bottom-row draw).
|
||||||
|
*/
|
||||||
|
public void setupInitialRows(int numPlayers) {
|
||||||
|
topRow.clear();
|
||||||
|
bottomRow.clear();
|
||||||
|
|
||||||
|
// Draw bottom row — events are bumped to top row
|
||||||
|
while (bottomRow.size() < numPlayers + 1) {
|
||||||
|
Card card = cardDeck.drawTribeOne();
|
||||||
|
if (card instanceof EventCard) {
|
||||||
|
topRow.add(card);
|
||||||
|
logger.info("Setup: Event card " + card.getCardId() + " moved to top row.");
|
||||||
|
} else {
|
||||||
|
bottomRow.add(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill top row up to numPlayers + 4
|
||||||
|
int needed = (numPlayers + 4) - topRow.size();
|
||||||
|
if (needed > 0) {
|
||||||
|
topRow.addAll(cardDeck.drawTribe(needed));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all Building Era.I on top row 2P 1 3-4-5 2
|
||||||
|
topRow.add(cardDeck.drawBuildingOne(Era.I));
|
||||||
|
if (numPlayers > 2){
|
||||||
|
topRow.add(cardDeck.drawBuildingOne(Era.I));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// End-of-round row management (rulebook "Fine del Round" steps 2-4)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the three row-management steps at the end of every round:
|
||||||
|
*
|
||||||
|
* Step 2 — Discard all Character and Event cards from the bottom row.
|
||||||
|
* Building cards stay.
|
||||||
|
* Step 3 — Move all Character and Event cards from the top row down to
|
||||||
|
* the bottom row. Building cards stay in the top row.
|
||||||
|
* Step 4 — Draw (numPlayers + 4) new cards into the top row.
|
||||||
|
*
|
||||||
|
* @return true if the newly drawn cards contain a card from the next Era,
|
||||||
|
* signalling that {@link #triggerEraChange()} should be called.
|
||||||
|
*/
|
||||||
|
public boolean advanceRows(int numPlayers) {
|
||||||
|
|
||||||
|
Era eraBeforeDraw = this.era;
|
||||||
|
|
||||||
|
// Step 2: discard non-building cards from the bottom row
|
||||||
|
bottomRow.removeIf(
|
||||||
|
c -> !(c instanceof BuildingCard)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 3: move non-building cards from top row down to bottom row
|
||||||
|
Iterator<Card> it = topRow.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Card c = it.next();
|
||||||
|
if (!(c instanceof BuildingCard)) {
|
||||||
|
logger.debug("move card from top to bottom " + c);
|
||||||
|
bottomRow.add(c);
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: draw fresh cards into the top row
|
||||||
|
List<Card> newCards = cardDeck.drawTribe(numPlayers + 4);
|
||||||
|
topRow.addAll(newCards);
|
||||||
|
logger.info("NEW CARDS ON TOP {} " , newCards);
|
||||||
|
return isNewEraRevealed(eraBeforeDraw, newCards);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if any of the newly drawn cards belongs to an era
|
||||||
|
* that is strictly later than the current one.
|
||||||
|
*/
|
||||||
|
private boolean isNewEraRevealed(Era currentEra, List<Card> newCards) {
|
||||||
|
for (Card c : newCards) {
|
||||||
|
Era cardEra = eraOf(c);
|
||||||
|
if (cardEra != null && cardEra.ordinal() > currentEra.ordinal()) {
|
||||||
|
logger.info("FOUND NEW ERA {} " , cardEra);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Era change (rulebook "Inizio della Nuova Era")
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advances the era and performs the three building-row transitions:
|
||||||
|
*
|
||||||
|
* Step 1 — (Era III only) Discard all Building cards from the bottom row.
|
||||||
|
* Step 2 — Move all Building cards from the top row to the bottom row
|
||||||
|
* (to the right of the Tribe cards). [Era II and III]
|
||||||
|
* Step 3 — Place the new era's Building cards face-up in the top row.
|
||||||
|
* [Era II and III]
|
||||||
|
*
|
||||||
|
* @return the new Era after the transition
|
||||||
|
*/
|
||||||
|
public Era triggerEraChange(int numPlayers) {
|
||||||
|
era = era.next(); // Era.I → II → III → FINAL
|
||||||
|
logger.info("ERA CHANGED → {} ", era);
|
||||||
|
|
||||||
|
// Step 1 (Era III only): remove old era buildings from the bottom row
|
||||||
|
if (era == Era.III) {
|
||||||
|
bottomRow.removeIf(c -> c instanceof BuildingCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: move buildings from top row to bottom row
|
||||||
|
List<BuildingCard> descending = new ArrayList<>();
|
||||||
|
Iterator<Card> it = topRow.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Card c = it.next();
|
||||||
|
if (c instanceof BuildingCard) {
|
||||||
|
descending.add((BuildingCard) c);
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bottomRow.addAll(descending);
|
||||||
|
logger.info("event=MOVED_BUILING era={} count={} cards={}", era, descending.size(), descending);
|
||||||
|
|
||||||
|
// Step 3: place the new era's building cards in the top row
|
||||||
|
int n = BuildingRules.getBuildingCards(numPlayers, era);
|
||||||
|
List<Card> newEraBuildings = cardDeck.drawBuilding(n , era);
|
||||||
|
topRow.addAll(newEraBuildings);
|
||||||
|
|
||||||
|
logger.info("event=ADD_BUILDINGS era={} count={} cards={}", era, n, newEraBuildings);
|
||||||
|
return era;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Offering tiles
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to place a player's totem on an offering tile.
|
||||||
|
*
|
||||||
|
* @return true if successful; false if the tile was already occupied
|
||||||
|
*/
|
||||||
|
public boolean placeTotem(Player player, OfferingTile tile, TurnTile turntile) {
|
||||||
|
if (!tile.isEmpty()) return false;
|
||||||
|
tile.setOccupant(player);
|
||||||
|
turntile.leaveTurnTile(player);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all totems from offering tiles. Call at the start of each new round.
|
||||||
|
*/
|
||||||
|
public void clearOfferingTiles() {
|
||||||
|
for (OfferingTile tile : offeringTiles) {
|
||||||
|
tile.removeOccupant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Card removal (called by Game when a player takes a card)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a card from the top row by ID and returns it.
|
||||||
|
*
|
||||||
|
* @return the card, or null if no card with that ID exists in the top row
|
||||||
|
*/
|
||||||
|
public Card takeFromTopRow(int cardId) {
|
||||||
|
return takeFromRow(topRow, cardId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a card from the bottom row by ID and returns it.
|
||||||
|
*
|
||||||
|
* @return the card, or null if no card with that ID exists in the bottom row
|
||||||
|
*/
|
||||||
|
public Card takeFromBottomRow(int cardId) {
|
||||||
|
return takeFromRow(bottomRow, cardId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Card takeFromRow(List<Card> row, int cardId) {
|
||||||
|
Iterator<Card> it = row.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Card c = it.next();
|
||||||
|
if (c.getCardId() == cardId) {
|
||||||
|
it.remove();
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Event visibility
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all EventCards currently in the bottom row, sorted by era
|
||||||
|
* (ascending) so they are resolved in the correct order.
|
||||||
|
* Sustainment is NOT sorted last here — that is Game's responsibility.
|
||||||
|
*/
|
||||||
|
public List<EventCard> getVisibleEvents() {
|
||||||
|
List<EventCard> events = new ArrayList<>();
|
||||||
|
for (Card c : bottomRow) {
|
||||||
|
if (c instanceof EventCard) {
|
||||||
|
events.add((EventCard) c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
events.sort((a, b) -> a.getEra().ordinal() - b.getEra().ordinal());
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns EventCards from BOTH rows.
|
||||||
|
* Used during the final round when all visible events must be resolved.
|
||||||
|
*/
|
||||||
|
public List<EventCard> getAllVisibleEvents() {
|
||||||
|
List<EventCard> events = new ArrayList<>();
|
||||||
|
for (Card c : bottomRow) {
|
||||||
|
if (c instanceof EventCard) events.add((EventCard) c);
|
||||||
|
}
|
||||||
|
for (Card c : topRow) {
|
||||||
|
if (c instanceof EventCard) events.add((EventCard) c);
|
||||||
|
}
|
||||||
|
events.sort((a, b) -> a.getEra().ordinal() - b.getEra().ordinal());
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Helpers
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Extracts the Era from a card, returning null for building cards. */
|
||||||
|
private Era eraOf(Card c) {
|
||||||
|
if (c instanceof CharacterCard) return ((CharacterCard) c).getEra();
|
||||||
|
if (c instanceof EventCard) return ((EventCard) c).getEra();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Getters
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public Era getEra() { return era; }
|
||||||
|
public List<Card> getTopRow() { return topRow; }
|
||||||
|
public List<Card> getBottomRow() { return bottomRow; }
|
||||||
|
public List<OfferingTile> getOfferingTiles() { return offeringTiles; }
|
||||||
|
public TurnTile getTurnTile() { return turnTile; }
|
||||||
|
public BuildingManager getBuildingManager() { return buildingManager; }
|
||||||
|
public CardDeck getCardDeck() { return cardDeck; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "GameBoard{" +
|
||||||
|
"era=" + era +
|
||||||
|
",\n offeringTiles=" + offeringTiles +
|
||||||
|
",\n turnTile=" + turnTile +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
148
src/main/java/Server/OfferingTile.java
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single tile on the offering track.
|
||||||
|
*
|
||||||
|
* Each tile has a fixed letter (A–G) and a fixed list of actions the
|
||||||
|
* occupying player must resolve. The tile is either empty or occupied
|
||||||
|
* by exactly one player's totem at any given time.
|
||||||
|
*
|
||||||
|
* Tile layout (from the rulebook):
|
||||||
|
*
|
||||||
|
* Letter │ orderId │ Actions │ Player count
|
||||||
|
* ───────┼─────────┼──────────────────────┼─────────────
|
||||||
|
* A │ 0 │ FOOD FOOD FOOD │ 5 only
|
||||||
|
* B │ 1 │ BOTTOM │ all
|
||||||
|
* C │ 2 │ UP │ all
|
||||||
|
* D │ 3 │ BOTTOM BOTTOM │ 3+
|
||||||
|
* E │ 4 │ BOTTOM UP │ all
|
||||||
|
* F │ 5 │ UP UP │ all
|
||||||
|
* G │ 6 │ BOTTOM UP UP │ 4+
|
||||||
|
*
|
||||||
|
* Which tiles are included in a game is decided by GameBoard.initOfferingTiles(),
|
||||||
|
* not by this class.
|
||||||
|
*/
|
||||||
|
public class OfferingTile {
|
||||||
|
|
||||||
|
private static final char FIRST_LETTER = 'A';
|
||||||
|
|
||||||
|
private final int orderId;
|
||||||
|
private final char letter;
|
||||||
|
private final List<Symbol> actions; // unmodifiable after construction
|
||||||
|
private Player occupant;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Constructor
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public OfferingTile(int orderId) {
|
||||||
|
this.orderId = orderId;
|
||||||
|
this.letter = (char) (FIRST_LETTER + orderId);
|
||||||
|
this.occupant = null;
|
||||||
|
this.actions = Collections.unmodifiableList(buildActions(orderId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the fixed action list for each tile orderId.
|
||||||
|
* Extracted into a private method to keep the constructor clean.
|
||||||
|
*/
|
||||||
|
private static List<Symbol> buildActions(int orderId) {
|
||||||
|
List<Symbol> list = new ArrayList<>();
|
||||||
|
switch (orderId) {
|
||||||
|
case 0: // A — food tile (5p only)
|
||||||
|
list.add(Symbol.FOOD);
|
||||||
|
list.add(Symbol.FOOD);
|
||||||
|
list.add(Symbol.FOOD);
|
||||||
|
break;
|
||||||
|
case 1: // B
|
||||||
|
list.add(Symbol.DOWN);
|
||||||
|
break;
|
||||||
|
case 2: // C
|
||||||
|
list.add(Symbol.UP);
|
||||||
|
break;
|
||||||
|
case 3: // D
|
||||||
|
list.add(Symbol.DOWN);
|
||||||
|
list.add(Symbol.DOWN);
|
||||||
|
break;
|
||||||
|
case 4: // E
|
||||||
|
list.add(Symbol.DOWN);
|
||||||
|
list.add(Symbol.UP);
|
||||||
|
break;
|
||||||
|
case 5: // F
|
||||||
|
list.add(Symbol.UP);
|
||||||
|
list.add(Symbol.UP);
|
||||||
|
break;
|
||||||
|
case 6: // G
|
||||||
|
list.add(Symbol.DOWN);
|
||||||
|
list.add(Symbol.UP);
|
||||||
|
list.add(Symbol.UP);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid OfferingTile orderId: " + orderId);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Occupant management
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Returns true if no totem is currently placed on this tile. */
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return occupant == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places a player's totem on this tile.
|
||||||
|
* Callers should check {@link #isEmpty()} before calling this.
|
||||||
|
*/
|
||||||
|
public void setOccupant(Player player) {
|
||||||
|
this.occupant = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the totem from this tile and returns the player who was on it,
|
||||||
|
* or null if the tile was already empty.
|
||||||
|
*/
|
||||||
|
public Player removeOccupant() {
|
||||||
|
Player previous = this.occupant;
|
||||||
|
this.occupant = null;
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Getters
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** The player currently occupying this tile, or null if empty. */
|
||||||
|
public Player getOccupant() {
|
||||||
|
return occupant;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actions this tile grants to its occupant (e.g. [BOTTOM, UP]).
|
||||||
|
* The returned list is unmodifiable.
|
||||||
|
*/
|
||||||
|
public List<Symbol> getActions() {
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 0-based position index; determines the tile's letter and action set. */
|
||||||
|
public int getOrderId() {
|
||||||
|
return orderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The tile's letter label ('A'–'G'), useful for display and logging. */
|
||||||
|
public char getLetter() {
|
||||||
|
return letter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Tile " + letter + " " + actions + (isEmpty() ? " [empty]" : " [" + occupant.getNickname() + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
96
src/main/java/Server/Player.java
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import Server.Cards.CharacterCard;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class Player {
|
||||||
|
|
||||||
|
//Attributes
|
||||||
|
private String nickname;
|
||||||
|
private TotemColor totemColor;
|
||||||
|
private int foodTokens;
|
||||||
|
private int prestigePoints;
|
||||||
|
private Tribe playerTribe;
|
||||||
|
|
||||||
|
|
||||||
|
//Constructor
|
||||||
|
public Player(String nickname, TotemColor totemColor) {
|
||||||
|
this.nickname = nickname;
|
||||||
|
this.totemColor = totemColor;
|
||||||
|
this.foodTokens = 0;
|
||||||
|
this.prestigePoints = 0;
|
||||||
|
this.playerTribe = new Tribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Methods
|
||||||
|
public void addFood(int food){
|
||||||
|
this.foodTokens += food;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean removeFood(int food){
|
||||||
|
if (this.foodTokens >= food) {
|
||||||
|
this.foodTokens -= food;
|
||||||
|
return true; // Pagamento andato a buon fine
|
||||||
|
}
|
||||||
|
return false; // Il giocatore non ha abbastanza cibo, transazione negata, la quantità di cibo rimane quella di prima.
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPrestigePoints(int prestige){
|
||||||
|
this.prestigePoints += prestige;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int removePrestigePoints(int prestige){
|
||||||
|
this.prestigePoints -= prestige;
|
||||||
|
return prestigePoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCharacterToTribe(CharacterCard card) {
|
||||||
|
int foodGained = this.playerTribe.addCharacter(card);
|
||||||
|
this.addFood(foodGained);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Getters
|
||||||
|
public String getNickname() {
|
||||||
|
return nickname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFoodTokens() {
|
||||||
|
return foodTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPrestigePoints() {
|
||||||
|
return prestigePoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TotemColor getTotemColor() {
|
||||||
|
return totemColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tribe getPlayerTribe() {
|
||||||
|
return playerTribe;
|
||||||
|
}
|
||||||
|
|
||||||
|
// two Player objects to be considered equal only by nickname
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Player player = (Player) o;
|
||||||
|
return Objects.equals(nickname, player.nickname);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(nickname);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Player(" +
|
||||||
|
"name='" + nickname + '\'' +
|
||||||
|
", food=" + foodTokens +
|
||||||
|
", pp=" + prestigePoints +
|
||||||
|
')';
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/main/java/Server/Symbol.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
public enum Symbol {
|
||||||
|
UP,
|
||||||
|
DOWN,
|
||||||
|
FOOD
|
||||||
|
}
|
||||||
21
src/main/java/Server/TotemColor.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
|
||||||
|
public enum TotemColor {
|
||||||
|
RED(Color.RED),
|
||||||
|
BLUE(Color.BLUE),
|
||||||
|
YELLOW(Color.YELLOW),
|
||||||
|
GREEN(Color.GREEN),
|
||||||
|
PURPLE(Color.PURPLE);
|
||||||
|
|
||||||
|
private final Color fxColor;
|
||||||
|
|
||||||
|
TotemColor(Color fxColor) {
|
||||||
|
this.fxColor = fxColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getFxColor() {
|
||||||
|
return fxColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
239
src/main/java/Server/Tribe.java
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import Server.Cards.BuildingCard;
|
||||||
|
import Server.Cards.CharacterCard;
|
||||||
|
import Server.Cards.CharacterType;
|
||||||
|
import Server.Cards.Trigger;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class Tribe {
|
||||||
|
// Attributes
|
||||||
|
private List<CharacterCard> characters;
|
||||||
|
private List<BuildingCard> buildings;
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public Tribe() {
|
||||||
|
this.characters = new ArrayList<>();
|
||||||
|
this.buildings = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CharacterCard> getCharacters() {
|
||||||
|
return characters;
|
||||||
|
}
|
||||||
|
public List<BuildingCard> getBuildingCard() {
|
||||||
|
return buildings;
|
||||||
|
}
|
||||||
|
// METODI
|
||||||
|
|
||||||
|
|
||||||
|
public int addCharacter(CharacterCard card) {
|
||||||
|
this.characters.add(card);
|
||||||
|
if (card.getCharacterType().equals(CharacterType.HUNTER)) {
|
||||||
|
return hunterGetFood(card);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metodo per aggiungere un nuovo building alla tribù
|
||||||
|
public void addBuilding(BuildingCard card) {
|
||||||
|
this.buildings.add(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metodo per ottenere lo sconto in cibo in base a quanti gatherer abbiamo nella tribù
|
||||||
|
public int gathererDiscount() {
|
||||||
|
int discount = 0;
|
||||||
|
for (CharacterCard c : characters) {
|
||||||
|
if (c.getCharacterType() == CharacterType.GATHERER) {
|
||||||
|
discount += 3; // i gatherers prendono sempre 3 cibi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return discount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metodo per ottenere lo sconto totale sugli edifici grazie ai builder nella tribù
|
||||||
|
public int buildersDiscount() {
|
||||||
|
int discount = 0;
|
||||||
|
for (CharacterCard c : characters) {
|
||||||
|
if (c.getCharacterType() == CharacterType.BUILDER) {
|
||||||
|
discount += c.getIconValue(); // con getIconValue intendo lo sconto del costruttore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return discount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metodo che conta quante stelle degli sciamani abbiamo in totale nella tribù
|
||||||
|
public int shamansIcons() {
|
||||||
|
int totalIcons = 0;
|
||||||
|
for (CharacterCard c : characters) {
|
||||||
|
if (c.getCharacterType() == CharacterType.SHAMAN) {
|
||||||
|
totalIcons += c.getIconValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalIcons;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metodo che restituisce il numero di artisti nella tribù
|
||||||
|
public int artistsNumber() {
|
||||||
|
return countCharactersByType(CharacterType.ARTIST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metodo che restituisce il numero di cacciatori nella tribù
|
||||||
|
public int huntersNumber() {
|
||||||
|
return countCharactersByType(CharacterType.HUNTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metodo universale per contare le carte di un certo tipo all'interno della tribù
|
||||||
|
public int countCharactersByType(CharacterType typeToCount) {
|
||||||
|
int count = 0;
|
||||||
|
for (CharacterCard c : characters) {
|
||||||
|
if (c.getCharacterType() == typeToCount) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metodo che ritorna il numero totale di cibi ottenuti dopo aver pescato il cacciatore col cosciotto
|
||||||
|
public int hunterGetFood(CharacterCard hunter) {
|
||||||
|
return huntersNumber() * hunter.getIconValue(); // getIconValue = 1 se il cacciatore ha il cosciotto
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metodo che restituisce i punti finali degli inventori
|
||||||
|
public int inventorsEndPoints() {
|
||||||
|
int inventorCount = 0;
|
||||||
|
List<Integer> uniqueInventions = new ArrayList<>();
|
||||||
|
|
||||||
|
for (CharacterCard c : characters) {
|
||||||
|
if (c.getCharacterType() == CharacterType.INVENTOR) {
|
||||||
|
inventorCount++;
|
||||||
|
|
||||||
|
int inventionId = c.getIconValue(); // Usiamo l'iconValue del file cards.csv
|
||||||
|
|
||||||
|
// Se non abbiamo ancora contato questa invenzione, la aggiungiamo
|
||||||
|
if (!uniqueInventions.contains(inventionId)) {
|
||||||
|
uniqueInventions.add(inventionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return inventorCount * uniqueInventions.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metodo che restituisce i punti finali degli artisti
|
||||||
|
public int artistsEndPoints() {
|
||||||
|
return (artistsNumber() / 2) * 10; // in java 1/2 fa 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metodo che restituisce i punti finali dei costruttori
|
||||||
|
public int buildersEndPoints() {
|
||||||
|
int points = 0;
|
||||||
|
for (CharacterCard c : characters) {
|
||||||
|
if (c.getCharacterType() == CharacterType.BUILDER) {
|
||||||
|
points += c.getPrestigePoints();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metodo che restituisce i punti finali guadagnati grazie agli EFFETTI delle carte building
|
||||||
|
private int buildingAbilitiesEndPoints() {
|
||||||
|
int bonusPoints = 0;
|
||||||
|
|
||||||
|
for (BuildingCard b : buildings) {
|
||||||
|
Trigger trigger = b.getAbilityTrigger(); // leggiamo il trigger della carta edificio
|
||||||
|
|
||||||
|
// Se la carta per qualche motivo non ha trigger, passiamo alla prossima
|
||||||
|
if (trigger == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (trigger) {
|
||||||
|
case ENDGAME_BUILDER_BONUS: // id carta: 107
|
||||||
|
bonusPoints += (buildersEndPoints() * 2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENDGAME_FOR_SIX: // id carta: 109
|
||||||
|
// Prepariamo i contatori per tutti e 6 i tipi di characters
|
||||||
|
int inv = 0, hun = 0, gat = 0, sha = 0, art = 0, bui = 0;
|
||||||
|
|
||||||
|
// contiamo le carte nella tribù
|
||||||
|
for (CharacterCard c : characters) {
|
||||||
|
switch (c.getCharacterType()) {
|
||||||
|
case INVENTOR: inv++; break;
|
||||||
|
case HUNTER: hun++; break;
|
||||||
|
case GATHERER: gat++; break;
|
||||||
|
case SHAMAN: sha++; break;
|
||||||
|
case ARTIST: art++; break;
|
||||||
|
case BUILDER: bui++; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// troviamo il numero di set completi
|
||||||
|
int min1 = Math.min(inv, hun);
|
||||||
|
int min2 = Math.min(gat, sha);
|
||||||
|
int min3 = Math.min(art, bui);
|
||||||
|
int completeSets = Math.min(Math.min(min1, min2), min3);
|
||||||
|
|
||||||
|
// aggiungiamo 6 punti prestigio per ogni set completo
|
||||||
|
bonusPoints += (completeSets * 6);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENDGAME_BONUS_CHARACTER:
|
||||||
|
|
||||||
|
int id = b.getCardId(); // uso l'id della carta per capire che edificio è
|
||||||
|
|
||||||
|
// il numero id corrisponde al numero di pagina nel pdf
|
||||||
|
if (id == 110) { // edificio dei cacciatori
|
||||||
|
bonusPoints += countCharactersByType(CharacterType.HUNTER) * 3;
|
||||||
|
}
|
||||||
|
else if (id == 111) { // edificio dei gatherer
|
||||||
|
bonusPoints += countCharactersByType(CharacterType.GATHERER) * 4;
|
||||||
|
}
|
||||||
|
else if (id == 112) { // edificio degli sciamani
|
||||||
|
bonusPoints += countCharactersByType(CharacterType.SHAMAN) * 4;
|
||||||
|
}
|
||||||
|
else if (id == 113) { // edificio dei costruttori
|
||||||
|
bonusPoints += countCharactersByType(CharacterType.BUILDER) * 4;
|
||||||
|
}
|
||||||
|
else if (id == 114) { // edificio degli artisti
|
||||||
|
bonusPoints += countCharactersByType(CharacterType.ARTIST) * 4;
|
||||||
|
}
|
||||||
|
else if (id == 115) { // edificio degli inventori
|
||||||
|
bonusPoints += countCharactersByType(CharacterType.INVENTOR) * 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENDGAME_BONUS_POINTS: // id carta: 117
|
||||||
|
bonusPoints += 25; // dà 25 punti bonus
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bonusPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metodo che calcola i punti finali totali
|
||||||
|
public int endPoints() {
|
||||||
|
int total = 0;
|
||||||
|
|
||||||
|
// sommiamo i punti calcolati dai vari personaggi
|
||||||
|
total += inventorsEndPoints();
|
||||||
|
total += artistsEndPoints();
|
||||||
|
total += buildersEndPoints();
|
||||||
|
|
||||||
|
// sommiamo i punti prestigio BASE di tutti gli edifici (EndPP)
|
||||||
|
for (BuildingCard b : buildings) {
|
||||||
|
total += b.getEndPP();
|
||||||
|
}
|
||||||
|
|
||||||
|
// sommiamo gli effetti degli edifici sul punteggio finale, NON quelli durante la partita
|
||||||
|
total += buildingAbilitiesEndPoints();
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
}
|
||||||
176
src/main/java/Server/TurnTile.java
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the Turn Order tile.
|
||||||
|
*
|
||||||
|
* Lifecycle per round:
|
||||||
|
* 1. At round start, read positions top-to-bottom via nextToPlace() to know
|
||||||
|
* which player places their totem on the offering track first.
|
||||||
|
* 2. After a player resolves all their offering actions, they call returnTotem().
|
||||||
|
* returnTotem() places them in the next free slot (top-to-bottom) and
|
||||||
|
* immediately gives them the position reward/penalty.
|
||||||
|
* 3. The order in which players called returnTotem() becomes the new turn
|
||||||
|
* order for the NEXT round.
|
||||||
|
* 4. resetTrack() is called at the start of each new round to restart the
|
||||||
|
* placement cursor and the return-slot counter, WITHOUT clearing positions
|
||||||
|
* (they already hold the new order from step 2-3).
|
||||||
|
*/
|
||||||
|
public class TurnTile {
|
||||||
|
private static final Logger logger = LogManager.getLogger(TurnTile.class);
|
||||||
|
private final Player[] positions; // current turn order; updated each round via returnTotem()
|
||||||
|
private int nextFreeSlot; // next available slot for a returning totem
|
||||||
|
private int placementCursor; // cursor used during the totem-placement phase
|
||||||
|
|
||||||
|
public TurnTile(int numP) {
|
||||||
|
this.positions = new Player[numP];
|
||||||
|
this.nextFreeSlot = 0;
|
||||||
|
this.placementCursor = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Setup
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Randomises the initial turn order. Call once during game setup.
|
||||||
|
*/
|
||||||
|
public void setInitialOrder(List<Player> players) {
|
||||||
|
|
||||||
|
List<Player> shuffled = new ArrayList<>(players);
|
||||||
|
Collections.shuffle(shuffled);
|
||||||
|
for (int i = 0; i < shuffled.size(); i++) {
|
||||||
|
positions[i] = shuffled.get(i);
|
||||||
|
}
|
||||||
|
logger.info("setInitialOrder " + this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Totem-placement phase (phase 1 of a round)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next player who must place their totem on the offering track
|
||||||
|
* (following the current turn order, top to bottom).
|
||||||
|
* Returns null when all players have already been served this round.
|
||||||
|
*/
|
||||||
|
public Player nextToPlace() {
|
||||||
|
if (placementCursor < positions.length) {
|
||||||
|
return positions[placementCursor++];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the player who is currently expected to place their totem
|
||||||
|
* (i.e. the last player returned by nextToPlace), or null if none yet.
|
||||||
|
*/
|
||||||
|
public Player getLastPlacedPlayer() {
|
||||||
|
if (placementCursor > 0) {
|
||||||
|
return positions[placementCursor - 1];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Action-resolution phase (phase 2 of a round)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a player has finished resolving all their offering actions.
|
||||||
|
* Places the player's totem in the next free slot (determining next-round
|
||||||
|
* order) and applies the food reward or penalty for that slot.
|
||||||
|
*
|
||||||
|
* Reward rules (from the rulebook):
|
||||||
|
* - Slot 0 (first to return): +1 food (2p), +2 food (3-4p), +3 food (5p)
|
||||||
|
* - Slot 1 (second to return, only 4-5 player games): +1 food
|
||||||
|
* - Last slot: pay 1 food; if unable, lose 2 PP instead
|
||||||
|
*
|
||||||
|
* @return the slot index the player was placed in
|
||||||
|
*/
|
||||||
|
public int returnTotem(Player player) {
|
||||||
|
logger.info("returnTotem " + player);
|
||||||
|
int slot = nextFreeSlot;
|
||||||
|
|
||||||
|
// --- Position food reward ---
|
||||||
|
int positionFood = 0;
|
||||||
|
if (slot == 0) {
|
||||||
|
positionFood = (positions.length == 2) ? 1
|
||||||
|
: (positions.length == 5) ? 3
|
||||||
|
: 2; // 3 or 4 players
|
||||||
|
} else if (slot == 1 && positions.length >= 4) {
|
||||||
|
positionFood = 1;
|
||||||
|
}
|
||||||
|
player.addFood(positionFood);
|
||||||
|
|
||||||
|
// --- Activate BONUS_FOOD_ENDTURN building if the player has it ---
|
||||||
|
// (The building gives +1 extra food whenever the player lands on a food slot)
|
||||||
|
BuildingManager.bonusEndTurn(player, positionFood);
|
||||||
|
|
||||||
|
// --- Last-slot penalty ---
|
||||||
|
if (slot == positions.length - 1) {
|
||||||
|
if (!player.removeFood(1)) {
|
||||||
|
player.removePrestigePoints(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
positions[slot] = player;
|
||||||
|
nextFreeSlot++;
|
||||||
|
|
||||||
|
logger.info(player.getNickname() + " returned totem to slot " + slot
|
||||||
|
+ (positionFood > 0 ? " and received " + positionFood + " food" : ""));
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void leaveTurnTile(Player player) {
|
||||||
|
for(int i=0;i<positions.length;i++)
|
||||||
|
if (positions[i] !=null && positions[i].equals(player)) positions[i]=null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Round reset
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the cursors for a new round.
|
||||||
|
* Does NOT clear positions: those already contain the new turn order
|
||||||
|
* established during the previous round's returnTotem() calls.
|
||||||
|
*/
|
||||||
|
public void resetTrack() {
|
||||||
|
nextFreeSlot = 0;
|
||||||
|
placementCursor = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Getters
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full positions array (turn order).
|
||||||
|
* Index 0 = first to act, last index = last to act.
|
||||||
|
*/
|
||||||
|
public Player[] getPositions() {
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** How many players have already returned their totem this round. */
|
||||||
|
public int getReturnedCount() {
|
||||||
|
return nextFreeSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TurnTile{" +
|
||||||
|
"positions=" + Arrays.toString(positions) +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
89
src/main/java/Server/TurnTileOld.java
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TurnTileOld {
|
||||||
|
|
||||||
|
private final Player[] positions;
|
||||||
|
private int place;
|
||||||
|
private int currentplayer;
|
||||||
|
|
||||||
|
|
||||||
|
public TurnTileOld(int numP) {
|
||||||
|
|
||||||
|
this.positions = new Player[numP];
|
||||||
|
this.place = 0;
|
||||||
|
this.currentplayer = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player[] startOrder (List<Player> players){
|
||||||
|
|
||||||
|
for (Player p: players){
|
||||||
|
|
||||||
|
positions[place]=p;
|
||||||
|
place++;
|
||||||
|
|
||||||
|
}
|
||||||
|
//Collections.shuffle(Arrays.asList(positions));
|
||||||
|
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player giveReward (Player player){
|
||||||
|
|
||||||
|
if (player == positions[positions.length-1]){
|
||||||
|
|
||||||
|
if (!player.removeFood(1)){
|
||||||
|
|
||||||
|
player.removePrestigePoints(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (player == positions[0]){
|
||||||
|
|
||||||
|
if (positions.length==2){
|
||||||
|
|
||||||
|
player.addFood(1);
|
||||||
|
}
|
||||||
|
else if (positions.length == 5){
|
||||||
|
|
||||||
|
player.addFood(3);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
|
||||||
|
player.addFood(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (player == positions[1] && positions.length >= 4){
|
||||||
|
|
||||||
|
player.addFood(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player nextPlayer() {
|
||||||
|
|
||||||
|
currentplayer++;
|
||||||
|
if (currentplayer== positions.length){
|
||||||
|
currentplayer=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return positions[currentplayer];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player addPlayer(Player player){
|
||||||
|
|
||||||
|
positions[currentplayer]=player;
|
||||||
|
|
||||||
|
return positions[currentplayer];
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/main/java/Server/Utils/EventsManagerException.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package Server.Utils;
|
||||||
|
|
||||||
|
public class EventsManagerException extends RuntimeException {
|
||||||
|
public EventsManagerException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/main/java/Server/Utils/GameException.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package Server.Utils;
|
||||||
|
|
||||||
|
public class GameException extends RuntimeException {
|
||||||
|
public GameException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/main/java/Server/Utils/LoadingCardsException.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package Server.Utils;
|
||||||
|
|
||||||
|
public class LoadingCardsException extends RuntimeException {
|
||||||
|
public LoadingCardsException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/main/java/module-info.java
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
module org.example.mesosll07 {
|
||||||
|
requires javafx.controls;
|
||||||
|
requires javafx.fxml;
|
||||||
|
|
||||||
|
requires org.controlsfx.controls;
|
||||||
|
requires javafx.swing;
|
||||||
|
requires org.apache.pdfbox;
|
||||||
|
requires org.apache.logging.log4j;
|
||||||
|
|
||||||
|
opens org.example.mesosll07 to javafx.fxml;
|
||||||
|
exports org.example.mesosll07;
|
||||||
|
exports Server;
|
||||||
|
opens Server to javafx.fxml;
|
||||||
|
exports Server.Cards;
|
||||||
|
opens Server.Cards to javafx.fxml;
|
||||||
|
exports Server.Automaton;
|
||||||
|
opens Server.Automaton to javafx.fxml;
|
||||||
|
}
|
||||||
208
src/main/java/org/example/mesosll07/DeckGridApp.java
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
package org.example.mesosll07;
|
||||||
|
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DeckGridApp extends Application {
|
||||||
|
|
||||||
|
// Inizializza il Logger
|
||||||
|
private static final Logger logger = LogManager.getLogger(DeckGridApp.class);
|
||||||
|
|
||||||
|
private PDDocument document;
|
||||||
|
private PDFRenderer pdfRenderer;
|
||||||
|
private int totalCards = 0;
|
||||||
|
|
||||||
|
// Contenitori per le tre righe
|
||||||
|
private HBox topRow;
|
||||||
|
private HBox centerRow;
|
||||||
|
private HBox bottomRow;
|
||||||
|
private Button btnShuffle;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Stage primaryStage) {
|
||||||
|
primaryStage.setTitle("Tavolo da Gioco - Carte Casuali");
|
||||||
|
logger.info("Avvio dell'applicazione JavaFX");
|
||||||
|
|
||||||
|
// Pulsanti
|
||||||
|
Button btnLoad = new Button("Carica PDF Mazzo");
|
||||||
|
btnShuffle = new Button("Rimescola Carte");
|
||||||
|
btnShuffle.setDisable(true); // Disabilitato finché non si carica un PDF
|
||||||
|
|
||||||
|
btnLoad.setOnAction(e -> loadPdf(primaryStage));
|
||||||
|
btnShuffle.setOnAction(e -> distributeRandomCards());
|
||||||
|
|
||||||
|
HBox topMenu = new HBox(15, btnLoad, btnShuffle);
|
||||||
|
topMenu.setAlignment(Pos.CENTER);
|
||||||
|
topMenu.setPadding(new Insets(10));
|
||||||
|
|
||||||
|
// Setup delle tre righe (spazio tra le carte impostato a 10px)
|
||||||
|
topRow = createRowContainer();
|
||||||
|
centerRow = createRowContainer();
|
||||||
|
bottomRow = createRowContainer();
|
||||||
|
|
||||||
|
// Contenitore verticale per le righe
|
||||||
|
VBox tableArea = new VBox(30,
|
||||||
|
new Label("TOP (8 Carte):"), topRow,
|
||||||
|
new Label("CENTER (6 Carte):"), centerRow,
|
||||||
|
new Label("BOTTOM (7 Carte):"), bottomRow
|
||||||
|
);
|
||||||
|
tableArea.setAlignment(Pos.CENTER);
|
||||||
|
tableArea.setPadding(new Insets(20));
|
||||||
|
|
||||||
|
// Mettiamo il tavolo in uno ScrollPane nel caso le carte siano troppo grandi per lo schermo
|
||||||
|
ScrollPane scrollPane = new ScrollPane(tableArea);
|
||||||
|
scrollPane.setFitToWidth(true);
|
||||||
|
|
||||||
|
BorderPane root = new BorderPane();
|
||||||
|
root.setTop(topMenu);
|
||||||
|
root.setCenter(scrollPane);
|
||||||
|
|
||||||
|
// Finestra un po' più grande per contenere tutte queste carte
|
||||||
|
Scene scene = new Scene(root, 1200, 800);
|
||||||
|
primaryStage.setScene(scene);
|
||||||
|
primaryStage.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private HBox createRowContainer() {
|
||||||
|
HBox row = new HBox(15); // 15px di spazio tra una carta e l'altra
|
||||||
|
row.setAlignment(Pos.CENTER);
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPdf(Stage stage) {
|
||||||
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
fileChooser.setTitle("Seleziona il PDF del mazzo di carte");
|
||||||
|
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("File PDF", "*.pdf"));
|
||||||
|
File file = fileChooser.showOpenDialog(stage);
|
||||||
|
|
||||||
|
if (file != null) {
|
||||||
|
try {
|
||||||
|
if (document != null) {
|
||||||
|
document.close();
|
||||||
|
}
|
||||||
|
logger.info("Caricamento file PDF: {}", file.getAbsolutePath());
|
||||||
|
document = PDDocument.load(file);
|
||||||
|
pdfRenderer = new PDFRenderer(document);
|
||||||
|
totalCards = document.getNumberOfPages();
|
||||||
|
|
||||||
|
logger.info("PDF caricato. Pagine totali (carte): {}", totalCards);
|
||||||
|
btnShuffle.setDisable(false);
|
||||||
|
|
||||||
|
// Distribuisci le carte per la prima volta
|
||||||
|
distributeRandomCards();
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("Errore nel caricamento del PDF", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void distributeRandomCards() {
|
||||||
|
if (document == null || totalCards == 0) return;
|
||||||
|
|
||||||
|
logger.info("Inizio rimescolamento e distribuzione carte...");
|
||||||
|
|
||||||
|
// Pulisce il tavolo dalle carte precedenti
|
||||||
|
topRow.getChildren().clear();
|
||||||
|
centerRow.getChildren().clear();
|
||||||
|
bottomRow.getChildren().clear();
|
||||||
|
|
||||||
|
// 1. Crea una lista con tutti gli indici (da 0 al numero totale di pagine)
|
||||||
|
List<Integer> deckIndices = new ArrayList<>();
|
||||||
|
for (int i = 0; i < totalCards; i++) {
|
||||||
|
deckIndices.add(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Mescola la lista (Simula la mescolata del mazzo)
|
||||||
|
Collections.shuffle(deckIndices);
|
||||||
|
|
||||||
|
// 3. Distribuisci nelle righe (evitando IndexOutOfBounds se il PDF ha meno di 21 pagine)
|
||||||
|
// Riga Top: 8 carte
|
||||||
|
populateRow(topRow, deckIndices, 0, 8);
|
||||||
|
|
||||||
|
// Riga Center: 6 carte
|
||||||
|
populateRow(centerRow, deckIndices, 8, 6);
|
||||||
|
|
||||||
|
// Riga Bottom: 7 carte
|
||||||
|
populateRow(bottomRow, deckIndices, 14, 7);
|
||||||
|
|
||||||
|
logger.info("Distribuzione completata.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateRow(HBox row, List<Integer> deckIndices, int startIndex, int numberOfCards) {
|
||||||
|
for (int i = 0; i < numberOfCards; i++) {
|
||||||
|
int actualIndex = startIndex + i;
|
||||||
|
|
||||||
|
// Se abbiamo esaurito le carte nel PDF, interrompiamo
|
||||||
|
if (actualIndex >= deckIndices.size()) {
|
||||||
|
logger.warn("Il PDF non ha abbastanza carte per riempire questa riga.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pageNumber = deckIndices.get(actualIndex);
|
||||||
|
ImageView cardImage = createCardImageView(pageNumber);
|
||||||
|
|
||||||
|
if (cardImage != null) {
|
||||||
|
row.getChildren().add(cardImage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImageView createCardImageView(int pageIndex) {
|
||||||
|
try {
|
||||||
|
// Renderizza la pagina. Usiamo 100 DPI invece di 150 per risparmiare RAM,
|
||||||
|
// dato che ora stiamo generando 21 immagini contemporaneamente.
|
||||||
|
BufferedImage bim = pdfRenderer.renderImageWithDPI(pageIndex, 100);
|
||||||
|
Image fxImage = SwingFXUtils.toFXImage(bim, null);
|
||||||
|
|
||||||
|
ImageView imageView = new ImageView(fxImage);
|
||||||
|
// Impostiamo l'altezza fissa per le carte, in modo che stiano sullo schermo
|
||||||
|
imageView.setFitHeight(180);
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.5), 5, 0, 0, 0);");
|
||||||
|
|
||||||
|
return imageView;
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("Errore durante il rendering della pagina {}", pageIndex, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() throws Exception {
|
||||||
|
if (document != null) {
|
||||||
|
document.close();
|
||||||
|
logger.info("Documento PDF chiuso correttamente.");
|
||||||
|
}
|
||||||
|
logger.info("Applicazione terminata.");
|
||||||
|
super.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
launch(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
263
src/main/java/org/example/mesosll07/DeckGridApp2.java
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
package org.example.mesosll07;
|
||||||
|
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.*;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.text.Font;
|
||||||
|
import javafx.scene.text.FontWeight;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class DeckGridApp2 extends Application {
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger(DeckGridApp2.class);
|
||||||
|
|
||||||
|
private PDDocument document;
|
||||||
|
private PDFRenderer pdfRenderer;
|
||||||
|
private int totalCards = 0;
|
||||||
|
|
||||||
|
// Contenitori Layout
|
||||||
|
private HBox topRow;
|
||||||
|
private HBox centerRow;
|
||||||
|
private HBox bottomRow;
|
||||||
|
private HBox playersArea; // NUOVO: Area per i giocatori
|
||||||
|
private Button btnShuffle;
|
||||||
|
|
||||||
|
// --- LOGICA DI GIOCO SIMULATA ---
|
||||||
|
public enum CardType {
|
||||||
|
CACCIA, UTENSILI, RITUALI, COSTRUZIONI
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Card {
|
||||||
|
int pdfPageIndex;
|
||||||
|
CardType type;
|
||||||
|
|
||||||
|
public Card(int pdfPageIndex, CardType type) {
|
||||||
|
this.pdfPageIndex = pdfPageIndex;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
public CardType getType() { return type; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Player {
|
||||||
|
String name;
|
||||||
|
int food;
|
||||||
|
int money;
|
||||||
|
List<Card> hand = new ArrayList<>();
|
||||||
|
|
||||||
|
public Player(String name, int food, int money) {
|
||||||
|
this.name = name;
|
||||||
|
this.food = food;
|
||||||
|
this.money = money;
|
||||||
|
}
|
||||||
|
public List<Card> getHand() { return hand; }
|
||||||
|
}
|
||||||
|
// --------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Stage primaryStage) {
|
||||||
|
primaryStage.setTitle("Tavolo da Gioco - Board & Players");
|
||||||
|
|
||||||
|
Button btnLoad = new Button("Carica PDF Mazzo");
|
||||||
|
btnShuffle = new Button("Rimescola & Distribuisci");
|
||||||
|
btnShuffle.setDisable(true);
|
||||||
|
|
||||||
|
btnLoad.setOnAction(e -> loadPdf(primaryStage));
|
||||||
|
btnShuffle.setOnAction(e -> distributeAll());
|
||||||
|
|
||||||
|
HBox topMenu = new HBox(15, btnLoad, btnShuffle);
|
||||||
|
topMenu.setAlignment(Pos.CENTER);
|
||||||
|
topMenu.setPadding(new Insets(10));
|
||||||
|
|
||||||
|
topRow = createRowContainer();
|
||||||
|
centerRow = createRowContainer();
|
||||||
|
bottomRow = createRowContainer();
|
||||||
|
|
||||||
|
// NUOVO: Inizializza l'area giocatori
|
||||||
|
playersArea = new HBox(30);
|
||||||
|
playersArea.setAlignment(Pos.CENTER);
|
||||||
|
playersArea.setPadding(new Insets(20));
|
||||||
|
|
||||||
|
VBox tableArea = new VBox(20,
|
||||||
|
new Label("TOP (8 Carte):"), topRow,
|
||||||
|
new Label("CENTER (6 Carte):"), centerRow,
|
||||||
|
new Label("BOTTOM (7 Carte):"), bottomRow,
|
||||||
|
new Label("--- SITUAZIONE GIOCATORI ---"), playersArea
|
||||||
|
);
|
||||||
|
tableArea.setAlignment(Pos.CENTER);
|
||||||
|
tableArea.setPadding(new Insets(20));
|
||||||
|
|
||||||
|
ScrollPane scrollPane = new ScrollPane(tableArea);
|
||||||
|
scrollPane.setFitToWidth(true);
|
||||||
|
|
||||||
|
BorderPane root = new BorderPane();
|
||||||
|
root.setTop(topMenu);
|
||||||
|
root.setCenter(scrollPane);
|
||||||
|
|
||||||
|
Scene scene = new Scene(root, 1300, 900);
|
||||||
|
primaryStage.setScene(scene);
|
||||||
|
primaryStage.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private HBox createRowContainer() {
|
||||||
|
HBox row = new HBox(10);
|
||||||
|
row.setAlignment(Pos.CENTER);
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPdf(Stage stage) {
|
||||||
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("File PDF", "*.pdf"));
|
||||||
|
File file = fileChooser.showOpenDialog(stage);
|
||||||
|
|
||||||
|
if (file != null) {
|
||||||
|
try {
|
||||||
|
if (document != null) document.close();
|
||||||
|
document = PDDocument.load(file);
|
||||||
|
pdfRenderer = new PDFRenderer(document);
|
||||||
|
totalCards = document.getNumberOfPages();
|
||||||
|
btnShuffle.setDisable(false);
|
||||||
|
|
||||||
|
distributeAll();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("Errore PDF", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void distributeAll() {
|
||||||
|
if (document == null || totalCards == 0) return;
|
||||||
|
|
||||||
|
topRow.getChildren().clear();
|
||||||
|
centerRow.getChildren().clear();
|
||||||
|
bottomRow.getChildren().clear();
|
||||||
|
playersArea.getChildren().clear();
|
||||||
|
|
||||||
|
List<Integer> deck = new ArrayList<>();
|
||||||
|
for (int i = 0; i < totalCards; i++) deck.add(i);
|
||||||
|
Collections.shuffle(deck);
|
||||||
|
|
||||||
|
// Distribuisce sul tavolo le prime 21 carte
|
||||||
|
int index = 0;
|
||||||
|
index = populateTable(topRow, deck, index, 8);
|
||||||
|
index = populateTable(centerRow, deck, index, 6);
|
||||||
|
index = populateTable(bottomRow, deck, index, 7);
|
||||||
|
|
||||||
|
// NUOVO: Simula 2 giocatori con le carte rimanenti
|
||||||
|
Random rand = new Random();
|
||||||
|
List<Player> players = Arrays.asList(
|
||||||
|
new Player("Giocatore 1 (Tribù Rossa)", rand.nextInt(15), rand.nextInt(50)),
|
||||||
|
new Player("Giocatore 2 (Tribù Blu)", rand.nextInt(15), rand.nextInt(50))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assegna 5 carte a caso a ogni giocatore e simula il loro Tipo
|
||||||
|
CardType[] types = CardType.values();
|
||||||
|
for (Player p : players) {
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
if (index < deck.size()) {
|
||||||
|
CardType randomType = types[rand.nextInt(types.length)];
|
||||||
|
p.getHand().add(new Card(deck.get(index), randomType));
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renderizza i giocatori nella UI
|
||||||
|
renderPlayers(players);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int populateTable(HBox row, List<Integer> deck, int startIndex, int amount) {
|
||||||
|
for (int i = 0; i < amount; i++) {
|
||||||
|
if (startIndex >= deck.size()) break;
|
||||||
|
ImageView card = createCardImage(deck.get(startIndex), 150); // Altezza 150px
|
||||||
|
if (card != null) row.getChildren().add(card);
|
||||||
|
startIndex++;
|
||||||
|
}
|
||||||
|
return startIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- NUOVO: RENDERIZZAZIONE GIOCATORI E RAGGRUPPAMENTO (JAVA 8) ---
|
||||||
|
private void renderPlayers(List<Player> players) {
|
||||||
|
for (Player player : players) {
|
||||||
|
VBox playerBox = new VBox(10);
|
||||||
|
playerBox.setPadding(new Insets(15));
|
||||||
|
playerBox.setStyle("-fx-border-color: #555; -fx-border-width: 2; -fx-border-radius: 10; -fx-background-color: #f9f9f9; -fx-background-radius: 10;");
|
||||||
|
|
||||||
|
// Intestazione Giocatore
|
||||||
|
Label nameLbl = new Label(player.name);
|
||||||
|
nameLbl.setFont(Font.font("System", FontWeight.BOLD, 16));
|
||||||
|
|
||||||
|
// Risorse Cibo e Soldi
|
||||||
|
Label statsLbl = new Label("🍖 Cibo: " + player.food + " | 💰 Money: " + player.money);
|
||||||
|
statsLbl.setTextFill(Color.DARKRED);
|
||||||
|
statsLbl.setFont(Font.font("System", FontWeight.BOLD, 14));
|
||||||
|
|
||||||
|
playerBox.getChildren().addAll(nameLbl, statsLbl);
|
||||||
|
|
||||||
|
// JAVA 8 MAGIA: Raggruppa le carte del giocatore per Tipo!
|
||||||
|
Map<CardType, List<Card>> groupedCards = player.getHand().stream()
|
||||||
|
.collect(Collectors.groupingBy(Card::getType));
|
||||||
|
|
||||||
|
// Itera sui gruppi creati e genera la grafica
|
||||||
|
groupedCards.forEach((type, cardsOfType) -> {
|
||||||
|
Label typeLbl = new Label("Tipo: " + type.name() + " (" + cardsOfType.size() + ")");
|
||||||
|
typeLbl.setFont(Font.font("System", FontWeight.NORMAL, 12));
|
||||||
|
|
||||||
|
HBox cardImagesRow = new HBox(5);
|
||||||
|
for (Card c : cardsOfType) {
|
||||||
|
// Carte più piccole (90px) per l'area giocatore
|
||||||
|
ImageView img = createCardImage(c.pdfPageIndex, 90);
|
||||||
|
if (img != null) cardImagesRow.getChildren().add(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
playerBox.getChildren().addAll(typeLbl, cardImagesRow);
|
||||||
|
});
|
||||||
|
|
||||||
|
playersArea.getChildren().add(playerBox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImageView createCardImage(int pageIndex, int height) {
|
||||||
|
try {
|
||||||
|
BufferedImage bim = pdfRenderer.renderImageWithDPI(pageIndex, 100);
|
||||||
|
Image fxImage = SwingFXUtils.toFXImage(bim, null);
|
||||||
|
ImageView imageView = new ImageView(fxImage);
|
||||||
|
imageView.setFitHeight(height);
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
|
||||||
|
return imageView;
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("Errore rendering pagina {}", pageIndex, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() throws Exception {
|
||||||
|
if (document != null) document.close();
|
||||||
|
super.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
launch(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
271
src/main/java/org/example/mesosll07/DeckGridAppFX.java
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
package org.example.mesosll07;
|
||||||
|
|
||||||
|
import Server.Automaton.Game;
|
||||||
|
import Server.Era;
|
||||||
|
import Server.Player;
|
||||||
|
import Server.TotemColor;
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.text.Font;
|
||||||
|
import javafx.scene.text.FontWeight;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import Server.Cards.*;
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
public class DeckGridAppFX extends Application {
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger(DeckGridAppFX.class);
|
||||||
|
|
||||||
|
private PDDocument documentFront;
|
||||||
|
private PDDocument documentBack;
|
||||||
|
private PDFRenderer pdfRendererFront;
|
||||||
|
private PDFRenderer pdfRendererBack;
|
||||||
|
private int totalCards = 0;
|
||||||
|
|
||||||
|
// Contenitori Layout
|
||||||
|
private HBox topRow;
|
||||||
|
private HBox centerRow;
|
||||||
|
private HBox bottomRow;
|
||||||
|
private HBox playersArea; // NUOVO: Area per i giocatori
|
||||||
|
private Button btnShuffle;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Stage primaryStage) {
|
||||||
|
|
||||||
|
List<Player> players = new ArrayList<>();
|
||||||
|
players.add(new Player("Yoshi", TotemColor.YELLOW));
|
||||||
|
players.add(new Player("Pippo", TotemColor.PURPLE));
|
||||||
|
players.add(new Player("John", TotemColor.RED));
|
||||||
|
players.add(new Player("Baggio", TotemColor.BLUE));
|
||||||
|
players.add(new Player("Gino", TotemColor.GREEN));
|
||||||
|
|
||||||
|
Game game = new Game(players);
|
||||||
|
String fileCards="/home/lorenzo/dev/Mesos2/src/main/resources/files/cards.csv";
|
||||||
|
String fileCardsImgFront="/home/lorenzo/dev/Mesos2/src/main/resources/files/Cards_total_front_PROMO.pdf";
|
||||||
|
String fileCardsImgCover="/home/lorenzo/dev/Mesos2/src/main/resources/files/Cards_total_back_PROMO.pdf";
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
File fileFront = new File(fileCardsImgFront);
|
||||||
|
File fileBack = new File(fileCardsImgCover);
|
||||||
|
|
||||||
|
documentFront = PDDocument.load(fileFront);
|
||||||
|
pdfRendererFront = new PDFRenderer(documentFront);
|
||||||
|
documentBack = PDDocument.load(fileBack);
|
||||||
|
pdfRendererBack = new PDFRenderer(documentBack);
|
||||||
|
|
||||||
|
} catch ( IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
game.newGame(fileCards);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
primaryStage.setTitle("Tavolo da Gioco - Board & Players");
|
||||||
|
|
||||||
|
|
||||||
|
btnShuffle = new Button("Rimescola & Distribuisci");
|
||||||
|
btnShuffle.setDisable(true);
|
||||||
|
|
||||||
|
|
||||||
|
btnShuffle.setOnAction(e -> distributeAll(game));
|
||||||
|
|
||||||
|
HBox topMenu = new HBox(15, btnShuffle);
|
||||||
|
topMenu.setAlignment(Pos.CENTER);
|
||||||
|
topMenu.setPadding(new Insets(10));
|
||||||
|
|
||||||
|
topRow = createRowContainer();
|
||||||
|
centerRow = createRowContainer();
|
||||||
|
bottomRow = createRowContainer();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// NUOVO: Inizializza l'area giocatori
|
||||||
|
playersArea = new HBox(30);
|
||||||
|
playersArea.setAlignment(Pos.CENTER);
|
||||||
|
playersArea.setPadding(new Insets(20));
|
||||||
|
|
||||||
|
distributeAll(game);
|
||||||
|
VBox tableArea = new VBox(20,
|
||||||
|
new Label("TOP"), topRow,
|
||||||
|
new Label("CENTER "), centerRow,
|
||||||
|
new Label("DOWN"), bottomRow,
|
||||||
|
new Label("--- SITUAZIONE GIOCATORI ---"), playersArea
|
||||||
|
);
|
||||||
|
tableArea.setAlignment(Pos.CENTER);
|
||||||
|
tableArea.setPadding(new Insets(20));
|
||||||
|
|
||||||
|
ScrollPane scrollPane = new ScrollPane(tableArea);
|
||||||
|
scrollPane.setFitToWidth(true);
|
||||||
|
|
||||||
|
BorderPane root = new BorderPane();
|
||||||
|
root.setTop(topMenu);
|
||||||
|
root.setCenter(scrollPane);
|
||||||
|
|
||||||
|
Scene scene = new Scene(root, 1300, 900);
|
||||||
|
primaryStage.setScene(scene);
|
||||||
|
primaryStage.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private HBox createRowContainer() {
|
||||||
|
HBox row = new HBox(10);
|
||||||
|
row.setAlignment(Pos.CENTER);
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void distributeAll(Game game) {
|
||||||
|
if (documentFront == null) return;
|
||||||
|
|
||||||
|
topRow.getChildren().clear();
|
||||||
|
centerRow.getChildren().clear();
|
||||||
|
bottomRow.getChildren().clear();
|
||||||
|
playersArea.getChildren().clear();
|
||||||
|
|
||||||
|
List<Integer> deck = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 118; i++) deck.add(i);
|
||||||
|
Collections.shuffle(deck);
|
||||||
|
|
||||||
|
// Distribuisce sul tavolo le prime 21 carte
|
||||||
|
int index = 0;
|
||||||
|
index = populateTable(topRow, deck, index, 8);
|
||||||
|
index = populateTable(centerRow, deck, index, 6);
|
||||||
|
index = populateTable(bottomRow, deck, index, 7);
|
||||||
|
|
||||||
|
// NUOVO: Simula 2 giocatori con le carte rimanenti
|
||||||
|
Random rand = new Random();
|
||||||
|
List<Player> players =game.getPlayers();
|
||||||
|
|
||||||
|
// Assegna 5 carte a caso a ogni giocatore e simula il loro Tipo
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (Player p : players) {
|
||||||
|
for (int j=0;j<4;j++){
|
||||||
|
int idx = rand.nextInt(game.getGameBoard().getCardDeck().getTribeDeck().size());
|
||||||
|
Card c = game.getGameBoard().getCardDeck().drawTribeOne();
|
||||||
|
p.getPlayerTribe().addCharacter((CharacterCard) c);
|
||||||
|
}
|
||||||
|
for (int j=0;j<2;j++){
|
||||||
|
int idx = rand.nextInt(game.getGameBoard().getCardDeck().getBuildingDeck(Era.I).size());
|
||||||
|
Card c = game.getGameBoard().getCardDeck().drawBuildingOne(Era.I);
|
||||||
|
p.getPlayerTribe().addBuilding((BuildingCard) c);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renderizza i giocatori nella UI
|
||||||
|
renderPlayers(players);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int populateTable(HBox row, List<Integer> deck, int startIndex, int amount) {
|
||||||
|
for (int i = 0; i < amount; i++) {
|
||||||
|
if (startIndex >= deck.size()) break;
|
||||||
|
ImageView card = createCardImage(deck.get(startIndex), 150, true); // Altezza 150px
|
||||||
|
if (card != null) row.getChildren().add(card);
|
||||||
|
startIndex++;
|
||||||
|
}
|
||||||
|
return startIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- NUOVO: RENDERIZZAZIONE GIOCATORI E RAGGRUPPAMENTO (JAVA 8) ---
|
||||||
|
private void renderPlayers(List<Player> players) {
|
||||||
|
for (Player player : players) {
|
||||||
|
VBox playerBox = new VBox(10);
|
||||||
|
playerBox.setPadding(new Insets(15));
|
||||||
|
playerBox.setStyle("-fx-border-color: #555; -fx-border-width: 2; -fx-border-radius: 10; -fx-background-color: #f9f9f9; -fx-background-radius: 10;");
|
||||||
|
|
||||||
|
// Intestazione Giocatore
|
||||||
|
Label nameLbl = new Label(player.getNickname());
|
||||||
|
nameLbl.setFont(Font.font("System", FontWeight.BOLD, 16));
|
||||||
|
|
||||||
|
// Risorse Cibo e Soldi
|
||||||
|
Label statsLbl = new Label("🍖 Cibo: " + player.getFoodTokens() + " | 💰 Money: " + player.getPrestigePoints());
|
||||||
|
statsLbl.setTextFill(Color.DARKRED);
|
||||||
|
statsLbl.setFont(Font.font("System", FontWeight.BOLD, 14));
|
||||||
|
|
||||||
|
playerBox.getChildren().addAll(nameLbl, statsLbl);
|
||||||
|
|
||||||
|
// JAVA 8 MAGIA: Raggruppa le carte del giocatore per Tipo!
|
||||||
|
Map<CharacterType, List<CharacterCard>> groupedCards = player.getPlayerTribe().getCharacters().stream()
|
||||||
|
.collect(Collectors.groupingBy(CharacterCard::getCharacterType));
|
||||||
|
|
||||||
|
// Itera sui gruppi creati e genera la grafica
|
||||||
|
groupedCards.forEach((type, cardsOfType) -> {
|
||||||
|
Label typeLbl = new Label("Tipo: " + type.name() + " (" + cardsOfType.size() + ")");
|
||||||
|
typeLbl.setFont(Font.font("System", FontWeight.NORMAL, 12));
|
||||||
|
|
||||||
|
HBox cardImagesRow = new HBox(5);
|
||||||
|
for (Card c : cardsOfType) {
|
||||||
|
// Carte più piccole (90px) per l'area giocatore
|
||||||
|
ImageView img = createCardImage(c.getCardId(), 90, true);
|
||||||
|
if (img != null) cardImagesRow.getChildren().add(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
playerBox.getChildren().addAll(typeLbl, cardImagesRow);
|
||||||
|
});
|
||||||
|
|
||||||
|
playersArea.getChildren().add(playerBox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImageView createCardImage(int pageIndex, int height, boolean front) {
|
||||||
|
try {
|
||||||
|
BufferedImage bim =null;
|
||||||
|
if (front) bim = pdfRendererFront.renderImageWithDPI(pageIndex, 100);
|
||||||
|
else bim = pdfRendererBack.renderImageWithDPI(pageIndex, 100);
|
||||||
|
|
||||||
|
Image fxImage = SwingFXUtils.toFXImage(bim, null);
|
||||||
|
ImageView imageView = new ImageView(fxImage);
|
||||||
|
imageView.setFitHeight(height);
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
|
||||||
|
return imageView;
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("Errore rendering pagina {}", pageIndex, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() throws Exception {
|
||||||
|
if (documentFront != null) documentFront.close();
|
||||||
|
super.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
launch(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
159
src/main/java/org/example/mesosll07/DeckViewerFX.java
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
package org.example.mesosll07;
|
||||||
|
|
||||||
|
import Server.Automaton.Game;
|
||||||
|
import Server.Player;
|
||||||
|
import Server.TotemColor;
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DeckViewerFX extends Application {
|
||||||
|
|
||||||
|
private PDDocument document;
|
||||||
|
private PDFRenderer pdfRenderer;
|
||||||
|
private int currentIndex = 0;
|
||||||
|
private int totalCards = 0;
|
||||||
|
|
||||||
|
private ImageView cardImageView;
|
||||||
|
private Label pageLabel;
|
||||||
|
private Button btnPrev;
|
||||||
|
private Button btnNext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Stage primaryStage) {
|
||||||
|
|
||||||
|
List<Player> players = new ArrayList<>();
|
||||||
|
players.add(new Player("Yoshi", TotemColor.YELLOW));
|
||||||
|
players.add(new Player("Pippo", TotemColor.PURPLE));
|
||||||
|
players.add(new Player("John", TotemColor.RED));
|
||||||
|
players.add(new Player("Baggio", TotemColor.BLUE));
|
||||||
|
players.add(new Player("Gino", TotemColor.GREEN));
|
||||||
|
|
||||||
|
Game game = new Game(players);
|
||||||
|
game.newGame("/home/lorenzo/dev/Mesos2/src/main/resources/files/cards.csv");
|
||||||
|
|
||||||
|
primaryStage.setTitle("Visualizzatore Mazzo di Carte (PDF)");
|
||||||
|
|
||||||
|
// Componente per mostrare la carta
|
||||||
|
cardImageView = new ImageView();
|
||||||
|
cardImageView.setFitHeight(500); // Altezza fissa, larghezza in proporzione
|
||||||
|
cardImageView.setPreserveRatio(true);
|
||||||
|
cardImageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.5), 10, 0, 0, 0);");
|
||||||
|
|
||||||
|
// Controlli UI
|
||||||
|
btnPrev = new Button("⬅ Carta Precedente");
|
||||||
|
btnNext = new Button("Carta Successiva ➡");
|
||||||
|
pageLabel = new Label("Nessun mazzo caricato");
|
||||||
|
Button btnLoad = new Button("Carica PDF Mazzo");
|
||||||
|
|
||||||
|
// Azioni dei pulsanti
|
||||||
|
btnLoad.setOnAction(e -> loadPdf(primaryStage));
|
||||||
|
btnPrev.setOnAction(e -> showCard(currentIndex - 1));
|
||||||
|
btnNext.setOnAction(e -> showCard(currentIndex + 1));
|
||||||
|
|
||||||
|
updateButtons();
|
||||||
|
|
||||||
|
// Layout
|
||||||
|
HBox controls = new HBox(15, btnPrev, pageLabel, btnNext);
|
||||||
|
controls.setAlignment(Pos.CENTER);
|
||||||
|
|
||||||
|
VBox topBox = new VBox(10, btnLoad);
|
||||||
|
topBox.setAlignment(Pos.CENTER);
|
||||||
|
topBox.setPadding(new Insets(10));
|
||||||
|
|
||||||
|
BorderPane root = new BorderPane();
|
||||||
|
root.setTop(topBox);
|
||||||
|
root.setCenter(cardImageView);
|
||||||
|
root.setBottom(controls);
|
||||||
|
BorderPane.setMargin(cardImageView, new Insets(20));
|
||||||
|
BorderPane.setMargin(controls, new Insets(20));
|
||||||
|
|
||||||
|
Scene scene = new Scene(root, 600, 700);
|
||||||
|
primaryStage.setScene(scene);
|
||||||
|
primaryStage.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPdf(Stage stage) {
|
||||||
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
fileChooser.setTitle("Seleziona il PDF del mazzo di carte");
|
||||||
|
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("File PDF", "*.pdf"));
|
||||||
|
File file = fileChooser.showOpenDialog(stage);
|
||||||
|
|
||||||
|
if (file != null) {
|
||||||
|
try {
|
||||||
|
// Chiude il documento precedente se esiste
|
||||||
|
if (document != null) {
|
||||||
|
document.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carica il nuovo PDF
|
||||||
|
document = PDDocument.load(file);
|
||||||
|
pdfRenderer = new PDFRenderer(document);
|
||||||
|
totalCards = document.getNumberOfPages();
|
||||||
|
|
||||||
|
// Mostra la prima carta
|
||||||
|
showCard(0);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
pageLabel.setText("Errore nel caricamento del file!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showCard(int index) {
|
||||||
|
if (document == null || index < 0 || index >= totalCards) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Renderizza la pagina del PDF in un'immagine BufferedImage (DPI 150 per buona qualità)
|
||||||
|
BufferedImage bim = pdfRenderer.renderImageWithDPI(index, 150);
|
||||||
|
|
||||||
|
// Converte l'immagine di AWT (Swing) in un'immagine JavaFX
|
||||||
|
Image fxImage = SwingFXUtils.toFXImage(bim, null);
|
||||||
|
|
||||||
|
cardImageView.setImage(fxImage);
|
||||||
|
currentIndex = index;
|
||||||
|
pageLabel.setText("Carta " + (currentIndex + 1) + " di " + totalCards);
|
||||||
|
|
||||||
|
updateButtons();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateButtons() {
|
||||||
|
btnPrev.setDisable(document == null || currentIndex == 0);
|
||||||
|
btnNext.setDisable(document == null || currentIndex == totalCards - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() throws Exception {
|
||||||
|
// Importante: chiudere il documento per liberare la memoria quando l'app si chiude
|
||||||
|
if (document != null) {
|
||||||
|
document.close();
|
||||||
|
}
|
||||||
|
super.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
launch(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/main/java/org/example/mesosll07/HelloApplication.java
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package org.example.mesosll07;
|
||||||
|
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class HelloApplication extends Application {
|
||||||
|
@Override
|
||||||
|
public void start(Stage stage) throws IOException {
|
||||||
|
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
|
||||||
|
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
|
||||||
|
stage.setTitle("Hello!");
|
||||||
|
stage.setScene(scene);
|
||||||
|
stage.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/main/java/org/example/mesosll07/HelloController.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package org.example.mesosll07;
|
||||||
|
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
|
||||||
|
public class HelloController {
|
||||||
|
@FXML
|
||||||
|
private Label welcomeText;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
protected void onHelloButtonClick() {
|
||||||
|
welcomeText.setText("Welcome to JavaFX Application!");
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/main/java/org/example/mesosll07/Launcher.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package org.example.mesosll07;
|
||||||
|
|
||||||
|
import javafx.application.Application;
|
||||||
|
|
||||||
|
public class Launcher {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Application.launch(HelloApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
src/main/resources/files/Cards_total_back_PROMO.pdf
Normal file
BIN
src/main/resources/files/Cards_total_front_PROMO.pdf
Normal file
BIN
src/main/resources/files/Start_2P.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
src/main/resources/files/Start_3P.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
src/main/resources/files/Start_4P.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
src/main/resources/files/Start_5P.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
117
src/main/resources/files/cards.csv
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
C;1;0;I;HUNTER;1;0
|
||||||
|
C;2;0;I;HUNTER;1;0
|
||||||
|
C;3;0;I;HUNTER;0;0
|
||||||
|
C;4;3;I;HUNTER;0;0
|
||||||
|
C;5;3;I;HUNTER;0;0
|
||||||
|
C;6;0;I;BUILDER;1;3
|
||||||
|
C;7;0;I;BUILDER;2;0
|
||||||
|
C;8;5;I;BUILDER;2;1
|
||||||
|
C;9;0;I;BUILDER;1;2
|
||||||
|
C;10;0;I;GATHERER;3;0
|
||||||
|
C;11;0;I;GATHERER;3;0
|
||||||
|
C;12;3;I;GATHERER;3;0
|
||||||
|
C;13;5;I;GATHERER;3;0
|
||||||
|
C;14;0;I;ARTIST;0;0
|
||||||
|
C;15;0;I;ARTIST;0;0
|
||||||
|
C;16;0;I;ARTIST;0;0
|
||||||
|
C;17;3;I;ARTIST;0;0
|
||||||
|
C;18;4;I;ARTIST;0;0
|
||||||
|
C;19;0;I;INVENTOR;8;0
|
||||||
|
C;20;0;I;INVENTOR;0;0
|
||||||
|
C;21;0;I;INVENTOR;1;0
|
||||||
|
C;22;0;I;INVENTOR;9;0
|
||||||
|
C;23;4;I;INVENTOR;5;0
|
||||||
|
C;24;4;I;INVENTOR;7;0
|
||||||
|
C;25;4;I;INVENTOR;4;0
|
||||||
|
C;26;5;I;SHAMAN;2;0
|
||||||
|
C;27;0;I;SHAMAN;2;0
|
||||||
|
C;28;0;I;SHAMAN;1;0
|
||||||
|
C;29;4;I;SHAMAN;1;0
|
||||||
|
C;30;0;II;HUNTER;0;0
|
||||||
|
C;31;0;II;HUNTER;0;0
|
||||||
|
C;32;3;II;HUNTER;1;0
|
||||||
|
C;33;0;II;HUNTER;1;0
|
||||||
|
C;34;4;II;HUNTER;1;0
|
||||||
|
C;35;5;II;HUNTER;0;0
|
||||||
|
C;36;0;II;BUILDER;1;4
|
||||||
|
C;37;0;II;BUILDER;2;1
|
||||||
|
C;38;3;II;BUILDER;1;2
|
||||||
|
C;39;0;II;BUILDER;2;3
|
||||||
|
C;40;0;II;GATHERER;3;0
|
||||||
|
C;41;3;II;GATHERER;3;0
|
||||||
|
C;42;4;II;GATHERER;3;0
|
||||||
|
C;43;5;II;GATHERER;3;0
|
||||||
|
C;44;3;II;ARTIST;0;0
|
||||||
|
C;45;0;II;ARTIST;0;0
|
||||||
|
C;46;0;II;ARTIST;0;0
|
||||||
|
C;47;0;II;ARTIST;0;0
|
||||||
|
C;48;0;II;INVENTOR;0;0
|
||||||
|
C;49;4;II;INVENTOR;0;0
|
||||||
|
C;50;0;II;INVENTOR;0;0
|
||||||
|
C;51;0;II;INVENTOR;0;0
|
||||||
|
C;52;0;II;INVENTOR;0;0
|
||||||
|
C;53;0;II;INVENTOR;0;0
|
||||||
|
C;54;0;II;SHAMAN;2;0
|
||||||
|
C;55;0;II;SHAMAN;2;0
|
||||||
|
C;56;5;II;SHAMAN;1;0
|
||||||
|
C;57;5;II;SHAMAN;2;0
|
||||||
|
C;58;5;III;HUNTER;1;0
|
||||||
|
C;59;0;III;HUNTER;0;0
|
||||||
|
C;60;0;III;HUNTER;0;0
|
||||||
|
C;61;0;III;HUNTER;1;0
|
||||||
|
C;62;0;III;BUILDER;1;5
|
||||||
|
C;63;0;III;BUILDER;2;3
|
||||||
|
C;64;5;III;BUILDER;1;4
|
||||||
|
C;65;0;III;BUILDER;2;2
|
||||||
|
C;66;5;III;GATHERER;3;0
|
||||||
|
C;67;4;III;GATHERER;3;0
|
||||||
|
C;68;0;III;GATHERER;3;0
|
||||||
|
C;69;5;III;ARTIST;0;0
|
||||||
|
C;70;0;III;ARTIST;0;0
|
||||||
|
C;71;0;III;ARTIST;0;0
|
||||||
|
C;72;0;III;ARTIST;0;0
|
||||||
|
C;73;4;III;INVENTOR;0;0
|
||||||
|
C;74;3;III;INVENTOR;0;0
|
||||||
|
C;75;3;III;INVENTOR;0;0
|
||||||
|
C;76;0;III;INVENTOR;0;0
|
||||||
|
C;77;0;III;INVENTOR;0;0
|
||||||
|
C;78;0;III;INVENTOR;0;0
|
||||||
|
C;79;0;III;INVENTOR;0;0
|
||||||
|
C;80;3;III;SHAMAN;2;0
|
||||||
|
C;81;0;III;SHAMAN;3;0
|
||||||
|
C;82;0;III;SHAMAN;2;0
|
||||||
|
C;83;0;III;SHAMAN;3;0
|
||||||
|
C;84;4;III;SHAMAN;2;0
|
||||||
|
E;85;0;I;HUNT;1;1
|
||||||
|
E;86;0;I;SUSTAINMENT;1;1
|
||||||
|
E;87;0;I;SHAMANIC_RITUAL;5;3
|
||||||
|
E;88;0;I;CAVE_PAINTINGS;2;1
|
||||||
|
E;89;0;II;HUNT;1;2
|
||||||
|
E;90;0;II;SUSTAINMENT;1;2
|
||||||
|
E;91;0;II;SHAMANIC_RITUAL;10;5
|
||||||
|
E;92;0;II;CAVE_PAINTINGS;2;2
|
||||||
|
E;93;0;III;HUNT;1;3
|
||||||
|
E;94;0;III;CAVE_PAINTINGS;2;3
|
||||||
|
E;95;0;FINAL;SUSTAINMENT;1;3
|
||||||
|
E;96;0;FINAL;SHAMANIC_RITUAL;15;7
|
||||||
|
B;97;0;I;4;3;FOOD_FOR_SIX
|
||||||
|
B;98;0;I;4;4;SUSTAIN_DISCOUNT
|
||||||
|
B;99;0;I;5;3;SUSTAIN_DISCOUNT
|
||||||
|
B;100;0;I;5;2;SHAMAN_NO_LOSS
|
||||||
|
B;101;0;I;3;3;BONUS_FOOD_ENDTURN
|
||||||
|
B;102;0;I;3;4;FOOD_PER_INVENTORS
|
||||||
|
B;103;0;II;7;0;SHAMAN_DOUBLE_POINTS
|
||||||
|
B;104;0;II;6;4;SHAMAN_BONUS
|
||||||
|
B;105;0;II;7;4;SUSTAIN_DISCOUNT
|
||||||
|
B;106;0;II;7;2;HUNT_BONUS
|
||||||
|
B;107;0;II;6;4;ENDGAME_BUILDER_BONUS
|
||||||
|
B;108;0;II;5;6;PAINTING_FOOD_BONUS
|
||||||
|
B;109;0;II;5;6;ENDGAME_FOR_SIX
|
||||||
|
B;110;0;III;8;8;ENDGAME_BONUS_CHARACTER
|
||||||
|
B;111;0;III;7;6;ENDGAME_BONUS_CHARACTER
|
||||||
|
B;112;0;III;7;4;ENDGAME_BONUS_CHARACTER
|
||||||
|
B;113;0;III;6;3;ENDGAME_BONUS_CHARACTER
|
||||||
|
B;114;0;III;7;4;ENDGAME_BONUS_CHARACTER
|
||||||
|
B;115;0;III;6;6;ENDGAME_BONUS_CHARACTER
|
||||||
|
B;116;0;III;9;3;EXTRA_DRAW
|
||||||
|
B;117;0;III;10;0;ENDGAME_BONUS_POINTS
|
||||||
|
BIN
src/main/resources/files/offeringA.png
Normal file
|
After Width: | Height: | Size: 834 KiB |
BIN
src/main/resources/files/offeringB.png
Normal file
|
After Width: | Height: | Size: 858 KiB |
BIN
src/main/resources/files/offeringC.png
Normal file
|
After Width: | Height: | Size: 927 KiB |
BIN
src/main/resources/files/offeringD.png
Normal file
|
After Width: | Height: | Size: 878 KiB |
BIN
src/main/resources/files/offeringE.png
Normal file
|
After Width: | Height: | Size: 876 KiB |
BIN
src/main/resources/files/offeringF.png
Normal file
|
After Width: | Height: | Size: 826 KiB |
BIN
src/main/resources/files/offeringG.png
Normal file
|
After Width: | Height: | Size: 904 KiB |
22
src/main/resources/log4j2.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Configuration status="WARN">
|
||||||
|
<Appenders>
|
||||||
|
<!-- Appender per scrivere sulla Console -->
|
||||||
|
<Console name="Console" target="SYSTEM_OUT">
|
||||||
|
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/>
|
||||||
|
</Console>
|
||||||
|
|
||||||
|
<!-- Appender per scrivere su un file chiamato app.log -->
|
||||||
|
<File name="LogFile" fileName="logs/mesos2.log">
|
||||||
|
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/>
|
||||||
|
</File>
|
||||||
|
</Appenders>
|
||||||
|
|
||||||
|
<Loggers>
|
||||||
|
<!-- Configurazione base: registra tutto ciò che è livello INFO o superiore -->
|
||||||
|
<Root level="info">
|
||||||
|
<AppenderRef ref="Console"/>
|
||||||
|
<AppenderRef ref="LogFile"/>
|
||||||
|
</Root>
|
||||||
|
</Loggers>
|
||||||
|
</Configuration>
|
||||||
16
src/main/resources/org/example/mesosll07/hello-view.fxml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="org.example.mesosll07.HelloController">
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
|
||||||
|
</padding>
|
||||||
|
|
||||||
|
<Label fx:id="welcomeText"/>
|
||||||
|
<Button text="Hello!" onAction="#onHelloButtonClick"/>
|
||||||
|
</VBox>
|
||||||
36
src/test/java/Server/Automaton/GameTest.java
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package Server.Automaton;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class GameTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void newGame() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void placeTotem() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolveCardAction() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void extraDrawAction() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void skipExtraDraw() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void endGame() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getPendingActions() {
|
||||||
|
}
|
||||||
|
}
|
||||||
205
src/test/java/Server/BuildingManagerTest.java
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import Server.Cards.BuildingCard;
|
||||||
|
|
||||||
|
import Server.Cards.CardDeck;
|
||||||
|
import Server.Cards.CharacterCard;
|
||||||
|
import Server.Cards.EventCard;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class BuildingManagerTest {
|
||||||
|
|
||||||
|
static List<Player> players = new ArrayList<>();
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void setUp(){
|
||||||
|
BuildingManager buildingManager = new BuildingManager();
|
||||||
|
|
||||||
|
Player winner = new Player("Winner", TotemColor.YELLOW);
|
||||||
|
winner.addFood(10);
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;1;2;I;SHAMAN;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;2;2;I;SHAMAN;2;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;3;2;I;BUILDER;3;1"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;14;0;I;ARTIST;0;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;15;0;I;ARTIST;0;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;21;0;I;ARTIST;1;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;22;0;I;INVENTOR;9;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;23;4;I;INVENTOR;5;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;43;0;I;HUNTER;0;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;44;3;I;HUNTER;0;0"));
|
||||||
|
players.add(winner);
|
||||||
|
|
||||||
|
Player medium = new Player("Medium", TotemColor.RED);
|
||||||
|
medium.addFood(10);
|
||||||
|
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;56;2;I;SHAMAN;3;0"));
|
||||||
|
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;34;2;I;BUILDER;3;1"));
|
||||||
|
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;32;3;I;GATHERER;3;0"));
|
||||||
|
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;33;0;I;ARTIST;0;0"));
|
||||||
|
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;45;3;I;HUNTER;0;0"));
|
||||||
|
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;72;0;I;ARTIST;9;0"));
|
||||||
|
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;73;4;I;INVENTOR;5;0"));
|
||||||
|
players.add(medium);
|
||||||
|
|
||||||
|
Player loser = new Player("Loser", TotemColor.BLUE);
|
||||||
|
loser.addFood(2);
|
||||||
|
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;54;2;I;SHAMAN;3;0"));
|
||||||
|
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;36;2;I;BUILDER;3;1"));
|
||||||
|
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;82;0;I;INVENTOR;9;0"));
|
||||||
|
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;93;4;I;INVENTOR;5;0"));
|
||||||
|
players.add(loser);
|
||||||
|
|
||||||
|
CardDeck cardDeck = new CardDeck();
|
||||||
|
cardDeck.setForNPlayer("./src/test/resources/files/only_building_deck.csv", 5);
|
||||||
|
|
||||||
|
//for(BuildingCard c: cardDeck.drawBuilding(30)){
|
||||||
|
// buildingManager.addActiveBuilding(c, players.getFirst());
|
||||||
|
//}
|
||||||
|
//buildingManager.addActiveBuilding(BuildingCard.parsRow("B;5;3;III;6;2;SHAMAN_NO_LOSS"), players.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void foodForSix() {
|
||||||
|
int initialFood = players.getFirst().getFoodTokens();
|
||||||
|
CharacterCard cardForSix = CharacterCard.parsRow("C;39;2;I;BUILDER;3;1");
|
||||||
|
BuildingManager.foodForSix(players.getFirst(), cardForSix);
|
||||||
|
assertEquals(initialFood + 6, players.getFirst().getFoodTokens());
|
||||||
|
|
||||||
|
players.getFirst().getPlayerTribe().addCharacter(cardForSix);
|
||||||
|
BuildingManager.foodForSix(players.getFirst(), cardForSix);
|
||||||
|
assertEquals(initialFood + 6, players.getFirst().getFoodTokens());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sustainDiscount() {
|
||||||
|
|
||||||
|
EventCard sustainment = EventCard.parsRow("E;1;0;I;SUSTAINMENT;2;0");
|
||||||
|
ArrayList<Integer> target = new ArrayList<>();
|
||||||
|
target.add(players.getFirst().getFoodTokens());
|
||||||
|
target.add(players.getFirst().getPrestigePoints());
|
||||||
|
target.add(players.get(1).getFoodTokens() - 4);
|
||||||
|
target.add(players.get(1).getPrestigePoints());
|
||||||
|
target.add(0);
|
||||||
|
target.add(players.get(2).getPrestigePoints()-4);
|
||||||
|
assertEquals(target, EventsSolver.sustainment(sustainment, players));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shamanicBuildings() {
|
||||||
|
EventCard shamanicRitual = EventCard.parsRow("E;1;0;I;SHAMANIC_RITUAL;10;5");
|
||||||
|
ArrayList<Integer> target = new ArrayList<>();
|
||||||
|
target.add(players.getFirst().getPrestigePoints() + 20);
|
||||||
|
target.add(players.get(1).getPrestigePoints()-5);
|
||||||
|
target.add(players.get(2).getPrestigePoints());
|
||||||
|
assertEquals(target, EventsSolver.shamanicRitual(shamanicRitual, players));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void bonusEndTurn() {
|
||||||
|
assertEquals(1, BuildingManager.bonusEndTurn(players.getFirst(), 1));
|
||||||
|
assertEquals(0, BuildingManager.bonusEndTurn(players.getFirst(), 0));
|
||||||
|
assertEquals(0, BuildingManager.bonusEndTurn(players.getFirst(), -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void foodPerInventors() {
|
||||||
|
int initialFood = players.getFirst().getFoodTokens();
|
||||||
|
CharacterCard builder = CharacterCard.parsRow("C;57;2;I;INVENTOR;9;0");
|
||||||
|
BuildingManager.foodPerInventors(players.getFirst(), builder);
|
||||||
|
assertEquals(initialFood + 3, players.getFirst().getFoodTokens());
|
||||||
|
|
||||||
|
players.getFirst().getPlayerTribe().addCharacter(builder);
|
||||||
|
BuildingManager.foodPerInventors(players.getFirst(), builder);
|
||||||
|
assertEquals(initialFood+3, players.getFirst().getFoodTokens());
|
||||||
|
|
||||||
|
initialFood = players.get(2).getFoodTokens();
|
||||||
|
BuildingManager.foodPerInventors(players.get(2), builder);
|
||||||
|
assertEquals(initialFood, players.get(2).getFoodTokens());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shamanBonus() {
|
||||||
|
assertEquals(3, BuildingManager.shamanBonus(players.getFirst()));
|
||||||
|
assertEquals(0, BuildingManager.shamanBonus(players.get(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void hunterBonus() {
|
||||||
|
EventCard hunt = EventCard.parsRow("E;1;0;I;HUNT;2;0");
|
||||||
|
ArrayList<Integer> target = new ArrayList<>();
|
||||||
|
target.add(players.getFirst().getFoodTokens()+4);
|
||||||
|
target.add(players.getFirst().getPrestigePoints()+6);
|
||||||
|
target.add(players.get(1).getFoodTokens()+1);
|
||||||
|
target.add(players.get(1).getPrestigePoints()+2);
|
||||||
|
target.add(players.get(2).getFoodTokens());
|
||||||
|
target.add(players.get(2).getPrestigePoints());
|
||||||
|
assertEquals(target, EventsSolver.hunt(hunt, players));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void endgameBuilderBonus() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void paintingFoodBonus() {
|
||||||
|
int winnerInitial = players.getFirst().getFoodTokens();
|
||||||
|
int middleInitial = players.get(1).getFoodTokens();
|
||||||
|
|
||||||
|
EventCard cavePainting = EventCard.parsRow("E;4;4;FINAL;CAVE_PAINTINGS;2;2");
|
||||||
|
EventsSolver.cavePaintings(cavePainting, players);
|
||||||
|
assertEquals(winnerInitial + 3 , players.getFirst().getFoodTokens());
|
||||||
|
assertEquals(middleInitial, players.get(1).getFoodTokens());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void endgameForSix() {
|
||||||
|
int winnerInitial = players.getFirst().getPrestigePoints();
|
||||||
|
int middleInitial = players.get(1).getPrestigePoints();
|
||||||
|
|
||||||
|
BuildingManager.endgameForSix(players.getFirst());
|
||||||
|
BuildingManager.endgameForSix(players.get(1));
|
||||||
|
assertEquals(winnerInitial + 6 , players.getFirst().getPrestigePoints());
|
||||||
|
assertEquals(middleInitial, players.get(1).getPrestigePoints());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void endgameBonusCharacter() {
|
||||||
|
int winnerInitial = players.getFirst().getPrestigePoints();
|
||||||
|
int middleInitial = players.get(1).getPrestigePoints();
|
||||||
|
|
||||||
|
BuildingManager.endgameBonusCharacter(players.getFirst());
|
||||||
|
BuildingManager.endgameBonusCharacter(players.get(1));
|
||||||
|
assertEquals(winnerInitial + 9 , players.getFirst().getPrestigePoints());
|
||||||
|
assertEquals(middleInitial, players.get(1).getPrestigePoints());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void extraDraw() {
|
||||||
|
assertTrue(BuildingManager.extraDraw(players.getFirst()));
|
||||||
|
assertFalse(BuildingManager.extraDraw(players.get(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void endgameBonusPoints() {
|
||||||
|
int initialWinner = players.getFirst().getPrestigePoints();
|
||||||
|
int initialMedium = players.get(1).getPrestigePoints();
|
||||||
|
|
||||||
|
BuildingManager.endgameBonusPoints(players.getFirst());
|
||||||
|
BuildingManager.endgameBonusPoints(players.get(1));
|
||||||
|
assertEquals(initialWinner+25, players.getFirst().getPrestigePoints());
|
||||||
|
assertEquals(initialMedium, players.get(1).getPrestigePoints());
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/test/java/Server/Cards/BuildingCardTest.java
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package Server.Cards;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static Server.Era.I;
|
||||||
|
import static Server.Era.II;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class BuildingCardTest {
|
||||||
|
|
||||||
|
|
||||||
|
//Test if the method parsRow is able to create a correct BuildingCard
|
||||||
|
@Test
|
||||||
|
void registerBuildingCard() {
|
||||||
|
BuildingCard buildingCard = BuildingCard.parsRow("B;1;2;I;3;1;SHAMAN_BONUS");
|
||||||
|
|
||||||
|
assertEquals(1, buildingCard.getCardId());
|
||||||
|
assertEquals(2, buildingCard.getForMinPlayer());
|
||||||
|
assertEquals(I, buildingCard.getEra());
|
||||||
|
assertEquals(3, buildingCard.getCost());
|
||||||
|
assertEquals(1, buildingCard.getCardId());
|
||||||
|
assertEquals(Trigger.SHAMAN_BONUS, buildingCard.getAbilityTrigger());
|
||||||
|
assertNull(buildingCard.getEffectTarget());
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerBuildingCardWithTarget() {
|
||||||
|
BuildingCard buildingCard = BuildingCard.parsRow("B;17;5;II;6;2;SUSTAIN_DISCOUNT;ARTIST");
|
||||||
|
|
||||||
|
assertEquals(17, buildingCard.getCardId());
|
||||||
|
assertEquals(5, buildingCard.getForMinPlayer());
|
||||||
|
assertEquals(II, buildingCard.getEra());
|
||||||
|
assertEquals(6, buildingCard.getCost());
|
||||||
|
assertEquals(2, buildingCard.getCardId());
|
||||||
|
assertEquals(Trigger.SUSTAIN_DISCOUNT, buildingCard.getAbilityTrigger());
|
||||||
|
assertEquals(CharacterType.ARTIST, buildingCard.getEffectTarget());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
103
src/test/java/Server/Cards/CardDeckTest.java
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package Server.Cards;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import Server.Era;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
class CardDeckTest {
|
||||||
|
|
||||||
|
//Load 2 cards from CSV to cardDeck
|
||||||
|
@Test
|
||||||
|
void loadOnlyCharacter(){
|
||||||
|
CardDeck cardDeck = new CardDeck();
|
||||||
|
cardDeck.setForNPlayer("./src/test/resources/files/only_character_deck.csv", 4);
|
||||||
|
assertEquals(2, cardDeck.getTribeDeck().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
//First create a new CardDeck filled with a 14 cards then he recreates said deck
|
||||||
|
// with a different set of cards to emulate a new game
|
||||||
|
@Test
|
||||||
|
void doubleLoad(){
|
||||||
|
CardDeck cardDeck = new CardDeck();
|
||||||
|
|
||||||
|
cardDeck.setForNPlayer("./src/test/resources/files/only_building_deck.csv", 4);
|
||||||
|
assertEquals(0, cardDeck.getTribeDeck().size());
|
||||||
|
assertEquals(6, cardDeck.getBuildingDeck(Era.I).size());
|
||||||
|
assertEquals(7, cardDeck.getBuildingDeck(Era.II).size());
|
||||||
|
assertEquals(8, cardDeck.getBuildingDeck(Era.III).size());
|
||||||
|
|
||||||
|
cardDeck.setForNPlayer("./src/test/resources/files/only_event_deck.csv", 4);
|
||||||
|
assertEquals(3, cardDeck.getTribeDeck().size());
|
||||||
|
assertEquals(0, cardDeck.getBuildingDeck(Era.I).size());
|
||||||
|
assertEquals(0, cardDeck.getBuildingDeck(Era.II).size());
|
||||||
|
assertEquals(0, cardDeck.getBuildingDeck(Era.III).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Draw 5 cards from TribeDeck and 3 from BuildingDeck, then check if they are correctly
|
||||||
|
// removed from cardDeck
|
||||||
|
@Test
|
||||||
|
void draw() {
|
||||||
|
|
||||||
|
int tribeDraw = 5;
|
||||||
|
int buildingDraw = 3;
|
||||||
|
int drawnCards = tribeDraw + buildingDraw;
|
||||||
|
|
||||||
|
CardDeck cardDeck = new CardDeck();
|
||||||
|
cardDeck.setForNPlayer("./src/test/resources/files/test_deck.csv", 5);
|
||||||
|
//int deckSize = cardDeck.getTribeDeck().size() + cardDeck.getBuildingDeck().size();
|
||||||
|
|
||||||
|
List<Card> cards = cardDeck.drawTribe(tribeDraw);
|
||||||
|
assertEquals(tribeDraw, cards.size());
|
||||||
|
//List<BuildingCard> buildingCards = cardDeck.drawBuilding(buildingDraw);
|
||||||
|
//assertEquals(buildingDraw, buildingCards.size());
|
||||||
|
|
||||||
|
//int newDeckSize = cardDeck.getTribeDeck().size() + cardDeck.getBuildingDeck().size();
|
||||||
|
//assertEquals(deckSize - drawnCards, newDeckSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
//draw 5 cards from TribeDeck
|
||||||
|
@Test
|
||||||
|
void drawTribe() {
|
||||||
|
CardDeck cardDeck = new CardDeck();
|
||||||
|
cardDeck.setForNPlayer("./src/test/resources/files/test_deck.csv", 5);
|
||||||
|
|
||||||
|
List<Card> cards = cardDeck.drawTribe(5);
|
||||||
|
assertEquals(5, cards.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
//draw 3 cards from BuildingDeck
|
||||||
|
@Test
|
||||||
|
void drawBuilding() {
|
||||||
|
CardDeck cardDeck = new CardDeck();
|
||||||
|
cardDeck.setForNPlayer("./src/test/resources/files/test_deck.csv", 5);
|
||||||
|
|
||||||
|
// List<BuildingCard> buildingCards = cardDeck.drawBuilding(3);
|
||||||
|
// assertEquals(3, buildingCards.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Try to draw 5 cards from a deck of only 3, then he returns only the remaining
|
||||||
|
//cards in the deck
|
||||||
|
@Test
|
||||||
|
void drawTooManyCards() {
|
||||||
|
CardDeck cardDeck = new CardDeck();
|
||||||
|
cardDeck.setForNPlayer("./src/test/resources/files/only_character_deck.csv", 5);
|
||||||
|
|
||||||
|
List<Card> cards = cardDeck.drawTribe(5);
|
||||||
|
assertEquals(3, cards.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shuffledDeck() {
|
||||||
|
CardDeck cardDeck = new CardDeck();
|
||||||
|
cardDeck.setForNPlayer("./src/test/resources/files/test_deck.csv", 5);
|
||||||
|
|
||||||
|
assertEquals(Era.I, cardDeck.drawTribe(1).getFirst().getEra());
|
||||||
|
assertEquals(Era.FINAL, cardDeck.drawTribe(100).getLast().getEra());
|
||||||
|
|
||||||
|
// assertEquals(Era.I, cardDeck.drawBuilding(1).getFirst().getEra());
|
||||||
|
// assertEquals(Era.III, cardDeck.drawBuilding(100).getLast().getEra());
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/test/java/Server/Cards/CharacterCardTest.java
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package Server.Cards;
|
||||||
|
|
||||||
|
import Server.Utils.LoadingCardsException;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static Server.Era.I;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class CharacterCardTest {
|
||||||
|
|
||||||
|
//Test if the method parsRow is able to create a correct CharacterCard
|
||||||
|
@Test
|
||||||
|
void parsRow() {
|
||||||
|
CharacterCard characterCard = CharacterCard.parsRow("C;1;2;I;SHAMAN;3;4");
|
||||||
|
|
||||||
|
assertEquals(1, characterCard.getCardId());
|
||||||
|
assertEquals(2, characterCard.getForMinPlayer());
|
||||||
|
assertEquals(I, characterCard.getEra());
|
||||||
|
assertEquals(CharacterType.SHAMAN, characterCard.getCharacterType());
|
||||||
|
assertEquals(3, characterCard.getIconValue());
|
||||||
|
assertEquals(4, characterCard.getPrestigePoints());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Tests if the method parsRow send an exception with the message "Not a character" when is given
|
||||||
|
//as an input the wrong type of card
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void wrongTypeOfCard(){
|
||||||
|
Exception exception = assertThrows(LoadingCardsException.class, () -> CharacterCard.parsRow("B;1;2;I;3;1;SHAMANIC_RITUAL"));
|
||||||
|
|
||||||
|
String actualMessage = exception.getMessage();
|
||||||
|
assertTrue(actualMessage.contains("Not a character card"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
34
src/test/java/Server/Cards/EventCardTest.java
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package Server.Cards;
|
||||||
|
|
||||||
|
import Server.Utils.LoadingCardsException;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static Server.Era.I;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class EventCardTest {
|
||||||
|
|
||||||
|
//Test if the method parsRow is able to create a correct EventCard
|
||||||
|
@Test
|
||||||
|
void parsRow() {
|
||||||
|
EventCard eventCard = EventCard.parsRow("E;1;2;I;CAVE_PAINTINGS;3;4");
|
||||||
|
|
||||||
|
assertEquals(1, eventCard.getCardId());
|
||||||
|
assertEquals(2, eventCard.getForMinPlayer());
|
||||||
|
assertEquals(I, eventCard.getEra());
|
||||||
|
assertEquals(Event.CAVE_PAINTINGS, eventCard.getEvent());
|
||||||
|
assertEquals(3, eventCard.getFirstValue());
|
||||||
|
assertEquals(4, eventCard.getSecondValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Tests if the method parsRow send an exception with the message "Not an EventCard" when is given
|
||||||
|
//as an input the wrong type of card
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void wrongTypeOfCard(){
|
||||||
|
Exception exception = assertThrows(LoadingCardsException.class, () -> EventCard.parsRow("B;1;2;I;3;1;SHAMANIC_RITUAL"));
|
||||||
|
|
||||||
|
String actualMessage = exception.getMessage();
|
||||||
|
assertTrue(actualMessage.contains("Not an EventCard"));
|
||||||
|
}
|
||||||
|
}
|
||||||
178
src/test/java/Server/EventsSolverTest.java
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import Server.Cards.Card;
|
||||||
|
import Server.Cards.CardDeck;
|
||||||
|
import Server.Cards.CharacterCard;
|
||||||
|
import Server.Cards.EventCard;
|
||||||
|
import Server.Utils.EventsManagerException;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class EventsSolverTest {
|
||||||
|
|
||||||
|
static List<Player> players = new ArrayList<>();
|
||||||
|
|
||||||
|
//Creating 4 players with a predefined tribe
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public static void setUp(){
|
||||||
|
BuildingManager buildingManager = new BuildingManager();
|
||||||
|
|
||||||
|
Player winner = new Player("Winner", TotemColor.YELLOW);
|
||||||
|
winner.addFood(10);
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;1;2;I;SHAMAN;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;2;2;I;SHAMAN;2;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;3;2;I;BUILDER;3;1"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;14;0;I;ARTIST;0;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;15;0;I;ARTIST;0;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;21;0;I;ARTIST;1;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;22;0;I;INVENTOR;9;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;23;4;I;INVENTOR;5;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;43;0;I;HUNTER;0;0"));
|
||||||
|
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;44;3;I;HUNTER;0;0"));
|
||||||
|
players.add(winner);
|
||||||
|
|
||||||
|
Player medium = new Player("Medium", TotemColor.RED);
|
||||||
|
medium.addFood(10);
|
||||||
|
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;56;2;I;SHAMAN;3;0"));
|
||||||
|
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;34;2;I;BUILDER;3;1"));
|
||||||
|
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;32;3;I;GATHERER;3;0"));
|
||||||
|
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;33;0;I;ARTIST;0;0"));
|
||||||
|
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;45;3;I;HUNTER;0;0"));
|
||||||
|
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;72;0;I;ARTIST;9;0"));
|
||||||
|
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;73;4;I;INVENTOR;5;0"));
|
||||||
|
players.add(medium);
|
||||||
|
|
||||||
|
Player loser = new Player("Loser", TotemColor.BLUE);
|
||||||
|
loser.addFood(2);
|
||||||
|
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;54;2;I;SHAMAN;2;0"));
|
||||||
|
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;36;2;I;BUILDER;3;1"));
|
||||||
|
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;82;0;I;INVENTOR;9;0"));
|
||||||
|
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;93;4;I;INVENTOR;5;0"));
|
||||||
|
players.add(loser);
|
||||||
|
|
||||||
|
Player loser2 = new Player("Loser2", TotemColor.PURPLE);
|
||||||
|
loser2.addFood(2);
|
||||||
|
loser2.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;54;2;I;SHAMAN;2;0"));
|
||||||
|
loser2.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;36;2;I;BUILDER;3;1"));
|
||||||
|
loser2.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;82;0;I;ARTIST;9;0"));
|
||||||
|
loser2.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;93;4;I;INVENTOR;5;0"));
|
||||||
|
players.add(loser2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Testing if solveEvents is able to execute a series of events
|
||||||
|
@Test
|
||||||
|
void solveEvents() {
|
||||||
|
CardDeck cardDeck = new CardDeck();
|
||||||
|
cardDeck.setForNPlayer("./src/test/resources/files/only_event_deck.csv", 5);
|
||||||
|
|
||||||
|
List<EventCard> events = new ArrayList<>();
|
||||||
|
for(Card c: cardDeck.getTribeDeck()){
|
||||||
|
events.add((EventCard) c);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(EventsSolver.solveEvents(events, players));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Testing if sustainment is giving the correct output after inserting a valid input
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sustainment() {
|
||||||
|
EventCard sustainment = EventCard.parsRow("E;1;0;I;SUSTAINMENT;2;0");
|
||||||
|
ArrayList<Integer> target = new ArrayList<>();
|
||||||
|
target.add(players.getFirst().getFoodTokens());
|
||||||
|
target.add(players.getFirst().getPrestigePoints());
|
||||||
|
target.add(players.get(1).getFoodTokens() - 4);
|
||||||
|
target.add(players.get(1).getPrestigePoints());
|
||||||
|
target.add(0);
|
||||||
|
target.add(players.get(3).getPrestigePoints()-8);
|
||||||
|
target.add(0);
|
||||||
|
target.add(players.get(3).getPrestigePoints()-8);
|
||||||
|
assertEquals(target, EventsSolver.sustainment(sustainment, players));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Testing if hunt is giving the correct output after inserting a valid input
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void hunt() {
|
||||||
|
EventCard hunt = EventCard.parsRow("E;1;0;I;HUNT;3;0");
|
||||||
|
ArrayList<Integer> target = new ArrayList<>();
|
||||||
|
target.add(players.getFirst().getFoodTokens() + 2);
|
||||||
|
target.add(players.getFirst().getPrestigePoints() + 6);
|
||||||
|
target.add(players.get(1).getFoodTokens() + 1);
|
||||||
|
target.add(players.get(1).getPrestigePoints() + 3);
|
||||||
|
target.add(players.get(2).getFoodTokens());
|
||||||
|
target.add(players.get(2).getPrestigePoints());
|
||||||
|
target.add(players.get(3).getFoodTokens());
|
||||||
|
target.add(players.get(3).getPrestigePoints());
|
||||||
|
assertEquals(target, EventsSolver.hunt(hunt, players));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Testing if shamanicRitual is giving the correct output after inserting a valid input
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shamanicRitual() {
|
||||||
|
EventCard shamanicRitual = EventCard.parsRow("E;1;0;I;SHAMANIC_RITUAL;10;5");
|
||||||
|
|
||||||
|
ArrayList<Integer> target = new ArrayList<>();
|
||||||
|
target.add(players.getFirst().getPrestigePoints() + 10);
|
||||||
|
target.add(players.get(1).getPrestigePoints());
|
||||||
|
target.add(players.get(2).getPrestigePoints()-5);
|
||||||
|
target.add(players.get(3).getPrestigePoints()-5);
|
||||||
|
assertEquals(target, EventsSolver.shamanicRitual(shamanicRitual, players));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Testing if cavePainting is giving the correct output after inserting a valid input
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void cavePaintings() {
|
||||||
|
EventCard cavePainting = EventCard.parsRow("E;4;4;FINAL;CAVE_PAINTINGS;2;2");
|
||||||
|
ArrayList<Integer> target = new ArrayList<>();
|
||||||
|
target.add(players.getFirst().getPrestigePoints() + 6);
|
||||||
|
target.add(players.get(1).getPrestigePoints() + 4);
|
||||||
|
target.add(players.get(2).getPrestigePoints()-2);
|
||||||
|
target.add(players.get(3).getPrestigePoints()-2);
|
||||||
|
assertEquals(target, EventsSolver.cavePaintings(cavePainting, players));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void wrongInputs(){
|
||||||
|
EventCard cavePainting = EventCard.parsRow("E;4;4;FINAL;CAVE_PAINTINGS;2;2");
|
||||||
|
|
||||||
|
Exception sustainmentException = assertThrows(EventsManagerException.class, () -> EventsSolver.sustainment(cavePainting, players));
|
||||||
|
String SustainmentMessage = sustainmentException.getMessage();
|
||||||
|
assertTrue(SustainmentMessage.contains("Not a sustainment card"));
|
||||||
|
|
||||||
|
Exception shamanicException = assertThrows(EventsManagerException.class, () -> EventsSolver.shamanicRitual(cavePainting, players));
|
||||||
|
String shamanicMessage = shamanicException.getMessage();
|
||||||
|
assertTrue(shamanicMessage.contains("Not a shamanic ritual card"));
|
||||||
|
|
||||||
|
Exception huntException = assertThrows(EventsManagerException.class, () -> EventsSolver.hunt(cavePainting, players));
|
||||||
|
String huntMessage = huntException.getMessage();
|
||||||
|
assertTrue(huntMessage.contains("Not a hunt card"));
|
||||||
|
|
||||||
|
EventCard sustainment = EventCard.parsRow("E;1;0;I;SUSTAINMENT;2;0");
|
||||||
|
|
||||||
|
Exception cavePaintingException = assertThrows(EventsManagerException.class, () -> EventsSolver.cavePaintings(sustainment, players));
|
||||||
|
String cavePaintingMessage = cavePaintingException.getMessage();
|
||||||
|
assertTrue(cavePaintingMessage.contains("Not a cave painting card"));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
134
src/test/java/Server/GameBoardTest.java
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import Server.Cards.*;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class GameBoardTest {
|
||||||
|
|
||||||
|
CardDeck deck = new CardDeck();
|
||||||
|
Player player1 = new Player("A", TotemColor.PURPLE);
|
||||||
|
Player player2 = new Player("B", TotemColor.YELLOW);
|
||||||
|
Player player3 = new Player("C", TotemColor.BLUE);
|
||||||
|
Player player4 = new Player("D", TotemColor.GREEN);
|
||||||
|
Player player5 = new Player("E", TotemColor.RED);
|
||||||
|
List<Player> players = new ArrayList<>();
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
players.add(player1);
|
||||||
|
players.add(player2);
|
||||||
|
players.add(player3);
|
||||||
|
players.add(player4);
|
||||||
|
players.add(player5);
|
||||||
|
deck.setForNPlayer(".\\src\\test\\resources\\files\\test_gameboard.csv",5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void setTotemOnTile() {
|
||||||
|
|
||||||
|
//tests that the tile is occupied
|
||||||
|
|
||||||
|
GameBoard gameBoard = new GameBoard(Era.I,deck, players.size());
|
||||||
|
Player player = new Player("Lucy", TotemColor.BLUE);
|
||||||
|
OfferingTile tile = new OfferingTile(4);
|
||||||
|
assertTrue(gameBoard.placeTotem(player,tile,gameBoard.getTurnTile() ));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void drawBottomCard() {
|
||||||
|
|
||||||
|
//test that a hunter card has been added to the player
|
||||||
|
CardDeck deck2 = new CardDeck();
|
||||||
|
deck2.setForNPlayer("./src/test/resources/files/test_gameboard_characters.csv",5);
|
||||||
|
GameBoard gameBoard = new GameBoard(Era.I,deck2,players.size());
|
||||||
|
Player player = new Player("Lucy", TotemColor.BLUE);
|
||||||
|
gameBoard.setupInitialRows(5);
|
||||||
|
// gameBoard.takeFromBottomRow(gameBoard.getBottomrow().get(2),player);
|
||||||
|
// assertEquals(1,player.getPlayerTribe().countCharactersByType(CharacterType.HUNTER));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void drawTopCard() {
|
||||||
|
//tests that a hunter card has been added to the player
|
||||||
|
CardDeck deck2 = new CardDeck();
|
||||||
|
deck2.setForNPlayer("./src/test/resources/files/test_gameboard_characters.csv",5);
|
||||||
|
GameBoard gameBoard = new GameBoard(Era.I,deck2,players.size());
|
||||||
|
Player player = new Player("Lucy", TotemColor.BLUE);
|
||||||
|
gameBoard.setupInitialRows(5);
|
||||||
|
//gameBoard.DrawTopCard(gameBoard.getToprow().get(2),player);
|
||||||
|
//assertEquals(1,player.getPlayerTribe().countCharactersByType(CharacterType.HUNTER));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void eventsToSolve() {
|
||||||
|
|
||||||
|
//tests that the result is a list of event cards
|
||||||
|
CardDeck deck2 = new CardDeck();
|
||||||
|
deck2.setForNPlayer(".\\src\\test\\resources\\files\\test_gameboard_events.csv",5);
|
||||||
|
GameBoard gameBoard = new GameBoard(Era.I,deck2,players.size());
|
||||||
|
|
||||||
|
gameBoard.setupInitialRows(5);
|
||||||
|
//assertTrue(gameBoard.EventsToSolve().getFirst().getCardId() > 84);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void setRows() {
|
||||||
|
//tests that the top row has n+4 cards
|
||||||
|
GameBoard gameBoard = new GameBoard(Era.I,deck,players.size());
|
||||||
|
//assertEquals(9,gameBoard.SetRows(5).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void setOffersToNull() {
|
||||||
|
|
||||||
|
//tests that the tiles are free
|
||||||
|
|
||||||
|
GameBoard gameBoard = new GameBoard(Era.I,deck,players.size());
|
||||||
|
|
||||||
|
// assertEquals(Collections.emptyList(),gameBoard.SetOffersToNull(5));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nextEra() {
|
||||||
|
|
||||||
|
//tests that the eras progress
|
||||||
|
|
||||||
|
GameBoard gameBoard = new GameBoard(Era.I,deck,players.size());
|
||||||
|
// assertEquals(Era.II,gameBoard.NextEra());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void changeBuildings() {
|
||||||
|
|
||||||
|
//tests that with era change the top row of building is of era II
|
||||||
|
CardDeck deck2 = new CardDeck();
|
||||||
|
deck2.setForNPlayer(".\\src\\test\\resources\\files\\only_building_deck.csv",5);
|
||||||
|
GameBoard gameBoard = new GameBoard(Era.II,deck2,players.size());
|
||||||
|
|
||||||
|
//assertEquals(Era.II,gameBoard.ChangeBuildings().getFirst().getEra());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getBuilding() {
|
||||||
|
|
||||||
|
//tests that the building has gained the player as owner
|
||||||
|
|
||||||
|
GameBoard gameBoard = new GameBoard(Era.I,deck,players.size());
|
||||||
|
Player player = new Player("Diego",TotemColor.BLUE);
|
||||||
|
player.addFood(4);
|
||||||
|
//BuildingCard target = new BuildingCard(111,0,Era.II,4,3, Trigger.ENDGAME_BONUS_POINTS,null);
|
||||||
|
//assertEquals(target.getOwner(),gameBoard.GetBuilding(player,target).getOwner());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
64
src/test/java/Server/OfferingTileTest.java
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class OfferingTileTest {
|
||||||
|
|
||||||
|
OfferingTile offeringTile;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isEmpty() {
|
||||||
|
|
||||||
|
//test the ability of the method to recognize an occupied tile
|
||||||
|
|
||||||
|
OfferingTile offeringTile = new OfferingTile(4);
|
||||||
|
|
||||||
|
assertTrue(offeringTile.isEmpty());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void init(){
|
||||||
|
offeringTile = new OfferingTile(4);
|
||||||
|
offeringTile.setOccupant(new Player("Johnny", TotemColor.BLUE));
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void RemoveOccupant(){
|
||||||
|
|
||||||
|
//test that the occupant has been removed
|
||||||
|
|
||||||
|
assertNull(offeringTile.removeOccupant());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void GetOccupant(){
|
||||||
|
|
||||||
|
assertEquals("Johnny",offeringTile.getOccupant().getNickname());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void SetOccupant(){
|
||||||
|
|
||||||
|
//test that the player has been set
|
||||||
|
|
||||||
|
|
||||||
|
assertEquals("Johnny",offeringTile.getOccupant().getNickname());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getActions(){
|
||||||
|
|
||||||
|
//tests that the actions associated to tile are the correct ones
|
||||||
|
OfferingTile offeringTile = new OfferingTile(4);
|
||||||
|
|
||||||
|
List<Symbol> correct = List.of(Symbol.DOWN,Symbol.UP);
|
||||||
|
|
||||||
|
assertEquals(correct,offeringTile.getActions());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
99
src/test/java/Server/PlayerTest.java
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class PlayerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Dovrebbe aggiungere la quantità di cibo indicata")
|
||||||
|
void shouldAddFood() {
|
||||||
|
//Arrange
|
||||||
|
Player player = new Player("Giovanni", TotemColor.YELLOW);
|
||||||
|
|
||||||
|
//Act
|
||||||
|
player.addFood(5);
|
||||||
|
player.addFood(2);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
assertEquals(7, player.getFoodTokens(), "5 + 2 deve fare 7");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Dovrebbe sottrarre il cibo quando i fondi sono sufficienti")
|
||||||
|
void shouldSubtractFoodWhenFundsAreSufficient() {
|
||||||
|
//Arrange
|
||||||
|
Player player = new Player("Mario", TotemColor.PURPLE);
|
||||||
|
|
||||||
|
//Act
|
||||||
|
player.addFood(5);
|
||||||
|
player.removeFood(2);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
assertEquals(3, player.getFoodTokens());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Non dovrebbe far scendere il cibo sotto zero quando i fondi sono insufficienti")
|
||||||
|
void shouldNotGoNegativeWhenFoodIsInsufficient() {
|
||||||
|
//Arrange
|
||||||
|
Player player = new Player("Luigi", TotemColor.YELLOW);
|
||||||
|
|
||||||
|
//Act
|
||||||
|
player.addFood(2);
|
||||||
|
player.removeFood(5);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
assertEquals(2, player.getFoodTokens()); //Se il cibo è insufficiente, il cibo rimane lo stesso di prima, non diminuisce!
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Dovrebbe sommare correttamente i Punti Prestigio aggiunti")
|
||||||
|
void shouldAddPrestigePoints() {
|
||||||
|
// Arrange
|
||||||
|
Player player = new Player("Yoshi", TotemColor.YELLOW);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
player.addPrestigePoints(3);
|
||||||
|
player.addPrestigePoints(4);
|
||||||
|
player.addPrestigePoints(10);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertEquals(17, player.getPrestigePoints());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Dovrebbe sottrarre correttamente i Punti Prestigio persi")
|
||||||
|
void shouldRemovePrestigePoints() {
|
||||||
|
//Arrange
|
||||||
|
Player player = new Player("Gianbruno", TotemColor.BLUE);
|
||||||
|
player.addPrestigePoints(10);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
player.removePrestigePoints(4);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertEquals(6, player.getPrestigePoints());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Dovrebbe sottrarre correttamente i Punti Prestigio persi")
|
||||||
|
void shouldRemovePrestigePoints2() {
|
||||||
|
//Arrange
|
||||||
|
Player player1 = new Player("Gino", TotemColor.BLUE);
|
||||||
|
Player player2 = new Player("Olivia", TotemColor.RED);
|
||||||
|
|
||||||
|
player1.addPrestigePoints(10);
|
||||||
|
player2.addPrestigePoints(2);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
player1.removePrestigePoints(4);
|
||||||
|
player2.removePrestigePoints(7);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertEquals(6, player1.getPrestigePoints());
|
||||||
|
assertEquals(-5, player2.getPrestigePoints());
|
||||||
|
}
|
||||||
|
}
|
||||||
131
src/test/java/Server/TribeTest.java
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import Server.Cards.BuildingCard;
|
||||||
|
import Server.Cards.CharacterCard;
|
||||||
|
import Server.Cards.CharacterType;
|
||||||
|
import Server.Cards.Trigger;
|
||||||
|
import Server.Era;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class TribeTest {
|
||||||
|
private Tribe tribe;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp(){
|
||||||
|
tribe = new Tribe(); // creiamo una tribù nuova prima di ogni test
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGathererDiscount() {
|
||||||
|
// 2 gatherer -> 6 cibi scontati
|
||||||
|
tribe.addCharacter(new CharacterCard(1, 2, Era.I, CharacterType.GATHERER, 3, 0));
|
||||||
|
tribe.addCharacter(new CharacterCard(2, 2, Era.I, CharacterType.GATHERER, 3, 0));
|
||||||
|
|
||||||
|
assertEquals(6,tribe.gathererDiscount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testArtistsEndPoints() {
|
||||||
|
// 5 artisti = 2 coppie. 2 * 10 = 20 punti.
|
||||||
|
tribe.addCharacter(new CharacterCard(14, 2, Era.I, CharacterType.ARTIST, 0, 0));
|
||||||
|
tribe.addCharacter(new CharacterCard(15, 2, Era.I, CharacterType.ARTIST, 0, 0));
|
||||||
|
tribe.addCharacter(new CharacterCard(16, 2, Era.I, CharacterType.ARTIST, 0, 0));
|
||||||
|
tribe.addCharacter(new CharacterCard(17, 3, Era.I, CharacterType.ARTIST, 0, 0));
|
||||||
|
tribe.addCharacter(new CharacterCard(18, 4, Era.I, CharacterType.ARTIST, 0, 0));
|
||||||
|
|
||||||
|
assertEquals(20, tribe.artistsEndPoints());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInventorsEndPoints() {
|
||||||
|
// 3 Inventori, ma solo 2 invenzioni diverse: 2 (amo da pesca) e 3 (corda)
|
||||||
|
// 3 * 2= 6 punti
|
||||||
|
tribe.addCharacter(new CharacterCard(19, 2, Era.I, CharacterType.INVENTOR, 2, 0)); //statuetta
|
||||||
|
tribe.addCharacter(new CharacterCard(53, 2, Era.II, CharacterType.INVENTOR, 2, 0)); //statuetta
|
||||||
|
tribe.addCharacter(new CharacterCard(20, 2, Era.I, CharacterType.INVENTOR, 0, 0)); // canoa
|
||||||
|
|
||||||
|
assertEquals(6, tribe.inventorsEndPoints());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuildingBonusPoints() {
|
||||||
|
// Aggiungiamo l'edificio con id 117 che dà 25 punti
|
||||||
|
BuildingCard card25 = new BuildingCard(117, 2, Era.III, 10, 0, Trigger.ENDGAME_BONUS_POINTS, null);
|
||||||
|
tribe.addBuilding(card25);
|
||||||
|
|
||||||
|
// Se la tribù è vuota, dovrebbe dare esattamente 25 punti
|
||||||
|
assertEquals(25, tribe.endPoints(), "L'edificio bonus dovrebbe dare 25 punti");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testComplexScenario() {
|
||||||
|
// Due Artisti (1 coppia = 10 punti)
|
||||||
|
tribe.addCharacter(new CharacterCard(14, 2, Era.I, CharacterType.ARTIST, 0, 0));
|
||||||
|
tribe.addCharacter(new CharacterCard(15, 2, Era.I, CharacterType.ARTIST, 0, 0));
|
||||||
|
|
||||||
|
// Un costruttore che vale 5 punti prestigio base
|
||||||
|
tribe.addCharacter(new CharacterCard(62, 2, Era.II, CharacterType.BUILDER, 1, 5));
|
||||||
|
|
||||||
|
// L'edificio che raddoppia i punti dei costruttori (card id: 107 - ENDGAME_BUILDER_BONUS)
|
||||||
|
BuildingCard doubleBuilderPoints = new BuildingCard(107, 2, Era.II, 6, 4, Trigger.ENDGAME_BUILDER_BONUS, null);
|
||||||
|
tribe.addBuilding(doubleBuilderPoints);
|
||||||
|
|
||||||
|
/* CALCOLO ATTESO:
|
||||||
|
- Artisti: 10 punti
|
||||||
|
- Costruttori base: 5 punti
|
||||||
|
- Bonus buildingAbilitiesEndPoints (buildersEndPoints * 2): 5 * 2 = 10 punti
|
||||||
|
- Buildings: 4 punti
|
||||||
|
TOTALE: 29 punti
|
||||||
|
*/
|
||||||
|
assertEquals(29, tribe.endPoints(), "Il totale dovrebbe essere 29");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testComplexScenario2() {
|
||||||
|
// PERSONAGGI
|
||||||
|
|
||||||
|
// 3 Artisti (1 coppia = 10 punti)
|
||||||
|
tribe.addCharacter(new CharacterCard(14, 2, Era.I, CharacterType.ARTIST, 0, 0));
|
||||||
|
tribe.addCharacter(new CharacterCard(15, 2, Era.I, CharacterType.ARTIST, 0, 0));
|
||||||
|
tribe.addCharacter(new CharacterCard(16, 2, Era.I, CharacterType.ARTIST, 0, 0));
|
||||||
|
|
||||||
|
// 2 Costruttori (7 punti base totali)
|
||||||
|
tribe.addCharacter(new CharacterCard(6, 2, Era.I, CharacterType.BUILDER, 1, 3));
|
||||||
|
tribe.addCharacter(new CharacterCard(36, 2, Era.II, CharacterType.BUILDER, 1, 4));
|
||||||
|
|
||||||
|
// 4 Inventori (2 del gruppo 1, 1 del gruppo 2, 1 del gruppo 3 -> 4*3 = 12 punti)
|
||||||
|
tribe.addCharacter(new CharacterCard(48, 2, Era.II, CharacterType.INVENTOR, 2, 0)); // Statuetta (iconValue=2)
|
||||||
|
tribe.addCharacter(new CharacterCard(76, 2, Era.III, CharacterType.INVENTOR, 2, 0)); // Statuetta
|
||||||
|
tribe.addCharacter(new CharacterCard(49, 4, Era.II, CharacterType.INVENTOR, 3, 0)); // Amo da pesca
|
||||||
|
tribe.addCharacter(new CharacterCard(23, 4, Era.I, CharacterType.INVENTOR, 5, 0)); // Corda
|
||||||
|
|
||||||
|
// Rimanenti per fare il set da 6
|
||||||
|
tribe.addCharacter(new CharacterCard(10, 2, Era.I, CharacterType.GATHERER, 3, 0));
|
||||||
|
tribe.addCharacter(new CharacterCard(11, 2, Era.I, CharacterType.GATHERER, 3, 0));
|
||||||
|
tribe.addCharacter(new CharacterCard(1, 2, Era.I, CharacterType.HUNTER, 1, 0));
|
||||||
|
tribe.addCharacter(new CharacterCard(27, 2, Era.I, CharacterType.SHAMAN, 2, 0));
|
||||||
|
|
||||||
|
// EDIFICI
|
||||||
|
|
||||||
|
// id 107 (Bonus Costruttori) - 4 Punti Base Edificio
|
||||||
|
tribe.addBuilding(new BuildingCard(107, 2, Era.II, 6, 4, Trigger.ENDGAME_BUILDER_BONUS, null));
|
||||||
|
|
||||||
|
// id 109 (Set da 6) - 6 Punti Base Edificio
|
||||||
|
tribe.addBuilding(new BuildingCard(109, 2, Era.II, 5, 6, Trigger.ENDGAME_FOR_SIX, null));
|
||||||
|
|
||||||
|
// id 112 (Bonus Sciamani) - 4 Punti Base Edificio
|
||||||
|
tribe.addBuilding(new BuildingCard(112, 2, Era.III, 7, 4, Trigger.ENDGAME_BONUS_CHARACTER, null));
|
||||||
|
|
||||||
|
// id 117 (25 Punti) - 0 Punti Base Edificio
|
||||||
|
tribe.addBuilding(new BuildingCard(117, 2, Era.III, 10, 0, Trigger.ENDGAME_BONUS_POINTS, null));
|
||||||
|
|
||||||
|
|
||||||
|
// VERIFICA
|
||||||
|
// Totale atteso = 92
|
||||||
|
assertEquals(92, tribe.endPoints(), "Il totale dovrebbe essere 92 punti");
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/test/java/Server/TurnTileOldTest.java
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class TurnTileOldTest {
|
||||||
|
TurnTileOld turnTileOld;
|
||||||
|
|
||||||
|
Player player1 = new Player("A", TotemColor.PURPLE);
|
||||||
|
Player player2 = new Player("B", TotemColor.YELLOW);
|
||||||
|
Player player3 = new Player("C", TotemColor.BLUE);
|
||||||
|
Player player4 = new Player("D", TotemColor.GREEN);
|
||||||
|
Player player5 = new Player("E", TotemColor.RED);
|
||||||
|
List<Player> players = new ArrayList<>();
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp(){
|
||||||
|
|
||||||
|
players.add(player1);
|
||||||
|
players.add(player2);
|
||||||
|
players.add(player3);
|
||||||
|
players.add(player4);
|
||||||
|
players.add(player5);
|
||||||
|
turnTileOld = new TurnTileOld(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void startOrder() {
|
||||||
|
assertEquals(5, turnTileOld.startOrder(players).length);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void giveReward() {
|
||||||
|
turnTileOld.startOrder(players);
|
||||||
|
player5.addFood(1);
|
||||||
|
assertEquals(0, turnTileOld.giveReward(player5).getFoodTokens());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nextPlayer() {
|
||||||
|
|
||||||
|
//test that the method proceeds along the array
|
||||||
|
turnTileOld.startOrder(players);
|
||||||
|
assertEquals(player2, turnTileOld.nextPlayer());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addPlayer() {
|
||||||
|
|
||||||
|
//verify that the first player changes
|
||||||
|
|
||||||
|
turnTileOld.startOrder(players);
|
||||||
|
assertEquals(player2, turnTileOld.addPlayer(player2));
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/test/java/Server/TurnTileTest.java
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package Server;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class TurnTileTest {
|
||||||
|
TurnTile turnTile;
|
||||||
|
|
||||||
|
Player player1 = new Player("A", TotemColor.PURPLE);
|
||||||
|
Player player2 = new Player("B", TotemColor.YELLOW);
|
||||||
|
Player player3 = new Player("C", TotemColor.BLUE);
|
||||||
|
Player player4 = new Player("D", TotemColor.GREEN);
|
||||||
|
Player player5 = new Player("E", TotemColor.RED);
|
||||||
|
List<Player> players = new ArrayList<>();
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp(){
|
||||||
|
|
||||||
|
players.add(player1);
|
||||||
|
players.add(player2);
|
||||||
|
players.add(player3);
|
||||||
|
players.add(player4);
|
||||||
|
players.add(player5);
|
||||||
|
turnTile = new TurnTile(5);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
@Test
|
||||||
|
void startOrder() {
|
||||||
|
assertEquals(5, turnTileOld.startOrder(players).length);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void giveReward() {
|
||||||
|
turnTileOld.startOrder(players);
|
||||||
|
player5.addFood(1);
|
||||||
|
assertEquals(0, turnTileOld.giveReward(player5).getFoodTokens());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nextPlayer() {
|
||||||
|
|
||||||
|
//test that the method proceeds along the array
|
||||||
|
turnTileOld.startOrder(players);
|
||||||
|
assertEquals(player2, turnTileOld.nextPlayer());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addPlayer() {
|
||||||
|
|
||||||
|
//verify that the first player changes
|
||||||
|
|
||||||
|
turnTileOld.startOrder(players);
|
||||||
|
assertEquals(player2, turnTileOld.addPlayer(player2));
|
||||||
|
}
|
||||||
|
**/
|
||||||
|
|
||||||
|
}
|
||||||
17
src/test/resources/files/only_building_deck.csv
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
B;1;2;I;3;1;SHAMAN_NO_LOSS
|
||||||
|
B;3;4;II;6;2;FOOD_FOR_SIX
|
||||||
|
B;3;5;II;6;2;SUSTAIN_DISCOUNT;INVENTOR
|
||||||
|
B;17;5;II;6;2;SUSTAIN_DISCOUNT;ARTIST
|
||||||
|
B;5;3;III;6;2;SHAMAN_NO_LOSS
|
||||||
|
B;6;3;II;6;2;BONUS_FOOD_ENDTURN
|
||||||
|
B;7;3;II;6;2;FOOD_PER_INVENTORS
|
||||||
|
B;8;3;II;6;2;SHAMAN_BONUS
|
||||||
|
B;9;3;II;6;2;SHAMAN_DOUBLE_POINTS
|
||||||
|
B;10;3;II;6;2;HUNT_BONUS
|
||||||
|
B;11;3;II;6;2;ENDGAME_BUILDER_BONUS
|
||||||
|
B;12;3;II;6;2;PAINTING_FOOD_BONUS
|
||||||
|
B;13;3;II;6;2;ENDGAME_FOR_SIX
|
||||||
|
B;14;3;II;6;2;ENDGAME_BONUS_CHARACTER;BUILDER
|
||||||
|
B;18;3;II;6;2;ENDGAME_BONUS_CHARACTER;SHAMAN
|
||||||
|
B;15;3;II;6;2;EXTRA_DRAW
|
||||||
|
B;16;3;II;6;2;ENDGAME_BONUS_POINTS
|
||||||
|
3
src/test/resources/files/only_character_deck.csv
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
C;24;3;I;INVENTOR;7;0
|
||||||
|
C;25;4;I;INVENTOR;4;0
|
||||||
|
C;26;5;II;SHAMAN;2;0
|
||||||
|
4
src/test/resources/files/only_event_deck.csv
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
E;1;0;I;SUSTAINMENT;1;0
|
||||||
|
E;2;5;II;HUNT;2;0
|
||||||
|
E;3;3;III;SHAMANIC_RITUAL;2;3
|
||||||
|
E;4;4;FINAL;CAVE_PAINTINGS;2;1
|
||||||
|
24
src/test/resources/files/test_deck.csv
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
B;1;2;I;3;1;SHAMAN_NO_LOSS
|
||||||
|
B;3;4;II;6;2;FOOD_FOR_SIX
|
||||||
|
B;3;5;II;6;2;SUSTAIN_DISCOUNT;INVENTOR
|
||||||
|
B;17;5;II;6;2;SUSTAIN_DISCOUNT;ARTIST
|
||||||
|
B;5;3;III;6;2;SHAMAN_NO_LOSS
|
||||||
|
B;6;3;II;6;2;BONUS_FOOD_ENDTURN
|
||||||
|
B;7;3;II;6;2;FOOD_PER_INVENTORS
|
||||||
|
B;8;3;II;6;2;SHAMAN_BONUS
|
||||||
|
B;9;3;II;6;2;SHAMAN_DOUBLE_POINTS
|
||||||
|
B;10;3;II;6;2;HUNT_BONUS
|
||||||
|
B;11;3;II;6;2;ENDGAME_BUILDER_BONUS
|
||||||
|
B;12;3;II;6;2;PAINTING_FOOD_BONUS
|
||||||
|
B;13;3;II;6;2;ENDGAME_FOR_SIX
|
||||||
|
B;14;3;II;6;2;ENDGAME_BONUS_CHARACTER;BUILDER
|
||||||
|
B;18;3;II;6;2;ENDGAME_BONUS_CHARACTER;SHAMAN
|
||||||
|
B;15;3;II;6;2;EXTRA_DRAW
|
||||||
|
B;16;3;II;6;2;ENDGAME_BONUS_POINTS
|
||||||
|
C;24;3;I;INVENTOR;7;0
|
||||||
|
C;25;4;I;INVENTOR;4;0
|
||||||
|
C;26;5;II;SHAMAN;2;0
|
||||||
|
E;21;0;I;SUSTAINMENT;1;0
|
||||||
|
E;22;5;II;HUNT;2;0
|
||||||
|
E;23;3;III;SHAMANIC_RITUAL;2;3
|
||||||
|
E;34;4;FINAL;CAVE_PAINTINGS;2;1
|
||||||
|
96
src/test/resources/files/test_gameboard.csv
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
C;1;0;I;HUNTER;1;0
|
||||||
|
C;2;0;I;HUNTER;1;0
|
||||||
|
C;3;0;I;HUNTER;0;0
|
||||||
|
C;4;3;I;HUNTER;0;0
|
||||||
|
C;5;3;I;HUNTER;0;0
|
||||||
|
C;6;0;I;HUNTER;1;3
|
||||||
|
C;7;0;I;HUNTER;2;0
|
||||||
|
C;8;5;I;HUNTER;2;1
|
||||||
|
C;9;0;I;HUNTER;1;2
|
||||||
|
C;10;0;I;HUNTER;3;0
|
||||||
|
C;11;0;I;HUNTER;3;0
|
||||||
|
C;12;3;I;HUNTER;3;0
|
||||||
|
C;13;5;I;HUNTER;3;0
|
||||||
|
C;14;0;I;HUNTER;0;0
|
||||||
|
C;15;0;I;HUNTER;0;0
|
||||||
|
C;16;0;I;HUNTER;0;0
|
||||||
|
C;17;3;I;HUNTER;0;0
|
||||||
|
C;18;4;I;HUNTER;0;0
|
||||||
|
C;19;0;I;HUNTER;8;0
|
||||||
|
C;20;0;I;HUNTER;0;0
|
||||||
|
C;21;0;I;HUNTER;1;0
|
||||||
|
C;22;0;I;HUNTER;9;0
|
||||||
|
C;23;4;I;HUNTER;5;0
|
||||||
|
C;24;4;I;HUNTER;7;0
|
||||||
|
C;25;4;I;HUNTER;4;0
|
||||||
|
C;26;5;I;HUNTER;2;0
|
||||||
|
C;27;0;I;HUNTER;2;0
|
||||||
|
C;28;0;I;HUNTER;1;0
|
||||||
|
C;29;4;I;HUNTER;1;0
|
||||||
|
C;30;0;II;HUNTER;0;0
|
||||||
|
C;31;0;II;HUNTER;0;0
|
||||||
|
C;32;3;II;HUNTER;1;0
|
||||||
|
C;33;0;II;HUNTER;1;0
|
||||||
|
C;34;4;II;HUNTER;1;0
|
||||||
|
C;35;5;II;HUNTER;0;0
|
||||||
|
C;36;0;II;HUNTER;1;4
|
||||||
|
C;37;0;II;HUNTER;2;1
|
||||||
|
C;38;3;II;HUNTER;1;2
|
||||||
|
C;39;0;II;HUNTER;2;3
|
||||||
|
C;40;0;II;HUNTER;3;0
|
||||||
|
C;41;3;II;HUNTER;3;0
|
||||||
|
C;42;4;II;HUNTER;3;0
|
||||||
|
C;43;5;II;HUNTER;3;0
|
||||||
|
C;44;3;II;HUNTER;0;0
|
||||||
|
C;45;0;II;HUNTER;0;0
|
||||||
|
C;46;0;II;HUNTER;0;0
|
||||||
|
C;47;0;II;HUNTER;0;0
|
||||||
|
C;48;0;II;HUNTER;0;0
|
||||||
|
C;49;4;II;HUNTER;0;0
|
||||||
|
C;50;0;II;HUNTER;0;0
|
||||||
|
C;51;0;II;HUNTER;0;0
|
||||||
|
C;52;0;II;HUNTER;0;0
|
||||||
|
C;53;0;II;HUNTER;0;0
|
||||||
|
C;54;0;II;HUNTER;2;0
|
||||||
|
C;55;0;II;HUNTER;2;0
|
||||||
|
C;56;5;II;HUNTER;1;0
|
||||||
|
C;57;5;II;HUNTER;2;0
|
||||||
|
C;58;5;III;HUNTER;1;0
|
||||||
|
C;59;0;III;HUNTER;0;0
|
||||||
|
C;60;0;III;HUNTER;0;0
|
||||||
|
C;61;0;III;HUNTER;1;0
|
||||||
|
C;62;0;III;HUNTER;1;5
|
||||||
|
C;63;0;III;HUNTER;2;3
|
||||||
|
C;64;5;III;HUNTER;1;4
|
||||||
|
C;65;0;III;HUNTER;2;2
|
||||||
|
C;66;5;III;HUNTER;3;0
|
||||||
|
C;67;4;III;HUNTER;3;0
|
||||||
|
C;68;0;III;HUNTER;3;0
|
||||||
|
C;69;5;III;HUNTER;0;0
|
||||||
|
C;70;0;III;HUNTER;0;0
|
||||||
|
C;71;0;III;HUNTER;0;0
|
||||||
|
C;72;0;III;HUNTER;0;0
|
||||||
|
C;73;4;III;HUNTER;0;0
|
||||||
|
C;74;3;III;HUNTER;0;0
|
||||||
|
C;75;3;III;HUNTER;0;0
|
||||||
|
C;76;0;III;HUNTER;0;0
|
||||||
|
C;77;0;III;HUNTER;0;0
|
||||||
|
C;78;0;III;HUNTER;0;0
|
||||||
|
C;79;0;III;HUNTER;0;0
|
||||||
|
C;80;3;III;HUNTER;2;0
|
||||||
|
C;81;0;III;HUNTER;3;0
|
||||||
|
C;82;0;III;HUNTER;2;0
|
||||||
|
C;83;0;III;HUNTER;3;0
|
||||||
|
C;84;4;III;HUNTER;2;0
|
||||||
|
E;85;0;I;HUNT;1;1
|
||||||
|
E;86;0;I;SUSTAINMENT;1;1
|
||||||
|
E;87;0;I;SHAMANIC_RITUAL;5;3
|
||||||
|
E;88;0;I;CAVE_PAINTINGS;2;1
|
||||||
|
E;89;0;II;HUNT;1;2
|
||||||
|
E;90;0;II;SUSTAINMENT;1;2
|
||||||
|
E;91;0;II;SHAMANIC_RITUAL;10;5
|
||||||
|
E;92;0;II;CAVE_PAINTINGS;2;2
|
||||||
|
E;93;0;III;HUNT;1;3
|
||||||
|
E;94;0;III;CAVE_PAINTINGS;2;3
|
||||||
|
E;95;0;FINAL;SUSTAINMENT;1;3
|
||||||
|
E;96;0;FINAL;SHAMANIC_RITUAL;15;7
|
||||||
|
84
src/test/resources/files/test_gameboard_characters.csv
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
C;1;0;I;HUNTER;1;0
|
||||||
|
C;2;0;I;HUNTER;1;0
|
||||||
|
C;3;0;I;HUNTER;0;0
|
||||||
|
C;4;3;I;HUNTER;0;0
|
||||||
|
C;5;3;I;HUNTER;0;0
|
||||||
|
C;6;0;I;HUNTER;1;3
|
||||||
|
C;7;0;I;HUNTER;2;0
|
||||||
|
C;8;5;I;HUNTER;2;1
|
||||||
|
C;9;0;I;HUNTER;1;2
|
||||||
|
C;10;0;I;HUNTER;3;0
|
||||||
|
C;11;0;I;HUNTER;3;0
|
||||||
|
C;12;3;I;HUNTER;3;0
|
||||||
|
C;13;5;I;HUNTER;3;0
|
||||||
|
C;14;0;I;HUNTER;0;0
|
||||||
|
C;15;0;I;HUNTER;0;0
|
||||||
|
C;16;0;I;HUNTER;0;0
|
||||||
|
C;17;3;I;HUNTER;0;0
|
||||||
|
C;18;4;I;HUNTER;0;0
|
||||||
|
C;19;0;I;HUNTER;8;0
|
||||||
|
C;20;0;I;HUNTER;0;0
|
||||||
|
C;21;0;I;HUNTER;1;0
|
||||||
|
C;22;0;I;HUNTER;9;0
|
||||||
|
C;23;4;I;HUNTER;5;0
|
||||||
|
C;24;4;I;HUNTER;7;0
|
||||||
|
C;25;4;I;HUNTER;4;0
|
||||||
|
C;26;5;I;HUNTER;2;0
|
||||||
|
C;27;0;I;HUNTER;2;0
|
||||||
|
C;28;0;I;HUNTER;1;0
|
||||||
|
C;29;4;I;HUNTER;1;0
|
||||||
|
C;30;0;II;HUNTER;0;0
|
||||||
|
C;31;0;II;HUNTER;0;0
|
||||||
|
C;32;3;II;HUNTER;1;0
|
||||||
|
C;33;0;II;HUNTER;1;0
|
||||||
|
C;34;4;II;HUNTER;1;0
|
||||||
|
C;35;5;II;HUNTER;0;0
|
||||||
|
C;36;0;II;HUNTER;1;4
|
||||||
|
C;37;0;II;HUNTER;2;1
|
||||||
|
C;38;3;II;HUNTER;1;2
|
||||||
|
C;39;0;II;HUNTER;2;3
|
||||||
|
C;40;0;II;HUNTER;3;0
|
||||||
|
C;41;3;II;HUNTER;3;0
|
||||||
|
C;42;4;II;HUNTER;3;0
|
||||||
|
C;43;5;II;HUNTER;3;0
|
||||||
|
C;44;3;II;HUNTER;0;0
|
||||||
|
C;45;0;II;HUNTER;0;0
|
||||||
|
C;46;0;II;HUNTER;0;0
|
||||||
|
C;47;0;II;HUNTER;0;0
|
||||||
|
C;48;0;II;HUNTER;0;0
|
||||||
|
C;49;4;II;HUNTER;0;0
|
||||||
|
C;50;0;II;HUNTER;0;0
|
||||||
|
C;51;0;II;HUNTER;0;0
|
||||||
|
C;52;0;II;HUNTER;0;0
|
||||||
|
C;53;0;II;HUNTER;0;0
|
||||||
|
C;54;0;II;HUNTER;2;0
|
||||||
|
C;55;0;II;HUNTER;2;0
|
||||||
|
C;56;5;II;HUNTER;1;0
|
||||||
|
C;57;5;II;HUNTER;2;0
|
||||||
|
C;58;5;III;HUNTER;1;0
|
||||||
|
C;59;0;III;HUNTER;0;0
|
||||||
|
C;60;0;III;HUNTER;0;0
|
||||||
|
C;61;0;III;HUNTER;1;0
|
||||||
|
C;62;0;III;HUNTER;1;5
|
||||||
|
C;63;0;III;HUNTER;2;3
|
||||||
|
C;64;5;III;HUNTER;1;4
|
||||||
|
C;65;0;III;HUNTER;2;2
|
||||||
|
C;66;5;III;HUNTER;3;0
|
||||||
|
C;67;4;III;HUNTER;3;0
|
||||||
|
C;68;0;III;HUNTER;3;0
|
||||||
|
C;69;5;III;HUNTER;0;0
|
||||||
|
C;70;0;III;HUNTER;0;0
|
||||||
|
C;71;0;III;HUNTER;0;0
|
||||||
|
C;72;0;III;HUNTER;0;0
|
||||||
|
C;73;4;III;HUNTER;0;0
|
||||||
|
C;74;3;III;HUNTER;0;0
|
||||||
|
C;75;3;III;HUNTER;0;0
|
||||||
|
C;76;0;III;HUNTER;0;0
|
||||||
|
C;77;0;III;HUNTER;0;0
|
||||||
|
C;78;0;III;HUNTER;0;0
|
||||||
|
C;79;0;III;HUNTER;0;0
|
||||||
|
C;80;3;III;HUNTER;2;0
|
||||||
|
C;81;0;III;HUNTER;3;0
|
||||||
|
C;82;0;III;HUNTER;2;0
|
||||||
|
C;83;0;III;HUNTER;3;0
|
||||||
|
C;84;4;III;HUNTER;2;0
|
||||||
|
12
src/test/resources/files/test_gameboard_events.csv
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
E;85;0;I;HUNT;1;1
|
||||||
|
E;86;0;I;SUSTAINMENT;1;1
|
||||||
|
E;87;0;I;SHAMANIC_RITUAL;5;3
|
||||||
|
E;88;0;I;CAVE_PAINTINGS;2;1
|
||||||
|
E;89;0;II;HUNT;1;2
|
||||||
|
E;90;0;II;SUSTAINMENT;1;2
|
||||||
|
E;91;0;II;SHAMANIC_RITUAL;10;5
|
||||||
|
E;92;0;II;CAVE_PAINTINGS;2;2
|
||||||
|
E;93;0;III;HUNT;1;3
|
||||||
|
E;94;0;III;CAVE_PAINTINGS;2;3
|
||||||
|
E;95;0;FINAL;SUSTAINMENT;1;3
|
||||||
|
E;96;0;FINAL;SHAMANIC_RITUAL;15;7
|
||||||
|