2 Commits

Author SHA1 Message Date
eb89753ed9 [Editor] Enable graphic assets preview in Project Structure panel 2021-02-21 20:28:07 +01:00
b2cda5fd20 [Editor] Enable creating snapshots when map is being saved
Because the new map creation is done in different place than saving
already existing maps, there is a bug that no snapshot is created
when new map is created. The snapshots only are created when
map is saved (Save via button with floppy disk icon in Map View
toolbar).
2021-02-21 20:27:00 +01:00
602 changed files with 3453 additions and 31219 deletions

1
.envrc
View File

@@ -1 +0,0 @@
use flake

10
.gitignore vendored
View File

@@ -139,13 +139,3 @@ gradle-app.setting
# BASE sample data # BASE sample data
**/resources/project/ **/resources/project/
# Gradle cache
gcache/
util/gradle
util/gradlew*
# Nix files
result
.direnv

View File

@@ -1,18 +0,0 @@
plugins {
id 'java-library'
}
group 'com.bartlomiejpluta.base'
version 'unspecified'
repositories {
mavenCentral()
}
dependencies {
implementation "org.joml:joml:${jomlVersion}"
implementation "org.slf4j:slf4j-api:${slf4jVersion}"
implementation "org.apache.commons:commons-math3:${apacheCommonsVersion}"
implementation "org.projectlombok:lombok:${lombokVersion}"
annotationProcessor "org.projectlombok:lombok:${lombokVersion}"
}

View File

@@ -1,8 +0,0 @@
package com.bartlomiejpluta.base.api.ai;
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
@FunctionalInterface
public interface AI {
void nextActivity(ObjectLayer layer, float dt);
}

View File

@@ -1,7 +0,0 @@
package com.bartlomiejpluta.base.api.ai;
import com.bartlomiejpluta.base.api.character.Character;
public interface NPC extends Character {
AI getStrategy();
}

View File

@@ -1,142 +0,0 @@
package com.bartlomiejpluta.base.api.animation;
import com.bartlomiejpluta.base.internal.program.Updatable;
/**
* Represents an object that can be animated by cycling through multiple frames of a texture.
*
* <p>This interface provides a complete animation system for 2D sprites and other visual objects
* in the game engine. Animated objects automatically cycle through their available frames at a
* specified speed when animation is enabled.
*
* <p>The animation system works by:
* <ul>
* <li>Maintaining an internal timer that tracks elapsed time</li>
* <li>Automatically advancing to the next frame when enough time has passed</li>
* <li>Cycling through available frames in sequence</li>
* <li>Supporting both full texture animation and subset-based animation</li>
* </ul>
*
* <p>Animation can be controlled in real-time through enable/disable methods and speed adjustments.
* The animation automatically resets and pauses when disabled.
*
* <p>This interface extends {@link Updatable}, meaning animated objects are automatically
* updated each frame by the game engine's update loop.
*/
public interface Animated extends Updatable {
/**
* Returns whether animation is currently enabled for this object.
*
* <p>When animation is disabled, the object remains on its current frame
* and the internal animation timer is reset to zero.
*
* @return {@code true} if animation is enabled, {@code false} otherwise
*/
boolean isAnimationEnabled();
/**
* Enables or disables animation for this object.
*
* <p>When animation is disabled:
* <ul>
* <li>The object stops cycling through frames</li>
* <li>The current frame remains displayed</li>
* <li>The internal animation timer is reset to zero</li>
* </ul>
*
* <p>When animation is enabled, the object resumes cycling through frames
* at the configured animation speed.
*
* @param enabled {@code true} to enable animation, {@code false} to disable
*/
void setAnimationEnabled(boolean enabled);
/**
* Enables animation for this object.
*
* <p>This is a convenience method equivalent to calling
* {@code setAnimationEnabled(true)}.
*
* @see #setAnimationEnabled(boolean)
*/
default void enableAnimation() {
setAnimationEnabled(true);
}
/**
* Disables animation for this object.
*
* <p>This is a convenience method equivalent to calling
* {@code setAnimationEnabled(false)}. The object will remain on its
* current frame and the animation timer will be reset.
*
* @see #setAnimationEnabled(boolean)
*/
default void disableAnimation() {
setAnimationEnabled(false);
}
/**
* Toggles the animation enabled state.
*
* <p>If animation is currently enabled, it will be disabled.
* If animation is currently disabled, it will be enabled.
*
* @see #isAnimationEnabled()
* @see #setAnimationEnabled(boolean)
*/
default void toggleAnimationEnabled() {
setAnimationEnabled(!isAnimationEnabled());
}
/**
* Returns the current animation speed.
*
* <p>Animation speed determines how quickly the object cycles through its frames.
* Higher values result in faster animation, while lower values create slower animation.
*
* <p>The speed is typically measured in relation to the game engine's target
* update rate, where a speed of 1.0 represents normal timing.
* If 60PFS (target update rate) is maintained, it should be measured in frames per second.
*
* @return the current animation speed as a positive float value
*/
float getAnimationSpeed();
/**
* Sets the animation speed for this object.
*
* <p>Animation speed controls how quickly frames are cycled during animation.
* The speed value is typically:
* <ul>
* <li>1.0 for normal speed animation</li>
* <li>Greater than 1.0 for faster animation</li>
* <li>Between 0.0 and 1.0 for slower animation</li>
* <li>Values are automatically clamped to prevent invalid speeds</li>
* </ul>
*
* <p>The actual frame timing is calculated based on the game engine's target
* update rate and the specified speed multiplier.
* If 60PFS (target update rate) is maintained, it should be measured in frames per second.
*
* @param speed the new animation speed as a positive float value
* @throws IllegalArgumentException if speed is negative or zero
*/
void setAnimationSpeed(float speed);
/**
* Manually sets the current animation frame.
*
* <p>This method allows direct control over which frame is currently displayed,
* bypassing the automatic animation timing. The frame index is automatically
* wrapped if it exceeds the available frame count.
*
*
* <p>Frame indices are zero-based, and values that exceed the available frame
* count are automatically wrapped using modulo operation.
*
* @param frame the zero-based index of the frame to display
*/
void setAnimationFrame(int frame);
}

View File

@@ -1,31 +0,0 @@
package com.bartlomiejpluta.base.api.animation;
import com.bartlomiejpluta.base.api.color.Colorful;
import com.bartlomiejpluta.base.api.map.layer.base.Layer;
import com.bartlomiejpluta.base.api.move.Movable;
import com.bartlomiejpluta.base.internal.program.Updatable;
import com.bartlomiejpluta.base.internal.render.Renderable;
import com.bartlomiejpluta.base.util.path.Path;
import java.util.concurrent.CompletableFuture;
public interface Animation extends Movable, Animated, Colorful, Renderable, Updatable {
Integer getRepeat();
void setRepeat(Integer repeat);
void followPath(Path<Animation> path, Integer repeat, boolean finishOnEnd, boolean finishOnFail);
void onAdd(Layer layer);
void onFinish(Layer layer);
CompletableFuture<Animation> getFuture();
void finish();
boolean finished();
Layer getLayer();
}

View File

@@ -1,19 +0,0 @@
package com.bartlomiejpluta.base.api.audio;
public interface Sound {
void play();
void pause();
void stop();
boolean isPlaying();
boolean isPaused();
boolean isStopped();
void setGain(float gain);
void setRepeat(boolean repeat);
}

View File

@@ -1,20 +0,0 @@
package com.bartlomiejpluta.base.api.camera;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.screen.Screen;
import com.bartlomiejpluta.base.internal.object.Placeable;
import com.bartlomiejpluta.base.internal.render.BoundingBox;
import com.bartlomiejpluta.base.internal.render.ShaderManager;
import org.joml.Matrix4fc;
public interface Camera extends Placeable, BoundingBox {
Matrix4fc computeViewModelMatrix(Matrix4fc modelMatrix);
Matrix4fc getProjectionMatrix();
boolean insideFrustum(float x, float y, float radius);
boolean insideFrustum(Context context, float x, float y);
void render(Screen screen, ShaderManager shaderManager);
}

View File

@@ -1,111 +0,0 @@
package com.bartlomiejpluta.base.api.character;
import com.bartlomiejpluta.base.api.animation.Animated;
import com.bartlomiejpluta.base.api.color.Colorful;
import com.bartlomiejpluta.base.api.entity.Entity;
import com.bartlomiejpluta.base.api.move.Direction;
import com.bartlomiejpluta.base.api.move.Movable;
import com.bartlomiejpluta.base.api.move.Movement;
import java.util.concurrent.CompletableFuture;
/**
* Represents a game world object that can face four cardinal directions and uses
* a special texture format called a character set (charset).
*
* <p>A character set is a sprite sheet organized in a 4-row, multi-column format where:
* <ul>
* <li>Row 1: Graphics for facing DOWN</li>
* <li>Row 2: Graphics for facing LEFT</li>
* <li>Row 3: Graphics for facing RIGHT</li>
* <li>Row 4: Graphics for facing UP</li>
* </ul>
*
* <p>Each column represents a different animation frame or object variant. For animated
* characters, columns contain sequential animation frames. For static objects (like boxes),
* each column may represent different object types or states (e.g., normal box, colored box,
* damaged box).
*
* <p>This interface combines movement capabilities ({@link Movable}), animation features
* ({@link Animated}), coloring capabilities ({@link Colorful}), and entity properties ({@link Entity}) to create a complete
* character system for 2D games.
*/
public interface Character extends Movable, Animated, Colorful, Entity {
/**
* Initiates movement in the specified direction.
*
* <p>This method handles the character's movement mechanics and automatically
* updates the facing direction to match the movement direction.
*
* @param direction the direction to move towards
* @return a {@link Movement} object representing the movement operation
* @throws IllegalArgumentException if direction is null
*/
Movement move(Direction direction);
/**
* Returns the current facing direction of the character.
*
* <p>The facing direction determines which row of the character set
* is used for rendering and animation.
*
* @return the current facing direction
*/
Direction getFaceDirection();
/**
* Sets the default sprite column to use from the character set.
*
* <p>This method is particularly useful for character sets that contain
* multiple object variants in different columns. For example, if a character
* set contains different box types in each column, this method selects
* which box type to display.
*
* <p>The column index is zero-based and must be within the bounds of the
* current character set.
*
* @param column the zero-based column index to use as default
* @throws IndexOutOfBoundsException if the column index is invalid
*/
void setDefaultSpriteColumn(int column);
/**
* Changes the character's facing direction without initiating movement.
*
* <p>This method updates which row of the character set is used for rendering.
* It can be used to create custom animations by cycling through different
* facing directions, or to orient the character without moving.
*
* @param direction the new facing direction
* @throws IllegalArgumentException if direction is null
*/
void setFaceDirection(Direction direction);
/**
* Changes the character set (sprite sheet) used by this character.
*
* <p>This allows dynamic switching between different visual representations
* of the character, such as different costumes, character states, or
* completely different character types.
*
* @param characterSetUid the unique identifier of the new character set
* @throws IllegalArgumentException if characterSetUid is null or empty
*/
void changeCharacterSet(String characterSetUid);
/**
* Performs an instant animation using all frames from the current facing direction.
*
* <p>This method executes an animation sequence by cycling through all columns
* of the row corresponding to the character's current facing direction. For example,
* if the character is facing UP, the animation will use all frames from the fourth
* row of the character set.
*
* <p>The animation runs asynchronously and the returned {@link CompletableFuture}
* completes when the animation finishes.
*
* @return a {@link CompletableFuture} that completes when the animation finishes
*/
CompletableFuture<Void> performInstantAnimation();
}

View File

@@ -1,123 +0,0 @@
package com.bartlomiejpluta.base.api.color;
import org.joml.Vector3fc;
import org.joml.Vector4fc;
/**
* Interface for objects that can be colored and have their color properties modified.
* This interface provides a comprehensive API for managing RGBA color values using
* various input formats including JOML vectors, individual float components, and
* packed integer values.
*
* <p>Color components are expected to be in the range [0.0, 1.0] for float values,
* where 0.0 represents no intensity and 1.0 represents full intensity.</p>
*/
public interface Colorful {
/**
* Returns the current color of this object.
*
* @return a Vector4fc containing the RGBA color components, where:
* x = red, y = green, z = blue, w = alpha.
* All components are in the range [0.0, 1.0].
*/
Vector4fc getColor();
/**
* Sets the color of this object using a 4D vector.
*
* @param color a Vector4fc containing the RGBA color components, where:
* x = red, y = green, z = blue, w = alpha.
* All components should be in the range [0.0, 1.0].
* @throws NullPointerException if color is null
*/
void setColor(Vector4fc color);
/**
* Sets the RGB color components of this object using a 3D vector.
* The alpha component will remain unchanged or be set to a default value.
*
* @param color a Vector3fc containing the RGB color components, where:
* x = red, y = green, z = blue.
* All components should be in the range [0.0, 1.0].
* @throws NullPointerException if color is null
*/
void setColor(Vector3fc color);
/**
* Sets the RGBA color components of this object using individual float values.
*
* @param red the red component in the range [0.0, 1.0]
* @param green the green component in the range [0.0, 1.0]
* @param blue the blue component in the range [0.0, 1.0]
* @param alpha the alpha component in the range [0.0, 1.0]
*/
void setColor(float red, float green, float blue, float alpha);
/**
* Sets the RGB color components of this object using individual float values.
* The alpha component will remain unchanged or be set to a default value.
*
* @param red the red component in the range [0.0, 1.0]
* @param green the green component in the range [0.0, 1.0]
* @param blue the blue component in the range [0.0, 1.0]
*/
void setColor(float red, float green, float blue);
/**
* Sets the color of this object using a packed RGBA integer value.
* The integer is expected to be in RGBA format where each component
* occupies 8 bits in the range [0, 255] and is automatically converted
* to the [0.0, 1.0] range by dividing by 255.
*
* <p>The bit layout is as follows:</p>
* <ul>
* <li>Bits 24-31: Red component</li>
* <li>Bits 16-23: Green component</li>
* <li>Bits 8-15: Blue component</li>
* <li>Bits 0-7: Alpha component</li>
* </ul>
*
* <p>For example, {@code 0xFF0000FF} represents full red with full alpha,
* while green and blue are zero.</p>
*
* @param rgba the packed RGBA color value
*/
default void setColor(int rgba) {
setColor(
((rgba >>> 24) & 0xFF) / 255f,
((rgba >>> 16) & 0xFF) / 255f,
((rgba >>> 8) & 0xFF) / 255f,
(rgba & 0xFF) / 255f
);
}
/**
* Sets only the red color component of this object.
*
* @param red the red component in the range [0.0, 1.0]
*/
void setRed(float red);
/**
* Sets only the green color component of this object.
*
* @param green the green component in the range [0.0, 1.0]
*/
void setGreen(float green);
/**
* Sets only the blue color component of this object.
*
* @param blue the blue component in the range [0.0, 1.0]
*/
void setBlue(float blue);
/**
* Sets only the alpha (transparency) component of this object.
*
* @param alpha the alpha component in the range [0.0, 1.0], where
* 0.0 is fully transparent and 1.0 is fully opaque
*/
void setAlpha(float alpha);
}

View File

@@ -1,96 +0,0 @@
package com.bartlomiejpluta.base.api.context;
import com.bartlomiejpluta.base.api.animation.Animation;
import com.bartlomiejpluta.base.api.audio.Sound;
import com.bartlomiejpluta.base.api.camera.Camera;
import com.bartlomiejpluta.base.api.character.Character;
import com.bartlomiejpluta.base.api.entity.Entity;
import com.bartlomiejpluta.base.api.event.Event;
import com.bartlomiejpluta.base.api.event.EventType;
import com.bartlomiejpluta.base.api.gui.GUI;
import com.bartlomiejpluta.base.api.icon.Icon;
import com.bartlomiejpluta.base.api.image.Image;
import com.bartlomiejpluta.base.api.input.Input;
import com.bartlomiejpluta.base.api.light.Light;
import com.bartlomiejpluta.base.api.map.model.GameMap;
import com.bartlomiejpluta.base.api.runner.GameRunner;
import com.bartlomiejpluta.base.api.screen.Screen;
import com.bartlomiejpluta.base.internal.gc.Disposable;
import com.bartlomiejpluta.base.internal.program.Updatable;
import com.bartlomiejpluta.base.internal.render.Renderable;
import com.bartlomiejpluta.base.util.lambda.UncheckedConsumer;
import com.bartlomiejpluta.base.util.lambda.UncheckedFunction;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.function.Consumer;
public interface Context extends Updatable, Renderable, Disposable {
GameRunner getGameRunner();
Screen getScreen();
Camera getCamera();
Input getInput();
String getProjectName();
GameMap getMap();
void openMap(String mapUid);
void closeMap();
Character createCharacter(String characterSetUid);
Animation createAnimation(String animationUid);
Entity createAbstractEntity();
Light createLight();
Icon createIcon(String iconSetUid, int row, int column);
Image getImage(String imageUid);
GUI newGUI();
Sound createSound(String soundUid);
void withDatabase(UncheckedConsumer<Connection, SQLException> consumer);
<T> T withDatabase(UncheckedFunction<Connection, T, SQLException> extractor);
void disposeSound(Sound sound);
void playSound(String soundUid);
void playSound(String soundUid, float gain);
boolean isRunning();
void close();
boolean isPaused();
void setPaused(boolean paused);
void pause();
void resume();
boolean togglePause();
void resetMaps();
<E extends Event> void fireEvent(E event);
<E extends Event> void addEventListener(EventType<E> type, Consumer<E> listener);
<E extends Event> void removeEventListener(EventType<E> type, Consumer<E> listener);
void init(Screen screen, Input input, Camera camera);
void input(Input input);
}

View File

@@ -1,19 +0,0 @@
package com.bartlomiejpluta.base.api.context;
import lombok.Getter;
import lombok.NonNull;
public enum ContextHolder {
INSTANCE;
@Getter
private Context context;
public void setContext(@NonNull Context context) {
if(this.context != null) {
throw new IllegalStateException("Context cannot be reassigned");
}
this.context = context;
}
}

