Merge branch 'master' into addPlanet
Conflicts: src/main/java/ru/windcorp/progressia/test/LayerTestGUI.java src/main/java/ru/windcorp/progressia/test/TestPlayerControls.java
This commit is contained in:
commit
0264e512ab
@ -16,7 +16,7 @@ temperature mechanics and a parallelism-capable server.
|
|||||||
- GNU/Linux (x64, arm32 or arm64), Windows XP or later (x64 or x86) or MacOS (x64)
|
- GNU/Linux (x64, arm32 or arm64), Windows XP or later (x64 or x86) or MacOS (x64)
|
||||||
- Java 8 or later
|
- Java 8 or later
|
||||||
- OpenGL 2.1 or later
|
- OpenGL 2.1 or later
|
||||||
- Probably at least 4 GiB RAM
|
- Probably about 0.5 GiB RAM
|
||||||
- Less than 1 GiB of storage space
|
- Less than 1 GiB of storage space
|
||||||
|
|
||||||
See [Build Guide](docs/building/BuildGuide.md) for compilation requirements.
|
See [Build Guide](docs/building/BuildGuide.md) for compilation requirements.
|
||||||
|
69
docs/CONTRIBUTING.md
Normal file
69
docs/CONTRIBUTING.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# Contributing Guidelines
|
||||||
|
|
||||||
|
This document lists conventions adopted by Progressia developers.
|
||||||
|
|
||||||
|
## git
|
||||||
|
|
||||||
|
### Branches
|
||||||
|
Progressia repository contains a `master` branch and several "feature" branches.
|
||||||
|
|
||||||
|
`master` is expected to contain a version of the game suitable for demonstration and forking/branching. Do not commit directly to `master` without OLEGSHA's approval.
|
||||||
|
- `master` must always correctly build without compiler warnings (see below).
|
||||||
|
- `master` must always pass all unit tests.
|
||||||
|
- `master` must always be able to launch successfully.
|
||||||
|
- `master` must always only contain working features.
|
||||||
|
- `master` should not contain excessive debug code.
|
||||||
|
- `master` must always have its code and filenames formatted (see below).
|
||||||
|
|
||||||
|
"Feature" branches are branches dedicated to the development of a single feature. When the feature reaches completion the branch is merged into `master` and removed. Intermediate merges into `master` may occur when some fitting milestone is reached. Intermediate merges from `master` may be done as necessary. Merges between "feature" branches should generally be avoided.
|
||||||
|
|
||||||
|
When beginning work on a new feature, create a new branch based on `master` (or on another "feature" branch if absolutely necessary). Use `all-small-with-dashes` to name the branch: `add-trees` or `rebalance-plastics` are good names. Do not fix unrelated bugs or work on unrelated features in a "feature" branch - create a new one, switch to an existing one or commit directly to `master` if the changes are small enough.
|
||||||
|
|
||||||
|
"Feature" branches may not be formatted properly. Formatting is required before merging into `master` or other branches.
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- Commits must leave the branch in a state that builds without compiler warnings (see below).
|
||||||
|
- Changes should be grouped in commits semantically. Avoid committing many small related changes in sequence; if necessary, wait and accumulate them. Avoid committing unrelated changes together; if necessary, split staged changes into several commits. This should normally result in about 1-2 commits for a day's work.
|
||||||
|
- Commit bulk changes (renaming, formatting, ...) separately. Don't ever commit whitespace changes outside formatting commits.
|
||||||
|
- Message format:
|
||||||
|
|
||||||
|
```
|
||||||
|
Short description of changes
|
||||||
|
<empty line>
|
||||||
|
- Enumeration of changes
|
||||||
|
- Nest when appropriate
|
||||||
|
- Use dashes only
|
||||||
|
- List not needed for small commits
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
Changed packages for relations, renamed Face to ShapePart
|
||||||
|
|
||||||
|
- Added BlockRelation as an abstract superclass to existing relations
|
||||||
|
- Must be given an absolute "up" direction before use
|
||||||
|
- Moved AbsFace, AbsRelation and BlockRelation to .world.rels
|
||||||
|
- Renamed Face to ShapePart to reduce confusion with AbsFace
|
||||||
|
```
|
||||||
|
|
||||||
|
- Only commit changes described in the commit message. Please double-check staged files before committing.
|
||||||
|
- Avoid merge conflicts. Pull before committing.
|
||||||
|
- Better sign commits than not. See: [git](https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work), [GitHub](https://docs.github.com/en/github/authenticating-to-github/managing-commit-signature-verification).
|
||||||
|
|
||||||
|
## Code
|
||||||
|
|
||||||
|
### Warnings
|
||||||
|
Make sure that all committed code contains no compiler warnings. This specifically includes unused imports, unused private members, missing `@Override`s and warnings related to generics.
|
||||||
|
|
||||||
|
Warnings about unknown tokens in `@SuppressWarnings` are temporarily ignored. Please disable them in your IDE.
|
||||||
|
|
||||||
|
### Code Style
|
||||||
|
Formatting code is important.
|
||||||
|
|
||||||
|
- The format is specified within the files inside `/templates_and_presets/eclipse_ide`. Import the specifications into Eclipse or IntelliJ IDEA and use the IDEs' format feature. Alternatively format the code manually in accordance with existing files.
|
||||||
|
- Only use tabs for indentation. Never indent with spaces even when wrapping lines. This is to ensure that indentation does not break when tab width is different.
|
||||||
|
- Don't use `I` prefix for interfaces (not `IDoable` but `Doable`).
|
||||||
|
- Prioritize readability over compactness. Do not hesitate to use (very) long identifiers if they aid comprehension.
|
||||||
|
- Document all mathematics unless it is trivial, especially when using math notation for variable names.
|
||||||
|
- Use proper English when writing comments. Avoid boxes in comments. Use `//` for single-line comments.
|
@ -40,6 +40,7 @@ public class ProgressiaLauncher {
|
|||||||
CrashReports.registerProvider(new OpenALContextProvider());
|
CrashReports.registerProvider(new OpenALContextProvider());
|
||||||
CrashReports.registerProvider(new ArgsContextProvider());
|
CrashReports.registerProvider(new ArgsContextProvider());
|
||||||
CrashReports.registerProvider(new LanguageContextProvider());
|
CrashReports.registerProvider(new LanguageContextProvider());
|
||||||
|
CrashReports.registerProvider(new ScreenContextProvider());
|
||||||
// Analyzers
|
// Analyzers
|
||||||
CrashReports.registerAnalyzer(new OutOfMemoryAnalyzer());
|
CrashReports.registerAnalyzer(new OutOfMemoryAnalyzer());
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ import ru.windcorp.progressia.common.resource.ResourceManager;
|
|||||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
import ru.windcorp.progressia.server.ServerState;
|
import ru.windcorp.progressia.server.ServerState;
|
||||||
import ru.windcorp.progressia.test.TestContent;
|
import ru.windcorp.progressia.test.TestContent;
|
||||||
|
import ru.windcorp.progressia.test.TestMusicPlayer;
|
||||||
|
|
||||||
public class ClientProxy implements Proxy {
|
public class ClientProxy implements Proxy {
|
||||||
|
|
||||||
@ -59,6 +60,8 @@ public class ClientProxy implements Proxy {
|
|||||||
|
|
||||||
ServerState.startServer();
|
ServerState.startServer();
|
||||||
ClientState.connectToLocalServer();
|
ClientState.connectToLocalServer();
|
||||||
|
|
||||||
|
TestMusicPlayer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import ru.windcorp.progressia.client.audio.backend.AudioReader;
|
|||||||
import ru.windcorp.progressia.client.audio.backend.Listener;
|
import ru.windcorp.progressia.client.audio.backend.Listener;
|
||||||
import ru.windcorp.progressia.client.audio.backend.SoundType;
|
import ru.windcorp.progressia.client.audio.backend.SoundType;
|
||||||
import ru.windcorp.progressia.client.audio.backend.Speaker;
|
import ru.windcorp.progressia.client.audio.backend.Speaker;
|
||||||
|
import ru.windcorp.progressia.common.resource.Resource;
|
||||||
|
|
||||||
import static org.lwjgl.openal.AL11.*;
|
import static org.lwjgl.openal.AL11.*;
|
||||||
import static org.lwjgl.openal.ALC10.*;
|
import static org.lwjgl.openal.ALC10.*;
|
||||||
@ -40,7 +41,6 @@ public class AudioManager {
|
|||||||
|
|
||||||
private static List<Speaker> soundSpeakers = new ArrayList<>(SOUNDS_NUM);
|
private static List<Speaker> soundSpeakers = new ArrayList<>(SOUNDS_NUM);
|
||||||
private static Speaker musicSpeaker;
|
private static Speaker musicSpeaker;
|
||||||
private static ArrayList<SoundType> soundsBuffer = new ArrayList<>();
|
|
||||||
|
|
||||||
public static void initAL() {
|
public static void initAL() {
|
||||||
String defaultDeviceName = alcGetString(
|
String defaultDeviceName = alcGetString(
|
||||||
@ -82,31 +82,19 @@ public class AudioManager {
|
|||||||
return speaker;
|
return speaker;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SoundType findSoundType(String soundID) throws Exception {
|
public static Speaker initSpeaker(SoundType st) {
|
||||||
for (SoundType s : soundsBuffer) {
|
|
||||||
if (s.getId().equals(soundID)) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Exception(
|
|
||||||
"ERROR: The selected sound is not loaded or" +
|
|
||||||
" not exists"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Speaker initSpeaker(String soundID) {
|
|
||||||
Speaker speaker = getLastSpeaker();
|
Speaker speaker = getLastSpeaker();
|
||||||
try {
|
try {
|
||||||
findSoundType(soundID).initSpeaker(speaker);
|
st.initSpeaker(speaker);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
return speaker;
|
return speaker;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Speaker initMusicSpeaker(String soundID) {
|
public static Speaker initMusicSpeaker(SoundType st) {
|
||||||
try {
|
try {
|
||||||
findSoundType(soundID).initSpeaker(musicSpeaker);
|
st.initSpeaker(musicSpeaker);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
@ -120,11 +108,11 @@ public class AudioManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void loadSound(String path, String id, AudioFormat format) {
|
public static void loadSound(Resource resource, String id, AudioFormat format) {
|
||||||
if (format == AudioFormat.MONO) {
|
if (format == AudioFormat.MONO) {
|
||||||
soundsBuffer.add(AudioReader.readAsMono(path, id));
|
AudioRegistry.getInstance().register(AudioReader.readAsMono(resource, id));
|
||||||
} else {
|
} else {
|
||||||
soundsBuffer.add(AudioReader.readAsStereo(path, id));
|
AudioRegistry.getInstance().register(AudioReader.readAsStereo(resource, id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* 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.audio;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.audio.backend.SoundType;
|
||||||
|
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
|
||||||
|
|
||||||
|
public class AudioRegistry extends NamespacedInstanceRegistry<SoundType> {
|
||||||
|
|
||||||
|
private static final AudioRegistry INSTANCE = new AudioRegistry();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the instance
|
||||||
|
*/
|
||||||
|
public static AudioRegistry getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.client.audio;
|
package ru.windcorp.progressia.client.audio;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||||
|
|
||||||
public class AudioSystem {
|
public class AudioSystem {
|
||||||
static public void initialize() {
|
static public void initialize() {
|
||||||
AudioManager.initAL();
|
AudioManager.initAL();
|
||||||
@ -28,7 +30,7 @@ public class AudioSystem {
|
|||||||
|
|
||||||
static void loadAudioData() {
|
static void loadAudioData() {
|
||||||
AudioManager.loadSound(
|
AudioManager.loadSound(
|
||||||
"assets/sounds/block_destroy_clap.ogg",
|
ResourceManager.getResource("assets/sounds/block_destroy_clap.ogg"),
|
||||||
"Progressia:BlockDestroy",
|
"Progressia:BlockDestroy",
|
||||||
AudioFormat.MONO
|
AudioFormat.MONO
|
||||||
);
|
);
|
||||||
|
@ -19,72 +19,37 @@
|
|||||||
package ru.windcorp.progressia.client.audio;
|
package ru.windcorp.progressia.client.audio;
|
||||||
|
|
||||||
import glm.vec._3.Vec3;
|
import glm.vec._3.Vec3;
|
||||||
|
import ru.windcorp.progressia.client.audio.backend.SoundType;
|
||||||
import ru.windcorp.progressia.client.audio.backend.Speaker;
|
import ru.windcorp.progressia.client.audio.backend.Speaker;
|
||||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
|
||||||
|
|
||||||
public class Music extends Namespaced {
|
public class Music
|
||||||
private Vec3 position = new Vec3();
|
extends Sound {
|
||||||
private Vec3 velocity = new Vec3();
|
|
||||||
private float pitch = 1.0f;
|
|
||||||
private float gain = 1.0f;
|
|
||||||
|
public Music(SoundType soundType, int timeLength, float pitch, float gain) {
|
||||||
|
super(soundType, timeLength, new Vec3(), new Vec3(), pitch, gain);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Music(SoundType soundType) {
|
||||||
|
super(soundType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Music(String id, int timeLength, float pitch, float gain) {
|
||||||
|
super(id, timeLength, new Vec3(), new Vec3(), pitch, gain);
|
||||||
|
}
|
||||||
|
|
||||||
public Music(String id) {
|
public Music(String id) {
|
||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Music(
|
@Override
|
||||||
String id,
|
protected Speaker initSpeaker() {
|
||||||
Vec3 position,
|
return AudioManager.initMusicSpeaker(soundType);
|
||||||
Vec3 velocity,
|
|
||||||
float pitch,
|
|
||||||
float gain
|
|
||||||
) {
|
|
||||||
this(id);
|
|
||||||
this.position = position;
|
|
||||||
this.velocity = velocity;
|
|
||||||
this.pitch = pitch;
|
|
||||||
this.gain = gain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void play(boolean loop) {
|
@Override
|
||||||
Speaker speaker = AudioManager.initMusicSpeaker(this.getId());
|
public void setPosition(Vec3 position) {
|
||||||
speaker.setGain(gain);
|
throw new UnsupportedOperationException();
|
||||||
speaker.setPitch(pitch);
|
|
||||||
speaker.setPosition(position);
|
|
||||||
speaker.setVelocity(velocity);
|
|
||||||
|
|
||||||
if (loop) {
|
|
||||||
speaker.playLoop();
|
|
||||||
} else {
|
|
||||||
speaker.play();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGain(float gain) {
|
|
||||||
this.gain = gain;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPitch(float pitch) {
|
|
||||||
this.pitch = pitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVelocity(Vec3 velocity) {
|
|
||||||
this.velocity = velocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vec3 getPosition() {
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getGain() {
|
|
||||||
return gain;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vec3 getVelocity() {
|
|
||||||
return velocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getPitch() {
|
|
||||||
return pitch;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,23 +19,30 @@
|
|||||||
package ru.windcorp.progressia.client.audio;
|
package ru.windcorp.progressia.client.audio;
|
||||||
|
|
||||||
import glm.vec._3.Vec3;
|
import glm.vec._3.Vec3;
|
||||||
|
import ru.windcorp.progressia.client.audio.backend.SoundType;
|
||||||
import ru.windcorp.progressia.client.audio.backend.Speaker;
|
import ru.windcorp.progressia.client.audio.backend.Speaker;
|
||||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
|
||||||
|
|
||||||
public class SoundEffect
|
public class Sound {
|
||||||
extends Namespaced {
|
|
||||||
|
|
||||||
private Vec3 position = new Vec3();
|
protected Vec3 position = new Vec3(0f, 0f, 0f);
|
||||||
private Vec3 velocity = new Vec3();
|
protected Vec3 velocity = new Vec3(0f, 0f, 0f);
|
||||||
private float pitch = 1.0f;
|
protected float pitch = 1.0f;
|
||||||
private float gain = 1.0f;
|
protected float gain = 1.0f;
|
||||||
|
protected int timeLength = 0;
|
||||||
public SoundEffect(String id) {
|
|
||||||
super(id);
|
protected SoundType soundType;
|
||||||
|
|
||||||
|
public Sound(SoundType soundType) {
|
||||||
|
this.soundType = soundType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SoundEffect(
|
public Sound(String id) {
|
||||||
|
this(AudioRegistry.getInstance().get(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sound(
|
||||||
String id,
|
String id,
|
||||||
|
int timeLength,
|
||||||
Vec3 position,
|
Vec3 position,
|
||||||
Vec3 velocity,
|
Vec3 velocity,
|
||||||
float pitch,
|
float pitch,
|
||||||
@ -47,9 +54,28 @@ public class SoundEffect
|
|||||||
this.pitch = pitch;
|
this.pitch = pitch;
|
||||||
this.gain = gain;
|
this.gain = gain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Sound(
|
||||||
|
SoundType soundType,
|
||||||
|
int timeLength,
|
||||||
|
Vec3 position,
|
||||||
|
Vec3 velocity,
|
||||||
|
float pitch,
|
||||||
|
float gain
|
||||||
|
) {
|
||||||
|
this(soundType);
|
||||||
|
this.position = position;
|
||||||
|
this.velocity = velocity;
|
||||||
|
this.pitch = pitch;
|
||||||
|
this.gain = gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Speaker initSpeaker() {
|
||||||
|
return AudioManager.initSpeaker(soundType);
|
||||||
|
}
|
||||||
|
|
||||||
public void play(boolean loop) {
|
public void play(boolean loop) {
|
||||||
Speaker speaker = AudioManager.initSpeaker(this.getId());
|
Speaker speaker = initSpeaker();
|
||||||
speaker.setGain(gain);
|
speaker.setGain(gain);
|
||||||
speaker.setPitch(pitch);
|
speaker.setPitch(pitch);
|
||||||
speaker.setPosition(position);
|
speaker.setPosition(position);
|
||||||
@ -93,4 +119,9 @@ public class SoundEffect
|
|||||||
public float getPitch() {
|
public float getPitch() {
|
||||||
return pitch;
|
return pitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getDuration() {
|
||||||
|
return soundType.getDuration();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -33,13 +33,11 @@ public class AudioReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO fix converting from mono-stereo
|
// TODO fix converting from mono-stereo
|
||||||
private static SoundType readAsSpecified(String path, String id, int format) {
|
private static SoundType readAsSpecified(Resource resource, String id, int format) {
|
||||||
IntBuffer channelBuffer = BufferUtils.createIntBuffer(1);
|
IntBuffer channelBuffer = BufferUtils.createIntBuffer(1);
|
||||||
IntBuffer rateBuffer = BufferUtils.createIntBuffer(1);
|
IntBuffer rateBuffer = BufferUtils.createIntBuffer(1);
|
||||||
|
|
||||||
Resource res = ResourceManager.getResource(path);
|
ShortBuffer rawAudio = decodeVorbis(resource, channelBuffer, rateBuffer);
|
||||||
|
|
||||||
ShortBuffer rawAudio = decodeVorbis(res, channelBuffer, rateBuffer);
|
|
||||||
|
|
||||||
return new SoundType(
|
return new SoundType(
|
||||||
id,
|
id,
|
||||||
@ -49,12 +47,12 @@ public class AudioReader {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SoundType readAsMono(String path, String id) {
|
public static SoundType readAsMono(Resource resource, String id) {
|
||||||
return readAsSpecified(path, id, AL_FORMAT_MONO16);
|
return readAsSpecified(resource, id, AL_FORMAT_MONO16);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SoundType readAsStereo(String path, String id) {
|
public static SoundType readAsStereo(Resource resource, String id) {
|
||||||
return readAsSpecified(path, id, AL_FORMAT_STEREO16);
|
return readAsSpecified(resource, id, AL_FORMAT_STEREO16);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ShortBuffer decodeVorbis(
|
private static ShortBuffer decodeVorbis(
|
||||||
|
@ -21,6 +21,9 @@ package ru.windcorp.progressia.client.audio.backend;
|
|||||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
|
|
||||||
import java.nio.ShortBuffer;
|
import java.nio.ShortBuffer;
|
||||||
|
|
||||||
|
import org.lwjgl.openal.AL10;
|
||||||
|
|
||||||
import static org.lwjgl.openal.AL11.*;
|
import static org.lwjgl.openal.AL11.*;
|
||||||
|
|
||||||
public class SoundType extends Namespaced {
|
public class SoundType extends Namespaced {
|
||||||
@ -29,6 +32,7 @@ public class SoundType extends Namespaced {
|
|||||||
private int sampleRate;
|
private int sampleRate;
|
||||||
private int format;
|
private int format;
|
||||||
private int audioBuffer;
|
private int audioBuffer;
|
||||||
|
private double duration;
|
||||||
|
|
||||||
public SoundType(
|
public SoundType(
|
||||||
String id,
|
String id,
|
||||||
@ -46,9 +50,14 @@ public class SoundType extends Namespaced {
|
|||||||
private void createAudioBuffer() {
|
private void createAudioBuffer() {
|
||||||
this.audioBuffer = alGenBuffers();
|
this.audioBuffer = alGenBuffers();
|
||||||
alBufferData(audioBuffer, format, rawAudio, sampleRate);
|
alBufferData(audioBuffer, format, rawAudio, sampleRate);
|
||||||
|
duration = rawAudio.limit() / (double) sampleRate / (format == AL10.AL_FORMAT_STEREO16 ? 2 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initSpeaker(Speaker speaker) {
|
public void initSpeaker(Speaker speaker) {
|
||||||
speaker.setAudioData(audioBuffer);
|
speaker.setAudioData(audioBuffer);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public double getDuration() {
|
||||||
|
return duration;
|
||||||
|
}
|
||||||
|
}
|
@ -120,6 +120,7 @@ public class Speaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setAudioData(int audioData) {
|
public void setAudioData(int audioData) {
|
||||||
|
stop();
|
||||||
this.audioData = audioData;
|
this.audioData = audioData;
|
||||||
alSourcei(this.sourceData, AL_BUFFER, audioData);
|
alSourcei(this.sourceData, AL_BUFFER, audioData);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 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.comms.controls;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
|
import ru.windcorp.progressia.common.comms.controls.PacketControl;
|
||||||
|
|
||||||
|
public class ControlTriggerLocalLambda extends ControlTriggerInputBased {
|
||||||
|
|
||||||
|
private final Predicate<InputEvent> predicate;
|
||||||
|
private final Consumer<InputEvent> action;
|
||||||
|
|
||||||
|
public ControlTriggerLocalLambda(
|
||||||
|
String id,
|
||||||
|
Predicate<InputEvent> predicate,
|
||||||
|
Consumer<InputEvent> action
|
||||||
|
) {
|
||||||
|
super(id);
|
||||||
|
|
||||||
|
this.predicate = predicate;
|
||||||
|
this.action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PacketControl onInputEvent(InputEvent event) {
|
||||||
|
if (!predicate.test(event))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
action.accept(event);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -142,6 +142,96 @@ public class ControlTriggers {
|
|||||||
predicates
|
predicates
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
///
|
||||||
|
///
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
public static ControlTriggerInputBased localOf(
|
||||||
|
String id,
|
||||||
|
Consumer<InputEvent> action,
|
||||||
|
Predicate<InputEvent> predicate
|
||||||
|
) {
|
||||||
|
return new ControlTriggerLocalLambda(id, predicate, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ControlTriggerInputBased localOf(
|
||||||
|
String id,
|
||||||
|
Runnable action,
|
||||||
|
Predicate<InputEvent> predicate
|
||||||
|
) {
|
||||||
|
return localOf(
|
||||||
|
id,
|
||||||
|
input -> action.run(),
|
||||||
|
predicate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <I extends InputEvent> ControlTriggerInputBased localOf(
|
||||||
|
String id,
|
||||||
|
Class<I> inputType,
|
||||||
|
Consumer<I> action,
|
||||||
|
Predicate<I>... predicates
|
||||||
|
) {
|
||||||
|
return localOf(
|
||||||
|
id,
|
||||||
|
createCheckedAction(inputType, action),
|
||||||
|
createCheckedCompoundPredicate(inputType, predicates)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <I extends InputEvent> ControlTriggerInputBased localOf(
|
||||||
|
String id,
|
||||||
|
Class<I> inputType,
|
||||||
|
Runnable action,
|
||||||
|
Predicate<I>... predicates
|
||||||
|
) {
|
||||||
|
return localOf(
|
||||||
|
id,
|
||||||
|
inputType,
|
||||||
|
input -> action.run(),
|
||||||
|
predicates
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static ControlTriggerInputBased localOf(
|
||||||
|
String id,
|
||||||
|
Consumer<InputEvent> action,
|
||||||
|
Predicate<InputEvent>... predicates
|
||||||
|
) {
|
||||||
|
return localOf(
|
||||||
|
id,
|
||||||
|
InputEvent.class,
|
||||||
|
action,
|
||||||
|
predicates
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <I extends InputEvent> ControlTriggerInputBased localOf(
|
||||||
|
String id,
|
||||||
|
Runnable action,
|
||||||
|
Predicate<InputEvent>... predicates
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
input -> action.run(),
|
||||||
|
predicates
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private static <I extends InputEvent> BiConsumer<InputEvent, ControlData> createCheckedDataWriter(
|
private static <I extends InputEvent> BiConsumer<InputEvent, ControlData> createCheckedDataWriter(
|
||||||
Class<I> inputType,
|
Class<I> inputType,
|
||||||
@ -149,6 +239,13 @@ public class ControlTriggers {
|
|||||||
) {
|
) {
|
||||||
return (inputEvent, control) -> dataWriter.accept(inputType.cast(inputEvent), control);
|
return (inputEvent, control) -> dataWriter.accept(inputType.cast(inputEvent), control);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <I extends InputEvent> Consumer<InputEvent> createCheckedAction(
|
||||||
|
Class<I> inputType,
|
||||||
|
Consumer<I> action
|
||||||
|
) {
|
||||||
|
return inputEvent -> action.accept(inputType.cast(inputEvent));
|
||||||
|
}
|
||||||
|
|
||||||
private static <I extends InputEvent> Predicate<InputEvent> createCheckedCompoundPredicate(
|
private static <I extends InputEvent> Predicate<InputEvent> createCheckedCompoundPredicate(
|
||||||
Class<I> inputType,
|
Class<I> inputType,
|
||||||
|
@ -34,7 +34,13 @@ public class Colors {
|
|||||||
DEBUG_BLUE = toVector(0xFF0000FF),
|
DEBUG_BLUE = toVector(0xFF0000FF),
|
||||||
DEBUG_CYAN = toVector(0xFF00FFFF),
|
DEBUG_CYAN = toVector(0xFF00FFFF),
|
||||||
DEBUG_MAGENTA = toVector(0xFFFF00FF),
|
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) {
|
public static Vec4 toVector(int argb) {
|
||||||
return toVector(argb, new Vec4());
|
return toVector(argb, new Vec4());
|
||||||
|
@ -24,6 +24,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
|
||||||
import ru.windcorp.progressia.client.graphics.input.CursorEvent;
|
import ru.windcorp.progressia.client.graphics.input.CursorEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.FrameResizeEvent;
|
import ru.windcorp.progressia.client.graphics.input.FrameResizeEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
@ -57,15 +58,24 @@ public class GUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void addBottomLayer(Layer layer) {
|
public static void addBottomLayer(Layer layer) {
|
||||||
modify(layers -> layers.add(layer));
|
modify(layers -> {
|
||||||
|
layers.add(layer);
|
||||||
|
layer.onAdded();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addTopLayer(Layer layer) {
|
public static void addTopLayer(Layer layer) {
|
||||||
modify(layers -> layers.add(0, layer));
|
modify(layers -> {
|
||||||
|
layers.add(0, layer);
|
||||||
|
layer.onAdded();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void removeLayer(Layer layer) {
|
public static void removeLayer(Layer layer) {
|
||||||
modify(layers -> layers.remove(layer));
|
modify(layers -> {
|
||||||
|
layers.remove(layer);
|
||||||
|
layer.onRemoved();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void modify(LayerStackModification mod) {
|
private static void modify(LayerStackModification mod) {
|
||||||
@ -78,12 +88,33 @@ public class GUI {
|
|||||||
|
|
||||||
public static void render() {
|
public static void render() {
|
||||||
synchronized (LAYERS) {
|
synchronized (LAYERS) {
|
||||||
MODIFICATION_QUEUE.forEach(action -> action.affect(LAYERS));
|
|
||||||
MODIFICATION_QUEUE.clear();
|
if (!MODIFICATION_QUEUE.isEmpty()) {
|
||||||
|
MODIFICATION_QUEUE.forEach(action -> action.affect(LAYERS));
|
||||||
|
MODIFICATION_QUEUE.clear();
|
||||||
|
|
||||||
|
boolean isMouseCurrentlyCaptured = GraphicsInterface.isMouseCaptured();
|
||||||
|
Layer.CursorPolicy policy = Layer.CursorPolicy.REQUIRE;
|
||||||
|
|
||||||
|
for (Layer layer : LAYERS) {
|
||||||
|
Layer.CursorPolicy currentPolicy = layer.getCursorPolicy();
|
||||||
|
|
||||||
|
if (currentPolicy != Layer.CursorPolicy.INDIFFERENT) {
|
||||||
|
policy = currentPolicy;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean shouldCaptureMouse = (policy == Layer.CursorPolicy.FORBID);
|
||||||
|
if (shouldCaptureMouse != isMouseCurrentlyCaptured) {
|
||||||
|
GraphicsInterface.setMouseCaptured(shouldCaptureMouse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = LAYERS.size() - 1; i >= 0; --i) {
|
for (int i = LAYERS.size() - 1; i >= 0; --i) {
|
||||||
LAYERS.get(i).render();
|
LAYERS.get(i).render();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,15 +30,52 @@ public abstract class Layer {
|
|||||||
private boolean hasInitialized = false;
|
private boolean hasInitialized = false;
|
||||||
|
|
||||||
private final AtomicBoolean isValid = new AtomicBoolean(false);
|
private final AtomicBoolean isValid = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents various requests that a {@link Layer} can make regarding the
|
||||||
|
* presence of a visible cursor. The value of the highest layer that is not
|
||||||
|
* {@link #INDIFFERENT} is used.
|
||||||
|
*/
|
||||||
|
public static enum CursorPolicy {
|
||||||
|
/**
|
||||||
|
* Require that a cursor is visible.
|
||||||
|
*/
|
||||||
|
REQUIRE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link Layer} should not affect the presence or absence of a
|
||||||
|
* visible cursor; lower layers should be consulted.
|
||||||
|
*/
|
||||||
|
INDIFFERENT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forbid a visible cursor.
|
||||||
|
*/
|
||||||
|
FORBID
|
||||||
|
}
|
||||||
|
|
||||||
|
private CursorPolicy cursorPolicy = CursorPolicy.INDIFFERENT;
|
||||||
|
|
||||||
public Layer(String name) {
|
public Layer(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Layer " + name;
|
return "Layer " + name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CursorPolicy getCursorPolicy() {
|
||||||
|
return cursorPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCursorPolicy(CursorPolicy cursorPolicy) {
|
||||||
|
this.cursorPolicy = cursorPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
void render() {
|
void render() {
|
||||||
GraphicsInterface.startNextLayer();
|
GraphicsInterface.startNextLayer();
|
||||||
@ -78,5 +115,13 @@ public abstract class Layer {
|
|||||||
protected int getHeight() {
|
protected int getHeight() {
|
||||||
return GraphicsInterface.getFrameHeight();
|
return GraphicsInterface.getFrameHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void onAdded() {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onRemoved() {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,11 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.client.graphics.backend;
|
package ru.windcorp.progressia.client.graphics.backend;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL11.*;
|
|
||||||
|
|
||||||
import glm.vec._2.i.Vec2i;
|
import glm.vec._2.i.Vec2i;
|
||||||
|
import org.lwjgl.glfw.GLFWVidMode;
|
||||||
|
|
||||||
import static org.lwjgl.glfw.GLFW.*;
|
import static org.lwjgl.glfw.GLFW.*;
|
||||||
|
import static org.lwjgl.opengl.GL11.*;
|
||||||
|
|
||||||
public class GraphicsBackend {
|
public class GraphicsBackend {
|
||||||
|
|
||||||
@ -38,9 +38,30 @@ public class GraphicsBackend {
|
|||||||
|
|
||||||
private static boolean faceCullingEnabled = false;
|
private static boolean faceCullingEnabled = false;
|
||||||
|
|
||||||
|
private static boolean isFullscreen = false;
|
||||||
|
private static boolean vSyncEnabled = false;
|
||||||
|
private static boolean isGLFWInitialized = false;
|
||||||
|
private static boolean isOpenGLInitialized = false;
|
||||||
|
|
||||||
private GraphicsBackend() {
|
private GraphicsBackend() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isGLFWInitialized() {
|
||||||
|
return isGLFWInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setGLFWInitialized(boolean isGLFWInitialized) {
|
||||||
|
GraphicsBackend.isGLFWInitialized = isGLFWInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isOpenGLInitialized() {
|
||||||
|
return isOpenGLInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setOpenGLInitialized(boolean isOpenGLInitialized) {
|
||||||
|
GraphicsBackend.isOpenGLInitialized = isOpenGLInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
public static void initialize() {
|
public static void initialize() {
|
||||||
startRenderThread();
|
startRenderThread();
|
||||||
}
|
}
|
||||||
@ -128,4 +149,61 @@ public class GraphicsBackend {
|
|||||||
faceCullingEnabled = useFaceCulling;
|
faceCullingEnabled = useFaceCulling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isFullscreen() {
|
||||||
|
return isFullscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isVSyncEnabled() {
|
||||||
|
return vSyncEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setFullscreen() {
|
||||||
|
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||||
|
glfwSetWindowMonitor(
|
||||||
|
getWindowHandle(),
|
||||||
|
glfwGetPrimaryMonitor(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
vidmode.width(),
|
||||||
|
vidmode.height(),
|
||||||
|
0);
|
||||||
|
isFullscreen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setWindowed() {
|
||||||
|
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||||
|
glfwSetWindowMonitor(
|
||||||
|
getWindowHandle(),
|
||||||
|
0,
|
||||||
|
(vidmode.width() - getFrameWidth()) / 2,
|
||||||
|
(vidmode.height() - getFrameHeight()) / 2,
|
||||||
|
getFrameWidth(),
|
||||||
|
getFrameHeight(),
|
||||||
|
0);
|
||||||
|
isFullscreen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setVSyncEnabled(boolean enable) {
|
||||||
|
glfwSwapInterval(enable ? 1 : 0);
|
||||||
|
vSyncEnabled = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getRefreshRate() {
|
||||||
|
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||||
|
return vidmode.refreshRate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMouseCaptured() {
|
||||||
|
return glfwGetInputMode(windowHandle, GLFW_CURSOR) == GLFW_CURSOR_DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setMouseCaptured(boolean capture) {
|
||||||
|
int mode = capture ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL;
|
||||||
|
glfwSetInputMode(windowHandle, GLFW_CURSOR, mode);
|
||||||
|
|
||||||
|
if (!capture) {
|
||||||
|
glfwSetCursorPos(windowHandle, FRAME_SIZE.x / 2.0, FRAME_SIZE.y / 2.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -73,4 +73,21 @@ public class GraphicsInterface {
|
|||||||
GraphicsBackend.startNextLayer();
|
GraphicsBackend.startNextLayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void makeFullscreen(boolean state) {
|
||||||
|
if (state) {
|
||||||
|
GraphicsBackend.setFullscreen();
|
||||||
|
} else {
|
||||||
|
GraphicsBackend.setWindowed();
|
||||||
|
}
|
||||||
|
GraphicsBackend.setVSyncEnabled(GraphicsBackend.isVSyncEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMouseCaptured() {
|
||||||
|
return GraphicsBackend.isMouseCaptured();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setMouseCaptured(boolean capture) {
|
||||||
|
GraphicsBackend.setMouseCaptured(capture);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ class LWJGLInitializer {
|
|||||||
private static void initializeGLFW() {
|
private static void initializeGLFW() {
|
||||||
// TODO Do GLFW error handling: check glfwInit, setup error callback
|
// TODO Do GLFW error handling: check glfwInit, setup error callback
|
||||||
glfwInit();
|
glfwInit();
|
||||||
|
GraphicsBackend.setGLFWInitialized(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void createWindow() {
|
private static void createWindow() {
|
||||||
@ -64,10 +65,8 @@ class LWJGLInitializer {
|
|||||||
|
|
||||||
GraphicsBackend.setWindowHandle(handle);
|
GraphicsBackend.setWindowHandle(handle);
|
||||||
|
|
||||||
glfwSetInputMode(handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
|
||||||
|
|
||||||
glfwMakeContextCurrent(handle);
|
glfwMakeContextCurrent(handle);
|
||||||
glfwSwapInterval(0);
|
glfwSwapInterval(0); // TODO: remove after config system is added
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void positionWindow() {
|
private static void positionWindow() {
|
||||||
@ -87,6 +86,7 @@ class LWJGLInitializer {
|
|||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
RenderTaskQueue.schedule(OpenGLObjectTracker::deleteEnqueuedObjects);
|
RenderTaskQueue.schedule(OpenGLObjectTracker::deleteEnqueuedObjects);
|
||||||
|
GraphicsBackend.setOpenGLInitialized(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setupWindowCallbacks() {
|
private static void setupWindowCallbacks() {
|
||||||
|
@ -189,13 +189,10 @@ public class RenderTarget {
|
|||||||
|
|
||||||
public void addCustomRenderer(Renderable renderable) {
|
public void addCustomRenderer(Renderable renderable) {
|
||||||
assembleCurrentClipFromFaces();
|
assembleCurrentClipFromFaces();
|
||||||
assembled.add(
|
|
||||||
new Clip(
|
float depth = this.depth--;
|
||||||
maskStack,
|
Mat4 transform = new Mat4().translate(0, 0, depth).mul(getTransform());
|
||||||
getTransform(),
|
assembled.add(new Clip(maskStack, transform, renderable));
|
||||||
renderable
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addFaceToCurrentClip(ShapePart face) {
|
protected void addFaceToCurrentClip(ShapePart face) {
|
||||||
|
@ -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.gui;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.font.Font;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.event.ButtonEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.event.EnableEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.event.FocusEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
|
|
||||||
|
public abstract class BasicButton extends Component {
|
||||||
|
|
||||||
|
private final Label label;
|
||||||
|
|
||||||
|
private boolean isPressed = false;
|
||||||
|
private final Collection<Consumer<BasicButton>> actions = Collections.synchronizedCollection(new ArrayList<>());
|
||||||
|
|
||||||
|
public BasicButton(String name, String label, Font labelFont) {
|
||||||
|
super(name);
|
||||||
|
this.label = new Label(name + ".Label", labelFont, label);
|
||||||
|
|
||||||
|
setLayout(new LayoutAlign(10));
|
||||||
|
addChild(this.label);
|
||||||
|
|
||||||
|
setFocusable(true);
|
||||||
|
reassembleAt(ARTrigger.HOVER, ARTrigger.FOCUS, ARTrigger.ENABLE);
|
||||||
|
|
||||||
|
// Click triggers
|
||||||
|
addListener(KeyEvent.class, e -> {
|
||||||
|
if (e.isRepeat()) {
|
||||||
|
return false;
|
||||||
|
} else if (
|
||||||
|
e.isLeftMouseButton() ||
|
||||||
|
e.getKey() == GLFW.GLFW_KEY_SPACE ||
|
||||||
|
e.getKey() == GLFW.GLFW_KEY_ENTER
|
||||||
|
) {
|
||||||
|
setPressed(e.isPress());
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addListener(new Object() {
|
||||||
|
|
||||||
|
// Release when losing focus
|
||||||
|
@Subscribe
|
||||||
|
public void onFocusChange(FocusEvent e) {
|
||||||
|
if (!e.getNewState()) {
|
||||||
|
setPressed(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release when hover ends
|
||||||
|
@Subscribe
|
||||||
|
public void onHoverEnded(HoverEvent e) {
|
||||||
|
if (!e.isNowHovered()) {
|
||||||
|
setPressed(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release when disabled
|
||||||
|
@Subscribe
|
||||||
|
public void onDisabled(EnableEvent e) {
|
||||||
|
if (!e.getComponent().isEnabled()) {
|
||||||
|
setPressed(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger virtualClick when button is released
|
||||||
|
@Subscribe
|
||||||
|
public void onRelease(ButtonEvent.Release e) {
|
||||||
|
virtualClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public BasicButton(String name, String label) {
|
||||||
|
this(name, label, new Font());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPressed() {
|
||||||
|
return isPressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void click() {
|
||||||
|
setPressed(true);
|
||||||
|
setPressed(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPressed(boolean isPressed) {
|
||||||
|
if (this.isPressed != isPressed) {
|
||||||
|
this.isPressed = isPressed;
|
||||||
|
|
||||||
|
if (isPressed) {
|
||||||
|
takeFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchEvent(ButtonEvent.create(this, this.isPressed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BasicButton addAction(Consumer<BasicButton> action) {
|
||||||
|
this.actions.add(Objects.requireNonNull(action, "action"));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean removeAction(Consumer<BasicButton> action) {
|
||||||
|
return this.actions.remove(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void virtualClick() {
|
||||||
|
this.actions.forEach(action -> {
|
||||||
|
action.accept(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Label getLabel() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* 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 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 Button(String name, String label, Font labelFont) {
|
||||||
|
super(name, label, labelFont);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Button(String name, String label) {
|
||||||
|
this(name, label, new Font());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assembleSelf(RenderTarget target) {
|
||||||
|
// Border
|
||||||
|
|
||||||
|
Vec4 borderColor;
|
||||||
|
if (isPressed() || isHovered() || isFocused()) {
|
||||||
|
borderColor = Colors.BLUE;
|
||||||
|
} else {
|
||||||
|
borderColor = Colors.LIGHT_GRAY;
|
||||||
|
}
|
||||||
|
target.fill(getX(), getY(), getWidth(), getHeight(), borderColor);
|
||||||
|
|
||||||
|
// Inside area
|
||||||
|
|
||||||
|
if (isPressed()) {
|
||||||
|
// Do nothing
|
||||||
|
} else {
|
||||||
|
Vec4 backgroundColor;
|
||||||
|
if (isHovered() && isEnabled()) {
|
||||||
|
backgroundColor = Colors.HOVER_BLUE;
|
||||||
|
} else {
|
||||||
|
backgroundColor = Colors.WHITE;
|
||||||
|
}
|
||||||
|
target.fill(getX() + 2, getY() + 2, getWidth() - 4, getHeight() - 4, backgroundColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change label font color
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* 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 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;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutHorizontal;
|
||||||
|
|
||||||
|
public class Checkbox extends BasicButton {
|
||||||
|
|
||||||
|
private class Tick extends Component {
|
||||||
|
|
||||||
|
public Tick() {
|
||||||
|
super(Checkbox.this.getName() + ".Tick");
|
||||||
|
|
||||||
|
setPreferredSize(new Vec2i(Typefaces.getDefault().getLineHeight() * 3 / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assembleSelf(RenderTarget target) {
|
||||||
|
|
||||||
|
int size = getPreferredSize().x;
|
||||||
|
int x = getX();
|
||||||
|
int y = getY() + (getHeight() - size) / 2;
|
||||||
|
|
||||||
|
// Border
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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, Colors.BLUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checked;
|
||||||
|
|
||||||
|
public Checkbox(String name, String label, Font labelFont, boolean check) {
|
||||||
|
super(name, label, labelFont);
|
||||||
|
this.checked = check;
|
||||||
|
|
||||||
|
assert getChildren().size() == 1 : "Checkbox expects that BasicButton contains exactly one child";
|
||||||
|
Component basicChild = getChild(0);
|
||||||
|
|
||||||
|
Group group = new Group(getName() + ".LabelAndTick", new LayoutHorizontal(0, 10));
|
||||||
|
removeChild(basicChild);
|
||||||
|
setLayout(new LayoutAlign(0, 0.5f, 10));
|
||||||
|
group.setLayoutHint(basicChild.getLayoutHint());
|
||||||
|
group.addChild(new Tick());
|
||||||
|
group.addChild(basicChild);
|
||||||
|
addChild(group);
|
||||||
|
|
||||||
|
addAction(b -> switchState());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Checkbox(String name, String label, Font labelFont) {
|
||||||
|
this(name, label, labelFont, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Checkbox(String name, String label, boolean check) {
|
||||||
|
this(name, label, new Font(), check);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Checkbox(String name, String label) {
|
||||||
|
this(name, label, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void switchState() {
|
||||||
|
setChecked(!isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the checked
|
||||||
|
*/
|
||||||
|
public boolean isChecked() {
|
||||||
|
return checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param checked the checked to set
|
||||||
|
*/
|
||||||
|
public void setChecked(boolean checked) {
|
||||||
|
this.checked = checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assembleSelf(RenderTarget target) {
|
||||||
|
// Change label font color
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -19,18 +19,23 @@
|
|||||||
package ru.windcorp.progressia.client.graphics.gui;
|
package ru.windcorp.progressia.client.graphics.gui;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.EnumMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
import com.google.common.eventbus.EventBus;
|
import com.google.common.eventbus.EventBus;
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
|
||||||
import glm.vec._2.i.Vec2i;
|
import glm.vec._2.i.Vec2i;
|
||||||
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
|
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
|
||||||
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
|
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.event.ChildAddedEvent;
|
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.ChildRemovedEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.event.EnableEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.event.FocusEvent;
|
import ru.windcorp.progressia.client.graphics.gui.event.FocusEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent;
|
import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.event.ParentChangedEvent;
|
import ru.windcorp.progressia.client.graphics.gui.event.ParentChangedEvent;
|
||||||
@ -61,6 +66,8 @@ public class Component extends Named {
|
|||||||
|
|
||||||
private Object layoutHint = null;
|
private Object layoutHint = null;
|
||||||
private Layout layout = null;
|
private Layout layout = null;
|
||||||
|
|
||||||
|
private boolean isEnabled = true;
|
||||||
|
|
||||||
private boolean isFocusable = false;
|
private boolean isFocusable = false;
|
||||||
private boolean isFocused = false;
|
private boolean isFocused = false;
|
||||||
@ -285,9 +292,30 @@ public class Component extends Named {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this component is focusable. A component needs to be
|
||||||
|
* focusable to become focused. A component that is focusable may not
|
||||||
|
* necessarily be ready to gain focus (see {@link #canGainFocusNow()}).
|
||||||
|
*
|
||||||
|
* @return {@code true} iff the component is focusable
|
||||||
|
* @see #canGainFocusNow()
|
||||||
|
*/
|
||||||
public boolean isFocusable() {
|
public boolean isFocusable() {
|
||||||
return isFocusable;
|
return isFocusable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this component can become focused at this moment.
|
||||||
|
* <p>
|
||||||
|
* The implementation of this method in {@link Component} considers the
|
||||||
|
* component a focus candidate if it is both focusable and enabled.
|
||||||
|
*
|
||||||
|
* @return {@code true} iff the component can receive focus
|
||||||
|
* @see #isFocusable()
|
||||||
|
*/
|
||||||
|
public boolean canGainFocusNow() {
|
||||||
|
return isFocusable() && isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
public Component setFocusable(boolean focusable) {
|
public Component setFocusable(boolean focusable) {
|
||||||
this.isFocusable = focusable;
|
this.isFocusable = focusable;
|
||||||
@ -337,7 +365,7 @@ public class Component extends Named {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component.isFocusable()) {
|
if (component.canGainFocusNow()) {
|
||||||
setFocused(false);
|
setFocused(false);
|
||||||
component.setFocused(true);
|
component.setFocused(true);
|
||||||
return;
|
return;
|
||||||
@ -379,7 +407,7 @@ public class Component extends Named {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component.isFocusable()) {
|
if (component.canGainFocusNow()) {
|
||||||
setFocused(false);
|
setFocused(false);
|
||||||
component.setFocused(true);
|
component.setFocused(true);
|
||||||
return;
|
return;
|
||||||
@ -432,13 +460,52 @@ public class Component extends Named {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables or disables this component. An {@link EnableEvent} is dispatched
|
||||||
|
* if the state changes.
|
||||||
|
*
|
||||||
|
* @param enabled {@code true} to enable the component, {@code false} to
|
||||||
|
* disable the component
|
||||||
|
* @see #setEnabledRecursively(boolean)
|
||||||
|
*/
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
if (this.isEnabled != enabled) {
|
||||||
|
if (isFocused() && isEnabled()) {
|
||||||
|
focusNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEnabled()) {
|
||||||
|
setHovered(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isEnabled = enabled;
|
||||||
|
dispatchEvent(new EnableEvent(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables or disables this component and all of its children recursively.
|
||||||
|
*
|
||||||
|
* @param enabled {@code true} to enable the components, {@code false} to
|
||||||
|
* disable the components
|
||||||
|
* @see #setEnabled(boolean)
|
||||||
|
*/
|
||||||
|
public void setEnabledRecursively(boolean enabled) {
|
||||||
|
setEnabled(enabled);
|
||||||
|
getChildren().forEach(c -> c.setEnabledRecursively(enabled));
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isHovered() {
|
public boolean isHovered() {
|
||||||
return isHovered;
|
return isHovered;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setHovered(boolean isHovered) {
|
protected void setHovered(boolean isHovered) {
|
||||||
if (this.isHovered != isHovered) {
|
if (this.isHovered != isHovered && isEnabled()) {
|
||||||
this.isHovered = isHovered;
|
this.isHovered = isHovered;
|
||||||
|
|
||||||
if (!isHovered && !getChildren().isEmpty()) {
|
if (!isHovered && !getChildren().isEmpty()) {
|
||||||
@ -502,7 +569,7 @@ public class Component extends Named {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void handleInput(Input input) {
|
protected void handleInput(Input input) {
|
||||||
if (inputBus != null) {
|
if (inputBus != null && isEnabled()) {
|
||||||
inputBus.dispatch(input);
|
inputBus.dispatch(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -598,6 +665,17 @@ public class Component extends Named {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules the reassembly to occur.
|
||||||
|
* <p>
|
||||||
|
* This method is invoked in root components whenever a
|
||||||
|
* {@linkplain #requestReassembly() reassembly request} is made by one of
|
||||||
|
* its children. When creating the dedicated root component, override this
|
||||||
|
* method to perform any implementation-specific actions that will cause a
|
||||||
|
* reassembly as soon as possible.
|
||||||
|
* <p>
|
||||||
|
* The default implementation of this method does nothing.
|
||||||
|
*/
|
||||||
protected void handleReassemblyRequest() {
|
protected void handleReassemblyRequest() {
|
||||||
// To be overridden
|
// To be overridden
|
||||||
}
|
}
|
||||||
@ -637,6 +715,135 @@ public class Component extends Named {
|
|||||||
protected void assembleChildren(RenderTarget target) {
|
protected void assembleChildren(RenderTarget target) {
|
||||||
getChildren().forEach(child -> child.assemble(target));
|
getChildren().forEach(child -> child.assemble(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Automatic Reassembly
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The various kinds of changes that may be used with
|
||||||
|
* {@link Component#reassembleAt(ARTrigger...)}.
|
||||||
|
*/
|
||||||
|
protected static enum ARTrigger {
|
||||||
|
/**
|
||||||
|
* Reassemble the component whenever its hover status changes, e.g.
|
||||||
|
* whenever the pointer enters or leaves its bounds.
|
||||||
|
*/
|
||||||
|
HOVER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reassemble the component whenever it gains or loses focus.
|
||||||
|
* <p>
|
||||||
|
* <em>Component must be focusable to be able to gain focus.</em> The
|
||||||
|
* component will not be reassembled unless
|
||||||
|
* {@link Component#setFocusable(boolean) setFocusable(true)} has been
|
||||||
|
* invoked.
|
||||||
|
*/
|
||||||
|
FOCUS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reassemble the component whenever it is enabled or disabled.
|
||||||
|
*/
|
||||||
|
ENABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All trigger objects (event listeners) that are currently registered with
|
||||||
|
* {@link #eventBus}. The field is {@code null} until the first trigger is
|
||||||
|
* installed.
|
||||||
|
*/
|
||||||
|
private Map<ARTrigger, Object> autoReassemblyTriggerObjects = null;
|
||||||
|
|
||||||
|
private Object createTriggerObject(ARTrigger type) {
|
||||||
|
switch (type) {
|
||||||
|
case HOVER:
|
||||||
|
return new Object() {
|
||||||
|
@Subscribe
|
||||||
|
public void onHoverChanged(HoverEvent e) {
|
||||||
|
requestReassembly();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
case FOCUS:
|
||||||
|
return new Object() {
|
||||||
|
@Subscribe
|
||||||
|
public void onFocusChanged(FocusEvent e) {
|
||||||
|
requestReassembly();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
case ENABLE:
|
||||||
|
return new Object() {
|
||||||
|
@Subscribe
|
||||||
|
public void onEnabled(EnableEvent e) {
|
||||||
|
requestReassembly();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
throw new NullPointerException("type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests that {@link #requestReassembly()} is invoked on this component
|
||||||
|
* whenever any of the specified changes occur. Duplicate attempts to
|
||||||
|
* register the same trigger are silently ignored.
|
||||||
|
* <p>
|
||||||
|
* {@code triggers} may be empty, which results in a no-op. It must not be
|
||||||
|
* {@code null}.
|
||||||
|
*
|
||||||
|
* @param triggers the {@linkplain ARTrigger triggers} to
|
||||||
|
* request reassembly with.
|
||||||
|
* @see #disableAutoReassemblyAt(ARTrigger...)
|
||||||
|
*/
|
||||||
|
protected synchronized void reassembleAt(ARTrigger... triggers) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(triggers, "triggers");
|
||||||
|
if (triggers.length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (autoReassemblyTriggerObjects == null) {
|
||||||
|
autoReassemblyTriggerObjects = new EnumMap<>(ARTrigger.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ARTrigger trigger : triggers) {
|
||||||
|
if (!autoReassemblyTriggerObjects.containsKey(trigger)) {
|
||||||
|
Object triggerObject = createTriggerObject(trigger);
|
||||||
|
addListener(trigger);
|
||||||
|
autoReassemblyTriggerObjects.put(trigger, triggerObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests that {@link #requestReassembly()} is no longer invoked on this
|
||||||
|
* component whenever any of the specified changes occur. After a trigger is
|
||||||
|
* removed, it may be reinstalled with
|
||||||
|
* {@link #reassembleAt(ARTrigger...)}. Attempts to remove a
|
||||||
|
* nonexistant trigger are silently ignored.
|
||||||
|
* <p>
|
||||||
|
* {@code triggers} may be empty, which results in a no-op. It must not be
|
||||||
|
* {@code null}.
|
||||||
|
*
|
||||||
|
* @param triggers the {@linkplain ARTrigger triggers} to remove
|
||||||
|
* @see #reassemblyAt(ARTrigger...)
|
||||||
|
*/
|
||||||
|
protected synchronized void disableAutoReassemblyAt(ARTrigger... triggers) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(triggers, "triggers");
|
||||||
|
if (triggers.length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (autoReassemblyTriggerObjects == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (ARTrigger trigger : triggers) {
|
||||||
|
Object triggerObject = autoReassemblyTriggerObjects.remove(trigger);
|
||||||
|
if (triggerObject != null) {
|
||||||
|
removeListener(trigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * Returns a component that displays this component in its center.
|
// * Returns a component that displays this component in its center.
|
||||||
|
28
src/main/java/ru/windcorp/progressia/client/graphics/gui/Group.java
Executable file
28
src/main/java/ru/windcorp/progressia/client/graphics/gui/Group.java
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public class Group extends Component {
|
||||||
|
|
||||||
|
public Group(String name, Layout layout) {
|
||||||
|
super(name);
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -82,6 +82,11 @@ public class Label extends Component {
|
|||||||
public Font getFont() {
|
public Font getFont() {
|
||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setFont(Font font) {
|
||||||
|
this.font = font;
|
||||||
|
requestReassembly();
|
||||||
|
}
|
||||||
|
|
||||||
public String getCurrentText() {
|
public String getCurrentText() {
|
||||||
return currentText;
|
return currentText;
|
||||||
@ -96,11 +101,7 @@ public class Label extends Component {
|
|||||||
float startX = getX() + font.getAlign() * (getWidth() - currentSize.x);
|
float startX = getX() + font.getAlign() * (getWidth() - currentSize.x);
|
||||||
|
|
||||||
target.pushTransform(
|
target.pushTransform(
|
||||||
new Mat4().identity().translate(startX, getY(), -1000) // TODO wtf
|
new Mat4().identity().translate(startX, getY(), 0).scale(2)
|
||||||
// is this
|
|
||||||
// magic
|
|
||||||
// <---
|
|
||||||
.scale(2)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
target.addCustomRenderer(font.assemble(currentText, maxWidth));
|
target.addCustomRenderer(font.assemble(currentText, maxWidth));
|
||||||
|
60
src/main/java/ru/windcorp/progressia/client/graphics/gui/Panel.java
Executable file → Normal file
60
src/main/java/ru/windcorp/progressia/client/graphics/gui/Panel.java
Executable file → Normal file
@ -15,14 +15,66 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ru.windcorp.progressia.client.graphics.gui;
|
package ru.windcorp.progressia.client.graphics.gui;
|
||||||
|
|
||||||
public class Panel extends Component {
|
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 {
|
||||||
|
|
||||||
|
private Vec4 fill;
|
||||||
|
private Vec4 border;
|
||||||
|
|
||||||
|
public Panel(String name, Layout layout, Vec4 fill, Vec4 border) {
|
||||||
|
super(name, layout);
|
||||||
|
|
||||||
|
this.fill = Objects.requireNonNull(fill, "fill");
|
||||||
|
this.border = border;
|
||||||
|
}
|
||||||
|
|
||||||
public Panel(String name, Layout layout) {
|
public Panel(String name, Layout layout) {
|
||||||
super(name);
|
this(name, layout, Colors.WHITE, Colors.LIGHT_GRAY);
|
||||||
setLayout(layout);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the fill
|
||||||
|
*/
|
||||||
|
public Vec4 getFill() {
|
||||||
|
return fill;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param fill the fill to set
|
||||||
|
*/
|
||||||
|
public void setFill(Vec4 fill) {
|
||||||
|
this.fill = Objects.requireNonNull(fill, "fill");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the border
|
||||||
|
*/
|
||||||
|
public Vec4 getBorder() {
|
||||||
|
return border;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param border the border to set
|
||||||
|
*/
|
||||||
|
public void setBorder(Vec4 border) {
|
||||||
|
this.border = border;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assembleSelf(RenderTarget target) {
|
||||||
|
if (border == null) {
|
||||||
|
target.fill(getX(), getY(), getWidth(), getHeight(), fill);
|
||||||
|
} else {
|
||||||
|
target.fill(getX(), getY(), getWidth(), getHeight(), border);
|
||||||
|
target.fill(getX() + 2, getY() + 2, getWidth() - 4, getHeight() - 4, fill);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
* 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 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;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutHorizontal;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
|
|
||||||
|
public class RadioButton extends BasicButton {
|
||||||
|
|
||||||
|
private class Tick extends Component {
|
||||||
|
|
||||||
|
public Tick() {
|
||||||
|
super(RadioButton.this.getName() + ".Tick");
|
||||||
|
|
||||||
|
setPreferredSize(new Vec2i(Typefaces.getDefault().getLineHeight() * 3 / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cross(RenderTarget target, int x, int y, int size, Vec4 color) {
|
||||||
|
target.fill(x + 4, y, size - 8, size, color);
|
||||||
|
target.fill(x + 2, y + 2, size - 4, size - 4, color);
|
||||||
|
target.fill(x, y + 4, size, size - 8, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assembleSelf(RenderTarget target) {
|
||||||
|
|
||||||
|
int size = getPreferredSize().x;
|
||||||
|
int x = getX();
|
||||||
|
int y = getY() + (getHeight() - size) / 2;
|
||||||
|
|
||||||
|
// Border
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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, Colors.BLUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checked;
|
||||||
|
|
||||||
|
private RadioButtonGroup group = null;
|
||||||
|
|
||||||
|
public RadioButton(String name, String label, Font labelFont, boolean check) {
|
||||||
|
super(name, label, labelFont);
|
||||||
|
this.checked = check;
|
||||||
|
|
||||||
|
assert getChildren().size() == 1 : "RadioButton expects that BasicButton contains exactly one child";
|
||||||
|
Component basicChild = getChild(0);
|
||||||
|
|
||||||
|
Group group = new Group(getName() + ".LabelAndTick", new LayoutHorizontal(0, 10));
|
||||||
|
removeChild(basicChild);
|
||||||
|
setLayout(new LayoutAlign(0, 0.5f, 10));
|
||||||
|
group.setLayoutHint(basicChild.getLayoutHint());
|
||||||
|
group.addChild(new Tick());
|
||||||
|
group.addChild(basicChild);
|
||||||
|
addChild(group);
|
||||||
|
|
||||||
|
addListener(KeyEvent.class, e -> {
|
||||||
|
if (e.isRelease()) return false;
|
||||||
|
|
||||||
|
if (e.getKey() == GLFW.GLFW_KEY_LEFT || e.getKey() == GLFW.GLFW_KEY_UP) {
|
||||||
|
if (this.group != null) {
|
||||||
|
this.group.selectPrevious();
|
||||||
|
this.group.getSelected().takeFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else if (e.getKey() == GLFW.GLFW_KEY_RIGHT || e.getKey() == GLFW.GLFW_KEY_DOWN) {
|
||||||
|
if (this.group != null) {
|
||||||
|
this.group.selectNext();
|
||||||
|
this.group.getSelected().takeFocus();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
addAction(b -> setChecked(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RadioButton(String name, String label, Font labelFont) {
|
||||||
|
this(name, label, labelFont, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RadioButton(String name, String label, boolean check) {
|
||||||
|
this(name, label, new Font(), check);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RadioButton(String name, String label) {
|
||||||
|
this(name, label, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param group the group to set
|
||||||
|
*/
|
||||||
|
public RadioButton setGroup(RadioButtonGroup group) {
|
||||||
|
|
||||||
|
if (this.group != null) {
|
||||||
|
group.selectNext();
|
||||||
|
removeAction(group.listener);
|
||||||
|
group.buttons.remove(this);
|
||||||
|
group.getSelected(); // Clear reference if this was the only button in the group
|
||||||
|
}
|
||||||
|
|
||||||
|
this.group = group;
|
||||||
|
|
||||||
|
if (this.group != null) {
|
||||||
|
group.buttons.add(this);
|
||||||
|
addAction(group.listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
setChecked(false);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the checked
|
||||||
|
*/
|
||||||
|
public boolean isChecked() {
|
||||||
|
return checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param checked the checked to set
|
||||||
|
*/
|
||||||
|
public void setChecked(boolean checked) {
|
||||||
|
this.checked = checked;
|
||||||
|
|
||||||
|
if (group != null) {
|
||||||
|
group.listener.accept(this); // Failsafe for manual invocations of setChecked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assembleSelf(RenderTarget target) {
|
||||||
|
// Change label font color
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* 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.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class RadioButtonGroup {
|
||||||
|
|
||||||
|
private final Collection<Consumer<RadioButtonGroup>> actions = Collections.synchronizedCollection(new ArrayList<>());
|
||||||
|
final List<RadioButton> buttons = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
|
||||||
|
private RadioButton selected = null;
|
||||||
|
|
||||||
|
Consumer<BasicButton> listener = b -> {
|
||||||
|
if (b instanceof RadioButton && ((RadioButton) b).isChecked() && buttons.contains(b)) {
|
||||||
|
select((RadioButton) b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public RadioButtonGroup addAction(Consumer<RadioButtonGroup> action) {
|
||||||
|
this.actions.add(Objects.requireNonNull(action, "action"));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean removeAction(Consumer<BasicButton> action) {
|
||||||
|
return this.actions.remove(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RadioButton> getButtons() {
|
||||||
|
return Collections.unmodifiableList(buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized RadioButton getSelected() {
|
||||||
|
if (!buttons.contains(selected)) {
|
||||||
|
selected = null;
|
||||||
|
}
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void select(RadioButton button) {
|
||||||
|
if (button != null && !buttons.contains(button)) {
|
||||||
|
throw new IllegalArgumentException("Button " + button + " is not in the group");
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelected(); // Clear if invalid
|
||||||
|
|
||||||
|
if (selected == button) {
|
||||||
|
return; // Terminate listener-setter recursion
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected != null) {
|
||||||
|
selected.setChecked(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
selected = button;
|
||||||
|
|
||||||
|
if (selected != null) {
|
||||||
|
selected.setChecked(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.forEach(action -> action.accept(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void selectNext() {
|
||||||
|
selectNeighbour(+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void selectPrevious() {
|
||||||
|
selectNeighbour(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void selectNeighbour(int direction) {
|
||||||
|
if (getSelected() == null) {
|
||||||
|
if (buttons.isEmpty()) {
|
||||||
|
throw new IllegalStateException("Cannot select neighbour button: group empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
select(buttons.get(0));
|
||||||
|
} else {
|
||||||
|
RadioButton button;
|
||||||
|
int index = buttons.indexOf(selected);
|
||||||
|
|
||||||
|
do {
|
||||||
|
index += direction;
|
||||||
|
|
||||||
|
if (index >= buttons.size()) {
|
||||||
|
index = 0;
|
||||||
|
} else if (index < 0) {
|
||||||
|
index = buttons.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
button = buttons.get(index);
|
||||||
|
} while (button != getSelected() && !button.isEnabled());
|
||||||
|
|
||||||
|
select(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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.gui.event;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.BasicButton;
|
||||||
|
|
||||||
|
public class ButtonEvent extends ComponentEvent {
|
||||||
|
|
||||||
|
public static class Press extends ButtonEvent {
|
||||||
|
public Press(BasicButton button) {
|
||||||
|
super(button, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Release extends ButtonEvent {
|
||||||
|
public Release(BasicButton button) {
|
||||||
|
super(button, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final boolean isPress;
|
||||||
|
|
||||||
|
protected ButtonEvent(BasicButton button, boolean isPress) {
|
||||||
|
super(button);
|
||||||
|
this.isPress = isPress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ButtonEvent create(BasicButton button, boolean isPress) {
|
||||||
|
if (isPress) {
|
||||||
|
return new Press(button);
|
||||||
|
} else {
|
||||||
|
return new Release(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPress() {
|
||||||
|
return isPress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRelease() {
|
||||||
|
return !isPress;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package ru.windcorp.progressia.client.graphics.gui.event;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Component;
|
||||||
|
|
||||||
|
public class EnableEvent extends ComponentEvent {
|
||||||
|
|
||||||
|
public EnableEvent(Component component) {
|
||||||
|
super(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* 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.layout;
|
||||||
|
|
||||||
|
import static java.lang.Math.max;
|
||||||
|
|
||||||
|
import glm.vec._2.i.Vec2i;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Component;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Layout;
|
||||||
|
|
||||||
|
public class LayoutFill implements Layout {
|
||||||
|
|
||||||
|
private final int margin;
|
||||||
|
|
||||||
|
public LayoutFill(int margin) {
|
||||||
|
this.margin = margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LayoutFill() {
|
||||||
|
this(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void layout(Component c) {
|
||||||
|
c.getChildren().forEach(child -> {
|
||||||
|
|
||||||
|
int cWidth = c.getWidth() - 2 * margin;
|
||||||
|
int cHeight = c.getHeight() - 2 * margin;
|
||||||
|
|
||||||
|
child.setBounds(
|
||||||
|
c.getX() + margin,
|
||||||
|
c.getY() + margin,
|
||||||
|
cWidth,
|
||||||
|
cHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vec2i calculatePreferredSize(Component c) {
|
||||||
|
Vec2i result = new Vec2i(0, 0);
|
||||||
|
|
||||||
|
c.getChildren().stream()
|
||||||
|
.map(child -> child.getPreferredSize())
|
||||||
|
.forEach(size -> {
|
||||||
|
result.x = max(size.x, result.x);
|
||||||
|
result.y = max(size.y, result.y);
|
||||||
|
});
|
||||||
|
|
||||||
|
result.x += 2 * margin;
|
||||||
|
result.y += 2 * margin;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + "(" + margin + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -97,16 +97,27 @@ public class LayoutGrid implements Layout {
|
|||||||
void setBounds(int column, int row, Component child, Component parent) {
|
void setBounds(int column, int row, Component child, Component parent) {
|
||||||
if (!isSummed)
|
if (!isSummed)
|
||||||
throw new IllegalStateException("Not summed yet");
|
throw new IllegalStateException("Not summed yet");
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
|
||||||
|
if (column == columns.length - 1) {
|
||||||
|
width = parent.getWidth() - margin - columns[column];
|
||||||
|
} else {
|
||||||
|
width = columns[column + 1] - columns[column] - gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row == rows.length - 1) {
|
||||||
|
height = parent.getHeight() - margin - rows[row];
|
||||||
|
} else {
|
||||||
|
height = rows[row + 1] - rows[row] - gap;
|
||||||
|
}
|
||||||
|
|
||||||
child.setBounds(
|
child.setBounds(
|
||||||
parent.getX() + columns[column],
|
parent.getX() + columns[column],
|
||||||
parent.getY() + rows[row],
|
parent.getY() + parent.getHeight() - (rows[row] + height),
|
||||||
|
|
||||||
(column != (columns.length - 1) ? (columns[column + 1] - columns[column] - gap)
|
width,
|
||||||
: (parent.getWidth() - margin - columns[column])),
|
height
|
||||||
|
|
||||||
(row != (rows.length - 1) ? (rows[row + 1] - rows[row] - gap)
|
|
||||||
: (parent.getHeight() - margin - rows[row]))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,10 +143,9 @@ public class LayoutGrid implements Layout {
|
|||||||
GridDimensions grid = calculateGrid(c);
|
GridDimensions grid = calculateGrid(c);
|
||||||
grid.sum();
|
grid.sum();
|
||||||
|
|
||||||
int[] coords;
|
|
||||||
for (Component child : c.getChildren()) {
|
for (Component child : c.getChildren()) {
|
||||||
coords = (int[]) child.getLayoutHint();
|
Vec2i coords = (Vec2i) child.getLayoutHint();
|
||||||
grid.setBounds(coords[0], coords[1], child, c);
|
grid.setBounds(coords.x, coords.y, child, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,11 +159,10 @@ public class LayoutGrid implements Layout {
|
|||||||
|
|
||||||
private GridDimensions calculateGrid(Component parent) {
|
private GridDimensions calculateGrid(Component parent) {
|
||||||
GridDimensions result = new GridDimensions();
|
GridDimensions result = new GridDimensions();
|
||||||
int[] coords;
|
|
||||||
|
|
||||||
for (Component child : parent.getChildren()) {
|
for (Component child : parent.getChildren()) {
|
||||||
coords = (int[]) child.getLayoutHint();
|
Vec2i coords = (Vec2i) child.getLayoutHint();
|
||||||
result.add(coords[0], coords[1], child.getPreferredSize());
|
result.add(coords.x, coords.y, child.getPreferredSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* 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.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.Component;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Label;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Layout;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Panel;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.bus.Input;
|
||||||
|
import ru.windcorp.progressia.client.localization.MutableString;
|
||||||
|
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
|
||||||
|
|
||||||
|
public class MenuLayer extends GUILayer {
|
||||||
|
|
||||||
|
private final Component content;
|
||||||
|
private final Component background;
|
||||||
|
|
||||||
|
private final Runnable closeAction = () -> {
|
||||||
|
GUI.removeLayer(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
public MenuLayer(String name, Component content) {
|
||||||
|
super(name, new LayoutFill(0));
|
||||||
|
|
||||||
|
setCursorPolicy(CursorPolicy.REQUIRE);
|
||||||
|
|
||||||
|
this.background = new Panel(name + ".Background", new LayoutAlign(10), Colors.toVector(0x66000000), null);
|
||||||
|
this.content = content;
|
||||||
|
|
||||||
|
background.addChild(content);
|
||||||
|
getRoot().addChild(background);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MenuLayer(String name, Layout contentLayout) {
|
||||||
|
this(name, new Panel(name + ".Content", contentLayout));
|
||||||
|
}
|
||||||
|
|
||||||
|
public MenuLayer(String name) {
|
||||||
|
this(name, new LayoutVertical(20, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Component getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Component getBackground() {
|
||||||
|
return background;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addTitle() {
|
||||||
|
String translationKey = "Layer" + getName() + ".Title";
|
||||||
|
MutableString titleText = new MutableStringLocalized(translationKey);
|
||||||
|
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, Colors.BLUE, null);
|
||||||
|
panel.setLayout(new LayoutFill() {
|
||||||
|
@Override
|
||||||
|
public Vec2i calculatePreferredSize(Component c) {
|
||||||
|
return new Vec2i(label.getPreferredSize().x + 40, 4);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
getContent().addChild(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Runnable getCloseAction() {
|
||||||
|
return closeAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleInput(Input input) {
|
||||||
|
|
||||||
|
if (!input.isConsumed()) {
|
||||||
|
InputEvent event = input.getEvent();
|
||||||
|
|
||||||
|
if (event instanceof KeyEvent) {
|
||||||
|
KeyEvent keyEvent = (KeyEvent) event;
|
||||||
|
if (keyEvent.isPress() && keyEvent.getKey() == GLFW.GLFW_KEY_ESCAPE) {
|
||||||
|
getCloseAction().run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.handleInput(input);
|
||||||
|
input.consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -59,6 +59,8 @@ public class LayerWorld extends Layer {
|
|||||||
super("World");
|
super("World");
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.inputBasedControls = new InputBasedControls(client);
|
this.inputBasedControls = new InputBasedControls(client);
|
||||||
|
|
||||||
|
setCursorPolicy(CursorPolicy.FORBID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
package ru.windcorp.progressia.client.world;
|
package ru.windcorp.progressia.client.world;
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.util.VectorUtil;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
import ru.windcorp.progressia.common.world.ChunkData;
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
import ru.windcorp.progressia.common.world.ChunkDataListener;
|
import ru.windcorp.progressia.common.world.ChunkDataListener;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||||
@ -66,25 +68,33 @@ class ChunkUpdateListener implements ChunkDataListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onLocationChanged(ChunkData chunk, Vec3i blockInChunk) {
|
private void onLocationChanged(ChunkData chunk, Vec3i blockInChunk) {
|
||||||
Vec3i chunkPos = new Vec3i(chunk.getPosition());
|
Vec3i chunkPos = Vectors.grab3i().set(chunk.getX(), chunk.getY(), chunk.getZ());
|
||||||
|
|
||||||
if (blockInChunk.x == 0) {
|
checkCoordinate(blockInChunk, chunkPos, VectorUtil.Axis.X);
|
||||||
chunkPos.x -= 1;
|
checkCoordinate(blockInChunk, chunkPos, VectorUtil.Axis.Y);
|
||||||
} else if (blockInChunk.x == ChunkData.BLOCKS_PER_CHUNK - 1) {
|
checkCoordinate(blockInChunk, chunkPos, VectorUtil.Axis.Z);
|
||||||
chunkPos.x += 1;
|
|
||||||
} else if (blockInChunk.y == 0) {
|
Vectors.release(chunkPos);
|
||||||
chunkPos.y -= 1;
|
}
|
||||||
} else if (blockInChunk.y == ChunkData.BLOCKS_PER_CHUNK - 1) {
|
|
||||||
chunkPos.y += 1;
|
private void checkCoordinate(Vec3i blockInChunk, Vec3i chunkPos, VectorUtil.Axis axis) {
|
||||||
} else if (blockInChunk.z == 0) {
|
int block = VectorUtil.get(blockInChunk, axis);
|
||||||
chunkPos.z -= 1;
|
int diff = 0;
|
||||||
} else if (blockInChunk.z == ChunkData.BLOCKS_PER_CHUNK - 1) {
|
|
||||||
chunkPos.z += 1;
|
if (block == 0) {
|
||||||
|
diff = -1;
|
||||||
|
} else if (block == ChunkData.BLOCKS_PER_CHUNK - 1) {
|
||||||
|
diff = +1;
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int previousChunkPos = VectorUtil.get(chunkPos, axis);
|
||||||
|
VectorUtil.set(chunkPos, axis, previousChunkPos + diff);
|
||||||
|
|
||||||
world.markChunkForUpdate(chunkPos);
|
world.markChunkForUpdate(chunkPos);
|
||||||
|
|
||||||
|
VectorUtil.set(chunkPos, axis, previousChunkPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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.common.resource;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.Progressia;
|
||||||
|
|
||||||
|
public class ClasspathResourceReader implements ResourceReader {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream read(String name) {
|
||||||
|
return Progressia.class.getClassLoader().getResourceAsStream(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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.common.resource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class FilesystemResourceReader implements ResourceReader {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream read(String name) {
|
||||||
|
try {
|
||||||
|
return Files.newInputStream(Paths.get(name));
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -30,19 +30,24 @@ import org.lwjgl.BufferUtils;
|
|||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
import com.google.common.io.CharStreams;
|
import com.google.common.io.CharStreams;
|
||||||
|
|
||||||
import ru.windcorp.progressia.Progressia;
|
|
||||||
import ru.windcorp.progressia.common.util.Named;
|
import ru.windcorp.progressia.common.util.Named;
|
||||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
public class Resource extends Named {
|
public class Resource extends Named {
|
||||||
|
|
||||||
|
private final ResourceReader resourceReader;
|
||||||
|
|
||||||
public Resource(String name) {
|
public Resource(String name, ResourceReader resourceReader) {
|
||||||
super(name);
|
super(name);
|
||||||
|
this.resourceReader = resourceReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream() {
|
public InputStream getInputStream() {
|
||||||
// TODO Do proper resource lookup
|
return getResourceReader().read(getName());
|
||||||
return Progressia.class.getClassLoader().getResourceAsStream(getName());
|
}
|
||||||
|
|
||||||
|
public ResourceReader getResourceReader() {
|
||||||
|
return resourceReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Reader getReader() {
|
public Reader getReader() {
|
||||||
|
@ -19,9 +19,16 @@
|
|||||||
package ru.windcorp.progressia.common.resource;
|
package ru.windcorp.progressia.common.resource;
|
||||||
|
|
||||||
public class ResourceManager {
|
public class ResourceManager {
|
||||||
|
|
||||||
|
private static final ResourceReader CLASSPATH_READER = new ClasspathResourceReader();
|
||||||
|
private static final ResourceReader FILESYSTEM_READER = new FilesystemResourceReader();
|
||||||
|
|
||||||
public static Resource getResource(String name) {
|
public static Resource getResource(String name) {
|
||||||
return new Resource(name);
|
return new Resource(name, CLASSPATH_READER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Resource getFileResource(String name) {
|
||||||
|
return new Resource(name, FILESYSTEM_READER);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Resource getTextureResource(String name) {
|
public static Resource getTextureResource(String name) {
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* 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.resource;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public interface ResourceReader {
|
||||||
|
|
||||||
|
InputStream read(String name);
|
||||||
|
|
||||||
|
}
|
@ -203,13 +203,12 @@ public class CrashReports {
|
|||||||
if (provider == null)
|
if (provider == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
addSeparator(output);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Map<String, String> buf = new HashMap<>();
|
Map<String, String> buf = new HashMap<>();
|
||||||
provider.provideContext(buf);
|
provider.provideContext(buf);
|
||||||
|
|
||||||
if (!buf.isEmpty()) {
|
if (!buf.isEmpty()) {
|
||||||
|
addSeparator(output);
|
||||||
output.append(StringUtil.center(provider.getName(), 80)).append("\n");
|
output.append(StringUtil.center(provider.getName(), 80)).append("\n");
|
||||||
for (Map.Entry<String, String> entry : buf.entrySet()) {
|
for (Map.Entry<String, String> entry : buf.entrySet()) {
|
||||||
output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
|
output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
|
||||||
|
@ -26,12 +26,12 @@ public class RAMContextProvider implements ContextProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void provideContext(Map<String, String> output) {
|
public void provideContext(Map<String, String> output) {
|
||||||
output.put("Max Memory", Long.toString(Runtime.getRuntime().maxMemory() / 1024 / 1024) + " MB");
|
output.put("Max Memory", Runtime.getRuntime().maxMemory() / 1024 / 1024 + " MB");
|
||||||
output.put("Total Memory", Long.toString(Runtime.getRuntime().totalMemory() / 1024 / 1024) + " MB");
|
output.put("Total Memory", Runtime.getRuntime().totalMemory() / 1024 / 1024 + " MB");
|
||||||
output.put("Free Memory", Long.toString(Runtime.getRuntime().freeMemory() / 1024 / 1024) + " MB");
|
output.put("Free Memory", Runtime.getRuntime().freeMemory() / 1024 / 1024 + " MB");
|
||||||
output.put(
|
output.put(
|
||||||
"Used Memory",
|
"Used Memory",
|
||||||
Long.toString((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024)
|
(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024
|
||||||
+ " MB"
|
+ " MB"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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.util.crash.providers;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.ContextProvider;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ScreenContextProvider implements ContextProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void provideContext(Map<String, String> output) {
|
||||||
|
if (GraphicsBackend.isGLFWInitialized()) {
|
||||||
|
output.put("Refresh rate", GraphicsBackend.getRefreshRate() + " Hz");
|
||||||
|
output.put("Size", GraphicsBackend.getFrameWidth() + "x" + GraphicsBackend.getFrameHeight());
|
||||||
|
output.put("Fullscreen", GraphicsBackend.isFullscreen() ? "enabled" : "disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Screen Context Provider";
|
||||||
|
}
|
||||||
|
}
|
@ -207,6 +207,18 @@ public class Server {
|
|||||||
return this.serverThread.getTicker().getTPS();
|
return this.serverThread.getTicker().getTPS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the amount of ticks performed since the server has started. This
|
||||||
|
* value resets on shutdowns. The counter is incremented at the end of a
|
||||||
|
* tick.
|
||||||
|
*
|
||||||
|
* @return the number of times the world has finished a tick since the
|
||||||
|
* server has started.
|
||||||
|
*/
|
||||||
|
public long getUptimeTicks() {
|
||||||
|
return this.serverThread.getTicker().getUptimeTicks();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link WorldAccessor} object for this server. Use the
|
* Returns the {@link WorldAccessor} object for this server. Use the
|
||||||
* provided accessor to request common {@link Evaluation}s and
|
* provided accessor to request common {@link Evaluation}s and
|
||||||
|
@ -82,6 +82,7 @@ public class TickerCoordinator {
|
|||||||
private boolean isTickStartSet = false;
|
private boolean isTickStartSet = false;
|
||||||
private long tickStart = -1;
|
private long tickStart = -1;
|
||||||
private double tickLength = 1.0 / 20; // Do something about it
|
private double tickLength = 1.0 / 20; // Do something about it
|
||||||
|
private long ticks = 0;
|
||||||
|
|
||||||
private final Logger logger = LogManager.getLogger("Ticker Coordinator");
|
private final Logger logger = LogManager.getLogger("Ticker Coordinator");
|
||||||
|
|
||||||
@ -151,6 +152,10 @@ public class TickerCoordinator {
|
|||||||
public double getTPS() {
|
public double getTPS() {
|
||||||
return 1 / tickLength;
|
return 1 / tickLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getUptimeTicks() {
|
||||||
|
return ticks;
|
||||||
|
}
|
||||||
|
|
||||||
private void onTickStart() {
|
private void onTickStart() {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
@ -163,6 +168,10 @@ public class TickerCoordinator {
|
|||||||
|
|
||||||
tickStart = System.currentTimeMillis();
|
tickStart = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onTickEnd() {
|
||||||
|
ticks++;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* runOneTick & Friends
|
* runOneTick & Friends
|
||||||
@ -182,6 +191,8 @@ public class TickerCoordinator {
|
|||||||
logger.debug("Pass complete");
|
logger.debug("Pass complete");
|
||||||
passes++;
|
passes++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onTickEnd();
|
||||||
|
|
||||||
logger.debug("Tick complete; run {} passes", passes);
|
logger.debug("Tick complete; run {} passes", passes);
|
||||||
|
|
||||||
@ -191,7 +202,7 @@ public class TickerCoordinator {
|
|||||||
// ...or almost silently
|
// ...or almost silently
|
||||||
logger.debug("Tick interrupted. WTF?");
|
logger.debug("Tick interrupted. WTF?");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
crash(e, "Coordinator");
|
throw CrashReports.report(e, "Coordinator");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import ru.windcorp.progressia.client.graphics.font.Font;
|
|||||||
import ru.windcorp.progressia.client.graphics.font.Typeface;
|
import ru.windcorp.progressia.client.graphics.font.Typeface;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
|
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.Label;
|
import ru.windcorp.progressia.client.graphics.gui.Label;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.Panel;
|
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.LayoutAlign;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
|
||||||
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
|
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
|
||||||
@ -33,12 +33,12 @@ public class LayerAbout extends GUILayer {
|
|||||||
public LayerAbout() {
|
public LayerAbout() {
|
||||||
super("LayerAbout", new LayoutAlign(1, 1, 5));
|
super("LayerAbout", new LayoutAlign(1, 1, 5));
|
||||||
|
|
||||||
Panel panel = new Panel("ControlDisplays", new LayoutVertical(5));
|
Group group = new Group("ControlDisplays", new LayoutVertical(5));
|
||||||
|
|
||||||
Font font = new Font().withColor(Colors.WHITE).deriveOutlined().withAlign(Typeface.ALIGN_RIGHT);
|
Font font = new Font().withColor(Colors.WHITE).deriveOutlined().withAlign(Typeface.ALIGN_RIGHT);
|
||||||
Font aboutFont = font.withColor(0xFF37A3E6).deriveBold();
|
Font aboutFont = font.withColor(0xFF37A3E6).deriveBold();
|
||||||
|
|
||||||
panel.addChild(
|
group.addChild(
|
||||||
new Label(
|
new Label(
|
||||||
"About",
|
"About",
|
||||||
aboutFont,
|
aboutFont,
|
||||||
@ -46,7 +46,7 @@ public class LayerAbout extends GUILayer {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
panel.addChild(
|
group.addChild(
|
||||||
new Label(
|
new Label(
|
||||||
"Version",
|
"Version",
|
||||||
font,
|
font,
|
||||||
@ -54,7 +54,7 @@ public class LayerAbout extends GUILayer {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
panel.addChild(
|
group.addChild(
|
||||||
new Label(
|
new Label(
|
||||||
"DebugHint",
|
"DebugHint",
|
||||||
font,
|
font,
|
||||||
@ -62,7 +62,7 @@ public class LayerAbout extends GUILayer {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
getRoot().addChild(panel);
|
getRoot().addChild(group);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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.test;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.Colors;
|
||||||
|
import ru.windcorp.progressia.client.graphics.font.Font;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Button;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Checkbox;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Label;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.RadioButton;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.RadioButtonGroup;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.menu.MenuLayer;
|
||||||
|
|
||||||
|
public class LayerButtonTest extends MenuLayer {
|
||||||
|
|
||||||
|
public LayerButtonTest() {
|
||||||
|
super("ButtonTest");
|
||||||
|
|
||||||
|
addTitle();
|
||||||
|
|
||||||
|
Button blockableButton;
|
||||||
|
getContent().addChild((blockableButton = new Button("BlockableButton", "Blockable")).addAction(b -> {
|
||||||
|
System.out.println("Button Blockable!");
|
||||||
|
}));
|
||||||
|
blockableButton.setEnabled(false);
|
||||||
|
|
||||||
|
getContent().addChild(new Checkbox("EnableButton", "Enable").addAction(b -> {
|
||||||
|
blockableButton.setEnabled(((Checkbox) b).isChecked());
|
||||||
|
}));
|
||||||
|
|
||||||
|
RadioButtonGroup group = new RadioButtonGroup().addAction(g -> {
|
||||||
|
System.out.println("RBG! " + g.getSelected().getLabel().getCurrentText());
|
||||||
|
});
|
||||||
|
|
||||||
|
getContent().addChild(new RadioButton("RB1", "Moon").setGroup(group));
|
||||||
|
getContent().addChild(new RadioButton("RB2", "Type").setGroup(group));
|
||||||
|
getContent().addChild(new RadioButton("RB3", "Ice").setGroup(group));
|
||||||
|
getContent().addChild(new RadioButton("RB4", "Cream").setGroup(group));
|
||||||
|
|
||||||
|
getContent().getChild(getContent().getChildren().size() - 1).setEnabled(false);
|
||||||
|
|
||||||
|
getContent().addChild(new Label("Hint", new Font().withColor(Colors.LIGHT_GRAY), "This is a MenuLayer"));
|
||||||
|
|
||||||
|
getContent().addChild(new Button("Continue", "Continue").addAction(b -> {
|
||||||
|
getCloseAction().run();
|
||||||
|
}));
|
||||||
|
|
||||||
|
getContent().addChild(new Button("Quit", "Quit").addAction(b -> {
|
||||||
|
System.exit(0);
|
||||||
|
}));
|
||||||
|
|
||||||
|
getContent().takeFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -18,22 +18,18 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.test;
|
package ru.windcorp.progressia.test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import glm.vec._3.Vec3;
|
import glm.vec._3.Vec3;
|
||||||
import glm.vec._4.Vec4;
|
import glm.vec._4.Vec4;
|
||||||
import ru.windcorp.progressia.client.Client;
|
import ru.windcorp.progressia.client.Client;
|
||||||
import ru.windcorp.progressia.client.ClientState;
|
import ru.windcorp.progressia.client.ClientState;
|
||||||
import ru.windcorp.progressia.client.graphics.Colors;
|
import ru.windcorp.progressia.client.graphics.Colors;
|
||||||
|
import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend;
|
||||||
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
|
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
|
||||||
import ru.windcorp.progressia.client.graphics.font.Font;
|
import ru.windcorp.progressia.client.graphics.font.Font;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.DynamicLabel;
|
import ru.windcorp.progressia.client.graphics.gui.DynamicLabel;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
|
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.Label;
|
import ru.windcorp.progressia.client.graphics.gui.Label;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.Panel;
|
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.LayoutAlign;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
|
||||||
import ru.windcorp.progressia.client.localization.Localizer;
|
import ru.windcorp.progressia.client.localization.Localizer;
|
||||||
@ -44,19 +40,24 @@ import ru.windcorp.progressia.common.util.dynstr.DynamicStrings;
|
|||||||
import ru.windcorp.progressia.server.Server;
|
import ru.windcorp.progressia.server.Server;
|
||||||
import ru.windcorp.progressia.server.ServerState;
|
import ru.windcorp.progressia.server.ServerState;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class LayerTestGUI extends GUILayer {
|
public class LayerTestGUI extends GUILayer {
|
||||||
|
|
||||||
public LayerTestGUI() {
|
public LayerTestGUI() {
|
||||||
super("LayerTestGui", new LayoutAlign(0, 1, 5));
|
super("LayerTestGui", new LayoutAlign(0, 1, 5));
|
||||||
|
|
||||||
Panel panel = new Panel("ControlDisplays", new LayoutVertical(5));
|
Group group = new Group("ControlDisplays", new LayoutVertical(5));
|
||||||
|
|
||||||
Vec4 color = Colors.WHITE;
|
Vec4 color = Colors.WHITE;
|
||||||
Font font = new Font().withColor(color).deriveOutlined();
|
Font font = new Font().withColor(color).deriveOutlined();
|
||||||
|
|
||||||
TestPlayerControls tpc = TestPlayerControls.getInstance();
|
TestPlayerControls tpc = TestPlayerControls.getInstance();
|
||||||
|
|
||||||
panel.addChild(
|
group.addChild(
|
||||||
new Label(
|
new Label(
|
||||||
"IsFlyingDisplay",
|
"IsFlyingDisplay",
|
||||||
font,
|
font,
|
||||||
@ -64,7 +65,7 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
panel.addChild(
|
group.addChild(
|
||||||
new Label(
|
new Label(
|
||||||
"IsSprintingDisplay",
|
"IsSprintingDisplay",
|
||||||
font,
|
font,
|
||||||
@ -72,15 +73,7 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
panel.addChild(
|
group.addChild(
|
||||||
new Label(
|
|
||||||
"IsMouseCapturedDisplay",
|
|
||||||
font,
|
|
||||||
tmp_dynFormat("LayerTestGUI.IsMouseCapturedDisplay", tpc::isMouseCaptured)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
panel.addChild(
|
|
||||||
new Label(
|
new Label(
|
||||||
"CameraModeDisplay",
|
"CameraModeDisplay",
|
||||||
font,
|
font,
|
||||||
@ -91,7 +84,7 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
panel.addChild(
|
group.addChild(
|
||||||
new Label(
|
new Label(
|
||||||
"LanguageDisplay",
|
"LanguageDisplay",
|
||||||
font,
|
font,
|
||||||
@ -99,7 +92,23 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
panel.addChild(
|
group.addChild(
|
||||||
|
new Label(
|
||||||
|
"FullscreenDisplay",
|
||||||
|
font,
|
||||||
|
tmp_dynFormat("LayerTestGUI.IsFullscreen", GraphicsBackend::isFullscreen)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
group.addChild(
|
||||||
|
new Label(
|
||||||
|
"VSyncDisplay",
|
||||||
|
font,
|
||||||
|
tmp_dynFormat("LayerTestGUI.IsVSync", GraphicsBackend::isVSyncEnabled)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
group.addChild(
|
||||||
new DynamicLabel(
|
new DynamicLabel(
|
||||||
"FPSDisplay",
|
"FPSDisplay",
|
||||||
font,
|
font,
|
||||||
@ -111,7 +120,7 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
panel.addChild(
|
group.addChild(
|
||||||
new DynamicLabel(
|
new DynamicLabel(
|
||||||
"TPSDisplay",
|
"TPSDisplay",
|
||||||
font,
|
font,
|
||||||
@ -120,7 +129,7 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
panel.addChild(
|
group.addChild(
|
||||||
new DynamicLabel(
|
new DynamicLabel(
|
||||||
"ChunkUpdatesDisplay",
|
"ChunkUpdatesDisplay",
|
||||||
font,
|
font,
|
||||||
@ -132,7 +141,7 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
panel.addChild(
|
group.addChild(
|
||||||
new DynamicLabel(
|
new DynamicLabel(
|
||||||
"PosDisplay",
|
"PosDisplay",
|
||||||
font,
|
font,
|
||||||
@ -141,7 +150,7 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
panel.addChild(
|
group.addChild(
|
||||||
new Label(
|
new Label(
|
||||||
"SelectedBlockDisplay",
|
"SelectedBlockDisplay",
|
||||||
font,
|
font,
|
||||||
@ -152,7 +161,7 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
panel.addChild(
|
group.addChild(
|
||||||
new Label(
|
new Label(
|
||||||
"SelectedTileDisplay",
|
"SelectedTileDisplay",
|
||||||
font,
|
font,
|
||||||
@ -163,7 +172,7 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
panel.addChild(
|
group.addChild(
|
||||||
new Label(
|
new Label(
|
||||||
"PlacementModeHint",
|
"PlacementModeHint",
|
||||||
font,
|
font,
|
||||||
@ -171,7 +180,7 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
getRoot().addChild(panel);
|
getRoot().addChild(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Runnable getUpdateCallback() {
|
public Runnable getUpdateCallback() {
|
||||||
|
@ -32,7 +32,7 @@ import org.lwjgl.glfw.GLFW;
|
|||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.client.ClientState;
|
import ru.windcorp.progressia.client.ClientState;
|
||||||
import ru.windcorp.progressia.client.audio.SoundEffect;
|
import ru.windcorp.progressia.client.audio.Sound;
|
||||||
import ru.windcorp.progressia.client.comms.controls.*;
|
import ru.windcorp.progressia.client.comms.controls.*;
|
||||||
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
||||||
@ -289,6 +289,15 @@ public class TestContent {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
logic.register(ControlLogic.of("Test:PlaceTile", TestContent::onTilePlaceReceived));
|
logic.register(ControlLogic.of("Test:PlaceTile", TestContent::onTilePlaceReceived));
|
||||||
|
|
||||||
|
triggers.register(
|
||||||
|
ControlTriggers.localOf(
|
||||||
|
"Test:StartNextMusic",
|
||||||
|
KeyEvent.class,
|
||||||
|
TestMusicPlayer::startNextNow,
|
||||||
|
KeyMatcher.of(GLFW.GLFW_KEY_M).matcher()
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void register(BlockData x) {
|
private static void register(BlockData x) {
|
||||||
@ -362,7 +371,7 @@ public class TestContent {
|
|||||||
|
|
||||||
private static void onBlockBreakTrigger(ControlData control) {
|
private static void onBlockBreakTrigger(ControlData control) {
|
||||||
((ControlBreakBlockData) control).setBlockInWorld(getSelection().getBlock());
|
((ControlBreakBlockData) control).setBlockInWorld(getSelection().getBlock());
|
||||||
SoundEffect sfx = new SoundEffect("Progressia:BlockDestroy");
|
Sound sfx = new Sound("Progressia:BlockDestroy");
|
||||||
sfx.setPosition(getSelection().getPoint());
|
sfx.setPosition(getSelection().getPoint());
|
||||||
sfx.setPitch((float) (Math.random() + 1 * 0.5));
|
sfx.setPitch((float) (Math.random() + 1 * 0.5));
|
||||||
sfx.play(false);
|
sfx.play(false);
|
||||||
|
152
src/main/java/ru/windcorp/progressia/test/TestMusicPlayer.java
Normal file
152
src/main/java/ru/windcorp/progressia/test/TestMusicPlayer.java
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* 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.test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.audio.AudioFormat;
|
||||||
|
import ru.windcorp.progressia.client.audio.AudioManager;
|
||||||
|
import ru.windcorp.progressia.client.audio.AudioRegistry;
|
||||||
|
import ru.windcorp.progressia.client.audio.Music;
|
||||||
|
import ru.windcorp.progressia.client.audio.Sound;
|
||||||
|
import ru.windcorp.progressia.client.audio.backend.SoundType;
|
||||||
|
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
|
public class TestMusicPlayer implements Runnable {
|
||||||
|
|
||||||
|
private static final int MIN_SILENCE = 15 * 1000; // 15 seconds
|
||||||
|
private static final int MAX_SILENCE = 60 * 1000; // one minute
|
||||||
|
|
||||||
|
private static TestMusicPlayer instance = null;
|
||||||
|
|
||||||
|
private final List<SoundType> compositions = new ArrayList<>();
|
||||||
|
|
||||||
|
private final Random random = new Random();
|
||||||
|
private long nextStart;
|
||||||
|
private Sound lastStarted = null;
|
||||||
|
|
||||||
|
public TestMusicPlayer() {
|
||||||
|
this.nextStart = System.currentTimeMillis();
|
||||||
|
|
||||||
|
instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void start() {
|
||||||
|
Thread thread = new Thread(new TestMusicPlayer(), "Music Thread");
|
||||||
|
thread.setDaemon(true);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
loadCompositions();
|
||||||
|
|
||||||
|
if (compositions.isEmpty()) {
|
||||||
|
LogManager.getLogger().warn("No music found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
synchronized (this) {
|
||||||
|
while (true) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (nextStart > now) {
|
||||||
|
wait(nextStart - now);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LogManager.getLogger().warn("Received interrupt in music thread, terminating thread...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startNextComposition();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadCompositions() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
Path directory = Paths.get("music");
|
||||||
|
|
||||||
|
if (!Files.isDirectory(directory)) {
|
||||||
|
Files.createDirectories(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<Path> it = Files.walk(directory).filter(Files::isRegularFile).iterator();
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (it.hasNext()) {
|
||||||
|
String file = it.next().toString();
|
||||||
|
if (!file.endsWith(".ogg") && !file.endsWith(".oga")) {
|
||||||
|
LogManager.getLogger().warn("Skipping " + file + ": not .ogg nor .oga");
|
||||||
|
}
|
||||||
|
|
||||||
|
String id = "Progressia:Music" + (i++);
|
||||||
|
|
||||||
|
AudioManager.loadSound(ResourceManager.getFileResource(file.toString()), id, AudioFormat.STEREO);
|
||||||
|
SoundType composition = AudioRegistry.getInstance().get(id);
|
||||||
|
compositions.add(composition);
|
||||||
|
|
||||||
|
LogManager.getLogger().info("Loaded " + file);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw CrashReports.report(e, "Could not load music");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void startNextComposition() {
|
||||||
|
int index = random.nextInt(compositions.size());
|
||||||
|
SoundType composition = compositions.get(index);
|
||||||
|
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
long durationInMs = (long) (composition.getDuration() * 1000);
|
||||||
|
long silence = random.nextInt(MAX_SILENCE - MIN_SILENCE) + MIN_SILENCE;
|
||||||
|
|
||||||
|
nextStart = now + durationInMs + silence;
|
||||||
|
|
||||||
|
lastStarted = new Music(composition);
|
||||||
|
lastStarted.play(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startNextNow() {
|
||||||
|
if (instance == null) return;
|
||||||
|
|
||||||
|
synchronized (instance) {
|
||||||
|
instance.nextStart = System.currentTimeMillis();
|
||||||
|
instance.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -84,8 +84,6 @@ public class TestPlayerControls {
|
|||||||
private double lastSpacePress = Double.NEGATIVE_INFINITY;
|
private double lastSpacePress = Double.NEGATIVE_INFINITY;
|
||||||
private double lastSprintPress = Double.NEGATIVE_INFINITY;
|
private double lastSprintPress = Double.NEGATIVE_INFINITY;
|
||||||
|
|
||||||
private boolean captureMouse = true;
|
|
||||||
|
|
||||||
private int selectedBlock = 0;
|
private int selectedBlock = 0;
|
||||||
private int selectedTile = 0;
|
private int selectedTile = 0;
|
||||||
private boolean isBlockSelected = true;
|
private boolean isBlockSelected = true;
|
||||||
@ -203,9 +201,24 @@ public class TestPlayerControls {
|
|||||||
case GLFW.GLFW_KEY_ESCAPE:
|
case GLFW.GLFW_KEY_ESCAPE:
|
||||||
if (!event.isPress())
|
if (!event.isPress())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
handleEscape();
|
handleEscape();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case GLFW.GLFW_KEY_F11:
|
||||||
|
if (!event.isPress())
|
||||||
|
return false;
|
||||||
|
GraphicsInterface.makeFullscreen(!GraphicsBackend.isFullscreen());
|
||||||
|
updateGUI();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GLFW.GLFW_KEY_F12:
|
||||||
|
if (!event.isPress())
|
||||||
|
return false;
|
||||||
|
GraphicsBackend.setVSyncEnabled(!GraphicsBackend.isVSyncEnabled());
|
||||||
|
updateGUI();
|
||||||
|
break;
|
||||||
|
|
||||||
case GLFW.GLFW_KEY_F3:
|
case GLFW.GLFW_KEY_F3:
|
||||||
if (!event.isPress())
|
if (!event.isPress())
|
||||||
return false;
|
return false;
|
||||||
@ -298,14 +311,10 @@ public class TestPlayerControls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleEscape() {
|
private void handleEscape() {
|
||||||
if (captureMouse) {
|
movementForward = 0;
|
||||||
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL);
|
movementRight = 0;
|
||||||
} else {
|
movementUp = 0;
|
||||||
GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED);
|
GUI.addTopLayer(new LayerButtonTest());
|
||||||
}
|
|
||||||
|
|
||||||
captureMouse = !captureMouse;
|
|
||||||
updateGUI();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleDebugLayerSwitch() {
|
private void handleDebugLayerSwitch() {
|
||||||
@ -344,10 +353,6 @@ public class TestPlayerControls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onMouseMoved(CursorMoveEvent event) {
|
private void onMouseMoved(CursorMoveEvent event) {
|
||||||
if (!captureMouse) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
|
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -445,10 +450,6 @@ public class TestPlayerControls {
|
|||||||
return isSprinting;
|
return isSprinting;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMouseCaptured() {
|
|
||||||
return captureMouse;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockData getSelectedBlock() {
|
public BlockData getSelectedBlock() {
|
||||||
return TestContent.PLACEABLE_BLOCKS.get(selectedBlock);
|
return TestContent.PLACEABLE_BLOCKS.get(selectedBlock);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ LayerAbout.DebugHint = Debug GUI: F3
|
|||||||
|
|
||||||
LayerTestGUI.IsFlyingDisplay = Flying: %5s (Space bar x2)
|
LayerTestGUI.IsFlyingDisplay = Flying: %5s (Space bar x2)
|
||||||
LayerTestGUI.IsSprintingDisplay = Sprinting: %5s (W x2)
|
LayerTestGUI.IsSprintingDisplay = Sprinting: %5s (W x2)
|
||||||
LayerTestGUI.IsMouseCapturedDisplay = Mouse captured: %5s (Esc)
|
|
||||||
LayerTestGUI.CameraModeDisplay = Camera mode: %5d (F5)
|
LayerTestGUI.CameraModeDisplay = Camera mode: %5d (F5)
|
||||||
LayerTestGUI.LanguageDisplay = Language: %5s (L)
|
LayerTestGUI.LanguageDisplay = Language: %5s (L)
|
||||||
LayerTestGUI.FPSDisplay = FPS:
|
LayerTestGUI.FPSDisplay = FPS:
|
||||||
@ -18,4 +17,8 @@ LayerTestGUI.PosDisplay.NA.Client = Pos: client n/a
|
|||||||
LayerTestGUI.PosDisplay.NA.Entity = Pos: entity n/a
|
LayerTestGUI.PosDisplay.NA.Entity = Pos: entity n/a
|
||||||
LayerTestGUI.SelectedBlockDisplay = %s Block: %s
|
LayerTestGUI.SelectedBlockDisplay = %s Block: %s
|
||||||
LayerTestGUI.SelectedTileDisplay = %s Tile: %s
|
LayerTestGUI.SelectedTileDisplay = %s Tile: %s
|
||||||
LayerTestGUI.PlacementModeHint = (Blocks %s Tiles: Ctrl + Mouse Wheel)
|
LayerTestGUI.PlacementModeHint = (Blocks %s Tiles: Ctrl + Mouse Wheel)
|
||||||
|
LayerTestGUI.IsFullscreen = Fullscreen: %5s (F11)
|
||||||
|
LayerTestGUI.IsVSync = VSync: %5s (F12)
|
||||||
|
|
||||||
|
LayerButtonTest.Title = Button Test
|
@ -6,7 +6,6 @@ LayerAbout.DebugHint = Отладочный GUI: F3
|
|||||||
|
|
||||||
LayerTestGUI.IsFlyingDisplay = Полёт: %5s (Пробел x2)
|
LayerTestGUI.IsFlyingDisplay = Полёт: %5s (Пробел x2)
|
||||||
LayerTestGUI.IsSprintingDisplay = Бег: %5s (W x2)
|
LayerTestGUI.IsSprintingDisplay = Бег: %5s (W x2)
|
||||||
LayerTestGUI.IsMouseCapturedDisplay = Захват мыши: %5s (Esc)
|
|
||||||
LayerTestGUI.CameraModeDisplay = Камера: %5d (F5)
|
LayerTestGUI.CameraModeDisplay = Камера: %5d (F5)
|
||||||
LayerTestGUI.LanguageDisplay = Язык: %5s (L)
|
LayerTestGUI.LanguageDisplay = Язык: %5s (L)
|
||||||
LayerTestGUI.FPSDisplay = FPS:
|
LayerTestGUI.FPSDisplay = FPS:
|
||||||
@ -18,4 +17,8 @@ LayerTestGUI.PosDisplay.NA.Client = Поз: клиент н/д
|
|||||||
LayerTestGUI.PosDisplay.NA.Entity = Поз: сущность н/д
|
LayerTestGUI.PosDisplay.NA.Entity = Поз: сущность н/д
|
||||||
LayerTestGUI.SelectedBlockDisplay = %s Блок: %s
|
LayerTestGUI.SelectedBlockDisplay = %s Блок: %s
|
||||||
LayerTestGUI.SelectedTileDisplay = %s Плитка: %s
|
LayerTestGUI.SelectedTileDisplay = %s Плитка: %s
|
||||||
LayerTestGUI.PlacementModeHint = (Блок %s плитки: Ctrl + прокрутка)
|
LayerTestGUI.PlacementModeHint = (Блок %s плитки: Ctrl + прокрутка)
|
||||||
|
LayerTestGUI.IsFullscreen = Полный экран: %5s (F11)
|
||||||
|
LayerTestGUI.IsVSync = Верт. синхр.: %5s (F12)
|
||||||
|
|
||||||
|
LayerButtonTest.Title = Тест Кнопок
|
Reference in New Issue
Block a user