Initial commit (basic backend stuff)
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# gradle specific directories
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
# IDE specific stuff
|
||||
.idea/
|
||||
39
build.gradle
Normal file
39
build.gradle
Normal file
@@ -0,0 +1,39 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
}
|
||||
|
||||
group 'de.siphalor'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains:annotations:16.0.2"
|
||||
|
||||
testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events "passed", "skipped", "failed"
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
mainClassName = "de.siphalor.was.Start"
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes(
|
||||
'Main-Class': 'de.siphalor.was.Start'
|
||||
)
|
||||
}
|
||||
}
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
183
gradlew
vendored
Normal file
183
gradlew
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed 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.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
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
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
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
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
100
gradlew.bat
vendored
Normal file
100
gradlew.bat
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem 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, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
1
packs/test/lang/de_de.lang
Normal file
1
packs/test/lang/de_de.lang
Normal file
@@ -0,0 +1 @@
|
||||
test.hello-world = Hallo Welt!
|
||||
2
settings.gradle
Normal file
2
settings.gradle
Normal file
@@ -0,0 +1,2 @@
|
||||
rootProject.name = 'what-a-storage'
|
||||
|
||||
16
src/main/java/de/siphalor/was/Start.java
Normal file
16
src/main/java/de/siphalor/was/Start.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package de.siphalor.was;
|
||||
|
||||
import de.siphalor.was.content.lang.I18n;
|
||||
|
||||
public class Start {
|
||||
public static void main(String[] args) {
|
||||
WhatAStorage was = WhatAStorage.getInstance();
|
||||
was.reload();
|
||||
was.start();
|
||||
|
||||
I18n.setLang("de_de", was.getContentManager());
|
||||
System.out.println(I18n.get("test.hello-world"));
|
||||
|
||||
was.run();
|
||||
}
|
||||
}
|
||||
129
src/main/java/de/siphalor/was/WhatAStorage.java
Normal file
129
src/main/java/de/siphalor/was/WhatAStorage.java
Normal file
@@ -0,0 +1,129 @@
|
||||
package de.siphalor.was;
|
||||
|
||||
import de.siphalor.was.content.ContentManager;
|
||||
import de.siphalor.was.content.lang.I18n;
|
||||
import de.siphalor.was.content.pack.FileContentPack;
|
||||
import de.siphalor.was.content.pack.JarContentPack;
|
||||
import de.siphalor.was.content.product.ProductManager;
|
||||
import de.siphalor.was.content.quest.QuestManager;
|
||||
import de.siphalor.was.state.MainMenuState;
|
||||
import de.siphalor.was.state.State;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class WhatAStorage {
|
||||
public static final String TITLE = "What a Storage";
|
||||
private static final WhatAStorage INSTANCE = new WhatAStorage();
|
||||
|
||||
public static WhatAStorage getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private final ContentManager contentManager;
|
||||
private final JarContentPack mainPack;
|
||||
private final ProductManager productManager;
|
||||
private final QuestManager questManager;
|
||||
|
||||
private Frame frame;
|
||||
private State state;
|
||||
|
||||
private long lastTick;
|
||||
private long minTickTime = 1000 / 60;
|
||||
private Canvas canvas;
|
||||
|
||||
private WhatAStorage() {
|
||||
contentManager = new ContentManager();
|
||||
mainPack = new JarContentPack("", "content");
|
||||
contentManager.addPack(mainPack);
|
||||
productManager = new ProductManager();
|
||||
questManager = new QuestManager();
|
||||
}
|
||||
|
||||
public ContentManager getContentManager() {
|
||||
return contentManager;
|
||||
}
|
||||
|
||||
public ProductManager getProductManager() {
|
||||
return productManager;
|
||||
}
|
||||
|
||||
public QuestManager getQuestManager() {
|
||||
return questManager;
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
contentManager.clear();
|
||||
|
||||
contentManager.addPack(mainPack);
|
||||
|
||||
File[] packDirs = Path.of("packs").toFile().listFiles(File::isDirectory);
|
||||
for (File dir : packDirs) {
|
||||
contentManager.addPack(new FileContentPack(dir.getName(), dir.toPath()));
|
||||
}
|
||||
|
||||
I18n.reload(contentManager);
|
||||
|
||||
productManager.clear();
|
||||
questManager.clear();
|
||||
productManager.reload(contentManager);
|
||||
questManager.reload(contentManager);
|
||||
|
||||
System.out.println("Reloaded game content");
|
||||
}
|
||||
|
||||
public void start() {
|
||||
frame = new Frame(TITLE);
|
||||
frame.setVisible(true);
|
||||
frame.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
super.windowClosing(e);
|
||||
frame.dispose();
|
||||
}
|
||||
});
|
||||
frame.addComponentListener(new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
super.componentResized(e);
|
||||
canvas.setSize(frame.getSize());
|
||||
state.onResize(frame.getWidth(), frame.getHeight());
|
||||
}
|
||||
});
|
||||
canvas = new Canvas();
|
||||
canvas.setSize(frame.getSize());
|
||||
frame.add(canvas);
|
||||
|
||||
changeState(new MainMenuState(canvas.getWidth(), canvas.getHeight()));
|
||||
}
|
||||
|
||||
public void run() {
|
||||
long time, timeDelta = minTickTime;
|
||||
lastTick = System.currentTimeMillis();
|
||||
|
||||
while (frame.isVisible()) {
|
||||
time = System.currentTimeMillis();
|
||||
timeDelta = time - lastTick - minTickTime;
|
||||
|
||||
if (timeDelta >= 0) {
|
||||
state.tick();
|
||||
state.render(canvas.getGraphics());
|
||||
lastTick = time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void changeState(State newState) {
|
||||
if (state != null) {
|
||||
state.leave();
|
||||
}
|
||||
state = newState;
|
||||
state.enter();
|
||||
state.onResize(canvas.getWidth(), canvas.getHeight());
|
||||
}
|
||||
}
|
||||
41
src/main/java/de/siphalor/was/assets/AssetsManager.java
Normal file
41
src/main/java/de/siphalor/was/assets/AssetsManager.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package de.siphalor.was.assets;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class AssetsManager {
|
||||
private static final Map<String, Image> imageCache = new HashMap<>();
|
||||
|
||||
public static final Image MISSINGNO = getImage("assets/missingno.png");
|
||||
|
||||
@Nullable
|
||||
public static InputStream getResource(@NotNull String path) {
|
||||
return Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
|
||||
}
|
||||
|
||||
public static Image getImage(@NotNull String path) {
|
||||
Image image = imageCache.get(path);
|
||||
if (image == null) {
|
||||
InputStream inputStream = getResource(path);
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
image = ImageIO.read(inputStream);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
image = MISSINGNO;
|
||||
}
|
||||
} else {
|
||||
image = MISSINGNO;
|
||||
}
|
||||
imageCache.put(path, image);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
}
|
||||
77
src/main/java/de/siphalor/was/content/ContentManager.java
Normal file
77
src/main/java/de/siphalor/was/content/ContentManager.java
Normal file
@@ -0,0 +1,77 @@
|
||||
package de.siphalor.was.content;
|
||||
|
||||
import de.siphalor.was.content.pack.ContentPack;
|
||||
import de.siphalor.was.content.resource.Resource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ContentManager {
|
||||
private final List<ContentPack> packs = new LinkedList<>();
|
||||
|
||||
public ContentManager() {
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
packs.clear();
|
||||
}
|
||||
|
||||
public void addPack(ContentPack pack) {
|
||||
packs.add(pack);
|
||||
}
|
||||
|
||||
public Collection<ContentPack> getPacks() {
|
||||
return packs;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Stream<Resource> getResources(@NotNull String location, @NotNull String type) {
|
||||
return packs.stream().flatMap(pack -> pack.getResources(location, type)).distinct();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Optional<Resource> getResource(@NotNull String location) {
|
||||
Resource resource;
|
||||
for (ContentPack pack : packs) {
|
||||
resource = pack.getResource(location);
|
||||
if (resource != null) {
|
||||
return Optional.of(resource);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Stream<Resource> getAllOfResource(@NotNull String location) {
|
||||
return packs.stream().flatMap(pack -> Stream.ofNullable(pack.getResource(location)));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Optional<Image> getImage(@NotNull String location) {
|
||||
return getResource(location).map(resource -> {
|
||||
InputStream inputStream = resource.getInputStream();
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
return ImageIO.read(inputStream);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
50
src/main/java/de/siphalor/was/content/lang/I18n.java
Normal file
50
src/main/java/de/siphalor/was/content/lang/I18n.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package de.siphalor.was.content.lang;
|
||||
|
||||
import de.siphalor.was.content.ContentManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class I18n {
|
||||
public static final String DEFAULT = "en_us";
|
||||
private static final Lang DEFAULT_LANG = new Lang(DEFAULT);
|
||||
|
||||
@Nullable
|
||||
private static Lang lang;
|
||||
|
||||
public static void setLang(@NotNull String code, @NotNull ContentManager contentManager) {
|
||||
if (lang == null || !lang.getCode().equals(code)) {
|
||||
lang = new Lang(code);
|
||||
lang.load(contentManager);
|
||||
}
|
||||
}
|
||||
|
||||
public static void reload(@NotNull ContentManager contentManager) {
|
||||
DEFAULT_LANG.load(contentManager);
|
||||
if (lang != null) {
|
||||
lang.load(contentManager);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String get(@NotNull String key) {
|
||||
String val;
|
||||
if (lang != null) {
|
||||
val = lang.get(key);
|
||||
if (val == null) {
|
||||
val = DEFAULT_LANG.get(key);
|
||||
}
|
||||
} else {
|
||||
val = DEFAULT_LANG.get(key);
|
||||
}
|
||||
|
||||
if (val == null) {
|
||||
return key;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String format(@NotNull String key, @Nullable Object... args) {
|
||||
return String.format(get(key), args);
|
||||
}
|
||||
}
|
||||
53
src/main/java/de/siphalor/was/content/lang/Lang.java
Normal file
53
src/main/java/de/siphalor/was/content/lang/Lang.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package de.siphalor.was.content.lang;
|
||||
|
||||
import de.siphalor.was.content.ContentManager;
|
||||
import de.siphalor.was.content.resource.Resource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
|
||||
public class Lang {
|
||||
private final String code;
|
||||
private final Properties properties = new Properties();
|
||||
|
||||
public Lang(@NotNull String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void load(@NotNull ContentManager contentManager) {
|
||||
properties.clear();
|
||||
contentManager.getAllOfResource("lang/" + code + ".lang").forEachOrdered(resource -> {
|
||||
InputStream inputStream = resource.getInputStream();
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
properties.load(inputStream);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (properties.isEmpty()) {
|
||||
System.out.println("Failed to load lang file for " + code);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String get(@NotNull String key) {
|
||||
return (String) properties.get(key);
|
||||
}
|
||||
}
|
||||
16
src/main/java/de/siphalor/was/content/pack/ContentPack.java
Normal file
16
src/main/java/de/siphalor/was/content/pack/ContentPack.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package de.siphalor.was.content.pack;
|
||||
|
||||
import de.siphalor.was.content.resource.Resource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface ContentPack {
|
||||
@NotNull
|
||||
Stream<Resource> getResources(@NotNull String location, @NotNull String type);
|
||||
@Nullable
|
||||
Resource getResource(@NotNull String location);
|
||||
@NotNull
|
||||
String getId();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package de.siphalor.was.content.pack;
|
||||
|
||||
import de.siphalor.was.content.resource.FileResource;
|
||||
import de.siphalor.was.content.resource.Resource;
|
||||
import de.siphalor.was.util.Util;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class FileContentPack implements ContentPack {
|
||||
private final String id;
|
||||
private final Path base;
|
||||
|
||||
public FileContentPack(@NotNull String id, @NotNull Path base) {
|
||||
this.id = id;
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Stream<Resource> getResources(@NotNull String location, @NotNull String type) {
|
||||
final String extension = "." + type;
|
||||
Path dir = base.resolve(Path.of(location));
|
||||
if (dir.toFile().isDirectory()) {
|
||||
try {
|
||||
return Files.find(dir, Integer.MAX_VALUE, (fPath, fileAttributes) -> fileAttributes.isRegularFile() && fPath.endsWith(extension)).map(path ->
|
||||
new FileResource(Util.pathToId(id, path.relativize(base).toString()), path.toFile())
|
||||
);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource getResource(@NotNull String location) {
|
||||
File file = base.resolve(Path.of(location)).toFile();
|
||||
if (file.isFile()) {
|
||||
return new FileResource(Util.pathToId(id, location), file);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
109
src/main/java/de/siphalor/was/content/pack/JarContentPack.java
Normal file
109
src/main/java/de/siphalor/was/content/pack/JarContentPack.java
Normal file
@@ -0,0 +1,109 @@
|
||||
package de.siphalor.was.content.pack;
|
||||
|
||||
import de.siphalor.was.content.resource.FileResource;
|
||||
import de.siphalor.was.content.resource.CallbackResource;
|
||||
import de.siphalor.was.content.resource.Resource;
|
||||
import de.siphalor.was.util.Util;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class JarContentPack implements ContentPack {
|
||||
private final String id;
|
||||
private final String baseLocation;
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
public JarContentPack(String id) {
|
||||
this(id, "");
|
||||
}
|
||||
|
||||
public JarContentPack(String id, String baseLocation) {
|
||||
this(id, baseLocation, Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
|
||||
public JarContentPack(String id, String baseLocation, ClassLoader classLoader) {
|
||||
this.id = id;
|
||||
this.baseLocation = baseLocation;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
// Inspired from this: https://stackoverflow.com/a/48190582/7582022
|
||||
@Override
|
||||
@NotNull
|
||||
public Stream<Resource> getResources(@NotNull String location, @NotNull String type) {
|
||||
final String extension = "." + type;
|
||||
try {
|
||||
URL url = classLoader.getResource(baseLocation + "/" + location);
|
||||
|
||||
if (url != null) {
|
||||
if ("file".equals(url.getProtocol())) {
|
||||
Path basePath = Path.of(url.toURI());
|
||||
return Files.find(basePath, Integer.MAX_VALUE, (path, attributes) -> attributes.isRegularFile() && path.getFileName().getName(0).toString().endsWith(extension)).map(path ->
|
||||
new FileResource(Util.pathToId(id, basePath.relativize(path).toString()), path.toFile())
|
||||
);
|
||||
} else if ("jar".equals(url.getProtocol())) {
|
||||
// The url path section begins with "file:" and ends with an exclamation mark and the path inside the jar
|
||||
// URLs also encode symbols with dollar sign so we need to decode them
|
||||
String urlPath = url.getPath();
|
||||
String jarPath = URLDecoder.decode(urlPath.substring(5, urlPath.indexOf('!')), StandardCharsets.UTF_8);
|
||||
|
||||
JarFile jarFile = new JarFile(jarPath);
|
||||
Enumeration<JarEntry> entries = jarFile.entries();
|
||||
|
||||
final String absLocation = baseLocation + "/" + location + "/";
|
||||
final int absLength = absLocation.length();
|
||||
|
||||
ArrayList<Resource> resources = new ArrayList<>();
|
||||
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String name = entry.getName();
|
||||
if (name.startsWith(absLocation) && name.length() != absLocation.length()) {
|
||||
resources.add(new CallbackResource(Util.pathToId(id, name.substring(absLength)), () -> classLoader.getResourceAsStream(name)));
|
||||
}
|
||||
}
|
||||
return resources.stream();
|
||||
}
|
||||
}
|
||||
} catch (IOException | URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Resource getResource(@NotNull String location) {
|
||||
try {
|
||||
URL url = classLoader.getResource(baseLocation + "/" + location);
|
||||
if (url != null) {
|
||||
if ("file".equals(url.getProtocol())) {
|
||||
return new FileResource(Util.pathToId(id, location), new File(url.toURI()));
|
||||
} else if ("jar".equals(url.getProtocol())) {
|
||||
return new CallbackResource(Util.pathToId(id, location), () -> classLoader.getResourceAsStream(baseLocation + "/" + location));
|
||||
}
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
14
src/main/java/de/siphalor/was/content/product/Product.java
Normal file
14
src/main/java/de/siphalor/was/content/product/Product.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package de.siphalor.was.content.product;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface Product {
|
||||
@NotNull
|
||||
String getPropertySpecifier();
|
||||
|
||||
int getDepth();
|
||||
boolean testY(int y);
|
||||
|
||||
@NotNull
|
||||
ProductType<?> getType();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package de.siphalor.was.content.product;
|
||||
|
||||
import de.siphalor.was.content.ContentManager;
|
||||
import de.siphalor.was.util.ResourceManager;
|
||||
import de.siphalor.was.content.product.dynamic.DynamicProductType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ProductManager implements ResourceManager<ProductType<?>> {
|
||||
private final Map<String, ProductType<?>> productTypes = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
productTypes.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload(@NotNull ContentManager contentManager) {
|
||||
contentManager.getResources("products", "properties").forEach(resource -> {
|
||||
InputStream inputStream = resource.getInputStream();
|
||||
if (inputStream != null) {
|
||||
productTypes.put(resource.getId(), DynamicProductType.from(inputStream));
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProductType<?> get(String id) {
|
||||
return productTypes.get(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.siphalor.was.content.product;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class ProductType<T extends Product> {
|
||||
|
||||
public ProductType() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public abstract T getProduct(String[] values);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package de.siphalor.was.content.product.dynamic;
|
||||
|
||||
import de.siphalor.was.content.product.Product;
|
||||
import de.siphalor.was.content.product.ProductType;
|
||||
import de.siphalor.was.util.Util;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.IntPredicate;
|
||||
|
||||
public class DynamicProduct implements Product {
|
||||
private ProductType<?> type;
|
||||
private final String[] properties;
|
||||
private int depth = 1;
|
||||
@NotNull
|
||||
private IntPredicate yPredicate = Util.dummyIntPredicate();
|
||||
|
||||
public DynamicProduct(String[] properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getPropertySpecifier() {
|
||||
return String.join("_", properties);
|
||||
}
|
||||
|
||||
public void setDepth(int depth) {
|
||||
this.depth = depth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDepth() {
|
||||
return depth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testY(int y) {
|
||||
return yPredicate.test(y);
|
||||
}
|
||||
|
||||
// This function exists to resolve the circular references of ProductType <-> Product on creation
|
||||
void setType(ProductType<?> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public ProductType<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setYPredicate(@NotNull IntPredicate yPredicate) {
|
||||
this.yPredicate = yPredicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DynamicProduct{" +
|
||||
"properties=" + Arrays.toString(properties) +
|
||||
", depth=" + depth +
|
||||
", yPredicate=" + yPredicate +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package de.siphalor.was.content.product.dynamic;
|
||||
|
||||
import de.siphalor.was.content.product.ProductType;
|
||||
import de.siphalor.was.util.Util;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.function.IntPredicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DynamicProductType extends ProductType<DynamicProduct> {
|
||||
private final Map<String, DynamicProduct> variations;
|
||||
private final String[] properties;
|
||||
|
||||
public DynamicProductType(List<DynamicProduct> variations, String[] properties) {
|
||||
this.variations = new HashMap<>();
|
||||
variations.forEach(p -> {
|
||||
p.setType(this);
|
||||
this.variations.put(p.getPropertySpecifier(), p);
|
||||
});
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static DynamicProductType from(@NotNull InputStream inputStream) {
|
||||
Properties propertiesFile = new Properties();
|
||||
|
||||
try {
|
||||
propertiesFile.load(inputStream);
|
||||
|
||||
String[] propNames = ((String) propertiesFile.get("properties")).split(",");
|
||||
|
||||
if (propNames.length > 0) {
|
||||
// propNameList = propNames without blank elements & all elements trimmed
|
||||
List<String> propNameList = new ArrayList<>(propNames.length);
|
||||
List<List<ProductPrototype>> properties = new ArrayList<>(propNames.length);
|
||||
for (String propName : propNames) {
|
||||
propName = propName.trim();
|
||||
if (propName.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
propNameList.add(propName);
|
||||
if (propertiesFile.containsKey(propName + ".variants")) {
|
||||
String[] variants = ((String) propertiesFile.get(propName + ".variants")).split(",");
|
||||
List<ProductPrototype> propertyVariants = new ArrayList<>(variants.length);
|
||||
for (String variant : variants) {
|
||||
variant = variant.trim();
|
||||
propertyVariants.add(makePrototype(propertiesFile, propName, variant));
|
||||
}
|
||||
properties.add(propertyVariants);
|
||||
} else {
|
||||
// if there are no variants assume true/false flag
|
||||
properties.add(List.of(
|
||||
makePrototype(propertiesFile, propName, "true"),
|
||||
makePrototype(propertiesFile, propName, "false")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
List<ProductPrototype> prototypes = properties.stream().reduce((pts1, pts2) -> {
|
||||
return pts1.stream().flatMap(pt -> {
|
||||
return pts2.stream().map(pt::combine);
|
||||
}).collect(Collectors.toList());
|
||||
}).get();
|
||||
|
||||
List<DynamicProduct> products = prototypes.stream().map(pt -> {
|
||||
DynamicProduct product = new DynamicProduct(pt.value.split(";"));
|
||||
pt.apply(product);
|
||||
return product;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
return new DynamicProductType(products, propNameList.toArray(String[]::new));
|
||||
}
|
||||
} catch (IOException | NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return new DynamicProductType(List.of(new DynamicProduct(Util.emptyArray())), Util.emptyArray());
|
||||
}
|
||||
|
||||
private static ProductPrototype makePrototype(Properties propertiesFile, String property, String variant) {
|
||||
ProductPrototype prototype = new ProductPrototype(variant);
|
||||
String base = property + "." + variant + ".";
|
||||
if (propertiesFile.containsKey(base + "depth")) {
|
||||
prototype.depth = Integer.parseInt((String) propertiesFile.get(base + "depth"));
|
||||
}
|
||||
if (propertiesFile.containsKey(base + "y")) {
|
||||
String val = (String) propertiesFile.get(base + "y");
|
||||
switch (val.charAt(0)) {
|
||||
case '>': {
|
||||
int i = Integer.parseInt(val.substring(1));
|
||||
prototype.yPredicate = value -> value > i;
|
||||
break;
|
||||
}
|
||||
case '<': {
|
||||
int i = Integer.parseInt(val.substring(1));
|
||||
prototype.yPredicate = value -> value < i;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
int i = Integer.parseInt(val);
|
||||
prototype.yPredicate = value -> value == i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return prototype;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicProduct getProduct(String[] values) {
|
||||
return variations.get(String.join("_", values));
|
||||
}
|
||||
|
||||
private static class ProductPrototype {
|
||||
String value;
|
||||
|
||||
Integer depth = null;
|
||||
IntPredicate yPredicate = null;
|
||||
|
||||
public ProductPrototype(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void apply(DynamicProduct product) {
|
||||
if (depth != null)
|
||||
product.setDepth(depth);
|
||||
if (yPredicate != null)
|
||||
product.setYPredicate(yPredicate);
|
||||
}
|
||||
|
||||
public ProductPrototype combine(ProductPrototype prototype) {
|
||||
ProductPrototype result = new ProductPrototype(value + ";" + prototype.value);
|
||||
if (prototype.depth != null)
|
||||
result.depth = prototype.depth;
|
||||
else
|
||||
result.depth = depth;
|
||||
if (result.yPredicate != null)
|
||||
result.yPredicate = prototype.yPredicate;
|
||||
else
|
||||
result.yPredicate = yPredicate;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DynamicProductType{" +
|
||||
"properties=" + String.join(" & ", properties) +
|
||||
", variations=\n" + variations.values().stream().map(Object::toString).collect(Collectors.joining(System.lineSeparator())) +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
36
src/main/java/de/siphalor/was/content/quest/Quest.java
Normal file
36
src/main/java/de/siphalor/was/content/quest/Quest.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package de.siphalor.was.content.quest;
|
||||
|
||||
import de.siphalor.was.content.product.Product;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class Quest {
|
||||
@NotNull
|
||||
private final Type type;
|
||||
private final int reward;
|
||||
@NotNull
|
||||
private final Product product;
|
||||
|
||||
public Quest(@NotNull Type type, int reward, @NotNull Product product) {
|
||||
this.product = product;
|
||||
this.reward = reward;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Product getProduct() {
|
||||
return product;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public int getReward() {
|
||||
return reward;
|
||||
}
|
||||
|
||||
enum Type {
|
||||
IN, OUT
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package de.siphalor.was.content.quest;
|
||||
|
||||
public interface QuestGenerator {
|
||||
/**
|
||||
* Restarts this generator
|
||||
*/
|
||||
void restart();
|
||||
|
||||
/**
|
||||
* Returns the next {@link Quest} but doesn't advance
|
||||
* @return the next {@link Quest}
|
||||
* @see QuestGenerator#next()
|
||||
*/
|
||||
Quest peek();
|
||||
|
||||
/**
|
||||
* Returns the next {@link Quest} and advances
|
||||
* @return the next {@link Quest}
|
||||
* @see QuestGenerator#peek()
|
||||
*/
|
||||
Quest next();
|
||||
|
||||
/**
|
||||
* Returns whether another {@link Quest} can be generated
|
||||
* @return whether the generator is at its end
|
||||
* @see QuestGenerator#restart()
|
||||
*/
|
||||
boolean hasNext();
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package de.siphalor.was.content.quest;
|
||||
|
||||
import de.siphalor.was.WhatAStorage;
|
||||
import de.siphalor.was.content.ContentManager;
|
||||
import de.siphalor.was.util.ResourceManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class QuestManager implements ResourceManager<QuestGenerator> {
|
||||
private final Map<String, QuestGenerator> questGenerators = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
questGenerators.clear();
|
||||
}
|
||||
|
||||
public void reload(@NotNull ContentManager contentManager) {
|
||||
contentManager.getResources("quests", "csv").forEach(resource -> {
|
||||
InputStream inputStream = resource.getInputStream();
|
||||
if (inputStream != null) {
|
||||
questGenerators.put(resource.getId(), StaticQuestGenerator.fromCsv(inputStream, WhatAStorage.getInstance().getProductManager()));
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuestGenerator get(String id) {
|
||||
return questGenerators.get(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package de.siphalor.was.content.quest;
|
||||
|
||||
import de.siphalor.was.content.product.Product;
|
||||
import de.siphalor.was.content.product.ProductManager;
|
||||
import de.siphalor.was.content.product.ProductType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class StaticQuestGenerator implements QuestGenerator {
|
||||
@NotNull
|
||||
private final List<Quest> quests;
|
||||
private int index = 0;
|
||||
|
||||
public StaticQuestGenerator(@NotNull List<Quest> quests) {
|
||||
this.quests = quests;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static StaticQuestGenerator fromCsv(@NotNull InputStream inputStream, @NotNull ProductManager productManager) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
String line;
|
||||
|
||||
List<Quest> quests = new ArrayList<>();
|
||||
|
||||
try {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
try {
|
||||
String[] parts = line.split(",");
|
||||
if (parts.length >= 3) {
|
||||
Quest.Type type = Quest.Type.valueOf(parts[0].toUpperCase(Locale.ENGLISH));
|
||||
ProductType<?> productType = productManager.get(parts[1]);
|
||||
if (productType != null) {
|
||||
int reward = Integer.parseInt(parts[2]);
|
||||
|
||||
Product product = productType.getProduct(Arrays.copyOfRange(parts, 3, parts.length));
|
||||
|
||||
if (product != null) {
|
||||
quests.add(new Quest(type, reward, product));
|
||||
} else {
|
||||
System.out.println("Invalid product in quest: " + line);
|
||||
}
|
||||
} else {
|
||||
System.out.println("Invalid product in quest: " + parts[2]);
|
||||
}
|
||||
} else {
|
||||
System.out.println("Invalid line in quests file: " + line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to load quest in csv file in line: " + line);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return new StaticQuestGenerator(quests);
|
||||
} catch (IOException e) {
|
||||
System.out.println("Failed to load quest csv file:");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restart() {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Quest peek() {
|
||||
return quests.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Quest next() {
|
||||
return quests.get(index++);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return index < quests.size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package de.siphalor.was.content.resource;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class CallbackResource extends Resource {
|
||||
Supplier<InputStream> supplier;
|
||||
|
||||
public CallbackResource(String id, Supplier<InputStream> supplier) {
|
||||
super(id);
|
||||
this.supplier = supplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable InputStream getInputStream() {
|
||||
return supplier.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package de.siphalor.was.content.resource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class FileResource extends Resource {
|
||||
private final File file;
|
||||
|
||||
public FileResource(String id, File file) {
|
||||
super(id);
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
try {
|
||||
return new FileInputStream(file);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/main/java/de/siphalor/was/content/resource/Resource.java
Normal file
20
src/main/java/de/siphalor/was/content/resource/Resource.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package de.siphalor.was.content.resource;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
public abstract class Resource {
|
||||
private final String id;
|
||||
|
||||
protected Resource(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public abstract InputStream getInputStream();
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
5
src/main/java/de/siphalor/was/game/Storage.java
Normal file
5
src/main/java/de/siphalor/was/game/Storage.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package de.siphalor.was.game;
|
||||
|
||||
public class Storage {
|
||||
|
||||
}
|
||||
35
src/main/java/de/siphalor/was/state/GameState.java
Normal file
35
src/main/java/de/siphalor/was/state/GameState.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package de.siphalor.was.state;
|
||||
|
||||
import de.siphalor.was.assets.AssetsManager;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
public class GameState extends State {
|
||||
private BufferedImage main;
|
||||
|
||||
public GameState(int width, int height) {
|
||||
super(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Graphics graphics) {
|
||||
graphics.drawImage(main, 0, 0, getWidth(), getHeight(), (img, infoflags, x, y, width, height) -> false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResize(int width, int height) {
|
||||
super.onResize(width, height);
|
||||
main = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
redrawBg(main.getGraphics());
|
||||
}
|
||||
|
||||
public void redrawBg(Graphics graphics) {
|
||||
graphics.drawImage(AssetsManager.getImage("assets/menu/bg.png"), 0, 0, getWidth(), getHeight(), (img, infoflags, x, y, width, height) -> false);
|
||||
}
|
||||
}
|
||||
31
src/main/java/de/siphalor/was/state/MainMenuState.java
Normal file
31
src/main/java/de/siphalor/was/state/MainMenuState.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package de.siphalor.was.state;
|
||||
|
||||
import de.siphalor.was.assets.AssetsManager;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public class MainMenuState extends State {
|
||||
private boolean bgDirty = true;
|
||||
|
||||
public MainMenuState(int width, int height) {
|
||||
super(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Graphics graphics) {
|
||||
if (bgDirty) {
|
||||
graphics.drawImage(AssetsManager.getImage("assets/bg.png"), 0, 0, getWidth() - 1, getHeight() - 1, (img, infoflags, x, y, width, height) -> false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResize(int width, int height) {
|
||||
super.onResize(width, height);
|
||||
bgDirty = true;
|
||||
}
|
||||
}
|
||||
37
src/main/java/de/siphalor/was/state/State.java
Normal file
37
src/main/java/de/siphalor/was/state/State.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package de.siphalor.was.state;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public abstract class State {
|
||||
private int width, height;
|
||||
|
||||
public State(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
onResize(width, height);
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void enter() {
|
||||
|
||||
}
|
||||
|
||||
public void leave() {
|
||||
|
||||
}
|
||||
|
||||
public abstract void tick();
|
||||
public abstract void render(Graphics graphics);
|
||||
|
||||
public void onResize(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
}
|
||||
39
src/main/java/de/siphalor/was/util/Pair.java
Normal file
39
src/main/java/de/siphalor/was/util/Pair.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package de.siphalor.was.util;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class Pair<A, B> {
|
||||
private final A first;
|
||||
private final B second;
|
||||
|
||||
public static <A, B> Pair<A, B> of(A first, B second) {
|
||||
return new Pair<>(first, second);
|
||||
}
|
||||
|
||||
public Pair(A first, B second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
public A getFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
public B getSecond() {
|
||||
return second;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Pair<?, ?> pair = (Pair<?, ?>) o;
|
||||
return Objects.equals(first, pair.first) &&
|
||||
Objects.equals(second, pair.second);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(first, second);
|
||||
}
|
||||
}
|
||||
11
src/main/java/de/siphalor/was/util/ResourceManager.java
Normal file
11
src/main/java/de/siphalor/was/util/ResourceManager.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package de.siphalor.was.util;
|
||||
|
||||
import de.siphalor.was.content.ContentManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface ResourceManager<T> {
|
||||
void clear();
|
||||
void reload(@NotNull ContentManager contentManager);
|
||||
|
||||
T get(String id);
|
||||
}
|
||||
37
src/main/java/de/siphalor/was/util/Util.java
Normal file
37
src/main/java/de/siphalor/was/util/Util.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package de.siphalor.was.util;
|
||||
|
||||
import java.util.function.IntPredicate;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class Util {
|
||||
private static final Predicate<?> DUMMY_PREDICATE = o -> true;
|
||||
private static final IntPredicate DUMMY_INT_PREDICATE = value -> true;
|
||||
|
||||
private static final Object[] EMPTY_ARRAY = new Object[0];
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Predicate<T> dummyPredicate() {
|
||||
return (Predicate<T>) DUMMY_PREDICATE;
|
||||
}
|
||||
|
||||
public static IntPredicate dummyIntPredicate() {
|
||||
return DUMMY_INT_PREDICATE;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T[] emptyArray() {
|
||||
return (T[]) EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
public static String pathToId(String packId, String path) {
|
||||
int dot = path.lastIndexOf('.');
|
||||
if (dot >= 0) {
|
||||
path = path.substring(0, dot);
|
||||
}
|
||||
path = path.replace('/', '.').replace('\\', '.');
|
||||
if (packId.isEmpty()) {
|
||||
return path;
|
||||
}
|
||||
return packId + "." + path;
|
||||
}
|
||||
}
|
||||
BIN
src/main/resources/assets/missingno.png
Normal file
BIN
src/main/resources/assets/missingno.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 342 B |
34
src/main/resources/content/lang/en_us.lang
Normal file
34
src/main/resources/content/lang/en_us.lang
Normal file
@@ -0,0 +1,34 @@
|
||||
test.hello-world = Hello World!
|
||||
|
||||
quests.normal = Normal
|
||||
|
||||
products.paper = Paper
|
||||
products.paper.color = Color
|
||||
products.paper.color.green = Green
|
||||
products.paper.color.blue = Blue
|
||||
products.paper.color.white = White
|
||||
# These paper formats are not equivalent but at least kind of similar in the amount they're used
|
||||
products.paper.format = Format
|
||||
products.paper.format.a3 = Ledger
|
||||
products.paper.format.a4 = Legal
|
||||
products.paper.format.a5 = Letter
|
||||
|
||||
products.wood = Wood
|
||||
products.wood.type = Type
|
||||
products.wood.type.beech = Beech
|
||||
products.wood.type.oak = Oak
|
||||
products.wood.type.pine = Pine
|
||||
products.wood.form = Form
|
||||
products.wood.form.pieces = Pieces
|
||||
products.wood.form.boards = Boards
|
||||
products.wood.form.beams = Beams
|
||||
|
||||
products.stone = Stone
|
||||
products.stone.type = Type
|
||||
products.stone.type.granite = Granite
|
||||
products.stone.type.marble = Marble
|
||||
products.stone.type.sandstone = Sandstone
|
||||
products.stone.weight = Weight
|
||||
products.stone.weight.light = Light
|
||||
products.stone.weight.medium = Medium
|
||||
products.stone.weight.heavy = Heavy
|
||||
3
src/main/resources/content/products/paper.properties
Normal file
3
src/main/resources/content/products/paper.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
properties = color, format
|
||||
color.variants =white, green, blue
|
||||
format.variants = a3, a4, a5
|
||||
4
src/main/resources/content/products/stone.properties
Normal file
4
src/main/resources/content/products/stone.properties
Normal file
@@ -0,0 +1,4 @@
|
||||
properties = type, weight
|
||||
type.variants = marble, granite, sandstone
|
||||
weight.variants = light, medium, heavy
|
||||
weight.heavy.y = 1
|
||||
4
src/main/resources/content/products/wood.properties
Normal file
4
src/main/resources/content/products/wood.properties
Normal file
@@ -0,0 +1,4 @@
|
||||
properties = type, form
|
||||
type.variants = pine, beech, oak
|
||||
form.variants = boards, pieces, beams
|
||||
form.beams.depth = 3
|
||||
47
src/main/resources/content/quests/normal.csv
Normal file
47
src/main/resources/content/quests/normal.csv
Normal file
@@ -0,0 +1,47 @@
|
||||
in,paper,200,white,a4
|
||||
in,paper,300,blue,a5
|
||||
in,wood,200,pine,boards
|
||||
in,wood,500,beech,beams
|
||||
in,wood,200,oak,pieces
|
||||
in,paper,200,blue,a5
|
||||
in,paper,200,blue,a5
|
||||
in,stone,400,marble,medium
|
||||
in,stone,500,granite,heavy
|
||||
in,stone,200,sandstone,light
|
||||
out,paper,1000,blue,a5
|
||||
out,wood,1200,oak,pieces
|
||||
out,stone,1000,marble,medium
|
||||
out,paper,1500,white,a5
|
||||
in,wood,400,oak,beams
|
||||
in,wood,600,oak,pieces
|
||||
in,wood,200,beech,pieces
|
||||
in,stone,400,granite,light
|
||||
in,paper,200,blue,a3
|
||||
in,paper,200,blue,a5
|
||||
in,wood,600,oak,pieces
|
||||
in,wood,600,beech,beams
|
||||
in,stone,200,sandstone,heavy
|
||||
in,stone,600,granite,heavy
|
||||
in,wood,400,beech,boards
|
||||
in,wood,200,beech,pieces
|
||||
out,wood,1000,beech,pieces
|
||||
out,paper,1200,blue,a5
|
||||
out,stone,1500,granite,heavy
|
||||
out,wood,1000,beech,beams
|
||||
out,stone,1300,sandstone,heavy
|
||||
in,stone,400,granite,heavy
|
||||
in,stone,600,marble,medium
|
||||
in,stone,400,granite,light
|
||||
in,stone,400,granite,light
|
||||
in,paper,400,white,a4
|
||||
in,stone,400,granite,light
|
||||
in,wood,600,beech,boards
|
||||
in,wood,600,pine,boards
|
||||
in,stone,400,sandstone,light
|
||||
out,paper,1000,white,a4
|
||||
out,stone,1200,marble,medium
|
||||
out,wood,1100,beech,boards
|
||||
out,paper,1500,white,a4
|
||||
out,wood,1000,pine,boards
|
||||
out,stone,1200,sandstone,light
|
||||
out,wood,1100,pine,boards
|
||||
|
2
src/test/java/Tests.java
Normal file
2
src/test/java/Tests.java
Normal file
@@ -0,0 +1,2 @@
|
||||
public class Tests {
|
||||
}
|
||||
Reference in New Issue
Block a user