View File

@@ -1,19 +0,0 @@
package com.bartlomiejpluta.base.api.context;
import com.bartlomiejpluta.base.api.event.Event;
import com.bartlomiejpluta.base.api.event.EventType;
import com.bartlomiejpluta.base.lib.event.BaseEvent;
import lombok.EqualsAndHashCode;
import lombok.Value;
@Value
@EqualsAndHashCode(callSuper = true)
public class GamePauseEvent extends BaseEvent {
public static final EventType<GamePauseEvent> TYPE = new EventType<>("GAME_PAUSE_EVENT");
boolean paused;
@Override
public EventType<? extends Event> getType() {
return TYPE;
}
}

View File

@@ -1,31 +0,0 @@
package com.bartlomiejpluta.base.api.entity;
import com.bartlomiejpluta.base.api.event.Event;
import com.bartlomiejpluta.base.api.event.EventType;
import com.bartlomiejpluta.base.api.event.Reactive;
import com.bartlomiejpluta.base.api.location.Locationable;
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
import com.bartlomiejpluta.base.internal.program.Updatable;
import com.bartlomiejpluta.base.internal.render.Renderable;
import java.util.function.Consumer;
public interface Entity extends Locationable, Renderable, Updatable, Reactive {
boolean isBlocking();
void setBlocking(boolean blocking);
int getZIndex();
void setZIndex(int zIndex);
ObjectLayer getLayer();
void onAdd(ObjectLayer layer);
void onRemove(ObjectLayer layer);
<E extends Event> void addEventListener(EventType<E> type, Consumer<E> listener);
<E extends Event> void removeEventListener(EventType<E> type, Consumer<E> listener);
}

View File

@@ -1,9 +0,0 @@
package com.bartlomiejpluta.base.api.event;
public interface Event {
EventType<? extends Event> getType();
boolean isConsumed();
void consume();
}

View File

@@ -1,10 +0,0 @@
package com.bartlomiejpluta.base.api.event;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
@EqualsAndHashCode
@RequiredArgsConstructor
public final class EventType<E extends Event> {
private final String type;
}

View File

@@ -1,5 +0,0 @@
package com.bartlomiejpluta.base.api.event;
public interface Reactive {
<E extends Event> void handleEvent(E event);
}

View File

@@ -1,11 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
import java.lang.annotation.*;
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Attribute {
String value();
String separator() default "|";
}

View File

@@ -1,35 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
public interface Color {
void setRGB(float red, float green, float blue);
void setRGBA(float red, float green, float blue, float alpha);
void setRGB(int hex);
void setRGBA(long hex);
void setRed(float value);
void setGreen(float value);
void setBlue(float value);
void setAlpha(float value);
void setRed(int value);
void setGreen(int value);
void setBlue(int value);
void setAlpha(int value);
float getRed();
float getGreen();
float getBlue();
float getAlpha();
}

View File

@@ -1,17 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
import java.util.List;
public interface Component extends Widget {
List<Component> getChildren();
void add(Component component);
void remove(Component component);
boolean isFocused();
void focus();
void blur();
}

View File

@@ -1,6 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
public enum DisplayMode {
DISPLAY_STACK,
DISPLAY_TOP
}

View File

@@ -1,134 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
import com.bartlomiejpluta.base.internal.gc.Disposable;
import com.bartlomiejpluta.base.internal.program.Updatable;
import com.bartlomiejpluta.base.internal.render.Renderable;
public interface GUI extends Renderable, Updatable, Disposable {
int ALIGN_LEFT = 1 << 0;
int ALIGN_CENTER = 1 << 1;
int ALIGN_RIGHT = 1 << 2;
int ALIGN_TOP = 1 << 3;
int ALIGN_MIDDLE = 1 << 4;
int ALIGN_BOTTOM = 1 << 5;
int ALIGN_BASELINE = 1 << 6;
int IMAGE_GENERATE_MIPMAPS = 1 << 0;
int IMAGE_REPEAT_X = 1 << 1;
int IMAGE_REPEAT_Y = 1 << 2;
int IMAGE_FLIP_Y = 1 << 3;
int IMAGE_PREMULTIPLIED = 1 << 4;
int IMAGE_NEAREST = 1 << 5;
Component inflateComponent(String widgetUid);
<T extends Component> T inflateComponent(String widgetUid, Class<T> type);
Window inflateWindow(String widgetUid);
<T extends Window> T inflateWindow(String widgetUid, Class<T> type);
boolean isVisible();
void setVisible(boolean visible);
void show();
void hide();
Widget getRoot();
void setRoot(Widget root);
Color createColor();
Paint createPaint();
Image getImage(String imageUid);
Image getImage(String imageUid, int imageFlags);
IconSet getIconSet(String iconSetUid);
void beginPath();
void closePath();
void drawRectangle(float x, float y, float width, float height);
void drawRoundedRectangle(float x, float y, float width, float height, float radius);
void drawCircle(float x, float y, float radius);
void drawEllipse(float x, float y, float radiusX, float radiusY);
void drawArc(float x, float y, float radius, float start, float end, WindingDirection direction);
void drawArcTo(float x1, float y1, float x2, float y2, float radius);
void drawLineTo(float x, float y);
void drawBezierTo(float controlX1, float controlY1, float controlX2, float controlY2, float targetX, float targetY);
void drawQuadBezierTo(float controlX, float controlY, float targetX, float targetY);
void setLineCap(LineCap cap);
void setLineJoin(LineCap join);
void moveTo(float x, float y);
void setPathWinding(WindingDirection direction);
void setFontFace(String fontUid);
void setFontSize(float size);
void setFontBlur(float blur);
void setTextAlignment(int alignment);
void setTextLineHeight(float textLineHeight);
void putText(float x, float y, CharSequence text, float[] outTextBounds);
void putText(float x, float y, CharSequence text);
void putTextBox(float x, float y, float lineWidth, CharSequence text, float[] outTextBounds);
void putTextBox(float x, float y, float lineWidth, CharSequence text);
void setGlobalAlpha(float alpha);
void setFillColor(Color color);
void setFillPaint(Paint paint);
void fill();
void setStrokeColor(Color color);
void setStrokePaint(Paint paint);
void setStrokeWidth(float width);
void stroke();
void boxGradient(float x, float y, float width, float height, float radius, float feather, Color inner, Color outer, Paint target);
void linearGradient(float x, float y, float endX, float endY, Color start, Color end, Paint target);
void radialGradient(float x, float y, float innerRadius, float outerRadius, Color start, Color end, Paint target);
Paint imagePattern(float x, float y, float angle, float alpha, Image image, Paint target);
Paint imagePattern(float x, float y, float width, float height, float angle, float alpha, Image image, Paint target);
void image(float x, float y, float scaleX, float scaleY, float angle, float alpha, Image image, Paint target);
void icon(float x, float y, float scaleX, float scaleY, float angle, float alpha, IconSet iconSet, int row, int column, Paint target);
void clip(float x, float y, float width, float height);
void resetClip();
}

View File

@@ -1,15 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
public interface IconSet {
int getWidth();
int getHeight();
int getRows();
int getColumns();
int getIconHeight();
int getIconWidth();
}

View File

@@ -1,7 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
public interface Image {
int getWidth();
int getHeight();
}

View File

@@ -1,7 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
public interface Inflatable {
default void onInflate() {
// do nothing
}
}

View File

@@ -1,9 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
public enum LineCap {
BUTT,
ROUND,
SQUARE,
BEVEL,
MITER
}

View File

@@ -1,4 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
public interface Paint {
}

View File

@@ -1,12 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Ref {
String value();
}

View File

@@ -1,7 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
public enum SizeMode {
AUTO,
ABSOLUTE,
RELATIVE
}

View File

@@ -1,6 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
public enum UpdateMode {
UPDATE_ALL,
UPDATE_TOP
}

View File

@@ -1,95 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
import com.bartlomiejpluta.base.api.event.Reactive;
import com.bartlomiejpluta.base.api.screen.Screen;
import com.bartlomiejpluta.base.internal.program.Updatable;
public interface Widget extends Updatable, Reactive {
Widget getParent();
void setParent(Widget parent);
float getX();
float getY();
void setX(Float x);
void setY(Float y);
void setPosition(Float x, Float y);
float getWidth();
float getActualWidth();
float getHeight();
float getActualHeight();
void setWidth(Float width);
void setHeight(Float height);
void setSize(Float width, Float height);
SizeMode getWidthMode();
void setWidthMode(SizeMode mode);
SizeMode getHeightMode();
void setHeightMode(SizeMode mode);
void setSizeMode(SizeMode widthMode, SizeMode heightMode);
void setMargin(Float top, Float right, Float bottom, Float left);
void setMargin(Float top, Float rightLeft, Float bottom);
void setMargin(Float topBottom, Float rightLeft);
void setMargin(Float all);
void setMarginTop(Float margin);
void setMarginRight(Float margin);
void setMarginBottom(Float margin);
void setMarginLeft(Float margin);
float getMarginTop();
float getMarginRight();
float getMarginBottom();
float getMarginLeft();
void setPadding(Float top, Float right, Float bottom, Float left);
void setPadding(Float top, Float rightLeft, Float bottom);
void setPadding(Float topBottom, Float rightLeft);
void setPadding(Float all);
void setPaddingTop(Float padding);
void setPaddingRight(Float padding);
void setPaddingBottom(Float padding);
void setPaddingLeft(Float padding);
float getPaddingTop();
float getPaddingRight();
float getPaddingBottom();
float getPaddingLeft();
void draw(Screen screen, GUI gui);
}

View File

@@ -1,6 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
public enum WindingDirection {
CLOCKWISE,
COUNTER_CLOCKWISE
}

View File

@@ -1,25 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
import java.util.concurrent.CompletableFuture;
public interface Window extends Widget {
void setContent(Component component);
WindowPosition getWindowPosition();
void setWindowPosition(WindowPosition windowPosition);
Component reference(String ref);
<T extends Component> T reference(String ref, Class<T> type);
CompletableFuture<Object> getFuture();
default void onOpen(WindowManager manager, Object[] args) {
// do nothing
}
default void onClose(WindowManager manager) {
// do nothing
}
}

View File

@@ -1,233 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.input.Input;
import com.bartlomiejpluta.base.api.input.KeyEvent;
import com.bartlomiejpluta.base.api.screen.Screen;
import com.bartlomiejpluta.base.lib.gui.BaseWidget;
import lombok.NonNull;
import lombok.Setter;
import java.util.Deque;
import java.util.LinkedList;
import java.util.concurrent.CompletableFuture;
public final class WindowManager extends BaseWidget {
private final Input input;
private final Deque<Window> windows = new LinkedList<>();
@NonNull
@Setter
private DisplayMode displayMode;
@NonNull
@Setter
private UpdateMode updateMode;
public WindowManager(Context context) {
this(context, DisplayMode.DISPLAY_STACK, UpdateMode.UPDATE_ALL);
}
public WindowManager(@NonNull Context context, @NonNull DisplayMode displayMode, @NonNull UpdateMode updateMode) {
this.input = context.getInput();
super.setSizeMode(SizeMode.RELATIVE, SizeMode.RELATIVE);
super.setSize(1f, 1f);
this.displayMode = displayMode;
this.updateMode = updateMode;
}
@Override
protected final float getContentWidth() {
return 0;
}
@Override
protected final float getContentHeight() {
return 0;
}
@Override
public final void setSizeMode(SizeMode widthMode, SizeMode heightMode) {
throw new UnsupportedOperationException("Window Manager is hardcoded to be of MATCH_PARENT mode");
}
@Override
public final void setWidthMode(SizeMode mode) {
throw new UnsupportedOperationException("Window Manager is hardcoded to be of MATCH_PARENT mode");
}
@Override
public final void setHeightMode(SizeMode mode) {
throw new UnsupportedOperationException("Window Manager is hardcoded to be of MATCH_PARENT mode");
}
@Override
@Attribute("width")
public void setWidth(String width) {
super.setWidth(width);
if (widthMode == SizeMode.AUTO) {
throw new IllegalStateException("Border layout does not support AUTO sizing mode");
}
}
@Override
@Attribute("height")
public void setHeight(String height) {
super.setHeight(height);
if (heightMode == SizeMode.AUTO) {
throw new IllegalStateException("Border layout does not support AUTO sizing mode");
}
}
public CompletableFuture<Object> open(@NonNull Window window, Object... args) {
if (windows.isEmpty()) {
input.addKeyEventHandler(this::forwardKeyEventToTopWindow);
}
windows.addLast(window);
window.setParent(this);
window.onOpen(this, args != null ? args : new Object[]{});
return window.getFuture();
}
public <T> CompletableFuture<T> openForResult(Class<T> resultClass, @NonNull Window window, Object... args) {
return open(window, args).thenApply(resultClass::cast);
}
private void forwardKeyEventToTopWindow(KeyEvent event) {
var topWindow = windows.peekLast();
if (topWindow != null) {
topWindow.handleEvent(event);
}
}
public void closeAll() {
// Use iterator to support removal from loop inside
for (var iterator = windows.iterator(); iterator.hasNext(); ) {
close();
}
}
public void resolve(Object result) {
if (windows.isEmpty()) {
return;
}
var window = windows.removeLast();
window.setParent(null);
window.onClose(this);
if (windows.isEmpty()) {
input.removeKeyEventHandler(this::forwardKeyEventToTopWindow);
}
window.getFuture().complete(result == null ? window : result);
}
public void close() {
resolve(null);
}
public int size() {
return windows.size();
}
public boolean isEmpty() {
return windows.isEmpty();
}
public Window top() {
return windows.peekLast();
}
@Override
public void update(float dt) {
switch (updateMode) {
case UPDATE_ALL -> {
for (var window : windows) {
window.update(dt);
}
}
case UPDATE_TOP -> {
var topWindow = windows.peekLast();
if (topWindow != null) {
topWindow.update(dt);
}
}
}
}
@Override
public void draw(Screen screen, GUI gui) {
switch (displayMode) {
case DISPLAY_STACK -> {
for (var window : windows) {
drawWindow(screen, window, gui);
}
}
case DISPLAY_TOP -> {
var topWindow = windows.peekLast();
if (topWindow != null) {
drawWindow(screen, topWindow, gui);
}
}
}
}
private void drawWindow(Screen screen, Window window, GUI gui) {
switch (window.getWindowPosition()) {
case TOP -> window.setPosition(
(screen.getWidth() - window.getWidth()) / 2,
window.getMarginTop()
);
case TOP_RIGHT -> window.setPosition(
screen.getWidth() - window.getWidth() - window.getMarginRight(),
window.getMarginTop()
);
case RIGHT -> window.setPosition(
screen.getWidth() - window.getWidth() - window.getMarginRight(),
(screen.getHeight() - window.getHeight()) / 2
);
case BOTTOM_RIGHT -> window.setPosition(
screen.getWidth() - window.getWidth() - window.getMarginRight(),
screen.getHeight() - window.getHeight() - window.getMarginBottom()
);
case BOTTOM -> window.setPosition(
(screen.getWidth() - window.getWidth()) / 2,
screen.getHeight() - window.getHeight() - window.getMarginBottom()
);
case BOTTOM_LEFT -> window.setPosition(
window.getMarginLeft(),
screen.getHeight() - window.getHeight() - window.getMarginBottom()
);
case LEFT -> window.setPosition(
window.getMarginLeft(),
(screen.getHeight() - window.getHeight()) / 2
);
case TOP_LEFT -> window.setPosition(
window.getMarginLeft(),
window.getMarginTop()
);
case CENTER -> window.setPosition(
(screen.getWidth() - window.getWidth()) / 2,
(screen.getHeight() - window.getHeight()) / 2
);
}
window.draw(screen, gui);
}
}

View File

@@ -1,13 +0,0 @@
package com.bartlomiejpluta.base.api.gui;
public enum WindowPosition {
TOP,
TOP_RIGHT,
RIGHT,
BOTTOM_RIGHT,
BOTTOM,
BOTTOM_LEFT,
LEFT,
TOP_LEFT,
CENTER
}

View File

@@ -1,16 +0,0 @@
package com.bartlomiejpluta.base.api.icon;
import com.bartlomiejpluta.base.api.color.Colorful;
import com.bartlomiejpluta.base.api.entity.Entity;
public interface Icon extends Colorful, Entity {
void changeIcon(int row, int column);
void changeIcon(String iconSetUid, int row, int column);
String getIconSetUid();
int getIconSetRow();
int getIconSetColumn();
}

View File

@@ -1,20 +0,0 @@
package com.bartlomiejpluta.base.api.image;
import com.bartlomiejpluta.base.internal.object.Placeable;
import com.bartlomiejpluta.base.internal.render.Renderable;
public interface Image extends Placeable, Renderable {
int getPrimaryWidth();
int getPrimaryHeight();
int getFactor();
int getWidth();
int getHeight();
void setOpacity(float opacity);
float getOpacity();
}

View File

@@ -1,11 +0,0 @@
package com.bartlomiejpluta.base.api.input;
import java.util.function.Consumer;
public interface Input {
boolean isKeyPressed(Key key);
void addKeyEventHandler(Consumer<KeyEvent> handler);
void removeKeyEventHandler(Consumer<KeyEvent> handler);
}

View File

