Initial Commit

This commit is contained in:
Aaron Helton
2017-07-13 23:13:08 -04:00
commit 8d56f8aae2
14 changed files with 805 additions and 0 deletions

65
.gitignore vendored Normal file
View File

@@ -0,0 +1,65 @@
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
bin/
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-debug/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### Intellij Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
.idea/sonarlint
# End of https://www.gitignore.io/api/intellij

22
.idea/TileGameEditor.iml generated Normal file
View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/res" type="java-resource" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="docs" level="project" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../../../Libraries/Java/PCollections/build/libs/pcollections-2.1.3-SNAPSHOT.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>

6
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="1.8 (1)" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/bin" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/TileGameEditor.iml" filepath="$PROJECT_DIR$/.idea/TileGameEditor.iml" />
</modules>
</component>
</project>

121
DesignDocument.txt Normal file
View File

@@ -0,0 +1,121 @@
Design Document for Nytework Tile Engine Version 0.1.0
This document describes the theoretical design of the Nytework Tile Engine,
Version 0.1.0. This Document was initially written by Aaron Helton, on July
10th, 2017.
The Nytework Tile Engine is designed primarily for four main component pieces.
The first two pieces describe the two different, physical pieces of software.
These are the Editor, and the Interpreter. The other two pieces describe the
Engine Implementation, and are colloquially known as the Upper Engine (UE) and
the Lower Engine (LE).
Physical Softare
The Nytework Tile Engine has two main pieces of software. The first is the
Editor, which is responsible for creating and working with elements of the Upper
Engine, hereon referred to as the UE.
Editor
The Editor is what allows potential RPG Game Creators to interface with
the Nytework Tile Engine's Upper Layer, where much of the game-specific
logic is designed, implemented, and tested. The Editor allows Game
Developers and Designers alike to collaborate on Games, which are
represented in a file format known simply as the NTE format, which is the
format in which individual are distributed and run. This format is described
later in the Design Document.
The Editor is different from the Interpreter, in that, individual games
cannot be "run" from the editor, and the editor does not implement the logic
necessary for the game to actually interface with the host operating system.
Instead, the editor is primarily focused on the more abstract part of creating
the actual game, and as such provides tools, utilities, and functionality
related specifically to designing, creating, debugging, and releasing these
games. The Editor does, however, provide the capability to execute the
interpreter with the WIP Game, so that functionality is not lost.
The Editor UI consists of a few, relatively simple elements. The center of
the Editor is always dominated by the whatever TileMap is currently being
worked on. Everything else, however, is configurable. Other elements of the
editor are presented in "docks", which can be freely moved about, removed,
expanded or shrunk, or added. These docks each provides various utilities
for editing portions of the TileMap, the Tiles themselves, the Quests
subsystem, the Actor Subsystem, or any other configurable portion of the
Upper Engine.
The Lower Engine
The Lower Engine is primarily implemented in the Interperter (See:
Interperter), and is focused primarly on communicating with the host system.
It is the implementation of the various subsystems that the Upper Engine may
utilize, such at the Actor Subsystem, Tile Subsystem, and the Quest
Subsystem. Much of the functionality is focused on providing a basis
specifically for RPG games.
The Lower Engine is responsible for the providing the environment,
libraries, tools, and any other utilities needed for the execution of Upper
Engine scripts. The Lower Engine directly interfaces with the host system to
create all the graphics necessary to render scenes as specified by the Upper
Engine.
The Lower Engine also managed some higher-level functionality that can't be
easily accomplished by the UE, such as swapping out the current map,
scrolling the screen, etc.
+--------------------------------------------------------------------------+
| Lower Engine |
+--------------------------------------------------------------------------+
| Quest Subsystem |
+--------------------------------------------------------------------------+
| Map Subsystem | |
+------------------------------+-------------------------------+ |
| Actor Subsystem | Tile Subsystem | Networking|
+--------------------------------------------------------------+ |
|File IO | Image Manipulation | Configuration | Error Handling | |
+--------------------------------------------------------------------------+
Fig 1.
The Interpreter
The Interpreter is the implementation of the Lower Engine, and is the piece
of software used to execute NTE games from .NTE files. It does this by first
indexing the NTE file to generate a Virtual File System that can be used by
the game for IO Purposes. Once the resources have been indexed,
NTE File Spec:
NTE is a PKZIP file format that contains the following standard entries:
Main.yaml -> Describes various global properties about the game, such
as its name, version, creators/credits, and what to do
at game startup based on various boolean entries.
Contents.yaml -> Contains a generated list of game resources, which
is used by the interpreter to build an index for
the game's VFS.
Maps.yaml -> Contains a list of the maps, providing their UUID and
Interpreter Process:
Startup
Read and Unzip .NTE file.
Read Main.yaml and set various global properties
Read Contents.yaml and create a VFS Index
If Main.yaml specified a Splash Screen:
Load Resources/Splash.png and display as specified size.
If Main.yaml specified to show Splash Progress:
Display progress of loading according to settings in Main.yaml
Read Maps.yaml and generate Map Index
Read Actors.yaml and generate Actor Index
Read Quests.yaml and generate Quest Index
Read Scripts.yaml and generate a Script Index
Once all indexes have been generated, load the Title Screen.

