Base alpha version with Apple HiRes and Apple Double HiRes support working.
5
OutlawEditor/catalog.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system">
|
||||
<system systemId="file:/home/brobert/Documents/Personal/LawlessLegends/github/lawless-legends/OutlawEditor/src/main/java/org/badvision/outlaweditor/resources/OutlawSchema.xsd" uri="src/main/resources/jaxb/OutlawSchema/OutlawSchema.xsd"/>
|
||||
<system systemId="file:/home/brobert/Documents/Personal/LawlessLegends/github/lawless-legends/OutlawEditor/src/main/resources/OutlawSchema.xsd" uri="src/main/resources/jaxb/OutlawSchema/OutlawSchema.xsd"/>
|
||||
</catalog>
|
18
OutlawEditor/nb-configuration.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project-shared-configuration>
|
||||
<!--
|
||||
This file contains additional configuration written by modules in the NetBeans IDE.
|
||||
The configuration is intended to be shared among all the users of project and
|
||||
therefore it is assumed to be part of version control checkout.
|
||||
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
|
||||
-->
|
||||
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
|
||||
<!--
|
||||
Properties that influence various parts of the IDE, especially code formatting and the like.
|
||||
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
|
||||
That way multiple projects can share the same settings (useful for formatting rules for example).
|
||||
Any value defined here will override the pom.xml file value but is only applicable to the current project.
|
||||
-->
|
||||
<netbeans.hint.jdkPlatform>JDK_1.7</netbeans.hint.jdkPlatform>
|
||||
</properties>
|
||||
</project-shared-configuration>
|
77
OutlawEditor/nbactions.xml
Normal file
@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<actions>
|
||||
<action>
|
||||
<actionName>run</actionName>
|
||||
<goals>
|
||||
<goal>process-classes</goal>
|
||||
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
|
||||
</goals>
|
||||
<properties>
|
||||
<exec.args>-classpath %classpath org.badvision.outlaweditor.Application</exec.args>
|
||||
<exec.executable>java</exec.executable>
|
||||
<exec.classpathScope>compile</exec.classpathScope>
|
||||
</properties>
|
||||
</action>
|
||||
<action>
|
||||
<actionName>run.single.main</actionName>
|
||||
<goals>
|
||||
<goal>process-classes</goal>
|
||||
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
|
||||
</goals>
|
||||
<properties>
|
||||
<exec.args>-classpath %classpath ${packageClassName}</exec.args>
|
||||
<exec.executable>java</exec.executable>
|
||||
<exec.classpathScope>compile</exec.classpathScope>
|
||||
</properties>
|
||||
</action>
|
||||
<action>
|
||||
<actionName>debug</actionName>
|
||||
<goals>
|
||||
<goal>process-classes</goal>
|
||||
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
|
||||
</goals>
|
||||
<properties>
|
||||
<exec.args>-Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath ${packageClassName}</exec.args>
|
||||
<exec.executable>java</exec.executable>
|
||||
<jpda.listen>true</jpda.listen>
|
||||
<exec.classpathScope>compile</exec.classpathScope>
|
||||
</properties>
|
||||
</action>
|
||||
<action>
|
||||
<actionName>debug.single.main</actionName>
|
||||
<goals>
|
||||
<goal>process-classes</goal>
|
||||
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
|
||||
</goals>
|
||||
<properties>
|
||||
<exec.args>-Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath ${packageClassName}</exec.args>
|
||||
<exec.executable>java</exec.executable>
|
||||
<exec.classpathScope>compile</exec.classpathScope>
|
||||
<jpda.listen>true</jpda.listen>
|
||||
</properties>
|
||||
</action>
|
||||
<action>
|
||||
<actionName>profile</actionName>
|
||||
<goals>
|
||||
<goal>process-classes</goal>
|
||||
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
|
||||
</goals>
|
||||
<properties>
|
||||
<exec.args>-classpath %classpath ${packageClassName}</exec.args>
|
||||
<exec.executable>java</exec.executable>
|
||||
<exec.classpathScope>compile</exec.classpathScope>
|
||||
</properties>
|
||||
</action>
|
||||
<action>
|
||||
<actionName>profile.single.main</actionName>
|
||||
<goals>
|
||||
<goal>process-classes</goal>
|
||||
<goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
|
||||
</goals>
|
||||
<properties>
|
||||
<exec.args>-classpath %classpath ${packageClassName}</exec.args>
|
||||
<exec.executable>java</exec.executable>
|
||||
<exec.classpathScope>compile</exec.classpathScope>
|
||||
</properties>
|
||||
</action>
|
||||
</actions>
|
72
OutlawEditor/pom.xml
Normal file
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.badvision</groupId>
|
||||
<artifactId>OutlawEditor</artifactId>
|
||||
<name>OutlawEditor</name>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<organization>
|
||||
<!-- Used as the 'Vendor' for JNLP generation -->
|
||||
<name>org.badvision</name>
|
||||
</organization>
|
||||
<build>
|
||||
<finalName>OutlawEditor</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.zenjava</groupId>
|
||||
<artifactId>javafx-maven-plugin</artifactId>
|
||||
<version>2.0</version>
|
||||
<configuration>
|
||||
outlawEditor
|
||||
<mainClass>org.badvision.outlaweditor.Application</mainClass>
|
||||
<!-- only required if signing the jar file -->
|
||||
<keyStoreAlias>example-user</keyStoreAlias>
|
||||
<keyStorePassword>example-password</keyStorePassword>
|
||||
<allPermissions>true</allPermissions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
<version>3.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jvnet.jaxb2.maven2</groupId>
|
||||
<artifactId>maven-jaxb2-plugin</artifactId>
|
||||
<version>0.8.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>generate</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<schemaIncludes>
|
||||
<include>jaxb/OutlawSchema/*.xsd</include>
|
||||
</schemaIncludes>
|
||||
<episodeFile>${project.build.directory}/generated-sources/xjc/META-INF/jaxb-OutlawSchema.episode</episodeFile>
|
||||
<generatePackage>org.badvision.outlaweditor.data.xml</generatePackage>
|
||||
</configuration>
|
||||
<id>jaxb-generate-OutlawSchema</id>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.oracle</groupId>
|
||||
<artifactId>javafx</artifactId>
|
||||
<version>2</version>
|
||||
<systemPath>/usr/lib/jvm/jdk1.7.0_21/jre/lib/jfxrt.jar</systemPath>
|
||||
<!--<systemPath>${java.home}/lib/jfxrt.jar</systemPath>-->
|
||||
<scope>system</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
BIN
OutlawEditor/src/main/deploy/package/macosx/OutlawEditor.icns
Normal file
BIN
OutlawEditor/src/main/deploy/package/windows/OutlawEditor.ico
Normal file
After Width: | Height: | Size: 97 KiB |
@ -0,0 +1,78 @@
|
||||
package org.badvision.outlaweditor;
|
||||
|
||||
import java.io.IOException;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.canvas.Canvas;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.WindowEvent;
|
||||
import static org.badvision.outlaweditor.UIAction.*;
|
||||
import org.badvision.outlaweditor.data.xml.GameData;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class Application extends javafx.application.Application {
|
||||
|
||||
public static GameData gameData = new GameData();
|
||||
public static Platform currentPlatform = Platform.AppleII;
|
||||
static Application instance;
|
||||
public Stage primaryStage;
|
||||
ApplicationUIControllerImpl controller;
|
||||
|
||||
public static Stage getPrimaryStage() {
|
||||
return instance.primaryStage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) {
|
||||
instance = this;
|
||||
this.primaryStage = primaryStage;
|
||||
javafx.application.Platform.setImplicitExit(true);
|
||||
|
||||
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/ApplicationUI.fxml"));
|
||||
fxmlLoader.setResources(null);
|
||||
try {
|
||||
AnchorPane node = (AnchorPane) fxmlLoader.load();
|
||||
controller = fxmlLoader.getController();
|
||||
Scene s = new Scene(node);
|
||||
primaryStage.setScene(s);
|
||||
} catch (IOException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
|
||||
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
|
||||
@Override
|
||||
public void handle(final WindowEvent t) {
|
||||
t.consume();
|
||||
if (quit()) {
|
||||
javafx.application.Platform.exit();
|
||||
// System.exit(0);
|
||||
}
|
||||
javafx.application.Platform.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
getPrimaryStage().show();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
primaryStage.show();
|
||||
}
|
||||
Canvas tilePreview;
|
||||
|
||||
/**
|
||||
* The main() method is ignored in correctly deployed JavaFX application.
|
||||
* main() serves only as fallback in case the application can not be
|
||||
* launched through deployment artifacts, e.g., in IDEs with limited FX
|
||||
* support. NetBeans ignores main().
|
||||
*
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
launch(args);
|
||||
}
|
||||
}
|
@ -0,0 +1,327 @@
|
||||
package org.badvision.outlaweditor;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.Event;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import org.badvision.outlaweditor.data.xml.Image;
|
||||
import org.badvision.outlaweditor.data.xml.Map;
|
||||
import org.badvision.outlaweditor.data.xml.Script;
|
||||
import org.badvision.outlaweditor.data.xml.Tile;
|
||||
|
||||
public abstract class ApplicationUIController {
|
||||
|
||||
@FXML // ResourceBundle that was given to the FXMLLoader
|
||||
protected ResourceBundle resources;
|
||||
@FXML // URL location of the FXML file that was given to the FXMLLoader
|
||||
protected URL location;
|
||||
@FXML // fx:id="imageCategoryField"
|
||||
protected TextField imageCategoryField; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="imageEditorAnchorPane"
|
||||
protected AnchorPane imageEditorAnchorPane; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="imageHeightField"
|
||||
protected TextField imageHeightField; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="imageNameField"
|
||||
protected TextField imageNameField; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="imagePatternMenu"
|
||||
protected Menu imagePatternMenu; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="imageSelector"
|
||||
protected ComboBox<Image> imageSelector; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="imageWidthField"
|
||||
protected TextField imageWidthField; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="mapEditorAnchorPane"
|
||||
protected AnchorPane mapEditorAnchorPane; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="mapHeightField"
|
||||
protected TextField mapHeightField; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="mapNameField"
|
||||
protected TextField mapNameField; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="mapScriptsList"
|
||||
protected ListView<Script> mapScriptsList; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="mapSelect"
|
||||
protected ComboBox<Map> mapSelect; // Value injected by FXMLLoader
|
||||
@FXML
|
||||
protected Menu mapSelectTile;
|
||||
@FXML // fx:id="mapWidthField"
|
||||
protected TextField mapWidthField; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="mapWrapAround"
|
||||
protected CheckBox mapWrapAround; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="tileCategoryField"
|
||||
protected TextField tileCategoryField; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="tileEditorAnchorPane"
|
||||
protected AnchorPane tileEditorAnchorPane; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="tileIdField"
|
||||
protected TextField tileIdField; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="tileNameField"
|
||||
protected TextField tileNameField; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="tileObstructionField"
|
||||
protected CheckBox tileObstructionField; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="tilePatternMenu"
|
||||
protected Menu tilePatternMenu; // Value injected by FXMLLoader
|
||||
@FXML // fx:id="tileSelector"
|
||||
protected ComboBox<Tile> tileSelector; // Value injected by FXMLLoader
|
||||
|
||||
// Handler for MenuItem[javafx.scene.control.MenuItem@3a4bc91a] onAction
|
||||
@FXML
|
||||
abstract public void imageBitMode(ActionEvent event);
|
||||
|
||||
// Handler for MenuItem[javafx.scene.control.MenuItem@2fd6cf0] onAction
|
||||
@FXML
|
||||
abstract public void imageDraw1BitMode(ActionEvent event);
|
||||
|
||||
// Handler for MenuItem[javafx.scene.control.MenuItem@2fd4f37f] onAction
|
||||
@FXML
|
||||
abstract public void imageDraw3BitMode(ActionEvent event);
|
||||
|
||||
// Handler for MenuItem[javafx.scene.control.MenuItem@73091451] onAction
|
||||
@FXML
|
||||
abstract public void imageDrawFilledRectMode(ActionEvent event);
|
||||
|
||||
// Handler for MenuItem[javafx.scene.control.MenuItem@5768f863] onAction
|
||||
@FXML
|
||||
abstract public void imageShift(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void imageTabActivated(Event event);
|
||||
|
||||
// Handler for MenuItem[javafx.scene.control.MenuItem@547638c0] onAction
|
||||
@FXML
|
||||
abstract public void imageTogglePanZoom(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button zoomInButton]] onAction
|
||||
@FXML
|
||||
abstract public void imageZoomIn(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button zoomOutButton]] onAction
|
||||
@FXML
|
||||
abstract public void imageZoomOut(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void mapDraw1(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void mapDraw3(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void mapDraw5(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void mapDrawFilledRectMode(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void mapTabActivated(Event event);
|
||||
|
||||
@FXML
|
||||
abstract public void mapTogglePanZoom(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button zoomInButton]] onAction
|
||||
@FXML
|
||||
abstract public void mapZoomIn(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button zoomOutButton]] onAction
|
||||
@FXML
|
||||
abstract public void mapZoomOut(ActionEvent event);
|
||||
|
||||
// Handler for ComboBox[fx:id="tileSelector"] onAction
|
||||
@FXML
|
||||
abstract public void onCurrentTileSelected(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void onChangePlatformAppleSolid(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void onChangePlatformAppleText(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void onChangePlatformAppleDHGRSolid(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void onChangePlatformAppleDHGRText(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void onChangePlatformC64(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void onEditCopy(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void onEditPaste(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void onEditSelect(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void onFileOpen(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void onFileQuit(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void onFileSave(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void onFileSaveAs(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void onHelpAbout(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button]] onAction
|
||||
@FXML
|
||||
abstract public void onImageClonePressed(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button]] onAction
|
||||
@FXML
|
||||
abstract public void onImageCreatePressed(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button]] onAction
|
||||
@FXML
|
||||
abstract public void onImageDeletePressed(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button]] onAction
|
||||
@FXML
|
||||
abstract public void onImageExportPressed(ActionEvent event);
|
||||
|
||||
// Handler for ComboBox[fx:id="imageSelector"] onAction
|
||||
@FXML
|
||||
abstract public void onImageSelected(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button]] onAction
|
||||
@FXML
|
||||
abstract public void onMapClonePressed(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button]] onAction
|
||||
@FXML
|
||||
abstract public void onMapCreatePressed(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button]] onAction
|
||||
@FXML
|
||||
abstract public void onMapDeletePressed(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button]] onAction
|
||||
@FXML
|
||||
abstract public void onMapExportPressed(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void onMapPreviewPressed(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button]] onAction
|
||||
@FXML
|
||||
abstract public void onMapScriptAddPressed(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button]] onAction
|
||||
@FXML
|
||||
abstract public void onMapScriptClonePressed(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button]] onAction
|
||||
@FXML
|
||||
abstract public void onMapScriptDeletePressed(ActionEvent event);
|
||||
|
||||
// Handler for ComboBox[id="tileSelect"] onAction
|
||||
@FXML
|
||||
abstract public void onMapSelected(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button]] onAction
|
||||
@FXML
|
||||
abstract public void onTileClonePressed(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button]] onAction
|
||||
@FXML
|
||||
abstract public void onTileCreatePressed(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button]] onAction
|
||||
@FXML
|
||||
abstract public void onTileDeletePressed(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button]] onAction
|
||||
@FXML
|
||||
abstract public void onTileExportPressed(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
|
||||
@FXML
|
||||
abstract public void scrollImageDown(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
|
||||
@FXML
|
||||
abstract public void scrollImageLeft(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
|
||||
@FXML
|
||||
abstract public void scrollImageRight(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
|
||||
@FXML
|
||||
abstract public void scrollImageUp(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
|
||||
@FXML
|
||||
abstract public void scrollMapDown(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
|
||||
@FXML
|
||||
abstract public void scrollMapLeft(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
|
||||
@FXML
|
||||
abstract public void scrollMapRight(ActionEvent event);
|
||||
|
||||
// Handler for Button[Button[id=null, styleClass=button moveButton]] onAction
|
||||
@FXML
|
||||
abstract public void scrollMapUp(ActionEvent event);
|
||||
|
||||
// Handler for MenuItem[javafx.scene.control.MenuItem@3b007e44] onAction
|
||||
@FXML
|
||||
abstract public void tileBitMode(ActionEvent event);
|
||||
|
||||
// Handler for MenuItem[javafx.scene.control.MenuItem@4771c0b8] onAction
|
||||
@FXML
|
||||
abstract public void tileDraw1BitMode(ActionEvent event);
|
||||
|
||||
// Handler for MenuItem[javafx.scene.control.MenuItem@766bd19d] onAction
|
||||
@FXML
|
||||
abstract public void tileDraw3BitMode(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void imageDraw5BitMode(ActionEvent event);
|
||||
|
||||
@FXML
|
||||
abstract public void tileTabActivated(Event event);
|
||||
|
||||
// Handler for MenuItem[javafx.scene.control.MenuItem@622410f1] onAction
|
||||
@FXML
|
||||
abstract public void tileShift(ActionEvent event);
|
||||
|
||||
@FXML // This method is called by the FXMLLoader when initialization is complete
|
||||
public void initialize() {
|
||||
assert imageCategoryField != null : "fx:id=\"imageCategoryField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert imageEditorAnchorPane != null : "fx:id=\"imageEditorAnchorPane\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert imageHeightField != null : "fx:id=\"imageHeightField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert imageNameField != null : "fx:id=\"imageNameField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert imagePatternMenu != null : "fx:id=\"imagePatternMenu\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert imageSelector != null : "fx:id=\"imageSelector\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert imageWidthField != null : "fx:id=\"imageWidthField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert mapEditorAnchorPane != null : "fx:id=\"mapEditorAnchorPane\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert mapHeightField != null : "fx:id=\"mapHeightField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert mapNameField != null : "fx:id=\"mapNameField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert mapScriptsList != null : "fx:id=\"mapScriptsList\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert mapSelect != null : "fx:id=\"mapSelect\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert mapSelectTile != null : "fx:id=\"mapSelectTile\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert mapWidthField != null : "fx:id=\"mapWidthField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert mapWrapAround != null : "fx:id=\"mapWrapAround\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert tileCategoryField != null : "fx:id=\"tileCategoryField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert tileEditorAnchorPane != null : "fx:id=\"tileEditorAnchorPane\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert tileIdField != null : "fx:id=\"tileIdField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert tileNameField != null : "fx:id=\"tileNameField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert tileObstructionField != null : "fx:id=\"tileObstructionField\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert tilePatternMenu != null : "fx:id=\"tilePatternMenu\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
assert tileSelector != null : "fx:id=\"tileSelector\" was not injected: check your FXML file 'ApplicationUI.fxml'.";
|
||||
|
||||
// Initialize your logic here: all @FXML variables will have been injected
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,835 @@
|
||||
package org.badvision.outlaweditor;
|
||||
|
||||
import org.badvision.outlaweditor.apple.AppleTileRenderer;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.Event;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.cell.ComboBoxListCell;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.util.Callback;
|
||||
import static org.badvision.outlaweditor.Application.currentPlatform;
|
||||
import static org.badvision.outlaweditor.Application.gameData;
|
||||
import static org.badvision.outlaweditor.data.PropertyHelper.*;
|
||||
import org.badvision.outlaweditor.data.TileUtils;
|
||||
import org.badvision.outlaweditor.data.TilesetUtils;
|
||||
import org.badvision.outlaweditor.data.xml.Image;
|
||||
import org.badvision.outlaweditor.data.xml.PlatformData;
|
||||
import org.badvision.outlaweditor.data.xml.Tile;
|
||||
|
||||
/**
|
||||
* Actual implementation of Application UI, isolated from auto-generated code
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class ApplicationUIControllerImpl extends ApplicationUIController {
|
||||
|
||||
public Tile currentTile = null;
|
||||
public TileEditor currentTileEditor = null;
|
||||
public org.badvision.outlaweditor.data.xml.Map currentMap = null;
|
||||
public MapEditor currentMapEditor = null;
|
||||
public Image currentImage = null;
|
||||
public ImageEditor currentImageEditor = null;
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
|
||||
TilesetUtils.addObserver(new org.badvision.outlaweditor.data.DataObserver() {
|
||||
@Override
|
||||
public void observedObjectChanged(Object object) {
|
||||
rebuildTileSelectors();
|
||||
}
|
||||
});
|
||||
tileSelector.setButtonCell(new ComboBoxListCell<Tile>() {
|
||||
{
|
||||
super.setPrefWidth(125);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(Tile item, boolean empty) {
|
||||
textProperty().unbind();
|
||||
super.updateItem(item, empty);
|
||||
if (item != null) {
|
||||
textProperty().bind(tileNameField.textProperty());
|
||||
} else {
|
||||
setText(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
tileSelector.setCellFactory(new Callback<ListView<Tile>, ListCell<Tile>>() {
|
||||
@Override
|
||||
public ListCell<Tile> call(ListView<Tile> param) {
|
||||
return new EntitySelectorCell<Tile>(tileNameField) {
|
||||
@Override
|
||||
public void finishUpdate(Tile item) {
|
||||
setGraphic(new ImageView(TileUtils.getImage(item, Application.currentPlatform)));
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
mapSelect.setButtonCell(new ComboBoxListCell<org.badvision.outlaweditor.data.xml.Map>() {
|
||||
{
|
||||
super.setPrefWidth(125);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(org.badvision.outlaweditor.data.xml.Map item, boolean empty) {
|
||||
textProperty().unbind();
|
||||
super.updateItem(item, empty);
|
||||
if (item != null) {
|
||||
textProperty().bind(mapNameField.textProperty());
|
||||
} else {
|
||||
setText(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
mapSelect.setCellFactory(new Callback<ListView<org.badvision.outlaweditor.data.xml.Map>, ListCell<org.badvision.outlaweditor.data.xml.Map>>() {
|
||||
@Override
|
||||
public ListCell<org.badvision.outlaweditor.data.xml.Map> call(ListView<org.badvision.outlaweditor.data.xml.Map> param) {
|
||||
return new EntitySelectorCell<org.badvision.outlaweditor.data.xml.Map>(mapNameField) {
|
||||
@Override
|
||||
public void finishUpdate(org.badvision.outlaweditor.data.xml.Map item) {
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
imageSelector.setButtonCell(new ComboBoxListCell<Image>() {
|
||||
{
|
||||
super.setPrefWidth(125);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(Image item, boolean empty) {
|
||||
textProperty().unbind();
|
||||
super.updateItem(item, empty);
|
||||
if (item != null) {
|
||||
textProperty().bind(imageNameField.textProperty());
|
||||
} else {
|
||||
setText(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
imageSelector.setCellFactory(new Callback<ListView<Image>, ListCell<Image>>() {
|
||||
@Override
|
||||
public ListCell<Image> call(ListView<Image> param) {
|
||||
return new EntitySelectorCell<Image>(imageNameField) {
|
||||
@Override
|
||||
public void finishUpdate(Image item) {
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imageBitMode(ActionEvent event) {
|
||||
if (currentImageEditor != null) {
|
||||
currentImageEditor.setDrawMode(ImageEditor.DrawMode.Toggle);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imageDraw1BitMode(ActionEvent event) {
|
||||
if (currentImageEditor != null) {
|
||||
currentImageEditor.setDrawMode(ImageEditor.DrawMode.Pencil1px);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imageDraw3BitMode(ActionEvent event) {
|
||||
if (currentImageEditor != null) {
|
||||
currentImageEditor.setDrawMode(ImageEditor.DrawMode.Pencil3px);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imageDraw5BitMode(ActionEvent event) {
|
||||
if (currentImageEditor != null) {
|
||||
currentImageEditor.setDrawMode(ImageEditor.DrawMode.Pencil5px);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imageDrawFilledRectMode(ActionEvent event) {
|
||||
if (currentImageEditor != null) {
|
||||
currentImageEditor.setDrawMode(ImageEditor.DrawMode.Rectangle);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imageShift(ActionEvent event) {
|
||||
if (currentImageEditor != null) {
|
||||
currentImageEditor.showShiftUI();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imageTogglePanZoom(ActionEvent event) {
|
||||
if (currentImageEditor != null) {
|
||||
currentImageEditor.togglePanZoom();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imageZoomIn(ActionEvent event) {
|
||||
if (currentImageEditor != null) {
|
||||
currentImageEditor.zoomIn();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imageZoomOut(ActionEvent event) {
|
||||
if (currentImageEditor != null) {
|
||||
currentImageEditor.zoomOut();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mapDraw1(ActionEvent event) {
|
||||
if (currentMapEditor != null) {
|
||||
currentMapEditor.setDrawMode(MapEditor.DrawMode.Pencil1px);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mapDraw3(ActionEvent event) {
|
||||
if (currentMapEditor != null) {
|
||||
currentMapEditor.setDrawMode(MapEditor.DrawMode.Pencil3px);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mapDraw5(ActionEvent event) {
|
||||
if (currentMapEditor != null) {
|
||||
currentMapEditor.setDrawMode(MapEditor.DrawMode.Pencil5px);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mapDrawFilledRectMode(ActionEvent event) {
|
||||
if (currentMapEditor != null) {
|
||||
currentMapEditor.setDrawMode(MapEditor.DrawMode.FilledRect);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mapTogglePanZoom(ActionEvent event) {
|
||||
if (currentMapEditor != null) {
|
||||
currentMapEditor.togglePanZoom();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mapZoomIn(ActionEvent event) {
|
||||
if (currentMapEditor != null) {
|
||||
currentMapEditor.zoomIn();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mapZoomOut(ActionEvent event) {
|
||||
if (currentMapEditor != null) {
|
||||
currentMapEditor.zoomOut();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCurrentTileSelected(ActionEvent event) {
|
||||
setCurrentTile(tileSelector.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChangePlatformAppleSolid(ActionEvent event) {
|
||||
AppleTileRenderer.useSolidPalette = true;
|
||||
Application.currentPlatform = Platform.AppleII;
|
||||
platformChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChangePlatformAppleText(ActionEvent event) {
|
||||
AppleTileRenderer.useSolidPalette = false;
|
||||
Application.currentPlatform = Platform.AppleII;
|
||||
platformChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChangePlatformAppleDHGRSolid(ActionEvent event) {
|
||||
AppleTileRenderer.useSolidPalette = true;
|
||||
Application.currentPlatform = Platform.AppleII_DHGR;
|
||||
platformChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChangePlatformAppleDHGRText(ActionEvent event) {
|
||||
AppleTileRenderer.useSolidPalette = false;
|
||||
Application.currentPlatform = Platform.AppleII_DHGR;
|
||||
platformChange();
|
||||
}
|
||||
|
||||
private void platformChange() {
|
||||
for (Tile t : Application.gameData.getTile()) {
|
||||
TileUtils.redrawTile(t);
|
||||
}
|
||||
Tile tile = currentTile;
|
||||
rebuildTileSelectors();
|
||||
setCurrentTile(tile);
|
||||
if (currentMapEditor != null) {
|
||||
currentMapEditor.redraw();
|
||||
}
|
||||
if (currentImageEditor != null) {
|
||||
currentImageEditor.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChangePlatformC64(ActionEvent event) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEditCopy(ActionEvent event) {
|
||||
if (getVisibleEditor() != null) {
|
||||
getVisibleEditor().copy();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEditPaste(ActionEvent event) {
|
||||
if (getVisibleEditor() != null) {
|
||||
getVisibleEditor().paste();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEditSelect(ActionEvent event) {
|
||||
if (getVisibleEditor() != null) {
|
||||
getVisibleEditor().select();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileOpen(ActionEvent event) {
|
||||
try {
|
||||
UIAction.actionPerformed(UIAction.MAIN_ACTIONS.Load);
|
||||
rebuildImageSelector();
|
||||
rebuildMapSelectors();
|
||||
rebuildTileSelectors();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileQuit(ActionEvent event) {
|
||||
UIAction.quit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileSave(ActionEvent event) {
|
||||
if (currentMapEditor != null) {
|
||||
currentMapEditor.currentMap.updateBackingMap();
|
||||
}
|
||||
try {
|
||||
UIAction.actionPerformed(UIAction.MAIN_ACTIONS.Save);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileSaveAs(ActionEvent event) {
|
||||
try {
|
||||
UIAction.actionPerformed(UIAction.MAIN_ACTIONS.Save_as);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHelpAbout(ActionEvent event) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImageClonePressed(ActionEvent event) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImageCreatePressed(ActionEvent event) {
|
||||
Image i = new Image();
|
||||
i.setName("Untitled");
|
||||
Application.gameData.getImage().add(i);
|
||||
setCurrentImage(i);
|
||||
rebuildImageSelector();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImageDeletePressed(ActionEvent event) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImageExportPressed(ActionEvent event) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImageSelected(ActionEvent event) {
|
||||
setCurrentImage(imageSelector.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapClonePressed(ActionEvent event) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapCreatePressed(ActionEvent event) {
|
||||
org.badvision.outlaweditor.data.xml.Map m = new org.badvision.outlaweditor.data.xml.Map();
|
||||
m.setName("Untitled");
|
||||
gameData.getMap().add(m);
|
||||
m.setWidth(512);
|
||||
m.setHeight(512);
|
||||
setCurrentMap(m);
|
||||
rebuildMapSelectors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapDeletePressed(ActionEvent event) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapExportPressed(ActionEvent event) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapScriptAddPressed(ActionEvent event) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapPreviewPressed(ActionEvent event) {
|
||||
if (currentMapEditor == null) {
|
||||
return;
|
||||
}
|
||||
currentMapEditor.showPreview();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapScriptClonePressed(ActionEvent event) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapScriptDeletePressed(ActionEvent event) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMapSelected(ActionEvent event) {
|
||||
setCurrentMap(mapSelect.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTileClonePressed(ActionEvent event) {
|
||||
if (currentTile == null) {
|
||||
return;
|
||||
}
|
||||
Tile t = new Tile();
|
||||
TileUtils.getId(t);
|
||||
t.setName(currentTile.getName() + " (clone)");
|
||||
t.setObstruction(currentTile.isObstruction());
|
||||
t.getCategory().addAll(currentTile.getCategory());
|
||||
for (PlatformData d : currentTile.getDisplayData()) {
|
||||
PlatformData p = new PlatformData();
|
||||
p.setHeight(d.getHeight());
|
||||
p.setWidth(d.getWidth());
|
||||
p.setPlatform(d.getPlatform());
|
||||
p.setValue(Arrays.copyOf(d.getValue(), d.getValue().length));
|
||||
t.getDisplayData().add(p);
|
||||
}
|
||||
TilesetUtils.add(t);
|
||||
rebuildTileSelectors();
|
||||
setCurrentTile(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTileCreatePressed(ActionEvent event) {
|
||||
Tile t = TileUtils.newTile();
|
||||
t.setName("Untitled");
|
||||
TilesetUtils.add(t);
|
||||
rebuildTileSelectors();
|
||||
setCurrentTile(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTileDeletePressed(ActionEvent event) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTileExportPressed(ActionEvent event) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollImageDown(ActionEvent event) {
|
||||
if (currentImageEditor != null) {
|
||||
currentImageEditor.scrollBy(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollImageLeft(ActionEvent event) {
|
||||
if (currentImageEditor != null) {
|
||||
currentImageEditor.scrollBy(-1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollImageRight(ActionEvent event) {
|
||||
if (currentImageEditor != null) {
|
||||
currentImageEditor.scrollBy(1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollImageUp(ActionEvent event) {
|
||||
if (currentImageEditor != null) {
|
||||
currentImageEditor.scrollBy(0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollMapDown(ActionEvent event) {
|
||||
if (currentMapEditor != null) {
|
||||
currentMapEditor.scrollBy(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollMapLeft(ActionEvent event) {
|
||||
if (currentMapEditor != null) {
|
||||
currentMapEditor.scrollBy(-1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollMapRight(ActionEvent event) {
|
||||
if (currentMapEditor != null) {
|
||||
currentMapEditor.scrollBy(1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollMapUp(ActionEvent event) {
|
||||
if (currentMapEditor != null) {
|
||||
currentMapEditor.scrollBy(0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tileBitMode(ActionEvent event) {
|
||||
if (currentTileEditor != null) {
|
||||
currentTileEditor.setDrawMode(TileEditor.DrawMode.Toggle);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tileDraw1BitMode(ActionEvent event) {
|
||||
if (currentTileEditor != null) {
|
||||
currentTileEditor.setDrawMode(TileEditor.DrawMode.Pencil1px);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tileDraw3BitMode(ActionEvent event) {
|
||||
if (currentTileEditor != null) {
|
||||
currentTileEditor.setDrawMode(TileEditor.DrawMode.Pencil3px);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tileShift(ActionEvent event) {
|
||||
if (currentTileEditor != null) {
|
||||
currentTileEditor.showShiftUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void setCurrentTileEditor(TileEditor editor) {
|
||||
if (editor != null) {
|
||||
editor.buildEditorUI(tileEditorAnchorPane);
|
||||
editor.buildPatternSelector(tilePatternMenu);
|
||||
}
|
||||
currentTileEditor = editor;
|
||||
}
|
||||
|
||||
public Tile getCurrentTile() {
|
||||
return currentTile;
|
||||
}
|
||||
|
||||
public void setCurrentTile(Tile t) {
|
||||
tileSelector.getSelectionModel().select(t);
|
||||
if (t != null && t.equals(currentTile)) {
|
||||
return;
|
||||
}
|
||||
tileEditorAnchorPane.getChildren().clear();
|
||||
if (t == null) {
|
||||
bind(tileIdField.textProperty(), null);
|
||||
bind(tileCategoryField.textProperty(), null);
|
||||
bind(tileObstructionField.selectedProperty(), null);
|
||||
bind(tileNameField.textProperty(), null);
|
||||
tileIdField.setDisable(true);
|
||||
tileCategoryField.setDisable(true);
|
||||
tileObstructionField.setDisable(true);
|
||||
tileNameField.setDisable(true);
|
||||
setCurrentTileEditor(null);
|
||||
} else {
|
||||
if (t.isObstruction() == null) {
|
||||
t.setObstruction(false);
|
||||
}
|
||||
try {
|
||||
tileIdField.setDisable(false);
|
||||
tileCategoryField.setDisable(false);
|
||||
tileObstructionField.setDisable(false);
|
||||
tileNameField.setDisable(false);
|
||||
bind(tileIdField.textProperty(), stringProp(t, "id"));
|
||||
bind(tileCategoryField.textProperty(), categoryProp(t, "category"));
|
||||
bind(tileObstructionField.selectedProperty(), boolProp(t, "obstruction"));
|
||||
bind(tileNameField.textProperty(), stringProp(t, "name"));
|
||||
TileEditor editor = Application.currentPlatform.tileEditor.newInstance();
|
||||
editor.setEntity(t);
|
||||
setCurrentTileEditor(editor);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (InstantiationException ex) {
|
||||
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (IllegalAccessException ex) {
|
||||
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
currentTile = t;
|
||||
}
|
||||
|
||||
public void rebuildTileSelectors() {
|
||||
tileSelector.getItems().clear();
|
||||
tileSelector.getItems().addAll(Application.gameData.getTile());
|
||||
tileSelector.getSelectionModel().select(getCurrentTile());
|
||||
mapSelectTile.getItems().clear();
|
||||
for (final Tile t : Application.gameData.getTile()) {
|
||||
WritableImage img = TileUtils.getImage(t, currentPlatform);
|
||||
ImageView iv = new ImageView(img);
|
||||
MenuItem mapSelectItem = new MenuItem(t.getName(), iv);
|
||||
mapSelectItem.setGraphic(new ImageView(TileUtils.getImage(t, currentPlatform)));
|
||||
mapSelectItem.setOnAction(new EventHandler<ActionEvent>() {
|
||||
@Override
|
||||
public void handle(ActionEvent event) {
|
||||
if (currentMapEditor != null) {
|
||||
currentMapEditor.setCurrentTile(t);
|
||||
}
|
||||
}
|
||||
});
|
||||
mapSelectTile.getItems().add(mapSelectItem);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCurrentMap(org.badvision.outlaweditor.data.xml.Map m) {
|
||||
if (currentMap != null && currentMap.equals(m)) {
|
||||
return;
|
||||
}
|
||||
// mapEditorAnchorPane.getChildren().clear();
|
||||
currentMap = m;
|
||||
if (currentMapEditor != null) {
|
||||
currentMapEditor.unregister();
|
||||
}
|
||||
if (m == null) {
|
||||
bind(mapHeightField.textProperty(), null);
|
||||
bind(mapNameField.textProperty(), null);
|
||||
bind(mapWidthField.textProperty(), null);
|
||||
bind(mapWrapAround.selectedProperty(), null);
|
||||
mapHeightField.setDisable(true);
|
||||
mapNameField.setDisable(true);
|
||||
mapWidthField.setDisable(true);
|
||||
mapWrapAround.setDisable(true);
|
||||
currentMapEditor = null;
|
||||
} else {
|
||||
if (m.getHeight() == null) {
|
||||
m.setHeight(512);
|
||||
}
|
||||
if (m.getWidth() == null) {
|
||||
m.setWidth(512);
|
||||
}
|
||||
if (m.getName() == null) {
|
||||
m.setName("Untitled");
|
||||
}
|
||||
try {
|
||||
mapHeightField.setDisable(false);
|
||||
mapNameField.setDisable(false);
|
||||
mapWidthField.setDisable(false);
|
||||
mapWrapAround.setDisable(false);
|
||||
// bind(mapHeightField.textProperty(), intProp(m, "height"));
|
||||
bind(mapNameField.textProperty(), stringProp(m, "name"));
|
||||
// bind(mapWidthField.textProperty(), intProp(m, "width"));
|
||||
// bind(mapWrapAround.selectedProperty(),boolProp(m, "wrap"));
|
||||
} catch (NoSuchMethodException ex) {
|
||||
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
currentMapEditor = new MapEditor();
|
||||
currentMapEditor.setEntity(m);
|
||||
currentMapEditor.buildEditorUI(mapEditorAnchorPane);
|
||||
}
|
||||
}
|
||||
|
||||
public void rebuildMapSelectors() {
|
||||
mapSelect.getItems().clear();
|
||||
mapSelect.getItems().addAll(Application.gameData.getMap());
|
||||
mapSelect.getSelectionModel().select(getCurrentMap());
|
||||
}
|
||||
|
||||
public org.badvision.outlaweditor.data.xml.Map getCurrentMap() {
|
||||
return currentMap;
|
||||
}
|
||||
|
||||
private void setCurrentImage(Image i) {
|
||||
if (currentImage != null && currentImage.equals(i)) {
|
||||
return;
|
||||
}
|
||||
currentImage = i;
|
||||
if (currentImageEditor != null) {
|
||||
currentImageEditor.unregister();
|
||||
}
|
||||
if (i == null) {
|
||||
bind(imageCategoryField.textProperty(), null);
|
||||
// bind(imageHeightField.textProperty(), null);
|
||||
bind(imageNameField.textProperty(), null);
|
||||
// bind(imageWidthField.textProperty(),null);
|
||||
imageCategoryField.setDisable(true);
|
||||
imageHeightField.setDisable(true);
|
||||
imageNameField.setDisable(true);
|
||||
imageWidthField.setDisable(true);
|
||||
currentImageEditor = null;
|
||||
} else {
|
||||
if (i.getName() == null) {
|
||||
i.setName("Untitled");
|
||||
}
|
||||
try {
|
||||
imageCategoryField.setDisable(false);
|
||||
imageHeightField.setDisable(false);
|
||||
imageNameField.setDisable(false);
|
||||
imageWidthField.setDisable(false);
|
||||
bind(imageNameField.textProperty(), stringProp(i, "name"));
|
||||
bind(imageCategoryField.textProperty(), categoryProp(i, "category"));
|
||||
} catch (NoSuchMethodException ex) {
|
||||
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
try {
|
||||
currentImageEditor = currentPlatform.imageEditor.newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException ex) {
|
||||
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
currentImageEditor.setEntity(i);
|
||||
currentImageEditor.buildEditorUI(imageEditorAnchorPane);
|
||||
currentImageEditor.buildPatternSelector(imagePatternMenu);
|
||||
}
|
||||
}
|
||||
|
||||
private Image getCurrentImage() {
|
||||
return currentImage;
|
||||
}
|
||||
|
||||
private void rebuildImageSelector() {
|
||||
imageSelector.getItems().clear();
|
||||
imageSelector.getItems().addAll(Application.gameData.getImage());
|
||||
imageSelector.getSelectionModel().select(getCurrentImage());
|
||||
}
|
||||
|
||||
public static enum TABS{image,map,tile};
|
||||
TABS currentTab;
|
||||
@Override
|
||||
public void imageTabActivated(Event event) {
|
||||
currentTab = TABS.image;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mapTabActivated(Event event) {
|
||||
currentTab = TABS.map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tileTabActivated(Event event) {
|
||||
currentTab = TABS.tile;
|
||||
}
|
||||
|
||||
public Editor getVisibleEditor() {
|
||||
switch (currentTab) {
|
||||
case image:
|
||||
return currentImageEditor;
|
||||
case map:
|
||||
return currentMapEditor;
|
||||
case tile:
|
||||
return currentTileEditor;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
abstract public static class EntitySelectorCell<T> extends ComboBoxListCell<T> {
|
||||
|
||||
static Map<TextField, Object> lastSelected = new HashMap<>();
|
||||
TextField nameField;
|
||||
|
||||
public EntitySelectorCell(TextField tileNameField) {
|
||||
super.setPrefWidth(125);
|
||||
nameField = tileNameField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSelected(boolean sel) {
|
||||
if (sel) {
|
||||
Object o = lastSelected.get(nameField);
|
||||
if (o != null && !o.equals(getItem())) {
|
||||
((ListCell) o).updateSelected(false);
|
||||
}
|
||||
textProperty().unbind();
|
||||
textProperty().bind(nameField.textProperty());
|
||||
lastSelected.put(nameField, this);
|
||||
} else {
|
||||
updateItem(getItem(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(T item, boolean empty) {
|
||||
textProperty().unbind();
|
||||
super.updateItem(item, empty);
|
||||
if (item != null && !(item instanceof String)) {
|
||||
try {
|
||||
textProperty().bind(stringProp(item, "name"));
|
||||
} catch (NoSuchMethodException ex) {
|
||||
Logger.getLogger(ApplicationUIControllerImpl.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
finishUpdate(item);
|
||||
} else {
|
||||
setText(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void finishUpdate(T item) {
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package org.badvision.outlaweditor;
|
||||
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import org.badvision.outlaweditor.data.DataObserver;
|
||||
import org.badvision.outlaweditor.data.DataProducer;
|
||||
|
||||
/**
|
||||
* Extremely generic editor abstraction -- useful for uniform edit features across application
|
||||
* @author brobert
|
||||
*/
|
||||
public abstract class Editor<T,D> implements DataObserver<T> {
|
||||
T editEntity;
|
||||
public void setEntity(T t) {
|
||||
editEntity = t;
|
||||
DataProducer.addObserver(t, this);
|
||||
}
|
||||
public T getEntity() {
|
||||
return editEntity;
|
||||
}
|
||||
abstract public void setDrawMode(D drawMode);
|
||||
abstract public void showShiftUI();
|
||||
abstract public void buildEditorUI(AnchorPane tileEditorAnchorPane);
|
||||
abstract public void unregister();
|
||||
|
||||
abstract public void copy();
|
||||
abstract public void paste();
|
||||
abstract public void select();
|
||||
abstract public void selectNone();
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
int endX = 0;
|
||||
int endY = 0;
|
||||
public void setSelectionArea(int x1, int y1, int x2, int y2) {
|
||||
startX = Math.min(x1, x2);
|
||||
startY = Math.min(y1, y2);
|
||||
endX = Math.max(x1, x2);
|
||||
endY = Math.max(y1, y2);
|
||||
if (startX + startY + endX + endY == 0) {
|
||||
selectInfo = null;
|
||||
} else {
|
||||
selectInfo="x1/"+startX+"/y1/"+startY+"/x2/"+endX+"/y2/"+endY;
|
||||
}
|
||||
}
|
||||
public String getSelectedAllInfo() {
|
||||
return "all";
|
||||
}
|
||||
|
||||
String selectInfo;
|
||||
public String getSelectionInfo() {
|
||||
if (selectInfo == null) {
|
||||
return getSelectedAllInfo();
|
||||
}
|
||||
return selectInfo;
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
package org.badvision.outlaweditor;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.Inflater;
|
||||
import javafx.stage.FileChooser;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class FileUtils {
|
||||
|
||||
public static enum Extension {
|
||||
|
||||
XML("XML files", "xml"),
|
||||
TILESET("Tileset", "ots"),
|
||||
ALL("All files", "*");
|
||||
String description;
|
||||
String extension;
|
||||
|
||||
Extension(String desc, String ext) {
|
||||
this.description = desc;
|
||||
this.extension = ext;
|
||||
}
|
||||
|
||||
public FileChooser.ExtensionFilter getExtensionFilter() {
|
||||
return new FileChooser.ExtensionFilter(description + " (*." + extension + ")", "*." + extension);
|
||||
}
|
||||
}
|
||||
|
||||
public static File getFile(File prevFile, String title, Boolean create, Extension... supportedExtensions) {
|
||||
FileChooser f = new FileChooser();
|
||||
if (prevFile != null) {
|
||||
if (prevFile.isFile()) {
|
||||
f.setInitialDirectory(prevFile.getParentFile());
|
||||
} else {
|
||||
f.setInitialDirectory(prevFile);
|
||||
}
|
||||
}
|
||||
f.setTitle(title);
|
||||
for (Extension e : supportedExtensions) {
|
||||
f.getExtensionFilters().add(e.getExtensionFilter());
|
||||
}
|
||||
if (create) {
|
||||
File file = f.showSaveDialog(Application.getPrimaryStage());
|
||||
if (!file.getName().contains(".")) {
|
||||
return new File(file.getParentFile(), file.getName()+"."+supportedExtensions[0].extension);
|
||||
} else {
|
||||
return file;
|
||||
}
|
||||
} else {
|
||||
return f.showOpenDialog(Application.getPrimaryStage());
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T loadFromFile(File f, Class<T> clazz) throws FileNotFoundException, IOException, ClassNotFoundException {
|
||||
if (!f.isFile() || !f.exists()) {
|
||||
return null;
|
||||
}
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
byte[] b = new byte[1024];
|
||||
try (FileInputStream in = new FileInputStream(f)) {
|
||||
int read;
|
||||
while ((read = in.read(b)) > 0) {
|
||||
buffer.write(b, 0, read);
|
||||
}
|
||||
byte[] decompressed = decompress(buffer.toByteArray());
|
||||
return byteToObject(decompressed);
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveToFile(File f, Object obj) throws IOException {
|
||||
try (FileOutputStream fis = new FileOutputStream(f, false)) {
|
||||
fis.write(compress(objectToByte(obj)));
|
||||
fis.flush();
|
||||
fis.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T byteToObject(byte[] data) throws IOException, ClassNotFoundException {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(data);
|
||||
ObjectInputStream is = new ObjectInputStream(in);
|
||||
return (T) is.readObject();
|
||||
}
|
||||
|
||||
public static byte[] objectToByte(Object o) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try (ObjectOutputStream os = new ObjectOutputStream(out)) {
|
||||
os.writeObject(o);
|
||||
os.flush();
|
||||
os.close();
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
public static byte[] compress(byte[] bytesToCompress) {
|
||||
Deflater deflater = new Deflater();
|
||||
deflater.setInput(bytesToCompress);
|
||||
deflater.finish();
|
||||
byte[] bytesCompressed = new byte[Short.MAX_VALUE];
|
||||
int numberOfBytesAfterCompression = deflater.deflate(bytesCompressed);
|
||||
byte[] returnValues = new byte[numberOfBytesAfterCompression];
|
||||
System.arraycopy(
|
||||
bytesCompressed,
|
||||
0,
|
||||
returnValues,
|
||||
0,
|
||||
numberOfBytesAfterCompression);
|
||||
return returnValues;
|
||||
}
|
||||
|
||||
public static byte[] decompress(byte[] bytesToDecompress) {
|
||||
int numberOfBytesToDecompress = bytesToDecompress.length;
|
||||
try {
|
||||
Inflater inflater = new Inflater();
|
||||
inflater.setInput(
|
||||
bytesToDecompress,
|
||||
0,
|
||||
numberOfBytesToDecompress);
|
||||
int compressionFactorMaxLikely = 3;
|
||||
int bufferSizeInBytes = numberOfBytesToDecompress * compressionFactorMaxLikely;
|
||||
byte[] bytesDecompressed = new byte[bufferSizeInBytes];
|
||||
int numberOfBytesAfterDecompression = inflater.inflate(bytesDecompressed);
|
||||
byte[] returnValues = new byte[numberOfBytesAfterDecompression];
|
||||
System.arraycopy(
|
||||
bytesDecompressed,
|
||||
0,
|
||||
returnValues,
|
||||
0,
|
||||
numberOfBytesAfterDecompression);
|
||||
inflater.end();
|
||||
return returnValues;
|
||||
} catch (DataFormatException dfe) {
|
||||
dfe.printStackTrace(System.err);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.badvision.outlaweditor;
|
||||
|
||||
import javafx.scene.control.Menu;
|
||||
import org.badvision.outlaweditor.data.xml.Image;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public abstract class ImageEditor extends Editor<Image, ImageEditor.DrawMode> {
|
||||
public static enum DrawMode {
|
||||
Toggle, Pencil1px, Pencil3px, Pencil5px, Rectangle, Circle, Stamp
|
||||
}
|
||||
|
||||
abstract public void buildPatternSelector(Menu tilePatternMenu);
|
||||
|
||||
abstract public void redraw();
|
||||
|
||||
public abstract void scrollBy(int deltaX, int deltaY);
|
||||
|
||||
public abstract void togglePanZoom();
|
||||
|
||||
public abstract void zoomIn();
|
||||
|
||||
public abstract void zoomOut();
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package org.badvision.outlaweditor;
|
||||
|
||||
import javafx.scene.image.WritableImage;
|
||||
import org.badvision.outlaweditor.data.TileMap;
|
||||
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public abstract class ImageRenderer {
|
||||
|
||||
public abstract WritableImage renderImage(WritableImage img, byte[] rawImage);
|
||||
|
||||
public abstract byte[] createImageBuffer();
|
||||
|
||||
public abstract WritableImage renderPreview(TileMap map, int startX, int startY);
|
||||
|
||||
public abstract WritableImage renderScanline(WritableImage currentImage, int y, byte[] imageData);
|
||||
|
||||
}
|
@ -0,0 +1,426 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.badvision.outlaweditor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.ImageCursor;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.canvas.Canvas;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.DataFormat;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.input.ScrollEvent;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import javafx.stage.Stage;
|
||||
import org.badvision.outlaweditor.data.TileMap;
|
||||
import org.badvision.outlaweditor.data.TileUtils;
|
||||
import org.badvision.outlaweditor.data.xml.Map;
|
||||
import org.badvision.outlaweditor.data.xml.Tile;
|
||||
import static org.badvision.outlaweditor.Application.currentPlatform;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class MapEditor extends Editor<Map, MapEditor.DrawMode> implements EventHandler<MouseEvent> {
|
||||
|
||||
AnchorPane anchorPane;
|
||||
Canvas drawCanvas;
|
||||
private Tile currentTile;
|
||||
int posX = 0;
|
||||
int posY = 0;
|
||||
double zoom = 1.0;
|
||||
DrawMode drawMode = DrawMode.Pencil1px;
|
||||
TileMap currentMap;
|
||||
double tileWidth = currentPlatform.tileRenderer.getWidth() * zoom;
|
||||
double tileHeight = currentPlatform.tileRenderer.getHeight() * zoom;
|
||||
|
||||
@Override
|
||||
public void setEntity(Map t) {
|
||||
super.setEntity(t);
|
||||
currentMap = new TileMap(t);
|
||||
}
|
||||
EventHandler<ScrollEvent> scrollHandler = new EventHandler<ScrollEvent>() {
|
||||
@Override
|
||||
public void handle(ScrollEvent t) {
|
||||
if (t.isShiftDown()) {
|
||||
t.consume();
|
||||
if (t.getDeltaY() > 0) {
|
||||
zoomIn();
|
||||
} else {
|
||||
zoomOut();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void setDrawMode(DrawMode drawMode) {
|
||||
this.drawMode = drawMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showShiftUI() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildEditorUI(AnchorPane tileEditorAnchorPane) {
|
||||
anchorPane = tileEditorAnchorPane;
|
||||
initCanvas();
|
||||
redraw();
|
||||
}
|
||||
|
||||
public void initCanvas() {
|
||||
if (drawCanvas != null) {
|
||||
anchorPane.getChildren().remove(drawCanvas);
|
||||
}
|
||||
drawCanvas = new Canvas();
|
||||
drawCanvas.heightProperty().bind(Application.getPrimaryStage().heightProperty().subtract(120));
|
||||
drawCanvas.widthProperty().bind(Application.getPrimaryStage().widthProperty().subtract(200));
|
||||
// drawCanvas.widthProperty().bind(anchorPane.widthProperty());
|
||||
drawCanvas.widthProperty().addListener(new ChangeListener<Number>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
|
||||
redraw();
|
||||
}
|
||||
});
|
||||
drawCanvas.heightProperty().addListener(new ChangeListener<Number>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
|
||||
redraw();
|
||||
}
|
||||
});
|
||||
drawCanvas.addEventFilter(ScrollEvent.ANY, scrollHandler);
|
||||
drawCanvas.setOnMousePressed(this);
|
||||
drawCanvas.setOnMouseDragged(this);
|
||||
drawCanvas.setOnMouseDragReleased(this);
|
||||
drawCanvas.setOnMouseReleased(this);
|
||||
anchorPane.getChildren().add(0, drawCanvas);
|
||||
}
|
||||
|
||||
public void togglePanZoom() {
|
||||
for (Node n : anchorPane.getChildren()) {
|
||||
if (n == drawCanvas) {
|
||||
continue;
|
||||
}
|
||||
n.setVisible(!n.isVisible());
|
||||
}
|
||||
}
|
||||
|
||||
public void scrollBy(int deltaX, int deltaY) {
|
||||
posX += deltaX * (drawCanvas.getWidth() / tileWidth / 2);
|
||||
posY += deltaY * (drawCanvas.getHeight() / tileWidth / 2);
|
||||
posX = Math.max(0, posX);
|
||||
posY = Math.max(0, posY);
|
||||
redraw();
|
||||
}
|
||||
|
||||
public void zoomOut() {
|
||||
if (zoom <= 1) {
|
||||
zoom(-0.1);
|
||||
} else {
|
||||
zoom(-0.25);
|
||||
}
|
||||
}
|
||||
|
||||
public void zoomIn() {
|
||||
if (zoom >= 1) {
|
||||
zoom(0.25);
|
||||
} else {
|
||||
zoom(0.1);
|
||||
}
|
||||
}
|
||||
|
||||
private void zoom(double delta) {
|
||||
double oldZoom = zoom;
|
||||
zoom += delta;
|
||||
zoom = Math.min(Math.max(0.15, zoom), 4.0);
|
||||
// double left = mapEditorScroll.getHvalue();
|
||||
// double top = mapEditorScroll.getVvalue();
|
||||
//
|
||||
// double pointerX = t.getX();
|
||||
// double pointerY = t.getY();
|
||||
//
|
||||
double ratio = zoom / oldZoom;
|
||||
//
|
||||
// double newLeft = (left + pointerX) * ratio - pointerX;
|
||||
// double newTop = (top + pointerY) * ratio - pointerY;
|
||||
tileWidth = currentPlatform.tileRenderer.getWidth() * zoom;
|
||||
tileHeight = currentPlatform.tileRenderer.getHeight() * zoom;
|
||||
redraw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void observedObjectChanged(Map object) {
|
||||
redraw();
|
||||
}
|
||||
private long redrawRequested;
|
||||
private Thread redrawThread;
|
||||
|
||||
public void redraw() {
|
||||
redrawRequested = System.nanoTime();
|
||||
if (redrawThread == null || redrawThread.isAlive()) {
|
||||
redrawThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
long test = redrawRequested;
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(MapEditor.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
while (test != redrawRequested) {
|
||||
test = redrawRequested;
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(MapEditor.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
Platform.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doRedraw();
|
||||
}
|
||||
});
|
||||
redrawThread = null;
|
||||
}
|
||||
});
|
||||
redrawThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void doRedraw() {
|
||||
drawCanvas.getGraphicsContext2D().clearRect(0, 0, drawCanvas.getWidth(), drawCanvas.getHeight());
|
||||
int cols = (int) (drawCanvas.getWidth() / tileWidth);
|
||||
int rows = (int) (drawCanvas.getHeight() / tileHeight);
|
||||
for (int x = 0; x <= cols; x++) {
|
||||
for (int y = 0; y <= rows; y++) {
|
||||
Tile tile = currentMap.get(posX + x, posY + y);
|
||||
doDraw(x, y, tile);
|
||||
}
|
||||
}
|
||||
anchorPane.getChildren().get(1).setLayoutX((drawCanvas.getWidth() - 30) / 2);
|
||||
anchorPane.getChildren().get(2).setLayoutY((drawCanvas.getHeight() - 30) / 2);
|
||||
anchorPane.getChildren().get(3).setLayoutX((drawCanvas.getWidth() - 30) / 2);
|
||||
anchorPane.getChildren().get(4).setLayoutY((drawCanvas.getHeight() - 30) / 2);
|
||||
}
|
||||
|
||||
private void doDraw(int x, int y, Tile tile) {
|
||||
double xx = x * tileWidth;
|
||||
double yy = y * tileHeight;
|
||||
if (tile != null) {
|
||||
drawCanvas.getGraphicsContext2D().drawImage(TileUtils.getImage(tile, currentPlatform), xx, yy, tileWidth, tileHeight);
|
||||
} else {
|
||||
drawCanvas.getGraphicsContext2D().clearRect(xx, yy, tileWidth, tileHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister() {
|
||||
drawCanvas.widthProperty().unbind();
|
||||
drawCanvas.heightProperty().unbind();
|
||||
anchorPane.getChildren().remove(drawCanvas);
|
||||
currentMap.updateBackingMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the currentTile
|
||||
*/
|
||||
public Tile getCurrentTile() {
|
||||
return currentTile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param currentTile the currentTile to set
|
||||
*/
|
||||
public void setCurrentTile(Tile currentTile) {
|
||||
this.currentTile = currentTile;
|
||||
ImageCursor cursor = new ImageCursor(TileUtils.getImage(currentTile, currentPlatform), 2, 2);
|
||||
drawCanvas.setCursor(cursor);
|
||||
}
|
||||
|
||||
public void showPreview() {
|
||||
WritableImage img = currentPlatform.imageRenderer.renderPreview(currentMap, posX, posY);
|
||||
Stage stage = new Stage();
|
||||
stage.setTitle("Preview");
|
||||
ImageView imgView = new ImageView(img);
|
||||
Group root = new Group(imgView);
|
||||
stage.setScene(new Scene(root, img.getWidth(), img.getHeight()));
|
||||
stage.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy() {
|
||||
WritableImage img = currentPlatform.imageRenderer.renderPreview(currentMap, posX, posY);
|
||||
java.util.Map<DataFormat,Object> clip = new HashMap<>();
|
||||
clip.put(DataFormat.IMAGE, img);
|
||||
clip.put(DataFormat.PLAIN_TEXT, "selection/map/"+Application.gameData.getMap().indexOf(getEntity())+"/"+getSelectionInfo());
|
||||
Clipboard.getSystemClipboard().setContent(clip);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectedAllInfo() {
|
||||
setSelectionArea(posX, posY, posX+19, posY+11);
|
||||
String result = getSelectionInfo();
|
||||
setSelectionArea(0,0,0,0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paste() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void select() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectNone() {
|
||||
setSelectionArea(0,0,0,0);
|
||||
}
|
||||
|
||||
public static enum DrawMode {
|
||||
|
||||
Pencil1px, Pencil3px, Pencil5px, FilledRect
|
||||
};
|
||||
|
||||
public void plot(final int x, final int y, final Tile t) {
|
||||
if (x < 0) {
|
||||
return;
|
||||
}
|
||||
if (y < 0) {
|
||||
return;
|
||||
}
|
||||
if (x >= getEntity().getWidth()) {
|
||||
System.out.println("X out of bounds " + getEntity().getWidth());
|
||||
return;
|
||||
}
|
||||
if (y >= getEntity().getHeight()) {
|
||||
System.out.println("Y out of bounds " + getEntity().getHeight());
|
||||
return;
|
||||
}
|
||||
currentMap.put(x, y, t);
|
||||
doDraw(x - posX, y - posY, t);
|
||||
}
|
||||
|
||||
public void drawBrush(int x, int y, int size, Tile t) {
|
||||
for (int xx = x - size; xx <= x + size; xx++) {
|
||||
for (int yy = y - size; yy <= y + size; yy++) {
|
||||
if (Math.sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y)) > size) {
|
||||
continue;
|
||||
}
|
||||
plot(xx, yy, getCurrentTile());
|
||||
}
|
||||
}
|
||||
}
|
||||
public static Rectangle selectRect = null;
|
||||
public double selectStartX = 0;
|
||||
public double selectStartY = 0;
|
||||
|
||||
private void startSelection(double x, double y) {
|
||||
selectRect = new Rectangle(1, 1, Color.NAVY);
|
||||
selectRect.setTranslateX(x);
|
||||
selectRect.setTranslateY(y);
|
||||
selectRect.setOpacity(0.5);
|
||||
selectStartX = x;
|
||||
selectStartY = y;
|
||||
anchorPane.getChildren().add(selectRect);
|
||||
}
|
||||
|
||||
private void updateSelection(double x, double y) {
|
||||
if (selectRect == null) {
|
||||
startSelection(x, y);
|
||||
}
|
||||
|
||||
double minX = Math.min(selectStartX, x);
|
||||
double minY = Math.min(selectStartY, y);
|
||||
double maxX = Math.max(selectStartX, x);
|
||||
double maxY = Math.max(selectStartY, y);
|
||||
selectRect.setTranslateX(minX);
|
||||
selectRect.setTranslateY(minY);
|
||||
selectRect.setWidth(maxX - minX);
|
||||
selectRect.setHeight(maxY - minY);
|
||||
}
|
||||
|
||||
private void fillSelection(double x, double y) {
|
||||
anchorPane.getChildren().remove(selectRect);
|
||||
selectRect = null;
|
||||
|
||||
int startx = (int) (selectStartX / tileWidth);
|
||||
int starty = (int) (selectStartY / tileHeight);
|
||||
int endx = (int) (x / tileWidth);
|
||||
int endy = (int) (y / tileHeight);
|
||||
for (int yy = Math.min(starty, endy); yy <= Math.max(starty, endy); yy++) {
|
||||
for (int xx = Math.min(startx, endx); xx <= Math.max(startx, endx); xx++) {
|
||||
plot(xx, yy, getCurrentTile());
|
||||
}
|
||||
}
|
||||
}
|
||||
public static int lastX = -1;
|
||||
public static int lastY = -1;
|
||||
DrawMode lastDrawMode = null;
|
||||
Tile lastTile = null;
|
||||
|
||||
@Override
|
||||
public void handle(MouseEvent t) {
|
||||
if (getCurrentTile() == null) {
|
||||
System.out.println("No tile selected, ignoring");
|
||||
return;
|
||||
}
|
||||
t.consume();
|
||||
int x = (int) (t.getX() / tileWidth) + posX;
|
||||
int y = (int) (t.getY() / tileHeight) + posY;
|
||||
boolean canSkip = false;
|
||||
if (getCurrentTile() == lastTile && x == lastX && y == lastY && drawMode == lastDrawMode) {
|
||||
canSkip = true;
|
||||
}
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
lastDrawMode = drawMode;
|
||||
lastTile = getCurrentTile();
|
||||
switch (drawMode) {
|
||||
case Pencil1px:
|
||||
if (canSkip) {
|
||||
return;
|
||||
}
|
||||
plot(x, y, getCurrentTile());
|
||||
break;
|
||||
case Pencil3px:
|
||||
if (canSkip) {
|
||||
return;
|
||||
}
|
||||
drawBrush(x, y, 2, getCurrentTile());
|
||||
break;
|
||||
case Pencil5px:
|
||||
if (canSkip) {
|
||||
return;
|
||||
}
|
||||
drawBrush(x, y, 5, getCurrentTile());
|
||||
break;
|
||||
case FilledRect:
|
||||
if (t.getEventType().equals(MouseEvent.MOUSE_RELEASED)) {
|
||||
fillSelection(t.getX(), t.getY());
|
||||
} else {
|
||||
updateSelection(t.getX(), t.getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package org.badvision.outlaweditor;
|
||||
|
||||
import org.badvision.outlaweditor.apple.AppleImageEditor;
|
||||
import org.badvision.outlaweditor.apple.AppleTileEditor;
|
||||
import org.badvision.outlaweditor.apple.AppleTileRenderer;
|
||||
import org.badvision.outlaweditor.apple.AppleImageRenderer;
|
||||
import org.badvision.outlaweditor.apple.dhgr.AppleDHGRImageEditor;
|
||||
import org.badvision.outlaweditor.apple.dhgr.AppleDHGRImageRenderer;
|
||||
import org.badvision.outlaweditor.apple.dhgr.AppleDHGRTileEditor;
|
||||
import org.badvision.outlaweditor.apple.dhgr.AppleDHGRTileRenderer;
|
||||
|
||||
/**
|
||||
* Enumeration of platforms
|
||||
* @author brobert
|
||||
*/
|
||||
public enum Platform {
|
||||
AppleII(AppleTileEditor.class, AppleImageEditor.class, new AppleTileRenderer(), new AppleImageRenderer(),2, 16),
|
||||
AppleII_DHGR(AppleDHGRTileEditor.class, AppleDHGRImageEditor.class, new AppleDHGRTileRenderer(), new AppleDHGRImageRenderer(),4, 16),
|
||||
C64(null, null, null, null, 16, 16);
|
||||
|
||||
public Class<? extends TileEditor> tileEditor;
|
||||
public Class<? extends ImageEditor> imageEditor;
|
||||
public TileRenderer tileRenderer;
|
||||
public ImageRenderer imageRenderer;
|
||||
public int dataWidth;
|
||||
public int dataHeight;
|
||||
|
||||
Platform(Class ed, Class imged, TileRenderer ren, ImageRenderer img, int w, int h) {
|
||||
tileEditor = ed;
|
||||
imageEditor = imged;
|
||||
tileRenderer = ren;
|
||||
imageRenderer = img;
|
||||
dataWidth = w;
|
||||
dataHeight = h;
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package org.badvision.outlaweditor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
import javafx.scene.control.Menu;
|
||||
import org.badvision.outlaweditor.data.xml.Tile;
|
||||
|
||||
public abstract class TileEditor extends Editor<Tile, TileEditor.DrawMode> {
|
||||
abstract public void buildPatternSelector(Menu tilePatternMenu);
|
||||
|
||||
public static enum DrawMode {
|
||||
Pencil1px, Pencil3px, Toggle
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package org.badvision.outlaweditor;
|
||||
|
||||
import javafx.scene.image.WritableImage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public abstract class TileRenderer {
|
||||
|
||||
public abstract WritableImage redrawSprite(byte[] spriteData, WritableImage image);
|
||||
public abstract int getWidth();
|
||||
public abstract int getHeight();
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.badvision.outlaweditor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javafx.application.Platform;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.control.MenuBar;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.xml.bind.JAXB;
|
||||
import org.badvision.outlaweditor.data.TilesetUtils;
|
||||
import org.badvision.outlaweditor.data.xml.GameData;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class UIAction {
|
||||
|
||||
private static File currentSaveFile;
|
||||
|
||||
public static enum MAIN_ACTIONS {
|
||||
|
||||
_General,
|
||||
Load, Save, Save_as, Apple_Mode, C64_Mode, About_Outlaw_Editor, Quit,
|
||||
_Map,
|
||||
Create_new_map, Export_map_to_image,
|
||||
_Tiles,
|
||||
Create_new_tile, Export_tile_to_image, Delete_tile,
|
||||
_Images,
|
||||
Create_new_image, Import_image, Export_image_as_raw, Delete_image;
|
||||
};
|
||||
|
||||
public static void actionPerformed(MAIN_ACTIONS action) throws IOException {
|
||||
switch (action) {
|
||||
case About_Outlaw_Editor:
|
||||
break;
|
||||
case Apple_Mode:
|
||||
break;
|
||||
case C64_Mode:
|
||||
break;
|
||||
case Delete_tile:
|
||||
// if (currentSelectedTile != null) {
|
||||
// Tile t = currentSelectedTile;
|
||||
// selectTile(null, null);
|
||||
// TilesetUtils.remove(t);
|
||||
// Application.instance.redrawTileSelector();
|
||||
// Application.instance.rebuildMapEditor();
|
||||
// }
|
||||
break;
|
||||
case Export_map_to_image:
|
||||
break;
|
||||
case Export_tile_to_image:
|
||||
break;
|
||||
case Load:
|
||||
File f = FileUtils.getFile(currentSaveFile, "Load game data", Boolean.FALSE, FileUtils.Extension.XML, FileUtils.Extension.ALL);
|
||||
if (f == null) {
|
||||
return;
|
||||
}
|
||||
currentSaveFile = f;
|
||||
GameData newData = JAXB.unmarshal(currentSaveFile, GameData.class);
|
||||
Application.instance.controller.setCurrentMap(null);
|
||||
Application.instance.controller.setCurrentTile(null);
|
||||
TilesetUtils.clear();
|
||||
Application.gameData = newData;
|
||||
Application.instance.controller.rebuildTileSelectors();
|
||||
Application.instance.controller.rebuildMapSelectors();
|
||||
break;
|
||||
case Quit:
|
||||
quit();
|
||||
break;
|
||||
case Save_as:
|
||||
f = FileUtils.getFile(currentSaveFile, "Save game data", Boolean.TRUE, FileUtils.Extension.XML, FileUtils.Extension.ALL);
|
||||
if (f == null) {
|
||||
return;
|
||||
}
|
||||
currentSaveFile = f;
|
||||
case Save:
|
||||
if (currentSaveFile == null) {
|
||||
currentSaveFile = FileUtils.getFile(currentSaveFile, "Save game data", Boolean.TRUE, FileUtils.Extension.XML, FileUtils.Extension.ALL);
|
||||
}
|
||||
if (currentSaveFile != null) {
|
||||
currentSaveFile.delete();
|
||||
JAXB.marshal(Application.gameData, currentSaveFile);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static MenuBar buildMenu() {
|
||||
MenuBar menu = new MenuBar();
|
||||
Menu currentMenu = null;
|
||||
for (final MAIN_ACTIONS action : UIAction.MAIN_ACTIONS.values()) {
|
||||
if (action.name().startsWith("_")) {
|
||||
if (currentMenu != null) {
|
||||
menu.getMenus().add(currentMenu);
|
||||
}
|
||||
currentMenu = new Menu(action.name().replace("_", ""));
|
||||
} else {
|
||||
MenuItem item = new MenuItem(action.name().replaceAll("_", " "));
|
||||
item.setOnAction(new EventHandler<ActionEvent>() {
|
||||
@Override
|
||||
public void handle(ActionEvent t) {
|
||||
try {
|
||||
actionPerformed(action);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(Application.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
currentMenu.getItems().add(item);
|
||||
}
|
||||
}
|
||||
menu.getMenus().add(currentMenu);
|
||||
menu.setMinWidth(1.0);
|
||||
return menu;
|
||||
}
|
||||
|
||||
public static boolean quit() {
|
||||
if (JOptionPane.showConfirmDialog(null, "Quit? Are you sure?") == JOptionPane.OK_OPTION) {
|
||||
Platform.exit();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Image badImage;
|
||||
|
||||
public static WritableImage getBadImage(int width, int height) {
|
||||
if (badImage == null) {
|
||||
badImage = new Image("/org/badvision/outlaw/resources/icon_brokenLink.png");
|
||||
}
|
||||
WritableImage img = new WritableImage(width, height);
|
||||
img.getPixelWriter().setPixels(0, 0, (int) badImage.getWidth(), (int) badImage.getHeight(), badImage.getPixelReader(), 0, 0);
|
||||
|
||||
return img;
|
||||
}
|
||||
}
|
@ -0,0 +1,402 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.badvision.outlaweditor.apple;
|
||||
|
||||
import java.util.HashMap;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.DataFormat;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import org.badvision.outlaweditor.Application;
|
||||
import org.badvision.outlaweditor.ImageEditor;
|
||||
import org.badvision.outlaweditor.Platform;
|
||||
import org.badvision.outlaweditor.data.DataObserver;
|
||||
import org.badvision.outlaweditor.data.xml.Image;
|
||||
import org.badvision.outlaweditor.data.xml.PlatformData;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class AppleImageEditor extends ImageEditor implements EventHandler<MouseEvent> {
|
||||
|
||||
public int[] currentFillPattern = FillPattern.White.bytePattern;
|
||||
public boolean hiBitMatters = true;
|
||||
protected DrawMode currentDrawMode = DrawMode.Pencil1px;
|
||||
protected WritableImage currentImage;
|
||||
protected AnchorPane anchorPane;
|
||||
protected ImageView screen;
|
||||
protected int posX = 0;
|
||||
protected int posY = 0;
|
||||
protected double zoom = 1.0;
|
||||
protected int xScale = 2;
|
||||
protected int yScale = 2;
|
||||
|
||||
public Platform getPlatform() {
|
||||
return Platform.AppleII;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildEditorUI(AnchorPane editorAnchorPane) {
|
||||
anchorPane = editorAnchorPane;
|
||||
redraw();
|
||||
screen = new ImageView(currentImage);
|
||||
anchorPane.getChildren().add(0, screen);
|
||||
screen.setOnMouseClicked(this);
|
||||
screen.setOnMouseReleased(this);
|
||||
screen.setOnMouseDragged(this);
|
||||
screen.setOnMouseDragReleased(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildPatternSelector(Menu tilePatternMenu) {
|
||||
FillPattern.buildMenu(tilePatternMenu, new DataObserver<FillPattern>() {
|
||||
@Override
|
||||
public void observedObjectChanged(FillPattern object) {
|
||||
changeCurrentPattern(object);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void changeCurrentPattern(FillPattern pattern) {
|
||||
currentFillPattern = pattern.getBytePattern();
|
||||
hiBitMatters = pattern.hiBitMatters;
|
||||
lastActionX = -1;
|
||||
lastActionY = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDrawMode(DrawMode drawMode) {
|
||||
currentDrawMode = drawMode;
|
||||
lastActionX = -1;
|
||||
lastActionY = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showShiftUI() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister() {
|
||||
anchorPane.getChildren().remove(screen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void observedObjectChanged(Image object) {
|
||||
redraw();
|
||||
}
|
||||
|
||||
public void redrawScanline(int y) {
|
||||
currentImage = getPlatform().imageRenderer.renderScanline(currentImage, y, getImageData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redraw() {
|
||||
System.out.println("Redraw "+getPlatform().name());
|
||||
currentImage = getPlatform().imageRenderer.renderImage(currentImage, getImageData());
|
||||
anchorPane.getChildren().get(1).setLayoutX((anchorPane.getWidth() - 30) / 2);
|
||||
anchorPane.getChildren().get(2).setLayoutY((anchorPane.getHeight() - 30) / 2);
|
||||
anchorPane.getChildren().get(3).setLayoutX((anchorPane.getWidth() - 30) / 2);
|
||||
anchorPane.getChildren().get(4).setLayoutY((anchorPane.getHeight() - 30) / 2);
|
||||
}
|
||||
private byte[] imageData = null;
|
||||
|
||||
public byte[] getImageData() {
|
||||
if (imageData == null) {
|
||||
PlatformData data = null;
|
||||
for (PlatformData d : getEntity().getDisplayData()) {
|
||||
if (d.getPlatform().equalsIgnoreCase(getPlatform().name())) {
|
||||
data = d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (data == null) {
|
||||
data = new PlatformData();
|
||||
data.setWidth(40);
|
||||
data.setHeight(192);
|
||||
data.setPlatform(getPlatform().name());
|
||||
data.setValue(getPlatform().imageRenderer.createImageBuffer());
|
||||
getEntity().getDisplayData().add(data);
|
||||
}
|
||||
imageData = data.getValue();
|
||||
}
|
||||
return imageData;
|
||||
}
|
||||
|
||||
public void setData(byte[] data) {
|
||||
imageData = data;
|
||||
for (PlatformData d : getEntity().getDisplayData()) {
|
||||
if (d.getPlatform().equalsIgnoreCase(getPlatform().name())) {
|
||||
d.setValue(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void togglePanZoom() {
|
||||
for (Node n : anchorPane.getChildren()) {
|
||||
if (n == screen) {
|
||||
continue;
|
||||
}
|
||||
n.setVisible(!n.isVisible());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollBy(int deltaX, int deltaY) {
|
||||
posX += deltaX * 10;
|
||||
posY += deltaY * 10;
|
||||
posX = Math.max(0, posX);
|
||||
posY = Math.max(0, posY);
|
||||
redraw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void zoomOut() {
|
||||
if (zoom <= 1) {
|
||||
zoom(-0.1);
|
||||
} else {
|
||||
zoom(-0.25);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void zoomIn() {
|
||||
if (zoom >= 1) {
|
||||
zoom(0.25);
|
||||
} else {
|
||||
zoom(0.1);
|
||||
}
|
||||
}
|
||||
|
||||
private void zoom(double delta) {
|
||||
double oldZoom = zoom;
|
||||
zoom += delta;
|
||||
zoom = Math.min(Math.max(0.15, zoom), 4.0);
|
||||
// double left = mapEditorScroll.getHvalue();
|
||||
// double top = mapEditorScroll.getVvalue();
|
||||
//
|
||||
// double pointerX = t.getX();
|
||||
// double pointerY = t.getY();
|
||||
//
|
||||
double ratio = zoom / oldZoom;
|
||||
//
|
||||
// double newLeft = (left + pointerX) * ratio - pointerX;
|
||||
// double newTop = (top + pointerY) * ratio - pointerY;
|
||||
redraw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(MouseEvent t) {
|
||||
performAction(t.isShiftDown() || t.isSecondaryButtonDown(), t.getEventType().equals(MouseEvent.MOUSE_RELEASED), (int) t.getX() / xScale, (int) t.getY() / yScale);
|
||||
}
|
||||
protected int lastActionX = -1;
|
||||
protected int lastActionY = -1;
|
||||
|
||||
public void performAction(boolean alt, boolean released, int x, int y) {
|
||||
y = Math.min(Math.max(y, 0), 191);
|
||||
x = Math.min(Math.max(x, 0), (getWidth() * 7) - 1);
|
||||
boolean canSkip = false;
|
||||
if (lastActionX == x && lastActionY == y) {
|
||||
canSkip = true;
|
||||
}
|
||||
lastActionX = x;
|
||||
lastActionY = y;
|
||||
switch (currentDrawMode) {
|
||||
case Toggle:
|
||||
if (canSkip) {
|
||||
return;
|
||||
}
|
||||
if (alt) {
|
||||
toggleHiBit(x, y);
|
||||
} else {
|
||||
toggle(x, y);
|
||||
}
|
||||
redrawScanline(y);
|
||||
break;
|
||||
case Pencil1px:
|
||||
if (canSkip) {
|
||||
return;
|
||||
}
|
||||
plot(x, y, currentFillPattern, hiBitMatters);
|
||||
redrawScanline(y);
|
||||
break;
|
||||
case Pencil3px:
|
||||
if (canSkip) {
|
||||
return;
|
||||
}
|
||||
drawBrush(x, y, 3, currentFillPattern, hiBitMatters);
|
||||
break;
|
||||
case Pencil5px:
|
||||
if (canSkip) {
|
||||
return;
|
||||
}
|
||||
drawBrush(x, y, 5, currentFillPattern, hiBitMatters);
|
||||
break;
|
||||
case Rectangle:
|
||||
if (released) {
|
||||
fillSelection(x, y);
|
||||
redraw();
|
||||
} else {
|
||||
updateSelection(x, y);
|
||||
}
|
||||
}
|
||||
// observedObjectChanged(getEntity());
|
||||
}
|
||||
public static Rectangle selectRect = null;
|
||||
public int selectStartX = 0;
|
||||
public int selectStartY = 0;
|
||||
|
||||
private void startSelection(int x, int y) {
|
||||
selectRect = new Rectangle(1, 1, Color.NAVY);
|
||||
selectRect.setTranslateX(x);
|
||||
selectRect.setTranslateY(y);
|
||||
selectRect.setOpacity(0.5);
|
||||
selectStartX = x;
|
||||
selectStartY = y;
|
||||
anchorPane.getChildren().add(selectRect);
|
||||
}
|
||||
|
||||
private void updateSelection(int x, int y) {
|
||||
if (selectRect == null) {
|
||||
startSelection(x, y);
|
||||
}
|
||||
|
||||
double minX = Math.min(selectStartX, x) * xScale;
|
||||
double minY = Math.min(selectStartY, y) * yScale;
|
||||
double maxX = Math.max(selectStartX, x) * xScale;
|
||||
double maxY = Math.max(selectStartY, y) * yScale;
|
||||
selectRect.setTranslateX(minX);
|
||||
selectRect.setTranslateY(minY);
|
||||
selectRect.setWidth(maxX - minX);
|
||||
selectRect.setHeight(maxY - minY);
|
||||
}
|
||||
|
||||
private void fillSelection(int x, int y) {
|
||||
anchorPane.getChildren().remove(selectRect);
|
||||
selectRect = null;
|
||||
|
||||
for (int yy = Math.min(selectStartY, y); yy <= Math.max(selectStartY, y); yy++) {
|
||||
for (int xx = Math.min(selectStartX, x); xx <= Math.max(selectStartX, x); xx++) {
|
||||
plot(xx, yy, currentFillPattern, hiBitMatters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void drawBrush(int x, int y, int size, int[] pattern, boolean hiBitMatters) {
|
||||
for (int yy = y - size; yy <= y + size; yy++) {
|
||||
for (int xx = x - size; xx <= x + size; xx++) {
|
||||
if (Math.sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y)) > size) {
|
||||
continue;
|
||||
}
|
||||
plot(xx, yy, pattern, hiBitMatters);
|
||||
}
|
||||
redrawScanline(yy);
|
||||
}
|
||||
}
|
||||
|
||||
public void plot(int x, int y, int[] pattern, boolean hiBitMatters) {
|
||||
if (x < 0 || y < 0 || x >= getWidth() * 7 || y >= getHeight()) {
|
||||
return;
|
||||
}
|
||||
int pat = pattern[(y % 16) * 4 + ((x / 7) % 4)];
|
||||
set((pat & (1 << (x % 7))) != 0, x, y);
|
||||
if (hiBitMatters) {
|
||||
setHiBit(pat >= 128, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
public void toggleHiBit(int x, int y) {
|
||||
byte[] data = getImageData();
|
||||
data[y * getWidth() + (x / 7)] ^= 128;
|
||||
setData(data);
|
||||
}
|
||||
|
||||
public void setHiBit(boolean on, int x, int y) {
|
||||
byte[] data = getImageData();
|
||||
if (on) {
|
||||
data[y * getWidth() + (x / 7)] |= 128;
|
||||
} else {
|
||||
data[y * getWidth() + (x / 7)] &= 127;
|
||||
}
|
||||
setData(data);
|
||||
}
|
||||
|
||||
public void toggle(int x, int y) {
|
||||
byte[] data = getImageData();
|
||||
data[y * getWidth() + (x / 7)] ^= (1 << (x % 7));
|
||||
setData(data);
|
||||
}
|
||||
|
||||
public void set(boolean on, int x, int y) {
|
||||
byte[] data = getImageData();
|
||||
data[y * getWidth() + (x / 7)] |= (1 << (x % 7));
|
||||
if (!on) {
|
||||
data[y * getWidth() + (x / 7)] ^= (1 << (x % 7));
|
||||
}
|
||||
setData(data);
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return 40;
|
||||
}
|
||||
public int getHeight() {
|
||||
return 192;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy() {
|
||||
java.util.Map<DataFormat, Object> clip = new HashMap<>();
|
||||
clip.put(DataFormat.IMAGE, currentImage);
|
||||
clip.put(DataFormat.PLAIN_TEXT, "selection/image/" + Application.gameData.getImage().indexOf(getEntity()) + "/" + getSelectionInfo());
|
||||
Clipboard.getSystemClipboard().setContent(clip);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paste() {
|
||||
if (Clipboard.getSystemClipboard().hasContent(DataFormat.PLAIN_TEXT)) {
|
||||
if (pasteAppContent((String) Clipboard.getSystemClipboard().getContent(DataFormat.PLAIN_TEXT))) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
if (Clipboard.getSystemClipboard().hasContent(DataFormat.IMAGE)) {
|
||||
javafx.scene.image.Image image = Clipboard.getSystemClipboard().getImage();
|
||||
importImage(image);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean pasteAppContent(String contentPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void select() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectNone() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
private void importImage(javafx.scene.image.Image image) {
|
||||
FloydSteinbergDither.floydSteinbergDither(image, getPlatform(), 0, 0, getWidth(), getHeight(), new FloydSteinbergDither.DitherCallback() {
|
||||
@Override
|
||||
public void ditherCompleted(byte[] data) {
|
||||
setData(data);
|
||||
redraw();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.badvision.outlaweditor.apple;
|
||||
|
||||
import javafx.scene.image.PixelWriter;
|
||||
import static org.badvision.outlaweditor.apple.AppleNTSCGraphics.*;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import org.badvision.outlaweditor.ImageRenderer;
|
||||
import org.badvision.outlaweditor.Platform;
|
||||
import org.badvision.outlaweditor.data.TileMap;
|
||||
import org.badvision.outlaweditor.data.TileUtils;
|
||||
import org.badvision.outlaweditor.data.xml.Tile;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class AppleImageRenderer extends ImageRenderer {
|
||||
|
||||
public static int BLACK = 0xff000000;
|
||||
public static int WHITE = 0xffffffff;
|
||||
// scanline is 20 16-bit words
|
||||
// If mixed-mode is used then useColor needs to be an 80-boolean array indicating which bytes are supposed to be BW
|
||||
|
||||
@Override
|
||||
public byte[] createImageBuffer() {
|
||||
return new byte[40 * 192];
|
||||
}
|
||||
|
||||
@Override
|
||||
public WritableImage renderPreview(TileMap map, int startX, int startY) {
|
||||
byte[] buffer = createImageBuffer();
|
||||
int pos = 0;
|
||||
for (int y = 0; y < 12; y++) {
|
||||
for (int yy = 0; yy < 16; yy++) {
|
||||
for (int x = 0; x < 20; x++) {
|
||||
Tile t = map.get(x + startX, y + startY);
|
||||
if (t == null) {
|
||||
buffer[pos++] = 0;
|
||||
buffer[pos++] = 0;
|
||||
} else {
|
||||
byte[] tileData = TileUtils.getPlatformData(t, Platform.AppleII);
|
||||
buffer[pos++] = tileData[yy * 2];
|
||||
buffer[pos++] = tileData[yy * 2 + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return renderImage(null, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WritableImage renderImage(WritableImage img, byte[] rawImage) {
|
||||
if (img == null) {
|
||||
img = new WritableImage(560, 384);
|
||||
}
|
||||
for (int y = 0; y < 192; y++) {
|
||||
renderScanline(img, y, rawImage);
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WritableImage renderScanline(WritableImage img, int y, byte[] rawImage) {
|
||||
int[] scanline = new int[20];
|
||||
boolean extraHalfBit = false;
|
||||
for (int x = 0; x < 40; x += 2) {
|
||||
int b1 = rawImage[y * 40 + x] & 255;
|
||||
int b2 = rawImage[y * 40 + x + 1] & 255;
|
||||
int i = hgrToDhgr[(extraHalfBit && x > 0) ? b1 | 0x0100 : b1][b2];
|
||||
extraHalfBit = (i & 0x10000000) != 0;
|
||||
scanline[x / 2] = i & 0xfffffff;
|
||||
}
|
||||
renderScanline(img.getPixelWriter(), y * 2, scanline, true, false);
|
||||
renderScanline(img.getPixelWriter(), y * 2 + 1, scanline, true, false);
|
||||
return img;
|
||||
}
|
||||
|
||||
public static void renderScanline(PixelWriter img, int y, int[] scanline, boolean hiresMode, boolean mixedMode, boolean... useColor) {
|
||||
int[][] activePalette = AppleTileRenderer.useSolidPalette ? solidPalette : textPalette;
|
||||
int byteCounter = 0;
|
||||
int x = 0;
|
||||
for (int s = 0; s < scanline.length; s++) {
|
||||
int add = 0;
|
||||
int bits = 0;
|
||||
if (hiresMode) {
|
||||
bits = scanline[s] << 2;
|
||||
if (s > 0) {
|
||||
bits |= (scanline[s - 1] >> 26) & 3;
|
||||
}
|
||||
} else {
|
||||
bits = scanline[s] << 3;
|
||||
if (s > 0) {
|
||||
bits |= (scanline[s - 1] >> 25) & 7;
|
||||
}
|
||||
}
|
||||
if (s < scanline.length - 1) {
|
||||
add = (scanline[s + 1] & 7);
|
||||
}
|
||||
boolean isBW = false;
|
||||
if (mixedMode) {
|
||||
for (int i = 0; i < 28; i++) {
|
||||
if (i % 7 == 0) {
|
||||
isBW = !hiresMode && !useColor[byteCounter];
|
||||
byteCounter++;
|
||||
}
|
||||
try {
|
||||
if (isBW) {
|
||||
img.setArgb(x++, y, ((bits & 0x8) == 0) ? BLACK : WHITE);
|
||||
} else {
|
||||
img.setArgb(x++, y, activePalette[i % 4][bits & 0x07f]);
|
||||
}
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
// Ignore
|
||||
}
|
||||
bits >>= 1;
|
||||
if (i == 20) {
|
||||
bits |= add << (hiresMode ? 9 : 10);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 28; i++) {
|
||||
try {
|
||||
img.setArgb(x++, y, activePalette[i % 4][bits & 0x07f]);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
// Ignore
|
||||
}
|
||||
bits >>= 1;
|
||||
if (i == 20) {
|
||||
bits |= add << (hiresMode ? 9 : 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package org.badvision.outlaweditor.apple;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class AppleNTSCGraphics {
|
||||
// i Range [-0.5957, 0.5957]
|
||||
public static final double MAX_I = 0.5957;
|
||||
// q Range [-0.5226, 0.5226]
|
||||
public static final double MAX_Q = 0.5226;
|
||||
public static final double MAX_Y = 1;
|
||||
// y Range [0,1]
|
||||
public static final double MIN_Y = 0;
|
||||
|
||||
public static int byteDoubler(byte b) {
|
||||
int num = ((b & 64) << 6) | ((b & 32) << 5) | ((b & 16) << 4) | ((b & 8) << 3) | ((b & 4) << 2) | ((b & 2) << 1) | (b & 1);
|
||||
return num | (num << 1);
|
||||
}
|
||||
|
||||
public static double normalize(double x, double minX, double maxX) {
|
||||
if (x < minX) {
|
||||
return minX;
|
||||
}
|
||||
if (x > maxX) {
|
||||
return maxX;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
public static int yiqToRgb(double y, double i, double q) {
|
||||
int r = (int) (normalize(y + 0.956 * i + 0.621 * q, 0, 1) * 255);
|
||||
int g = (int) (normalize(y - 0.272 * i - 0.647 * q, 0, 1) * 255);
|
||||
int b = (int) (normalize(y - 1.105 * i + 1.702 * q, 0, 1) * 255);
|
||||
return (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
public static int getRed(int col) {
|
||||
return ((col >> 16) & 255);
|
||||
}
|
||||
|
||||
public static int getGreen(int col) {
|
||||
return ((col >> 8) & 255);
|
||||
}
|
||||
|
||||
public static int getBlue(int col) {
|
||||
return (col & 255);
|
||||
}
|
||||
public static int[][] hgrToDhgr;
|
||||
public static int[][] textPalette = new int[4][128];
|
||||
public static int[][] hgrToDhgrBW;
|
||||
public static int[][] solidPalette = new int[4][128];
|
||||
|
||||
public static void initPalettes() {
|
||||
double[][] yiq = {
|
||||
{0.0, 0.0, 0.0},
|
||||
{0.25, 0.5, 0.5},
|
||||
{0.25, -0.5, 0.5},
|
||||
{0.5, 0.0, 1.0},
|
||||
{0.25, -0.5, -0.5},
|
||||
{0.5, 0.0, 0.0},
|
||||
{0.5, -1.0, 0.0},
|
||||
{0.75, -0.5, 0.5},
|
||||
{0.25, 0.5, -0.5},
|
||||
{0.5, 1.0, 0.0},
|
||||
{0.5, 0.0, 0.0},
|
||||
{0.75, 0.5, 0.5},
|
||||
{0.5, 0.0, -1.0},
|
||||
{0.75, 0.5, -0.5},
|
||||
{0.75, -0.5, -0.5},
|
||||
{1.0, 0.0, 0.0}};
|
||||
|
||||
int maxLevel = 10;
|
||||
for (int offset = 0; offset < 4; offset++) {
|
||||
for (int pattern = 0; pattern < 128; pattern++) {
|
||||
int level = (pattern & 1) + ((pattern >> 1) & 1) * 1 + ((pattern >> 2) & 1) * 2 + ((pattern >> 3) & 1) * 4 + ((pattern >> 4) & 1) * 2 + ((pattern >> 5) & 1) * 1;
|
||||
int col = (pattern >> 2) & 15;
|
||||
for (int rot = 0; rot < offset; rot++) {
|
||||
col = ((col & 8) >> 3) | ((col << 1) & 15);
|
||||
}
|
||||
double y1 = yiq[col][0];
|
||||
double y2 = (double) level / (double) maxLevel;
|
||||
solidPalette[offset][pattern] = (255 << 24) | yiqToRgb(y1, yiq[col][1] * MAX_I, yiq[col][2] * MAX_Q);
|
||||
textPalette[offset][pattern] = (255 << 24) | yiqToRgb(y2, yiq[col][1] * MAX_I, yiq[col][2] * MAX_Q);
|
||||
}
|
||||
}
|
||||
hgrToDhgr = new int[512][256];
|
||||
hgrToDhgrBW = new int[256][256];
|
||||
for (int bb1 = 0; bb1 < 512; bb1++) {
|
||||
for (int bb2 = 0; bb2 < 256; bb2++) {
|
||||
int value = ((bb1 & 385) >= 257) ? 1 : 0;
|
||||
int b1 = byteDoubler((byte) (bb1 & 127));
|
||||
if ((bb1 & 128) != 0) {
|
||||
b1 <<= 1;
|
||||
}
|
||||
int b2 = byteDoubler((byte) (bb2 & 127));
|
||||
if ((bb2 & 128) != 0) {
|
||||
b2 <<= 1;
|
||||
}
|
||||
if ((bb1 & 64) == 64 && (bb2 & 1) != 0) {
|
||||
b2 |= 1;
|
||||
}
|
||||
value |= b1 | (b2 << 14);
|
||||
if ((bb2 & 64) != 0) {
|
||||
value |= 268435456;
|
||||
}
|
||||
hgrToDhgr[bb1][bb2] = value;
|
||||
hgrToDhgrBW[bb1 & 255][bb2] = byteDoubler((byte) bb1) | (byteDoubler((byte) bb2) << 14);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
initPalettes();
|
||||
}
|
||||
}
|
@ -0,0 +1,238 @@
|
||||
package org.badvision.outlaweditor.apple;
|
||||
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import javafx.scene.shape.Shape;
|
||||
import org.badvision.outlaweditor.Platform;
|
||||
import org.badvision.outlaweditor.TileEditor;
|
||||
import org.badvision.outlaweditor.data.DataObserver;
|
||||
import org.badvision.outlaweditor.data.xml.Tile;
|
||||
import org.badvision.outlaweditor.data.TileUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class AppleTileEditor extends TileEditor {
|
||||
|
||||
FillPattern currentPattern = FillPattern.DarkViolet1;
|
||||
DrawMode drawMode = DrawMode.Toggle;
|
||||
|
||||
@Override
|
||||
public void setEntity(Tile t) {
|
||||
super.setEntity(t);
|
||||
if (TileUtils.getPlatformData(t, Platform.AppleII) == null) {
|
||||
TileUtils.setPlatformData(t, Platform.AppleII, new byte[32]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildEditorUI(AnchorPane tileEditorAnchorPane) {
|
||||
grid = new Rectangle[14][16];
|
||||
gridGroup = new Group();
|
||||
for (int y = 0; y < 16; y++) {
|
||||
for (int x = 0; x < 14; x++) {
|
||||
final int xx = x;
|
||||
final int yy = y;
|
||||
Rectangle rect = new Rectangle(zoom * x + 5, zoom * y + 5, zoom - 2, zoom - 2);
|
||||
rect.setOnMouseDragged(new EventHandler<MouseEvent>() {
|
||||
@Override
|
||||
public void handle(MouseEvent t) {
|
||||
performDragAction((int) (t.getX() / zoom), (int) (t.getY() / zoom));
|
||||
}
|
||||
});
|
||||
rect.setOnMousePressed(new EventHandler<MouseEvent>() {
|
||||
@Override
|
||||
public void handle(MouseEvent t) {
|
||||
handleMouse(t, xx, yy);
|
||||
}
|
||||
});
|
||||
grid[x][y] = rect;
|
||||
gridGroup.getChildren().add(rect);
|
||||
rect.setStrokeWidth(2);
|
||||
}
|
||||
}
|
||||
|
||||
Group mainGroup = new Group();
|
||||
Rectangle background = new Rectangle(0, 0, zoom * 14 + 10, zoom * 16 + 10);
|
||||
background.setFill(Color.BLACK);
|
||||
mainGroup.getChildren().add(background);
|
||||
mainGroup.getChildren().add(gridGroup);
|
||||
|
||||
tileEditorAnchorPane.getChildren().add(mainGroup);
|
||||
TileUtils.redrawTile(getEntity());
|
||||
observedObjectChanged(getEntity());
|
||||
}
|
||||
|
||||
private void handleMouse(MouseEvent t, int x, int y) {
|
||||
t.consume();
|
||||
if (t.getButton() == null || t.getButton() == MouseButton.NONE) {
|
||||
return;
|
||||
}
|
||||
performAction(t.isShiftDown() || t.isSecondaryButtonDown(), x, y);
|
||||
}
|
||||
int lastActionX = -1;
|
||||
int lastActionY = -1;
|
||||
public void performDragAction(int x, int y) {
|
||||
performAction(false, x, y);
|
||||
}
|
||||
|
||||
private void performAction(boolean alt, int x, int y) {
|
||||
y = Math.min(Math.max(y,0), 15);
|
||||
x = Math.min(Math.max(x,0), 13);
|
||||
if (lastActionX == x && lastActionY == y) {
|
||||
return;
|
||||
}
|
||||
lastActionX = x;
|
||||
lastActionY = y;
|
||||
switch (drawMode) {
|
||||
case Toggle:
|
||||
if (alt) {
|
||||
toggleHiBit(x, y);
|
||||
} else {
|
||||
toggle(x, y);
|
||||
}
|
||||
break;
|
||||
case Pencil1px:
|
||||
int pat = currentPattern.getBytePattern()[y * 4 + (x / 7)];
|
||||
set((pat & (1 << (x % 7))) != 0, x, y);
|
||||
if (currentPattern.hiBitMatters) {
|
||||
setHiBit(pat >= 128, x, y);
|
||||
}
|
||||
break;
|
||||
case Pencil3px:
|
||||
for (int xx = x-1; xx <= x+1; xx++) {
|
||||
if (xx < 0 || xx >= 14) continue;
|
||||
pat = currentPattern.getBytePattern()[y * 4 + (xx / 7)];
|
||||
set((pat & (1 << (xx % 7))) != 0, xx, y);
|
||||
if (currentPattern.hiBitMatters) {
|
||||
setHiBit(pat >= 128, xx, y);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
observedObjectChanged(getEntity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void observedObjectChanged(Tile tile) {
|
||||
recolorGrid(
|
||||
TileUtils.getPlatformData(tile, Platform.AppleII),
|
||||
grid,
|
||||
TileUtils.getImage(tile, Platform.AppleII));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showShiftUI() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDrawMode(DrawMode drawMode) {
|
||||
this.drawMode = drawMode;
|
||||
lastActionX = -1;
|
||||
lastActionY = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildPatternSelector(Menu tilePatternMenu) {
|
||||
FillPattern.buildMenu(tilePatternMenu, new DataObserver<FillPattern>() {
|
||||
@Override
|
||||
public void observedObjectChanged(FillPattern object) {
|
||||
changeCurrentPattern(object);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void changeCurrentPattern(FillPattern pat) {
|
||||
currentPattern = pat;
|
||||
lastActionX = -1;
|
||||
lastActionY = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister() {
|
||||
}
|
||||
int zoom = 25;
|
||||
Group gridGroup;
|
||||
Rectangle[][] grid;
|
||||
|
||||
public void toggleHiBit(int x, int y) {
|
||||
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII);
|
||||
data[y * 2 + (x / 7)] ^= 128;
|
||||
TileUtils.setPlatformData(getEntity(), Platform.AppleII, data);
|
||||
TileUtils.redrawTile(getEntity());
|
||||
}
|
||||
|
||||
public void setHiBit(boolean on, int x, int y) {
|
||||
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII);
|
||||
if (on) {
|
||||
data[y * 2 + (x / 7)] |= 128;
|
||||
} else {
|
||||
data[y * 2 + (x / 7)] &= 127;
|
||||
}
|
||||
TileUtils.setPlatformData(getEntity(), Platform.AppleII, data);
|
||||
TileUtils.redrawTile(getEntity());
|
||||
}
|
||||
|
||||
public void toggle(int x, int y) {
|
||||
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII);
|
||||
data[y * 2 + (x / 7)] ^= (1 << (x % 7));
|
||||
TileUtils.setPlatformData(getEntity(), Platform.AppleII, data);
|
||||
TileUtils.redrawTile(getEntity());
|
||||
}
|
||||
|
||||
public void set(boolean on, int x, int y) {
|
||||
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII);
|
||||
data[y * 2 + (x / 7)] |= (1 << (x % 7));
|
||||
if (!on) {
|
||||
data[y * 2 + (x / 7)] ^= (1 << (x % 7));
|
||||
}
|
||||
TileUtils.setPlatformData(getEntity(), Platform.AppleII, data);
|
||||
TileUtils.redrawTile(getEntity());
|
||||
}
|
||||
|
||||
public void recolorGrid(byte[] spriteData, Shape[][] grid, WritableImage img) {
|
||||
for (int y = 0; y < 16; y++) {
|
||||
for (int x = 0; x < 14; x++) {
|
||||
boolean isHiBit = ((spriteData[y * 2 + x / 7] & 128) != 0);
|
||||
grid[x][y].setFill(img.getPixelReader().getColor(x * 2 + (isHiBit ? 1 : 0), y * 2));
|
||||
if ((spriteData[y * 2 + x / 7] & (1 << (x % 7))) != 0) {
|
||||
grid[x][y].setStroke(Color.ANTIQUEWHITE);
|
||||
} else {
|
||||
grid[x][y].setStroke(
|
||||
isHiBit
|
||||
? (x % 2 == 1) ? Color.CHOCOLATE : Color.CORNFLOWERBLUE
|
||||
: (x % 2 == 1) ? Color.GREEN : Color.VIOLET);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paste() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void select() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectNone() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package org.badvision.outlaweditor.apple;
|
||||
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.badvision.outlaweditor.TileRenderer;
|
||||
import static org.badvision.outlaweditor.apple.AppleNTSCGraphics.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class AppleTileRenderer extends TileRenderer {
|
||||
public static boolean useSolidPalette = true;
|
||||
@Override
|
||||
public WritableImage redrawSprite(byte[] spriteData, WritableImage img) {
|
||||
if (img == null) {
|
||||
img = new WritableImage(28, 32);
|
||||
}
|
||||
if (spriteData == null) return img;
|
||||
int[][] palette = useSolidPalette ? solidPalette : textPalette;
|
||||
for (int y = 0; y < 16; y++) {
|
||||
int bleedOver = (spriteData[y * 2 + 1] & 192)==192 ? 256 : 0;
|
||||
int scan = hgrToDhgr[bleedOver | (spriteData[y * 2] & 255)][spriteData[y * 2 + 1] & 255];
|
||||
int last = (scan >> 26) & 3;
|
||||
int keep = scan & 0xff;
|
||||
scan <<= 2;
|
||||
scan |= last;
|
||||
for (int x = 0; x < 14; x++) {
|
||||
boolean isHiBit = ((spriteData[y * 2 + x / 7] & 128) != 0);
|
||||
|
||||
int col1 = palette[ (x & 1) << 1][scan & 0x07f];
|
||||
Color color1 = Color.rgb(getRed(col1), getGreen(col1), getBlue(col1));
|
||||
scan >>= 1;
|
||||
if (x == 12) {
|
||||
scan = scan & (isHiBit ? 0x07f : 0x01f) | (keep << 5);
|
||||
}
|
||||
int col2 = palette[ ((x & 1) << 1) + 1][scan & 0x07f];
|
||||
Color color2 = Color.rgb(getRed(col2), getGreen(col2), getBlue(col2));
|
||||
scan >>= 1;
|
||||
img.getPixelWriter().setColor(x * 2, y * 2, color1);
|
||||
img.getPixelWriter().setColor(x * 2, y * 2 + 1, color1);
|
||||
img.getPixelWriter().setColor(x * 2 + 1, y * 2, color2);
|
||||
img.getPixelWriter().setColor(x * 2 + 1, y * 2 + 1, color2);
|
||||
}
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return 28;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return 32;
|
||||
}
|
||||
}
|
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.badvision.outlaweditor.apple;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import org.badvision.outlaweditor.Platform;
|
||||
import static org.badvision.outlaweditor.apple.AppleNTSCGraphics.hgrToDhgr;
|
||||
import org.badvision.outlaweditor.data.DataObserver;
|
||||
import org.badvision.outlaweditor.data.TileUtils;
|
||||
import org.badvision.outlaweditor.data.xml.Tile;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public enum FillPattern {
|
||||
Violet(true, 2, false,
|
||||
"+-+-+-+-+-+-+-"),
|
||||
DarkViolet1(true, 4, false,
|
||||
"+---+---+---+---+---+---+---",
|
||||
"--+---+---+---+---+---+---+-"
|
||||
),
|
||||
DarkViolet2(true, 4, false,
|
||||
"--+---+---+---+---+---+---+-",
|
||||
"+---+---+---+---+---+---+---"
|
||||
),
|
||||
LightViolet1(true, 4, false,
|
||||
"+++-+++-+++-+++-+++-+++-+++-",
|
||||
"+-+++-+++-+++-+++-+++-+++-++"
|
||||
),
|
||||
LightViolet2(true, 4, false,
|
||||
"+-+++-+++-+++-+++-+++-+++-++",
|
||||
"+++-+++-+++-+++-+++-+++-+++-"
|
||||
),
|
||||
Green(true, 2, false,
|
||||
"-+-+-+-+-+-+-+"),
|
||||
DarkGreen1(true, 4, false,
|
||||
"-+---+---+---+---+---+---+--",
|
||||
"---+---+---+---+---+---+---+"
|
||||
),
|
||||
DarkGreen2(true, 4, false,
|
||||
"---+---+---+---+---+---+---+",
|
||||
"-+---+---+---+---+---+---+--"
|
||||
),
|
||||
LightGreen1(true, 4, false,
|
||||
"++-+++-+++-+++-+++-+++-+++-+",
|
||||
"-+++-+++-+++-+++-+++-+++-+++"
|
||||
),
|
||||
LightGreen2(true, 4, false,
|
||||
"-+++-+++-+++-+++-+++-+++-+++",
|
||||
"++-+++-+++-+++-+++-+++-+++-+"),
|
||||
Blue(true, 2, true,
|
||||
"+-+-+-+-+-+-+-"),
|
||||
DarkBlue(true, 4, true,
|
||||
"+---+---+---+---+---+---+---",
|
||||
"--+---+---+---+---+---+---+-"
|
||||
),
|
||||
DarkBlue2(true, 4, true,
|
||||
"--+---+---+---+---+---+---+-",
|
||||
"+---+---+---+---+---+---+---"
|
||||
),
|
||||
LightBlue1(true, 4, true,
|
||||
"+++-+++-+++-+++-+++-+++-+++-",
|
||||
"+-+++-+++-+++-+++-+++-+++-++"
|
||||
),
|
||||
LightBlue2(true, 4, true,
|
||||
"+-+++-+++-+++-+++-+++-+++-++",
|
||||
"+++-+++-+++-+++-+++-+++-+++-"
|
||||
),
|
||||
Orange(true, 2, true,
|
||||
"-+-+-+-+-+-+-+"),
|
||||
DarkOrange1(true, 4, true,
|
||||
"-+---+---+---+---+---+---+--",
|
||||
"---+---+---+---+---+---+---+"
|
||||
),
|
||||
DarkOrange(true, 4, true,
|
||||
"---+---+---+---+---+---+---+",
|
||||
"-+---+---+---+---+---+---+--"
|
||||
),
|
||||
LightOrange1(true, 4, true,
|
||||
"++-+++-+++-+++-+++-+++-+++-+",
|
||||
"-+++-+++-+++-+++-+++-+++-+++"
|
||||
),
|
||||
LightOrange2(true, 4, true,
|
||||
"-+++-+++-+++-+++-+++-+++-+++",
|
||||
"++-+++-+++-+++-+++-+++-+++-+"),
|
||||
Black(false, 1, false, "-------"),
|
||||
White(false, 1, false, "+++++++"),
|
||||
BW1(false, 4, false,
|
||||
"++--++--++--++--++--++--++--",
|
||||
"--++--++--++--++--++--++--++"
|
||||
),
|
||||
BW2(false, 4, false,
|
||||
"--++--++--++--++--++--++--++",
|
||||
"++--++--++--++--++--++--++--"
|
||||
);
|
||||
// ,
|
||||
// BW3(false, 4, false,
|
||||
// "+--++--++--++--++--++--++--+",
|
||||
// "-++--++--++--++--++--++--++-"
|
||||
// ),
|
||||
// BW4(false, 4, false,
|
||||
// "-++--++--++--++--++--++--++-",
|
||||
// "+--++--++--++--++--++--++--+"
|
||||
// );
|
||||
|
||||
public static int[] bitmask(boolean hiBit, String pattern) {
|
||||
int[] out = new int[pattern.length()/7];
|
||||
int place = 1;
|
||||
int pos = 0;
|
||||
int value = hiBit ? 128 : 0;
|
||||
for (int i=0; i < pattern.length(); i++) {
|
||||
char c = pattern.charAt(i);
|
||||
if (c == '1' || c == '+') {
|
||||
value |= place;
|
||||
}
|
||||
out[pos] = value;
|
||||
place <<= 1;
|
||||
if (place >= 128) {
|
||||
place = 1;
|
||||
value = hiBit ? 128 : 0;
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public static Integer[] buildPattern(boolean hiBit, String... pattern) {
|
||||
List<Integer> out = new ArrayList<>();
|
||||
for (String s : pattern) {
|
||||
for (int i : bitmask(hiBit, s)) {
|
||||
out.add(i);
|
||||
}
|
||||
}
|
||||
return out.toArray(new Integer[0]);
|
||||
}
|
||||
|
||||
public static void buildMenu(Menu target, final DataObserver<FillPattern> dataObserver) {
|
||||
target.getItems().clear();
|
||||
for (final FillPattern fill : FillPattern.values()) {
|
||||
MenuItem i = new MenuItem(fill.name(), new ImageView(fill.getPreview()));
|
||||
i.setOnAction(new EventHandler<ActionEvent>() {
|
||||
@Override
|
||||
public void handle(ActionEvent t) {
|
||||
dataObserver.observedObjectChanged(fill);
|
||||
}
|
||||
});
|
||||
target.getItems().add(i);
|
||||
}
|
||||
}
|
||||
Integer[] pattern;
|
||||
int[] bytePattern;
|
||||
int width;
|
||||
boolean hiBitMatters;
|
||||
WritableImage preview;
|
||||
|
||||
private FillPattern(boolean hiBitMatters, int width, boolean hiBit, String... pattern) {
|
||||
this.pattern = buildPattern(hiBit, pattern);
|
||||
this.width = width;
|
||||
this.hiBitMatters = hiBitMatters;
|
||||
this.bytePattern = getBytePattern();
|
||||
}
|
||||
|
||||
public WritableImage getPreview() {
|
||||
if (preview == null) {
|
||||
preview = renderPreview(null, bytePattern);
|
||||
}
|
||||
return preview;
|
||||
}
|
||||
|
||||
public int[] getBytePattern() {
|
||||
int[] out = new int[16 * 4];
|
||||
int pos = 0;
|
||||
int patternHeight = pattern.length / width;
|
||||
for (int y = 0; y < 16; y++) {
|
||||
int yOffset = (y % patternHeight) * width;
|
||||
for (int x = 0; x < 4; x++) {
|
||||
out[pos++] = pattern[yOffset + (x % width)];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public static int[] interleave(int[] pat1, int[] pat2) {
|
||||
int[] out = Arrays.copyOf(pat1, pat1.length);
|
||||
for (int y = 1; y < 16; y += 2) {
|
||||
int offset = y * 4;
|
||||
for (int x = 0; x < 4; x++) {
|
||||
out[offset + x] = pat2[offset + x];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public static int[] fromTile(Tile t) {
|
||||
byte[] raw = TileUtils.getPlatformData(t, Platform.AppleII);
|
||||
int[] out = new int[16 * 4];
|
||||
int pos = 0;
|
||||
for (int y = 0; y < 16; y++) {
|
||||
for (int c = 0; c < 2; c++) {
|
||||
for (int x = 0; x < 2; x++) {
|
||||
out[pos++] = raw[y * 2 + x] & 0x0ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public static WritableImage renderPreview(WritableImage img, int[] pattern) {
|
||||
if (img == null) {
|
||||
img = new WritableImage(28, 32);
|
||||
}
|
||||
int[] scan = new int[2];
|
||||
for (int y = 0; y < 16; y++) {
|
||||
int b1 = pattern[y * 4] & 255;
|
||||
int b2 = pattern[y * 4 + 1] & 255;
|
||||
int i = hgrToDhgr[b1][b2];
|
||||
boolean extraHalfBit = (i & 0x10000000) != 0;
|
||||
scan[0] = i & 0xfffffff;
|
||||
b1 = pattern[y * 4 + 2] & 255;
|
||||
b2 = pattern[y * 4 + 3] & 255;
|
||||
i = hgrToDhgr[(extraHalfBit) ? b1 | 0x0100 : b1][b2];
|
||||
scan[1] = i & 0xfffffff;
|
||||
AppleImageRenderer.renderScanline(img.getPixelWriter(), y * 2, scan, true, false);
|
||||
AppleImageRenderer.renderScanline(img.getPixelWriter(), y * 2 + 1, scan, true, false);
|
||||
}
|
||||
return img;
|
||||
}
|
||||
}
|
@ -0,0 +1,479 @@
|
||||
package org.badvision.outlaweditor.apple;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.SnapshotParameters;
|
||||
import javafx.scene.canvas.Canvas;
|
||||
import javafx.scene.effect.DropShadow;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.image.PixelFormat;
|
||||
import javafx.scene.image.PixelReader;
|
||||
import javafx.scene.image.PixelWriter;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.stage.Stage;
|
||||
import static org.badvision.outlaweditor.apple.AppleNTSCGraphics.hgrToDhgr;
|
||||
|
||||
/* Copyright (c) 2013 the authors listed at the following URL, and/or
|
||||
the authors of referenced articles or incorporated external code:
|
||||
http://en.literateprograms.org/Floyd-Steinberg_dithering_(Java)?action=history&offset=20080201121723
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Retrieved from: http://en.literateprograms.org/Floyd-Steinberg_dithering_(Java)?oldid=12476
|
||||
* Original code by Spoon! (Feb 2008)
|
||||
* Modified and adapted to work with Apple Game Server by Brendan Robert (2013)
|
||||
* Some of the original code of this class was migrated over to the Palette class which already manages colors in AGS.
|
||||
*/
|
||||
public class FloydSteinbergDither {
|
||||
|
||||
static final int totalPasses = 6;
|
||||
static final int nonErrorPasses = 1;
|
||||
|
||||
public static interface DitherCallback {
|
||||
|
||||
public void ditherCompleted(byte[] data);
|
||||
}
|
||||
|
||||
public static void floydSteinbergDither(
|
||||
Image img,
|
||||
final org.badvision.outlaweditor.Platform platform,
|
||||
final int startX,
|
||||
final int startY,
|
||||
final int width,
|
||||
final int height,
|
||||
final DitherCallback callback) {
|
||||
final AppleImageRenderer renderer = (AppleImageRenderer) platform.imageRenderer;
|
||||
final int errorWindow = 6;
|
||||
final int overlap = 2;
|
||||
final int pixelShift = -2;
|
||||
final WritableImage source = getScaledImage(img, 560, 192);
|
||||
AnchorPane pane = new AnchorPane();
|
||||
Scene s = new Scene(pane);
|
||||
final ImageView previewImage = new ImageView(source);
|
||||
previewImage.setLayoutX(0);
|
||||
previewImage.setLayoutY(0);
|
||||
pane.getChildren().add(previewImage);
|
||||
final Text status = new Text("Status");
|
||||
status.setLayoutX(100);
|
||||
status.setLayoutY(40);
|
||||
status.setFont(Font.font("Arial", 18));
|
||||
status.setStroke(Color.BLACK);
|
||||
status.setEffect(new DropShadow(10.0, 0, 0, Color.WHITE));
|
||||
pane.getChildren().add(status);
|
||||
Stage progress = new Stage();
|
||||
progress.setScene(s);
|
||||
progress.show();
|
||||
|
||||
Thread t = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final WritableImage keepScaled = new WritableImage(source.getPixelReader(), 560, 192);
|
||||
WritableImage tmpScaled = new WritableImage(source.getPixelReader(), 560, 192);
|
||||
final byte[] screen = renderer.createImageBuffer();
|
||||
for (int i = 0; i < screen.length; i++) {
|
||||
screen[i] = (byte) Math.max(255, Math.random() * 256.0);
|
||||
}
|
||||
// Platform.runLater(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// previewImage.setCache(false);
|
||||
// previewImage.setImage(keepScaled);
|
||||
// }
|
||||
// });
|
||||
// try {
|
||||
// while (previewImage.getImage() != keepScaled) {
|
||||
// Thread.sleep(10);
|
||||
// }
|
||||
// } catch (InterruptedException ex) {
|
||||
// Logger.getLogger(FloydSteinbergDither.class.getName()).log(Level.SEVERE, null, ex);
|
||||
// }
|
||||
int[] scanline = new int[3];
|
||||
List<Integer> pixels = new ArrayList<>();
|
||||
for (int pass = 0; pass < totalPasses; pass++) {
|
||||
keepScaled.getPixelWriter().setPixels(0, 0, 560, 192, source.getPixelReader(), 0, 0);
|
||||
tmpScaled.getPixelWriter().setPixels(0, 0, 560, 192, source.getPixelReader(), 0, 0);
|
||||
final String statusText = "Pass: " + (pass + 1) + " of " + totalPasses;
|
||||
Platform.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
status.setText(statusText);
|
||||
}
|
||||
});
|
||||
System.out.println("Image type: " + platform.name());
|
||||
for (int y = startY; y < height + startY; y++) {
|
||||
for (int x = startX; x < startX + width; x += 2) {
|
||||
Thread.yield();
|
||||
switch (platform) {
|
||||
case AppleII:
|
||||
hiresDither(screen, y, x, scanline, pixels, tmpScaled, pass, keepScaled);
|
||||
break;
|
||||
case AppleII_DHGR:
|
||||
doubleHiresDither(screen, y, x, scanline, pixels, tmpScaled, pass, keepScaled);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Platform.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callback.ditherCompleted(screen);
|
||||
}
|
||||
});
|
||||
}
|
||||
status.setText("Complete!");
|
||||
}
|
||||
|
||||
void hiresDither(final byte[] screen, int y, int x, int[] scanline, List<Integer> pixels, WritableImage tmpScaled, int pass, final WritableImage keepScaled) {
|
||||
int bb1 = screen[y * 40 + x] & 255;
|
||||
int bb2 = screen[y * 40 + x + 1] & 255;
|
||||
int next = bb2 & 127; // Preserve hi-bit so last pixel stays solid, it is a very minor detail
|
||||
int prev = 0;
|
||||
if (x > 0) {
|
||||
prev = screen[y * 40 + x - 1] & 255;
|
||||
}
|
||||
if (x < 38) {
|
||||
next = screen[y * 40 + x + 2] & 255;
|
||||
}
|
||||
// First byte, compared with a sliding window encompassing the previous byte, if any.
|
||||
int leastError = Integer.MAX_VALUE;
|
||||
for (int hi = 0; hi < 2; hi++) {
|
||||
int b1 = (hi << 7) | (bb1 & 0x07f);
|
||||
int totalError = 0;
|
||||
for (int c = 0; c < 7; c++) {
|
||||
// for (int c = 6; c >= 0; c--) {
|
||||
int on = b1 | (1 << c);
|
||||
int off = on ^ (1 << c);
|
||||
// get values for "off"
|
||||
int i = hgrToDhgr[0][prev];
|
||||
scanline[0] = i & 0x0fffffff;
|
||||
i = hgrToDhgr[(i & 0x10000000) != 0 ? off | 0x0100 : off][bb2];
|
||||
scanline[1] = i & 0x0fffffff;
|
||||
// scanline[2] = hgrToDhgr[(i & 0x10000000) != 0 ? next | 0x0100 : next][0] & 0x0fffffff;
|
||||
int errorOff = getError(x * 14 - overlap + c * 2, y, 28 + c * 2 - overlap + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
|
||||
int off1 = pixels.get(c * 2 + 28 + pixelShift);
|
||||
int off2 = pixels.get(c * 2 + 29 + pixelShift);
|
||||
// get values for "on"
|
||||
i = hgrToDhgr[0][prev];
|
||||
scanline[0] = i & 0x0fffffff;
|
||||
i = hgrToDhgr[(i & 0x10000000) != 0 ? on | 0x0100 : on][bb2];
|
||||
scanline[1] = i & 0x0fffffff;
|
||||
// scanline[2] = hgrToDhgr[(i & 0x10000000) != 0 ? next | 0x0100 : next][0] & 0x0fffffff;
|
||||
int errorOn = getError(x * 14 - overlap + c * 2, y, 28 + c * 2 - overlap + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
|
||||
int on1 = pixels.get(c * 2 + 28 + pixelShift);
|
||||
int on2 = pixels.get(c * 2 + 29 + pixelShift);
|
||||
int[] col1;
|
||||
int[] col2;
|
||||
if (errorOff < errorOn) {
|
||||
totalError += errorOff;
|
||||
b1 = off;
|
||||
col1 = Palette.parseIntColor(off1);
|
||||
col2 = Palette.parseIntColor(off2);
|
||||
} else {
|
||||
totalError += errorOn;
|
||||
b1 = on;
|
||||
col1 = Palette.parseIntColor(on1);
|
||||
col2 = Palette.parseIntColor(on2);
|
||||
}
|
||||
if (pass >= nonErrorPasses) {
|
||||
propagateError(x * 14 + c * 2, y, tmpScaled, col1, true, false);
|
||||
propagateError(x * 14 + c * 2 + 1, y, tmpScaled, col2, false, true);
|
||||
}
|
||||
}
|
||||
if (totalError < leastError) {
|
||||
keepScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, tmpScaled.getPixelReader(), 0, y);
|
||||
leastError = totalError;
|
||||
bb1 = b1;
|
||||
} else {
|
||||
tmpScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, keepScaled.getPixelReader(), 0, y);
|
||||
}
|
||||
}
|
||||
// Second byte, compared with a sliding window encompassing the next byte, if any.
|
||||
leastError = Integer.MAX_VALUE;
|
||||
for (int hi = 0; hi < 2; hi++) {
|
||||
int b2 = (hi << 7) | (bb2 & 0x07f);
|
||||
int totalError = 0;
|
||||
for (int c = 0; c < 7; c++) {
|
||||
// for (int c = 6; c >= 0; c--) {
|
||||
int on = b2 | (1 << c);
|
||||
int off = on ^ (1 << c);
|
||||
// get values for "off"
|
||||
int i = hgrToDhgr[bb1][off];
|
||||
scanline[0] = i & 0xfffffff;
|
||||
scanline[1] = hgrToDhgr[(i & 0x10000000) != 0 ? next | 0x0100 : next][0];
|
||||
int errorOff = getError(x * 14 + 14 - overlap + c * 2, y, 14 - overlap + c * 2 + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
|
||||
int off1 = pixels.get(c * 2 + 14 + pixelShift);
|
||||
int off2 = pixels.get(c * 2 + 15 + pixelShift);
|
||||
// get values for "on"
|
||||
i = hgrToDhgr[bb1][on];
|
||||
scanline[0] = i & 0xfffffff;
|
||||
scanline[1] = hgrToDhgr[(i & 0x10000000) != 0 ? next | 0x0100 : next][0];
|
||||
int errorOn = getError(x * 14 + 14 - overlap + c * 2, y, 14 - overlap + c * 2 + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
|
||||
int on1 = pixels.get(c * 2 + 14 + pixelShift);
|
||||
int on2 = pixels.get(c * 2 + 15 + pixelShift);
|
||||
int[] col1;
|
||||
int[] col2;
|
||||
if (errorOff < errorOn) {
|
||||
totalError += errorOff;
|
||||
b2 = off;
|
||||
col1 = Palette.parseIntColor(off1);
|
||||
col2 = Palette.parseIntColor(off2);
|
||||
} else {
|
||||
totalError += errorOn;
|
||||
b2 = on;
|
||||
col1 = Palette.parseIntColor(on1);
|
||||
col2 = Palette.parseIntColor(on2);
|
||||
}
|
||||
if (pass >= nonErrorPasses) {
|
||||
propagateError(x * 14 + c * 2 + 14, y, tmpScaled, col1, true, false);
|
||||
propagateError(x * 14 + c * 2 + 15, y, tmpScaled, col2, false, true);
|
||||
}
|
||||
}
|
||||
if (totalError < leastError) {
|
||||
keepScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, tmpScaled.getPixelReader(), 0, y);
|
||||
leastError = totalError;
|
||||
bb2 = b2;
|
||||
} else {
|
||||
tmpScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, keepScaled.getPixelReader(), 0, y);
|
||||
}
|
||||
}
|
||||
screen[y * 40 + x] = (byte) bb1;
|
||||
screen[y * 40 + x + 1] = (byte) bb2;
|
||||
}
|
||||
|
||||
void doubleHiresDither(final byte[] screen, int y, int x, int[] scanline, List<Integer> pixels, WritableImage tmpScaled, int pass, final WritableImage keepScaled) {
|
||||
if (x % 4 != 0) {
|
||||
return;
|
||||
}
|
||||
scanline[0] = 0;
|
||||
if (x >= 4) {
|
||||
scanline[0] = screen[y * 80 + x - 1] << 21;
|
||||
}
|
||||
scanline[1] = 0;
|
||||
if (x < 76) {
|
||||
scanline[2] = screen[y * 80 + x + 4];
|
||||
}
|
||||
int bytes[] = new int[]{
|
||||
screen[y * 80 + x] & 255,
|
||||
screen[y * 80 + x + 1] & 255,
|
||||
screen[y * 80 + x + 2] & 255,
|
||||
screen[y * 80 + x + 3] & 255
|
||||
};
|
||||
|
||||
for (int xx = 0; xx < 4; xx++) {
|
||||
// First byte, compared with a sliding window encompassing the previous byte, if any.
|
||||
int leastError = Integer.MAX_VALUE;
|
||||
int b1 = (bytes[xx] & 0x07f);
|
||||
for (int c = 0; c < 7; c++) {
|
||||
int on = b1 | (1 << c);
|
||||
int off = on ^ (1 << c);
|
||||
// get values for "off"
|
||||
int i = (xx == 3) ? off : bytes[3] & 255;
|
||||
i <<= 7;
|
||||
i |= (xx == 2) ? off : bytes[2] & 255;
|
||||
i <<= 7;
|
||||
i |= (xx == 1) ? off : bytes[1] & 255;
|
||||
i <<= 7;
|
||||
i |= (xx == 0) ? off : bytes[0] & 255;
|
||||
scanline[1] = i;
|
||||
int errorOff = getError((x + xx) * 7 - overlap + c, y, 28 + (xx * 7) + c - overlap + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
|
||||
int off1 = pixels.get(xx * 7 + c + 28 + pixelShift);
|
||||
// get values for "on"
|
||||
i = (xx == 3) ? on : bytes[3] & 255;
|
||||
i <<= 7;
|
||||
i |= (xx == 2) ? on : bytes[2] & 255;
|
||||
i <<= 7;
|
||||
i |= (xx == 1) ? on : bytes[1] & 255;
|
||||
i <<= 7;
|
||||
i |= (xx == 0) ? on : bytes[0] & 255;
|
||||
scanline[1] = i;
|
||||
int errorOn = getError((x + xx) * 7 - overlap + c, y, 28 + (xx * 7) + c - overlap + pixelShift, errorWindow, pixels, tmpScaled.getPixelReader(), scanline);
|
||||
int on1 = pixels.get(xx * 7 + c + 28 + pixelShift);
|
||||
|
||||
int[] col1;
|
||||
if (errorOff < errorOn) {
|
||||
// totalError += errorOff;
|
||||
b1 = off;
|
||||
col1 = Palette.parseIntColor(off1);
|
||||
} else {
|
||||
// totalError += errorOn;
|
||||
b1 = on;
|
||||
col1 = Palette.parseIntColor(on1);
|
||||
}
|
||||
if (pass >= nonErrorPasses) {
|
||||
propagateError((x + xx) * 7 + c, y, tmpScaled, col1, false, false);
|
||||
}
|
||||
}
|
||||
// if (totalError < leastError) {
|
||||
keepScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, tmpScaled.getPixelReader(), 0, y);
|
||||
// leastError = totalError;
|
||||
bytes[xx] = b1;
|
||||
// } else {
|
||||
// tmpScaled.getPixelWriter().setPixels(0, y, 560, (y < 191) ? 2 : 1, keepScaled.getPixelReader(), 0, y);
|
||||
}
|
||||
screen[y * 80 + x] = (byte) bytes[0];
|
||||
screen[y * 80 + x + 1] = (byte) bytes[1];
|
||||
screen[y * 80 + x + 2] = (byte) bytes[2];
|
||||
screen[y * 80 + x + 3] = (byte) bytes[3];
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
// progress.close();
|
||||
}
|
||||
|
||||
private static void propagateError(int x, int y, WritableImage img, int[] newColor, boolean propagateLeft, boolean propagateRight) {
|
||||
int solid = 255 << 24;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (x + 1 < img.getWidth() && propagateRight) {
|
||||
int error = Palette.getComponent(img.getPixelReader().getArgb(x + 1, y), i) - newColor[i];
|
||||
int c = img.getPixelReader().getArgb(x + 1, y);
|
||||
img.getPixelWriter().setArgb(x + 1, y, solid | Palette.addError(c, i, (error * 7) >> 4));
|
||||
}
|
||||
if (y + 1 < img.getHeight()) {
|
||||
int error = Palette.getComponent(img.getPixelReader().getArgb(x, y), i) - newColor[i];
|
||||
if (x - 1 > 0 && propagateLeft) {
|
||||
int c = img.getPixelReader().getArgb(x - 1, y + 1);
|
||||
img.getPixelWriter().setArgb(x - 1, y + 1, solid | Palette.addError(c, i, (error * 3) >> 4));
|
||||
}
|
||||
int c = img.getPixelReader().getArgb(x, y + 1);
|
||||
img.getPixelWriter().setArgb(x, y + 1, solid | Palette.addError(c, i, (error * 5) >> 4));
|
||||
if (x + 1 < img.getWidth() && propagateRight) {
|
||||
c = img.getPixelReader().getArgb(x + 1, y + 1);
|
||||
img.getPixelWriter().setArgb(x + 1, y + 1, solid | Palette.addError(c, i, error >> 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getError(int imageXStart, int y, int scanlineXStart, int window, final List<Integer> pixels, PixelReader source, int[] scanline) {
|
||||
pixels.clear();
|
||||
PixelWriter fakeWriter = new PixelWriter() {
|
||||
@Override
|
||||
public PixelFormat getPixelFormat() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArgb(int x, int y, int c) {
|
||||
pixels.add(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColor(int i, int i1, Color color) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Buffer> void setPixels(int i, int i1, int i2, int i3, PixelFormat<T> pf, T t, int i4) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPixels(int i, int i1, int i2, int i3, PixelFormat<ByteBuffer> pf, byte[] bytes, int i4, int i5) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPixels(int i, int i1, int i2, int i3, PixelFormat<IntBuffer> pf, int[] ints, int i4, int i5) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPixels(int i, int i1, int i2, int i3, PixelReader reader, int i4, int i5) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
};
|
||||
AppleImageRenderer.renderScanline(fakeWriter, 0, scanline, true, false);
|
||||
double max = 0;
|
||||
double min = Double.MAX_VALUE;
|
||||
double total = 0;
|
||||
List<Double> err = new ArrayList<>();
|
||||
for (int p = 0; p < window; p++) {
|
||||
if ((imageXStart + p) < 0 || (imageXStart + p) >= 560) {
|
||||
continue;
|
||||
}
|
||||
int[] c1 = Palette.parseIntColor(pixels.get(scanlineXStart + p));
|
||||
int[] c2 = Palette.parseIntColor(source.getArgb(imageXStart + p, y));
|
||||
double dist = Palette.distance(c1, c2);
|
||||
total += dist;
|
||||
max = Math.max(dist, max);
|
||||
min = Math.min(dist, min);
|
||||
err.add(dist);
|
||||
}
|
||||
// double avg = total/((double) window);
|
||||
// double range = max-min;
|
||||
// double totalDev = 0.0;
|
||||
// for (Double d : err) {
|
||||
// totalDev = Math.pow(d-avg, 2);
|
||||
//// errorTotal += (d-min)/range;
|
||||
// }
|
||||
// totalDev /= ((double) window);
|
||||
// double stdDev = Math.sqrt(totalDev);
|
||||
// return (int) (min+(avg*(stdDev/range)));
|
||||
return (int) total;
|
||||
}
|
||||
|
||||
// int currentPixel = source.getRGB(x, y);
|
||||
//
|
||||
// for (int i = 0; i < 3; i++) {
|
||||
// int error = Palette.getComponent(currentPixel, i) - Palette.getComponent(closestColor, i);
|
||||
// if (x + 1 < source.getWidth()) {
|
||||
// int c = source.getRGB(x + 1, y);
|
||||
// source.setRGB(x + 1, y, Palette.addError(c, i, (error * 7) >> 4));
|
||||
// }
|
||||
// if (y + 1 < source.getHeight()) {
|
||||
// if (x - 1 > 0) {
|
||||
// int c = source.getRGB(x - 1, y + 1);
|
||||
// source.setRGB(x - 1, y + 1, Palette.addError(c, i, (error * 3) >> 4));
|
||||
// }
|
||||
// {
|
||||
// int c = source.getRGB(x, y + 1);
|
||||
// source.setRGB(x, y + 1, Palette.addError(c, i, (error * 5) >> 4));
|
||||
// }
|
||||
// if (x + 1 < source.getWidth()) {
|
||||
// int c = source.getRGB(x + 1, y + 1);
|
||||
// source.setRGB(x + 1, y + 1, Palette.addError(c, i, error >> 4));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return dest;
|
||||
// }
|
||||
private static WritableImage getScaledImage(Image img, int width, int height) {
|
||||
Canvas c = new Canvas(width, height);
|
||||
c.getGraphicsContext2D().drawImage(img, 0, 0, width, height);
|
||||
WritableImage newImg = new WritableImage(width, height);
|
||||
SnapshotParameters sp = new SnapshotParameters();
|
||||
c.snapshot(sp, newImg);
|
||||
return newImg;
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package org.badvision.outlaweditor.apple;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Palette {
|
||||
public int MATCH_TOLERANCE = 0;
|
||||
List colors = null;
|
||||
public static int COLOR_DISTANCE_MAX = 0x2f708fd;
|
||||
|
||||
public Palette() {
|
||||
MATCH_TOLERANCE = 64;
|
||||
colors = new ArrayList();
|
||||
initPalette();
|
||||
}
|
||||
|
||||
protected abstract void initPalette();
|
||||
|
||||
public int[] getColor(int col) {
|
||||
return (int[])colors.get(col);
|
||||
}
|
||||
|
||||
public int getColorInt(int c) {
|
||||
int col[] = getColor(c);
|
||||
return toRGBInt(col);
|
||||
}
|
||||
|
||||
public void addColor(int col[]) {
|
||||
/* 45*/ colors.add(col);
|
||||
}
|
||||
|
||||
public void addColor(int r, int g, int b) {
|
||||
int col[] = new int[3];
|
||||
col[0] = r;
|
||||
col[1] = g;
|
||||
col[2] = b;
|
||||
addColor(col);
|
||||
}
|
||||
|
||||
public int findColor(int color) {
|
||||
int col[] = parseIntColor(color);
|
||||
return findColor(col);
|
||||
}
|
||||
|
||||
public static int[] parseIntColor(int color) {
|
||||
return new int[] {getR(color), getG(color), getB(color)};
|
||||
}
|
||||
public static int toRGBInt(int[] col) {
|
||||
return 0x10000 * col[0] + 256 * col[1] + col[2];
|
||||
}
|
||||
|
||||
public int findColor(int color[]) {
|
||||
int lastDiff = COLOR_DISTANCE_MAX;
|
||||
int bestFit = 0;
|
||||
for(int i = 0; i < colors.size(); i++) {
|
||||
int test[] = (int[])colors.get(i);
|
||||
int diff = (int)distance(color, test);
|
||||
if(diff < lastDiff) {
|
||||
lastDiff = diff;
|
||||
bestFit = i;
|
||||
}
|
||||
}
|
||||
|
||||
return bestFit;
|
||||
}
|
||||
|
||||
public static double distance(int color[], int test[]) {
|
||||
return Math.pow(Math.abs(color[0] - test[0]), 3D) + Math.pow(Math.abs(color[1] - test[1]), 3D) + Math.pow(Math.abs(color[2] - test[2]), 3D);
|
||||
}
|
||||
|
||||
public static int getR(int color) {
|
||||
return ((color >> 16) & 255);
|
||||
}
|
||||
public static int getG(int color) {
|
||||
return ((color >> 8) & 255);
|
||||
}
|
||||
public static int getB(int color) {
|
||||
return color & 255;
|
||||
}
|
||||
public static int getComponent(int color, int component) {
|
||||
switch (component) {
|
||||
case 0: return getR(color);
|
||||
case 1: return getG(color);
|
||||
case 2: return getB(color);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static int addError(int color, int component, int error) {
|
||||
int[] sourceColor = parseIntColor(color);
|
||||
sourceColor[component] = Math.max(0, Math.min(255, sourceColor[component] + error));
|
||||
return toRGBInt(sourceColor);
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.badvision.outlaweditor.apple.dhgr;
|
||||
|
||||
import org.badvision.outlaweditor.apple.*;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import org.badvision.outlaweditor.Platform;
|
||||
import org.badvision.outlaweditor.data.DataObserver;
|
||||
import org.badvision.outlaweditor.data.xml.PlatformData;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class AppleDHGRImageEditor extends AppleImageEditor implements EventHandler<MouseEvent> {
|
||||
|
||||
public AppleDHGRImageEditor() {
|
||||
xScale = 1;
|
||||
yScale = 2;
|
||||
changeCurrentPattern(FillPattern.Magenta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Platform getPlatform() {
|
||||
return Platform.AppleII_DHGR;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void buildPatternSelector(Menu tilePatternMenu) {
|
||||
FillPattern.buildMenu(tilePatternMenu, new DataObserver<FillPattern>() {
|
||||
@Override
|
||||
public void observedObjectChanged(FillPattern object) {
|
||||
changeCurrentPattern(object);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void changeCurrentPattern(FillPattern pattern) {
|
||||
currentFillPattern = pattern.getBytePattern();
|
||||
hiBitMatters = pattern.hiBitMatters;
|
||||
lastActionX = -1;
|
||||
lastActionY = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redrawScanline(int y) {
|
||||
currentImage = Platform.AppleII_DHGR.imageRenderer.renderScanline(currentImage, y, getImageData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return 80;
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.badvision.outlaweditor.apple.dhgr;
|
||||
|
||||
import org.badvision.outlaweditor.apple.*;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import org.badvision.outlaweditor.Platform;
|
||||
import org.badvision.outlaweditor.data.TileMap;
|
||||
import org.badvision.outlaweditor.data.TileUtils;
|
||||
import org.badvision.outlaweditor.data.xml.Tile;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class AppleDHGRImageRenderer extends AppleImageRenderer {
|
||||
|
||||
public static int BLACK = 0xff000000;
|
||||
public static int WHITE = 0xffffffff;
|
||||
// scanline is 20 16-bit words
|
||||
// If mixed-mode is used then useColor needs to be an 80-boolean array indicating which bytes are supposed to be BW
|
||||
|
||||
@Override
|
||||
public byte[] createImageBuffer() {
|
||||
return new byte[80 * 192];
|
||||
}
|
||||
|
||||
@Override
|
||||
public WritableImage renderPreview(TileMap map, int startX, int startY) {
|
||||
byte[] buffer = createImageBuffer();
|
||||
int pos = 0;
|
||||
for (int y = 0; y < 12; y++) {
|
||||
for (int yy = 0; yy < 16; yy++) {
|
||||
for (int x = 0; x < 20; x++) {
|
||||
Tile t = map.get(x + startX, y + startY);
|
||||
if (t == null) {
|
||||
buffer[pos++] = 0;
|
||||
buffer[pos++] = 0;
|
||||
buffer[pos++] = 0;
|
||||
buffer[pos++] = 0;
|
||||
} else {
|
||||
byte[] tileData = TileUtils.getPlatformData(t, Platform.AppleII_DHGR);
|
||||
buffer[pos++] = tileData[yy * 2];
|
||||
buffer[pos++] = tileData[yy * 2 + 1];
|
||||
buffer[pos++] = tileData[yy * 2 + 2];
|
||||
buffer[pos++] = tileData[yy * 2 + 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return renderImage(null, buffer);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public WritableImage renderImage(WritableImage img, byte[] rawImage) {
|
||||
// if (img == null) {
|
||||
// img = new WritableImage(560, 384);
|
||||
// }
|
||||
// for (int y = 0; y < 192; y++) {
|
||||
// renderScanline(img, y, rawImage);
|
||||
// }
|
||||
// return img;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public WritableImage renderScanline(WritableImage img, int y, byte[] rawImage) {
|
||||
if (y < 0) return img;
|
||||
int[] scanline = new int[20];
|
||||
for (int x = 0; x < 80; x += 4) {
|
||||
int scan = rawImage[y * 80 + x + 3] & 255;
|
||||
scan <<=7;
|
||||
scan |= rawImage[y * 80 + x + 2] & 255;
|
||||
scan <<=7;
|
||||
scan |= rawImage[y * 80 + x + 1] & 255;
|
||||
scan <<=7;
|
||||
scan |= rawImage[y * 80 + x] & 255;
|
||||
scanline[x / 4] = scan;
|
||||
}
|
||||
renderScanline(img.getPixelWriter(), y * 2, scanline, true, false);
|
||||
renderScanline(img.getPixelWriter(), y * 2 + 1, scanline, true, false);
|
||||
return img;
|
||||
}
|
||||
//
|
||||
// public static void renderScanline(PixelWriter img, int y, int[] scanline, boolean hiresMode, boolean mixedMode, boolean... useColor) {
|
||||
// AppleImageRenderer.renderScanline(img, y, scanline, hiresMode, mixedMode, useColor);
|
||||
// }
|
||||
}
|
@ -0,0 +1,248 @@
|
||||
package org.badvision.outlaweditor.apple.dhgr;
|
||||
|
||||
import org.badvision.outlaweditor.apple.*;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import javafx.scene.shape.Shape;
|
||||
import org.badvision.outlaweditor.Platform;
|
||||
import org.badvision.outlaweditor.TileEditor;
|
||||
import org.badvision.outlaweditor.data.DataObserver;
|
||||
import org.badvision.outlaweditor.data.xml.Tile;
|
||||
import org.badvision.outlaweditor.data.TileUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class AppleDHGRTileEditor extends TileEditor {
|
||||
|
||||
FillPattern currentPattern = FillPattern.Magenta;
|
||||
DrawMode drawMode = DrawMode.Toggle;
|
||||
|
||||
@Override
|
||||
public void setEntity(Tile t) {
|
||||
super.setEntity(t);
|
||||
if (TileUtils.getPlatformData(t, Platform.AppleII_DHGR) == null) {
|
||||
TileUtils.setPlatformData(t, Platform.AppleII_DHGR, new byte[64]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildEditorUI(AnchorPane tileEditorAnchorPane) {
|
||||
grid = new Rectangle[28][16];
|
||||
gridGroup = new Group();
|
||||
for (int y = 0; y < 16; y++) {
|
||||
for (int x = 0; x < 28; x++) {
|
||||
final int xx = x;
|
||||
final int yy = y;
|
||||
Rectangle rect = new Rectangle((zoom/2) * x + 5, zoom * y + 5, zoom/2 - 2, zoom - 2);
|
||||
rect.setOnMouseDragged(new EventHandler<MouseEvent>() {
|
||||
@Override
|
||||
public void handle(MouseEvent t) {
|
||||
performDragAction((int) (t.getX() / (zoom/2)), (int) (t.getY() / zoom));
|
||||
}
|
||||
});
|
||||
rect.setOnMousePressed(new EventHandler<MouseEvent>() {
|
||||
@Override
|
||||
public void handle(MouseEvent t) {
|
||||
handleMouse(t, xx, yy);
|
||||
lastActionX=-1;
|
||||
lastActionY=-1;
|
||||
}
|
||||
});
|
||||
grid[x][y] = rect;
|
||||
gridGroup.getChildren().add(rect);
|
||||
rect.setStrokeWidth(2);
|
||||
}
|
||||
}
|
||||
|
||||
Group mainGroup = new Group();
|
||||
Rectangle background = new Rectangle(0, 0, zoom * 14 + 10, zoom * 16 + 10);
|
||||
background.setFill(Color.BLACK);
|
||||
mainGroup.getChildren().add(background);
|
||||
mainGroup.getChildren().add(gridGroup);
|
||||
|
||||
tileEditorAnchorPane.getChildren().add(mainGroup);
|
||||
TileUtils.redrawTile(getEntity());
|
||||
observedObjectChanged(getEntity());
|
||||
}
|
||||
|
||||
private void handleMouse(MouseEvent t, int x, int y) {
|
||||
t.consume();
|
||||
if (t.getButton() == null || t.getButton() == MouseButton.NONE) {
|
||||
return;
|
||||
}
|
||||
performAction(t.isShiftDown() || t.isSecondaryButtonDown(), x, y);
|
||||
}
|
||||
int lastActionX = -1;
|
||||
int lastActionY = -1;
|
||||
public void performDragAction(int x, int y) {
|
||||
performAction(false, x, y);
|
||||
}
|
||||
|
||||
private void performAction(boolean alt, int x, int y) {
|
||||
y = Math.min(Math.max(y,0), 15);
|
||||
x = Math.min(Math.max(x,0), 27);
|
||||
if (lastActionX == x && lastActionY == y) {
|
||||
return;
|
||||
}
|
||||
lastActionX = x;
|
||||
lastActionY = y;
|
||||
switch (drawMode) {
|
||||
case Toggle:
|
||||
if (alt) {
|
||||
toggleHiBit(x, y);
|
||||
} else {
|
||||
toggle(x, y);
|
||||
}
|
||||
break;
|
||||
case Pencil1px:
|
||||
int pat = currentPattern.getBytePattern()[y * 4 + (x / 7)];
|
||||
set((pat & (1 << (x % 7))) != 0, x, y);
|
||||
if (currentPattern.hiBitMatters) {
|
||||
setHiBit(pat >= 128, x, y);
|
||||
}
|
||||
break;
|
||||
case Pencil3px:
|
||||
for (int xx = x-1; xx <= x+1; xx++) {
|
||||
if (xx < 0 || xx >= 28) continue;
|
||||
pat = currentPattern.getBytePattern()[y * 4 + (xx / 7)];
|
||||
set((pat & (1 << (xx % 7))) != 0, xx, y);
|
||||
if (currentPattern.hiBitMatters) {
|
||||
setHiBit(pat >= 128, xx, y);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
observedObjectChanged(getEntity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void observedObjectChanged(Tile tile) {
|
||||
recolorGrid(
|
||||
TileUtils.getPlatformData(tile, Platform.AppleII_DHGR),
|
||||
grid,
|
||||
TileUtils.getImage(tile, Platform.AppleII_DHGR));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showShiftUI() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDrawMode(DrawMode drawMode) {
|
||||
this.drawMode = drawMode;
|
||||
lastActionX = -1;
|
||||
lastActionY = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildPatternSelector(Menu tilePatternMenu) {
|
||||
FillPattern.buildMenu(tilePatternMenu, new DataObserver<FillPattern>() {
|
||||
@Override
|
||||
public void observedObjectChanged(FillPattern object) {
|
||||
changeCurrentPattern(object);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void changeCurrentPattern(FillPattern pat) {
|
||||
currentPattern = pat;
|
||||
lastActionX = -1;
|
||||
lastActionY = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister() {
|
||||
}
|
||||
int zoom = 25;
|
||||
Group gridGroup;
|
||||
Rectangle[][] grid;
|
||||
|
||||
public void toggleHiBit(int x, int y) {
|
||||
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII_DHGR);
|
||||
data[y * 4 + (x / 7)] ^= 128;
|
||||
TileUtils.setPlatformData(getEntity(), Platform.AppleII_DHGR, data);
|
||||
TileUtils.redrawTile(getEntity());
|
||||
}
|
||||
|
||||
public void setHiBit(boolean on, int x, int y) {
|
||||
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII_DHGR);
|
||||
if (on) {
|
||||
data[y * 4 + (x / 7)] |= 128;
|
||||
} else {
|
||||
data[y * 4 + (x / 7)] &= 127;
|
||||
}
|
||||
TileUtils.setPlatformData(getEntity(), Platform.AppleII_DHGR, data);
|
||||
TileUtils.redrawTile(getEntity());
|
||||
}
|
||||
|
||||
public void toggle(int x, int y) {
|
||||
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII_DHGR);
|
||||
data[y * 4 + (x / 7)] ^= (1 << (x % 7));
|
||||
TileUtils.setPlatformData(getEntity(), Platform.AppleII_DHGR, data);
|
||||
TileUtils.redrawTile(getEntity());
|
||||
}
|
||||
|
||||
public void set(boolean on, int x, int y) {
|
||||
byte[] data = TileUtils.getPlatformData(getEntity(), Platform.AppleII_DHGR);
|
||||
data[y * 4 + (x / 7)] |= (1 << (x % 7));
|
||||
if (!on) {
|
||||
data[y * 4 + (x / 7)] ^= (1 << (x % 7));
|
||||
}
|
||||
TileUtils.setPlatformData(getEntity(), Platform.AppleII_DHGR, data);
|
||||
TileUtils.redrawTile(getEntity());
|
||||
}
|
||||
|
||||
public void recolorGrid(byte[] spriteData, Shape[][] grid, WritableImage img) {
|
||||
for (int y = 0; y < 16; y++) {
|
||||
for (int x = 0; x < 28; x++) {
|
||||
grid[x][y].setFill(img.getPixelReader().getColor(x, y * 2));
|
||||
if ((spriteData[y * 4 + x / 7] & (1 << (x % 7))) != 0) {
|
||||
grid[x][y].setStroke(Color.ANTIQUEWHITE);
|
||||
} else {
|
||||
Color stroke = Color.ANTIQUEWHITE;
|
||||
switch (x%4) {
|
||||
case 0: stroke=Color.BROWN;
|
||||
break;
|
||||
case 1: stroke=Color.BLUE;
|
||||
break;
|
||||
case 2: stroke=Color.GREEN;
|
||||
break;
|
||||
case 3: stroke=Color.CHOCOLATE;
|
||||
break;
|
||||
}
|
||||
grid[x][y].setStroke(stroke);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paste() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void select() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectNone() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package org.badvision.outlaweditor.apple.dhgr;
|
||||
|
||||
import org.badvision.outlaweditor.apple.*;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javafx.scene.paint.Color;
|
||||
import org.badvision.outlaweditor.TileRenderer;
|
||||
import static org.badvision.outlaweditor.apple.AppleNTSCGraphics.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class AppleDHGRTileRenderer extends AppleTileRenderer {
|
||||
@Override
|
||||
public WritableImage redrawSprite(byte[] spriteData, WritableImage img) {
|
||||
if (img == null) {
|
||||
img = new WritableImage(28, 32);
|
||||
}
|
||||
if (spriteData == null) return img;
|
||||
int[][] palette = useSolidPalette ? solidPalette : textPalette;
|
||||
for (int y = 0; y < 16; y++) {
|
||||
// int bleedOver = (spriteData[y * 2 + 1] & 128)==128? 256 : 0;
|
||||
int scan = spriteData[y * 4 + 3] & 255;
|
||||
scan <<=7;
|
||||
scan |= spriteData[y * 4 + 2] & 255;
|
||||
scan <<=7;
|
||||
scan |= spriteData[y * 4 + 1] & 255;
|
||||
scan <<=7;
|
||||
scan |= spriteData[y * 4] & 255;
|
||||
int last = (scan >> 26) & 3;
|
||||
int keep = scan & 0xff;
|
||||
scan <<= 2;
|
||||
scan |= last;
|
||||
for (int x = 0; x < 14; x++) {
|
||||
boolean isHiBit = ((spriteData[y * 2 + x / 7] & 128) != 0);
|
||||
|
||||
int col1 = palette[ (x & 1) << 1][scan & 0x07f];
|
||||
Color color1 = Color.rgb(getRed(col1), getGreen(col1), getBlue(col1));
|
||||
scan >>= 1;
|
||||
if (x == 12) {
|
||||
scan = scan & (isHiBit ? 0x07f : 0x01f) | (keep << 5);
|
||||
}
|
||||
int col2 = palette[ ((x & 1) << 1) + 1][scan & 0x07f];
|
||||
Color color2 = Color.rgb(getRed(col2), getGreen(col2), getBlue(col2));
|
||||
scan >>= 1;
|
||||
img.getPixelWriter().setColor(x * 2, y * 2, color1);
|
||||
img.getPixelWriter().setColor(x * 2, y * 2 + 1, color1);
|
||||
img.getPixelWriter().setColor(x * 2 + 1, y * 2, color2);
|
||||
img.getPixelWriter().setColor(x * 2 + 1, y * 2 + 1, color2);
|
||||
}
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return 28;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return 32;
|
||||
}
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.badvision.outlaweditor.apple.dhgr;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import org.badvision.outlaweditor.Platform;
|
||||
import org.badvision.outlaweditor.data.DataObserver;
|
||||
import org.badvision.outlaweditor.data.TileUtils;
|
||||
import org.badvision.outlaweditor.data.xml.Tile;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public enum FillPattern {
|
||||
Magenta(true, 4, false,
|
||||
"+---+---+---+---+---+---+---"),
|
||||
Pink(true, 4, false,
|
||||
"++-+++-+++-+++-+++-+++-+++-+"),
|
||||
Purple(true, 4, false,
|
||||
"++--++--++--++--++--++--++--"),
|
||||
LtBlue(true, 4, false,
|
||||
"+++-+++-+++-+++-+++-+++-+++-"),
|
||||
MedBlue(true, 4, false,
|
||||
"-++--++--++--++--++--++--++-"),
|
||||
Blue(true, 4, false,
|
||||
"-+---+---+---+---+---+---+--"),
|
||||
Green(true, 4, false,
|
||||
"--+---+---+---+---+---+---+-"),
|
||||
LtGreen(true, 4, false,
|
||||
"--++--++--++--++--++--++--++"),
|
||||
Aqua(true, 4, false,
|
||||
"-+++-+++-+++-+++-+++-+++-+++"),
|
||||
Yellow(true, 4, false,
|
||||
"+-+++-+++-+++-+++-+++-+++-++"),
|
||||
Orange(true, 4, false,
|
||||
"+--++--++--++--++--++--++--+"),
|
||||
Brown(true, 4, false,
|
||||
"---+---+---+---+---+---+---+"),
|
||||
Black(false, 1, false, "-------"),
|
||||
White(false, 1, false, "+++++++"),
|
||||
Grey1(false, 2, false,
|
||||
"+-+-+-+-+-+-+-"),
|
||||
Grey2(false, 2, false,
|
||||
"-+-+-+-+-+-+-+");
|
||||
// ,
|
||||
// BW3(false, 4, false,
|
||||
// "+--++--++--++--++--++--++--+",
|
||||
// "-++--++--++--++--++--++--++-"
|
||||
// ),
|
||||
// BW4(false, 4, false,
|
||||
// "-++--++--++--++--++--++--++-",
|
||||
// "+--++--++--++--++--++--++--+"
|
||||
// );
|
||||
|
||||
public static int[] bitmask(boolean hiBit, String pattern) {
|
||||
int[] out = new int[pattern.length()/7];
|
||||
int place = 1;
|
||||
int pos = 0;
|
||||
int value = hiBit ? 128 : 0;
|
||||
for (int i=0; i < pattern.length(); i++) {
|
||||
char c = pattern.charAt(i);
|
||||
if (c == '1' || c == '+') {
|
||||
value |= place;
|
||||
}
|
||||
out[pos] = value;
|
||||
place <<= 1;
|
||||
if (place >= 128) {
|
||||
place = 1;
|
||||
value = hiBit ? 128 : 0;
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public static Integer[] buildPattern(boolean hiBit, String... pattern) {
|
||||
List<Integer> out = new ArrayList<>();
|
||||
for (String s : pattern) {
|
||||
for (int i : bitmask(hiBit, s)) {
|
||||
out.add(i);
|
||||
}
|
||||
}
|
||||
return out.toArray(new Integer[0]);
|
||||
}
|
||||
|
||||
public static void buildMenu(Menu target, final DataObserver<FillPattern> dataObserver) {
|
||||
target.getItems().clear();
|
||||
for (final FillPattern fill : FillPattern.values()) {
|
||||
MenuItem i = new MenuItem(fill.name(), new ImageView(fill.getPreview()));
|
||||
i.setOnAction(new EventHandler<ActionEvent>() {
|
||||
@Override
|
||||
public void handle(ActionEvent t) {
|
||||
dataObserver.observedObjectChanged(fill);
|
||||
}
|
||||
});
|
||||
target.getItems().add(i);
|
||||
}
|
||||
}
|
||||
Integer[] pattern;
|
||||
int[] bytePattern;
|
||||
int width;
|
||||
boolean hiBitMatters;
|
||||
WritableImage preview;
|
||||
|
||||
private FillPattern(boolean hiBitMatters, int width, boolean hiBit, String... pattern) {
|
||||
this.pattern = buildPattern(hiBit, pattern);
|
||||
this.width = width;
|
||||
this.hiBitMatters = hiBitMatters;
|
||||
this.bytePattern = getBytePattern();
|
||||
}
|
||||
|
||||
public WritableImage getPreview() {
|
||||
if (preview == null) {
|
||||
preview = renderPreview(null, bytePattern);
|
||||
}
|
||||
return preview;
|
||||
}
|
||||
|
||||
public int[] getBytePattern() {
|
||||
int[] out = new int[16 * 4];
|
||||
int pos = 0;
|
||||
int patternHeight = pattern.length / width;
|
||||
for (int y = 0; y < 16; y++) {
|
||||
int yOffset = (y % patternHeight) * width;
|
||||
for (int x = 0; x < 4; x++) {
|
||||
out[pos++] = pattern[yOffset + (x % width)];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public static int[] interleave(int[] pat1, int[] pat2) {
|
||||
int[] out = Arrays.copyOf(pat1, pat1.length);
|
||||
for (int y = 1; y < 16; y += 2) {
|
||||
int offset = y * 4;
|
||||
for (int x = 0; x < 4; x++) {
|
||||
out[offset + x] = pat2[offset + x];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public static int[] fromTile(Tile t) {
|
||||
byte[] raw = TileUtils.getPlatformData(t, Platform.AppleII_DHGR);
|
||||
int[] out = new int[16 * 4];
|
||||
int pos = 0;
|
||||
for (int y = 0; y < 16; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
out[pos++] = raw[y * 4 + x] & 0x0ff;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public static WritableImage renderPreview(WritableImage img, int[] pattern) {
|
||||
byte[] b = new byte[pattern.length];
|
||||
for (int i=0; i < pattern.length; i++) {
|
||||
b[i]=(byte) pattern[i];
|
||||
}
|
||||
return Platform.AppleII_DHGR.tileRenderer.redrawSprite(b, null);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package org.badvision.outlaweditor.data;
|
||||
|
||||
import org.badvision.outlaweditor.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public interface DataObserver<T> {
|
||||
public void observedObjectChanged(T object);
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.badvision.outlaweditor.data;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import static org.badvision.outlaweditor.data.TileUtils.clear;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class DataProducer {
|
||||
static Map<Object, List<WeakReference<DataObserver>>> observers;
|
||||
static {
|
||||
clear();
|
||||
}
|
||||
public static void clear() {
|
||||
observers = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public static List<WeakReference<DataObserver>> getObservers(Object o) {
|
||||
if (observers.get(o) == null) {
|
||||
observers.put(o, new ArrayList<WeakReference<DataObserver>>());
|
||||
}
|
||||
return observers.get(o);
|
||||
}
|
||||
|
||||
public static void addObserver(Object o, DataObserver observer) {
|
||||
getObservers(o).add(new WeakReference<>(observer));
|
||||
}
|
||||
|
||||
public static void notifyObservers(Object o) {
|
||||
for (WeakReference<DataObserver> ref : getObservers(o)) {
|
||||
DataObserver observer = ref.get();
|
||||
if (observer != null) {
|
||||
observer.observedObjectChanged(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.badvision.outlaweditor.data;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.adapter.JavaBeanBooleanProperty;
|
||||
import javafx.beans.property.adapter.JavaBeanBooleanPropertyBuilder;
|
||||
import javafx.beans.property.adapter.JavaBeanIntegerProperty;
|
||||
import javafx.beans.property.adapter.JavaBeanIntegerPropertyBuilder;
|
||||
import javafx.beans.property.adapter.JavaBeanStringProperty;
|
||||
import javafx.beans.property.adapter.JavaBeanStringPropertyBuilder;
|
||||
import org.badvision.outlaweditor.ApplicationUIController;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class PropertyHelper {
|
||||
|
||||
public static JavaBeanIntegerProperty intProp(Object t, String fieldName) throws NoSuchMethodException {
|
||||
return new JavaBeanIntegerPropertyBuilder().bean(t).name(fieldName).build();
|
||||
}
|
||||
|
||||
public static JavaBeanBooleanProperty boolProp(Object t, String fieldName) throws NoSuchMethodException {
|
||||
return new JavaBeanBooleanPropertyBuilder().bean(t).name(fieldName).build();
|
||||
}
|
||||
|
||||
public static Property categoryProp(final Object t, String fieldName) throws NoSuchMethodException {
|
||||
final String camel = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
|
||||
SimpleStringProperty prop = new SimpleStringProperty() {
|
||||
@Override
|
||||
public String get() {
|
||||
try {
|
||||
Method getter = t.getClass().getMethod("get" + camel);
|
||||
List<String> list = (List<String>) getter.invoke(t);
|
||||
String out = "";
|
||||
for (String s : list) {
|
||||
if (out.length() > 0) {
|
||||
out += ",";
|
||||
}
|
||||
out += s;
|
||||
}
|
||||
return out;
|
||||
} catch (NoSuchMethodException ex) {
|
||||
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (SecurityException ex) {
|
||||
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (IllegalAccessException ex) {
|
||||
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (InvocationTargetException ex) {
|
||||
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String string) {
|
||||
try {
|
||||
Method getter = t.getClass().getMethod("get" + camel);
|
||||
List<String> list = (List<String>) getter.invoke(t);
|
||||
list.clear();
|
||||
Collections.addAll(list, string.split(","));
|
||||
} catch (NoSuchMethodException ex) {
|
||||
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (SecurityException ex) {
|
||||
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (IllegalAccessException ex) {
|
||||
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (InvocationTargetException ex) {
|
||||
Logger.getLogger(ApplicationUIController.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
return prop;
|
||||
}
|
||||
|
||||
public static JavaBeanStringProperty stringProp(Object t, String fieldName) throws NoSuchMethodException {
|
||||
return new JavaBeanStringPropertyBuilder().bean(t).name(fieldName).build();
|
||||
}
|
||||
|
||||
static private Map<Property, Property> boundProperties = new HashMap<>();
|
||||
|
||||
static public void bind(Property formProp, Property sourceProp) {
|
||||
if (boundProperties.containsKey(formProp)) {
|
||||
formProp.unbindBidirectional(boundProperties.get(formProp));
|
||||
boundProperties.get(formProp).unbindBidirectional(formProp);
|
||||
boundProperties.get(formProp).unbind();
|
||||
boundProperties.remove(formProp);
|
||||
}
|
||||
formProp.unbind();
|
||||
if (sourceProp != null) {
|
||||
formProp.bindBidirectional(sourceProp);
|
||||
boundProperties.put(formProp, sourceProp);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package org.badvision.outlaweditor.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import org.badvision.outlaweditor.data.xml.Script;
|
||||
import org.badvision.outlaweditor.data.xml.Tile;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class TileInstance implements Serializable {
|
||||
transient Tile tile;
|
||||
String tileId;
|
||||
public TileInstance(Tile t) {
|
||||
tile = t;
|
||||
if (t != null) {
|
||||
tileId = t.getId();
|
||||
}
|
||||
}
|
||||
|
||||
public Tile getTile() {
|
||||
if (tile == null && tileId != null) {
|
||||
tile = TilesetUtils.getTileById(tileId);
|
||||
}
|
||||
return tile;
|
||||
}
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
package org.badvision.outlaweditor.data;
|
||||
|
||||
import org.badvision.outlaweditor.Platform;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.xml.bind.JAXBElement;
|
||||
import org.badvision.outlaweditor.Application;
|
||||
import org.badvision.outlaweditor.UIAction;
|
||||
import org.badvision.outlaweditor.data.xml.Map;
|
||||
import org.badvision.outlaweditor.data.xml.Map.Chunk;
|
||||
import org.badvision.outlaweditor.data.xml.ObjectFactory;
|
||||
import org.badvision.outlaweditor.data.xml.Tile;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class TileMap extends ArrayList<ArrayList<Tile>> implements Serializable {
|
||||
|
||||
public static final long serialVersionUID = 6486309334559843742L;
|
||||
Map backingMap;
|
||||
boolean backingMapStale;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
public TileMap(Map m) {
|
||||
backingMapStale = false;
|
||||
width = 0;
|
||||
height = 0;
|
||||
loadFromMap(m);
|
||||
}
|
||||
|
||||
public Tile get(int x, int y) {
|
||||
if (size() <= y || get(y) == null) {
|
||||
return null;
|
||||
}
|
||||
if (get(y).size() <= x) {
|
||||
return null;
|
||||
}
|
||||
return get(y).get(x);
|
||||
}
|
||||
|
||||
public void put(int x, int y, Tile t) {
|
||||
width = Math.max(x+1, width);
|
||||
height = Math.max(y+1, height);
|
||||
for (int i = size(); i <= y; i++) {
|
||||
add(null);
|
||||
}
|
||||
if (get(y) == null) {
|
||||
set(y, new ArrayList<Tile>());
|
||||
}
|
||||
List<Tile> row = get(y);
|
||||
for (int i = row.size(); i <= x; i++) {
|
||||
row.add(null);
|
||||
}
|
||||
row.set(x, t);
|
||||
backingMapStale = true;
|
||||
}
|
||||
|
||||
public Map getBackingMap() {
|
||||
return backingMap;
|
||||
}
|
||||
|
||||
public void updateBackingMap() {
|
||||
ObjectFactory f = new ObjectFactory();
|
||||
backingMap.getChunk().clear();
|
||||
Chunk c = new Map.Chunk();
|
||||
c.setX(0);
|
||||
c.setY(0);
|
||||
for (int y = 0; y < height; y++) {
|
||||
List<String> row = new ArrayList<>();
|
||||
for (int x = 0; x < width; x++) {
|
||||
Tile t = get(x, y);
|
||||
if (t == null) {
|
||||
row.add(NULL_TILE_ID);
|
||||
} else {
|
||||
row.add(TileUtils.getId(t));
|
||||
}
|
||||
}
|
||||
c.getRow().add(f.createMapChunkRow(row));
|
||||
}
|
||||
backingMap.getChunk().add(c);
|
||||
backingMapStale = false;
|
||||
}
|
||||
|
||||
private void loadFromMap(Map m) {
|
||||
clear();
|
||||
width = 0;
|
||||
height = 0;
|
||||
Set<Tile> unknownTiles = new HashSet<>();
|
||||
for (Chunk c : m.getChunk()) {
|
||||
int y = c.getY();
|
||||
for (JAXBElement<List<String>> row : c.getRow()) {
|
||||
int x = c.getX();
|
||||
for (String tileId : row.getValue()) {
|
||||
Tile t = null;
|
||||
if (!isNullTile(tileId)) {
|
||||
t = TilesetUtils.getTileById(tileId);
|
||||
if (t == null) {
|
||||
t = new Tile();
|
||||
unknownTiles.add(t);
|
||||
Platform p = Application.currentPlatform;
|
||||
WritableImage img = UIAction.getBadImage(p.tileRenderer.getWidth(), p.tileRenderer.getHeight());
|
||||
TileUtils.setImage(t, p, img);
|
||||
}
|
||||
}
|
||||
put(x, y, t);
|
||||
x++;
|
||||
}
|
||||
y++;
|
||||
}
|
||||
}
|
||||
if (!unknownTiles.isEmpty()) {
|
||||
int numMissing = unknownTiles.size();
|
||||
JOptionPane.showMessageDialog(null, (numMissing > 1
|
||||
? "There were " + numMissing + " missing tiles." : "There was a missing tile.")
|
||||
+ "Blank placeholders have been added.");
|
||||
}
|
||||
backingMap = m;
|
||||
backingMapStale = false;
|
||||
}
|
||||
public static String NULL_TILE_ID = "_";
|
||||
|
||||
public static boolean isNullTile(String tileId) {
|
||||
return tileId.equalsIgnoreCase(NULL_TILE_ID);
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package org.badvision.outlaweditor.data;
|
||||
|
||||
import org.badvision.outlaweditor.Platform;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javafx.scene.image.WritableImage;
|
||||
import org.badvision.outlaweditor.data.xml.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class TileUtils {
|
||||
static Map<String, Map<Platform, WritableImage>> display;
|
||||
static {
|
||||
clear();
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
display = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public static Tile newTile() {
|
||||
Tile t = new Tile();
|
||||
t.setObstruction(false);
|
||||
updateId(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
public static void updateId(Tile t) {
|
||||
if (t.getId() == null) {
|
||||
t.setId("TILE" + Integer.toHexString((int) (Math.random() * 1000000.0)));
|
||||
}
|
||||
}
|
||||
|
||||
public static String getId(Tile t) {
|
||||
if (t.getId() == null) {
|
||||
updateId(t);
|
||||
}
|
||||
return t.getId();
|
||||
}
|
||||
|
||||
public static Map<Platform, WritableImage> getDisplay(Tile t) {
|
||||
if (display.get(getId(t)) == null) {
|
||||
display.put(getId(t), new EnumMap<Platform, WritableImage>(Platform.class));
|
||||
}
|
||||
return display.get(getId(t));
|
||||
}
|
||||
|
||||
public static void redrawTile(Tile t) {
|
||||
Map<Platform, WritableImage> displays = getDisplay(t);
|
||||
for (PlatformData d : t.getDisplayData()) {
|
||||
Platform p = Platform.valueOf(d.getPlatform());
|
||||
displays.put(p, p.tileRenderer.redrawSprite(d.getValue(), displays.get(p)));
|
||||
}
|
||||
DataProducer.notifyObservers(t);
|
||||
}
|
||||
|
||||
public static byte[] getPlatformData(Tile t, Platform p) {
|
||||
for (PlatformData d : t.getDisplayData()) {
|
||||
if (d.getPlatform().equalsIgnoreCase(p.name())) {
|
||||
return d.getValue();
|
||||
}
|
||||
}
|
||||
byte[] out = new byte[p.dataHeight*p.dataWidth];
|
||||
setPlatformData(t, p, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
public static WritableImage getImage(Tile t, Platform p) {
|
||||
Map<Platform, WritableImage> displays = getDisplay(t);
|
||||
byte[] data = getPlatformData(t, p);
|
||||
return displays.put(p, p.tileRenderer.redrawSprite(data, displays.get(p)));
|
||||
}
|
||||
|
||||
public static void setImage(Tile t, Platform p, WritableImage img) {
|
||||
Map<Platform, WritableImage> displays = getDisplay(t);
|
||||
displays.put(p, img);
|
||||
}
|
||||
|
||||
public static void setPlatformData(Tile t, Platform p, byte[] b) {
|
||||
for (PlatformData d : t.getDisplayData()) {
|
||||
if (d.getPlatform().equalsIgnoreCase(p.name())) {
|
||||
d.setValue(b);
|
||||
return;
|
||||
}
|
||||
}
|
||||
PlatformData d = new PlatformData();
|
||||
d.setPlatform(p.name());
|
||||
d.setValue(b);
|
||||
t.getDisplayData().add(d);
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package org.badvision.outlaweditor.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.badvision.outlaweditor.Application;
|
||||
import org.badvision.outlaweditor.data.xml.Tile;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author brobert
|
||||
*/
|
||||
public class TilesetUtils implements Serializable {
|
||||
|
||||
public static void addObserver(DataObserver o) {
|
||||
DataProducer.addObserver(TilesetUtils.class, o);
|
||||
}
|
||||
|
||||
public static boolean add(Tile e) {
|
||||
boolean output = Application.gameData.getTile().add(e);
|
||||
DataProducer.notifyObservers(TilesetUtils.class);
|
||||
return output;
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
Application.gameData.getTile().clear();
|
||||
DataProducer.notifyObservers(TilesetUtils.class);
|
||||
}
|
||||
|
||||
public static void remove(Tile t) {
|
||||
Application.gameData.getTile().remove(t);
|
||||
DataProducer.notifyObservers(TilesetUtils.class);
|
||||
}
|
||||
// The tileset should have been a map in retrospect but now it has to
|
||||
// stay this way to preserve compatibility with existing files.
|
||||
static Map<String, Tile> lookup;
|
||||
|
||||
public static Tile getTileById(String tileId) {
|
||||
if (lookup == null || (lookup.get(tileId) == null && !lookup.containsKey(tileId))) {
|
||||
lookup = new HashMap<>();
|
||||
for (Tile t : Application.gameData.getTile()) {
|
||||
lookup.put(TileUtils.getId(t), t);
|
||||
}
|
||||
if (lookup.get(tileId) == null) {
|
||||
lookup.put(tileId, null);
|
||||
}
|
||||
}
|
||||
return lookup.get(tileId);
|
||||
}
|
||||
}
|
280
OutlawEditor/src/main/resources/ApplicationUI.fxml
Normal file
@ -0,0 +1,280 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.*?>
|
||||
<?import java.net.*?>
|
||||
<?import java.util.*?>
|
||||
<?import javafx.collections.*?>
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.scene.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.image.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?scenebuilder-stylesheet applicationui.css?>
|
||||
|
||||
<AnchorPane id="AnchorPane" prefHeight="500.0" prefWidth="800.0" styleClass="mainFxmlClass" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="org.badvision.outlaweditor.ApplicationUIControllerImpl">
|
||||
<children>
|
||||
<VBox prefHeight="500.0" prefWidth="800.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||
<children>
|
||||
<MenuBar>
|
||||
<menus>
|
||||
<Menu mnemonicParsing="false" text="File">
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" onAction="#onFileOpen" text="Open" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#onFileSave" text="Save" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#onFileSaveAs" text="Save As..." />
|
||||
<MenuItem mnemonicParsing="false" onAction="#onFileQuit" text="Quit" />
|
||||
</items>
|
||||
</Menu>
|
||||
<Menu mnemonicParsing="false" text="Edit">
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" onAction="#onEditSelect" text="Select" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#onEditCopy" text="Copy" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#onEditPaste" text="Paste" />
|
||||
<Menu mnemonicParsing="false" text="Change Platform">
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" onAction="#onChangePlatformAppleSolid" text="Apple (solid)" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#onChangePlatformAppleText" text="Apple (text-friendly)" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#onChangePlatformAppleDHGRSolid" text="Apple (DHGR solid)" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#onChangePlatformAppleDHGRText" text="Apple (DHGR text)" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#onChangePlatformC64" text="C64" />
|
||||
</items>
|
||||
</Menu>
|
||||
</items>
|
||||
</Menu>
|
||||
<Menu mnemonicParsing="false" text="Help">
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" onAction="#onHelpAbout" text="About" />
|
||||
</items>
|
||||
</Menu>
|
||||
</menus>
|
||||
</MenuBar>
|
||||
<TabPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="455.0" prefWidth="676.9998779296875" rotateGraphic="false" tabClosingPolicy="UNAVAILABLE" VBox.vgrow="ALWAYS">
|
||||
<tabs>
|
||||
<Tab onSelectionChanged="#tileTabActivated" text="Tiles">
|
||||
<content>
|
||||
<AnchorPane id="tilesTab" minHeight="0.0" minWidth="0.0" prefHeight="420.0000999999975" prefWidth="677.0">
|
||||
<children>
|
||||
<VBox prefHeight="420.0000999999975" prefWidth="677.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||
<children>
|
||||
<ToolBar prefWidth="686.0">
|
||||
<items>
|
||||
<Label text="Tile:" />
|
||||
<ComboBox fx:id="tileSelector" minWidth="125.0" onAction="#onCurrentTileSelected">
|
||||
<items>
|
||||
<FXCollections fx:factory="observableArrayList">
|
||||
<String fx:value="Item 1" />
|
||||
<String fx:value="Item 2" />
|
||||
<String fx:value="Item 3" />
|
||||
</FXCollections>
|
||||
</items>
|
||||
</ComboBox>
|
||||
<Button mnemonicParsing="false" onAction="#onTileCreatePressed" text="Create new" />
|
||||
<Button mnemonicParsing="false" onAction="#onTileExportPressed" text="Export" />
|
||||
<Button mnemonicParsing="false" onAction="#onTileClonePressed" prefWidth="64.9998779296875" text="Clone" />
|
||||
<Button mnemonicParsing="false" onAction="#onTileDeletePressed" text="Delete" />
|
||||
<MenuButton mnemonicParsing="false" text="Tools">
|
||||
<items>
|
||||
<Menu mnemonicParsing="false" text="Pattern" fx:id="tilePatternMenu" />
|
||||
<Menu mnemonicParsing="false" text="Draw mode">
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" onAction="#tileBitMode" text="Bit Toggle" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#tileDraw1BitMode" text="1 bit-wide" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#tileDraw3BitMode" text="3 bit wide" />
|
||||
</items>
|
||||
</Menu>
|
||||
<MenuItem mnemonicParsing="false" onAction="#tileShift" text="Shift..." />
|
||||
</items>
|
||||
</MenuButton>
|
||||
</items>
|
||||
</ToolBar>
|
||||
<HBox prefHeight="387.0" prefWidth="677.0" VBox.vgrow="ALWAYS">
|
||||
<children>
|
||||
<AnchorPane id="imageDetailsPane" opacity="1.0" prefHeight="200.0" prefWidth="200.0" rotate="0.0" style="">
|
||||
<children>
|
||||
<Label layoutX="4.0" layoutY="14.0" text="Name" />
|
||||
<TextField id="" fx:id="tileNameField" layoutX="53.0" layoutY="11.0" prefWidth="147.0" />
|
||||
<Label layoutX="4.0" layoutY="36.0" text="ID" />
|
||||
<TextField fx:id="tileIdField" layoutX="53.0" layoutY="33.0" prefWidth="147.0" />
|
||||
<Label layoutX="4.0" layoutY="58.0" text="Category" />
|
||||
<TextField fx:id="tileCategoryField" layoutX="74.0" layoutY="55.0" prefWidth="126.0" />
|
||||
<CheckBox fx:id="tileObstructionField" layoutX="4.0" layoutY="78.0" mnemonicParsing="false" text="Physical Obstruction" />
|
||||
</children>
|
||||
</AnchorPane>
|
||||
<AnchorPane fx:id="tileEditorAnchorPane" prefHeight="387.0" prefWidth="477.0000999999975" HBox.hgrow="ALWAYS" />
|
||||
</children>
|
||||
</HBox>
|
||||
</children>
|
||||
</VBox>
|
||||
</children>
|
||||
</AnchorPane>
|
||||
</content>
|
||||
</Tab>
|
||||
<Tab onSelectionChanged="#mapTabActivated" text="Maps">
|
||||
<content>
|
||||
<AnchorPane id="mapsTab" minHeight="0.0" minWidth="0.0" prefHeight="-1.0" prefWidth="-1.0">
|
||||
<children>
|
||||
<VBox prefHeight="200.0" prefWidth="100.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||
<children>
|
||||
<ToolBar prefWidth="677.0">
|
||||
<items>
|
||||
<Label text="Map:" />
|
||||
<ComboBox fx:id="mapSelect" minWidth="125.0" onAction="#onMapSelected">
|
||||
<items>
|
||||
<FXCollections fx:factory="observableArrayList">
|
||||
<String fx:value="Item 1" />
|
||||
<String fx:value="Item 2" />
|
||||
<String fx:value="Item 3" />
|
||||
</FXCollections>
|
||||
</items>
|
||||
</ComboBox>
|
||||
<Button mnemonicParsing="false" onAction="#onMapCreatePressed" text="Create new" />
|
||||
<Button mnemonicParsing="false" onAction="#onMapClonePressed" text="Clone" />
|
||||
<Button mnemonicParsing="false" onAction="#onMapExportPressed" text="Export" />
|
||||
<Button mnemonicParsing="false" onAction="#onMapDeletePressed" text="Delete" />
|
||||
<Button mnemonicParsing="false" onAction="#onMapPreviewPressed" text="Preview" />
|
||||
<MenuButton mnemonicParsing="false" text="Tools">
|
||||
<items>
|
||||
<Menu mnemonicParsing="false" text="Change tile" fx:id="mapSelectTile" />
|
||||
<Menu mnemonicParsing="false" text="Draw mode">
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" onAction="#mapDraw1" text="Radius 1" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#mapDraw3" text="Radius 3" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#mapDraw5" text="Radius 5" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#mapDrawFilledRectMode" text="Filled Rectangle" />
|
||||
</items>
|
||||
</Menu>
|
||||
<MenuItem mnemonicParsing="false" onAction="#mapTogglePanZoom" text="Toggle pan/zoom controls" />
|
||||
</items>
|
||||
</MenuButton>
|
||||
</items>
|
||||
</ToolBar>
|
||||
<HBox prefHeight="389.0" prefWidth="677.0" VBox.vgrow="ALWAYS">
|
||||
<children>
|
||||
<AnchorPane prefHeight="200.0" prefWidth="200.0" HBox.hgrow="NEVER">
|
||||
<children>
|
||||
<Label text="Name" AnchorPane.leftAnchor="4.0" AnchorPane.topAnchor="14.0" />
|
||||
<TextField id="mapNameFiled" fx:id="mapNameField" layoutX="53.0" layoutY="11.0" prefWidth="147.0" />
|
||||
<TextField fx:id="mapWidthField" layoutX="53.0" layoutY="33.0" prefWidth="147.0" />
|
||||
<Label layoutX="4.0" layoutY="36.0" text="Width" />
|
||||
<TextField fx:id="mapHeightField" layoutX="53.0" layoutY="55.0" prefWidth="147.0" />
|
||||
<Label layoutX="4.0" layoutY="58.0" text="Height" />
|
||||
<CheckBox fx:id="mapWrapAround" alignment="CENTER_LEFT" contentDisplay="RIGHT" layoutX="4.0" layoutY="77.0" mnemonicParsing="false" text="Wrap at edges" />
|
||||
<Separator layoutX="4.0" layoutY="101.0" prefWidth="189.0" />
|
||||
<Label layoutX="4.0" layoutY="108.0" text="Scripts" />
|
||||
<ScrollPane fitToHeight="true" fitToWidth="true" prefHeight="232.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="157.0">
|
||||
<content>
|
||||
<AnchorPane id="Content" minHeight="0.0" minWidth="0.0" prefHeight="200.0" prefWidth="200.0">
|
||||
<children>
|
||||
<ListView fx:id="mapScriptsList" orientation="VERTICAL" prefHeight="217.0" prefWidth="199.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
|
||||
</children>
|
||||
</AnchorPane>
|
||||
</content>
|
||||
</ScrollPane>
|
||||
<ToolBar layoutX="0.0" layoutY="124.0" prefWidth="200.0">
|
||||
<items>
|
||||
<Button mnemonicParsing="false" onAction="#onMapScriptAddPressed" text="+" />
|
||||
<Button mnemonicParsing="false" onAction="#onMapScriptDeletePressed" text="-" />
|
||||
<Button mnemonicParsing="false" onAction="#onMapScriptClonePressed" text="Clone" />
|
||||
</items>
|
||||
</ToolBar>
|
||||
</children>
|
||||
</AnchorPane>
|
||||
<AnchorPane fx:id="mapEditorAnchorPane" prefHeight="389.0" prefWidth="477.0000999999975" HBox.hgrow="SOMETIMES">
|
||||
<children>
|
||||
<Button alignment="TOP_CENTER" layoutX="265.0" mnemonicParsing="false" onAction="#scrollMapUp" styleClass="moveButton" text="Up" AnchorPane.topAnchor="5.0" />
|
||||
<Button layoutY="185.0" mnemonicParsing="false" onAction="#scrollMapLeft" rotate="270.0" styleClass="moveButton" text="Left" AnchorPane.leftAnchor="-20.0" />
|
||||
<Button layoutX="265.0" mnemonicParsing="false" onAction="#scrollMapDown" rotate="180.0" styleClass="moveButton" text="Down" AnchorPane.bottomAnchor="5.0" />
|
||||
<Button layoutY="175.0" mnemonicParsing="false" onAction="#scrollMapRight" rotate="90.0" styleClass="moveButton" text="Right" AnchorPane.rightAnchor="-15.0" />
|
||||
<Button mnemonicParsing="false" onAction="#mapZoomIn" styleClass="zoomInButton" text="+" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="5.0" />
|
||||
<Button mnemonicParsing="false" onAction="#mapZoomOut" prefHeight="23.999908447265625" styleClass="zoomOutButton" text="-" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="30.0" />
|
||||
</children>
|
||||
</AnchorPane>
|
||||
</children>
|
||||
</HBox>
|
||||
</children>
|
||||
</VBox>
|
||||
</children>
|
||||
</AnchorPane>
|
||||
</content>
|
||||
</Tab>
|
||||
<Tab onSelectionChanged="#imageTabActivated" text="Images">
|
||||
<content>
|
||||
<AnchorPane id="tilesTab" minHeight="0.0" minWidth="0.0" prefHeight="420.0" prefWidth="677.0">
|
||||
<children>
|
||||
<VBox prefHeight="420.0000999999975" prefWidth="677.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||
<children>
|
||||
<ToolBar prefWidth="686.0">
|
||||
<items>
|
||||
<Label text="Image:" />
|
||||
<ComboBox id="tileSelect" fx:id="imageSelector" onAction="#onImageSelected">
|
||||
<items>
|
||||
<FXCollections fx:factory="observableArrayList">
|
||||
<String fx:value="Item 1" />
|
||||
<String fx:value="Item 2" />
|
||||
<String fx:value="Item 3" />
|
||||
</FXCollections>
|
||||
</items>
|
||||
</ComboBox>
|
||||
<Button mnemonicParsing="false" onAction="#onImageCreatePressed" text="Create new" />
|
||||
<Button mnemonicParsing="false" onAction="#onImageClonePressed" text="Clone" />
|
||||
<Button mnemonicParsing="false" onAction="#onImageExportPressed" text="Export" />
|
||||
<Button mnemonicParsing="false" onAction="#onImageDeletePressed" text="Delete" />
|
||||
<MenuButton mnemonicParsing="false" text="Tools">
|
||||
<items>
|
||||
<Menu mnemonicParsing="false" text="Pattern" fx:id="imagePatternMenu" />
|
||||
<Menu mnemonicParsing="false" text="Draw mode">
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" onAction="#imageBitMode" text="Bit Toggle mode" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#imageDraw1BitMode" text="1 bit-wide" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#imageDraw3BitMode" text="3 bit wide" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#imageDraw5BitMode" text="5 bit wide" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#imageDrawFilledRectMode" text="Filled Rectangle" />
|
||||
</items>
|
||||
</Menu>
|
||||
<MenuItem mnemonicParsing="false" onAction="#imageTogglePanZoom" text="Toggle pan/zoom controls" />
|
||||
<MenuItem mnemonicParsing="false" onAction="#imageShift" text="Shift..." />
|
||||
</items>
|
||||
</MenuButton>
|
||||
</items>
|
||||
</ToolBar>
|
||||
<HBox prefHeight="387.0" prefWidth="677.0" VBox.vgrow="ALWAYS">
|
||||
<children>
|
||||
<AnchorPane id="imageDetailsPane" opacity="1.0" prefHeight="200.0" prefWidth="200.0" rotate="0.0" style="">
|
||||
<children>
|
||||
<Label layoutX="4.0" layoutY="14.0" text="Name" />
|
||||
<TextField id="" fx:id="imageNameField" layoutX="53.0" layoutY="11.0" prefWidth="147.0" />
|
||||
<Label layoutX="4.0" layoutY="36.0" text="Category" />
|
||||
<TextField id="tileCategoryField" fx:id="imageCategoryField" layoutX="74.0" layoutY="33.0" prefWidth="126.0" />
|
||||
<TextField fx:id="imageWidthField" layoutY="55.0" prefWidth="145.0" AnchorPane.leftAnchor="53.0" AnchorPane.rightAnchor="0.0" />
|
||||
<Label layoutX="4.0" layoutY="58.0" text="Width" />
|
||||
<TextField id="imageWidthField" fx:id="imageHeightField" layoutX="53.0" layoutY="77.0" prefWidth="147.0" />
|
||||
<Label layoutX="4.0" layoutY="83.0" text="Height" />
|
||||
</children>
|
||||
</AnchorPane>
|
||||
<AnchorPane id="mapEditorAnchorPane" fx:id="imageEditorAnchorPane" prefHeight="389.0" prefWidth="477.0" HBox.hgrow="ALWAYS">
|
||||
<children>
|
||||
<Button layoutX="236.0" mnemonicParsing="false" onAction="#scrollImageUp" styleClass="moveButton" text="Up" AnchorPane.topAnchor="5.0" />
|
||||
<Button layoutY="185.0" mnemonicParsing="false" onAction="#scrollImageLeft" rotate="270.0" styleClass="moveButton" text="Left" AnchorPane.leftAnchor="-20.0" />
|
||||
<Button layoutX="236.0" mnemonicParsing="false" onAction="#scrollImageDown" rotate="180.0" styleClass="moveButton" text="Down" AnchorPane.bottomAnchor="5.0" />
|
||||
<Button layoutY="175.0" mnemonicParsing="false" onAction="#scrollImageRight" rotate="90.0" styleClass="moveButton" text="Right" AnchorPane.rightAnchor="-15.0" />
|
||||
<Button mnemonicParsing="false" onAction="#imageZoomIn" styleClass="zoomInButton" text="+" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="5.0" />
|
||||
<Button mnemonicParsing="false" onAction="#imageZoomOut" prefHeight="23.999908447265625" styleClass="zoomOutButton" text="-" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="30.0" />
|
||||
</children>
|
||||
</AnchorPane>
|
||||
</children>
|
||||
</HBox>
|
||||
</children>
|
||||
</VBox>
|
||||
</children>
|
||||
</AnchorPane>
|
||||
</content>
|
||||
</Tab>
|
||||
</tabs>
|
||||
</TabPane>
|
||||
</children>
|
||||
</VBox>
|
||||
</children>
|
||||
<stylesheets>
|
||||
<URL value="@styles/applicationui.css" />
|
||||
</stylesheets>
|
||||
</AnchorPane>
|
23
OutlawEditor/src/main/resources/fxml/hello.fxml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.*?>
|
||||
<?import javafx.scene.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import org.tbee.javafx.scene.layout.fxml.MigPane?>
|
||||
<?import layouttests.migfxml.sample1.*?>
|
||||
|
||||
<MigPane id="rootPane" fx:controller="org.badvision.outlaweditor.HelloController"
|
||||
styleClass="main-panel"
|
||||
layout="insets 20"
|
||||
cols="[label, pref!][grow, 50::]"
|
||||
rows=""
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
<Label text="First Name:" /> <TextField fx:id="firstNameField" prefColumnCount="30" MigPane.cc="growx, wrap" />
|
||||
<Label text="Last Name:" /> <TextField fx:id="lastNameField" prefColumnCount="30" MigPane.cc="growx, wrap" />
|
||||
|
||||
<Button text="Say Hello" onAction="#sayHello" MigPane.cc="skip, gap :push, gaptop 15, wrap" />
|
||||
|
||||
<Label fx:id="messageLabel" styleClass="hello-message" MigPane.cc="span, growx, gaptop 15" />
|
||||
|
||||
</MigPane >
|
BIN
OutlawEditor/src/main/resources/images/arrowUp.png
Normal file
After Width: | Height: | Size: 567 B |
BIN
OutlawEditor/src/main/resources/images/background.jpg
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
OutlawEditor/src/main/resources/images/draw-rectangle.png
Normal file
After Width: | Height: | Size: 699 B |
BIN
OutlawEditor/src/main/resources/images/fill.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
OutlawEditor/src/main/resources/images/icon_brokenLink.png
Normal file
After Width: | Height: | Size: 657 B |
BIN
OutlawEditor/src/main/resources/images/pencil_big.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
OutlawEditor/src/main/resources/images/pencil_med.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
OutlawEditor/src/main/resources/images/pencil_small.png
Normal file
After Width: | Height: | Size: 1021 B |
BIN
OutlawEditor/src/main/resources/images/zoomIn.png
Normal file
After Width: | Height: | Size: 334 B |
BIN
OutlawEditor/src/main/resources/images/zoomOut.png
Normal file
After Width: | Height: | Size: 218 B |
@ -0,0 +1,99 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
To change this template, choose Tools | Templates
|
||||
and open the template in the editor.
|
||||
-->
|
||||
|
||||
<xs:schema version="1.0"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified" targetNamespace="outlaw" xmlns:tns="outlaw">
|
||||
<xs:complexType name="image">
|
||||
<xs:sequence>
|
||||
<xs:element name="category" type="xs:string" maxOccurs="unbounded" minOccurs="0"/>
|
||||
<xs:element name="displayData" type="tns:platformData" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="tile">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="tns:image">
|
||||
<xs:attribute name="id" type="xs:string" use="required"/>
|
||||
<xs:attribute name="obstruction" type="xs:boolean"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="platformData">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:hexBinary">
|
||||
<xs:attribute name="platform" type="xs:string" use="required"/>
|
||||
<xs:attribute name="width" type="xs:int" use="required"/>
|
||||
<xs:attribute name="height" type="xs:int" use="required"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="script">
|
||||
<xs:sequence>
|
||||
<xs:element name="name" type="xs:string"/>
|
||||
<xs:element name="description" type="xs:string" minOccurs="0"/>
|
||||
<xs:element name="script" type="xs:string"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="locationScript">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="tns:script">
|
||||
<xs:attribute name="x" type="xs:int"/>
|
||||
<xs:attribute name="y" type="xs:int"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="intervalScript">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="tns:script">
|
||||
<xs:attribute name="start" type="xs:int" use="optional"/>
|
||||
<xs:attribute name="end" type="xs:int" use="optional"/>
|
||||
<xs:attribute name="period" type="xs:int" use="optional"/>
|
||||
<xs:attribute name="ifTrue" type="xs:string" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="map">
|
||||
<xs:sequence>
|
||||
<xs:element name="chunk" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="row" maxOccurs="unbounded" type="xs:NMTOKENS"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="x" type="xs:int"/>
|
||||
<xs:attribute name="y" type="xs:int"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="scripts">
|
||||
<xs:complexType>
|
||||
<xs:choice maxOccurs="unbounded">
|
||||
<xs:element name="enter" type="tns:script" minOccurs="0"/>
|
||||
<xs:element name="exit" type="tns:script" minOccurs="0"/>
|
||||
<xs:element name="stepOn" type="tns:locationScript" minOccurs="0"/>
|
||||
<xs:element name="stepOff" type="tns:locationScript" minOccurs="0" />
|
||||
<xs:element name="interact" type="tns:locationScript" minOccurs="0"/>
|
||||
<xs:element name="interval" type="tns:intervalScript" minOccurs="0"/>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:string"/>
|
||||
<xs:attribute name="width" type="xs:int"/>
|
||||
<xs:attribute name="height" type="xs:int"/>
|
||||
<xs:attribute name="wrap" type="xs:boolean" default="false"/>
|
||||
<xs:attribute name="startX" type="xs:int" default="0"/>
|
||||
<xs:attribute name="startY" type="xs:int" default="0"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="gameData">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="image" minOccurs="0" maxOccurs="unbounded" type="tns:image"/>
|
||||
<xs:element name="tile" minOccurs="0" maxOccurs="unbounded" type="tns:tile"/>
|
||||
<xs:element name="map" minOccurs="0" maxOccurs="unbounded" type="tns:map"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
25
OutlawEditor/src/main/resources/styles/applicationui.css
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Empty Stylesheet file.
|
||||
*/
|
||||
|
||||
.mainFxmlClass {
|
||||
|
||||
}
|
||||
|
||||
.moveButton {
|
||||
-fx-graphic: url("arrowUp.png");
|
||||
}
|
||||
|
||||
.zoomOutButton {
|
||||
-fx-graphic: url("zoomOut.png");
|
||||
}
|
||||
|
||||
.zoomInButton {
|
||||
-fx-graphic: url("zoomIn.png");
|
||||
}
|
||||
|
||||
.moveButton, .zoomInButton, .zoomOutButton{
|
||||
-fx-background-color: rgba(0,0,0,0);
|
||||
-fx-content-display: graphic-only;
|
||||
-fx-effect: dropshadow(gaussian, navy, 10, 0.5, 0, 0);
|
||||
}
|
34
OutlawEditor/src/main/resources/styles/styles.css
Normal file
@ -0,0 +1,34 @@
|
||||
/* Application wide styles */
|
||||
|
||||
.label {
|
||||
-fx-font-size: 12px;
|
||||
-fx-font-weight: bold;
|
||||
-fx-text-fill: #333333;
|
||||
-fx-effect: dropshadow( gaussian , rgba(255,255,255,0.5) , 0,0,0,1 );
|
||||
}
|
||||
|
||||
.button {
|
||||
-fx-text-fill: white;
|
||||
-fx-font-family: "Arial Narrow";
|
||||
-fx-font-weight: bold;
|
||||
-fx-background-color: linear-gradient(#61a2b1, #2A5058);
|
||||
-fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 5, 0.0 , 0 , 1 );
|
||||
}
|
||||
|
||||
.button:hover{
|
||||
-fx-base: #395bae;
|
||||
}
|
||||
|
||||
/* Component specific styles */
|
||||
|
||||
|
||||
.main-panel {
|
||||
-fx-background-image: url("../images/background.jpg");
|
||||
}
|
||||
|
||||
.hello-message {
|
||||
-fx-text-fill: #AA0000;
|
||||
-fx-font-weight: bold;
|
||||
-fx-effect: dropshadow( gaussian , rgba(255,255,255,0.5) , 0,0,0,1 );
|
||||
}
|
||||
|