@@ -1,125 +0,0 @@
package com.bartlomiejpluta.base.api.input;
public enum Key {
KEY_SPACE,
KEY_APOSTROPHE,
KEY_COMMA,
KEY_MINUS,
KEY_PERIOD,
KEY_SLASH,
KEY_0,
KEY_1,
KEY_2,
KEY_3,
KEY_4,
KEY_5,
KEY_6,
KEY_7,
KEY_8,
KEY_9,
KEY_SEMICOLON,
KEY_EQUAL,
KEY_A,
KEY_B,
KEY_C,
KEY_D,
KEY_E,
KEY_F,
KEY_G,
KEY_H,
KEY_I,
KEY_J,
KEY_K,
KEY_L,
KEY_M,
KEY_N,
KEY_O,
KEY_P,
KEY_Q,
KEY_R,
KEY_S,
KEY_T,
KEY_U,
KEY_V,
KEY_W,
KEY_X,
KEY_Y,
KEY_Z,
KEY_LEFT_BRACKET,
KEY_BACKSLASH,
KEY_RIGHT_BRACKET,
KEY_GRAVE_ACCENT,
KEY_WORLD_1,
KEY_WORLD_2,
KEY_ESCAPE,
KEY_ENTER,
KEY_TAB,
KEY_BACKSPACE,
KEY_INSERT,
KEY_DELETE,
KEY_RIGHT,
KEY_LEFT,
KEY_DOWN,
KEY_UP,
KEY_PAGE_UP,
KEY_PAGE_DOWN,
KEY_HOME,
KEY_END,
KEY_CAPS_LOCK,
KEY_SCROLL_LOCK,
KEY_NUM_LOCK,
KEY_PRINT_SCREEN,
KEY_PAUSE,
KEY_F1,
KEY_F2,
KEY_F3,
KEY_F4,
KEY_F5,
KEY_F6,
KEY_F7,
KEY_F8,
KEY_F9,
KEY_F10,
KEY_F11,
KEY_F12,
KEY_F13,
KEY_F14,
KEY_F15,
KEY_F16,
KEY_F17,
KEY_F18,
KEY_F19,
KEY_F20,
KEY_F21,
KEY_F22,
KEY_F23,
KEY_F24,
KEY_F25,
KEY_KP_0,
KEY_KP_1,
KEY_KP_2,
KEY_KP_3,
KEY_KP_4,
KEY_KP_5,
KEY_KP_6,
KEY_KP_7,
KEY_KP_8,
KEY_KP_9,
KEY_KP_DECIMAL,
KEY_KP_DIVIDE,
KEY_KP_MULTIPLY,
KEY_KP_SUBTRACT,
KEY_KP_ADD,
KEY_KP_ENTER,
KEY_KP_EQUAL,
KEY_LEFT_SHIFT,
KEY_LEFT_CONTROL,
KEY_LEFT_ALT,
KEY_LEFT_SUPER,
KEY_RIGHT_SHIFT,
KEY_RIGHT_CONTROL,
KEY_RIGHT_ALT,
KEY_RIGHT_SUPER,
KEY_MENU,
KEY_LAST
}

View File

@@ -1,7 +0,0 @@
package com.bartlomiejpluta.base.api.input;
public enum KeyAction {
PRESS,
RELEASE,
REPEAT
}

View File

@@ -1,12 +0,0 @@
package com.bartlomiejpluta.base.api.input;
import com.bartlomiejpluta.base.api.event.Event;
import com.bartlomiejpluta.base.api.event.EventType;
public interface KeyEvent extends Event {
Key getKey();
KeyAction getAction();
EventType<KeyEvent> TYPE = new EventType<>("KEY_EVENT");
}

View File

@@ -1,56 +0,0 @@
package com.bartlomiejpluta.base.api.light;
import com.bartlomiejpluta.base.api.location.Locationable;
import com.bartlomiejpluta.base.internal.program.Updatable;
import com.bartlomiejpluta.base.internal.render.Renderable;
import org.joml.Vector3fc;
public interface Light extends Locationable, Updatable, Renderable {
boolean isLuminescent();
void setLuminescent(boolean luminescent);
default void enableLuminescent() {
setLuminescent(true);
}
default void disableLuminescent() {
setLuminescent(false);
}
default void toggleLuminescent() {
setLuminescent(!isLuminescent());
}
Vector3fc getIntensity();
default void setIntensity(Vector3fc intensity) {
setIntensity(intensity.x(), intensity.y(), intensity.z());
}
void setIntensity(float red, float green, float blue);
float getConstantAttenuation();
float getLinearAttenuation();
float getQuadraticAttenuation();
void setConstantAttenuation(float attenuation);
void setLinearAttenuation(float attenuation);
void setQuadraticAttenuation(float attenuation);
default void setAttenuation(float constant, float linear, float quadratic) {
setConstantAttenuation(constant);
setLinearAttenuation(linear);
setQuadraticAttenuation(quadratic);
}
default void setAttenuation(Vector3fc attenuation) {
setConstantAttenuation(attenuation.x());
setLinearAttenuation(attenuation.y());
setQuadraticAttenuation(attenuation.z());
}
}

View File

@@ -1,36 +0,0 @@
package com.bartlomiejpluta.base.api.location;
import com.bartlomiejpluta.base.api.move.Direction;
import com.bartlomiejpluta.base.internal.object.Placeable;
import org.joml.Vector2fc;
import org.joml.Vector2ic;
public interface Locationable extends Placeable {
void setStepSize(float x, float y);
Vector2ic getCoordinates();
void setCoordinates(Vector2ic coordinates);
void setCoordinates(int x, int y);
Vector2fc getPositionOffset();
void setPositionOffset(Vector2fc offset);
void setPositionOffset(float offsetX, float offsetY);
Direction getDirectionTowards(Locationable target);
int chebyshevDistance(Vector2ic coordinates);
int manhattanDistance(Vector2ic coordinates);
double euclideanDistance(Vector2ic coordinates);
int chebyshevDistance(Locationable other);
int manhattanDistance(Locationable other);
double euclideanDistance(Locationable other);
}

View File

@@ -1,28 +0,0 @@
package com.bartlomiejpluta.base.api.map.handler;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.input.Input;
import com.bartlomiejpluta.base.api.map.model.GameMap;
import com.bartlomiejpluta.base.api.screen.Screen;
public interface MapHandler {
default void onCreate(Context context, GameMap map) {
// do nothing
}
default void onOpen(Context context, GameMap map) {
// do nothing
}
default void input(Input input) {
// do nothing
}
default void update(Context context, GameMap map, float dt) {
// do nothing
}
default void postRender(Screen screen) {
// do nothing
}
}

View File

@@ -1,7 +0,0 @@
package com.bartlomiejpluta.base.api.map.layer.autotile;
import com.bartlomiejpluta.base.api.map.layer.base.Layer;
public interface AutoTileLayer extends Layer {
}

View File

@@ -1,24 +0,0 @@
package com.bartlomiejpluta.base.api.map.layer.base;
import com.bartlomiejpluta.base.api.animation.Animation;
import com.bartlomiejpluta.base.api.event.Reactive;
import com.bartlomiejpluta.base.api.light.Light;
import com.bartlomiejpluta.base.api.map.model.GameMap;
import com.bartlomiejpluta.base.internal.program.Updatable;
import com.bartlomiejpluta.base.internal.render.Renderable;
import java.util.List;
public interface Layer extends Renderable, Updatable, Reactive {
GameMap getMap();
void pushAnimation(Animation animation);
void addLight(Light light);
void removeLight(Light light);
void clearLights();
List<Light> getLights();
}

View File

@@ -1,19 +0,0 @@
package com.bartlomiejpluta.base.api.map.layer.color;
import com.bartlomiejpluta.base.api.map.layer.base.Layer;
import com.bartlomiejpluta.base.internal.object.Placeable;
import com.bartlomiejpluta.base.internal.render.Renderable;
public interface ColorLayer extends Placeable, Renderable, Layer {
void setColor(float red, float green, float blue, float alpha);
void setColor(float red, float green, float blue);
void setRed(float red);
void setGreen(float green);
void setBlue(float blue);
void setAlpha(float alpha);
}

View File

@@ -1,18 +0,0 @@
package com.bartlomiejpluta.base.api.map.layer.image;
import com.bartlomiejpluta.base.api.image.Image;
import com.bartlomiejpluta.base.api.map.layer.base.Layer;
public interface ImageLayer extends Layer {
void setImage(Image image);
void setOpacity(float opacity);
void setPosition(float x, float y);
void setMode(ImageLayerMode mode);
void setScale(float scaleX, float scaleY);
void setParallax(boolean parallax);
}

View File

@@ -1,7 +0,0 @@
package com.bartlomiejpluta.base.api.map.layer.image;
public enum ImageLayerMode {
NORMAL,
FIT_MAP,
FIT_SCREEN
}

View File

@@ -1,20 +0,0 @@
package com.bartlomiejpluta.base.api.map.layer.object;
import lombok.Builder;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import org.joml.Vector2i;
import org.joml.Vector2ic;
@Value
@Builder
public class MapPin {
String map;
int layer;
int x;
int y;
public Vector2ic toCoordinates() {
return new Vector2i(x, y);
}
}

View File

@@ -1,27 +0,0 @@
package com.bartlomiejpluta.base.api.map.layer.object;
import com.bartlomiejpluta.base.api.entity.Entity;
import com.bartlomiejpluta.base.api.event.Reactive;
import com.bartlomiejpluta.base.api.map.layer.base.Layer;
import com.bartlomiejpluta.base.api.move.Movement;
import org.joml.Vector2ic;
import java.util.List;
public interface ObjectLayer extends Layer, Reactive {
void addEntity(Entity entity);
void removeEntity(Entity entity);
void clearEntities();
List<Entity> getEntities();
void setPassageAbility(int row, int column, PassageAbility passageAbility);
PassageAbility[][] getPassageMap();
boolean isTileReachable(Vector2ic tileCoordinates);
void pushMovement(Movement movement);
}

View File

@@ -1,6 +0,0 @@
package com.bartlomiejpluta.base.api.map.layer.object;
public enum PassageAbility {
BLOCK,
ALLOW
}

View File

@@ -1,9 +0,0 @@
package com.bartlomiejpluta.base.api.map.layer.tile;
import com.bartlomiejpluta.base.api.map.layer.base.Layer;
public interface TileLayer extends Layer {
void setTile(int row, int column, int tileId);
void clearTile(int row, int column);
}

View File

@@ -1,42 +0,0 @@
package com.bartlomiejpluta.base.api.map.model;
import com.bartlomiejpluta.base.api.event.Reactive;
import com.bartlomiejpluta.base.api.map.layer.base.Layer;
import com.bartlomiejpluta.base.api.map.layer.color.ColorLayer;
import com.bartlomiejpluta.base.api.map.layer.image.ImageLayer;
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
import com.bartlomiejpluta.base.api.map.layer.tile.TileLayer;
import org.joml.Vector2fc;
import org.joml.Vector3fc;
public interface GameMap extends Reactive {
float getWidth();
float getHeight();
int getRows();
int getColumns();
Vector2fc getSize();
Vector2fc getStepSize();
Layer getLayer(int layerIndex);
TileLayer getTileLayer(int layerIndex);
ImageLayer getImageLayer(int layerIndex);
ColorLayer getColorLayer(int layerIndex);
ObjectLayer getObjectLayer(int layerIndex);
Vector3fc getAmbientColor();
default void setAmbientColor(Vector3fc color) {
setAmbientColor(color.x(), color.y(), color.z());
}
void setAmbientColor(float red, float green, float blue);
}

View File

@@ -1,40 +0,0 @@
package com.bartlomiejpluta.base.api.move;
import com.bartlomiejpluta.base.api.animation.Animation;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import org.joml.Vector2i;
import org.joml.Vector2ic;
@Getter
@EqualsAndHashCode
public final class AnimationMovement implements Movement {
private final Animation object;
private final Direction direction;
private final Vector2ic from;
private final Vector2ic to;
public AnimationMovement(@NonNull Animation object, @NonNull Direction direction) {
this.object = object;
this.direction = direction;
this.from = object.getCoordinates();
this.to = direction.vector.add(object.getCoordinates(), new Vector2i());
}
@Override
public boolean perform() {
return object.move(this);
}
@Override
public void abort() {
object.abortMove();
}
@Override
public void onFinish() {
// do nothing
}
}

View File

@@ -1,45 +0,0 @@
package com.bartlomiejpluta.base.api.move;
import com.bartlomiejpluta.base.api.character.Character;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import org.joml.Vector2i;
import org.joml.Vector2ic;
@Getter
@EqualsAndHashCode
public final class CharacterMovement implements Movement {
private final Character object;
private final Direction direction;
private final Vector2ic from;
private final Vector2ic to;
public CharacterMovement(@NonNull Character object, @NonNull Direction direction) {
this.object = object;
this.direction = direction;
this.from = object.getCoordinates();
this.to = direction.vector.add(object.getCoordinates(), new Vector2i());
}
@Override
public boolean perform() {
var result = object.move(this);
if (result) {
object.getLayer().handleEvent(new MoveEvent(MoveEvent.Action.BEGIN, object, this));
}
return result;
}
@Override
public void abort() {
object.abortMove();
}
@Override
public void onFinish() {
object.getLayer().handleEvent(new MoveEvent(MoveEvent.Action.END, object, this));
}
}

View File

@@ -1,84 +0,0 @@
package com.bartlomiejpluta.base.api.move;
import org.joml.Vector2i;
import org.joml.Vector2ic;
import java.util.Map;
import static java.lang.Math.PI;
import static org.joml.Math.atan2;
public enum Direction {
RIGHT(1, 0, 0),
UP(0, -1, 90),
LEFT(-1, 0, 180),
DOWN(0, 1, 270);
private static final Map<Direction, Direction[]> PERPENDICULARS = Map.of(
Direction.UP, new Direction[] { Direction.LEFT, Direction.RIGHT },
Direction.DOWN, new Direction[] { Direction.LEFT, Direction.RIGHT },
Direction.LEFT, new Direction[] { Direction.UP, Direction.DOWN },
Direction.RIGHT, new Direction[] { Direction.UP, Direction.DOWN }
);
public final int x;
public final int y;
public final int xAngle;
public final Vector2ic vector;
Direction(int x, int y, int xAngle) {
this.x = x;
this.y = y;
this.xAngle = xAngle;
this.vector = new Vector2i(x, y);
}
public Direction opposite() {
return switch (this) {
case UP -> DOWN;
case RIGHT -> LEFT;
case DOWN -> UP;
case LEFT -> RIGHT;
};
}
public Direction[] perpendiculars() {
return PERPENDICULARS.get(this);
}
public static Direction ofVector(Vector2ic vector) {
// X Versor = [1, 0]
// dot = 1 * vector.x + 0 * vector.y = vector.x
// det = 1 * vector.y - 0 * vector.x = vector.y
// angle = atan2(det, dot) = atan2(vector.y, vector.x)
float angle = atan2(vector.y(), vector.x());
if (-PI / 4 < angle && angle < PI / 4) {
return RIGHT;
} else if (PI / 4 <= angle && angle <= 3 * PI / 4) {
return DOWN;
} else if (3 * PI / 4 < angle && angle < 5 * PI / 4) {
return LEFT;
} else {
return UP;
}
}
public static Direction ofVector(int x, int y) {
// X Versor = [1, 0]
// dot = 1 * vector.x + 0 * vector.y = vector.x
// det = 1 * vector.y - 0 * vector.x = vector.y
// angle = atan2(det, dot) = atan2(vector.y, vector.x)
float angle = atan2(y, x);
if (-PI / 4 < angle && angle < PI / 4) {
return RIGHT;
} else if (PI / 4 <= angle && angle <= 3 * PI / 4) {
return DOWN;
} else if (3 * PI / 4 < angle && angle < 5 * PI / 4) {
return LEFT;
} else {
return UP;
}
}
}

View File

@@ -1,17 +0,0 @@
package com.bartlomiejpluta.base.api.move;
import com.bartlomiejpluta.base.api.location.Locationable;
public interface Movable extends Locationable {
void setSpeed(float speed);
Movement prepareMovement(Direction direction);
Movement getMovement();
boolean isMoving();
boolean move(Movement movement);
void abortMove();
}

View File

@@ -1,24 +0,0 @@
package com.bartlomiejpluta.base.api.move;
import com.bartlomiejpluta.base.api.event.Event;
import com.bartlomiejpluta.base.api.event.EventType;
import com.bartlomiejpluta.base.lib.event.BaseEvent;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public final class MoveEvent extends BaseEvent {
public enum Action {BEGIN, END}
private final Action action;
private final Movable movable;
private final Movement movement;
@Override
public EventType<? extends Event> getType() {
return TYPE;
}
public static EventType<MoveEvent> TYPE = new EventType<>("MOVE_EVENT");
}

View File

@@ -1,18 +0,0 @@
package com.bartlomiejpluta.base.api.move;
import org.joml.Vector2ic;
public interface Movement {
Vector2ic getFrom();
Vector2ic getTo();
Direction getDirection();
boolean perform();
void abort();
void onFinish();
}

View File

@@ -1,24 +0,0 @@
package com.bartlomiejpluta.base.api.runner;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.input.Input;
import com.bartlomiejpluta.base.internal.gc.Disposable;
public interface GameRunner extends Disposable {
default void init(Context context) {
// do nothing
}
default void input(Input input) {
// do nothing
}
default void update(float dt) {
// do nothing
}
@Override
default void dispose() {
// do nothing
}
}

View File

@@ -1,46 +0,0 @@
package com.bartlomiejpluta.base.api.screen;
import org.joml.Vector2fc;
import org.joml.Vector2ic;
public interface Screen {
int getWidth();
int getHeight();
Vector2fc getSize();
boolean isResized();
Vector2ic getCurrentResolution();
void setResized(boolean resized);
void setFullscreenMode();
void setFullscreenMode(int width, int height);
void setWindowMode(int xPos, int yPos, int width, int height);
void setPosition(int x, int y);
void setSize(int width, int height);
void setResizable(boolean resizable);
void show();
void hide();
boolean shouldClose();
void update();
void clear(float r, float g, float b, float alpha);
void restoreState();
void init();
long getID();
}

View File

@@ -1,7 +0,0 @@
package com.bartlomiejpluta.base.internal.map;
import com.bartlomiejpluta.base.api.context.Context;
public interface MapInitializer {
void initialize(Context context);
}

View File