11
TileGameEditor.iml Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

BIN
res/Water.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,124 @@
package com.aargonian.com.aargonian.tile;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.pcollections.HashPMap;
import org.pcollections.HashTreePMap;
/**
* Created by aargonian on 7/4/17.
*/
public class TileImpl
{
/**
* Very important magic string values used by the tile class for common properties/values
*/
public static final String PROPERTY_TILETYPE = "TileType";
public static final String PROPERTY_IMG = "ImageRef";
public static final String VALUE_TRUE = "True";
public static final String VALUE_FALSE = "False";
private final HashPMap<String, String> properties;
private final HashPMap<String, Object> resources;
private int hash = 0;
public TileImpl(Map<String, String> startingProperties, Map<String, Object> startingResources)
{
if(startingProperties != null && startingProperties.size() > 0)
this.properties = HashTreePMap.from(startingProperties);
else
this.properties = null;
if(startingResources != null && startingResources.size() > 0)
this.resources = HashTreePMap.from(startingResources);
else
this.resources = null;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TileImpl tile = (TileImpl) o;
if (properties != null ? !properties.equals(tile.properties) : tile.properties != null) return false;
return resources != null ? resources.equals(tile.resources) : tile.resources == null;
}
@Override
public int hashCode() {
int result = hash;
if(result == 0) {
result = properties != null ? properties.hashCode() : 0;
result = 31 * result + (resources != null ? resources.hashCode() : 0);
hash = result;
}
return result;
}
private TileImpl(HashPMap<String, String> implProperties, HashPMap<String, Object> implResources)
{
this.properties = implProperties;
this.resources = implResources;
}
public TileImpl removeProperty(String property)
{
if(properties == null)
return this;
return new TileImpl(properties.minus(property), resources);
}
public String getProperty(String property)
{
if(properties == null)
return null;
return properties.get(property);
}
public TileImpl setProperty(String property, String value)
{
if(properties == null)
return new TileImpl(HashTreePMap.singleton(property, value), resources);
return new TileImpl(properties.plus(property, value), resources);
}
public boolean hasProperty(String property)
{
if(properties == null)
return false;
return properties.containsKey(property) && properties.get(property) != null;
}
public TileImpl removeResource(String resource)
{
if(resources == null)
return this;
return new TileImpl(properties, resources.minus(resource));
}
public Object getResource(String resource)
{
if(resources == null)
return null;
return resources.get(resource);
}
public TileImpl setResource(String resourceKey, Object resource)
{
if(resources == null)
return new TileImpl(properties, HashTreePMap.singleton(resourceKey, resource));
return new TileImpl(properties, resources.plus(resourceKey, resource));
}
public boolean hasResource(String resourceKey)
{
if(resources == null)
return false;
return resources.containsKey(resourceKey) && resources.get(resourceKey) != null;
}
}

View File

@@ -0,0 +1,124 @@
package com.aargonian.com.aargonian.tile;
import java.util.ArrayList;
/**
* Created by aargonian on 7/4/17.
*/
public class TileMap
{
private int rows;
private int columns;
private final ArrayList<TileImpl> tilesImplementations;
private final Tile[] tiles;
public TileMap(int columns, int rows)
{
this.columns = columns;
this.rows = rows;
tilesImplementations = new ArrayList<TileImpl>();
tilesImplementations.add(new TileImpl(null, null));
tilesImplementations.get(0).setProperty(TileImpl.PROPERTY_TILETYPE, "EmptyTile");
tiles = new Tile[columns * rows];
for(int i = 0; i < tiles.length; i++)
{
tiles[i] = new Tile(tilesImplementations.get(0));
}
}
public void addTileImplementation(TileImpl impl)
{
tilesImplementations.add(impl);
}
public ArrayList<TileImpl> getTileImplementations()
{
return tilesImplementations;
}
public int getRows() {
return rows;
}
public int getColumns() {
return columns;
}
private final void setTileImplementation(Tile tile, TileImpl newImpl)
{
for(TileImpl implementation : tilesImplementations)
{
if(implementation.hashCode() == newImpl.hashCode())
{
if(implementation.equals(newImpl))
{
tile.setTileImplementation(implementation);
newImpl = null;
return;
}
}
}
//For loop found no duplicates. Add the new implementation to the list.
tilesImplementations.add(newImpl);
tile.setTileImplementation(newImpl);
}
public String getTilePropertyAt(String property, int x, int y)
{
return tiles[(y * columns) + x].getTileImplementation().getProperty(property);
}
public void setTilePropertyAt(String property, String value, int x, int y)
{
Tile tile = tiles[(y*columns) + x];
TileImpl newImpl = tile.getTileImplementation().setProperty(property, value);
setTileImplementation(tile, newImpl);
}
public Object getTileResourceAt(String resource, int x, int y)
{
return tiles[(y*columns) + x].getTileImplementation().getResource(resource);
}
public void setTileResourceAt(String resourceKey, Object resource, int x, int y)
{
Tile tile = tiles[(y*columns)+x];
TileImpl newImpl = tile.getTileImplementation().setResource(resourceKey, resource);
setTileImplementation(tile, newImpl);
}
private final class Tile
{
private TileImpl implementation;
public Tile(TileImpl impl)
{
this.implementation = impl;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tile tile = (Tile) o;
return implementation.equals(tile.implementation);
}
@Override
public int hashCode() {
return implementation.hashCode();
}
public void setTileImplementation(TileImpl impl)
{
this.implementation = impl;
}
public TileImpl getTileImplementation()
{
return this.implementation;
}
}
}

