Compare commits

..

23 Commits

Author SHA1 Message Date
38021852d0
Made inventory windows draggable 2021-12-22 01:41:55 +03:00
9c85164ed1
Fixed inventory hiding and improved HUD event handling 2021-12-21 23:28:19 +03:00
ce573b51ce
Merge branch 'master' into add-items 2021-12-21 17:12:45 +03:00
a028f6f3c7
Merge branch 'master' into add-items
If I ever become a supervillain, I will torture my victims by forcing
them to merge branches in Progressia

Does not work properly, waiting for an additional patch from master
2021-12-20 23:36:55 +03:00
9b67897896
Moved window layout into a separate file 2021-12-07 14:40:31 +03:00
3641c4130b
Rewrote ItemContainers from scratch
- ItemContainers have been entirely rewritten
  - ItemSlots are now wrappers, not owners
- Added InventoryOwners
  - Not yet used
- Added ItemDataWithContainers
- Added some basic windowing functionality to WindowedHUD
2021-11-16 23:49:17 +03:00
be6203719a
Attempted to make slots dynamic and failed 2021-10-31 15:26:48 +03:00
9885a1ca42
Visual improvements
- Added a custom font for item amount displays
- Added an indicator for openable items
- Changed open item indicator to be more obvious
- Fixed ExponentAnimation overshooting target during FPS spikes
2021-10-17 15:10:24 +03:00
c6e6dc6851
Added the concept of inventories
I'll end myself if I spend any more time on this neverending commit
2021-10-17 14:02:54 +03:00
30464febf6 Added item containers
- Added ItemDataContainer. It is an item that has a container
  - Added Test:CardboardBackpack