@@ -1,38 +0,0 @@
package com.bartlomiejpluta.base.internal.object;
import org.joml.Matrix4fc;
import org.joml.Vector2fc;
public interface Placeable {
Vector2fc getPosition();
void setPosition(float x, float y);
void setPosition(Vector2fc position);
void movePosition(float x, float y);
void movePosition(Vector2fc position);
float getRotation();
void setRotation(float rotation);
void moveRotation(float rotation);
float getScaleX();
void setScaleX(float scale);
float getScaleY();
void setScaleY(float scale);
void setScale(float scale);
void setScale(float scaleX, float scaleY);
float euclideanDistance(Placeable other);
Matrix4fc getModelMatrix();
}

View File

@@ -1,27 +0,0 @@
package com.bartlomiejpluta.base.internal.render;
import javax.swing.*;
public interface BoundingBox {
float getMinX();
float getMaxX();
float getMinY();
float getMaxY();
default boolean containsBox(float minX, float maxX, float minY, float maxY) {
return !(this.getMaxX() < minX ||
this.getMinX() > maxX ||
this.getMaxY() < minY ||
this.getMinY() > maxY);
}
default boolean containsBox(BoundingBox box) {
return !(this.getMaxX() < box.getMinX() ||
this.getMinX() > box.getMaxX() ||
this.getMaxY() < box.getMinY() ||
this.getMinY() > box.getMaxY());
}
}

View File

@@ -1,8 +0,0 @@
package com.bartlomiejpluta.base.internal.render;
import com.bartlomiejpluta.base.api.camera.Camera;
import com.bartlomiejpluta.base.api.screen.Screen;
public interface Renderable {
void render(Screen screen, Camera camera, ShaderManager shaderManager);
}

View File

@@ -1,40 +0,0 @@
package com.bartlomiejpluta.base.internal.render;
import com.bartlomiejpluta.base.internal.gc.Disposable;
import org.joml.*;
public interface ShaderProgram extends Disposable {
void createUniform(String uniformName);
void createUniform(String uniformName, Uniform uniform);
void createUniforms(String uniformName, int size);
void createUniforms(String uniformName, int size, Uniform uniform);
void setUniform(String uniformName, int value);
void setUniform(String uniformName, boolean value);
void setUniform(String uniformName, float value);
void setUniform(String uniformName, Vector2fc value);
void setUniform(String uniformName, Vector3fc value);
void setUniform(String uniformName, Vector4fc value);
void setUniform(String uniformName, Matrix3fc value);
void setUniform(String uniformName, Matrix4fc value);
void setUniform(String uniformName, Uniform uniform);
void setUniform(String uniformName, int index, Uniform uniform);
void setUniforms(String uniformName, Uniform[] uniforms);
void use();
void detach();
}

View File

@@ -1,110 +0,0 @@
package com.bartlomiejpluta.base.lib.ai;
import com.bartlomiejpluta.base.api.ai.AI;
import com.bartlomiejpluta.base.api.ai.NPC;
import com.bartlomiejpluta.base.api.entity.Entity;
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
import com.bartlomiejpluta.base.api.move.MoveEvent;
import com.bartlomiejpluta.base.util.path.MovementPath;
import com.bartlomiejpluta.base.util.path.PathExecutor;
import com.bartlomiejpluta.base.util.pathfinder.PathFinder;
import lombok.NonNull;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public abstract class FollowObjectAI<N extends NPC, T extends Entity> implements AI {
private final PathFinder finder;
private final PathExecutor<N> executor;
private final N npc;
@Setter(onParam = @__(@NonNull))
private T target;
private MovementPath<N> path = null;
private boolean sees = false;
protected FollowObjectAI(@NonNull PathFinder finder, @NonNull N npc, @NonNull T target) {
this.finder = finder;
this.npc = npc;
this.target = target;
this.executor = new PathExecutor<>(npc);
npc.addEventListener(MoveEvent.TYPE, this::onMove);
}
private void onMove(MoveEvent event) {
if (npc.getStrategy() != this) {
return;
}
var movable = event.getMovable();
if (movable == npc) {
this.sees = sees(npc, target);
return;
}
// Recalculate path only when target has been displaced
// or another object is blocking current path
if (movable == target) {
this.path = null;
this.sees = sees(npc, target);
return;
}
// Recalculate path when another object is blocking current path
if (path != null && path.contains(movable)) {
this.path = null;
}
}
@Override
public void nextActivity(ObjectLayer layer, float dt) {
if (npc.isMoving()) {
return;
}
var distance = npc.manhattanDistance(target);
if (distance == 1) {
npc.setFaceDirection(npc.getDirectionTowards(target));
interact(npc, target, layer, dt);
return;
}
if (sees) {
follow(npc, target, layer, dt);
// Calculate path
if (path == null || path.isEmpty()) {
path = finder.findPath(layer, npc, target.getCoordinates());
executor.setPath(path);
}
executor.execute(layer, dt);
return;
}
idle(npc, target, layer, dt);
}
protected abstract boolean sees(N npc, T target);
protected void interact(N npc, T target, ObjectLayer layer, float dt) {
}
protected void follow(N npc, T target, ObjectLayer layer, float dt) {
}
protected void idle(N npc, T target, ObjectLayer layer, float dt) {
}
}

View File

@@ -1,40 +0,0 @@
package com.bartlomiejpluta.base.lib.ai;
import com.bartlomiejpluta.base.api.ai.AI;
import com.bartlomiejpluta.base.api.ai.NPC;
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
import com.bartlomiejpluta.base.util.path.Path;
import com.bartlomiejpluta.base.util.path.PathExecutor;
import com.bartlomiejpluta.base.util.path.PathProgress;
import lombok.Getter;
public class FollowPathAI<T extends NPC> implements AI {
private final PathExecutor<T> executor;
@Getter
private PathProgress progress = PathProgress.NOT_STARTED;
public FollowPathAI(T npc) {
this.executor = new PathExecutor<>(npc);
}
public FollowPathAI<T> setPath(Path<T> path) {
executor.setPath(path);
return this;
}
public FollowPathAI<T> setRepeat(Integer repeat) {
executor.setRepeat(repeat);
return this;
}
public FollowPathAI<T> reset() {
executor.reset();
return this;
}
@Override
public void nextActivity(ObjectLayer layer, float dt) {
progress = executor.execute(layer, dt);
}
}

View File

@@ -1,172 +0,0 @@
package com.bartlomiejpluta.base.lib.ai;
import com.bartlomiejpluta.base.api.ai.AI;
import com.bartlomiejpluta.base.api.ai.NPC;
import com.bartlomiejpluta.base.api.location.Locationable;
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
import com.bartlomiejpluta.base.api.move.MoveEvent;
import com.bartlomiejpluta.base.util.path.MovementPath;
import com.bartlomiejpluta.base.util.path.PathExecutor;
import com.bartlomiejpluta.base.util.pathfinder.PathFinder;
import lombok.NonNull;
import lombok.Setter;
import org.joml.Vector2i;
import org.joml.Vector2ic;
import java.util.ArrayList;
public abstract class KeepStraightDistanceAI<N extends NPC, T extends Locationable> implements AI {
private final N npc;
@Setter(onParam = @__(@NonNull))
private T target;
private final PathFinder finder;
private final PathExecutor<N> executor;
private final int minRange;
private final int maxRange;
private MovementPath<N> path = null;
public KeepStraightDistanceAI(@NonNull PathFinder finder, @NonNull N npc, @NonNull T target, int minRange, int maxRange) {
this.npc = npc;
this.target = target;
this.minRange = minRange;
this.maxRange = maxRange;
this.finder = finder;
this.executor = new PathExecutor<>(npc);
npc.addEventListener(MoveEvent.TYPE, this::onMove);
}
private void onMove(MoveEvent event) {
if (npc.getStrategy() != this) {
return;
}
var movable = event.getMovable();
// Refresh only when target has been displaced
// or another object is blocking current path
if (movable == target || (path != null && path.contains(movable))) {
path = null;
}
}
@Override
public void nextActivity(ObjectLayer layer, float dt) {
if (npc.isMoving()) {
return;
}
if (!sees(npc, target, layer)) {
idle(npc, target, layer, dt);
}
if (path == null || path.isEmpty()) {
// We are considering only straight positions against the target ("@"), for example
// when minRange is 3 and maxRange is 6, then we are considering only "O"-marked positions.
// The X means some obstacle for which we'd like to prune the positions after that:
// 5 4 3 2 1 0 1 2 3 4 5
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | | | | | | | | | | | | | | | | | | |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | | | | | | | | | O | | | | | | | | | |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | | | | | | | | | O | | | | | | | | | | 5
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | | | | | | | | | O | | | | | | | | | | 4
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | | | | | | | | | O | | | | | | | | | | 3
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | | | | | | | | | | | | | | | | | | | 2
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | | | | | | | | | | | | | | | | | | | 1
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | | | O | O | O | O | | | @ | | | O | X | | | | | | 0
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | | | | | | | | | | | | | | | | | | | 1
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | | | | | | | | | | | | | | | | | | | 2
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | | | | | | | | | O | | | | | | | | | | 3
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | | | | | | | | | O | | | | | | | | | | 4
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | | | | | | | | | O | | | | | | | | | | 5
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | | | | | | | | | O | | | | | | | | | |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | | | | | | | | | | | | | | | | | | |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
var consideredPositions = new ArrayList<Vector2ic>(4 * (maxRange - minRange + 1));
var x = target.getCoordinates().x();
var y = target.getCoordinates().y();
var right = true;
var up = true;
var left = true;
var down = true;
for (int i = 1; i <= maxRange; ++i) {
var vecUp = new Vector2i(x, y + i);
var vecRight = new Vector2i(x + i, y);
var vecDown = new Vector2i(x, y - i);
var vecLeft = new Vector2i(x - i, y);
// We are pruning the directions that are
// blocked by some obstacle.
// Apart from using layer.isTileReachable() method,
// we need also to make sure that if the method does return
// 'true' because the NPC itself is currently occupying given place,
// which is of course the desired situation.
// Without this check, we would reject this position from considered list
// and keep looking for another one which would end up with infinite loop.
if (up && !layer.isTileReachable(vecUp) && !vecUp.equals(npc.getCoordinates())) up = false;
if (right && !layer.isTileReachable(vecRight) && !vecRight.equals(npc.getCoordinates())) right = false;
if (down && !layer.isTileReachable(vecDown) && !vecDown.equals(npc.getCoordinates())) down = false;
if (left && !layer.isTileReachable(vecLeft) && !vecLeft.equals(npc.getCoordinates())) left = false;
if (i >= minRange && up) consideredPositions.add(vecUp);
if (i >= minRange && right) consideredPositions.add(vecRight);
if (i >= minRange && down) consideredPositions.add(vecDown);
if (i >= minRange && left) consideredPositions.add(vecLeft);
}
consideredPositions.sort(this::comparator);
// If we are already on any of considered position
// we abandon finding another path and start to interact
for (var position : consideredPositions) {
if (npc.getCoordinates().equals(position)) {
npc.setFaceDirection(npc.getDirectionTowards(target));
interact(npc, target, layer, dt);
return;
}
}
// Otherwise we try to find the best path
// basing on heuristically considered targets
for (var position : consideredPositions) {
if (layer.isTileReachable(position)) {
path = finder.findPath(layer, npc, position);
executor.setPath(path);
break;
}
}
}
// If no idle and no interact, it means we are following calculated path
follow(npc, target, layer, dt);
executor.execute(layer, dt);
}
protected abstract boolean sees(N npc, T target, ObjectLayer layer);
protected abstract void interact(N npc, T target, ObjectLayer layer, float dt);
protected abstract void follow(N npc, T target, ObjectLayer layer, float dt);
protected abstract void idle(N npc, T target, ObjectLayer layer, float dt);
private int comparator(Vector2ic a, Vector2ic b) {
return Integer.compare(npc.manhattanDistance(a), npc.manhattanDistance(b));
}
}

View File

@@ -1,14 +0,0 @@
package com.bartlomiejpluta.base.lib.ai;
import com.bartlomiejpluta.base.api.ai.AI;
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
public class NoopAI implements AI {
@Override
public void nextActivity(ObjectLayer layer, float dt) {
// do nothing
}
public static AI INSTANCE = new NoopAI();
}

View File

@@ -1,55 +0,0 @@
package com.bartlomiejpluta.base.lib.ai;
import com.bartlomiejpluta.base.api.ai.AI;
import com.bartlomiejpluta.base.api.ai.NPC;
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
import com.bartlomiejpluta.base.api.move.Direction;
import com.bartlomiejpluta.base.util.math.Distance;
import org.joml.Vector2ic;
import java.util.Random;
public class RandomMovementAI<N extends NPC> implements AI {
private final Random random = new Random();
private final N npc;
private final float intervalSeconds;
private final int radius;
private final Vector2ic origin;
private float accumulator = 0.0f;
private float threshold = 0.0f;
public RandomMovementAI(N npc, float intervalSeconds) {
this.npc = npc;
this.intervalSeconds = intervalSeconds;
this.radius = 0;
origin = null;
}
public RandomMovementAI(N npc, float intervalSeconds, Vector2ic origin, int radius) {
this.npc = npc;
this.intervalSeconds = intervalSeconds;
this.radius = radius;
this.origin = origin;
}
@Override
public void nextActivity(ObjectLayer layer, float dt) {
if (!npc.isMoving()) {
if (accumulator > threshold) {
var direction = Direction.values()[random.nextInt(4)];
var movement = npc.prepareMovement(direction);
if (origin != null && Distance.euclidean(origin, movement.getTo()) > radius) {
return;
}
npc.getLayer().pushMovement(movement);
accumulator = 0.0f;
threshold = random.nextFloat() * intervalSeconds;
}
accumulator += dt;
}
}
}

View File

@@ -1,55 +0,0 @@
package com.bartlomiejpluta.base.lib.ai;
import com.bartlomiejpluta.base.api.ai.AI;
import com.bartlomiejpluta.base.api.ai.NPC;
import com.bartlomiejpluta.base.api.location.Locationable;
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
import com.bartlomiejpluta.base.api.move.Direction;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.Setter;
import java.util.Random;
@AllArgsConstructor
public class RunawayAI<N extends NPC, T extends Locationable> implements AI {
@NonNull
private final N npc;
@Setter(onParam = @__(@NonNull))
private T danger;
private final Random random = new Random();
@Override
public void nextActivity(ObjectLayer layer, float dt) {
if (npc.isMoving()) {
return;
}
var direction = npc.getDirectionTowards(danger).opposite();
if (tryToMove(layer, direction)) {
return;
}
var perpendiculars = direction.perpendiculars();
var first = random.nextInt(2);
var second = 1 - first;
if (tryToMove(layer, perpendiculars[first])) {
return;
}
tryToMove(layer, perpendiculars[second]);
}
private boolean tryToMove(ObjectLayer layer, Direction direction) {
var movement = npc.prepareMovement(direction);
if (layer.isTileReachable(movement.getTo())) {
layer.pushMovement(movement);
return true;
}
return false;
}
}

View File