View File

@@ -0,0 +1,37 @@
package com.aargonian.editor;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
/**
* Created by aargonian on 7/13/17.
*
* The EditableTileSheet Class is an implementation for a collection of Tiles arranged within a Sheet, as used by the editing
* program. Although this can be thought of in the traditional TileSheet sense (and in fact, a TileSheet can be
* constructed from a TileSheetReader from an actual TileSheet image), the EditableTileSheet is mutable, and doesn't
* keep each tile instance as a subimage within a larger sheet, unless written out by a TileSheetWriter. Instead,
* individual tile images can be added or removed from the set at will.
*
*/
public class EditableTileSheet
{
/*
* An EditableTileSheet is the backing model for the TileSheets dock of the editor, and as such implements the Observer
* pattern for the sake of a cleaner MVC implementation.
*/
private final List<Image> tileImages;
/**
* Creates an empty EditableTileSheet.
*/
public EditableTileSheet()
{
tileImages = new ArrayList<Image>();
}
public EditableTileSheet(List<Image> tileImages)
{
this.tileImages = tileImages;
}
}

View File

@@ -0,0 +1,83 @@
package com.aargonian.editor;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
/**
* Created by aargonian on 7/8/17.
*/
public class GameFrame {
public static final String PROG_TITLE = "TileEditor 0.1.0";
public static void main(String[] args) {
EventQueue.invokeLater(() ->
{
GameFrame.setupUI();
}
);
}
//TODO: Add an Icon Image
private static final JFrame setupFrame()
{
JFrame frame = new JFrame(PROG_TITLE);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
if(screenSize.width <= 1024 || screenSize.height <= 800) {
frame.setExtendedState(JFrame.MAXIMIZED_BOTH); //maximize by default if the screen is really small
} else {
frame.setSize((int)Math.round(screenSize.width * 0.8), (int)Math.round(screenSize.height * 0.8));
frame.setLocationRelativeTo(null); //Centers the frame
}
return frame;
}
/**
* Creates the pane that holds the different tile implementations the user may choose to put into the editor.
* @return The Tile Pane Component, Fully Constructed
*/
private static final JComponent setupTilesPanel()
{
throw new UnsupportedOperationException("Not Supported Yet.");
}
public static final void setupUI()
{
JFrame frame = setupFrame();
/*
TileMap map = new TileMap(10, 10);
map.setTileResourceAt(TileImpl.PROPERTY_IMG, ImageReader.readImage("res/Water.png"), 2, 2);
TileMapDisplay.OptionsBuilder displayOptions = new TileMapDisplay.OptionsBuilder();
displayOptions = displayOptions
.tileSize(32, 32)
.displaySize(640, 480)
.borderColor(Color.black);
TileMapDisplay currentTileMapDisplay = new TileMapDisplay(displayOptions);
currentTileMapDisplay.setCurrentTileMap(map);
System.out.println("TILE MAP DISPLAY WIDTH: " + currentTileMapDisplay.getPreferredSize().getWidth());
System.out.println("TILE MAP DISPLAY HEIGHT: " + currentTileMapDisplay.getPreferredSize().getHeight());
frame.add(currentTileMapDisplay);
*/
ArrayList<Image> images = new ArrayList<>(6);
for(int i = 0; i < 20; i++)
{
images.add(ImageReader.readImage("res/Water.png"));
}
TilesetDisplay display = new TilesetDisplay(32, images);
frame.add(display);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
/*
currentTileMapDisplay.repaint();
*/
}
}

View File

