hexworks / zircon Goto Github PK
View Code? Open in Web Editor NEWZircon is an extensible and user-friendly, multiplatform tile engine.
Home Page: https://hexworks.org/projects/zircon/
License: Apache License 2.0
Zircon is an extensible and user-friendly, multiplatform tile engine.
Home Page: https://hexworks.org/projects/zircon/
License: Apache License 2.0
There should be a simple format for monospaced tilesets (like with Dwarf Fortress tilesets but supporting Unicode). The format should also contain metadata about the image - character lookup.
If there are more images for a character they should be differentiated (by tagging possibly).
This format should be specified as part of this task.
A terminal has the function putCharacter, which prints a character and moves the cursor one column further.
It would be nice to have an overloaded function which accepts a TextChar, as well that the screen implements the same.
There should be a Terminal implementation which uses JavaFx instead of Swing.
See conversation with Adam:
So this is how it works:
We have a VirtualTerminal class: https://github.com/Hexworks/zircon/blob/master/src/main/kotlin/org/codetome/zircon/internal/terminal/virtual/VirtualTerminal.kt
this handles all the "virtual" aspects of the Terminal interface
including dirty checking, resizing, and modifying its contents (TextCharacters, Layers, etc)
this basically means that you only have to implement the rendering logic if you want to have a JavaFX implementation
but
We have the renderig logic which is common to Java2D implementations in Java2DTerminalImplementation : https://github.com/Hexworks/zircon/blob/master/src/main/kotlin/org/codetome/zircon/internal/terminal/swing/Java2DTerminalImplementation.kt
This class handles the lifecycle of the underlying GUI framework, like onCreated and onDestroyed
and it also handles how drawing happens (cursor, coloring, TextCharacters and such)
and what we have in mind right now is to make this class generic
by extracting all Java2D specific aspects
and using interfaces to represent them
so after this refactor implementing something like a JavaFX version will be much easier
So that issue should be on hold until the mentioned refactoring is done.
A basic component system should be implemented. Depends on:
TextGraphics
First controls to implement:
Currently we use ComponentsLayerable
:
class ComponentsLayerable(
private val layerable: Layerable,
private val components: DefaultContainer)
: Layerable by layerable {
override val layers: List<Layer>
get() = components.toFlattenedLayers().plus(layerable.layers)
}
which composes a simple Layerable
and a Container
to manage both Component
s and simple Layer
s.
This should be improved by having a stack of Layerable
s:
Component
sComponent
s should be ModalComponent
s which have a size of the screen so the user can't interact with ordinary Component
s below a modal.Layer
s.Things to do:
addModal
next to addComponent
to ComponentContainer
.CompoentContainer
instead of what we have now in TileGridScreen
DefaultComponentContainer
into its own classThere should be an option to load fonts as bitmaps (using BMFont exports for example). Currently there is a custom loader which loads CP437 based Dwarf Fortress tilesets. There should be another one which reads custom tileset files (this format should be researched and implemented in #6 ).
The flag goes into TerminalBuilder
.
This is important for encapsulating the draw logic without hard-coding GUI technology-specific details. So BufferedImage
will become some interface
which will have methods which will have different implementations depending on the underlying GUI tech (BufferedImage
in Java2D and TextureRegion
in libGDX for example).
Using a tiled window manager that allows floating windows, when moving the mouse beyond the left hand side it generates the following exception
AWT-EventQueue-0] ERROR org.codetome.zircon.internal.terminal.swing.SwingTerminal$listener$1 - position for mouse event 'java.lang.IllegalArgumentException: A position must have a column and a row number which is greater than or equal to 0!' was out of bounds. It is dropped.
java.lang.IllegalArgumentException: A position must have a column and a row number which is greater than or equal to 0!
at org.codetome.zircon.api.Position.<init>(Position.kt:14)
at org.codetome.zircon.api.Position$Companion.of(Position.kt:181)
at org.codetome.zircon.internal.terminal.swing.TerminalMouseListener.addActionToKeyQueue(TerminalMouseListener.kt:77)
at org.codetome.zircon.internal.terminal.swing.TerminalMouseListener.mouseExited(TerminalMouseListener.kt:53)
at java.awt.Component.processMouseEvent(Component.java:6539)
at java.awt.Component.processEvent(Component.java:6298)
at java.awt.Component.dispatchEventImpl(Component.java:4889)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:90)
at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.awt.EventQueue$4.run(EventQueue.java:729)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
CTRL+Z
(and probably other combinations) do not workCtrl+Z should be catched correctly, that is keystroke.getCharacter should return 'z'
keystroke.getCharacter returns '\u001A' 26
Tested on Windows 10 + Ubuntu 17.10 using JDK 9.0.1
import org.codetome.zircon.api.Position
import org.codetome.zircon.api.Size
import org.codetome.zircon.api.builder.TerminalBuilder
import org.codetome.zircon.api.builder.TextImageBuilder
import org.codetome.zircon.api.resource.CP437TilesetResource
import java.util.function.Consumer
object InputTest {
@JvmStatic
fun main(args: Array<String>) {
val terminal = TerminalBuilder.newBuilder()
.initialTerminalSize(Size.of(32, 16))
.font(CP437TilesetResource.REX_PAINT_18X18.toFont())
.build()
val screen = TerminalBuilder.createScreenFor(terminal)
val textImage = TextImageBuilder.newBuilder().size(Size(10, 10)).build()
textImage.putText("hi", Position(2, 2))
screen.setCharacterAt(Position.of(2, 2), 'e')
screen.onInput(listener = Consumer { input ->
run {
if (input.isKeyStroke())
println(input.asKeyStroke().toString())
}
})
screen.display()
}
}
Remark: Issues seems to be within AWT: KeyEvent also already the wrong character
Animation
s should be specialized Layer
s. An animation is basically a linked list of Layer
s which represent some animation. An Animation
can be moving, or stationary and consists of frames. Animation
s should be governed by a simplified GameLoop pattern. Some characteristics:
STATIONARY
or some direction like RIGHT
Animation
should be removed after the last frameTextColor
should support transform operations:
tint
: adding a different color to this oneshade
: lighten or darken the colorinvert
: flip the color to its opposite (blue vs red for example)These operations should go into the TextColor
interface and work in a way which is consistent with all other operations on immutable objects: they should create a new TextColor
object with the result of the operation.
The implementations should go into DefaultTextColor
.
More information about how tinting and shading works can be found here.
Characters should support some additional Modifier
s like
VERTICAL_FLIP
HORIZONTAL_FLIP
INVISIBLE
If any ANSITextColor
enum constant is referenced from a unit test weird things happen like NoClassDefFoundError
s or NullPointerException
s. This should be investigated. It is not sure whether this is a Kotlin issue or a Java one.
Currently if we have a connecting box (which connects to other boxes) and the other boxes are gone the connecting characters still remain which is ugly. This needs to be fixed.
There should be an Application
/ Renderer
implementation which uses libGDX instead of Swing.
Basically we need to be able to replace EMPTY
TextCharacter
s with empty coordinates to spare memory.
A new pair of behaviors (FontOverrideSupport
and FontOverride
) will be introduced.
FontOverrideSupport
will be implemented in Layerable
, Terminal
and Screen
while FontOverriede
will be implemented in Component
, Layer
and Terminal
.
FontOverrideSupport
is an internal interface which is responsible for knowing what Font
Size
can be used in them (this comes from the TerminalBuilder
which a Terminal
is built from and can't be changed later).
FontOverride
holds a reference to a Font
object which will be used when drawing. If you try to add / draw / etc an object which implements FontOverride
to an object which implements FontOverrideSupport
, the Font
's size will be checked against the information in the object implementing FontOverrideSupport
. If the Size
does not fit the object will throw an exception.
The current rule is that there can be one font Size
for each Terminal
object and every other object must use Font
s with that size.
As a later feature half-width fonts will be also supported (eg. 16x16 + 8x16).
Example:
Exception in thread "main" kotlin.NotImplementedError: An operation is not implemented.
at org.codetome.zircon.internal.font.impl.FontSettings$NO_FONT$1.getWidth(FontSettings.kt:18)
at org.codetome.zircon.api.font.Font$DefaultImpls.getSize(Font.kt:45)
at org.codetome.zircon.internal.font.impl.FontSettings$NO_FONT$1.getSize(FontSettings.kt:11)
at org.codetome.zircon.internal.component.impl.DefaultContainer.addComponent(DefaultContainer.kt:46)
at org.codetome.zircon.examples.interactive.ShootingExample.main(ShootingExample.java:91)
This is because the container like a Panel
does not have a Font
by default. The reason for this is that if we would have had a default font with any size it would have lead to an error if the user would have used a different sized font as our default.
Since the user sets the font for the whole Screen
we can't add a component to another container component until a Font
is set for it.
This needs to be clarified in the error message above!
A "Log" refers to a GUI component, often used in roguelike games to display information to the user about a variety of events.
A dedicated "Log" Component would be a useful way to easily provide this functionality in a GUI.
The Orc's attack hits!
The Orc attacks you!
You missed...
You attacked the Orc!
The current work around is to use a TextImage:
For Example
public class Playground {
public static void main(String[] args) {
final Terminal terminal = TerminalBuilder.newBuilder()
.font(CP437TilesetResource.REX_PAINT_20X20.toFont())
.initialTerminalSize(Size.of(10, 5))
.build();
final TextImage img = TextImageBuilder.newBuilder()
.size(Size.of(10, 5))
.build(); // we create a new image to draw onto the terminal
img.setForegroundColor(ANSITextColor.WHITE);
img.setBackgroundColor(ANSITextColor.BLUE); // `putText` will use these
BoxBuilder.newBuilder()
.boxType(BoxType.DOUBLE)
.size(Size.of(10, 5))
.style(StyleSetBuilder.newBuilder()
.foregroundColor(ANSITextColor.CYAN)
.backgroundColor(ANSITextColor.BLUE)
.build())
.build()
.drawOnto(img, Position.DEFAULT_POSITION); // we create a box and draw it onto the image
final List<String> logElements = new ArrayList<>();
logElements.add("foo");
logElements.add("bar"); // our log entries
for(int i = 0; i < logElements.size(); i++) {
img.putText(logElements.get(i), Position.OFFSET_1x1.withRelativeRow(i)); // we have to offset because of the box
}
terminal.draw(img, Position.DEFAULT_POSITION); // you have to draw each time the image changes
terminal.flush();
}
}
TextBox
Currently, the TextBox is not tested and its implementation needs some refurbishment.
Tests should be written for the use cases (including the corner cases) and the implementation should be refactored according to the changes made in Scrollable.
TextCharacterString
s are an aggregation of TextCharacter
s into an array. You can draw a TextCharacterString
onto any DrawSurface
and you can expect it to behave in a way like handwriting would (if a string does not fit in a line it continues in a new line).
Text wrapping is managed by TextWrap
which is an enum
with NO_WRAPPING
and WRAP
options.
TextCharacterString
comes with its own builder and you can create them in a simple way from plain Java String
s.
Drag'n drop will work for floating windows in the first implementation.
A FloatingWindow
can be dragged when the mouse is pressed anywhere over its DragHandle
(an arbitrary region over the component configured by the user) region.
On the drag event the window should be repositioned and its bounds checked with other floating windows.
Related to #23 which will use this
Depends on #24 since it will introduce the layerable stack
A MapComponent
is a game-related component which
MapComponent
. This can be done by writing an interface
, MapSegment
which abstracts the means of providing TextCharacter
and Layer
information for the MapComponent
.For the first prototype version an abstraction for a 2D space is desired, but this needs to be extendable to a 3D segment (x, y, z).
Currently it is not possible to embed an arbitrary image/bitmap (e.g. a background image)
Embed background images or as a GUI element
Ideally, an approach like the TextImage would be provided to embed images.
Also it might be useful to have a dedicated component for embedding a bitmap as a GUI control
A Player handler should be a simple utility for handling the player @
and it should also take into account scrolling.
This is a common enough feature for roguelikes.
A PlayerHandler
should handle how the player is added to a GameComponent
as a layer (TextImage
) and how it should be positioned when the user moves the player to the edges (in this case it won't stay in the center).
The Screen
class should support layering. A layer is an additional 2d plane over or under the Screen
layer which is drawn accordingly (from bottom to top).
A Layer
should be a TextImage
and should support all operations defined on it.
A Layer
should support visibility toggling.
If a TextCharacterString has a length of 1, it crashes with
at org.codetome.zircon.internal.graphics.DefaultTextCharacterString.drawOnto(DefaultTextCharacterString.kt:35) at org.codetome.zircon.internal.terminal.virtual.VirtualTerminal.draw(VirtualTerminal.kt:66) at org.codetome.zircon.internal.screen.TerminalScreen.draw(TerminalScreen.kt)
(Iterator is exhausted)
Example:
object Sandbox {
@JvmStatic
fun main(args: Array<String>) {
val terminal = TerminalBuilder.newBuilder()
.initialTerminalSize(Size.of(32, 16))
.font(CP437TilesetResource.REX_PAINT_18X18.toFont())
.build();
val screen = TerminalBuilder.createScreenFor(terminal)
val stringOfLength1 = "D"
val textDrawable = TextCharacterStringBuilder.newBuilder()
.text(stringOfLength1)
.build()
screen.draw(textDrawable, Position.of(0,0))
screen.display()
}
}
A REXPaint file should be loaded into a Set
of Layer
s. Since REXPaint does not store font information this task is not related to font-based tasks. We need to look into this
Currently Zircon uses java.awt.Font
for fonts. This needs to be abstracted so a custom tileset or font can be used. This new Font
should also be used for real fonts (like the ones which come from ttf
files) and tilesets together but should be differentiated based on the format they store the data (BufferedImage
for swing and Texture
for Libgdx for example).
There should be an option for loading custom ttf
fonts. This is possible using java.awt.Font
and not hard to implement. This task depends on #2
If the user tries to add a non-printable character to the screen (ASCII 1 to 31) Zircon throws an exception right now:
// excerpt from Java2DFont.kt
require(hasDataForChar(textCharacter.getCharacter())) {
"No metadata exists for character: '${textCharacter.getCharacter().toInt()}'!"
}
val filtered = metadata[textCharacter.getCharacter()]!!.filter { it.tags.containsAll(tags.toList()) }
require(filtered.isNotEmpty()) {
"No metadata found for tag(s): ${tags.joinToString()}"
}
This needs to be clarified so users will know what the problem is:
textCharacter
has a non-printable character (can be checked with TextUtils#isPrintableCharacter
the exception should be something like
"Can't find font texture region for non-printable character 'x'"
"Can't find font texture region for tag(s): x,y,z"
Support for using multiple fonts which can be half-width (eg. using a 16x16 font with a 8x16 Font
).
Related: #31
The problem is that the supposedly static
methods miss the @JvmStatic
keyword.
Some extra modifiers will be added:
GaussianBlur
MotionBlur
Glow
RayShade
Sharpen
TextShadow
For each parameterized Modifier
an accompanying Builder
will be added.
There are some common behaviors already implemented (like CursorHolder
). This should be extended to graphical objects supporting:
Shadow
Scroll
Border
DragNDrop
I'm trying out examples in the README. First problem: enableModifier
is not a function of Terminal. Eg:
terminal.enableModifier(Modifiers.VERTICAL_FLIP);
A Floating window is an Component
which can have an arbitrary position on the screen over any ordinary Component
. This means that the boundary check should be relaxed for floating windows and they should be able to be added anywhere.
The caveat is that a floating window should not overlap with any other floating window.
Depends on #24 because it will introduce the Layerable
stack
Related to #22 because Drag'n drop is the main use case for moving floating windows
Dwarf Fortress tilesets need to be supported.
enum
If I run the code below a black background is drawn for the TextImage
. This bug should be investigated.
Example:
final Terminal terminal = TerminalBuilder.newBuilder()
.font(CP437TilesetResource.REX_PAINT_20X20.toFont())
.initialTerminalSize(Size.of(10, 5))
.build();
final TextImage img = TextImageBuilder.newBuilder()
.size(Size.of(10, 5))
.build(); // we create a new image to draw onto the terminal
img.setForegroundColor(ANSITextColor.WHITE);
img.setBackgroundColor(TextColorFactory.TRANSPARENT); // `putText` will use these
BoxBuilder.newBuilder()
.boxType(BoxType.DOUBLE)
.size(Size.of(10, 5))
.style(StyleSetBuilder.newBuilder()
.foregroundColor(ANSITextColor.CYAN)
.backgroundColor(ANSITextColor.BLUE)
.build())
.build()
.drawOnto(img, Position.DEFAULT_POSITION); // we create a box and draw it onto the image
final List<String> logElements = new ArrayList<>();
logElements.add("foo");
logElements.add("bar"); // our log entries
for(int i = 0; i < logElements.size(); i++) {
img.putText(logElements.get(i), Position.OFFSET_1x1.withRelativeRow(i)); // we have to offset because of the box
}
terminal.draw(img, Position.DEFAULT_POSITION); // you have to draw each time the image changes
terminal.flush();
A Box
should be connectable to other Box
es when it is drawn upon a DrawSurface
.
Target is < 1ms
. This task depends on #2
I'm trying to render the following simple sub-part of a screen:
This gets rendered correctly.
Now look at the following image:
The 'X' symbol in the row 'X P I' has changed to 'T P I ' (By intention by setting the appropriate TextCharacter). Now this has the effect that the '8' in the Mark 48 text gets changed to Mark 4T too!
Analysis
Sometimes debugging has the effect that the correct char is rendered!
See
I've set a conditional breakpoint in the mentioned function of ApplicationTerminal.kt . Since this slowed down the application considerably, I've seen once that the '8' got replaced by 'T' after a couple of seconds.
Remarks:
Snipped how the text is rendered
def drawSimpleAttribute(simpleAttribute: SimpleAttribute, xPos: Int): Unit = {
val attrDrawable =
if (simpleAttribute.bgColor.isEmpty)
TextCharacterStringBuilder.newBuilder()
.text(simpleAttribute.getValue)
.foregroundColor(surface.fxToTextColor(simpleAttribute.fgColor))
.build()
else
TextCharacterStringBuilder.newBuilder()
.text(simpleAttribute.getValue)
.foregroundColor(surface.fxToTextColor(simpleAttribute.fgColor))
.backgroundColor(surface.fxToTextColor(simpleAttribute.bgColor.get))
.build()
surface.screen.draw(attrDrawable, surface.getAbsolutePosition(Position.of(xPos, simpleAttribute.y + model.startYPos)))
}
If the experts also have no clue, I'll have to write a comprehensive test app, trying to reproduce this issue.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.