@@ -1,344 +0,0 @@
package com.bartlomiejpluta.base.lib.animation;
import com.bartlomiejpluta.base.api.animation.Animation;
import com.bartlomiejpluta.base.api.camera.Camera;
import com.bartlomiejpluta.base.api.location.Locationable;
import com.bartlomiejpluta.base.api.map.layer.base.Layer;
import com.bartlomiejpluta.base.api.move.AnimationMovement;
import com.bartlomiejpluta.base.api.move.Direction;
import com.bartlomiejpluta.base.api.move.Movement;
import com.bartlomiejpluta.base.api.screen.Screen;
import com.bartlomiejpluta.base.internal.object.Placeable;
import com.bartlomiejpluta.base.internal.render.ShaderManager;
import com.bartlomiejpluta.base.util.path.Path;
import org.joml.*;
import java.util.concurrent.CompletableFuture;
public abstract class AnimationDelegate implements Animation {
private final Animation animation;
public AnimationDelegate(Animation animation) {
this.animation = animation;
}
@Override
public void setStepSize(float x, float y) {
animation.setStepSize(x, y);
}
@Override
public Vector2ic getCoordinates() {
return animation.getCoordinates();
}
@Override
public void setCoordinates(Vector2ic coordinates) {
animation.setCoordinates(coordinates);
}
@Override
public void setCoordinates(int x, int y) {
animation.setCoordinates(x, y);
}
@Override
public boolean isAnimationEnabled() {
return animation.isAnimationEnabled();
}
@Override
public void setAnimationEnabled(boolean enabled) {
animation.setAnimationEnabled(enabled);
}
@Override
public void enableAnimation() {
animation.enableAnimation();
}
@Override
public void disableAnimation() {
animation.disableAnimation();
}
@Override
public void toggleAnimationEnabled() {
animation.toggleAnimationEnabled();
}
@Override
public void setAnimationFrame(int frame) {
animation.setAnimationFrame(frame);
}
@Override
public float getAnimationSpeed() {
return animation.getAnimationSpeed();
}
@Override
public void setAnimationSpeed(float speed) {
animation.setAnimationSpeed(speed);
}
@Override
public Integer getRepeat() {
return animation.getRepeat();
}
@Override
public void setRepeat(Integer repeat) {
animation.setRepeat(repeat);
}
@Override
public boolean finished() {
return animation.finished();
}
@Override
public void update(float dt) {
animation.update(dt);
}
@Override
public Vector2fc getPosition() {
return animation.getPosition();
}
@Override
public void setPosition(float x, float y) {
animation.setPosition(x, y);
}
@Override
public void setPosition(Vector2fc position) {
animation.setPosition(position);
}
@Override
public void movePosition(float x, float y) {
animation.movePosition(x, y);
}
@Override
public void movePosition(Vector2fc position) {
animation.movePosition(position);
}
@Override
public float getRotation() {
return animation.getRotation();
}
@Override
public void setRotation(float rotation) {
animation.setRotation(rotation);
}
@Override
public void moveRotation(float rotation) {
animation.moveRotation(rotation);
}
@Override
public float getScaleX() {
return animation.getScaleX();
}
@Override
public void setScaleX(float scale) {
animation.setScaleX(scale);
}
@Override
public float getScaleY() {
return animation.getScaleY();
}
@Override
public void setScaleY(float scale) {
animation.setScaleY(scale);
}
@Override
public void setScale(float scale) {
animation.setScale(scale);
}
@Override
public void setScale(float scaleX, float scaleY) {
animation.setScale(scaleX, scaleY);
}
@Override
public float euclideanDistance(Placeable other) {
return animation.euclideanDistance(other);
}
@Override
public double euclideanDistance(Vector2ic coordinates) {
return animation.euclideanDistance(coordinates);
}
@Override
public Matrix4fc getModelMatrix() {
return animation.getModelMatrix();
}
@Override
public Movement prepareMovement(Direction direction) {
return new AnimationMovement(this, direction);
}
@Override
public Movement getMovement() {
return animation.getMovement();
}
@Override
public boolean isMoving() {
return animation.isMoving();
}
@Override
public Direction getDirectionTowards(Locationable target) {
return animation.getDirectionTowards(target);
}
@Override
public int chebyshevDistance(Vector2ic coordinates) {
return animation.chebyshevDistance(coordinates);
}
@Override
public int manhattanDistance(Vector2ic coordinates) {
return animation.manhattanDistance(coordinates);
}
@Override
public int chebyshevDistance(Locationable other) {
return animation.chebyshevDistance(other);
}
@Override
public int manhattanDistance(Locationable other) {
return animation.manhattanDistance(other);
}
@Override
public double euclideanDistance(Locationable other) {
return animation.euclideanDistance(other);
}
@Override
public Vector2fc getPositionOffset() {
return animation.getPositionOffset();
}
@Override
public void setPositionOffset(Vector2fc offset) {
animation.setPositionOffset(offset);
}
@Override
public void setPositionOffset(float offsetX, float offsetY) {
animation.setPositionOffset(offsetX, offsetY);
}
@Override
public void followPath(Path<Animation> path, Integer repeat, boolean finishOnEnd, boolean finishOnFail) {
animation.followPath(path, repeat, finishOnEnd, finishOnFail);
}
@Override
public void setSpeed(float speed) {
animation.setSpeed(speed);
}
@Override
public boolean move(Movement movement) {
return animation.move(movement);
}
@Override
public void abortMove() {
animation.abortMove();
}
@Override
public void onAdd(Layer layer) {
animation.onAdd(layer);
}
@Override
public void onFinish(Layer layer) {
animation.onFinish(layer);
}
@Override
public void finish() {
animation.finish();
}
@Override
public CompletableFuture<Animation> getFuture() {
return animation.getFuture();
}
@Override
public Layer getLayer() {
return animation.getLayer();
}
@Override
public Vector4fc getColor() {
return animation.getColor();
}
@Override
public void setColor(Vector4fc color) {
animation.setColor(color);
}
@Override
public void setColor(Vector3fc color) {
animation.setColor(color);
}
@Override
public void setColor(float red, float green, float blue, float alpha) {
animation.setColor(red, green, blue, alpha);
}
@Override
public void setColor(float red, float green, float blue) {
animation.setColor(red, green, blue);
}
@Override
public void setRed(float red) {
animation.setRed(red);
}
@Override
public void setGreen(float green) {
animation.setGreen(green);
}
@Override
public void setBlue(float blue) {
animation.setBlue(blue);
}
@Override
public void setAlpha(float alpha) {
animation.setAlpha(alpha);
}
@Override
public void render(Screen screen, Camera camera, ShaderManager shaderManager) {
animation.render(screen, camera, shaderManager);
}
}

View File

@@ -1,32 +0,0 @@
package com.bartlomiejpluta.base.lib.animation;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.entity.Entity;
import com.bartlomiejpluta.base.api.map.layer.base.Layer;
import com.bartlomiejpluta.base.api.move.Movable;
import lombok.NonNull;
import org.joml.Vector2fc;
import java.util.concurrent.CompletableFuture;
public interface AnimationRunner {
CompletableFuture<Void> run(Context context, Layer layer, Vector2fc origin);
CompletableFuture<Void> run(Context context, Layer layer, Movable origin);
default CompletableFuture<Void> run(Context context, Entity entity) {
return run(context, entity.getLayer(), entity.getPosition());
}
static SimpleAnimationRunner simple(@NonNull String animationUid) {
return new SimpleAnimationRunner(animationUid);
}
static RandomAnimationsRunner random(int count) {
return new RandomAnimationsRunner(count);
}
static BulletAnimationRunner bullet(@NonNull String animationUid) {
return new BulletAnimationRunner(animationUid);
}
}

View File

@@ -1,215 +0,0 @@
package com.bartlomiejpluta.base.lib.animation;
import com.bartlomiejpluta.base.api.animation.Animation;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.entity.Entity;
import com.bartlomiejpluta.base.api.map.layer.base.Layer;
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
import com.bartlomiejpluta.base.api.move.Direction;
import com.bartlomiejpluta.base.api.move.Movable;
import com.bartlomiejpluta.base.util.path.BasePath;
import com.bartlomiejpluta.base.util.path.Path;
import lombok.NonNull;
import org.joml.Vector2fc;
import org.joml.Vector2i;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class BulletAnimationRunner implements AnimationRunner {
private static final Path<Animation> UP = new BasePath<Animation>().move(Direction.UP);
private static final Path<Animation> DOWN = new BasePath<Animation>().move(Direction.DOWN);
private static final Path<Animation> LEFT = new BasePath<Animation>().move(Direction.LEFT);
private static final Path<Animation> RIGHT = new BasePath<Animation>().move(Direction.RIGHT);
private final String animationUid;
private Integer range;
private Integer repeat = 1;
private float scale = 1.0f;
private int delay = 0;
private float animationSpeed = 3f;
private float speed = 3f;
private float rotation = 0f;
private Direction direction;
private Path<Animation> path;
private BiConsumer<Movable, Entity> onHit;
private BiConsumer<Movable, Animation> onMiss;
private Consumer<Animation> customizer;
private float offsetX = 0;
private float offsetY = 0;
public BulletAnimationRunner(@NonNull String animationUid) {
this.animationUid = animationUid;
}
public BulletAnimationRunner onHit(@NonNull BiConsumer<Movable, Entity> action) {
this.onHit = action;
return this;
}
public BulletAnimationRunner onHit(@NonNull Consumer<Entity> action) {
this.onHit = (m, e) -> action.accept(e);
return this;
}
public BulletAnimationRunner onMiss(@NonNull BiConsumer<Movable, Animation> action) {
this.onMiss = action;
return this;
}
public BulletAnimationRunner onMiss(@NonNull Consumer<Animation> action) {
this.onMiss = (m, a) -> action.accept(a);
return this;
}
public BulletAnimationRunner range(int n) {
this.range = n;
return this;
}
public BulletAnimationRunner infiniteRange() {
this.range = null;
return this;
}
public BulletAnimationRunner repeat(int n) {
this.repeat = n;
return this;
}
public BulletAnimationRunner infinite() {
this.repeat = null;
return this;
}
public BulletAnimationRunner scale(float scale) {
this.scale = scale;
return this;
}
public BulletAnimationRunner delay(int delay) {
this.delay = delay;
return this;
}
public BulletAnimationRunner animationSpeed(float speed) {
this.animationSpeed = speed;
return this;
}
public BulletAnimationRunner speed(float speed) {
this.speed = speed;
return this;
}
public BulletAnimationRunner rotation(float rotation) {
this.rotation = rotation;
return this;
}
public BulletAnimationRunner offset(float x, float y) {
this.offsetX = x;
this.offsetY = y;
return this;
}
public BulletAnimationRunner direction(@NonNull Direction direction) {
this.direction = direction;
this.path = switch (direction) {
case UP -> UP;
case DOWN -> DOWN;
case LEFT -> LEFT;
case RIGHT -> RIGHT;
};
return this;
}
public BulletAnimationRunner customize(@NonNull Consumer<Animation> customizer) {
this.customizer = customizer;
return this;
}
@Override
public CompletableFuture<Void> run(Context context, Layer layer, Vector2fc origin) {
var animation = new BulletAnimation(context.createAnimation(animationUid), delay, direction, null, onHit, onMiss);
animation.setPosition(origin);
animation.setScale(scale);
animation.setAnimationSpeed(animationSpeed);
animation.setSpeed(speed);
animation.setRotation(rotation);
animation.setRepeat(repeat);
animation.setPositionOffset(offsetX, offsetY);
animation.followPath(path, range, true, true);
layer.pushAnimation(animation);
if(customizer != null) {
customizer.accept(animation);
}
return animation.getFuture().thenApply(a -> null);
}
@Override
public CompletableFuture<Void> run(Context context, Layer layer, Movable origin) {
var animation = new BulletAnimation(context.createAnimation(animationUid), delay, direction, origin, onHit, onMiss);
animation.setCoordinates(origin.getCoordinates());
animation.setScale(scale);
animation.setAnimationSpeed(animationSpeed);
animation.setSpeed(speed);
animation.setRotation(rotation);
animation.setRepeat(repeat);
animation.setPositionOffset(offsetX, offsetY);
animation.followPath(path, range, true, true);
layer.pushAnimation(animation);
if(customizer != null) {
customizer.accept(animation);
}
return animation.getFuture().thenApply(a -> null);
}
private static class BulletAnimation extends DelayedAnimation {
private final Direction direction;
private final Movable movable;
private final BiConsumer<Movable, Entity> onHit;
private final BiConsumer<Movable, Animation> onMiss;
public BulletAnimation(Animation animation, int delay, Direction direction, Movable movable, BiConsumer<Movable, Entity> onHit, BiConsumer<Movable, Animation> onMiss) {
super(animation, delay);
this.direction = direction;
this.movable = movable;
this.onHit = onHit;
this.onMiss = onMiss;
}
@Override
public void onFinish(Layer layer) {
if (onHit != null) {
var target = getCoordinates().add(direction.vector, new Vector2i());
if (layer instanceof ObjectLayer) {
for (var entity : ((ObjectLayer) layer).getEntities()) {
var movement = (entity instanceof Movable) ? ((Movable) entity).getMovement() : null;
if ((entity.getCoordinates().equals(target) || movement != null && movement.getTo().equals(target)) && entity.isBlocking()) {
onHit.accept(movable, entity);
return;
}
}
}
}
if (onMiss != null) {
onMiss.accept(movable, this);
}
}
}
}

View File

@@ -1,32 +0,0 @@
package com.bartlomiejpluta.base.lib.animation;
import com.bartlomiejpluta.base.api.animation.Animation;
import com.bartlomiejpluta.base.api.camera.Camera;
import com.bartlomiejpluta.base.api.screen.Screen;
import com.bartlomiejpluta.base.internal.render.ShaderManager;
public class DelayedAnimation extends AnimationDelegate {
private final int delay;
private int accumulator = 0;
public DelayedAnimation(Animation animation, int delay) {
super(animation);
this.delay = delay;
}
@Override
public void update(float dt) {
if (accumulator >= delay) {
super.update(dt);
} else {
accumulator += dt * 1000;
}
}
@Override
public void render(Screen screen, Camera camera, ShaderManager shaderManager) {
if (accumulator >= delay) {
super.render(screen, camera, shaderManager);
}
}
}

View File

@@ -1,205 +0,0 @@
package com.bartlomiejpluta.base.lib.animation;
import com.bartlomiejpluta.base.api.animation.Animation;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.map.layer.base.Layer;
import com.bartlomiejpluta.base.api.move.Movable;
import lombok.NonNull;
import org.apache.commons.math3.distribution.NormalDistribution;
import org.apache.commons.math3.distribution.RealDistribution;
import org.apache.commons.math3.distribution.UniformRealDistribution;
import org.joml.Vector2fc;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import static java.lang.Math.max;
public class RandomAnimationsRunner implements AnimationRunner {
private final Random random = new Random();
private final List<String> animationUids = new ArrayList<>();
private final int count;
private float scale = 1.0f;
private RealDistribution scaleDistribution;
private float rangeX = 0f;
private float rangeY = 0f;
private RealDistribution rangeDistribution;
private float delay = 0f;
private RealDistribution delayDistribution;
private float animationSpeed = 3f;
private RealDistribution animationSpeedDistribution;
private float rotation = 0f;
private RealDistribution rotationDistribution;
private float offsetX = 0;
private float offsetY = 0;
private BiConsumer<Integer, Animation> customizer;
public RandomAnimationsRunner(int count) {
this.count = max(count, 0);
}
public RandomAnimationsRunner with(String animationUid) {
animationUids.add(animationUid);
return this;
}
public RandomAnimationsRunner scale(float scale) {
this.scale = scale;
return this;
}
public RandomAnimationsRunner uScale(float min, float max) {
this.scaleDistribution = new UniformRealDistribution(min, max);
return this;
}
public RandomAnimationsRunner nScale(float mean, float sd) {
this.scaleDistribution = new NormalDistribution(mean, sd);
return this;
}
public RandomAnimationsRunner range(float rangeX, float rangeY) {
this.rangeX = rangeX;
this.rangeY = rangeY;
return this;
}
public RandomAnimationsRunner uRange(float min, float max) {
this.rangeDistribution = new UniformRealDistribution(min, max);
return this;
}
public RandomAnimationsRunner nRange(float mean, float sd) {
this.rangeDistribution = new NormalDistribution(mean, sd);
return this;
}
public RandomAnimationsRunner animationSpeed(float speed) {
this.animationSpeed = speed;
return this;
}
public RandomAnimationsRunner uAnimationSpeed(float min, float max) {
this.animationSpeedDistribution = new UniformRealDistribution(min, max);
return this;
}
public RandomAnimationsRunner nAnimationSpeed(float mean, float sd) {
this.animationSpeedDistribution = new NormalDistribution(mean, sd);
return this;
}
public RandomAnimationsRunner delay(float delay) {
this.delay = delay;
return this;
}
public RandomAnimationsRunner uDelay(float min, float max) {
this.delayDistribution = new UniformRealDistribution(min, max);
return this;
}
public RandomAnimationsRunner nDelay(float mean, float sd) {
this.delayDistribution = new NormalDistribution(mean, sd);
return this;
}
public RandomAnimationsRunner rotation(float rotation) {
this.rotation = rotation;
return this;
}
public RandomAnimationsRunner uRotation(float min, float max) {
this.rotationDistribution = new UniformRealDistribution(min, max);
return this;
}
public RandomAnimationsRunner nRotation(float mean, float sd) {
this.rotationDistribution = new NormalDistribution(mean, sd);
return this;
}
public RandomAnimationsRunner offset(float x, float y) {
this.offsetX = x;
this.offsetY = y;
return this;
}
public RandomAnimationsRunner customize(@NonNull BiConsumer<Integer, Animation> customizer) {
this.customizer = customizer;
return this;
}
@Override
public CompletableFuture<Void> run(Context context, Layer layer, Vector2fc origin) {
var futures = new CompletableFuture[count];
for (int i = 0; i < count; ++i) {
var animation = context.createAnimation(animationUids.get(random.nextInt(animationUids.size())));
if (rangeDistribution != null) {
animation.setPosition(origin.x() + (float) rangeDistribution.sample(), origin.y() + (float) rangeDistribution.sample());
} else {
animation.setPosition(origin.x() + rangeX, origin.y() + rangeY);
}
animation.setPositionOffset(offsetX, offsetY);
animation.setScale(scaleDistribution != null ? (float) scaleDistribution.sample() : scale);
animation.setAnimationSpeed(animationSpeedDistribution != null ? (float) animationSpeedDistribution.sample() : animationSpeed);
animation.setRotation(rotationDistribution != null ? (float) rotationDistribution.sample() : rotation);
layer.pushAnimation(new DelayedAnimation(animation, (int) (delayDistribution != null ? delayDistribution.sample() : delay)));
if (customizer != null) {
customizer.accept(i, animation);
}
futures[i] = animation.getFuture();
}
return CompletableFuture.allOf(futures);
}
@Override
public CompletableFuture<Void> run(Context context, Layer layer, Movable origin) {
var futures = new CompletableFuture[count];
for (int i = 0; i < count; ++i) {
var animation = context.createAnimation(animationUids.get(random.nextInt(animationUids.size())));
var position = origin.getPosition();
var offset = origin.getPositionOffset();
if (rangeDistribution != null) {
animation.setPosition(position.x() - offset.x() + (float) rangeDistribution.sample(), position.y() - offset.y() + (float) rangeDistribution.sample());
} else {
animation.setPosition(position.x() - offset.x() + rangeX, position.y() - offset.y() + rangeY);
}
animation.setPositionOffset(offsetX, offsetY);
animation.setScale(scaleDistribution != null ? (float) scaleDistribution.sample() : scale);
animation.setAnimationSpeed(animationSpeedDistribution != null ? (float) animationSpeedDistribution.sample() : animationSpeed);
animation.setRotation(rotationDistribution != null ? (float) rotationDistribution.sample() : rotation);
layer.pushAnimation(new DelayedAnimation(animation, (int) (delayDistribution != null ? delayDistribution.sample() : delay)));
if (customizer != null) {
customizer.accept(i, animation);
}
futures[i] = animation.getFuture();
}
return CompletableFuture.allOf(futures);
}
}

View File