- Added back inventory GUIs
  - Now in window form!
    - *not interactive (don't sue us)
- Hand background icons now only render when an inventory is open
- Removed intrinsic player inventory because we're hardcore
2021-09-10 23:11:15 +03:00
782b3ef553
Hotfixed custom renderables and Hider
Man, flat render code is a huge mess
2021-09-10 20:38:42 +03:00
05b1c73fbc
Rewrote inventory GUI code to be more coherent. WIP
- Added an equipment slot
- Added equipment slot displays
- Added HUDManager and refactored layer management
- Moved most files from .test.inv to .client.graphics.world.hud
- Added component hider
  - Which is broken because z-index
    - Why do I use depth stencils in flat layers again?
- Refactored KeyMatcher: removed Builders, added action selection
2021-09-10 00:27:12 +03:00
IvanZ
bd6442318d
Added hand icons by IvanZ 2021-09-05 21:04:48 +03:00
6cd812f7c3 Added player species and refactored everything hands-related
- Players species is now a thing
  - Currently defines hands, equipment slots (unused), collision model
and appearance
  - EntityDataPlayer now contains a species-provided datalet
  - EntityRenderPlayer now searches for a species-defined delegate to
render players
- Hand count can now be arbitrary
- Split ItemContainerSingle into ItemContainerHand and
ItemContainerEquipment
- Added HUDTextures class
2021-09-05 11:16:54 +03:00
73ee339dcc
Working toward player species? I think?
- Left/right hand can now be permanently switched by tapping ctrl
- Moved LayerHUD into .hud subpackage
  - Extracted PermanentHUD out of LayerHUD
- Fixed the issuer of NewLocalEntityEvent
2021-09-04 17:12:54 +03:00
4749be6c60
Added hand item indicators, placeholder art included 2021-09-02 22:54:25 +03:00
a4b731e8a5
Added mass/volume limit enforcement and displays
- Also added Test:RedGraniteCobblestone
2021-08-30 23:36:13 +03:00
e7d0e8fe40
Added a visual indicator of selected hand and added scroll actions 2021-08-30 21:33:01 +03:00
040a4f7fd7
Added stack merging, RMB actions, Sticks and fixed an unholy bug
- Added Test:Stick
- When left-clicking, stacks that can merge will
- Added right-clicking behavior straight from Minecraft
- hooooooly sh*t man StatefulObject.equals was totally broken omfg how
tf i did notice :o
2021-08-30 18:09:08 +03:00
b3ac7b6afe
Item stack size is now defined by ItemSlot, not ItemData 2021-08-28 21:07:08 +03:00
2fe84dc59e
Working on item management 2021-08-28 17:50:45 +03:00
00ea4a6281
Added player hand containers
- Added EntityDataPlayer.{left,right}Hand
  - No in-world effect yet
- List-like functionality of ItemContainer extracted into
ItemContainerMixed
- Reworked implementation of Inventory screen
- Added optional arguments for Group
- Added Components.center()
2021-08-28 00:26:35 +03:00
f186fc602d
Added ItemData, ItemRender and related stuff
- Added ItemData, ItemSlot, ItemContainer
- Added ItemRender
- Added a player inventory
- Added temporary inventory display
- Font now has a scale setting
2021-08-26 18:56:32 +03:00
181 changed files with 7109 additions and 2337 deletions

2
.gitattributes vendored
View File

@ -6,4 +6,4 @@
* text=auto eol=lf
*.bat text eol=crlf
*.nsi text eol=crlf

View File

@ -1,49 +0,0 @@
name: Bug Report
description: Let us know about a problem
labels: [bug]
body:
- type: markdown
attributes:
value: |
Use this template to report identified problems. If the game suddenly crashed, and you need help identifying the cause of a crashreport, please use the Investigate Crashreport template instead.
**Do not forget to give your issue a descriptive title.**
- type: textarea
attributes:
label: Steps to reproduce
description: |
What did you do just before the problem appeared, or where can it be observed?
If the issue occurs unreliably, please estimate the probability of the crash.
placeholder: |
1. Look at a cow
2. Wait for the cow to look back
3. The cow turns away after a minute
The issue occurs approximately once every 10 attempts.
Player inventory must be empty. Does not occur in multiplayer. Occurred inside a virtual machine.
validations:
required: true
- type: textarea
attributes:
label: Expected behaviour
description: |
What should happen?
placeholder: |
The cow should stare back indefinitely, because obviously it should, and because comedy.
validations:
required: true
- type: textarea
attributes:
label: Other information
description: What else can you tell us about this bug?
validations:
required: false
- type: textarea
attributes:
label: Crashreport
description: If you have a relevant crashreport, paste the contents of the crashreport file here
render: text
validations:
required: false

View File

@ -1,5 +0,0 @@
blank_issues_enabled: true
contact_links:
- name: Official Progressia Discord
url: https://discord.gg/TWuXbVmX23
about: Ask developers or community members for help

View File

@ -1,41 +0,0 @@
name: Investigate Crashreport
description: Submit a crashreport to determine the problem
labels: [bug, crashreport]
body:
- type: markdown
attributes:
value: |
Use this template to submit crashreports for initial analisys. If you don't have a crashreport, or you have a good understanding of the underlying cause, please use the Bug Report template instead.
Crashreports can be found in `WORKING-DIRECTORY/crashreports` (take notice of the timestamp). `WORKING-DIRECTORY` is `C:\Users\<windows-user>\AppData\Roaming\Progressia` on Windows when using the installer.
**Do not forget to give your issue a descriptive title.**
- type: textarea
attributes:
label: Steps to reproduce
description: |
What did you do just before the crash? How could someone else reproduce it? What other context might be relevant?
If the issue occurs unreliably, please estimate the probability of the crash.
placeholder: |
1. Enter a new world
2. Turn around
3. Wait 5 minutes
4. Game crashes half of the time
Player inventory must be empty. Does not crash in multiplayer. Occurred inside a virtual machine.
validations:
required: true
- type: textarea
attributes:
label: Other information
description: What else can you tell us about this crashreport?
validations:
required: false
- type: textarea
attributes:
label: Crashreport
description: Paste the contents of the crashreport file here
render: text
validations:
required: true

View File

@ -1,29 +1,23 @@
/*
* Build logic for Progressia
* build.gradle
*
* Please refer to
*
* docs/building/BuildScriptReference.md
*
* for user reference.
* build.gradle for Progressia
*/
plugins {
id 'java'
// GrGit
// A JGit wrapper for Groovy used to access git commit info
id 'org.ajoberstar.grgit' version '4.1.1'
// Apply the java-library plugin to add support for Java Library
id 'java-library'
/*
* Uncomment the following line to enable the Eclipse plugin.
* This is only necessary if you don't use Buildship plugin from the IDE
*/
//id 'eclipse'
}
/*
* Configure Java version
*/
java {
/*
* We're Java 8 for now.
* Why? As of 2020, most users have Oracle Java, which only supports Java 8.
*/
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
@ -40,18 +34,6 @@ compileJava {
}
}
/*
* Set encoding
*/
compileJava {
options.encoding = 'utf8'
}
/*
* Dependencies
*/
@ -60,7 +42,7 @@ repositories {
mavenCentral()
/*
* Windcorp Maven repository
* Specify Windcorp Maven repository
* Currently used by:
* - ru.windcorp.fork.io.github.java-graphics:glm:1.0.1
*/
@ -70,7 +52,7 @@ repositories {
dependencies {
// Google Guava
// A generic utilities library
implementation 'com.google.guava:guava:31.0.1-jre'
implementation 'com.google.guava:guava:30.0-jre'
// Trove4j
// Provides optimized Collections for primitive types
@ -83,311 +65,238 @@ dependencies {
// Log4j
// A logging library
implementation 'org.apache.logging.log4j:log4j-api:2.17.1'
implementation 'org.apache.logging.log4j:log4j-core:2.17.1'
implementation 'org.apache.logging.log4j:log4j-api:2.17.0'
implementation 'org.apache.logging.log4j:log4j-core:2.17.0'
// JUnit
// A unit-testing library
testImplementation 'junit:junit:4.13.2'
// Also see LWJGL dependencies in build_logic/lwjgl.gradle
// See LWJGL dependencies below
}
/*
* Version resolution
* Progressia uses the following LWJGL libraries:
* - Core libraries
* - OpenGL
* - OpenAL
* - GLFW
* - STB
*/
import org.ajoberstar.grgit.*
/*
* LWJGL
* (auto-generated script)
* ((here be dragons))
*/
// Pattern: vMAJOR.MINOR.PATCH[-SUFFIX]
project.ext.tagFormat = /^v(\d+)\.(\d+)\.(\d+)(-[\w\-]*)?$/
import org.gradle.internal.os.OperatingSystem
String version_parseVersion(String tag, boolean increment) {
try {
def data = (tag =~ tagFormat)[0]
def major = data[1]
def minor = data[2]
def patch = data[3] as int
def suffix = data[4] ?: ''
if (increment) {
def oldVersion = "$major.$minor.$patch$suffix"
patch++
def newVersion = "$major.$minor.$patch$suffix"
logger.info "Version parsed from Git: $oldVersion, incremented to $newVersion"
return newVersion
} else {
def newVersion = "$major.$minor.$patch$suffix"
logger.info "Version parsed from Git: $newVersion"
return newVersion
}
} catch (any) {
logger.warn "Could not parse version from tag \"$tag\""
return tag
}
project.ext.lwjglVersion = "3.2.3"
switch (OperatingSystem.current()) {
case OperatingSystem.LINUX:
def osArch = System.getProperty("os.arch")
project.ext.lwjglNatives = osArch.startsWith("arm") || osArch.startsWith("aarch64")
? "natives-linux-${osArch.contains("64") || osArch.startsWith("armv8") ? "arm64" : "arm32"}"
: "natives-linux"
break
case OperatingSystem.MAC_OS:
project.ext.lwjglNatives = "natives-macos"
break
case OperatingSystem.WINDOWS:
project.ext.lwjglNatives = System.getProperty("os.arch").contains("64") ? "natives-windows" : "natives-windows-x86"
break
}
Tag version_findRelevantTag(Grgit git) {
def tags = git.tag.list()
def commits = [ git.head() ]
def visited = new HashSet<>()
while (true) {
if (commits.isEmpty()) return null
def nextCommits = new HashSet<>()
def formatSpecificationPrinted = false
for (def commit : commits) {
def tag = tags.findAll { it.commit == commit } ?.max { it.dateTime }
if (tag != null) {
if (tag.name ==~ tagFormat) {
return tag
} else {
if (!formatSpecificationPrinted) {
formatSpecificationPrinted = true
logger.info 'Expecting tag format: vMAJOR.MINOR.PATCH[-SUFFIX]'
}
logger.info 'Ignoring tag due to invalid format: {}', tag.name
}
dependencies {
implementation platform("org.lwjgl:lwjgl-bom:$lwjglVersion")
implementation "org.lwjgl:lwjgl"
implementation "org.lwjgl:lwjgl-glfw"
implementation "org.lwjgl:lwjgl-openal"
implementation "org.lwjgl:lwjgl-opengl"
implementation "org.lwjgl:lwjgl-stb"
runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
}
// LWJGL END
/*
* Tasks
*/
/*
* Additional native libraries specification
*/
project.ext.platforms = new HashSet<>()
task addNativeDependencies {
doFirst {
def archs = project.ext.platforms
switch (archs.size()) {
case 0:
println "Adding LWJGL native dependencies for local platform only:\n\t$lwjglNatives"
archs.add project.ext.lwjglNatives
break
case 1:
println "Adding LWJGL native dependencies for platform\n\t" + archs.get(0)
break
default:
println "Adding LWJGL native dependencies for platforms:\n\t" + archs.join("\n\t")
}
if (project.ext.lwjglNatives.isEmpty()) println "WTF"
dependencies {
archs.each { arch ->
runtimeOnly "org.lwjgl:lwjgl::$arch"
runtimeOnly "org.lwjgl:lwjgl-glfw::$arch"
runtimeOnly "org.lwjgl:lwjgl-openal::$arch"
runtimeOnly "org.lwjgl:lwjgl-opengl::$arch"
runtimeOnly "org.lwjgl:lwjgl-stb::$arch"
}
nextCommits.addAll commit.parentIds.collect(git.resolve.&toCommit)
}
visited.addAll commits
nextCommits.removeAll visited
commits = nextCommits
}
}
task resolveVersion {
description 'Resolves version information from Git repository or project properties.'
compileJava.mustRunAfter addNativeDependencies // Make sure runtimeOnly has not been resolved
task requestLinuxDependencies {
description 'Adds linux, linux-arm64 and linux-arm32 native libraries to built artifacts.'
doFirst {
project.ext.platforms.addAll([
'natives-linux',
'natives-linux-arm64',
'natives-linux-arm32'
])
}
}
task requestWindowsDependencies {
description 'Adds windows and windows-x86 native libraries to built artifacts.'
doFirst {
project.ext.platforms.addAll([
'natives-windows',
'natives-windows-x86'
])
}
}
task requestMacOSDependencies {
description 'Adds macos native libraries to built artifacts.'
doFirst {
project.ext.platforms.addAll(['natives-macos'])
}
}
def dependencySpecificationTasks = tasks.findAll { task -> task.name.startsWith('request') && task.name.endsWith('Dependencies') }
task requestCrossPlatformDependencies {
description 'Adds native libraries for all available platforms to built artifacts.'
dependsOn dependencySpecificationTasks
}
addNativeDependencies.mustRunAfter dependencySpecificationTasks
/*
* Determines if the provided dependency should be packaged
*/
def isDependencyRequested(String dep) {
if (dep.endsWith(".jar")) {
dep = dep.substring(0, dep.length() - ".jar".length())
}
return !dep.contains("natives-") ||
project.ext.platforms.contains(dep.substring(dep.indexOf("natives-"), dep.length()))
}
/*
* Manifest specification
*/
task specifyLocalManifest {
dependsOn addNativeDependencies // Make sure all native dependencies are specified
doFirst {
project.ext.commit = System.env.GIT_COMMIT
project.ext.branch = System.env.GIT_BRANCH
try {
def git = Grgit.open(dir: project.projectDir)
project.ext.commit = commit ?: git.head().id
project.ext.branch = branch ?: git.branch.current().name
if (project.version != 'unspecified') {
// Leave version as-is
def classPath = []
configurations.runtimeClasspath.each {
if (isDependencyRequested(it.getName())) {
classPath.add("lib/" + it.getName())
} else {
// Resolve version from Git
def tag = version_findRelevantTag(git)
if (tag == null) {
String suffix
if (project.hasProperty('buildId')) {
suffix = project.buildId;
} else {
suffix = java.time.ZonedDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern('yyyy_MM_dd'))
}
project.version = "999.0.0-$suffix"
logger.warn 'Git repository does not contain an applicable tag, using dummy version {}\nSpecify version with -Pversion=1.2.3 or create a Git tag named v1.2.3', project.version
} else {
project.version = version_parseVersion(tag.name, tag.commit != git.head())
}
println "\tRemoving from JAR classpath (not requested): " + it.getName()
}
} catch (org.eclipse.jgit.errors.RepositoryNotFoundException e) {
if (project.version == 'unspecified') project.version = 'dev'
project.ext.commit = commit ?: '-'
project.ext.branch = branch ?: '-'
logger.warn 'No Git repository found in project root, using dummy version {}\nSpecify version with -Pversion=1.2.3 or create a Git tag named v1.2.3', project.version
}
if (branch.contains '/') {
// Strip remote - no one wants that
project.ext.branch = branch.takeAfter '/'
if (classPath.size() == configurations.runtimeClasspath.size()) {
println "Nothing removed from JAR classpath"
}
if (!project.hasProperty('buildId')) {
project.ext.buildId = '-'
jar {
manifest {
attributes(
"Main-Class": "ru.windcorp.progressia.client.ProgressiaClientMain",
"Class-Path": configurations.runtimeClasspath.collect { "lib/" + it.getName() } .findAll { isDependencyRequested(it) } .join(' ')
)
}
}
}
doLast {
}
}
jar.dependsOn specifyLocalManifest
/*
* Apply LWJGL logic
*/
apply from: 'build_logic/lwjgl.gradle'
/*
* Copy libraries into build/libs/lib directory, next to Progressia.jar
* Library export
*/
task exportLibs(type: Sync) {
description 'Copies runtime libraries into a subdirectory next to the output JAR.'
jar.dependsOn exportLibs
dependsOn lwjgl_addNativesToRuntimeOnly
mustRunAfter addNativeDependencies
// from defined in configureManifest
into 'build/libs/lib'
into libsDirectory.get().getAsFile().getPath() + "/lib"
exclude { !isDependencyRequested(it.getName()) }
from configurations.runtimeClasspath
}
jar.dependsOn(exportLibs)
/*
* Configure JAR manifest
* Packaging
*/
task configureManifest {
description 'Populates JAR manifest with Main-Class, Class-Path and version metadata.'
task packageDebian(type: Exec) {
description 'Builds the project and creates a Debain package.'
group 'Progressia'
jar.dependsOn configureManifest
dependsOn resolveVersion
dependsOn lwjgl_addNativesToRuntimeOnly
dependsOn build
dependsOn requestLinuxDependencies
doFirst {
def classPath = project.lwjgl.replaceNativesIn(configurations.runtimeClasspath)
exportLibs.from classPath
jar.manifest.attributes(
'Main-Class': 'ru.windcorp.progressia.client.ProgressiaClientMain',
'Class-Path': classPath.collect { "lib/${java.net.URLEncoder.encode it.name}" } .join(' '),
'Specification-Title': 'Progressia',
'Implementation-Title': 'Progressia',
'Implementation-Version': project.version,
'Implementation-Version-Git-Commit': project.commit,
'Implementation-Version-Git-Branch': project.branch,
'Implementation-Version-BuildId': project.buildId,
)
commandLine './buildPackages.sh', 'debian'
doLast {
println "Debian package available in build_packages/"
}
}
task packageWindows(type: Exec) {
description 'Builds the project and creates a Windows installer.'
group 'Progressia'
dependsOn build
dependsOn requestWindowsDependencies
/*
* Packaging working directory configuration
*/
commandLine './buildPackages.sh', 'windows'
import java.nio.file.*
import java.nio.file.attribute.*
task deletePackagingDirs(type: Delete) {
description 'Deletes build/tmp/packaging directory.'
followSymlinks = false
delete 'build/tmp/packaging'
}
task linkBuildOutputForPackaging {
description 'Symlinks the contents of build/libs into packaging working directory.'
dependsOn build
mustRunAfter deletePackagingDirs
onlyIf { preparePackaging.ext.mode == 'symlink' }
ext.from = 'build/libs'
ext.into = "build/tmp/packaging/workingDir/${ -> preparePackaging.ext.buildDest}"
inputs.dir from
outputs.dir into
doLast {
def fromPath = Paths.get from
def intoPath = Paths.get into
Files.createDirectories intoPath
Files.list(fromPath).each {
def fileName = it.fileName.toString()
// Exclude all JARs except the current one
if (fileName ==~ "${project.name}.*\\.jar" && fileName != tasks.jar.archiveFileName.get())
return
Files.createSymbolicLink intoPath.resolve(it.fileName), intoPath.relativize(it)
}
}
}
task copyBuildOutputForPackaging(type: Copy) {
description 'Copies the contents of build/libs into packaging working directory.'
dependsOn build
mustRunAfter deletePackagingDirs
onlyIf { preparePackaging.ext.mode == 'copy' }
from 'build/libs'
filesMatching("${project.name}*.jar") {
include tasks.jar.archiveFileName.get()
}
into "build/tmp/packaging/workingDir/${ -> preparePackaging.ext.buildDest}"
}
task preparePackaging {
preparePackaging.ext.buildDest = ''
preparePackaging.ext.mode = 'symlink'
dependsOn deletePackagingDirs
dependsOn linkBuildOutputForPackaging
dependsOn copyBuildOutputForPackaging
}
/*
* Apply all packaging scripts
*/
new File(projectDir, 'build_logic/packaging').list().each {
apply from: "build_logic/packaging/$it/script.gradle"
}
/*
* Ensure no more than one packaging task is scheduled
*/
gradle.taskGraph.whenReady { graph ->
if (graph.allTasks.count { it.name ==~ /package[^_]*/ } > 1) {
def offenders = graph.allTasks.findAll { it.name ==~ /package[^_]*/ }
throw new GradleException("Cannot execute multiple package tasks within a single build\n" +
"\tOffending tasks: $offenders")
doLast {
println "Windows installer available in build_packages/"
}
}
/*
* Convenience build tasks
*/
task buildCrossPlatform {
description 'Builds the project including native libraries for all available platforms.'
group 'Progressia'
@ -396,17 +305,17 @@ task buildCrossPlatform {
dependsOn build
doLast {
logger.info 'Native libraries for all platforms have been added'
println "Native libraries for all platforms have been added"
}
}
task buildLocal {
description "Builds the project including only native libraries for current platform (${lwjgl.localArch})."
description "Builds the project including only native libraries for current platform ($lwjglNatives)."
group 'Progressia'
dependsOn build
doLast {
logger.info "Native libraries only for platform ${lwjgl.localArch} have been added"
println "Native libraries only for platform $lwjglNatives have been added"
}
}

191
buildPackages.sh Executable file
View File

@ -0,0 +1,191 @@
#!/bin/bash
#
# Progressia
# Copyright (C) 2020-2021 Wind Corporation and contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
echoerr() { echo "$@" 1>&2; }
buildDebianPackage() {
# Commands that must be available to execute this action
requiredCommands='dpkg-deb fakeroot'
# Package name. Sync with control file manually!
name='progressia-techdemo'
# Version that the package will receive. Sync with control file manually!
version='1.0_all'
# This directory will be copied into $tmpDir
templateDirectory="build_packages/DEB/template"
# Files that must be present
requiredFiles="$templateDirectory/DEBIAN/control"
nameAndVersion="$name-$version"
tmpDir="build_packages/DEB/$nameAndVersion"
outputFile="build_packages/DEB/$nameAndVersion.deb"
echo "Checking environment to build Debian package"
for item in $requiredCommands; do
if command -v "$item" &> /dev/null; then
echo "- $item found"
else
echoerr "Command $item not found, cannot package"
exit 100
fi
done
for file in $requiredFiles; do
if ! [ -r "$file" ]; then
echoerr "$file is missing or not readable, cannot package"
exit 101
else
echo "- $file is present and readable"
fi
done
echo "Environment OK; packaging Debian package"
exitCode=0
{
shareDir="$tmpDir/usr/share/progressia"
mkdir -p "$tmpDir" &&
mkdir -p "$shareDir" &&
cp -r "$templateDirectory"/* "$tmpDir" &&
cp -r 'build/libs/lib' "$shareDir/lib" &&
cp 'build/libs/Progressia.jar' "$shareDir/Progressia.jar" &&
echo "------ DPKG-DEB ------" &&
fakeroot dpkg-deb --build "$tmpDir" &&
echo "---- DPKG-DEB END ----" &&
mv "$outputFile" build_packages
} || {
echoerr "Could not create Debian package"
exitCode=1
}
{
if [ -d "$tmpDir" ]; then
rm -r "$tmpDir"
fi
echo "Cleaned up"
} || {
echoerr "Could not clean up after packaging Debian package"
exitCode=2
}
exit "$exitCode"
}
buildWindowsInstaller() {
# Commands that must be available to execute this action
requiredCommands='makensis'
# NSIS configuration file that must be present
configurationFile='build_packages/NSIS/ProgressiaInstaller.nsi'
# File that will be output
outputFile='build_packages/NSIS/ProgressiaInstaller.exe'
echo "Checking environment to build Windows installer"
for item in $requiredCommands; do
if command -v "$item" &> /dev/null; then
echo "- $item found"
else
echoerr "Command $item not found, cannot build"
exit 100
fi
done
if ! [ -r "$configurationFile" ]; then
echoerr "$configurationFile is missing or not readable, cannot build"
exit 101
else
echo "- $configurationFile is present and readable"
fi
echo "Environment OK; building Windows installer"
exitCode=0
{
cp -r 'build/libs/lib' 'build_packages/NSIS/lib' &&
cp 'build/libs/Progressia.jar' 'build_packages/NSIS/Progressia.jar' &&
cp 'LICENSE' 'build_packages/NSIS/LICENSE.txt' &&
echo "------ NSIS ------" &&
makensis "$configurationFile" &&
echo "---- NSIS END ----" &&
mv "$outputFile" build_packages
} || {
echoerr "Could not build Windows installer"
exitCode=1
}
{
if [ -d 'build_packages/NSIS/lib' ]; then
rm -r 'build_packages/NSIS/lib'
fi
if [ -e 'build_packages/NSIS/Progressia.jar' ]; then
rm 'build_packages/NSIS/Progressia.jar'
fi
if [ -e 'build_packages/NSIS/LICENSE.txt' ]; then
rm 'build_packages/NSIS/LICENSE.txt'
fi
echo "Cleaned up"
} || {
echoerr "Could not clean up after building Windows installer"
exitCode=2
}
exit "$exitCode"
}
printUsage() {
echoerr "Usage: $0 TARGET"
echoerr " where TARGET is 'debian' or 'windows'"
}
if [ -n "$2" ]; then
echoerr "Too many arguments."
printUsage
exit 202
fi
case "$1" in
"debian")
buildDebianPackage
;;
"windows")
buildWindowsInstaller
;;
"")
echoerr "No action specified"
printUsage
exit 200
;;
"--help" | "-help" | "help" | "?")
printUsage
;;
*)
echoerr "Unknown action '$1'"
printUsage
exit 201
;;
esac

View File

@ -1,149 +0,0 @@
/*
* Build logic for Progressia
* LWJGL dependency logic
*/
project.ext.lwjgl = new HashMap<>()
// Version of LWJGL
lwjgl.version = '3.3.0'
/*
* Target platforms for current operation.
* This is filled in by the request* tasks. This is referenced by the addLwjglNatives task.
* When empty, current platform is assumed.
*/
lwjgl.targets = new HashSet<String>()
// LWJGL components. To include org.lwjgl:lwjgl-foobar, add 'foobar' to this list.
lwjgl.libraries = [
'opengl',
'glfw',
'openal',
'stb'
]
// Determine the architecture of the build environment
import org.gradle.internal.os.OperatingSystem
switch (OperatingSystem.current()) {
case OperatingSystem.LINUX:
def osArch = System.getProperty('os.arch')
lwjgl.localArch = osArch.startsWith('arm') || osArch.startsWith('aarch64')
? "linux-${osArch.contains('64') || osArch.startsWith('armv8') ? 'arm64' : 'arm32'}"
: 'linux'
break
case OperatingSystem.MAC_OS:
lwjgl.localArch = System.getProperty('os.arch').startsWith('aarch64') ? 'macos-arm64' : 'macos'
break
case OperatingSystem.WINDOWS:
def osArch = System.getProperty('os.arch')
lwjgl.localArch = osArch.contains('64')
? "windows${osArch.startsWith('aarch64') ? '-arm64' : ''}"
: 'windows-x86'
break
default:
logger.info "Unknown or unsupported OS type according to Gradle's org.gradle.internal.os.OperatingSystem: ${OperatingSystem.current()}"
break
}
configurations {
create 'lwjglNatives'
}
// Declare pure-Java dependencies
dependencies {
// BOM
def bom = platform("org.lwjgl:lwjgl-bom:${lwjgl.version}")
implementation bom
lwjglNatives bom
// Core
implementation 'org.lwjgl:lwjgl'
// Local natives for core
runtimeOnly "org.lwjgl:lwjgl::natives-${lwjgl.localArch}"
// Components
lwjgl.libraries.each { lib ->
implementation "org.lwjgl:lwjgl-$lib"
// Local natives for component
runtimeOnly "org.lwjgl:lwjgl-$lib::natives-${lwjgl.localArch}"
}
}
/*
* Adds LWJGL native libraries to lwjglNatives configuration
*/
task lwjgl_addNativesToRuntimeOnly {
doFirst {
if (project.hasProperty('forceTargets')) {
try {
def oldTargets = lwjgl.targets.join(',')
lwjgl.targets.clear()
lwjgl.targets.addAll project.forceTargets.split(',')*.trim().collect { it == 'local' ? lwjgl.localArch : it }
logger.info 'Overriding selected platforms {} with {}', oldTargets, lwjgl.targets.join(',')
} catch (Exception e) {
throw new GradleException("Could not parse forceTargets \"${project.forceTargets}\", expecting platform-1,platform-2,local", e)
}
}
if (lwjgl.targets.isEmpty()) {
logger.info 'Adding LWJGL native dependencies for local platform only: {}', lwjgl.localArch
lwjgl.targets.add lwjgl.localArch
} else {
logger.info 'Adding LWJGL native dependencies for platforms: {}', lwjgl.targets.sort().join(', ')
}
if (lwjgl.targets.contains(null)) {
if (lwjgl.localArch != null) {
throw new GradleException("Requested local LWJGL natives; could not determine local architecture for OS ${OperatingSystem.current()} with os.arch ${System.getProperty('os.arch')}")
} else {
throw new GradleException("LWJGL targets resolved to ${lwjgl.targets}")
}
}
dependencies {
lwjgl.targets.each { target ->
lwjglNatives "org.lwjgl:lwjgl::natives-$target"
lwjgl.libraries.each { lib ->
lwjglNatives "org.lwjgl:lwjgl-$lib::natives-$target"
}
}
}
}
}
// Replaces LWJGL natives in the given configuration with the requested ones
lwjgl.replaceNativesIn = { config ->
new ArrayList<File>().tap {
addAll config
removeIf { it.name ==~ /.*lwjgl.*natives.*/ }
addAll project.configurations.lwjglNatives
}
}
task requestCrossPlatformDependencies {
description 'Adds LWJGL natives for all available platforms.'
lwjgl_addNativesToRuntimeOnly.mustRunAfter requestCrossPlatformDependencies
}
def requestTask(String name, String... targets) {
def theTask = task "request${name}Dependencies"
theTask.doFirst {
lwjgl.targets.addAll targets
}
theTask.description "Adds LWJGL natives for $name (${targets.join(', ')})."
requestCrossPlatformDependencies.dependsOn theTask
lwjgl_addNativesToRuntimeOnly.mustRunAfter theTask
}
requestTask 'Linux', 'linux', 'linux-arm32', 'linux-arm64'
requestTask 'Windows', 'windows', 'windows-arm64', 'windows-x86'
requestTask 'MacOS', 'macos', 'macos-arm64'

View File

@ -1,42 +0,0 @@
task packageDeb_processResources(type: Copy) {
dependsOn resolveVersion
dependsOn preparePackaging
from 'src/packaging/deb'
filesMatching('DEBIAN/control') {
expand(version: { -> project.version})
}
into 'build/tmp/packaging/workingDir'
}
task packageDeb_configure() {
preparePackaging.mustRunAfter packageDeb_configure
doLast {
tasks.preparePackaging.ext.buildDest = 'usr/share/progressia'
tasks.preparePackaging.ext.mode = 'copy'
}
}
task packageDeb(type: Exec) {
description 'Builds the project and creates a Debian package.'
group 'Progressia'
dependsOn packageDeb_configure
dependsOn requestLinuxDependencies
dependsOn build
dependsOn preparePackaging
dependsOn packageDeb_processResources
doFirst {
mkdir 'build/packages'
}
executable 'dpkg-deb'
args '--root-owner-group'
args '--build', 'build/tmp/packaging/workingDir'
args 'build/packages'
}

View File

@ -1,54 +0,0 @@
task packageNsis_processResources(type: Copy) {
dependsOn preparePackaging
from ('src/packaging/nsis') {
exclude 'left_side.png'
}
from('LICENSE') {
rename 'LICENSE', 'LICENSE.txt'
}
into 'build/tmp/packaging/workingDir'
}
task packageNsis_generateIcon(type: Exec) {
mustRunAfter preparePackaging
executable 'convert'
args files('src/main/resources/assets/icons/*.original.png').files*.path
args 'build/tmp/packaging/workingDir/logo.ico'
}
task packageNsis_generateLeftSide(type: Exec) {
mustRunAfter preparePackaging
executable 'convert'
args 'src/packaging/nsis/left_side.png'
args '-alpha', 'off'
args 'BMP3:build/tmp/packaging/workingDir/left_side.bmp'
}
task packageNsis(type: Exec) {
description 'Builds the project and creates a Windows NSIS installer.'
group 'Progressia'
dependsOn requestWindowsDependencies
dependsOn build
dependsOn resolveVersion
dependsOn preparePackaging
dependsOn packageNsis_processResources
dependsOn packageNsis_generateIcon
dependsOn packageNsis_generateLeftSide
doFirst {
mkdir 'build/packages'
}
executable 'makensis'
args '-NOCONFIG'
args "-DPROJECT_NAME=${project.name}"
args "-DPROJECT_VERSION=${ -> project.version}"
args "-DMAIN_JAR_FILE=${ -> project.tasks.jar.archiveFileName.get()}"
args "-DOUTPUT_DIR=${project.buildDir.absolutePath}/packages"
args 'build/tmp/packaging/workingDir/config.nsi'
}

View File

@ -1,45 +0,0 @@
task packageZip_processResources(type: Copy) {
dependsOn preparePackaging
from ('src/packaging/zip') {
filesMatching('start.*') {
filter(
org.apache.tools.ant.filters.ReplaceTokens,
tokens: [mainJarFile: project.tasks.jar.archiveFileName.get()]
)
}
}
from ('src/main/resource/assets/icons/logo256.original.png') {
rename 'logo256.original.png', 'logo.png'
}
from('LICENSE') {
rename 'LICENSE', 'LICENSE.txt'
}
into 'build/tmp/packaging/workingDir'
}
task packageZip(type: Zip) {
description 'Builds the project and creates a cross-platform ZIP package.'
group 'Progressia'
dependsOn resolveVersion
dependsOn requestCrossPlatformDependencies
dependsOn build
dependsOn preparePackaging
dependsOn packageZip_processResources
archiveBaseName = project.name
archiveAppendix = 'universal'
doFirst {
archiveVersion = project.version
}
doFirst {
mkdir 'build/packages'
}
from 'build/tmp/packaging/workingDir'
destinationDirectory = file('build/packages')
}

View File

@ -1,8 +1,8 @@
Package: progressia
Version: ${version}
Package: progressia-techdemo
Version: 1.0
Section: custom
Priority: optional
Architecture: all
Maintainer: Javapony <kvadropups@gmail.com>
Depends: java8-runtime
Description: Progressia - a 3D sandbox survival game
Description: Progressia Techdemo release

View File

@ -1,164 +1,161 @@
;NSIS Modern User Interface
;Welcome/Finish Page Example Script
;Written by Joost Verburg
;--------------------------------
;Include Modern UI
!include "MUI2.nsh"
;--------------------------------
;General
; Expecting the following symbols from caller:
; PROJECT_NAME
; PROJECT_VERSION
; MAIN_JAR_FILE
; MUI Settings / Icons
!define MUI_ICON "logo.ico"
;!define MUI_UNICON ;Uninstall icon
; MUI Settings / Header
; !define MUI_HEADERIMAGE
; !define MUI_HEADERIMAGE_RIGHT
; !define MUI_HEADERIMAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Header\orange-r-nsis.bmp"
; !define MUI_HEADERIMAGE_UNBITMAP "${NSISDIR}\Contrib\Graphics\Header\orange-uninstall-r-nsis.bmp"
; MUI Settings / Wizard
!define MUI_WELCOMEFINISHPAGE_BITMAP "left_side.bmp"
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "left_side.bmp"
;Name and file
Name "${PROJECT_NAME}"
OutFile "${OUTPUT_DIR}/${PROJECT_NAME}-${PROJECT_VERSION}-installer.exe"
Unicode True
;Default installation folder
InstallDir "$PROGRAMFILES\${PROJECT_NAME}"
;Get installation folder from registry if available
InstallDirRegKey HKLM "Software\${PROJECT_NAME}" ""
;Request application privileges for Windows Vista
RequestExecutionLevel admin
;--------------------------------
;Interface Settings
!define MUI_ABORTWARNING
;--------------------------------
;Pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "LICENSE.txt"
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_TEXT "Start ${PROJECT_NAME}"
!define MUI_FINISHPAGE_RUN_FUNCTION "LaunchLink"
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_COMPONENTS
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
;--------------------------------
;Languages
!insertmacro MUI_LANGUAGE "English"
;--------------------------------
;Installer Sections
Section "Install ${PROJECT_NAME}" SEC0000
SectionIn RO ;Make it read-only
SetOutPath "$INSTDIR"
SetOverwrite on
;Files
File "${MAIN_JAR_FILE}"
File logo.ico
File /r lib
;Store installation folder
WriteRegStr HKLM "SOFTWARE\${PROJECT_NAME}" "Install_Dir" "$INSTDIR"
;Create uninstaller
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "DisplayName" "${PROJECT_NAME} (remove only)"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "UninstallString" "$INSTDIR\Uninstall.exe"
WriteUninstaller "$INSTDIR\Uninstall.exe"
SectionEnd
Section "Create Desktop Shortcut" SEC0001
SetOutPath "$APPDATA\${PROJECT_NAME}"
CreateShortCut "$DESKTOP\${PROJECT_NAME}.lnk" "$INSTDIR\${MAIN_JAR_FILE}" "" "$INSTDIR\logo.ico"
SectionEnd
Section "Start Menu Shortcuts" SEC0002
CreateDirectory "$SMPROGRAMS\${PROJECT_NAME}"
CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\${PROJECT_NAME}.lnk" "$INSTDIR\${MAIN_JAR_FILE}" "" "$INSTDIR\logo.ico"
SectionEnd
;--------------------------------
;Uninstaller Section
Section "Uninstall"
;ADD YOUR OWN FILES HERE...
Delete $INSTDIR\Uninstall.exe
Delete "$INSTDIR\${MAIN_JAR_FILE}"
Delete $INSTDIR\lib\*.*
Delete $INSTDIR\logo.ico
RMDir $INSTDIR\lib
Delete $DESKTOP\${PROJECT_NAME}.lnk
Delete $SMPROGRAMS\${PROJECT_NAME}\Uninstall.lnk
Delete $SMPROGRAMS\${PROJECT_NAME}\${PROJECT_NAME}.lnk
RMDir $INSTDIR
RMDir /r $SMPROGRAMS\${PROJECT_NAME}
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}"
DeleteRegKey HKLM "Software\${PROJECT_NAME}"
SectionEnd
Section "un.Remove user data"
RMDir /r "$APPDATA\${PROJECT_NAME}"
SectionEnd
;--------------------------------
;Functions
Function LaunchLink
SetOutPath "$APPDATA\${PROJECT_NAME}"
ExecShell "" "$INSTDIR\${MAIN_JAR_FILE}"
FunctionEnd
;--------------------------------
;Descriptions
;Language strings
LangString DESC_SecDummy ${LANG_ENGLISH} "Install ${PROJECT_NAME}."
;Assign language strings to sections
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SEC0000} $(DESC_SecDummy)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
;NSIS Modern User Interface
;Welcome/Finish Page Example Script
;Written by Joost Verburg
;--------------------------------
;Include Modern UI
!include "MUI2.nsh"
;--------------------------------
;General
!define PROJECT_NAME "Progressia"
; MUI Settings / Icons
!define MUI_ICON "logo.ico"
;!define MUI_UNICON ;Uninstall icon
; MUI Settings / Header
; !define MUI_HEADERIMAGE
; !define MUI_HEADERIMAGE_RIGHT
; !define MUI_HEADERIMAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Header\orange-r-nsis.bmp"
; !define MUI_HEADERIMAGE_UNBITMAP "${NSISDIR}\Contrib\Graphics\Header\orange-uninstall-r-nsis.bmp"
; MUI Settings / Wizard
!define MUI_WELCOMEFINISHPAGE_BITMAP "left_side.bmp"
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "left_side.bmp"
;Name and file
Name "${PROJECT_NAME}"
OutFile "${PROJECT_NAME}Installer.exe"
Unicode True
;Default installation folder
InstallDir "$PROGRAMFILES\${PROJECT_NAME}"
;Get installation folder from registry if available
InstallDirRegKey HKLM "Software\${PROJECT_NAME}" ""
;Request application privileges for Windows Vista
RequestExecutionLevel admin
;--------------------------------
;Interface Settings
!define MUI_ABORTWARNING
;--------------------------------
;Pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "LICENSE.txt"
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_TEXT "Start ${PROJECT_NAME}"
!define MUI_FINISHPAGE_RUN_FUNCTION "LaunchLink"
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_COMPONENTS
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
;--------------------------------
;Languages
!insertmacro MUI_LANGUAGE "English"
;--------------------------------
;Installer Sections
Section "Install ${PROJECT_NAME}" SEC0000
SectionIn RO ;Make it read-only
SetOutPath "$INSTDIR"
SetOverwrite on
;Files
File Progressia.jar
File logo.ico
File /r lib
;Store installation folder
WriteRegStr HKLM SOFTWARE\Progressia "Install_Dir" "$INSTDIR"
;Create uninstaller
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "DisplayName" "${PROJECT_NAME} (remove only)"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "UninstallString" "$INSTDIR\Uninstall.exe"
WriteUninstaller "$INSTDIR\Uninstall.exe"
SectionEnd
Section "Create Desktop Shortcut" SEC0001
SetOutPath "$APPDATA\${PROJECT_NAME}"
CreateShortCut "$DESKTOP\${PROJECT_NAME}.lnk" "$INSTDIR\${PROJECT_NAME}.jar" "" "$INSTDIR\logo.ico"
SectionEnd
Section "Start Menu Shortcuts" SEC0002
CreateDirectory "$SMPROGRAMS\${PROJECT_NAME}"
CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\${PROJECT_NAME}.lnk" "$INSTDIR\${PROJECT_NAME}.jar" "" "$INSTDIR\logo.ico"
SectionEnd
;--------------------------------
;Uninstaller Section
Section "Uninstall"
;ADD YOUR OWN FILES HERE...
Delete $INSTDIR\Uninstall.exe
Delete $INSTDIR\Progressia.jar
Delete $INSTDIR\lib\*.*
Delete $INSTDIR\logo.ico
RMDir $INSTDIR\lib
Delete $DESKTOP\${PROJECT_NAME}.lnk
Delete $SMPROGRAMS\${PROJECT_NAME}\Uninstall.lnk
Delete $SMPROGRAMS\${PROJECT_NAME}\${PROJECT_NAME}.lnk
RMDir $INSTDIR
RMDir /r $SMPROGRAMS\${PROJECT_NAME}
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}"
DeleteRegKey HKLM "Software\${PROJECT_NAME}"
SectionEnd
Section "un.Remove user data"
RMDir /r "$APPDATA\${PROJECT_NAME}"
SectionEnd
;--------------------------------
;Functions
Function LaunchLink
SetOutPath "$APPDATA\${PROJECT_NAME}"
ExecShell "" "$INSTDIR\${PROJECT_NAME}.jar"
FunctionEnd
;--------------------------------
;Descriptions
;Language strings
LangString DESC_SecDummy ${LANG_ENGLISH} "Install ${PROJECT_NAME}."
;Assign language strings to sections
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SEC0000} $(DESC_SecDummy)
!insertmacro MUI_FUNCTION_DESCRIPTION_END

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

View File

@ -1,10 +1,8 @@
# Build Guide
This document is a guide to building Progressia from source. For quick reference, see
[Build Script Reference](BuildScriptReference.md).
This document is a guide to building Progressia from source.
Compilation should be possible on all platforms that support JDK 8 or later, however, packaging scripts require
additional programs in `PATH`.
Compilation should be possible on all platforms that support JDK 8 or later, however, packaging scripts require Bash.
This guide assumes you are familiar with using a terminal or Windows Command Prompt or PowerShell.
@ -143,8 +141,8 @@ build.
Some users might find the need to build for a specific set of platforms. Inclusion of GNU/Linux, Windows or MacOS
dependencies individually can be controlled with the following arguments to the `./gradlew build` command:
- `requestLinuxDependencies` requests that `natives-linux`, `natives-linux-arm32` and `natives-linux-arm64` binaries are included;
- `requestWindowsDependencies` requests that `natives-windows`, `natives-windows-arm64` and `natives-windows-x86` binaries are included;
- `requestMacOSDependencies` requests that `natives-macos`, `natives-macos-arm64` binaries are included.
- `requestWindowsDependencies` requests that `natives-windows` and `natives-windows-x86` binaries are included;
- `requestMacOSDependencies` requests that `natives-macos` binaries are included.
These requests can be applied in any combination. For example, the following command produces a build containing only
GNU/Linux and Windows natives:
@ -152,46 +150,54 @@ GNU/Linux and Windows natives:
./gradlew build requestLinuxDependencies requestWindowsDependencies
```
For finer control please edit `build.gradle` manually by adding the desired natives to the `project.ext.platforms` set like so:
```
project.ext.platforms = new HashSet<>()
project.ext.platforms.add 'natives-windows-x86'
```
## Packaging
A universal ZIP distribution, a Debian package and a Windows NSIS installer may be created automatically by the build
script.
### Creating a universal ZIP package
A universal cross-platform ZIP archive can be created with the following Gradle task:
```
./gradlew packageZip
```
Gradle will then build all artifacts necessary to run the game on all available platforms and package game files,
libraries, launch scripts, etc. into a compressed ZIP archive.
The resulting file can be found in `build/packages/`
A Debian package and a Windows installer can be created automatically on systems that support Bash. These tasks are delegated
by Gradle to `buildPackages.sh` in repository root. This script checks the environment and assembles the requested output; the
resulting files are moved into `build_packages`.
### Creating a Debian package
A Debian package can be created with the following Gradle task:
```
./gradlew packageDeb
./gradlew packageDebian
```
Gradle will then build all artifacts necessary to run the game on GNU/Linux (all three architectures) and invoke
`dpkg-deb`. Commands `dpkg-deb` must be available in system path in order to build the package.
`./buildPackages.sh debian`. Commands `dpkg-deb` and `fakeroot` must be available in system path in order to build the package.
### Creating a Windows installer
A Windows NSIS installer can be created with the following Gradle task:
A Windows installer can be created with the following Gradle task:
```
./gradlew packageNsis
./gradlew packageWindows
```
Gradle will then build all artifacts necessary to run the game on Windows (both x64 and x86 architectures) and invoke
`makensis`.
`./buildPackages.sh windows`.
Windows installers are implemented with [NSIS](https://nsis.sourceforge.io/). [ImageMagick](https://imagemagick.org),
a command-line image editing tool, is used to generate some assets for the installer. Commands `makensis` and
`convert` (from ImageMagick) must be available in system path in order to build the installer.
Windows installers are implemented with [NSIS](https://nsis.sourceforge.io/). Command `makensis` must be available in system
path in order to build the installer.
## Gradle tasks summary
- `buildLocal` creates a build optimized for current platform. Use this to quickly build the game during development.
- `buildCrossPlatform` creates a build that supports all known architectures. Use this to build a universal version of the game.
- `build` currently a synonym of `buildLocal`; creates a default build.
- `packageDebian` creates a Debian package. Do not invoke together with `packageWindows`.
- `packageWindows` creates a Windows installer. Do not invoke together with `packageDebian`.
- `requestLinuxDependencies` requests that `natives-linux`, `natives-linux-arm32` and `natives-linux-arm64` binaries are included when building.
- `requestWindowsDependencies` requests that `natives-windows` and `natives-windows-x86` binaries are included when building.
- `requestMacOSDependencies` requests that `natives-macos` binaries are included when building.
- `requestCrossPlatformDependencies` requests that all binaries are included when building.
All other basic and Java-related Gradle tasks are available as well.

View File

@ -1,112 +0,0 @@
# Build Script Reference
This document is a user's reference for the build script of Progressia. For a beginner-friendly guide, see
[Build Guide](BuildGuide.md).
## Gradle tasks summary
- `buildLocal` creates a build optimized for current platform. Use this to quickly build the game during development.
- `buildCrossPlatform` creates a build that supports all known architectures. Use this to build a universal version of the game.
- `build` currently a synonym of `buildLocal`; creates a default build.
- `packageZip` creates a universal ZIP. Incompatible with other `package` tasks.
- `packageDeb` creates a Debian package. Incompatible with other `package` tasks.
- `packageNsis` creates a Windows NSIS installer. Incompatible with other `package` tasks.
- `requestLinuxDependencies` requests that `natives-linux`, `natives-linux-arm32` and `natives-linux-arm64` binaries are included when building.
- `requestWindowsDependencies` requests that `natives-windows`, `natives-windows-arm64` and `natives-windows-x86` binaries are included when building.
- `requestMacOSDependencies` requests that `natives-macos` and `natives-macos-arm64` binaries are included when building.
- `requestCrossPlatformDependencies` requests that all binaries are included when building.
To execute a task, run `./gradlew <task-name>`.
`build`-type tasks output the executable JAR and all libraries required at runtime into `build/libs`. `package`-type
tasks output packages into `build/packages`.
## Packaging tasks
Some packaging tasks require additional software in `PATH`.
| Task | Commands required in `PATH` |
|---------------|------------------------------------------|
| `packageDeb` | `dpkg-deb` |
| `packageZip` | _none_ |
| `packageNsis` | `makensis`, `convert` (from ImageMagick) |
## Version and metadata
### Version scheme
Progressia builds are identified by four parameters: version, Git commit, Git branch and build ID.
Versions roughly follow [semantic versioning](https://semver.org/spec/v2.0.0.html), with each version fitting the
`MAJOR.MINOR.PATCH[-SUFFIX]` pattern. Depending on the build environment (see below), version is either "real" with
no metadata (e.g. `0.43.2` or `1.2.1-beta`) or a dummy fallback with build metadata (e.g. `999.0.0-2021_07_23` or
`999.0.0-WJ3`).
### Version detection
Build script considers three scenarios when determining the version:
1. `version` project property is set explicitly. This may be done in a variety of ways, for example with command line
argument `-Pversion=1.2.3`
(see [Gradle docs](https://docs.gradle.org/current/userguide/build_environment.html#sec:project_properties))
2. Local Git repository is found, and HEAD is tagged appropriately: version is the tag name with leading `v`
stripped. Example: `v1.2.3` is version `1.2.3`
3. Local Git repository is found, and some ancestor of HEAD is tagged appropriately: version is the tag name with
leading `v` stripped and PATCH incremented by one. Example: `v1.2.3` is version `1.2.4`
Tags not named like `vMAJOR.MINOR.PATCH[-SUFFIX]` are ignored for cases 2 and 3.
In all other cases, a fallback dummy value is used for version, appended with build ID or current date.
### Git metadata
Git commit is determined from `GIT_COMMIT` environment variable if it exists, or the state of the local Git
repository, if any, or `-`.
Git branch is determined from `GIT_BRANCH` environment variable if it exists, or the state of the local Git
repository, if any, or `-`.
The names of the environment variables are picked to assist Jenkins builds.
### Build ID
Build ID uniquely identifies artifacts produced by automated build systems. Build ID must be provided explicitly; it
is `-` unless specified otherwise.
The proposed scheme for naming build IDs is `<builder><build-system><build attempt no.>`. For example, builds
executed by WindCorp Jenkins suite have build IDs like `WJ3` or `WJ142`.
Build ID may be set with `buildId` project property. This may be done in a variety of ways, for example with command
line argument `-PbuildId=WJ3`
(see [Gradle docs](https://docs.gradle.org/current/userguide/build_environment.html#sec:project_properties)).
## Native libraries
LWJGL uses native libraries. Build script declares platform-specific dependencies based on the set of target
platforms, `project.ext.lwjgl.targets` (aka `lwjgl.targets`). These dependencies are added to `runtimeOnly`
configuration.
When this set is empty, the script selects natives for current platform. Otherwise, all platforms in the set are
included.
`lwjgl.targets` is populated automatically by packaging tasks and by `buildCrossPlatform`. To add extra targets,
``requestXxxDependencies` tasks may be used.
Target selection mechanism may be overridden with `forceTargets` project property. This may be done in a variety of
ways, for example with command line argument `-PforceTargets=windows-x86,local`
(see [Gradle docs](https://docs.gradle.org/current/userguide/build_environment.html#sec:project_properties)). The
value is a comma-separated list of target architectures. `local` target will be replaced with the automatically
detected current architecture.
### Available targets
| Name | Task |
|-----------------|------------------------------|
| `linux` | `requestLinuxDependencies` |
| `linux-arm32` | `requestLinuxDependencies` |
| `linux-arm64` | `requestLinuxDependencies` |
| `windows` | `requestWindowsDependencies` |
| `windows-arm64` | `requestWindowsDependencies` |
| `windows-x86` | `requestWindowsDependencies` |
| `macos` | `requestMacOSDependencies` |
| `macos-arm64` | `requestMacOSDependencies` |

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

259
gradlew vendored
View File

@ -1,7 +1,7 @@
#!/bin/sh
#!/usr/bin/env sh
#
# Copyright © 2015-2021 the original authors.
# 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.
@ -17,113 +17,78 @@
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
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
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
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
MAX_FD="maximum"
warn () {
echo "$*"
} >&2
}
die () {
echo
echo "$*"
echo
exit 1
} >&2
}
# 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 ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
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
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD=$JAVA_HOME/bin/java
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -132,7 +97,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
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
@ -140,95 +105,79 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
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
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# 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" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
# 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
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
# 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" "$@"

25
gradlew.bat vendored
View File

@ -29,9 +29,6 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@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"
@ -40,7 +37,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -54,7 +51,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@ -64,14 +61,28 @@ 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 %*
"%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

View File

@ -1,45 +0,0 @@
/*
* JPUtil
* Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.jputil;
public class ConstantsMapException extends RuntimeException {
private static final long serialVersionUID = -4298704891780063127L;
public ConstantsMapException() {
}
public ConstantsMapException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public ConstantsMapException(String message, Throwable cause) {
super(message, cause);
}
public ConstantsMapException(String message) {
super(message);
}
public ConstantsMapException(Throwable cause) {
super(cause);
}
}

View File

@ -1,307 +0,0 @@
/*
* JPUtil
* Copyright (C) 2019-2022 OLEGSHA/Javapony and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.jputil;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntPredicate;
import java.util.function.Predicate;
import java.util.regex.Pattern;
public class IntConstantsMap {
private final Map<Integer, String> namesByValue;
private final Map<String, Integer> valuesByName;
protected IntConstantsMap(Map<Integer, String> namesByValue, Map<String, Integer> valuesByName) {
this.namesByValue = namesByValue;
this.valuesByName = valuesByName;
}
public int getValue(String name) {
Integer value = valuesByName.get(name);
if (value == null) {
throw new NoSuchElementException("No constant with name " + name);
}
return value.intValue();
}
public boolean hasConstant(String name) {
return valuesByName.containsKey(name);
}
public String getName(int value) {
String name = namesByValue.get(value);
if (name == null) {
throw new NoSuchElementException("No constant with value " + value);
}
return name;
}
public boolean hasConstant(int value) {
return namesByValue.containsKey(value);
}
public Map<String, Integer> getAll() {
return valuesByName;
}
@Override
public String toString() {
return valuesByName.toString();
}
public static Builder from(Class<?> clazz) {
return new Builder(clazz);
}
public static class Builder {
@FunctionalInterface
public static interface Filter {
boolean test(String name, int value);
}
public class ConstantSpec {
public String name;
public int value;
public void drop() {
if (!extra.contains(name)) {
name = null;
}
}
}
private final List<Consumer<ConstantSpec>> transforms = new ArrayList<>();
private final Set<String> extra = new HashSet<>();
private final Class<?> source;
public Builder(Class<?> source) {
this.source = source;
}
public Builder apply(Consumer<ConstantSpec> transform) {
transforms.add(transform);
return this;
}
public Builder only(Filter filter) {
return apply(s -> {
if (!filter.test(s.name, s.value)) {
s.drop();
}
});
}
public Builder only(Predicate<String> nameFilter) {
return apply(s -> {
if (!nameFilter.test(s.name)) {
s.drop();
}
});
}
public Builder onlyValued(IntPredicate valueFilter) {
return apply(s -> {
if (!valueFilter.test(s.value)) {
s.drop();
}
});
}
public Builder regex(String regex) {
return only(Pattern.compile(regex).asPredicate());
}
public Builder prefix(String prefix) {
return only(n -> n.startsWith(prefix) && n.length() > prefix.length());
}
public Builder exclude(Filter filter) {
return only((n, v) -> !filter.test(n, v));
}
public Builder exclude(Predicate<String> nameFilter) {
return only(nameFilter.negate());
}
public Builder exclude(String... names) {
Set<String> excluded = new HashSet<>();
for (String name : names) {
excluded.add(name);
}
return exclude(excluded::contains);
}
public Builder excludeRegex(String... nameRegexes) {
List<Predicate<String>> tests = new ArrayList<>();
for (String regex : nameRegexes) {
tests.add(Pattern.compile(regex).asPredicate());
}
return only((n, v) -> {
for (Predicate<String> test : tests) {
if (test.test(n)) {
return false;
}
}
return true;
});
}
public Builder extra(String... names) {
for (String name : names) {
extra.add(name);
}
return this;
}
public Builder rename(Function<String, String> renamer) {
apply(s -> {
s.name = renamer.apply(s.name);
});
return this;
}
public Builder stripPrefix(String prefix) {
return apply(s -> {
if (s.name.startsWith(prefix)) {
s.name = s.name.substring(prefix.length());
} else if (extra.contains(s.name)) {
return;
} else {
s.drop();
}
});
}
public IntConstantsMap scan() {
return build(true);
}
public IntConstantsMap scanAll() {
return build(false);
}
private IntConstantsMap build(boolean onlyPublic) {
Map<Integer, String> namesByValue = new HashMap<>();
Map<String, Integer> valuesByName = new HashMap<>();
BiConsumer<String, Integer> putter = (name, value) -> {
if (namesByValue.containsKey(value)) {
throw newDuplicateException("value", value, name, namesByValue.get(value));
}
if (valuesByName.containsKey(name)) {
throw newDuplicateException("name", name, value, valuesByName.get(name));
}
namesByValue.put(value, name);
valuesByName.put(name, value);
};
try {
for (Field field : source.getDeclaredFields()) {
processField(field, putter, onlyPublic);
}
} catch (IllegalAccessException e) {
throw new ConstantsMapException(e);
}
return new IntConstantsMap(
Collections.unmodifiableMap(namesByValue),
Collections.unmodifiableMap(valuesByName)
);
}
private void processField(Field field, BiConsumer<String, Integer> putter, boolean onlyPublic)
throws IllegalAccessException {
if (!Modifier.isStatic(field.getModifiers())) {
return;
}
if (!Modifier.isFinal(field.getModifiers())) {
return;
}
boolean clearAccessible = false;
if (!Modifier.isPublic(field.getModifiers())) {
if (onlyPublic) {
return;
} else if (!isAccessibleFlagSet(field)) {
field.setAccessible(true);
clearAccessible = true;
}
}
try {
ConstantSpec spec = new ConstantSpec();
spec.name = field.getName();
spec.value = field.getInt(null);
for (Consumer<ConstantSpec> t : transforms) {
t.accept(spec);
if (spec.name == null) {
return;
}
}
putter.accept(spec.name, spec.value);
} finally {
if (clearAccessible) {
field.setAccessible(false);
}
}
}
/*
* Yes, this method exists only so that neither Java 8 nor Java 9 complain about deprecation.
*/
@Deprecated
private boolean isAccessibleFlagSet(Field f) {
return f.isAccessible();
}
private ConstantsMapException newDuplicateException(String what, Object common, Object current, Object old) {
return new ConstantsMapException(
String.format(
"Duplicate %1$s: %2$s -> %3$s and %2$s -> %4$s",
what,
common,
current,
old
)
);
}
}
}

View File

@ -15,180 +15,9 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.apache.logging.log4j.LogManager;
import ru.windcorp.progressia.common.util.crash.CrashReports;
/**
* A class providing access to build metadata.
*/
public class Progressia {
private static final String NAME = "Progressia";
private static String version;
private static String gitCommit;
private static String gitBranch;
private static String buildId;
static {
try {
Manifest manifest = findManifest();
if (manifest == null) {
setDevelopmentMetadata();
LogManager.getLogger().info(
"Manifest with Specification-Title not found. "
+ "Either you are in a development environment or something has gone horribly wrong with classloaders."
);
} else {
fillMetadata(manifest);
}
} catch (Throwable t) {
CrashReports.crash(t, "Something went wrong while loading metadata");
}
}
private static Manifest findManifest() {
try {
Enumeration<URL> resources = Progressia.class.getClassLoader().getResources("META-INF/MANIFEST.MF");
Collection<IOException> exceptions = new ArrayList<>();
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
try {
Manifest manifest = new Manifest(url.openStream());
Attributes mainAttributes = manifest.getMainAttributes();
if (NAME.equals(mainAttributes.getValue("Specification-Title"))) {
return manifest;
}
} catch (IOException e) {
exceptions.add(e);
}
}
if (exceptions.isEmpty()) {
return null;
}
IOException scapegoat = null;
for (IOException e : exceptions) {
if (scapegoat == null) {
scapegoat = e;
} else {
scapegoat.addSuppressed(e);
}
}
throw CrashReports.report(scapegoat, "Could not read manifest");
} catch (IOException e) {
throw CrashReports.report(e, "Could not read manifest");
}
}
private static void setDevelopmentMetadata() {
version = "dev";
gitCommit = "-";
gitBranch = "-";
buildId = "-";
}
private static void fillMetadata(Manifest manifest) {
version = getAttributeOrCrash(manifest, "Implementation-Version");
gitCommit = getAttributeOrCrash(manifest, "Implementation-Version-Git-Commit");
gitBranch = getAttributeOrCrash(manifest, "Implementation-Version-Git-Branch");
buildId = getAttributeOrCrash(manifest, "Implementation-Version-BuildId");
}
private static String getAttributeOrCrash(Manifest manifest, String key) {
String result = manifest.getMainAttributes().getValue(key);
if (result == null) {
throw CrashReports.report(null, "Manifest exists but attribute " + key + " not found");
}
return result;
}
public static String getName() {
return NAME;
}
/**
* Returns the version of the game as a String. Version data is retrieved
* from a {@code META-INF/MANIFEST.MF} file located in the main JAR. Version
* format depends on way the game was built:
* <ul>
* <li><code>dev</code> if no matching manifest was found, e.g. when launching from an IDE</li>
* <li>The value of <code>Implementation-Version</code> specified in the manifest:
* <ul>
* <li>[Stage-]Major.Minor.Patch, e.g. <code>alpha-0.3.2</code> or <code>1.4.2</code>, for released versions</li>
* <li>BuildId, e.g. <code>WJ7</code>, for snapshots built by automation systems</li>
* <li>YYYY-MM-DD, e.g. <code>2021-12-32</code>, for snapshots built manually</li>
* </ul>
* </li>
* </ul>
*
* @return the version
*/
public static String getVersion() {
return version;
}
public static String getFullerVersion() {
if (isDefaultGitBranch() || "-".equals(gitBranch)) {
return version;
} else {
return String.format("%s/%s", version, gitBranch);
}
}
/**
* @return the buildId or <code>"-"</code>
*/
public static String getBuildId() {
return buildId;
}
/**
* @return the Git commit or <code>"-"</code>
*/
public static String getGitCommit() {
return gitCommit;
}
public static String getGitCommitShort() {
if (gitCommit == null || "-".equals(gitCommit)) {
return gitCommit;
}
return gitCommit.substring(0, Math.min(7, gitCommit.length()));
}
/**
* @return the Git branch or <code>"-"</code>
*/
public static String getGitBranch() {
return gitBranch;
}
public static boolean isDefaultGitBranch() {
return "master".equals(gitBranch) || "main".equals(gitBranch);
}
public static String getFullVersion() {
return String.format("%s/%s/%s/%s", version, gitBranch, getGitCommitShort(), buildId);
}
}

View File

@ -18,8 +18,6 @@
package ru.windcorp.progressia;
import org.apache.logging.log4j.LogManager;
import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.util.crash.analyzers.OutOfMemoryAnalyzer;
@ -34,8 +32,6 @@ public class ProgressiaLauncher {
public static void launch(String[] args, Proxy proxy) {
arguments = args.clone();
setupCrashReports();
LogManager.getRootLogger().info("Launching " + Progressia.getName() + " version " + Progressia.getFullVersion());
proxy.initialize();
ProgressiaLauncher.proxy = proxy;
@ -48,7 +44,6 @@ public class ProgressiaLauncher {
private static void setupCrashReports() {
// Context providers
CrashReports.registerProvider(new VersionProvider());
CrashReports.registerProvider(new OSContextProvider());
CrashReports.registerProvider(new RAMContextProvider());
CrashReports.registerProvider(new JavaVersionContextProvider());

View File

@ -18,21 +18,42 @@
package ru.windcorp.progressia.client;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import ru.windcorp.progressia.client.comms.DefaultClientCommsListener;
import ru.windcorp.progressia.client.comms.ServerCommsChannel;
import ru.windcorp.progressia.client.events.ClientEvent;
import ru.windcorp.progressia.client.events.NewLocalEntityEvent;
import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.world.Camera;
import ru.windcorp.progressia.client.graphics.world.EntityAnchor;
import ru.windcorp.progressia.client.graphics.world.LayerWorld;
import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
import ru.windcorp.progressia.client.graphics.world.hud.HUDManager;
import ru.windcorp.progressia.client.world.WorldRender;
import ru.windcorp.progressia.common.util.crash.ReportingEventBus;
import ru.windcorp.progressia.common.world.DefaultWorldData;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.test.LayerAbout;
import ru.windcorp.progressia.test.LayerDebug;
import ru.windcorp.progressia.test.LayerTestUI;
public class Client {
private final WorldRender world;
private final LayerWorld layerWorld = new LayerWorld(this);
private final LayerTestUI layerTestUI = new LayerTestUI();
private final LayerAbout layerAbout = new LayerAbout();
private final LayerDebug layerDebug = new LayerDebug();
private final LocalPlayer localPlayer = new LocalPlayer(this);
private final Camera camera = new Camera((float) Math.toRadians(70));
private final EventBus eventBus = ReportingEventBus.create("ClientEvents");
private final HUDManager hudManager = new HUDManager(this);
private final ServerCommsChannel comms;
@ -41,6 +62,22 @@ public class Client {
this.comms = comms;
comms.addListener(new DefaultClientCommsListener(this));
subscribe(this);
}
public void install() {
GUI.addBottomLayer(layerWorld);
GUI.addTopLayer(layerTestUI);
hudManager.install();
GUI.addTopLayer(layerAbout);
}
public void remove() {
GUI.removeLayer(layerWorld);
GUI.removeLayer(layerTestUI);
hudManager.remove();
GUI.removeLayer(layerAbout);
GUI.removeLayer(layerDebug);
}
public WorldRender getWorld() {
@ -62,18 +99,45 @@ public class Client {
public ServerCommsChannel getComms() {
return comms;
}
public HUDManager getHUD() {
return hudManager;
}
public void onLocalPlayerEntityChanged(EntityData entity, EntityData lastKnownEntity) {
if (entity == null) {
public void toggleDebugLayer() {
if (GUI.getLayers().contains(layerDebug)) {
GUI.removeLayer(layerDebug);
} else {
GUI.addTopLayer(layerDebug);
}
}
@Subscribe
private void onLocalPlayerEntityChanged(NewLocalEntityEvent e) {
if (e.getNewEntity() == null) {
getCamera().setAnchor(null);
return;
}
getCamera().setAnchor(
new EntityAnchor(
getWorld().getEntityRenderable(entity)
getWorld().getEntityRenderable(e.getNewEntity())
)
);
}
public void subscribe(Object object) {
eventBus.register(object);
}
public void unsubscribe(Object object) {
eventBus.unregister(object);
}
public void postEvent(ClientEvent event) {
event.setClient(this);
eventBus.post(event);
event.setClient(null);
}
}

View File

@ -25,9 +25,9 @@ import ru.windcorp.progressia.client.graphics.backend.RenderTaskQueue;
import ru.windcorp.progressia.client.graphics.flat.FlatRenderProgram;
import ru.windcorp.progressia.client.graphics.font.GNUUnifontLoader;
import ru.windcorp.progressia.client.graphics.font.Typefaces;
import ru.windcorp.progressia.client.graphics.gui.ColorScheme;
import ru.windcorp.progressia.client.graphics.texture.Atlases;
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.graphics.world.hud.HUDTextures;
import ru.windcorp.progressia.client.localization.Localizer;
import ru.windcorp.progressia.common.resource.ResourceManager;
import ru.windcorp.progressia.common.util.crash.CrashReports;
@ -48,11 +48,11 @@ public class ClientProxy implements Proxy {
() -> Typefaces
.setDefault(GNUUnifontLoader.load(ResourceManager.getResource("assets/unifont-13.0.03.hex.gz")))
);
RenderTaskQueue.waitAndInvoke(HUDTextures::loadItemAmountTypeface);
} catch (InterruptedException e) {
throw CrashReports.report(e, "ClientProxy failed");
}
ColorScheme.load(ResourceManager.getResource("assets/default.colorScheme"));
Localizer.getInstance().setLanguage("en-US");
TestContent.registerContent();

View File

@ -20,14 +20,10 @@ package ru.windcorp.progressia.client;
import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel;
import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.Layer;
import ru.windcorp.progressia.client.graphics.world.LayerWorld;
import ru.windcorp.progressia.common.world.DefaultWorldData;
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
import ru.windcorp.progressia.server.ServerState;
import ru.windcorp.progressia.test.LayerAbout;
import ru.windcorp.progressia.test.LayerTestText;
import ru.windcorp.progressia.test.LayerTestUI;
import ru.windcorp.progressia.test.TestContent;
public class ClientState {
@ -70,24 +66,14 @@ public class ClientState {
if (client != null && client.getLocalPlayer().hasEntity()) {
GUI.removeLayer(layer);
// TODO refactor, this shouldn't be here
LayerWorld layerWorld = new LayerWorld(client);
LayerTestUI layerUI = new LayerTestUI();
LayerAbout layerAbout = new LayerAbout();
GUI.addBottomLayer(layerWorld);
GUI.addTopLayer(layerUI);
GUI.addTopLayer(layerAbout);
client.install();
}
}));
}
public static void disconnectFromLocalServer() {
getInstance().getComms().disconnect();
for (Layer layer : GUI.getLayers()) {
GUI.removeLayer(layer);
}
getInstance().remove();
}
private ClientState() {

View File

@ -0,0 +1,68 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.events;
import ru.windcorp.progressia.client.Client;
/**
* An interface for all events issued by a {@link Client}.
*/
public interface ClientEvent {
/**
* Returns the client instance that this event happened on.
*
* @return the client
*/
Client getClient();
/**
* Sets the client instance that the event is posted on. The value provided
* to this method must be returned by subsequent calls to
* {@link #getClient()}. Do not call this method when handling the event.
*
* @param client the client dispatching the event or {@code null} to unbind
* any previously bound client
*/
void setClient(Client client);
/**
* A default implementation of {@link ClientEvent}. This is not necessarily
* extended by client events.
*/
public static abstract class Default implements ClientEvent {
private Client client;
public Default(Client client) {
this.client = client;
}
@Override
public Client getClient() {
return client;
}
@Override
public void setClient(Client client) {
this.client = client;
}
}
}

View File

@ -0,0 +1,51 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.events;
import ru.windcorp.progressia.client.Client;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
public interface NewLocalEntityEvent extends ClientEvent {
EntityDataPlayer getNewEntity();
EntityDataPlayer getPreviousEntity();
public class Immutable extends ClientEvent.Default implements NewLocalEntityEvent {
private final EntityDataPlayer newEntity;
private final EntityDataPlayer previousEntity;
public Immutable(Client client, EntityDataPlayer newEntity, EntityDataPlayer previousEntity) {
super(client);
this.newEntity = newEntity;
this.previousEntity = previousEntity;
}
@Override
public EntityDataPlayer getNewEntity() {
return newEntity;
}
@Override
public EntityDataPlayer getPreviousEntity() {
return previousEntity;
}
}
}

View File

@ -34,7 +34,13 @@ public class Colors {
DEBUG_BLUE = toVector(0xFF0000FF),
DEBUG_CYAN = toVector(0xFF00FFFF),
DEBUG_MAGENTA = toVector(0xFFFF00FF),
DEBUG_YELLOW = toVector(0xFFFFFF00);
DEBUG_YELLOW = toVector(0xFFFFFF00),
LIGHT_GRAY = toVector(0xFFCBCBD0),
BLUE = toVector(0xFF37A2E6),
HOVER_BLUE = toVector(0xFFC3E4F7),
DISABLED_GRAY = toVector(0xFFE5E5E5),
DISABLED_BLUE = toVector(0xFFB2D8ED);
public static Vec4 toVector(int argb) {
return toVector(argb, new Vec4());
@ -49,6 +55,25 @@ public class Colors {
output = new Vec4();
return color.mul(multiplier, multiplier, multiplier, 1, output);
}
public static Vec4 mix(Vec4 zero, Vec4 one, float t, Vec4 output) {
if (output == null) {
output = new Vec4();
}
if (t <= 0) {
return output.set(zero);
} else if (t >= 1) {
return output.set(one);
}
return output.set(
zero.x * (1 - t) + one.x * t,
zero.y * (1 - t) + one.y * t,
zero.z * (1 - t) + one.z * t,
zero.w * (1 - t) + one.w * t
);
}
public static Vec4 toVector(int argb, Vec4 output) {
output.w = ((argb & 0xFF000000) >>> 24) / (float) 0xFF; // Alpha

View File

@ -0,0 +1,61 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
public class ExponentAnimation {
private final float speed;
private float value;
public ExponentAnimation(float speed, float value) {
this.speed = speed;
this.value = value;
}
public float getSpeed() {
return speed;
}
public float getValue() {
return value;
}
public void setValue(float value) {
this.value = value;
}
public float update(float target, double timeStep) {
float difference = value - target;
value += difference * (1 - Math.exp(speed * timeStep));
float newDifference = value - target;
if (difference * newDifference < 0) {
// Whoops, we've overshot
value = target;
}
return value;
}
public float updateForFrame(float target) {
return update(target, GraphicsInterface.getFrameLength());
}
}

View File

@ -65,6 +65,12 @@ public class GUI {
layer.onRemoved();
});
}
public static void updateLayer(Layer layer) {
modify(layers -> {
// Do nothing
});
}
private static void modify(LayerStackModification mod) {
MODIFICATION_QUEUE.add(mod);

View File

@ -1,56 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2022 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.backend;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWErrorCallback;
import ru.windcorp.jputil.ConstantsMapException;
import ru.windcorp.jputil.IntConstantsMap;
import ru.windcorp.progressia.common.util.crash.CrashReports;
public class GLFWErrorHandler {
private static final IntConstantsMap ERROR_CODES;
static {
try {
ERROR_CODES = IntConstantsMap.from(GLFW.class)
.stripPrefix("GLFW_")
.onlyValued(i -> i >= 0x10000 && i <= 0x1FFFF)
.extra("GLFW_NO_ERROR")
.scan();
} catch (ConstantsMapException e) {
throw CrashReports.report(e, "Could not analyze GLFW error codes");
}
}
public void onError(int errorCode, long descriptionPointer) {
String description = GLFWErrorCallback.getDescription(descriptionPointer);
String errorCodeName;
if (ERROR_CODES.hasConstant(errorCode)) {
errorCodeName = ERROR_CODES.getName(errorCode);
} else {
errorCodeName = "<unknown " + Integer.toHexString(errorCode) + ">";
}
throw CrashReports.report(null, "GLFW error detected: " + errorCodeName + " %s", description);
}
}

View File

@ -15,31 +15,20 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.backend;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.system.MemoryUtil.*;
import java.io.IOException;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWImage;
import org.lwjgl.opengl.GL;
import com.google.common.eventbus.Subscribe;
import ru.windcorp.progressia.Progressia;
import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.input.FrameResizeEvent;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.client.graphics.texture.TextureDataEditor;
import ru.windcorp.progressia.client.graphics.texture.TextureLoader;
import ru.windcorp.progressia.client.graphics.texture.TextureSettings;
import ru.windcorp.progressia.common.resource.Resource;
import ru.windcorp.progressia.common.resource.ResourceManager;
import ru.windcorp.progressia.common.util.crash.CrashReports;
class LWJGLInitializer {
@ -56,7 +45,6 @@ class LWJGLInitializer {
setupWindowCallbacks();
glfwShowWindow(GraphicsBackend.getWindowHandle());
GraphicsBackend.onFrameResized(GraphicsBackend.getWindowHandle(), 800, 600);
}
private static void checkEnvironment() {
@ -64,12 +52,8 @@ class LWJGLInitializer {
}
private static void initializeGLFW() {
GLFWErrorCallback.create(new GLFWErrorHandler()::onError).set();
if (!glfwInit()) {
throw CrashReports.report(null, "GLFW could not be initialized: glfwInit() has failed");
}
// TODO Do GLFW error handling: check glfwInit, setup error callback
glfwInit();
GraphicsBackend.setGLFWInitialized(true);
}
@ -77,18 +61,16 @@ class LWJGLInitializer {
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);
String windowTitle = Progressia.getName() + " " + Progressia.getFullerVersion();
long handle = glfwCreateWindow(800, 600, windowTitle, NULL, NULL);
if (handle == 0) {
throw CrashReports.report(null, "Could not create game window");
}
long handle = glfwCreateWindow(900, 900, "ProgressiaTest", NULL, NULL);
// TODO Check that handle != NULL
GraphicsBackend.setWindowHandle(handle);
glfwMakeContextCurrent(handle);
glfwSwapInterval(0); // TODO: remove after config system is added
glfwSwapInterval(0); // TODO: remove after config system is added
}
private static void positionWindow() {
@ -97,30 +79,8 @@ class LWJGLInitializer {
}
private static void createWindowIcons() {
if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND) {
// Wayland does not support changing window icons
return;
}
final String prefix = "assets/icons/";
// TODO Auto-generated method stub
String[] sizes = ResourceManager.getResource(prefix + "logoSizes.txt").readAsString().split(" ");
try (GLFWImage.Buffer buffer = GLFWImage.malloc(sizes.length)) {
for (int i = 0; i < sizes.length; ++i) {
Resource resource = ResourceManager.getResource(prefix + "logo" + sizes[i].trim() + ".png");
TextureDataEditor icon = TextureLoader.loadPixels(resource, new TextureSettings(false, true));
buffer.position(i)
.width(icon.getContentWidth())
.height(icon.getContentHeight())
.pixels(icon.getData().getData());
}
glfwSetWindowIcon(GraphicsBackend.getWindowHandle(), buffer);
} catch (IOException e) {
throw CrashReports.report(e, "Could not load window icons");
}
}
private static void initializeOpenGL() {
@ -152,19 +112,19 @@ class LWJGLInitializer {
glfwSetScrollCallback(handle, InputHandler::handleWheelScroll);
GraphicsInterface.subscribeToInputEvents(new Object() {
@Subscribe
public void onFrameResized(FrameResizeEvent event) {
GUI.invalidateEverything();
}
@Subscribe
public void onInputEvent(InputEvent event) {
GUI.dispatchInput(event);
}
});
}
}

View File

@ -191,7 +191,8 @@ public class RenderTarget {
assembleCurrentClipFromFaces();
float depth = this.depth--;
Mat4 transform = new Mat4().translate(0, 0, depth).mul(getTransform());
final float kostyl = 1e-2f;
Mat4 transform = new Mat4().translate(0, 0, depth).scale(1, 1, kostyl).mul(getTransform());
assembled.add(new Clip(maskStack, transform, renderable));
}

View File

@ -32,20 +32,23 @@ public class Font {
private final int style;
private final float align;
private final Vec4 color;
private final int scale;
public Font(Typeface typeface, int style, float align, Vec4 color) {
public Font(Typeface typeface, int style, float align, Vec4 color, int scale) {
this.typeface = typeface;
this.style = style;
this.align = align;
this.color = color;
this.scale = scale;
}
public Font(Typeface typeface, int style, float align, int color) {
this(typeface, style, align, Colors.toVector(color));
public Font(Typeface typeface, int style, float align, int color, int scale) {
this(typeface, style, align, Colors.toVector(color), scale);
}
public Font(Typeface typeface) {
this(typeface, Typeface.Style.PLAIN, Typeface.ALIGN_LEFT, Colors.WHITE);
this(typeface, Typeface.Style.PLAIN, Typeface.ALIGN_LEFT, Colors.WHITE, 2);
}
public Font() {
@ -67,31 +70,63 @@ public class Font {
public Vec4 getColor() {
return color;
}
public Renderable assemble(
CharSequence chars,
float maxWidth
) {
return typeface.assembleStatic(chars, style, align, maxWidth, color);
public int getScale() {
return scale;
}
private Renderable applyScale(Renderable unscaled) {
if (scale == 1) {
return unscaled;
}
return renderer -> {
renderer.pushTransform().scale(scale);
unscaled.render(renderer);
renderer.popTransform();
};
}
public Renderable assembleDynamic(
Supplier<CharSequence> supplier,
float maxWidth
) {
return typeface.assembleDynamic(supplier, style, align, maxWidth, color);
public Renderable assemble(CharSequence chars, float maxWidth) {
return applyScale(typeface.assembleStatic(chars, style, align, maxWidth, color));
}
public Renderable assembleDynamic(Supplier<CharSequence> supplier, float maxWidth) {
return applyScale(typeface.assembleDynamic(supplier, style, align, maxWidth, color));
}
public Renderable assemble(CharSequence chars) {
return assemble(chars, Float.POSITIVE_INFINITY);
}
public Renderable assembleDynamic(Supplier<CharSequence> supplier) {
return assembleDynamic(supplier, Float.POSITIVE_INFINITY);
}
public int getWidth(CharSequence chars, float maxWidth) {
return typeface.getWidth(chars, style, align, maxWidth);
return scale * typeface.getWidth(chars, style, align, maxWidth);
}
public int getHeight(CharSequence chars, float maxWidth) {
return typeface.getHeight(chars, style, align, maxWidth);
return scale * typeface.getHeight(chars, style, align, maxWidth);
}
public Vec2i getSize(CharSequence chars, float maxWidth, Vec2i result) {
return typeface.getSize(chars, style, align, maxWidth, result);
result = typeface.getSize(chars, style, align, maxWidth, result);
result.mul(scale);
return result;
}
public int getWidth(CharSequence chars) {
return getWidth(chars, Float.POSITIVE_INFINITY);
}
public int getHeight(CharSequence chars) {
return getHeight(chars, Float.POSITIVE_INFINITY);
}
public Vec2i getSize(CharSequence chars, Vec2i result) {
return getSize(chars, Float.POSITIVE_INFINITY, result);
}
public boolean supports(char c) {
@ -106,7 +141,7 @@ public class Font {
* @return the new font
*/
public Font withStyle(int style) {
return new Font(getTypeface(), style, getAlign(), getColor());
return new Font(getTypeface(), style, getAlign(), getColor(), getScale());
}
public Font deriveBold() {
@ -158,15 +193,19 @@ public class Font {
}
public Font withAlign(float align) {
return new Font(getTypeface(), getStyle(), align, getColor());
return new Font(getTypeface(), getStyle(), align, getColor(), getScale());
}
public Font withColor(Vec4 color) {
return new Font(getTypeface(), getStyle(), getAlign(), color);
return new Font(getTypeface(), getStyle(), getAlign(), color, getScale());
}
public Font withColor(int color) {
return new Font(getTypeface(), getStyle(), getAlign(), color);
return new Font(getTypeface(), getStyle(), getAlign(), color, getScale());
}
public Font withScale(int scale) {
return new Font(getTypeface(), getStyle(), getAlign(), getColor(), scale);
}
}

View File

@ -45,7 +45,7 @@ public class GNUUnifontLoader {
private static final AtlasGroup ATLAS_GROUP_GNU_UNIFONT = new AtlasGroup("GNUUnifont", 1 << 12);
private static final TextureSettings TEXTURE_SETTINGS = new TextureSettings(false, false);
private static final TextureSettings TEXTURE_SETTINGS = new TextureSettings(false);
private static final int BITS_PER_HEX_DIGIT = 4;
private static final int PREFIX_LENGTH = "0000:".length();

View File

@ -48,7 +48,9 @@ public abstract class BasicButton extends Component {
this.label = label;
setLayout(new LayoutAlign(10));
addChild(this.label);
if (label != null) {
addChild(this.label);
}
setFocusable(true);
reassembleAt(ARTrigger.HOVER, ARTrigger.FOCUS, ARTrigger.ENABLE);
@ -151,5 +153,9 @@ public abstract class BasicButton extends Component {
public Label getLabel() {
return label;
}
public boolean hasLabel() {
return label != null;
}
}

View File

@ -18,11 +18,16 @@
package ru.windcorp.progressia.client.graphics.gui;
import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.Colors;
public class Button extends BasicButton {
public static final int MARGIN = 2;
public static final int BORDER = 2;
public Button(String name, String label, Font labelFont) {
super(name, label, labelFont);
}
@ -37,26 +42,53 @@ public class Button extends BasicButton {
@Override
protected void assembleSelf(RenderTarget target) {
String state;
if (!isEnabled()) {
state = "Disabled";
} else if (isPressed()) {
state = "Pressed";
} else if (isHovered()) {
state = "Hovered";
} else if (isFocused()) {
state = "Focused";
} else {
state = "Inactive";
}
// Border
target.fill(getX(), getY(), getWidth(), getHeight(), ColorScheme.get("Core:ButtonBorder" + state));
Vec4 borderColor;
if (isPressed() || isHovered() || isFocused()) {
borderColor = Colors.BLUE;
} else {
borderColor = Colors.LIGHT_GRAY;
}
target.fill(getX(), getY(), getWidth(), getHeight(), borderColor);
// Inside area
target.fill(getX() + 2, getY() + 2, getWidth() - 4, getHeight() - 4, ColorScheme.get("Core:ButtonFill" + state));
if (isPressed()) {
// Do nothing
} else {
Vec4 backgroundColor;
if (isHovered() && isEnabled()) {
backgroundColor = Colors.HOVER_BLUE;
} else {
backgroundColor = Colors.WHITE;
}
target.fill(
getX() + MARGIN,
getY() + MARGIN,
getWidth() - 2 * MARGIN,
getHeight() - 2 * MARGIN,
backgroundColor
);
}
// Change label font color
getLabel().setFont(getLabel().getFont().withColor(ColorScheme.get("Core:ButtonText" + state)));
if (hasLabel()) {
if (isPressed()) {
getLabel().setFont(getLabel().getFont().withColor(Colors.WHITE));
} else {
getLabel().setFont(getLabel().getFont().withColor(Colors.BLACK));
}
}
}
@Override
protected void postAssembleSelf(RenderTarget target) {
// Apply disable tint
if (!isEnabled()) {
target.fill(getX(), getY(), getWidth(), getHeight(), Colors.toVector(0x88FFFFFF));
}
}
}

View File

@ -18,6 +18,8 @@
package ru.windcorp.progressia.client.graphics.gui;
import glm.vec._2.i.Vec2i;
import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.font.Typefaces;
@ -40,29 +42,35 @@ public class Checkbox extends BasicButton {
int size = getPreferredSize().x;
int x = getX();
int y = getY() + (getHeight() - size) / 2;
String state;
if (!Checkbox.this.isEnabled()) {
state = "Disabled";
} else if (Checkbox.this.isPressed()) {
state = "Pressed";
} else if (Checkbox.this.isHovered()) {
state = "Hovered";
} else if (Checkbox.this.isFocused()) {
state = "Focused";
} else {
state = "Inactive";
}
// Border
target.fill(x, y, size, size, ColorScheme.get("Core:CheckboxBorder" + state));
Vec4 borderColor;
if (Checkbox.this.isPressed() || Checkbox.this.isHovered() || Checkbox.this.isFocused()) {
borderColor = Colors.BLUE;
} else {
borderColor = Colors.LIGHT_GRAY;
}
target.fill(x, y, size, size, borderColor);
// Inside area
target.fill(x + 2, y + 2, size - 4, size - 4, ColorScheme.get("Core:CheckboxFill" + state));
if (Checkbox.this.isPressed()) {
// Do nothing
} else {
Vec4 backgroundColor;
if (Checkbox.this.isHovered() && Checkbox.this.isEnabled()) {
backgroundColor = Colors.HOVER_BLUE;
} else {
backgroundColor = Colors.WHITE;
}
target.fill(x + 2, y + 2, size - 4, size - 4, backgroundColor);
}
// "Tick"
if (Checkbox.this.isChecked()) {
target.fill(x + 4, y + 4, size - 8, size - 8, ColorScheme.get("Core:CheckboxCheck" + state));
target.fill(x + 4, y + 4, size - 8, size - 8, Colors.BLUE);
}
}
@ -120,21 +128,22 @@ public class Checkbox extends BasicButton {
@Override
protected void assembleSelf(RenderTarget target) {
String state;
if (!Checkbox.this.isEnabled()) {
state = "Disabled";
} else if (Checkbox.this.isPressed()) {
state = "Pressed";
} else if (Checkbox.this.isHovered()) {
state = "Hovered";
} else if (Checkbox.this.isFocused()) {
state = "Focused";
} else {
state = "Inactive";
}
// Change label font color
getLabel().setFont(getLabel().getFont().withColor(ColorScheme.get("Core:CheckboxText" + state)));
if (isPressed()) {
getLabel().setFont(getLabel().getFont().withColor(Colors.BLUE));
} else {
getLabel().setFont(getLabel().getFont().withColor(Colors.BLACK));
}
}
@Override
protected void postAssembleSelf(RenderTarget target) {
// Apply disable tint
if (!isEnabled()) {
target.fill(getX(), getY(), getWidth(), getHeight(), Colors.toVector(0x88FFFFFF));
}
}
}

View File

@ -1,145 +0,0 @@
/*
* Progressia
* Copyright (C) 2020-2022 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.gui;
import java.util.HashMap;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import glm.vec._4.Vec4;
import ru.windcorp.jputil.SyntaxException;
import ru.windcorp.jputil.chars.StringUtil;
import ru.windcorp.progressia.common.resource.Resource;
import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.util.namespaces.IllegalIdException;
import ru.windcorp.progressia.common.util.namespaces.NamespacedUtil;
public class ColorScheme {
private static Map<String, Vec4> defaultScheme;
public static Vec4 get(String key) {
Vec4 color = defaultScheme.get(key);
if (color == null) {
throw CrashReports.report(null, "ColorScheme does not contain color %s", key);
}
return color;
}
public static void load(Resource resource) {
Map<String, Vec4> tmpMap = new HashMap<>();
try {
for (String fullLine : StringUtil.split(resource.readAsString(), '\n')) {
String line = fullLine.trim();
if (line.isEmpty() || line.startsWith("#")) {
continue;
}
String[] parts = StringUtil.split(line, '=', 2);
if (parts[1] == null) {
throw new SyntaxException("Could not parse \"" + line + "\": '=' not found");
}
String key = parts[0].trim();
checkKeyName(key);
if (tmpMap.containsKey(key)) {
throw new SyntaxException("Duplicate key " + key);
}
String value = parts[1].trim();
Vec4 color;
if (value.startsWith("#")) {
color = parseValueAsHex(value);
} else if (value.startsWith("$")) {
color = parseValueAsReference(value, tmpMap);
} else {
throw new SyntaxException("Unknown value format \"" + value + "\"");
}
tmpMap.put(key, color);
}
} catch (SyntaxException e) {
throw CrashReports.report(e, "Could not load ColorScheme from %s", resource);
}
defaultScheme = ImmutableMap.copyOf(tmpMap);
}
private static void checkKeyName(String key) throws SyntaxException {
try {
NamespacedUtil.checkId(key);
} catch (IllegalIdException e) {
throw new SyntaxException("Illegal key name \"" + key + "\"", e);
}
}
private static int parseHex(String from, int start, int length) {
return Integer.parseInt(from.substring(start, start + length), 0x10);
}
private static Vec4 parseValueAsHex(String value) throws SyntaxException {
int a = 0xFF;
int r, g, b;
switch (value.length() - 1) {
case 3:
// #RGB
r = parseHex(value, 1, 1) * 0x11;
g = parseHex(value, 2, 1) * 0x11;
b = parseHex(value, 3, 1) * 0x11;
break;
case 4:
// #ARGB
a = parseHex(value, 1, 1) * 0x11;
r = parseHex(value, 2, 1) * 0x11;
g = parseHex(value, 3, 1) * 0x11;
b = parseHex(value, 4, 1) * 0x11;
break;
case 6:
// #RRGGBB
r = parseHex(value, 1, 2);
g = parseHex(value, 3, 2);
b = parseHex(value, 5, 2);
break;
case 8:
// #AARRGGBB
a = parseHex(value, 1, 2);
r = parseHex(value, 3, 2);
g = parseHex(value, 5, 2);
b = parseHex(value, 7, 2);
break;
default:
throw new SyntaxException("Could not parse hex color \"" + value + "\": expecting #RGB, #ARGB, #RRGGBB or #AARRGGBB");
}
return new Vec4(r / 255.0, g / 255.0, b / 255.0, a / 255.0);
}
private static Vec4 parseValueAsReference(String value, Map<String, Vec4> map) throws SyntaxException {
String otherKey = value.substring(1);
checkKeyName(otherKey);
if (!map.containsKey(otherKey)) {
throw new SyntaxException("Key $" + otherKey + " not found");
}
return map.get(otherKey);
}
}

View File

@ -30,7 +30,9 @@ import com.google.common.eventbus.Subscribe;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
import ru.windcorp.progressia.client.graphics.flat.AssembledFlatRenderHelper;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget.Clip;
import ru.windcorp.progressia.client.graphics.gui.event.ChildAddedEvent;
import ru.windcorp.progressia.client.graphics.gui.event.ChildRemovedEvent;
import ru.windcorp.progressia.client.graphics.gui.event.EnableEvent;
@ -43,6 +45,7 @@ import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
import ru.windcorp.progressia.client.graphics.input.bus.InputBus;
import ru.windcorp.progressia.client.graphics.input.bus.InputListener;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.common.util.Named;
import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.util.crash.ReportingEventBus;
@ -557,9 +560,7 @@ public class Component extends Named {
}
public void removeInputListener(InputListener<?> listener) {
if (inputBus != null) {
inputBus.unregister(listener);
}
inputBus.unregister(listener);
}
protected boolean passInputToChildren(InputEvent e) {
@ -761,6 +762,20 @@ public class Component extends Named {
}
}
public Renderable assembleToRenderable() {
RenderTarget target = new RenderTarget();
assemble(target);
Clip[] clips = target.assemble();
return renderer -> {
for (Clip clip : clips) {
clip.render((AssembledFlatRenderHelper) renderer);
}
};
}
// /**
// * Returns a component that displays this component in its center.

View File

@ -0,0 +1,37 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.gui;
import java.util.function.BooleanSupplier;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
public class Components {
public static Component center(Component c) {
return new Group(c.getName() + ".Centerer", new LayoutAlign(), c);
}
public static Component hide(Component c, BooleanSupplier shouldHide) {
return new Hider(c.getName() + ".Hider", c, shouldHide);
}
private Components() {
}
}

View File

@ -33,7 +33,7 @@ public class DynamicLabel extends Component {
super(name);
this.font = font;
this.contents = contents;
setPreferredSize(width, font.getHeight("", Float.POSITIVE_INFINITY) * 2);
setPreferredSize(width, font.getHeight("", Float.POSITIVE_INFINITY));
}
public Font getFont() {
@ -46,7 +46,7 @@ public class DynamicLabel extends Component {
@Override
protected void assembleSelf(RenderTarget target) {
target.pushTransform(new Mat4().identity().translate(getX(), getY(), -1000).scale(2));
target.pushTransform(new Mat4().identity().translate(getX(), getY(), -1000));
target.addCustomRenderer(font.assembleDynamic(getContentSupplier(), Float.POSITIVE_INFINITY));
target.popTransform();
}

View File

@ -24,5 +24,13 @@ public class Group extends Component {
super(name);
setLayout(layout);
}
public Group(String name, Layout layout, Component... children) {
this(name, layout);
for (Component child : children) {
addChild(child);
}
}
}

View File

@ -0,0 +1,65 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.gui;
import java.util.function.BooleanSupplier;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.client.graphics.model.Renderable;
public class Hider extends Component {
private final BooleanSupplier shouldHide;
private final Component contents;
public Hider(String name, Component contents, BooleanSupplier shouldHide) {
super(name);
this.contents = contents;
this.shouldHide = shouldHide;
setLayout(new LayoutFill());
addChild(contents);
}
@Override
protected boolean passInputToChildren(InputEvent e) {
return !shouldHide.getAsBoolean();
}
@Override
public synchronized Component findFocused() {
if (shouldHide.getAsBoolean()) {
return null;
}
return super.findFocused();
}
@Override
protected void assembleChildren(RenderTarget target) {
Renderable renderable = contents.assembleToRenderable();
target.addCustomRenderer(renderer -> {
if (!shouldHide.getAsBoolean()) {
renderable.render(renderer);
}
});
}
}

View File

@ -70,7 +70,7 @@ public class Label extends Component {
public void update() {
currentText = contents.get();
currentSize = font.getSize(currentText, maxWidth, null).mul(2);
currentSize = font.getSize(currentText, maxWidth, null);
requestReassembly();
}
@ -99,12 +99,8 @@ public class Label extends Component {
protected void assembleSelf(RenderTarget target) {
float startX = getX() + font.getAlign() * (getWidth() - currentSize.x);
target.pushTransform(
new Mat4().identity().translate(startX, getY(), 0).scale(2)
);
target.pushTransform(new Mat4().identity().translate(startX, getY(), 0));
target.addCustomRenderer(font.assemble(currentText, maxWidth));
target.popTransform();
}

View File

@ -20,6 +20,7 @@ package ru.windcorp.progressia.client.graphics.gui;
import java.util.Objects;
import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
public class Panel extends Group {
@ -35,7 +36,7 @@ public class Panel extends Group {
}
public Panel(String name, Layout layout) {
this(name, layout, ColorScheme.get("Core:Background"), ColorScheme.get("Core:Border"));
this(name, layout, Colors.WHITE, Colors.LIGHT_GRAY);
}
/**

View File

@ -21,6 +21,7 @@ import org.lwjgl.glfw.GLFW;
import glm.vec._2.i.Vec2i;
import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.font.Typefaces;
@ -50,29 +51,35 @@ public class RadioButton extends BasicButton {
int size = getPreferredSize().x;
int x = getX();
int y = getY() + (getHeight() - size) / 2;
String state;
if (!RadioButton.this.isEnabled()) {
state = "Disabled";
} else if (RadioButton.this.isPressed()) {
state = "Pressed";
} else if (RadioButton.this.isHovered()) {
state = "Hovered";
} else if (RadioButton.this.isFocused()) {
state = "Focused";
} else {
state = "Inactive";
}
// Border
cross(target, x, y, size, ColorScheme.get("Core:RadioButtonBorder" + state));
Vec4 borderColor;
if (RadioButton.this.isPressed() || RadioButton.this.isHovered() || RadioButton.this.isFocused()) {
borderColor = Colors.BLUE;
} else {
borderColor = Colors.LIGHT_GRAY;
}
cross(target, x, y, size, borderColor);
// Inside area
cross(target, x + 2, y + 2, size - 4, ColorScheme.get("Core:RadioButtonFill" + state));
if (RadioButton.this.isPressed()) {
// Do nothing
} else {
Vec4 backgroundColor;
if (RadioButton.this.isHovered() && RadioButton.this.isEnabled()) {
backgroundColor = Colors.HOVER_BLUE;
} else {
backgroundColor = Colors.WHITE;
}
cross(target, x + 2, y + 2, size - 4, backgroundColor);
}
// "Tick"
if (RadioButton.this.isChecked()) {
cross(target, x + 4, y + 4, size - 8, ColorScheme.get("Core:RadioButtonCheck" + state));
cross(target, x + 4, y + 4, size - 8, Colors.BLUE);
}
}
@ -139,8 +146,8 @@ public class RadioButton extends BasicButton {
group.selectNext();
removeAction(group.listener);
group.buttons.remove(this);
// Clear reference if this was the only button in the group
group.getSelected();
group.getSelected(); // Clear reference if this was the only button
// in the group
}
this.group = group;
@ -176,21 +183,22 @@ public class RadioButton extends BasicButton {
@Override
protected void assembleSelf(RenderTarget target) {
String state;
if (!RadioButton.this.isEnabled()) {
state = "Disabled";
} else if (RadioButton.this.isPressed()) {
state = "Pressed";
} else if (RadioButton.this.isHovered()) {
state = "Hovered";
} else if (RadioButton.this.isFocused()) {
state = "Focused";
} else {
state = "Inactive";
}
// Change label font color
getLabel().setFont(getLabel().getFont().withColor(ColorScheme.get("Core:RadioButtonText" + state)));
if (isPressed()) {
getLabel().setFont(getLabel().getFont().withColor(Colors.BLUE));
} else {
getLabel().setFont(getLabel().getFont().withColor(Colors.BLACK));
}
}
@Override
protected void postAssembleSelf(RenderTarget target) {
// Apply disable tint
if (!isEnabled()) {
target.fill(getX(), getY(), getWidth(), getHeight(), Colors.toVector(0x88FFFFFF));
}
}
}

View File

@ -20,9 +20,9 @@ package ru.windcorp.progressia.client.graphics.gui.menu;
import org.lwjgl.glfw.GLFW;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.gui.ColorScheme;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
import ru.windcorp.progressia.client.graphics.gui.Label;
@ -50,7 +50,7 @@ public class MenuLayer extends GUILayer {
setCursorPolicy(CursorPolicy.REQUIRE);
this.background = new Panel(name + ".Background", new LayoutAlign(10), ColorScheme.get("Core:MenuLayerTint"), null);
this.background = new Panel(name + ".Background", new LayoutAlign(10), Colors.toVector(0x66000000), null);
this.content = content;
background.addChild(content);
@ -76,12 +76,12 @@ public class MenuLayer extends GUILayer {
protected void addTitle() {
String translationKey = "Layer" + getName() + ".Title";
MutableString titleText = new MutableStringLocalized(translationKey);
Font titleFont = new Font().deriveBold().withColor(ColorScheme.get("Core:Text")).withAlign(0.5f);
Font titleFont = new Font().deriveBold().withColor(Colors.BLACK).withAlign(0.5f);
Label label = new Label(getName() + ".Title", titleFont, titleText);
getContent().addChild(label);
Panel panel = new Panel(getName() + ".Title.Underscore", null, ColorScheme.get("Core:Accent"), null);
Panel panel = new Panel(getName() + ".Title.Underscore", null, Colors.BLUE, null);
panel.setLayout(new LayoutFill() {
@Override
public Vec2i calculatePreferredSize(Component c) {

View File

@ -125,6 +125,36 @@ public class Shapes {
return result;
}
public static Shape createParallelogram(
// Try saying that 10 times fast
ShapeRenderProgram program,
Vec3 origin,
Vec3 width,
Vec3 height,
Vec4 colorMultiplier,
Texture texture
) {
Shape result = new Shape(
Usage.STATIC,
program,
ShapeParts.createRectangle(
program,
texture,
colorMultiplier,
origin,
width,
height,
false
)
);
return result;
}
public static class PppBuilder {
@ -315,4 +345,108 @@ public class Shapes {
}
public static class PgmBuilder {
private final ShapeRenderProgram program;
private final Vec3 origin = new Vec3(0, 0, 0);
private final Vec3 width = new Vec3(1, 0, 0);
private final Vec3 height = new Vec3(0, 1, 0);
private final Vec4 colorMultiplier = new Vec4(1, 1, 1, 1);
private final Texture texture;
public PgmBuilder(ShapeRenderProgram program, Texture texture) {
this.program = program;
this.texture = texture;
}
public PgmBuilder setOrigin(Vec3 origin) {
this.origin.set(origin);
return this;
}
public PgmBuilder setOrigin(float x, float y, float z) {
this.origin.set(x, y, z);
return this;
}
public PgmBuilder setColorMultiplier(Vec4 colorMultiplier) {
this.colorMultiplier.set(colorMultiplier);
return this;
}
public PgmBuilder setColorMultiplier(float r, float g, float b) {
this.colorMultiplier.set(r, g, b, 1);
return this;
}
public PgmBuilder setColorMultiplier(float r, float g, float b, float a) {
this.colorMultiplier.set(r, g, b, a);
return this;
}
public PgmBuilder setWidth(Vec3 vector) {
this.width.set(vector);
return this;
}
public PgmBuilder setWidth(float x, float y, float z) {
this.width.set(x, y, z);
return this;
}
public PgmBuilder setWidth(float x) {
this.width.set(x, 0, 0);
return this;
}
public PgmBuilder setHeight(Vec3 vector) {
this.height.set(vector);
return this;
}
public PgmBuilder setHeight(float x, float y, float z) {
this.height.set(x, y, z);
return this;
}
public PgmBuilder setHeight(float y) {
this.height.set(0, y, 0);
return this;
}
public PgmBuilder setSize(float x, float y) {
return this.setWidth(x).setHeight(y);
}
public PgmBuilder setSize(float size) {
return this.setSize(size, size);
}
public PgmBuilder centerAt(float x, float y, float z) {
origin.set(x, y, z);
origin.mul(2);
origin.sub(width);
origin.div(2);
return this;
}
public Shape create() {
return createParallelogram(
program,
origin,
width,
height,
colorMultiplier,
texture
);
}
}
}

View File

@ -163,7 +163,7 @@ public class Atlases {
}
}
private static final TextureSettings SETTINGS = new TextureSettings(false, false);
private static final TextureSettings SETTINGS = new TextureSettings(false);
private static final Map<Resource, Sprite> LOADED = new HashMap<>();
private static final Multimap<AtlasGroup, Atlas> ATLASES = MultimapBuilder.hashKeys().arrayListValues().build();

View File

@ -28,7 +28,7 @@ import ru.windcorp.progressia.common.util.crash.CrashReports;
public class SimpleTextures {
private static final TextureSettings SETTINGS = new TextureSettings(false, false);
private static final TextureSettings SETTINGS = new TextureSettings(false);
private static final Map<Resource, Texture> TEXTURES = new HashMap<>();

View File

@ -23,7 +23,7 @@ import static org.lwjgl.opengl.GL12.*;
import java.nio.ByteBuffer;
public class TextureData {
class TextureData {
private final ByteBuffer data;

View File

@ -41,17 +41,9 @@ public class TextureLoader {
int width = readResult.getWidth();
int height = readResult.getHeight();
int bufferWidth;
int bufferHeight;
if (settings.allocateExactBuffer()) {
bufferWidth = width;
bufferHeight = height;
} else {
bufferWidth = BinUtil.roundToGreaterPowerOf2(width);
bufferHeight = BinUtil.roundToGreaterPowerOf2(height);
}
int bufferWidth = BinUtil.roundToGreaterPowerOf2(width);
int bufferHeight = BinUtil.roundToGreaterPowerOf2(height);
WritableRaster raster = TextureUtil.createRaster(
bufferWidth,

View File

@ -21,19 +21,13 @@ package ru.windcorp.progressia.client.graphics.texture;
public class TextureSettings {
private final boolean isFiltered;
private final boolean allocateExactBuffer;
public TextureSettings(boolean isFiltered, boolean allocateExactBuffer) {
public TextureSettings(boolean isFiltered) {
this.isFiltered = isFiltered;
this.allocateExactBuffer = allocateExactBuffer;
}
public boolean isFiltered() {
return isFiltered;
}
public boolean allocateExactBuffer() {
return allocateExactBuffer;
}
}

View File

@ -15,20 +15,25 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world;
import org.apache.logging.log4j.LogManager;
import ru.windcorp.progressia.client.Client;
import ru.windcorp.progressia.client.events.ClientEvent;
import ru.windcorp.progressia.client.events.NewLocalEntityEvent;
import ru.windcorp.progressia.client.world.WorldRender;
import ru.windcorp.progressia.client.world.entity.EntityRenderable;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
public class LocalPlayer {
private final Client client;
private long entityId = EntityData.NULL_ENTITY_ID;
private EntityData lastKnownEntity = null;
private EntityDataPlayer lastKnownEntity = null;
private final Selection selection = new Selection();
@ -59,19 +64,32 @@ public class LocalPlayer {
return getEntity() != null;
}
public EntityData getEntity() {
public EntityDataPlayer getEntity() {
if (!hasEntityId()) {
return null;
}
EntityData entity = getClient().getWorld().getData().getEntity(getEntityId());
EntityDataPlayer playerEntity;
if (entity != lastKnownEntity) {
getClient().onLocalPlayerEntityChanged(entity, lastKnownEntity);
this.lastKnownEntity = entity;
if (entity == null || entity instanceof EntityDataPlayer) {
playerEntity = (EntityDataPlayer) entity;
} else {
LogManager.getLogger().warn(
"Entity ID of local player is {}, but the entity is not an EntityDataPlayer: {}",
EntityData.formatEntityId(getEntityId()),
entity
);
playerEntity = null;
}
return entity;
if (playerEntity != lastKnownEntity) {
ClientEvent event = new NewLocalEntityEvent.Immutable(getClient(), playerEntity, lastKnownEntity);
this.lastKnownEntity = playerEntity;
getClient().postEvent(event);
}
return playerEntity;
}
public Selection getSelection() {

View File

@ -0,0 +1,113 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world.hud;
import glm.vec._3.Vec3;
import glm.vec._4.Vec4;
import ru.windcorp.jputil.functions.FloatSupplier;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.backend.Usage;
import ru.windcorp.progressia.client.graphics.flat.FlatRenderProgram;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.Shape;
import ru.windcorp.progressia.client.graphics.model.ShapeParts;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
public class Bar extends Component {
private static final int THICKNESS = 5;
private final boolean isVertical;
private final FloatSupplier value;
private final FloatSupplier maxValue;
private final Vec4 color;
private final Vec4 backgroundColor;
private static Renderable unitSquare = null;
public Bar(String name, boolean isVertical, Vec4 color, FloatSupplier value, FloatSupplier maxValue) {
super(name);
this.isVertical = isVertical;
this.value = value;
this.maxValue = maxValue;
this.color = color;
this.backgroundColor = Colors.mix(color, Colors.WHITE, 0.75f, null);
if (unitSquare == null) {
unitSquare = new Shape(
Usage.STATIC,
FlatRenderProgram.getDefault(),
ShapeParts.createRectangle(
FlatRenderProgram.getDefault(),
null,
Colors.WHITE,
new Vec3(0, 0, 0),
new Vec3(1, 0, 0),
new Vec3(0, 1, 0),
false
)
);
}
setPreferredSize(THICKNESS, THICKNESS);
}
@Override
protected void assembleSelf(RenderTarget target) {
target.addCustomRenderer(this::renderSelf);
}
private void renderSelf(ShapeRenderHelper renderer) {
renderer.pushTransform()
.translate(getX(), getY(), 0)
.scale(getWidth(), getHeight(), 1);
float length = value.getAsFloat() / maxValue.getAsFloat();
if (length < 0) {
length = 0;
} else if (length > 1) {
length = 1;
}
// TODO why is the order reverse????
renderRectangle(renderer, color, length);
renderRectangle(renderer, backgroundColor, 1);
renderer.popTransform();
}
private void renderRectangle(ShapeRenderHelper renderer, Vec4 color, float length) {
renderer.pushColorMultiplier().mul(color);
if (length != 1) {
renderer.pushTransform().scale(isVertical ? 1 : length, isVertical ? length : 1, 1);
}
unitSquare.render(renderer);
if (length != 1) {
renderer.popTransform();
}
renderer.popColorMultiplier();
}
}

View File

@ -0,0 +1,151 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world.hud;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.ExponentAnimation;
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.client.world.entity.SpeciesRender;
import ru.windcorp.progressia.client.world.entity.SpeciesRenderRegistry;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
import ru.windcorp.progressia.common.world.entity.SpeciesData.Hand;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainerHand;
public class CursorHUD extends Component {
private class CursorBoundSlot {
private final SlotComponent component;
private final Renderable renderable;
/**
* 0 is not selected, 1 is selected
*/
private final ExponentAnimation selection = new ExponentAnimation(10, 0);
private final double angle;
public CursorBoundSlot(SlotComponent component, double angle) {
this.component = component;
this.angle = angle;
Vec2i size = component.getPreferredSize();
component.setBounds(-size.x / 2, -size.y / 2, size);
this.renderable = component.assembleToRenderable();
if (player.getHandCount() == 1) {
// Disable opening animation hint
selection.setValue(1);
}
}
public void render(ShapeRenderHelper renderer) {
float target = player.getSelectedHand() == component.getSlot().getContainer() ? 1 : 0;
float sel = selection.updateForFrame(target);
float distance = CursorHUD.this.distance * (1 - sel);
float x = (float) Math.cos(angle) * distance;
float y = (float) Math.sin(angle) * distance;
float scale = 0.5f + 0.5f * sel;
renderer.pushTransform().translate(x, y, 0).scale(scale);
boolean popColor = false;
if (sel > 0.5f && component.getSlot().isEmpty()) {
renderer.pushColorMultiplier().mul(1, 1, 1, 1 - 2 * (sel - 0.5f));
popColor = true;
}
renderable.render(renderer);
if (popColor) {
renderer.popColorMultiplier();
}
renderer.popTransform();
}
}
private final EntityDataPlayer player;
private final float distance = 50;
private final double startAngle = Math.PI / 4;
private final double endAngle = -3 * Math.PI / 4;
private final CursorBoundSlot[] slots;
public CursorHUD(String name, EntityDataPlayer player) {
super(name);
this.player = player;
this.slots = new CursorBoundSlot[player.getHandCount()];
// This produces NaN when there is only one hand, but then it is unused
double angleStep = (endAngle - startAngle) / (slots.length - 1);
double angle = startAngle;
for (int i = 0; i < slots.length; ++i) {
SpeciesRender speciesRender = SpeciesRenderRegistry.getInstance().get(player);
ItemContainerHand container = player.getHand(i);
Hand hand = container.getHand();
SlotComponent component = new SlotComponent(name + ".Hand" + hand.getName(), container, 0)
.setBackground(speciesRender.getHandBackground(hand));
addChild(component);
slots[i] = new CursorBoundSlot(component, angle);
angle += angleStep;
}
setLayout(null);
}
@Override
protected void assembleChildren(RenderTarget target) {
target.addCustomRenderer(renderer -> {
renderer.pushTransform().translate(
(float) InputTracker.getCursorX(),
(float) InputTracker.getCursorY(),
0
);
for (CursorBoundSlot slot : slots) {
slot.render(renderer);
}
renderer.popTransform();
});
}
}

View File

@ -0,0 +1,125 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world.hud;
import com.google.common.eventbus.Subscribe;
import ru.windcorp.progressia.client.Client;
import ru.windcorp.progressia.client.events.NewLocalEntityEvent;
import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.world.item.inventory.InventoryComponent;
import ru.windcorp.progressia.client.world.item.inventory.InventoryRender;
import ru.windcorp.progressia.client.world.item.inventory.InventoryRenderRegistry;
import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.world.item.inventory.Inventory;
import ru.windcorp.progressia.common.world.item.inventory.event.InventoryClosingEvent;
import ru.windcorp.progressia.common.world.item.inventory.event.InventoryOpenedEvent;
public class HUDManager implements HUDWorkspace {
private final Client client;
private final LayerHUD layer;
public HUDManager(Client client) {
this.client = client;
this.layer = new LayerHUD(this);
client.subscribe(new Object() {
@Subscribe
public void onLocalEntityChanged(NewLocalEntityEvent e) {
if (e.getNewEntity() != null) {
e.getNewEntity().subscribe(HUDManager.this);
}
if (e.getPreviousEntity() != null) {
e.getPreviousEntity().unsubscribe(HUDManager.this);
}
}
});
}
public void install() {
GUI.addTopLayer(layer);
}
public void remove() {
GUI.removeLayer(layer);
}
@Override
public void openInventory(InventoryComponent component) {
InventoryWindow window = new InventoryWindow("Window", component, this);
layer.getWindowManager().addWindow(window);
}
public void closeEverything() {
System.err.println("closeEverything NYI");
}
public boolean isHidden() {
return layer.isHidden();
}
public void setHidden(boolean hide) {
layer.setHidden(hide);
}
public boolean isInventoryShown() {
return layer.isInventoryShown();
}
public void setInventoryShown(boolean showInventory) {
layer.setInventoryShown(showInventory);
}
@Override
public Client getClient() {
return client;
}
@Subscribe
private void onInventoryOpened(InventoryOpenedEvent event) {
Inventory inventory = event.getInventory();
InventoryRender render = InventoryRenderRegistry.getInstance().get(inventory.getId());
if (render == null) {
throw CrashReports.report(null, "InventoryRender not found for ID %s", inventory.getId());
}
try {
InventoryComponent component = render.createComponent(inventory, this);
openInventory(component);
} catch (Exception e) {
throw CrashReports.report(e, "Could not open inventory %s", inventory.getId());
}
}
@Subscribe
private void onInventoryClosing(InventoryClosingEvent event) {
Inventory inventory = event.getInventory();
for (Component component : layer.getWindowManager().getChildren()) {
if (component instanceof InventoryWindow) {
InventoryWindow window = (InventoryWindow) component;
if (window.getContent().getInventory() == inventory) {
layer.getWindowManager().closeWindow(window);
}
}
}
}
}

View File

@ -0,0 +1,60 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world.hud;
import java.io.IOException;
import ru.windcorp.progressia.client.graphics.texture.Atlases;
import ru.windcorp.progressia.client.graphics.texture.SimpleTexture;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.graphics.texture.Atlases.AtlasGroup;
import ru.windcorp.progressia.common.resource.ResourceManager;
import ru.windcorp.progressia.common.util.crash.CrashReports;
public class HUDTextures {
private static final AtlasGroup HUD_ATLAS_GROUP = new AtlasGroup("HUD", 1 << 12);
private static ItemAmountTypeface itemAmountTypeface = null;
public static Texture getHUDTexture(String name) {
return new SimpleTexture(
Atlases.getSprite(
ResourceManager.getTextureResource("gui/" + name),
HUD_ATLAS_GROUP
)
);
}
public static AtlasGroup getHUDAtlasGroup() {
return HUD_ATLAS_GROUP;
}
public static ItemAmountTypeface getItemAmountTypeface() {
return itemAmountTypeface;
}
public static void loadItemAmountTypeface() {
try {
itemAmountTypeface = new ItemAmountTypeface();
} catch (IOException e) {
throw CrashReports.report(e, "Could not load item amount typeface");
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world.hud;
import ru.windcorp.progressia.client.Client;
import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
import ru.windcorp.progressia.client.world.item.inventory.InventoryComponent;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainerHand;
public interface HUDWorkspace {
Client getClient();
default LocalPlayer getPlayer() {
return getClient().getLocalPlayer();
}
default EntityDataPlayer getPlayerEntity() {
return getClient().getLocalPlayer().getEntity();
}
default ItemContainerHand getHand() {
return getClient().getLocalPlayer().getEntity().getSelectedHand();
}
void openInventory(InventoryComponent component);
}

View File

@ -0,0 +1,134 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world.hud;
import java.util.Arrays;
import java.util.Map;
import com.google.common.collect.Maps;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.ExponentAnimation;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.Group;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutBorderHorizontal;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutHorizontal;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.world.entity.SpeciesRender;
import ru.windcorp.progressia.client.world.entity.SpeciesRenderRegistry;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
import ru.windcorp.progressia.common.world.entity.SpeciesData.Hand;
public class HandsHUD extends Component {
public class ScaledSlotComponent extends Component {
private final SlotComponent slotComponent;
private final ExponentAnimation selected = new ExponentAnimation(10, 1);
public ScaledSlotComponent(SlotComponent component) {
super(component.getName() + ".Scaled");
this.slotComponent = component;
addChild(component);
Vec2i size = slotComponent.getPreferredSize();
setPreferredSize(size.x * 2, size.y * 2);
slotComponent.setBounds(-size.x / 2, 0, size);
}
@Override
protected void assembleChildren(RenderTarget target) {
Renderable renderable = slotComponent.assembleToRenderable();
target.addCustomRenderer(renderer -> {
float scale = manager.getHand() == slotComponent.getSlot().getContainer() ? 2 : 1;
renderer.pushTransform()
.translate(getX() + getWidth() / 2, getY(), 0)
.scale(selected.updateForFrame(scale));
renderable.render(renderer);
renderer.popTransform();
});
}
}
public enum Side {
LEFT("Left", LayoutBorderHorizontal.LEFT, 0.0),
RIGHT("Right", LayoutBorderHorizontal.RIGHT, 1.0),
CENTER("Center", LayoutBorderHorizontal.CENTER, 0.5);
private final String ccName;
private final Object lbhHint;
private final double align;
private Side(String ccName, Object lbhHint, double align) {
this.ccName = ccName;
this.lbhHint = lbhHint;
this.align = align;
}
}
private final HUDManager manager;
public HandsHUD(String name, HUDManager manager) {
super(name);
this.manager = manager;
EntityDataPlayer entity = manager.getPlayerEntity();
String speciesId = entity.getSpecies().getId();
SpeciesRender speciesRender = SpeciesRenderRegistry.getInstance().get(speciesId);
Map<Side, Component> containers = Maps.toMap(
Arrays.asList(Side.values()),
side -> new Group(name + "." + side.ccName, new LayoutHorizontal(15))
);
for (int i = 0; i < entity.getHandCount(); ++i) {
Hand hand = entity.getSpecies().getHands().get(i);
SlotComponent display = new SlotComponent(name + "." + hand.getName(), entity.getHand(i), 0)
.setBackground(speciesRender.getHandBackground(hand), this::shouldRenderHandPlaceholder)
.setScale(2);
Component scaledDisplay = new ScaledSlotComponent(display);
containers.get(speciesRender.getHandSide(hand)).addChild(scaledDisplay);
}
setLayout(new LayoutBorderHorizontal());
containers.forEach((side, comp) -> {
addChild(
new Group(name + "." + side.ccName + ".Aligner", new LayoutAlign(side.align, 0.5, 0), comp)
.setLayoutHint(side.lbhHint)
);
});
}
private boolean shouldRenderHandPlaceholder() {
return manager.isInventoryShown();
}
}

View File

@ -0,0 +1,181 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world.hud;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.gui.Button;
import ru.windcorp.progressia.client.graphics.gui.Label;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
import ru.windcorp.progressia.client.graphics.input.WheelScrollEvent;
import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.world.item.ItemDataContainer;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainer;
import ru.windcorp.progressia.common.world.item.inventory.ItemSlot;
import ru.windcorp.progressia.common.world.item.inventory.Items;
public class InteractiveSlotComponent extends Button {
private static final double MIN_PICK_ALL_DELAY = Units.get("0.5 s");
private double lastMainAction = Double.NEGATIVE_INFINITY;
private final SlotComponent slotComponent;
private final HUDWorkspace workspace;
public InteractiveSlotComponent(String name, ItemContainer container, int index, HUDWorkspace workspace) {
this(name, new SlotComponent(name, container, index), workspace);
}
public InteractiveSlotComponent(SlotComponent component, HUDWorkspace workspace) {
this(component.getName() + ".Interactive", component, workspace);
}
public InteractiveSlotComponent(String name, SlotComponent component, HUDWorkspace workspace) {
super(name, (Label) null);
this.slotComponent = component;
this.workspace = workspace;
Vec2i size = slotComponent.getPreferredSize().add(2 * BORDER);
setPreferredSize(size);
addChild(this.slotComponent);
setLayout(new LayoutFill(MARGIN));
addListeners();
}
private void addListeners() {
addAction(button -> onMainAction());
addKeyListener(KeyMatcher.RMB, this::onAltAction);
addInputListener(WheelScrollEvent.class, event -> {
if (event.hasVerticalMovement()) {
onSingleMoveAction(event.isDown());
event.consume();
}
});
}
private void onMainAction() {
ItemSlot handSlot = workspace.getHand().slot();
ItemSlot invSlot = getSlot();
if (invSlot == null) {
return;
}
boolean success = false;
double now = GraphicsInterface.getTime();
if (now - lastMainAction < MIN_PICK_ALL_DELAY) {
lastMainAction = Double.NEGATIVE_INFINITY;
pickAll(handSlot);
success = true;
} else {
lastMainAction = now;
}
if (!success) {
success = Items.pour(handSlot, invSlot) != 0;
}
if (!success) {
success = Items.swap(handSlot, invSlot);
}
if (!success && handSlot.isEmpty()) {
success = Items.pour(invSlot, handSlot) != 0;
}
if (success) {
requestReassembly();
}
}
private void pickAll(ItemSlot handSlot) {
int maxIndex = getSlot().getContainer().getMaxIndex();
for (int index = 0; index < maxIndex; ++index) {
Items.pour(new ItemSlot(getSlot().getContainer(), index), handSlot);
if (handSlot.isEmpty()) {
break;
}
}
}
private void onAltAction(KeyEvent e) {
ItemSlot handSlot = workspace.getHand().slot();
ItemSlot invSlot = getSlot();
if (invSlot == null) {
return;
}
boolean success = false;
if (handSlot.isEmpty()) {
success = tryToOpen(invSlot);
}
if (!success && handSlot.isEmpty()) {
success = Items.pour(invSlot, handSlot, invSlot.getCount() / 2) != 0;
}
if (!success) {
success = Items.pour(handSlot, invSlot, 1) != 0;
}
if (!success) {
success = Items.swap(handSlot, invSlot);
}
if (success) {
requestReassembly();
}
}
private boolean tryToOpen(ItemSlot invSlot) {
if (invSlot.getCount() != 1) {
return false;
}
if (!(invSlot.getItem() instanceof ItemDataContainer)) {
return false;
}
ItemDataContainer item = (ItemDataContainer) invSlot.getItem();
return item.open(workspace.getPlayerEntity()) != null;
}
private void onSingleMoveAction(boolean fromHand) {
ItemSlot handSlot = workspace.getHand().slot();
ItemSlot invSlot = getSlot();
ItemSlot from = fromHand ? handSlot : invSlot;
ItemSlot into = fromHand ? invSlot : handSlot;
if (Items.pour(from, into, 1) != 0) {
requestReassembly();
}
}
public ItemSlot getSlot() {
return slotComponent.getSlot();
}
}

View File

@ -0,0 +1,88 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world.hud;
import java.util.Arrays;
import java.util.Map;
import com.google.common.collect.Maps;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.Group;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutBorderHorizontal;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
import ru.windcorp.progressia.client.world.entity.SpeciesRender;
import ru.windcorp.progressia.client.world.entity.SpeciesRenderRegistry;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
import ru.windcorp.progressia.common.world.entity.SpeciesData.EquipmentSlot;
public class InventoryHUD extends Component {
public enum Side {
LEFT("Left", LayoutBorderHorizontal.LEFT),
RIGHT("Right", LayoutBorderHorizontal.RIGHT);
private final String ccName;
private final Object lbhHint;
private Side(String ccName, Object lbhHint) {
this.ccName = ccName;
this.lbhHint = lbhHint;
}
}
public InventoryHUD(String name, HUDWorkspace workspace) {
super(name);
setLayout(new LayoutBorderHorizontal());
EntityDataPlayer entity = workspace.getPlayerEntity();
String speciesId = entity.getSpecies().getId();
SpeciesRender speciesRender = SpeciesRenderRegistry.getInstance().get(speciesId);
Map<Side, Component> containers = Maps.toMap(
Arrays.asList(Side.values()),
side -> new Group(name + "." + side.ccName, new LayoutVertical(15))
);
for (int i = 0; i < entity.getEquipmentCount(); ++i) {
EquipmentSlot slot = entity.getSpecies().getEquipmentSlots().get(i);
SlotComponent display = new SlotComponent(name + "." + slot.getName(), entity.getEquipmentSlot(i), 0)
.setBackground(speciesRender.getEquipmentSlotBackground(slot))
.setScale(2);
InteractiveSlotComponent interactiveDisplay = new InteractiveSlotComponent(
display,
workspace
);
containers.get(speciesRender.getEquipmentSlotSide(slot)).addChild(interactiveDisplay);
}
containers.forEach((side, comp) -> {
addChild(
new Group(name + "." + side.ccName + ".Aligner", new LayoutAlign(0, 1, 0), comp)
.setLayoutHint(side.lbhHint)
);
});
}
}

View File

@ -0,0 +1,154 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world.hud;
import com.google.common.eventbus.Subscribe;
import glm.vec._2.Vec2;
import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.font.Typeface;
import ru.windcorp.progressia.client.graphics.gui.Button;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.DragManager;
import ru.windcorp.progressia.client.graphics.gui.Group;
import ru.windcorp.progressia.client.graphics.gui.Label;
import ru.windcorp.progressia.client.graphics.gui.Panel;
import ru.windcorp.progressia.client.graphics.gui.event.DragEvent;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutBorderHorizontal;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
import ru.windcorp.progressia.client.localization.MutableString;
import ru.windcorp.progressia.client.localization.MutableStringConcat;
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
import ru.windcorp.progressia.client.world.item.inventory.InventoryComponent;
public class InventoryWindow extends Panel {
private static final String CLOSE_CHAR = "\u2715";
private static final String HANDLE_CHAR = "\u2800";
private static final Vec4 CLOSE_BUTTON_IDLE = Colors.toVector(0xFFBC1515);
private static final Vec4 CLOSE_BUTTON_HOVER = Colors.toVector(0xFFFA6464);
private static final Vec4 CLOSE_BUTTON_PRESSED = Colors.BLACK;
private final InventoryComponent content;
private final HUDWorkspace workspace;
private final Vec2 relativePosition = new Vec2(Float.NaN);
public InventoryWindow(String name, InventoryComponent component, HUDWorkspace workspace) {
super(name, new LayoutVertical(15, 15));
this.content = component;
this.workspace = workspace;
Group titleBar = new Group(getName() + ".TitleBar", new LayoutBorderHorizontal());
titleBar.addChild(createLabel(component).setLayoutHint(LayoutBorderHorizontal.CENTER));
titleBar.addChild(createCloseButton(component).setLayoutHint(LayoutBorderHorizontal.RIGHT));
new DragManager().install(titleBar);
titleBar.addListener(new Object() {
@Subscribe
public void onWindowDragged(DragEvent e) {
Vec2 change = new Vec2((float) e.getCurrentChangeX(), (float) e.getCurrentChangeY());
change.div(getParent().getWidth(), getParent().getHeight());
relativePosition.add(change);
requestReassembly();
}
});
addChild(titleBar);
addChild(component);
}
private Label createLabel(InventoryComponent component) {
String translationKey = "Inventory." + component.getInventory().getId() + ".Title";
MutableString titleText = new MutableStringConcat(
HANDLE_CHAR + " ",
new MutableStringLocalized(translationKey)
);
Font titleFont = new Font().deriveBold().withColor(Colors.BLACK).withAlign(Typeface.ALIGN_LEFT);
return new Label(getName() + ".Title", titleFont, titleText);
}
private WindowedHUD getManager() {
Component parent = getParent();
if (parent instanceof WindowedHUD) {
return (WindowedHUD) parent;
}
return null;
}
/**
* @return the relativePosition
*/
public Vec2 getRelativePosition() {
return relativePosition;
}
private Component createCloseButton(InventoryComponent component) {
Button button = new Button(getName() + ".CloseButton", CLOSE_CHAR) {
@Override
protected void assembleSelf(RenderTarget target) {
Vec4 color = CLOSE_BUTTON_IDLE;
if (isPressed()) {
color = CLOSE_BUTTON_PRESSED;
} else if (isHovered()) {
color = CLOSE_BUTTON_HOVER;
}
if (hasLabel()) {
getLabel().setFont(getLabel().getFont().withColor(color));
}
}
};
button.addAction(b -> {
content.getInventory().close(workspace.getPlayerEntity());
WindowedHUD manager = getManager();
if (manager == null) {
return;
}
manager.closeWindow(this);
});
button.setLayout(new LayoutFill());
int height = button.getLabel().getFont().getHeight(button.getLabel().getCurrentText());
button.setPreferredSize(height, height);
return button;
}
public InventoryComponent getContent() {
return content;
}
}

View File

@ -0,0 +1,71 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world.hud;
import java.io.IOException;
import ru.windcorp.progressia.client.graphics.flat.FlatRenderProgram;
import ru.windcorp.progressia.client.graphics.font.SpriteTypeface;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram;
import ru.windcorp.progressia.client.graphics.texture.ComplexTexture;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.graphics.texture.TextureLoader;
import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive;
import ru.windcorp.progressia.client.graphics.texture.TextureSettings;
import ru.windcorp.progressia.common.resource.ResourceManager;
public class ItemAmountTypeface extends SpriteTypeface {
public static final int HEIGHT = 5;
public static final int WIDTH = 3;
private final Texture[] textures = new Texture[10];
public ItemAmountTypeface() throws IOException {
super("ItemAmount", HEIGHT, 1);
ComplexTexture atlas = new ComplexTexture(new TexturePrimitive(
TextureLoader.loadPixels(
ResourceManager.getTextureResource("gui/ItemAmountTypeface"),
new TextureSettings(false)
).getData()
), 30, 5);
for (int i = 0; i <= 9; ++i) {
textures[i] = atlas.get(i * WIDTH, 0, WIDTH, HEIGHT);
}
}
@Override
public Texture getTexture(char c) {
if (!supports(c))
return textures[0];
return textures[c - '0'];
}
@Override
public ShapeRenderProgram getProgram() {
return FlatRenderProgram.getDefault();
}
@Override
public boolean supports(char c) {
return c >= '0' && c <= '9';
}
}

View File

@ -0,0 +1,153 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world.hud;
import com.google.common.eventbus.Subscribe;
import ru.windcorp.progressia.client.events.NewLocalEntityEvent;
import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.Components;
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
import ru.windcorp.progressia.client.graphics.gui.Group;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
import ru.windcorp.progressia.client.graphics.input.bus.InputBus;
import ru.windcorp.progressia.test.controls.TestPlayerControls;
public class LayerHUD extends GUILayer {
private final HUDManager manager;
private WindowedHUD windowManager = null;
private boolean showInventory = false;
private boolean isHidden = false;
public LayerHUD(HUDManager manager) {
super("LayerHUD", new LayoutFill(15));
this.manager = manager;
setCursorPolicy(CursorPolicy.INDIFFERENT);
manager.getClient().subscribe(this);
getRoot().addKeyListener(new KeyMatcher("Escape"), e -> {
setInventoryShown(!showInventory);
e.consume();
}, InputBus.Option.IGNORE_FOCUS);
getRoot().addKeyListener(new KeyMatcher("E"), e -> {
setInventoryShown(!showInventory);
e.consume();
}, InputBus.Option.IGNORE_FOCUS);
getRoot().addKeyListener(new KeyMatcher("Left Control"), e -> {
TestPlayerControls.getInstance().getInventoryControls().switchHandsWithCtrl(e);
e.consume();
}, InputBus.Option.IGNORE_ACTION, InputBus.Option.IGNORE_FOCUS);
getRoot().addKeyListener(new KeyMatcher("Right Control"), e -> {
TestPlayerControls.getInstance().getInventoryControls().switchHandsWithCtrl(e);
e.consume();
}, InputBus.Option.IGNORE_ACTION, InputBus.Option.IGNORE_FOCUS);
}
@Subscribe
private void onEntityChanged(NewLocalEntityEvent e) {
while (!getRoot().getChildren().isEmpty()) {
getRoot().removeChild(getRoot().getChild(0));
}
if (e.getNewEntity() == null) {
getRoot().requestReassembly();
return;
}
getRoot().addChild(new PermanentHUD(getName() + ".Permanent", manager));
Component inventoryGroup = new Group(getName() + ".InventoryGroup", new LayoutFill());
inventoryGroup.addChild(new InventoryHUD(getName() + ".Equipment", manager));
windowManager = new WindowedHUD(getName() + ".Windows");
inventoryGroup.addChild(windowManager);
inventoryGroup.addChild(new CursorHUD(getName() + ".Cursor", manager.getPlayerEntity()));
getRoot().addChild(Components.hide(inventoryGroup, () -> !showInventory));
getRoot().requestReassembly();
}
public WindowedHUD getWindowManager() {
return windowManager;
}
public boolean isInventoryShown() {
return showInventory;
}
public void setInventoryShown(boolean showInventory) {
this.showInventory = showInventory;
updateCursorPolicy();
}
public boolean isHidden() {
return isHidden;
}
public void setHidden(boolean isHidden) {
this.isHidden = isHidden;
updateCursorPolicy();
}
private void updateCursorPolicy() {
if (showInventory && !isHidden) {
setCursorPolicy(CursorPolicy.REQUIRE);
} else {
setCursorPolicy(CursorPolicy.INDIFFERENT);
}
GUI.updateLayer(this);
}
@Override
public void handleInput(InputEvent event) {
if (isHidden) {
return;
}
super.handleInput(event);
if (getCursorPolicy() == CursorPolicy.REQUIRE) {
event.consume();
}
}
@Override
protected void doRender() {
if (isHidden) {
return;
}
super.doRender();
}
}

View File

@ -0,0 +1,38 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world.hud;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutBorderVertical;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
public class PermanentHUD extends Component {
public PermanentHUD(String name, HUDManager manager) {
super(name);
setLayout(new LayoutBorderVertical());
EntityDataPlayer entity = manager.getPlayerEntity();
if (entity == null) {
throw new IllegalStateException("Player " + manager.getPlayer() + " does not have an associated entity");
}
addChild(new HandsHUD(name + ".Hands", manager).setLayoutHint(LayoutBorderVertical.UP));
}
}

View File

@ -0,0 +1,217 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world.hud;
import java.util.function.BooleanSupplier;
import glm.mat._4.Mat4;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
import ru.windcorp.progressia.client.graphics.flat.FlatRenderProgram;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.DynamicLabel;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.client.graphics.model.Shapes.PgmBuilder;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.world.item.ItemRenderRegistry;
import ru.windcorp.progressia.client.world.item.ItemRenderable;
import ru.windcorp.progressia.common.world.item.ItemData;
import ru.windcorp.progressia.common.world.item.ItemDataContainer;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainer;
import ru.windcorp.progressia.common.world.item.inventory.ItemSlot;
public class SlotComponent extends Component {
static final float TEXTURE_SIZE = 24;
private static boolean drawVirtualSlots;
static {
String key = SlotComponent.class.getName() + ".drawVirtualSlots";
drawVirtualSlots = Boolean.parseBoolean(System.getProperty(key, "true"));
}
private static Renderable containerOpenDecoration = null;
private static Renderable containerOpenableDecoration = null;
private final ItemSlot slot;
private float scale = 2;
private ItemRenderable itemRenderer = null;
private int amountDisplayInt = 0;
private String amountDisplayString = "";
private Renderable background = null;
private BooleanSupplier backgroundCondition = null;
public SlotComponent(String name, ItemContainer container, int index) {
super(name);
this.slot = new ItemSlot(container, index);
setScale(2);
Font sizeFont = new Font(HUDTextures.getItemAmountTypeface()).deriveOutlined().withScale(2);
addChild(new DynamicLabel(getName() + ".Size", sizeFont, () -> amountDisplayString, getPreferredSize().x));
setLayout(new LayoutAlign(0, 0, 0));
if (containerOpenDecoration == null) {
containerOpenDecoration = new PgmBuilder(
FlatRenderProgram.getDefault(),
HUDTextures.getHUDTexture("DecorationContainerOpen")
).setSize(TEXTURE_SIZE + 2).setOrigin(-1, -1, 0).create();
}
if (containerOpenableDecoration == null) {
containerOpenableDecoration = new PgmBuilder(
FlatRenderProgram.getDefault(),
HUDTextures.getHUDTexture("DecorationContainerOpenable")
).setSize(TEXTURE_SIZE + 2).setOrigin(-1, -1, 0).create();
}
}
/**
* @return the container
*/
public ItemContainer getSlotContainer() {
return slot.getContainer();
}
/**
* @return the index
*/
public int getSlotIndex() {
return slot.getIndex();
}
public ItemSlot getSlot() {
return slot;
}
public SlotComponent setScale(float scale) {
this.scale = scale;
int side = (int) (TEXTURE_SIZE * scale);
setPreferredSize(side, side);
invalidate();
return this;
}
public SlotComponent setBackground(Texture texture, BooleanSupplier when) {
background = new PgmBuilder(FlatRenderProgram.getDefault(), texture).setSize(TEXTURE_SIZE).create();
setBackgroundDisplayCondition(when);
return this;
}
public SlotComponent setBackground(Texture texture) {
return setBackground(texture, null);
}
public SlotComponent setBackgroundDisplayCondition(BooleanSupplier backgroundCondition) {
this.backgroundCondition = backgroundCondition;
return this;
}
@Override
protected void assembleSelf(RenderTarget target) {
super.assembleSelf(target);
assembleItem(target);
}
private void updateItemRenderer() {
ItemData contents;
contents = slot.getItem();
if (contents == null) {
itemRenderer = null;
amountDisplayInt = 0;
amountDisplayString = "";
} else {
if (itemRenderer == null || itemRenderer.getData() != contents) {
itemRenderer = ItemRenderRegistry.getInstance().get(contents.getId()).createRenderable(contents);
}
int newAmount = slot.getCount();
if (newAmount != amountDisplayInt) {
amountDisplayInt = newAmount;
amountDisplayString = newAmount == 1 ? "" : Integer.toString(newAmount);
}
}
}
private void assembleItem(RenderTarget target) {
target.pushTransform(new Mat4().translate(getX(), getY(), 0).scale(scale, scale, 1));
target.addCustomRenderer(renderer -> {
updateItemRenderer();
if (drawVirtualSlots && getSlot() == null) {
renderer.pushColorMultiplier().set(Colors.DEBUG_GREEN);
containerOpenDecoration.render(renderer);
renderer.popColorMultiplier();
return;
}
if (itemRenderer != null) {
itemRenderer.render(renderer);
renderDecorations(renderer);
} else if (background != null) {
if (backgroundCondition == null || backgroundCondition.getAsBoolean()) {
background.render(renderer);
}
}
});
target.popTransform();
}
private void renderDecorations(ShapeRenderHelper renderer) {
ItemData contents = getSlot().getItem();
if (contents instanceof ItemDataContainer) {
ItemDataContainer asContainer = (ItemDataContainer) contents;
if (asContainer.isOpen()) {
renderer.pushColorMultiplier().mul(Colors.BLUE);
containerOpenDecoration.render(renderer);
renderer.popColorMultiplier();
} else {
double dx = InputTracker.getCursorX() - (getX() + getWidth() / 2);
double dy = InputTracker.getCursorY() - (getY() + getHeight() / 2);
double distanceToCursorSquared = dx*dx + dy*dy;
final double maxDistanceSquared = (scale * TEXTURE_SIZE * 4) * (scale * TEXTURE_SIZE * 4);
float opacity = (float) (1 - distanceToCursorSquared / maxDistanceSquared);
if (opacity > 0) {
renderer.pushColorMultiplier().mul(Colors.BLUE.x, Colors.BLUE.y, Colors.BLUE.z, opacity);
containerOpenableDecoration.render(renderer);
renderer.popColorMultiplier();
}
}
}
}
}

View File

@ -0,0 +1,48 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world.hud;
import ru.windcorp.progressia.client.graphics.gui.Component;
public class WindowedHUD extends Component {
public WindowedHUD(String name) {
super(name);
setLayout(new WindowedLayout());
}
public void addWindow(InventoryWindow window) {
addChild(window);
window.setSize(window.getPreferredSize());
centerWindow(window);
}
public void closeWindow(InventoryWindow window) {
removeChild(window);
requestReassembly();
}
private void centerWindow(InventoryWindow window) {
window.setPosition(
(getWidth() - window.getWidth()) / 2,
(getHeight() - window.getHeight()) / 2
);
}
}

View File

@ -0,0 +1,64 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.graphics.world.hud;
import glm.Glm;
import glm.vec._2.Vec2;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.Layout;
public class WindowedLayout implements Layout {
@Override
public void layout(Component c) {
for (Component component : c.getChildren()) {
InventoryWindow window = (InventoryWindow) component;
Vec2i size = new Vec2i(c.getWidth(), c.getHeight());
Glm.min(window.getPreferredSize(), size, size);
window.setSize(size);
Vec2 relPos = window.getRelativePosition();
if (Float.isNaN(relPos.x) || Float.isNaN(relPos.y)) {
relPos.x = 0.5f;
relPos.y = 2 / 3.0f;
} else {
float minPosX = 0;
float minPosY = window.getHeight() / (float) c.getHeight();
float maxPosX = 1;
float maxPosY = 1;
relPos.x = Glm.clamp(relPos.x, minPosX, maxPosX);
relPos.y = Glm.clamp(relPos.y, minPosY, maxPosY);
}
window.setPosition(
(int) (relPos.x * c.getWidth() - window.getWidth() / 2.0f),
(int) (relPos.y * c.getHeight() - window.getHeight())
);
}
}
@Override
public Vec2i calculatePreferredSize(Component c) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,51 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
public abstract class UpdatingRenderable implements Renderable {
private long stateComputedForFrame = -1;
/**
* Updates the state of this model. This method is invoked exactly once per
* renderable per frame before this model is queried for the first time.
*/
protected void update() {
// Do nothing
}
protected void updateIfNecessary() {
if (stateComputedForFrame != GraphicsInterface.getFramesRendered()) {
update();
stateComputedForFrame = GraphicsInterface.getFramesRendered();
}
}
@Override
public final void render(ShapeRenderHelper renderer) {
updateIfNecessary();
doRender(renderer);
}
protected abstract void doRender(ShapeRenderHelper renderer);
}

View File

@ -0,0 +1,39 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.entity;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
public class EntityRenderPlayer extends EntityRender {
public EntityRenderPlayer(String id) {
super(id);
}
@Override
public EntityRenderable createRenderable(EntityData entity) {
EntityDataPlayer playerEntity = (EntityDataPlayer) entity;
String speciesId = playerEntity.getSpecies().getId();
SpeciesRender speciesRender = SpeciesRenderRegistry.getInstance().get(speciesId);
return speciesRender.createRenderable(playerEntity);
}
}

View File

@ -42,7 +42,7 @@ public class EntityRenderRegistry extends NamespacedInstanceRegistry<EntityRende
ResourceManager.getTextureResource(
"entities/" + name
),
new TextureSettings(false, false)
new TextureSettings(false)
).getData()
);
} catch (IOException e) {

View File

@ -19,45 +19,18 @@
package ru.windcorp.progressia.client.world.entity;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.client.world.UpdatingRenderable;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.generic.EntityGeneric;
public abstract class EntityRenderable implements Renderable, EntityGeneric {
public abstract class EntityRenderable extends UpdatingRenderable implements EntityGeneric {
private final EntityData data;
private long stateComputedForFrame = -1;
public EntityRenderable(EntityData data) {
this.data = data;
}
/**
* Updates the state of this model. This method is invoked exactly once per
* renderable per frame before this entity is queried for the first time.
*/
protected void update() {
// Do nothing
}
private void updateIfNecessary() {
if (stateComputedForFrame != GraphicsInterface.getFramesRendered()) {
update();
stateComputedForFrame = GraphicsInterface.getFramesRendered();
}
}
@Override
public final void render(ShapeRenderHelper renderer) {
updateIfNecessary();
doRender(renderer);
}
protected abstract void doRender(ShapeRenderHelper renderer);
public EntityData getData() {
return data;
}

View File

@ -0,0 +1,52 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.entity;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.graphics.world.hud.InventoryHUD;
import ru.windcorp.progressia.client.graphics.world.hud.HUDTextures;
import ru.windcorp.progressia.client.graphics.world.hud.HandsHUD;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
import ru.windcorp.progressia.common.world.entity.SpeciesData.EquipmentSlot;
import ru.windcorp.progressia.common.world.entity.SpeciesData.Hand;
public abstract class SpeciesRender extends Namespaced {
public SpeciesRender(String id) {
super(id);
}
public abstract EntityRenderable createRenderable(EntityDataPlayer entity);
public abstract HandsHUD.Side getHandSide(Hand hand);
public abstract InventoryHUD.Side getEquipmentSlotSide(EquipmentSlot equipmentSlot);
public Texture getTexture(String name) {
return HUDTextures.getHUDTexture(getNamespace() + "_" + getName() + "/" + name);
}
public Texture getHandBackground(Hand hand) {
return getTexture("Hand" + hand.getName());
}
public Texture getEquipmentSlotBackground(EquipmentSlot slot) {
return getTexture("EquipmentSlot" + slot.getName());
}
}

View File

@ -0,0 +1,35 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.entity;
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
public class SpeciesRenderRegistry extends NamespacedInstanceRegistry<SpeciesRender> {
private static final SpeciesRenderRegistry INSTANCE = new SpeciesRenderRegistry();
public static SpeciesRenderRegistry getInstance() {
return INSTANCE;
}
public SpeciesRender get(EntityDataPlayer player) {
return get(player.getSpecies().getId());
}
}

View File

@ -0,0 +1,31 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.item;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.item.ItemData;
public abstract class ItemRender extends Namespaced {
public ItemRender(String id) {
super(id);
}
public abstract ItemRenderable createRenderable(ItemData data);
}

View File

@ -0,0 +1,51 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.item;
import ru.windcorp.progressia.client.graphics.texture.Atlases;
import ru.windcorp.progressia.client.graphics.texture.SimpleTexture;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.graphics.texture.Atlases.AtlasGroup;
import ru.windcorp.progressia.common.resource.ResourceManager;
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
public class ItemRenderRegistry extends NamespacedInstanceRegistry<ItemRender> {
private static final ItemRenderRegistry INSTANCE = new ItemRenderRegistry();
private static final AtlasGroup ITEMS_ATLAS_GROUP = new AtlasGroup("Items", 1 << 12);
public static ItemRenderRegistry getInstance() {
return INSTANCE;
}
public static Texture getItemTexture(String name) {
return new SimpleTexture(
Atlases.getSprite(
ResourceManager.getTextureResource("items/" + name),
ITEMS_ATLAS_GROUP
)
);
}
public static AtlasGroup getItemsAtlasGroup() {
return ITEMS_ATLAS_GROUP;
}
}

View File

@ -0,0 +1,69 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.item;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.backend.Usage;
import ru.windcorp.progressia.client.graphics.flat.FlatRenderProgram;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.Shape;
import ru.windcorp.progressia.client.graphics.model.ShapeParts;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.common.world.item.ItemData;
public class ItemRenderSimple extends ItemRender {
private Texture texture;
private Renderable renderable;
public ItemRenderSimple(String id, Texture texture) {
super(id);
this.texture = texture;
this.renderable = new Shape(
Usage.STATIC,
FlatRenderProgram.getDefault(),
ShapeParts.createRectangle(
FlatRenderProgram.getDefault(),
texture,
Colors.WHITE,
new Vec3(0, 0, 0),
new Vec3(24, 0, 0),
new Vec3(0, 24, 0),
false
)
);
}
public Texture getTexture() {
return texture;
}
@Override
public ItemRenderable createRenderable(ItemData data) {
return new ItemRenderable(data) {
@Override
protected void doRender(ShapeRenderHelper renderer) {
renderable.render(renderer);
}
};
}
}

View File

@ -0,0 +1,35 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.item;
import ru.windcorp.progressia.client.world.UpdatingRenderable;
import ru.windcorp.progressia.common.world.item.ItemData;
public abstract class ItemRenderable extends UpdatingRenderable {
private final ItemData data;
public ItemRenderable(ItemData data) {
this.data = data;
}
public ItemData getData() {
return data;
}
}

View File

@ -0,0 +1,36 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.item.inventory;
import java.util.Collection;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.world.hud.InteractiveSlotComponent;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainer;
public abstract class ContainerComponent extends Component {
public ContainerComponent(String name) {
super(name);
}
public abstract ItemContainer getContainer();
public abstract Collection<InteractiveSlotComponent> getSlots();
}

View File

@ -0,0 +1,137 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.item.inventory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.google.common.eventbus.Subscribe;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.Group;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutBorderHorizontal;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutBorderVertical;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutGrid;
import ru.windcorp.progressia.client.graphics.world.hud.Bar;
import ru.windcorp.progressia.client.graphics.world.hud.HUDWorkspace;
import ru.windcorp.progressia.client.graphics.world.hud.InteractiveSlotComponent;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainer;
import ru.windcorp.progressia.common.world.item.inventory.event.ItemSlotChangedEvent;
public class ContainerComponentSimple extends ContainerComponent {
private final Group slots = new Group("Inventory.Slots", new LayoutGrid(0, 15));
private final List<InteractiveSlotComponent> slotCollection = new ArrayList<>();
private final ItemContainer container;
private final HUDWorkspace workspace;
private int tmp__getSlotsPerRow() {
return 6;
}
public ContainerComponentSimple(ItemContainer container, HUDWorkspace workspace) {
super("Inventory");
this.container = container;
this.workspace = workspace;
if (container.getInventory() != null) {
container.getInventory().subscribe(this);
}
setLayout(new LayoutBorderHorizontal(15));
Bar massBar = new Bar(
"MassBar",
true,
Colors.toVector(0xFF44AAAA),
container::getMass,
container::getMassLimit
);
Bar volumeBar = new Bar(
"VolumeBar",
false,
Colors.toVector(0xFFAA4444),
container::getVolume,
container::getVolumeLimit
);
Component slotsAndVolumeBar = new Group(
"SlotsAndVolumeBar",
new LayoutBorderVertical(15),
slots.setLayoutHint(LayoutBorderVertical.CENTER),
volumeBar.setLayoutHint(LayoutBorderVertical.UP)
);
addChild(slotsAndVolumeBar.setLayoutHint(LayoutBorderHorizontal.CENTER));
addChild(massBar.setLayoutHint(LayoutBorderHorizontal.LEFT));
onSlotChanged(null);
}
private void addSlot(int index) {
final int maxX = tmp__getSlotsPerRow();
InteractiveSlotComponent component = new InteractiveSlotComponent("Inventory.Slot." + index, container, index, workspace);
Vec2i pos = new Vec2i(index % maxX, index / maxX);
slots.addChild(component.setLayoutHint(pos));
slotCollection.add(component);
}
private void removeSlot(int index) {
slots.removeChild(slotCollection.remove(index));
}
@Override
public ItemContainer getContainer() {
return container;
}
@Override
public Collection<InteractiveSlotComponent> getSlots() {
return slotCollection;
}
@Subscribe
private void onSlotChanged(ItemSlotChangedEvent e) {
if (e != null && e.getContainer() != container) {
return;
}
int wantSlots = container.getLastFilledSlot();
int slotsPerRow = tmp__getSlotsPerRow();
wantSlots = (wantSlots / slotsPerRow + 2) * slotsPerRow;
if (wantSlots < slotsPerRow) {
wantSlots = slotsPerRow;
}
while (wantSlots > slotCollection.size()) {
addSlot(slotCollection.size());
}
while (wantSlots < slotCollection.size()) {
removeSlot(slotCollection.size() - 1);
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.item.inventory;
import java.util.Collection;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.common.world.item.inventory.Inventory;
public abstract class InventoryComponent extends Component {
public InventoryComponent(String name) {
super(name);
}
public abstract Inventory getInventory();
public abstract Collection<? extends ContainerComponent> getContainers();
}

View File

@ -0,0 +1,55 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.item.inventory;
import java.util.ArrayList;
import java.util.Collection;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
import ru.windcorp.progressia.client.graphics.world.hud.HUDWorkspace;
import ru.windcorp.progressia.common.world.item.inventory.Inventory;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainer;
public class InventoryComponentSimple extends InventoryComponent {
private final Inventory inventory;
private final Collection<ContainerComponentSimple> containers = new ArrayList<>();
public InventoryComponentSimple(String name, Inventory inventory, HUDWorkspace workspace) {
super(name);
setLayout(new LayoutFill());
this.inventory = inventory;
for (ItemContainer container : inventory.getContainers()) {
ContainerComponentSimple component = new ContainerComponentSimple(container, workspace);
containers.add(component);
addChild(component);
}
}
@Override
public Inventory getInventory() {
return inventory;
}
@Override
public Collection<? extends ContainerComponent> getContainers() {
return containers;
}
}

View File

@ -0,0 +1,32 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.item.inventory;
import ru.windcorp.progressia.client.graphics.world.hud.HUDWorkspace;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.item.inventory.Inventory;
public abstract class InventoryRender extends Namespaced {
public InventoryRender(String id) {
super(id);
}
public abstract InventoryComponent createComponent(Inventory inventory, HUDWorkspace workspace);
}

View File

@ -0,0 +1,30 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.item.inventory;
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
public class InventoryRenderRegistry extends NamespacedInstanceRegistry<InventoryRender> {
private static final InventoryRenderRegistry INSTANCE = new InventoryRenderRegistry();
public static InventoryRenderRegistry getInstance() {
return INSTANCE;
}
}

View File

@ -65,6 +65,8 @@ public class Units {
// Volume
public static final float CUBIC_CENTIMETERS = CENTIMETERS * CENTIMETERS * CENTIMETERS;
@RegisteredUnit("L")
public static final float LITERS = (10 * CENTIMETERS) * (10 * CENTIMETERS) * (10 * CENTIMETERS);
public static final float CUBIC_METERS = METERS * METERS * METERS;
public static final float CUBIC_MILLIMETERS = MILLIMETERS * MILLIMETERS * MILLIMETERS;
public static final float CUBIC_KILOMETERS = KILOMETERS * KILOMETERS * KILOMETERS;

View File

@ -0,0 +1,87 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.common.state;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class BooleanStateField extends StateField {
public BooleanStateField(
String id,
boolean isLocal,
int index
) {
super(id, isLocal, index);
}
public boolean get(StatefulObject object) {
return object.getStorage().getBoolean(getIndex());
}
public void setNow(StatefulObject object, boolean value) {
object.getStorage().setBoolean(getIndex(), value);
}
public void set(StateChanger changer, boolean value) {
changer.setBoolean(this, value);
}
@Override
public void read(
StatefulObject object,
DataInput input,
IOContext context
)
throws IOException {
object.getStorage().setBoolean(getIndex(), input.readBoolean());
}
@Override
public void write(
StatefulObject object,
DataOutput output,
IOContext context
)
throws IOException {
output.writeBoolean(object.getStorage().getBoolean(getIndex()));
}
@Override
public void copy(StatefulObject from, StatefulObject to) {
setNow(to, get(from));
}
@Override
public int computeHashCode(StatefulObject object) {
return Boolean.hashCode(get(object));
}
@Override
public boolean areEqual(StatefulObject a, StatefulObject b) {
return get(a) == get(b);
}
@Override
public void setDefault(StateStorage storage) {
storage.setBoolean(getIndex(), false);
}
}

View File

@ -22,10 +22,13 @@ import gnu.trove.map.TIntIntMap;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntIntHashMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.set.TIntSet;
import gnu.trove.set.hash.TIntHashSet;
public class HashMapStateStorage extends StateStorage {
private final TIntIntMap ints = new TIntIntHashMap();
private final TIntSet booleans = new TIntHashSet();
private final TIntObjectMap<Object> objects = new TIntObjectHashMap<>();
@Override
@ -38,6 +41,20 @@ public class HashMapStateStorage extends StateStorage {
ints.put(index, value);
}
@Override
public boolean getBoolean(int index) {
return booleans.contains(index);
}
@Override
public void setBoolean(int index, boolean value) {
if (value) {
booleans.add(index);
} else {
booleans.remove(index);
}
}
@Override
public Object getObject(int index) {
return objects.get(index);

View File

@ -85,6 +85,21 @@ public class InspectingStatefulObjectLayout
}
private class Boolean implements StateFieldBuilder.Boolean {
@Override
public BooleanStateField build() {
return registerField(
new BooleanStateField(
id,
isLocal,
fieldIndexCounters.getBooleansThenIncrement()
)
);
}
}
private class Obj<T> implements StateFieldBuilder.Obj<T> {
private final ObjectCodec<T> codec;
@ -123,6 +138,11 @@ public class InspectingStatefulObjectLayout
return new Int();
}
@Override
public Boolean ofBoolean() {
return new Boolean();
}
@Override
public <T> Obj<T> of(ObjectCodec<T> codec, Supplier<T> defaultValue) {
return new Obj<T>(codec, defaultValue);

View File

@ -21,11 +21,13 @@ package ru.windcorp.progressia.common.state;
public class OptimizedStateStorage extends StateStorage {
private final int[] ints;
private final boolean[] booleans;
private final Object[] objects;
public OptimizedStateStorage(PrimitiveCounters sizes) {
this.ints = new int[sizes.getInts()];
this.objects = new Object[sizes.getObjects()];
this.ints = sizes.getInts() == 0 ? null : new int[sizes.getInts()];
this.booleans = sizes.getBooleans() == 0 ? null : new boolean[sizes.getBooleans()];
this.objects = sizes.getObjects() == 0 ? null : new Object[sizes.getObjects()];
}
@Override
@ -38,6 +40,16 @@ public class OptimizedStateStorage extends StateStorage {
ints[index] = value;
}
@Override
public boolean getBoolean(int index) {
return booleans[index];
}
@Override
public void setBoolean(int index, boolean value) {
booleans[index] = value;
}
@Override
public Object getObject(int index) {
return objects[index];

View File

@ -75,6 +75,16 @@ public class OptimizedStatefulObjectLayout
};
}
@Override
public Boolean ofBoolean() {
return new Boolean() {
@Override
public BooleanStateField build() {
return (BooleanStateField) result;
}
};
}
@Override
public <T> Obj<T> of(ObjectCodec<T> codec, Supplier<T> defaultValue) {
return new Obj<T>() {

View File

@ -21,6 +21,7 @@ package ru.windcorp.progressia.common.state;
class PrimitiveCounters {
private int ints = 0;
private int booleans = 0;
private int objects = 0;
public PrimitiveCounters() {
@ -28,6 +29,7 @@ class PrimitiveCounters {
public PrimitiveCounters(PrimitiveCounters copyFrom) {
this.ints = copyFrom.ints;
this.booleans = copyFrom.booleans;
this.objects = copyFrom.objects;
}
@ -39,6 +41,14 @@ class PrimitiveCounters {
return this.ints++;
}
public int getBooleans() {
return booleans;
}
public int getBooleansThenIncrement() {
return this.booleans++;
}
public int getObjects() {
return objects;
}

View File

@ -21,6 +21,7 @@ package ru.windcorp.progressia.common.state;
public interface StateChanger {
void setInt(IntStateField field, int value);
void setBoolean(BooleanStateField field, boolean value);
<T> void setObject(ObjectStateField<T> field, T value);
}

View File

@ -29,12 +29,18 @@ public interface StateFieldBuilder {
IntStateField build();
}
public static interface Boolean {
BooleanStateField build();
}
public static interface Obj<T> {
ObjectStateField<T> build();
}
Int ofInt();
Boolean ofBoolean();
<T> Obj<T> of(ObjectCodec<T> codec, Supplier<T> defaultValue);
default <T> Obj<T> of(Class<T> clazz, Supplier<T> defaultValue) {

View File

@ -24,6 +24,10 @@ public abstract class StateStorage {
public abstract void setInt(int index, int value);
public abstract boolean getBoolean(int index);
public abstract void setBoolean(int index, boolean value);
public abstract Object getObject(int index);
public abstract void setObject(int index, Object object);

View File

@ -42,6 +42,16 @@ public class StatefulObjectRegistry<T extends StatefulObject> {
*/
T build();
}
@FunctionalInterface
public static interface IdFactory<T> {
/**
* Initializes a new, independent instance of the stateful object.
*
* @return the created object
*/
T build(String id);
}
protected static class Type<T> extends Namespaced {
@ -110,5 +120,9 @@ public class StatefulObjectRegistry<T extends StatefulObject> {
public void register(String id, Factory<T> factory) {
registry.register(new Type<>(id, factory));
}
public void register(String id, IdFactory<T> factory) {
register(id, () -> factory.build(id));
}
}

View File

@ -0,0 +1,102 @@
/*
* Progressia
* Copyright (C) 2020-2021 Wind Corporation and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.common.world.entity;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import ru.windcorp.progressia.common.state.IntStateField;
import ru.windcorp.progressia.common.state.ObjectStateField;
import ru.windcorp.progressia.common.util.crash.ReportingEventBus;
import ru.windcorp.progressia.common.world.item.inventory.InventoryUser;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainerEquipment;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainerHand;
import ru.windcorp.progressia.common.world.item.inventory.event.InventoryClosingEvent;
import ru.windcorp.progressia.common.world.item.inventory.event.InventoryOpenedEvent;
public class EntityDataPlayer extends EntityData implements InventoryUser {
private final ObjectStateField<SpeciesDatalet> speciesDatalet = field("Core:SpeciesDatalet").setShared()
.of(SpeciesDataRegistry.getInstance().getCodec()).build();
private final IntStateField selectedHand = field("Core:SelectedHand").setShared().ofInt().build();
private final EventBus eventBus = ReportingEventBus.create("EntityDataPlayer");
public EntityDataPlayer(String id, SpeciesData species) {
super(id);
setSpecies(species);
}
private void setSpecies(SpeciesData species) {
speciesDatalet.setNow(this, species.createDatalet());
setCollisionModel(species.getCollisionModel());
}
public SpeciesData getSpecies() {
return speciesDatalet.get(this).getSpecies();
}
public ItemContainerHand getHand(int index) {
return speciesDatalet.get(this).getHands()[index];
}
public int getHandCount() {
return speciesDatalet.get(this).getHands().length;
}
public ItemContainerEquipment getEquipmentSlot(int index) {
return speciesDatalet.get(this).getEquipment()[index];
}
public int getEquipmentCount() {
return speciesDatalet.get(this).getEquipment().length;
}
public int getSelectedHandIndex() {
return selectedHand.get(this);
}
public void setSelectedHandIndexNow(int index) {
selectedHand.setNow(this, index);
}
public ItemContainerHand getSelectedHand() {
return getHand(getSelectedHandIndex());
}
@Subscribe
private void onInventoryOpened(InventoryOpenedEvent event) {
eventBus.post(event);
}
@Subscribe
private void onInventoryClosed(InventoryClosingEvent event) {
eventBus.post(event);
}
public void subscribe(Object listener) {
eventBus.register(listener);
}
public void unsubscribe(Object listener) {
eventBus.unregister(listener);
}
}

Some files were not shown because too many files have changed in this diff Show More