@@ -0,0 +1,23 @@
package com.aargonian.editor;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
/**
* Created by aargonian on 7/8/17.
*/
public class ImageReader
{
public static final BufferedImage readImage(String imagePath)
{
try {
BufferedImage img = ImageIO.read(new File(imagePath));
return img;
} catch(IOException ex) {
System.err.println("Exception Occurred: " + ex);
}
return null;
}
}

View File

@@ -0,0 +1,132 @@
package com.aargonian.editor;
import com.aargonian.com.aargonian.tile.TileImpl;
import com.aargonian.com.aargonian.tile.TileMap;
import javax.swing.*;
import java.awt.*;
/**
* Created by aargonian on 7/8/17.
*/
public class TileMapDisplay extends JComponent
{
private TileMap tileMap;
private final OptionsBuilder options;
private static final OptionsBuilder DEFAULT_OPTIONS =
new OptionsBuilder().borderColor(Color.black).displaySize(640, 480).tileSize(32, 32);
public TileMapDisplay()
{
this.options = DEFAULT_OPTIONS;
}
public TileMapDisplay(OptionsBuilder options)
{
this.options = options;
this.setPreferredSize(new Dimension(options.displayWidth(), options.displayHeight()));
}
public OptionsBuilder getOptions()
{
return options;
}
public void setCurrentTileMap(TileMap map)
{
this.tileMap = map;
}
public TileMap getCurrentTileMap()
{
return this.tileMap;
}
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.setColor(options.borderColor());
g2.fillRect(0, 0, this.getWidth(), this.getHeight());
for(int x = 0; x < tileMap.getColumns(); x++)
{
for(int y = 0; y < tileMap.getRows(); y++)
{
Image img = (Image)tileMap.getTileResourceAt(TileImpl.PROPERTY_IMG, x, y);
if(img != null)
g2.drawImage(img,
x*options.tileWidth(), y*options.tileHeight(), options.tileWidth(), options.tileHeight(),
null);
else
{
GradientPaint defPaint = new GradientPaint(x*options.tileWidth(), y*options.tileHeight(),
Color.white, (x+1)*options.tileWidth(), (y+1)*options.tileHeight, Color.black);
g2.setPaint(defPaint);
g2.fillRect(x*options.tileWidth(), y*options.tileHeight(), options.tileWidth(), options.tileHeight());
}
}
}
}
public static final class OptionsBuilder
{
private int tileWidth = 32;
private int tileHeight = 32;
private int displayWidth = 640;
private int displayHeight = 480;
private Color borderColor = Color.black;
private boolean fullscreen = false;
public OptionsBuilder tileWidth(int width)
{
this.tileWidth = width <= 0 ? 0 : width;
return this;
}
public OptionsBuilder tileHeight(int height)
{
this.tileHeight = height <= 0 ? 0 : height;
return this;
}
public OptionsBuilder tileSize(int width, int height)
{
return this.tileWidth(width).tileHeight(height);
}
public OptionsBuilder borderColor(Color c)
{
this.borderColor = c;
return this;
}
public OptionsBuilder displayWidth(int width)
{
this.displayWidth = width <= 0 ? 0 : width;
return this;
}
public OptionsBuilder displayHeight(int height)
{
this.displayHeight = height <= 0 ? 0 : height;
return this;
}
public OptionsBuilder displaySize(int width, int height)
{
return this.displayWidth(width).displayHeight(height);
}
public OptionsBuilder fullscreen(boolean value)
{
this.fullscreen = value;
return this;
}
public int tileWidth() { return tileWidth; }
public int tileHeight() { return tileHeight; }
public int displayWidth() { return displayWidth; }
public int displayHeight() { return displayHeight; }
public Color borderColor() { return borderColor; }
public boolean fulllscreen() { return fullscreen; }
}
}

View File

@@ -0,0 +1,49 @@
package com.aargonian.editor;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
/**
* Created by aargonian on 7/9/17.
*
* This class implements a display for a set of tiles.
*/
public final class TilesetDisplay extends JComponent
{
private final ArrayList<Image> images;
private int tileSize;
public TilesetDisplay(int tileSize, ArrayList<Image> tileImages)
{
if(tileImages == null)
throw new NullPointerException("Passed Image Set is Null.");
this.images = tileImages;
this.tileSize = tileSize;
}
public void addImageToDisplay(Image img)
{
images.add(img);
}
public void removeImageFromDisplay(Image img)
{
images.remove(img);
}
//Todo: Update this to use a scrollpane and avoid divide by zero.
@Override
public void paintComponent(Graphics g)
{
g.setColor(Color.gray);
g.fillRect(0,0, getWidth(), getHeight());
int columns = (int)(getWidth()/tileSize);
if(columns != 0) { // Avoid divide by zero and simply display nothing.
for (int i = 0; i < images.size(); i++) {
g.drawImage(images.get(i),
(i % columns) * tileSize, ((int) (i / columns)) * tileSize, tileSize, tileSize, null);
}
}
}
}