@@ -1,142 +0,0 @@
package com.bartlomiejpluta.base.lib.animation;
import com.bartlomiejpluta.base.api.animation.Animation;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.map.layer.base.Layer;
import com.bartlomiejpluta.base.api.move.Movable;
import com.bartlomiejpluta.base.util.path.Path;
import lombok.NonNull;
import org.joml.Vector2fc;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
public class SimpleAnimationRunner implements AnimationRunner {
private final String animationUid;
private Integer repeat = 1;
private float scale = 1.0f;
private int delay = 0;
private float animationSpeed = 3f;
private float speed = 3f;
private float rotation = 0f;
private Path<Animation> path;
private Integer repeatPath;
private boolean finishOnPathEnd;
private boolean finishOnPathFail;
private float offsetX = 0;
private float offsetY = 0;
private Consumer<Animation> customizer;
public SimpleAnimationRunner(String animationUid) {
this.animationUid = animationUid;
}
public SimpleAnimationRunner repeat(int n) {
this.repeat = n;
return this;
}
public SimpleAnimationRunner infinite() {
this.repeat = null;
return this;
}
public SimpleAnimationRunner scale(float scale) {
this.scale = scale;
return this;
}
public SimpleAnimationRunner delay(int delay) {
this.delay = delay;
return this;
}
public SimpleAnimationRunner animationSpeed(float speed) {
this.animationSpeed = speed;
return this;
}
public SimpleAnimationRunner speed(float speed) {
this.speed = speed;
return this;
}
public SimpleAnimationRunner rotation(float rotation) {
this.rotation = rotation;
return this;
}
public SimpleAnimationRunner offset(float x, float y) {
this.offsetX = x;
this.offsetY = y;
return this;
}
public SimpleAnimationRunner repeatPath(int n) {
this.repeatPath = n;
return this;
}
public SimpleAnimationRunner path(Path<Animation> path, boolean finishOnEnd, boolean finishOnFail) {
this.path = path;
this.finishOnPathEnd = finishOnEnd;
this.finishOnPathFail = finishOnFail;
return this;
}
public SimpleAnimationRunner customize(@NonNull Consumer<Animation> customizer) {
this.customizer = customizer;
return this;
}
@Override
public CompletableFuture<Void> run(Context context, Layer layer, Vector2fc origin) {
var animation = new DelayedAnimation(context.createAnimation(animationUid), delay);
animation.setPosition(origin);
animation.setPositionOffset(offsetX, offsetY);
animation.setScale(scale);
animation.setAnimationSpeed(animationSpeed);
animation.setSpeed(speed);
animation.setRotation(rotation);
animation.setRepeat(repeat);
if (path != null) {
animation.followPath(path, repeatPath, finishOnPathEnd, finishOnPathFail);
}
layer.pushAnimation(animation);
if (customizer != null) {
customizer.accept(animation);
}
return animation.getFuture().thenApply(a -> null);
}
@Override
public CompletableFuture<Void> run(Context context, Layer layer, Movable origin) {
var animation = new DelayedAnimation(context.createAnimation(animationUid), delay);
animation.setCoordinates(origin.getCoordinates());
animation.setPositionOffset(offsetX, offsetY);
animation.setScale(scale);
animation.setAnimationSpeed(animationSpeed);
animation.setSpeed(speed);
animation.setRotation(rotation);
animation.setRepeat(repeat);
if (path != null) {
animation.followPath(path, repeatPath, finishOnPathEnd, finishOnPathFail);
}
layer.pushAnimation(animation);
if (customizer != null) {
customizer.accept(animation);
}
return animation.getFuture().thenApply(a -> null);
}
}

View File

@@ -1,5 +0,0 @@
package com.bartlomiejpluta.base.lib.camera;
public interface CameraController {
void update();
}

View File

@@ -1,61 +0,0 @@
package com.bartlomiejpluta.base.lib.camera;
import com.bartlomiejpluta.base.api.camera.Camera;
import com.bartlomiejpluta.base.api.map.model.GameMap;
import com.bartlomiejpluta.base.api.screen.Screen;
import lombok.NonNull;
import org.joml.Vector2fc;
public class FollowingCameraController implements CameraController {
private final Vector2fc screenSize;
private final Vector2fc mapSize;
private final Camera camera;
private Vector2fc target;
private FollowingCameraController(@NonNull Screen screen, @NonNull Camera camera, @NonNull GameMap map) {
this.screenSize = screen.getSize();
this.camera = camera;
this.mapSize = map.getSize();
}
public FollowingCameraController follow(@NonNull Vector2fc target) {
this.target = target;
return this;
}
public FollowingCameraController stop() {
this.target = null;
return this;
}
@Override
public void update() {
if (target != null) {
var newCameraPosX = 0.0f;
var newCameraPosY = 0.0f;
if (mapSize.x() < screenSize.x() / camera.getScaleX()) {
newCameraPosX = (mapSize.x() - screenSize.x() / camera.getScaleX())/2;
} else {
final var bottomRightConstraintX = mapSize.x() - screenSize.x() / camera.getScaleX();
newCameraPosX = target.x() - screenSize.x() / (2f * camera.getScaleX());
newCameraPosX = (newCameraPosX < bottomRightConstraintX) ? (newCameraPosX > 0 ? newCameraPosX : 0) : bottomRightConstraintX;
}
if (mapSize.y() < screenSize.y() / camera.getScaleY()) {
newCameraPosY = (mapSize.y() - screenSize.y() / camera.getScaleY())/2;
} else {
final var bottomRightConstraintY = mapSize.y() - screenSize.y() / camera.getScaleY();
newCameraPosY = target.y() - screenSize.y() / (2f * camera.getScaleY());
newCameraPosY = (newCameraPosY < bottomRightConstraintY) ? (newCameraPosY > 0 ? newCameraPosY : 0) : bottomRightConstraintY;
}
camera.setPosition(newCameraPosX, newCameraPosY);
}
}
public static FollowingCameraController on(Screen screen, Camera camera, GameMap map) {
return new FollowingCameraController(screen, camera, map);
}
}

View File

@@ -1,383 +0,0 @@
package com.bartlomiejpluta.base.lib.character;
import com.bartlomiejpluta.base.api.camera.Camera;
import com.bartlomiejpluta.base.api.character.Character;
import com.bartlomiejpluta.base.api.event.Event;
import com.bartlomiejpluta.base.api.event.EventType;
import com.bartlomiejpluta.base.api.location.Locationable;
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
import com.bartlomiejpluta.base.api.move.CharacterMovement;
import com.bartlomiejpluta.base.api.move.Direction;
import com.bartlomiejpluta.base.api.move.Movement;
import com.bartlomiejpluta.base.api.screen.Screen;
import com.bartlomiejpluta.base.internal.object.Placeable;
import com.bartlomiejpluta.base.internal.render.ShaderManager;
import org.joml.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
public abstract class CharacterDelegate implements Character {
protected final Character character;
protected CharacterDelegate(Character character) {
this.character = character;
}
@Override
public void setStepSize(float x, float y) {
character.setStepSize(x, y);
}
@Override
public Vector2ic getCoordinates() {
return character.getCoordinates();
}
@Override
public void setCoordinates(Vector2ic coordinates) {
character.setCoordinates(coordinates);
}
@Override
public void setCoordinates(int x, int y) {
character.setCoordinates(x, y);
}
@Override
public Movement prepareMovement(Direction direction) {
return new CharacterMovement(this, direction);
}
@Override
public Movement getMovement() {
return character.getMovement();
}
@Override
public Movement move(Direction direction) {
var movement = prepareMovement(direction);
getLayer().pushMovement(movement);
return movement;
}
@Override
public Direction getFaceDirection() {
return character.getFaceDirection();
}
@Override
public void setFaceDirection(Direction direction) {
character.setFaceDirection(direction);
}
@Override
public void setSpeed(float speed) {
character.setSpeed(speed);
}
@Override
public boolean isAnimationEnabled() {
return character.isAnimationEnabled();
}
@Override
public void setAnimationEnabled(boolean enabled) {
character.setAnimationEnabled(enabled);
}
@Override
public void enableAnimation() {
character.enableAnimation();
}
@Override
public void disableAnimation() {
character.disableAnimation();
}
@Override
public void toggleAnimationEnabled() {
character.toggleAnimationEnabled();
}
@Override
public void setAnimationFrame(int frame) {
character.setAnimationFrame(frame);
}
@Override
public float getAnimationSpeed() {
return character.getAnimationSpeed();
}
@Override
public void setAnimationSpeed(float speed) {
character.setAnimationSpeed(speed);
}
@Override
public boolean isMoving() {
return character.isMoving();
}
@Override
public Vector2fc getPositionOffset() {
return character.getPositionOffset();
}
@Override
public void setPositionOffset(Vector2fc offset) {
character.setPositionOffset(offset);
}
@Override
public void setPositionOffset(float offsetX, float offsetY) {
character.setPositionOffset(offsetX, offsetY);
}
@Override
public int chebyshevDistance(Locationable other) {
return character.chebyshevDistance(other);
}
@Override
public int manhattanDistance(Locationable other) {
return character.manhattanDistance(other);
}
@Override
public double euclideanDistance(Locationable other) {
return character.euclideanDistance(other);
}
@Override
public Direction getDirectionTowards(Locationable target) {
return character.getDirectionTowards(target);
}
@Override
public Vector2fc getPosition() {
return character.getPosition();
}
@Override
public void setPosition(Vector2fc position) {
character.setPosition(position);
}
@Override
public void setPosition(float x, float y) {
character.setPosition(x, y);
}
@Override
public void movePosition(float x, float y) {
character.movePosition(x, y);
}
@Override
public void movePosition(Vector2fc position) {
character.movePosition(position);
}
@Override
public float getRotation() {
return character.getRotation();
}
@Override
public void setRotation(float rotation) {
character.setRotation(rotation);
}
@Override
public void moveRotation(float rotation) {
character.moveRotation(rotation);
}
@Override
public float getScaleX() {
return character.getScaleX();
}
@Override
public void setScaleX(float scale) {
character.setScaleX(scale);
}
@Override
public float getScaleY() {
return character.getScaleY();
}
@Override
public void setScaleY(float scale) {
character.setScaleY(scale);
}
@Override
public void setScale(float scale) {
character.setScale(scale);
}
@Override
public void setScale(float scaleX, float scaleY) {
character.setScale(scaleX, scaleY);
}
@Override
public float euclideanDistance(Placeable other) {
return character.euclideanDistance(other);
}
@Override
public double euclideanDistance(Vector2ic coordinates) {
return character.euclideanDistance(coordinates);
}
@Override
public int chebyshevDistance(Vector2ic coordinates) {
return character.chebyshevDistance(coordinates);
}
@Override
public int manhattanDistance(Vector2ic coordinates) {
return character.manhattanDistance(coordinates);
}
@Override
public Matrix4fc getModelMatrix() {
return character.getModelMatrix();
}
@Override
public ObjectLayer getLayer() {
return character.getLayer();
}
@Override
public void onAdd(ObjectLayer layer) {
character.onAdd(layer);
}
@Override
public void onRemove(ObjectLayer layer) {
character.onRemove(layer);
}
@Override
public boolean isBlocking() {
return character.isBlocking();
}
@Override
public void setBlocking(boolean blocking) {
character.setBlocking(blocking);
}
@Override
public void changeCharacterSet(String characterSetUid) {
character.changeCharacterSet(characterSetUid);
}
@Override
public int getZIndex() {
return character.getZIndex();
}
@Override
public void setZIndex(int zIndex) {
character.setZIndex(zIndex);
}
@Override
public void setDefaultSpriteColumn(int column) {
character.setDefaultSpriteColumn(column);
}
@Override
public <E extends Event> void handleEvent(E event) {
character.handleEvent(event);
}
@Override
public <E extends Event> void addEventListener(EventType<E> type, Consumer<E> listener) {
character.addEventListener(type, listener);
}
@Override
public <E extends Event> void removeEventListener(EventType<E> type, Consumer<E> listener) {
character.removeEventListener(type, listener);
}
@Override
public CompletableFuture<Void> performInstantAnimation() {
return character.performInstantAnimation();
}
@Override
public boolean move(Movement movement) {
return character.move(movement);
}
@Override
public void abortMove() {
character.abortMove();
}
@Override
public Vector4fc getColor() {
return character.getColor();
}
@Override
public void setColor(Vector4fc color) {
character.setColor(color);
}
@Override
public void setColor(Vector3fc color) {
character.setColor(color);
}
@Override
public void setColor(float red, float green, float blue, float alpha) {
character.setColor(red, green, blue, alpha);
}
@Override
public void setColor(float red, float green, float blue) {
character.setColor(red, green, blue);
}
@Override
public void setRed(float red) {
character.setRed(red);
}
@Override
public void setGreen(float green) {
character.setGreen(green);
}
@Override
public void setBlue(float blue) {
character.setBlue(blue);
}
@Override
public void setAlpha(float alpha) {
character.setAlpha(alpha);
}
@Override
public void update(float dt) {
character.update(dt);
}
@Override
public void render(Screen screen, Camera camera, ShaderManager shaderManager) {
character.render(screen, camera, shaderManager);
}
}

View File

@@ -1,13 +0,0 @@
package com.bartlomiejpluta.base.lib.db;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum Ordering {
ASC("ASC"),
DESC("DESC");
@Getter
private final String ordering;
}

View File

@@ -1,17 +0,0 @@
package com.bartlomiejpluta.base.lib.db;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum Relop {
EQ("="),
NE("<>"),
GT(">"),
GE(">="),
LT("<"),
LE("<=");
@Getter
private final String op;
}

View File

@@ -1,235 +0,0 @@
package com.bartlomiejpluta.base.lib.entity;
import com.bartlomiejpluta.base.api.camera.Camera;
import com.bartlomiejpluta.base.api.entity.Entity;
import com.bartlomiejpluta.base.api.event.Event;
import com.bartlomiejpluta.base.api.event.EventType;
import com.bartlomiejpluta.base.api.location.Locationable;
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
import com.bartlomiejpluta.base.api.move.Direction;
import com.bartlomiejpluta.base.api.screen.Screen;
import com.bartlomiejpluta.base.internal.object.Placeable;
import com.bartlomiejpluta.base.internal.render.ShaderManager;
import org.joml.Matrix4fc;
import org.joml.Vector2fc;
import org.joml.Vector2ic;
import java.util.function.Consumer;
public abstract class EntityDelegate implements Entity {
protected final Entity entity;
protected EntityDelegate(Entity entity) {
this.entity = entity;
}
@Override
public void setStepSize(float x, float y) {
entity.setStepSize(x, y);
}
@Override
public Vector2ic getCoordinates() {
return entity.getCoordinates();
}
@Override
public void setCoordinates(Vector2ic coordinates) {
entity.setCoordinates(coordinates);
}
@Override
public void setCoordinates(int x, int y) {
entity.setCoordinates(x, y);
}
@Override
public Vector2fc getPositionOffset() {
return entity.getPositionOffset();
}
@Override
public void setPositionOffset(Vector2fc offset) {
entity.setPositionOffset(offset);
}
@Override
public void setPositionOffset(float offsetX, float offsetY) {
entity.setPositionOffset(offsetX, offsetY);
}
@Override
public int chebyshevDistance(Locationable other) {
return entity.chebyshevDistance(other);
}
@Override
public int manhattanDistance(Locationable other) {
return entity.manhattanDistance(other);
}
@Override
public double euclideanDistance(Locationable other) {
return entity.euclideanDistance(other);
}
@Override
public Direction getDirectionTowards(Locationable target) {
return entity.getDirectionTowards(target);
}
@Override
public Vector2fc getPosition() {
return entity.getPosition();
}
@Override
public void setPosition(Vector2fc position) {
entity.setPosition(position);
}
@Override
public void setPosition(float x, float y) {
entity.setPosition(x, y);
}
@Override
public void movePosition(float x, float y) {
entity.movePosition(x, y);
}
@Override
public void movePosition(Vector2fc position) {
entity.movePosition(position);
}
@Override
public float getRotation() {
return entity.getRotation();
}
@Override
public void setRotation(float rotation) {
entity.setRotation(rotation);
}
@Override
public void moveRotation(float rotation) {
entity.moveRotation(rotation);
}
@Override
public float getScaleX() {
return entity.getScaleX();
}
@Override
public void setScaleX(float scale) {
entity.setScaleX(scale);
}
@Override
public float getScaleY() {
return entity.getScaleY();
}
@Override
public void setScaleY(float scale) {
entity.setScaleY(scale);
}
@Override
public void setScale(float scale) {
entity.setScale(scale);
}
@Override
public void setScale(float scaleX, float scaleY) {
entity.setScale(scaleX, scaleY);
}
@Override
public float euclideanDistance(Placeable other) {
return entity.euclideanDistance(other);
}
@Override
public double euclideanDistance(Vector2ic coordinates) {
return entity.euclideanDistance(coordinates);
}
@Override
public int chebyshevDistance(Vector2ic coordinates) {
return entity.chebyshevDistance(coordinates);
}
@Override
public int manhattanDistance(Vector2ic coordinates) {
return entity.manhattanDistance(coordinates);
}
@Override
public Matrix4fc getModelMatrix() {
return entity.getModelMatrix();
}
@Override
public ObjectLayer getLayer() {
return entity.getLayer();
}
@Override
public void onAdd(ObjectLayer layer) {
entity.onAdd(layer);
}
@Override
public void onRemove(ObjectLayer layer) {
entity.onRemove(layer);
}
@Override
public boolean isBlocking() {
return entity.isBlocking();
}
@Override
public void setBlocking(boolean blocking) {
entity.setBlocking(blocking);
}
@Override
public int getZIndex() {
return entity.getZIndex();
}
@Override
public void setZIndex(int zIndex) {
entity.setZIndex(zIndex);
}
@Override
public <E extends Event> void addEventListener(EventType<E> type, Consumer<E> listener) {
entity.addEventListener(type, listener);
}
@Override
public <E extends Event> void removeEventListener(EventType<E> type, Consumer<E> listener) {
entity.removeEventListener(type, listener);
}
@Override
public <E extends Event> void handleEvent(E event) {
entity.handleEvent(event);
}
@Override
public void update(float dt) {
entity.update(dt);
}
@Override
public void render(Screen screen, Camera camera, ShaderManager shaderManager) {
entity.render(screen, camera, shaderManager);
}
}

View File

@@ -1,17 +0,0 @@
package com.bartlomiejpluta.base.lib.event;
import com.bartlomiejpluta.base.api.event.Event;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@EqualsAndHashCode
public abstract class BaseEvent implements Event {
@Getter
private boolean consumed = false;
@Override
public void consume() {
consumed = true;
}
}

View File

@@ -1,52 +0,0 @@
package com.bartlomiejpluta.base.lib.event;
import com.bartlomiejpluta.base.api.event.Event;
import com.bartlomiejpluta.base.api.event.EventType;
import com.bartlomiejpluta.base.api.event.Reactive;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
public final class EventHandler implements Reactive {
private final Map<EventType<?>, ArrayList<Consumer<? extends Event>>> listeners = new HashMap<>();
@SuppressWarnings("unchecked")
@Override
public <E extends Event> void handleEvent(E event) {
var list = listeners.get(event.getType());
if (list != null) {
for (var i = list.size() - 1; i >= 0; --i) {
if (event.isConsumed()) {
return;
}
((Consumer<E>) list.get(i)).accept(event);
}
}
}
public <E extends Event> void addListener(EventType<E> type, Consumer<E> listener) {
var list = this.listeners.get(type);
if (list != null) {
list.add(listener);
} else {
list = new ArrayList<>();
list.add(listener);
listeners.put(type, list);
}
}
public <E extends Event> void removeListener(EventType<E> type, Consumer<E> listener) {
var list = this.listeners.get(type);
if (list != null) {
list.remove(listener);
if (list.isEmpty()) {
this.listeners.remove(type);
}
}
}
}

View File

@@ -1,60 +0,0 @@
package com.bartlomiejpluta.base.lib.gui;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.gui.Component;
import com.bartlomiejpluta.base.api.gui.GUI;
import com.bartlomiejpluta.base.api.gui.SizeMode;
import com.bartlomiejpluta.base.api.screen.Screen;
import java.util.Map;
public class AbsoluteLayout extends BaseContainer {
public AbsoluteLayout(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
super.setSizeMode(SizeMode.ABSOLUTE, SizeMode.ABSOLUTE);
super.setSize(0f, 0f);
}
@Override
public final void setWidth(Float width) {
throw new UnsupportedOperationException("Absolute layout does not define a size");
}
@Override
public final void setHeight(Float height) {
throw new UnsupportedOperationException("Absolute layout does not define a size");
}
@Override
public final void setSizeMode(SizeMode widthMode, SizeMode heightMode) {
throw new UnsupportedOperationException("Absolute layout does not define a size");
}
@Override
public final void setWidthMode(SizeMode mode) {
throw new UnsupportedOperationException("Absolute layout does not define a size");
}
@Override
public final void setHeightMode(SizeMode mode) {
throw new UnsupportedOperationException("Absolute layout does not define a size");
}
@Override
protected final float getContentWidth() {
return 0;
}
@Override
protected final float getContentHeight() {
return 0;
}
@Override
public void draw(Screen screen, GUI gui) {
for (var child : children) {
child.draw(screen, gui);
}
}
}

View File

@@ -1,63 +0,0 @@
package com.bartlomiejpluta.base.lib.gui;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.event.Event;
import com.bartlomiejpluta.base.api.gui.Component;
import com.bartlomiejpluta.base.api.gui.GUI;
import java.util.List;
import java.util.Map;
import static java.util.Collections.emptyList;
public abstract class BaseComponent extends BaseWidget implements Component {
protected final Context context;
protected final GUI gui;
protected final Map<String, Component> refs;
protected boolean focused;
protected BaseComponent(Context context, GUI gui, Map<String, Component> refs) {
this.context = context;
this.gui = gui;
this.refs = refs;
}
@Override
public List<Component> getChildren() {
return emptyList();
}
@Override
public void add(Component component) {
throw new UnsupportedOperationException();
}
@Override
public void remove(Component component) {
throw new UnsupportedOperationException();
}
@Override
public boolean isFocused() {
return focused;
}
@Override
public void focus() {
focused = true;
}
@Override
public <E extends Event> void handleEvent(E event) {
if(!focused) {
return;
}
super.handleEvent(event);
}
@Override
public void blur() {
focused = false;
}
}

View File

@@ -1,131 +0,0 @@
package com.bartlomiejpluta.base.lib.gui;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.event.Event;
import com.bartlomiejpluta.base.api.gui.Component;
import com.bartlomiejpluta.base.api.gui.GUI;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import static java.util.Collections.unmodifiableList;
import static java.util.Objects.requireNonNull;
public abstract class BaseContainer extends BaseComponent {
protected final List<Component> children = new LinkedList<>();
private final List<Component> readOnlyChildren = unmodifiableList(children);
public BaseContainer(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
}
@Override
public List<Component> getChildren() {
return readOnlyChildren;
}
@Override
public void add(Component component) {
this.children.add(requireNonNull(component));
component.setParent(this);
}
@Override
public void remove(Component component) {
this.children.remove(requireNonNull(component));
component.setParent(null);
}
public void removeAllChildren() {
this.children.clear();
}
protected float maxChildrenWidth() {
var theWidestChild = 0.0f;
for (var child : children) {
var width = child.getMarginLeft() + child.getActualWidth() + child.getMarginRight();
if (width > theWidestChild) {
theWidestChild = width;
}
}
return theWidestChild;
}
protected float maxChildrenHeight() {
var theHighestChild = 0.0f;
for (var child : children) {
var height = child.getMarginTop() + child.getActualHeight() + child.getMarginBottom();
if (height > theHighestChild) {
theHighestChild = height;
}
}
return theHighestChild;
}
protected float sumChildrenWidth() {
var childrenWidth = 0.0f;
for (var child : children) {
childrenWidth += child.getMarginLeft() + child.getWidth() + child.getMarginRight();
}
return childrenWidth;
}
protected float sumChildrenHeight() {
var childrenHeight = 0.0f;
for (var child : children) {
childrenHeight += child.getMarginTop() + child.getHeight() + child.getMarginBottom();
}
return childrenHeight;
}
@Override
public void focus() {
blur();
super.focus();
}
@Override
public void blur() {
super.blur();
for (var child : children) {
child.blur();
}
}
protected void blurChildren() {
for (var child : children) {
child.blur();
}
}
@Override
public <E extends Event> void handleEvent(E event) {
// Populate event downstream
for (var child : children) {
if (event.isConsumed()) {
return;
}
child.handleEvent(event);
}
eventHandler.handleEvent(event);
}
@Override
public void update(float dt) {
for (var child : children) {
child.update(dt);
}
}
}

View File

@@ -1,337 +0,0 @@
package com.bartlomiejpluta.base.lib.gui;
import com.bartlomiejpluta.base.api.event.Event;
import com.bartlomiejpluta.base.api.event.EventType;
import com.bartlomiejpluta.base.api.gui.Attribute;
import com.bartlomiejpluta.base.api.gui.SizeMode;
import com.bartlomiejpluta.base.api.gui.Widget;
import com.bartlomiejpluta.base.lib.event.EventHandler;
import java.util.Locale;
import java.util.function.Consumer;
public abstract class BaseWidget implements Widget {
protected final EventHandler eventHandler = new EventHandler();
protected Widget parent;
protected SizeMode widthMode = SizeMode.AUTO;
protected SizeMode heightMode = SizeMode.AUTO;
protected float x;
protected float y;
protected float width;
protected float height;
protected float marginTop;
protected float marginRight;
protected float marginBottom;
protected float marginLeft;
protected float paddingTop;
protected float paddingRight;
protected float paddingBottom;
protected float paddingLeft;
protected abstract float getContentWidth();
protected abstract float getContentHeight();
@Override
public float getWidth() {
return widthMode == SizeMode.RELATIVE
? (parent != null ? width * parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight() - marginLeft - marginRight : 0)
: getActualWidth();
}
@Override
public void setWidth(Float width) {
this.width = width;
}
@Attribute("width")
public void setWidth(String width) {
var widthLowerCased = width.toLowerCase(Locale.ROOT);
if (widthLowerCased.equals("auto")) {
this.widthMode = SizeMode.AUTO;
} else if (widthLowerCased.equals("relative")) {
this.widthMode = SizeMode.RELATIVE;
this.width = 1f;
} else {
this.widthMode = SizeMode.ABSOLUTE;
this.width = Float.parseFloat(width);
}
}
@Override
public float getActualWidth() {
return paddingLeft + (widthMode == SizeMode.ABSOLUTE ? width : getContentWidth()) + paddingRight;
}
@Override
public float getHeight() {
return heightMode == SizeMode.RELATIVE
? (parent != null ? height * parent.getHeight() - parent.getPaddingTop() - parent.getPaddingBottom() - marginTop - marginBottom : 0)
: getActualHeight();
}
@Override
public void setHeight(Float height) {
this.height = height;
}
@Attribute("height")
public void setHeight(String height) {
var heightLowerCased = height.toLowerCase(Locale.ROOT);
if (heightLowerCased.equals("auto")) {
this.heightMode = SizeMode.AUTO;
} else if (heightLowerCased.equals("relative")) {
this.heightMode = SizeMode.RELATIVE;
this.height = 1f;
} else {
this.heightMode = SizeMode.ABSOLUTE;
this.height = Float.parseFloat(height);
}
}
@Override
public float getActualHeight() {
return paddingTop + (heightMode == SizeMode.ABSOLUTE ? height : getContentHeight()) + paddingBottom;
}
@Override
public void setSize(Float width, Float height) {
this.width = width;
this.height = height;
}
@Override
public void setSizeMode(SizeMode widthMode, SizeMode heightMode) {
this.widthMode = widthMode;
this.heightMode = heightMode;
}
@Override
public SizeMode getWidthMode() {
return widthMode;
}
@Override
@Attribute("widthMode")
public void setWidthMode(SizeMode mode) {
this.widthMode = mode;
}
@Override
public SizeMode getHeightMode() {
return heightMode;
}
@Override
@Attribute("heightMode")
public void setHeightMode(SizeMode mode) {
this.heightMode = mode;
}
@Override
public Widget getParent() {
return parent;
}
@Override
public void setParent(Widget parent) {
this.parent = parent;
}
@Override
public float getX() {
return x;
}
@Override
public void setX(Float x) {
this.x = x;
}
@Override
public float getY() {
return y;
}
@Override
public void setY(Float y) {
this.y = y;
}
@Override
public void setPosition(Float x, Float y) {
this.x = x;
this.y = y;
}
@Override
public void setMargin(Float top, Float right, Float bottom, Float left) {
this.marginTop = top;
this.marginRight = right;
this.marginBottom = bottom;
this.marginLeft = left;
}
@Override
public void setMargin(Float top, Float rightLeft, Float bottom) {
this.marginTop = top;
this.marginRight = rightLeft;
this.marginBottom = bottom;
this.marginLeft = rightLeft;
}
@Override
public void setMargin(Float topBottom, Float rightLeft) {
this.marginTop = topBottom;
this.marginRight = rightLeft;
this.marginBottom = topBottom;
this.marginLeft = rightLeft;
}
@Override
public void setMargin(Float all) {
this.marginTop = all;
this.marginRight = all;
this.marginBottom = all;
this.marginLeft = all;
}
@Override
public float getMarginTop() {
return marginTop;
}
@Override
public void setMarginTop(Float margin) {
this.marginTop = margin;
}
@Override
public float getMarginRight() {
return marginRight;
}
@Override
public void setMarginRight(Float margin) {
this.marginRight = margin;
}
@Override
public float getMarginBottom() {
return marginBottom;
}
@Override
public void setMarginBottom(Float margin) {
this.marginBottom = margin;
}
@Override
public float getMarginLeft() {
return marginLeft;
}
@Override
public void setMarginLeft(Float margin) {
this.marginLeft = margin;
}
@Override
public void setPadding(Float top, Float right, Float bottom, Float left) {
this.paddingTop = top;
this.paddingRight = right;
this.paddingBottom = bottom;
this.paddingLeft = left;
}
@Override
public void setPadding(Float top, Float rightLeft, Float bottom) {
this.paddingTop = top;
this.paddingRight = rightLeft;
this.paddingBottom = bottom;
this.paddingLeft = rightLeft;
}
@Override
public void setPadding(Float topBottom, Float rightLeft) {
this.paddingTop = topBottom;
this.paddingRight = rightLeft;
this.paddingBottom = topBottom;
this.paddingLeft = rightLeft;
}
@Override
public void setPadding(Float all) {
this.paddingTop = all;
this.paddingRight = all;
this.paddingBottom = all;
this.paddingLeft = all;
}
@Override
public float getPaddingTop() {
return paddingTop;
}
@Override
public void setPaddingTop(Float padding) {
this.paddingTop = padding;
}
@Override
public float getPaddingRight() {
return paddingRight;
}
@Override
public void setPaddingRight(Float padding) {
this.paddingRight = padding;
}
@Override
public float getPaddingBottom() {
return paddingBottom;
}
@Override
public void setPaddingBottom(Float padding) {
this.paddingBottom = padding;
}
@Override
public float getPaddingLeft() {
return paddingLeft;
}
@Override
public void setPaddingLeft(Float padding) {
this.paddingLeft = padding;
}
@Override
public <E extends Event> void handleEvent(E event) {
eventHandler.handleEvent(event);
}
@Override
public void update(float dt) {
// do nothing
}
protected <E extends Event> void addEventListener(EventType<E> type, Consumer<E> listener) {
eventHandler.addListener(type, listener);
}
protected <E extends Event> void removeEventListener(EventType<E> type, Consumer<E> listener) {
eventHandler.removeListener(type, listener);
}
}

View File

@@ -1,126 +0,0 @@
package com.bartlomiejpluta.base.lib.gui;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.event.Event;
import com.bartlomiejpluta.base.api.gui.*;
import com.bartlomiejpluta.base.api.screen.Screen;
import lombok.Getter;
import lombok.NonNull;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
public abstract class BaseWindow extends BaseWidget implements Window {
@Getter
private final Map<String, Component> refs;
protected final Context context;
protected final GUI gui;
protected WindowManager manager;
protected Component content;
protected WindowPosition windowPosition = WindowPosition.CENTER;
@Getter
protected CompletableFuture<Object> future;
protected BaseWindow(Context context, GUI gui, Map<String, Component> refs) {
this.context = context;
this.gui = gui;
this.refs = refs;
}
protected void close() {
if (manager != null) {
manager.close();
}
}
protected void resolve(Object result) {
if (manager != null) {
manager.resolve(result);
}
}
@Override
public void setContent(Component component) {
if (this.content != null) {
this.content.setParent(null);
}
this.content = component;
if (component != null) {
component.setParent(this);
}
}
@Override
public Component reference(@NonNull String ref) {
return requireNonNull(refs.get(ref), format("Referenced component (with ref=[%s]) does not exist", ref));
}
@Override
public <T extends Component> T reference(String ref, Class<T> type) {
return type.cast(requireNonNull(refs.get(ref), format("Referenced component (with ref=[%s]) does not exist", ref)));
}
@Override
public WindowPosition getWindowPosition() {
return windowPosition;
}
@Override
@Attribute("windowPosition")
public void setWindowPosition(WindowPosition windowPosition) {
this.windowPosition = requireNonNull(windowPosition);
}
@Override
protected float getContentWidth() {
return content.getMarginLeft() + content.getActualWidth() + content.getMarginRight();
}
@Override
protected float getContentHeight() {
return content.getMarginTop() + content.getActualHeight() + content.getMarginBottom();
}
@Override
public void draw(Screen screen, GUI gui) {
content.setPosition(x + paddingLeft, y + paddingTop);
content.draw(screen, gui);
}
@Override
public <E extends Event> void handleEvent(E event) {
if (content != null) {
content.handleEvent(event);
}
if(!event.isConsumed()) {
super.handleEvent(event);
}
}
@Override
public void update(float dt) {
if (content != null) {
content.update(dt);
}
}
@Override
public void onOpen(WindowManager manager, Object[] args) {
this.manager = manager;
this.future = new CompletableFuture<>();
content.focus();
}
@Override
public void onClose(WindowManager manager) {
this.manager = null;
content.blur();
}
}

View File

@@ -1,209 +0,0 @@
package com.bartlomiejpluta.base.lib.gui;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.gui.Attribute;
import com.bartlomiejpluta.base.api.gui.Component;
import com.bartlomiejpluta.base.api.gui.GUI;
import com.bartlomiejpluta.base.api.gui.SizeMode;
import com.bartlomiejpluta.base.api.screen.Screen;
import java.util.Map;
public class BorderLayout extends BaseContainer {
public BorderLayout(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
super.setSizeMode(SizeMode.RELATIVE, SizeMode.RELATIVE);
super.setSize(1f, 1f);
}
@Override
public void add(Component component) {
if (!(component instanceof Slot)) {
throw new IllegalStateException("Border layout directly accepts only Slot children");
}
super.add(component);
}
@Override
@Attribute("width")
public void setWidth(String width) {
super.setWidth(width);
if (widthMode == SizeMode.AUTO) {
throw new IllegalStateException("Border layout does not support AUTO sizing mode");
}
}
@Override
@Attribute("height")
public void setHeight(String height) {
super.setHeight(height);
if (heightMode == SizeMode.AUTO) {
throw new IllegalStateException("Border layout does not support AUTO sizing mode");
}
}
@Override
public final void setSizeMode(SizeMode widthMode, SizeMode heightMode) {
if (widthMode == SizeMode.AUTO || heightMode == SizeMode.AUTO) {
throw new IllegalStateException("Border layout does not support AUTO sizing mode");
}
super.setSizeMode(widthMode, heightMode);
}
@Override
public final void setWidthMode(SizeMode mode) {
if (mode == SizeMode.AUTO) {
throw new IllegalStateException("Border layout does not support AUTO sizing mode");
}
super.setWidthMode(mode);
}
@Override
public final void setHeightMode(SizeMode mode) {
if (mode == SizeMode.AUTO) {
throw new IllegalStateException("Border layout does not support AUTO sizing mode");
}
super.setHeightMode(mode);
}
@Override
protected final float getContentWidth() {
return 0;
}
@Override
protected final float getContentHeight() {
return 0;
}
@Override
public void draw(Screen screen, GUI gui) {
for (var child : children) {
((Slot) child).setPosition(this);
child.draw(screen, gui);
}
}
public static abstract class Slot extends ComponentWrapper {
protected Slot(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
}
protected abstract void setPosition(BorderLayout layout);
}
public static class Top extends Slot {
public Top(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
}
@Override
protected void setPosition(BorderLayout layout) {
setPosition((layout.getWidth() - getWidth()) / 2, layout.getMarginTop() + layout.getPaddingTop());
}
}
public static class TopRight extends Slot {
public TopRight(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
}
@Override
protected void setPosition(BorderLayout layout) {
setPosition(layout.getWidth() - getWidth() - getMarginRight() - layout.getPaddingRight(), getMarginTop() + layout.getPaddingTop());
}
}
public static class Right extends Slot {
public Right(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
}
@Override
protected void setPosition(BorderLayout layout) {
setPosition(layout.getWidth() - getWidth() - getMarginRight() - layout.getPaddingRight(), (layout.getHeight() - getHeight()) / 2);
}
}
public static class BottomRight extends Slot {
public BottomRight(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
}
@Override
protected void setPosition(BorderLayout layout) {
setPosition(layout.getWidth() - getWidth() - getMarginRight() - layout.getPaddingRight(), layout.getHeight() - getHeight() - getMarginBottom() - layout.getPaddingBottom());
}
}
public static class Bottom extends Slot {
public Bottom(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
}
@Override
protected void setPosition(BorderLayout layout) {
setPosition((layout.getWidth() - getWidth()) / 2, layout.getHeight() - getHeight() - getMarginBottom() - layout.getPaddingBottom());
}
}
public static class BottomLeft extends Slot {
public BottomLeft(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
}
@Override
protected void setPosition(BorderLayout layout) {
setPosition(getMarginLeft() + layout.getPaddingLeft(), layout.getHeight() - getHeight() - getMarginBottom() - layout.getPaddingBottom());
}
}
public static class Left extends Slot {
public Left(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
}
@Override
protected void setPosition(BorderLayout layout) {
setPosition(getMarginLeft() + layout.getPaddingLeft(), (layout.getHeight() - getHeight()) / 2);
}
}
public static class TopLeft extends Slot {
public TopLeft(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
}
@Override
protected void setPosition(BorderLayout layout) {
setPosition(getMarginLeft() + layout.getPaddingLeft(), getMarginTop() + layout.getPaddingTop());
}
}
public static class Center extends Slot {
public Center(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
}
@Override
protected void setPosition(BorderLayout layout) {
setPosition((layout.getWidth() - getWidth()) / 2, (layout.getHeight() - getHeight()) / 2);
}
}
}

View File

@@ -1,45 +0,0 @@
package com.bartlomiejpluta.base.lib.gui;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.gui.Component;
import com.bartlomiejpluta.base.api.gui.GUI;
import com.bartlomiejpluta.base.api.screen.Screen;
import java.util.Map;
import static java.util.Objects.requireNonNull;
public abstract class ComponentWrapper extends BaseComponent {
protected Component component;
protected ComponentWrapper(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
}
@Override
public void add(Component component) {
this.component = requireNonNull(component);
component.setParent(this);
}
@Override
protected float getContentWidth() {
return component.getMarginLeft() + component.getWidth() + component.getMarginRight();
}
@Override
protected float getContentHeight() {
return component.getMarginTop() + component.getHeight() + component.getMarginBottom();
}
@Override
public void update(float dt) {
component.update(dt);
}
@Override
public void draw(Screen screen, GUI gui) {
component.setPosition(x + paddingLeft + component.getMarginLeft(), y + paddingTop + component.getMarginTop());
component.draw(screen, gui);
}
}

View File

@@ -1,84 +0,0 @@
package com.bartlomiejpluta.base.lib.gui;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.gui.Attribute;
import com.bartlomiejpluta.base.api.gui.Color;
import com.bartlomiejpluta.base.api.gui.Component;
import com.bartlomiejpluta.base.api.gui.GUI;
import com.bartlomiejpluta.base.api.screen.Screen;
import com.bartlomiejpluta.base.util.profiler.FPSProfiler;
import java.util.Map;
public class FPSMonitor extends BaseComponent {
private final Color color;
private final Color background;
private FPSProfiler fpsProfiler = FPSProfiler.create(5, 40);
private float lineWidth = 1f;
public FPSMonitor(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
color = gui.createColor();
background = gui.createColor();
color.setRGBA(0xFF0000FF);
background.setRGBA(0x444444AA);
}
@Attribute(value = "monitor", separator = ",")
public void setMonitor(Integer batchSize, Integer samples) {
this.fpsProfiler = FPSProfiler.create(batchSize, samples);
}
public void setColor(Integer hex) {
color.setRGBA(hex);
}
public void setBackground(Integer hex) {
background.setRGBA(hex);
}
public void setLineWidth(Float width) {
this.lineWidth = width;
}
@Override
protected float getContentWidth() {
return width;
}
@Override
protected float getContentHeight() {
return height;
}
@Override
public void update(float dt) {
super.update(dt);
fpsProfiler.update(dt);
}
@Override
public void draw(Screen screen, GUI gui) {
var values = fpsProfiler.getSamples();
var actualHeight = height - paddingTop - paddingBottom;
var step = (width - paddingLeft - paddingRight) / values.size();
gui.beginPath();
gui.drawRectangle(x, y, width, height);
gui.setFillColor(background);
gui.fill();
gui.closePath();
gui.beginPath();
gui.moveTo(x + paddingLeft, (float) (actualHeight - values.get(0) / 60 * actualHeight + y + paddingTop));
for (int i = 1; i < values.size(); ++i) {
gui.drawLineTo(i * step + x + paddingLeft, (float) (actualHeight - values.get(i) / 60 * actualHeight + y + paddingTop));
}
gui.setStrokeColor(color);
gui.setStrokeWidth(lineWidth);
gui.stroke();
gui.closePath();
}
}

View File

@@ -1,105 +0,0 @@
package com.bartlomiejpluta.base.lib.gui;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.gui.Component;
import com.bartlomiejpluta.base.api.gui.GUI;
import com.bartlomiejpluta.base.api.screen.Screen;
import lombok.NonNull;
import java.util.Map;
public class HGridLayout extends BaseContainer {
protected float offsetX = 0.0f;
protected float offsetY = 0.0f;
protected int rows = 2;
private float[] heights = new float[rows];
public HGridLayout(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
}
public void setRows(@NonNull Integer rows) {
this.rows = rows;
heights = new float[rows];
}
@Override
protected float getContentWidth() {
int lastId = children.size() - 1;
var totalWidth = 0f;
var maxWidth = 0f;
var width = 0f;
var i = 0;
for (var child : children) {
width = child.getMarginLeft() + child.getWidth() + child.getMarginRight();
if (maxWidth < width) {
maxWidth = width;
}
if (i % rows == rows - 1 || i == lastId) {
totalWidth += maxWidth;
maxWidth = 0f;
}
++i;
}
return totalWidth;
}
@Override
protected float getContentHeight() {
var maxHeight = 0f;
for (var height : heights) {
maxHeight += height;
}
return maxHeight;
}
@Override
public void draw(Screen screen, GUI gui) {
var currentX = x + paddingLeft + offsetX;
var currentY = y + paddingTop + offsetY;
var maxWidth = 0f;
var i = 0;
for (var child : children) {
var index = i % rows;
var height = child.getMarginTop() + child.getHeight() + child.getMarginBottom();
if (heights[index] < height) {
heights[index] = height;
}
++i;
}
var row = 0;
for (var child : children) {
var childAbsX = currentX + child.getMarginLeft();
var childAbsY = currentY + child.getMarginTop();
child.setX(childAbsX);
child.setY(childAbsY);
if (row < rows - 1) {
currentY += heights[row];
var currentWidth = child.getMarginLeft() + child.getWidth() + child.getMarginRight();
if (maxWidth < currentWidth) {
maxWidth = currentWidth;
}
++row;
} else {
row = 0;
currentY = y + paddingTop + offsetY;
currentX += maxWidth;
maxWidth = 0f;
}
child.draw(screen, gui);
}
}
}

View File

@@ -1,220 +0,0 @@
package com.bartlomiejpluta.base.lib.gui;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.event.Event;
import com.bartlomiejpluta.base.api.gui.Component;
import com.bartlomiejpluta.base.api.gui.GUI;
import com.bartlomiejpluta.base.api.input.Key;
import com.bartlomiejpluta.base.api.input.KeyAction;
import com.bartlomiejpluta.base.api.input.KeyEvent;
import com.bartlomiejpluta.base.api.screen.Screen;
import com.bartlomiejpluta.base.util.math.MathUtil;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import java.util.EnumSet;
import java.util.Map;
import java.util.function.Consumer;
public class HGridOptionChoice extends HGridLayout {
private static final EnumSet<KeyAction> ACTIONS = EnumSet.of(KeyAction.PRESS, KeyAction.REPEAT);
private int selectedRow = 0;
private int selectedColumn = 0;
private float scrollX = 0;
private float scrollY = 0;
private float scrollSpeedX = 1f;
private float scrollSpeedY = 1f;
@Getter
private Component selectedComponent = null;
@Setter
private Consumer<Component> onSelect;
public HGridOptionChoice(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
addEventListener(KeyEvent.TYPE, this::switchOption);
}
public void setScrollSpeed(@NonNull Float scrollSpeed) {
var coerced = MathUtil.clamp(scrollSpeed, 0f, 1f);
this.scrollSpeedX = coerced;
this.scrollSpeedY = coerced;
}
public void setScrollSpeedX(@NonNull Float scrollSpeedX) {
this.scrollSpeedX = MathUtil.clamp(scrollSpeedX, 0f, 1f);
}
public void setScrollSpeedY(@NonNull Float scrollSpeedY) {
this.scrollSpeedY = MathUtil.clamp(scrollSpeedY, 0f, 1f);
}
@Override
public void focus() {
super.focus();
if (!children.isEmpty()) {
selectedComponent = children.get(rows * selectedColumn + selectedRow);
selectedComponent.focus();
}
}
public void select(int row, int column) {
selectedRow = row;
selectedColumn = column;
selectedComponent = children.get(rows * selectedColumn + selectedRow);
if(onSelect != null) {
onSelect.accept(selectedComponent);
}
}
@Override
public <E extends Event> void handleEvent(E event) {
if(!focused) {
return;
}
var index = rows * selectedColumn + selectedRow;
if (index < children.size()) {
selectedComponent = children.get(index);
selectedComponent.handleEvent(event);
}
if (!event.isConsumed()) {
eventHandler.handleEvent(event);
}
}
private void switchOption(KeyEvent event) {
if (children.isEmpty()) {
return;
}
if (event.getKey() == Key.KEY_DOWN && ACTIONS.contains(event.getAction())) {
selectNextV();
event.consume();
} else if (event.getKey() == Key.KEY_UP && ACTIONS.contains(event.getAction())) {
selectPreviousV();
event.consume();
} else if (event.getKey() == Key.KEY_RIGHT && ACTIONS.contains(event.getAction())) {
selectNextH();
event.consume();
} else if (event.getKey() == Key.KEY_LEFT && ACTIONS.contains(event.getAction())) {
selectPreviousH();
event.consume();
}
}
public void selectPreviousH() {
blurChildren();
int size = 0;
for (int i = 0; i < children.size(); ++i) {
if (i % rows == selectedRow) ++size;
}
if (size == 0) return;
selectedColumn = ((--selectedColumn) + size) % size;
selectedComponent = children.get(rows * selectedColumn + selectedRow);
selectedComponent.focus();
if(onSelect != null) {
onSelect.accept(selectedComponent);
}
}
public void selectNextH() {
blurChildren();
int size = 0;
for (int i = 0; i < children.size(); ++i) {
if (i % rows == selectedRow) ++size;
}
if (size == 0) return;
selectedColumn = (++selectedColumn) % size;
selectedComponent = children.get(rows * selectedColumn + selectedRow);
selectedComponent.focus();
if(onSelect != null) {
onSelect.accept(selectedComponent);
}
}
public void selectPreviousV() {
blurChildren();
int size = 0;
for (int i = 0; i < children.size(); ++i) {
if (i / rows == selectedColumn) ++size;
}
if (size == 0) return;
selectedRow = ((--selectedRow) + size) % size;
selectedComponent = children.get(rows * selectedColumn + selectedRow);
selectedComponent.focus();
if(onSelect != null) {
onSelect.accept(selectedComponent);
}
}
public void selectNextV() {
blurChildren();
int size = 0;
for (int i = 0; i < children.size(); ++i) {
if (i / rows == selectedColumn) ++size;
}
if (size == 0) return;
selectedRow = (++selectedRow) % size;
selectedComponent = children.get(rows * selectedColumn + selectedRow);
selectedComponent.focus();
if(onSelect != null) {
onSelect.accept(selectedComponent);
}
}
@Override
public void update(float dt) {
super.update(dt);
if (selectedComponent != null) {
if (selectedComponent.getX() + selectedComponent.getWidth() > getWidth() + getX()) {
scrollX += (selectedComponent.getWidth() + selectedComponent.getX() - (getWidth() + getX())) * scrollSpeedX;
} else if (selectedComponent.getX() < getX()) {
scrollX += (selectedComponent.getX() - getX()) * scrollSpeedX;
}
}
if (selectedComponent != null) {
if (selectedComponent.getY() + selectedComponent.getHeight() > getHeight() + getY()) {
scrollY += (selectedComponent.getHeight() + selectedComponent.getY() - (getHeight() + getY())) * scrollSpeedY;
} else if (selectedComponent.getY() < getY()) {
scrollY += (selectedComponent.getY() - getY()) * scrollSpeedY;
}
}
}
@Override
public void draw(Screen screen, GUI gui) {
gui.clip(x, y, getWidth(), getHeight());
offsetX = -scrollX;
offsetY = -scrollY;
super.draw(screen, gui);
gui.resetClip();
}
}

View File

@@ -1,44 +0,0 @@
package com.bartlomiejpluta.base.lib.gui;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.gui.Component;
import com.bartlomiejpluta.base.api.gui.GUI;
import com.bartlomiejpluta.base.api.screen.Screen;
import java.util.Map;
public class HLayout extends BaseContainer {
protected float offsetX = 0.0f;
protected float offsetY = 0.0f;
public HLayout(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
}
@Override
protected float getContentWidth() {
return sumChildrenWidth();
}
@Override
protected float getContentHeight() {
return maxChildrenHeight();
}
@Override
public void draw(Screen screen, GUI gui) {
var currentX = x + paddingLeft + offsetX;
var currentY = y + paddingTop + offsetY;
for (var child : children) {
var childAbsX = currentX + child.getMarginLeft();
var childAbsY = currentY + child.getMarginTop();
child.setX(childAbsX);
child.setY(childAbsY);
currentX += child.getMarginLeft() + child.getWidth() + child.getMarginRight();
child.draw(screen, gui);
}
}
}

View File

@@ -1,135 +0,0 @@
package com.bartlomiejpluta.base.lib.gui;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.event.Event;
import com.bartlomiejpluta.base.api.gui.Component;
import com.bartlomiejpluta.base.api.gui.GUI;
import com.bartlomiejpluta.base.api.input.Key;
import com.bartlomiejpluta.base.api.input.KeyAction;
import com.bartlomiejpluta.base.api.input.KeyEvent;
import com.bartlomiejpluta.base.api.screen.Screen;
import com.bartlomiejpluta.base.util.math.MathUtil;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import java.util.EnumSet;
import java.util.Map;
import java.util.function.Consumer;
public class HOptionChoice extends HLayout {
private static final EnumSet<KeyAction> ACTIONS = EnumSet.of(KeyAction.PRESS, KeyAction.REPEAT);
private int selected = 0;
private float scroll = 0;
private float scrollSpeed = 1f;
@Getter
private Component selectedComponent = null;
@Setter
private Consumer<Component> onSelect;
public HOptionChoice(Context context, GUI gui, Map<String, Component> refs) {
super(context, gui, refs);
addEventListener(KeyEvent.TYPE, this::switchOption);
}
public void setScrollSpeed(@NonNull Float scrollSpeed) {
this.scrollSpeed = MathUtil.clamp(scrollSpeed, 0f, 1f);
}
@Override
public void focus() {
super.focus();
if (!children.isEmpty()) {
selectedComponent = children.get(selected);
selectedComponent.focus();
}
}
public void select(int index) {
selected = index;
selectedComponent = children.get(selected);
if(onSelect != null) {
onSelect.accept(selectedComponent);
}
}
@Override
public <E extends Event> void handleEvent(E event) {
if(!focused) {
return;
}
if (selected < children.size()) {
selectedComponent = children.get(selected);
selectedComponent.handleEvent(event);
}
if (!event.isConsumed()) {
eventHandler.handleEvent(event);
}
}
private void switchOption(KeyEvent event) {
if (children.isEmpty()) {
return;
}
if (event.getKey() == Key.KEY_RIGHT && ACTIONS.contains(event.getAction())) {
selectNext();
event.consume();
} else if (event.getKey() == Key.KEY_LEFT && ACTIONS.contains(event.getAction())) {
selectPrevious();
event.consume();
}
}
public void selectPrevious() {
blurChildren();
var size = children.size();
selected = (((--selected) % size) + size) % size;
selectedComponent = children.get(selected);
selectedComponent.focus();
if (onSelect != null) {
onSelect.accept(selectedComponent);
}
}
public void selectNext() {
blurChildren();
selected = (++selected) % children.size();
selectedComponent = children.get(selected);
selectedComponent.focus();
if (onSelect != null) {
onSelect.accept(selectedComponent);
}
}
@Override
public void update(float dt) {
super.update(dt);
if (selectedComponent != null) {
if (selectedComponent.getX() + selectedComponent.getWidth() > getWidth() + getX()) {
scroll += (selectedComponent.getWidth() + selectedComponent.getX() - (getWidth() + getX())) * scrollSpeed;
} else if (selectedComponent.getX() < getX()) {
scroll += (selectedComponent.getX() - getX()) * scrollSpeed;
}
}
}
@Override
public void draw(Screen screen, GUI gui) {
gui.clip(x, y, getWidth(), getHeight());
offsetX = -scroll;
super.draw(screen, gui);
gui.resetClip();
}
}

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