Compare commits
25 Commits
c244d36bd6
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
344f71192f | ||
|
|
f7747346d7 | ||
|
|
94f12b5676 | ||
|
|
1b6a4fac03 | ||
|
|
2fc918a728 | ||
|
|
0071e5af80 | ||
|
|
c10da7ccf9 | ||
|
|
32542df86c | ||
|
|
0ff64797d9 | ||
|
|
807970db5e | ||
|
|
07df031670 | ||
|
|
8e7937b3c6 | ||
|
|
adef7235f1 | ||
|
|
e9284e2158 | ||
|
|
2474879831 | ||
|
|
a9fc91ddde | ||
|
|
6defc0ec04 | ||
|
|
2b001a9ab0 | ||
|
|
62d2e5a15e | ||
|
|
ec80e51f95 | ||
|
|
d78b873de0 | ||
|
|
7c9942066f | ||
|
|
4d456d7e5d | ||
|
|
11b03b02d2 | ||
|
|
e008892c13 |
1
Builder/.gitignore
vendored
Normal file
1
Builder/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/bin/
|
||||||
49
Builder/src/fmon/builder/AnimationBuilder.fxml
Normal file
49
Builder/src/fmon/builder/AnimationBuilder.fxml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ComboBox?>
|
||||||
|
<?import javafx.scene.control.ListView?>
|
||||||
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.layout.TilePane?>
|
||||||
|
|
||||||
|
<GridPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fmon.builder.AnimationBuilder">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="234.0" minWidth="10.0" prefWidth="127.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="308.0" minWidth="10.0" prefWidth="308.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints maxHeight="267.0" minHeight="10.0" prefHeight="267.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="125.0" minHeight="0.0" prefHeight="34.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="124.0" minHeight="10.0" prefHeight="99.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<ListView fx:id="animationList" prefWidth="150.0" />
|
||||||
|
<HBox alignment="CENTER_LEFT" GridPane.rowIndex="1">
|
||||||
|
<children>
|
||||||
|
<Button mnemonicParsing="false" onAction="#addAnimation" text="+" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#quickBuildAnimation" text="Build" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<ImageView fx:id="viewpane" fitHeight="269.0" fitWidth="308.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="1" />
|
||||||
|
<ListView fx:id="frameList" prefWidth="150.0" GridPane.columnIndex="2" />
|
||||||
|
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="1">
|
||||||
|
<children>
|
||||||
|
<Button mnemonicParsing="false" onAction="#playAnimation" text="Play" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#addFrame" text="+" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#removeFrame" text="-" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<ScrollPane GridPane.columnSpan="3" GridPane.rowIndex="2">
|
||||||
|
<content>
|
||||||
|
<TilePane fx:id="tiles" hgap="5.0" vgap="5.0" />
|
||||||
|
</content>
|
||||||
|
</ScrollPane>
|
||||||
|
<ComboBox fx:id="fileChooser" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
207
Builder/src/fmon/builder/AnimationBuilder.scala
Normal file
207
Builder/src/fmon/builder/AnimationBuilder.scala
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
package fmon.builder
|
||||||
|
|
||||||
|
import java.io._
|
||||||
|
|
||||||
|
import javafx.application.Application
|
||||||
|
import javafx.fxml.FXML
|
||||||
|
import javafx.fxml.FXMLLoader
|
||||||
|
import javafx.fxml.JavaFXBuilderFactory
|
||||||
|
import javafx.scene.{control => jfxsc, Parent, Scene}
|
||||||
|
import javafx.stage.Stage
|
||||||
|
|
||||||
|
import scalafx.Includes._
|
||||||
|
import scalafx.beans.property._
|
||||||
|
import scalafx.collections.ObservableBuffer
|
||||||
|
import scalafx.scene.control._
|
||||||
|
import scalafx.scene.image.ImageView
|
||||||
|
import scalafx.scene.input.MouseEvent
|
||||||
|
import scalafx.scene.paint.Color
|
||||||
|
import scalafx.scene.layout._
|
||||||
|
import scalafx.util.Duration
|
||||||
|
import scalafx.util.StringConverter
|
||||||
|
|
||||||
|
import fmon.draw._
|
||||||
|
import fmon.util.YamlHelper
|
||||||
|
|
||||||
|
class ObsAnimation(name_ : String) {
|
||||||
|
val name = new StringProperty(this, "name", name_)
|
||||||
|
val frames = new ObservableBuffer[Frame]()
|
||||||
|
|
||||||
|
def toToken = {
|
||||||
|
val filename = frames(0).file.getName
|
||||||
|
val indices = frames.map(_.index).toIndexedSeq
|
||||||
|
AnimationToken(filename, indices)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def toString = name()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Frame(val img: StillImg, val file: File, val index: Int) {
|
||||||
|
def toToken = ImageToken(file.getName, index)
|
||||||
|
|
||||||
|
override def toString = s"${file.getName} ${index}"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
case class ImageToken(val filename: String, val index: Int) {
|
||||||
|
def toFrame(dir: File) = {
|
||||||
|
val file = new File(s"${dir.getPath}/$filename")
|
||||||
|
val pal = Palette.square(new FileInputStream(file), 5).images(5)
|
||||||
|
}
|
||||||
|
override def toString = s"${filename} ${index}"
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimationBuilder extends Savable {
|
||||||
|
@FXML var animationList: jfxsc.ListView[ObsAnimation] = _
|
||||||
|
@FXML var frameList: jfxsc.ListView[Frame] = _
|
||||||
|
@FXML var viewpane: javafx.scene.image.ImageView = _
|
||||||
|
@FXML var tiles: javafx.scene.layout.TilePane = _
|
||||||
|
@FXML var fileChooser: jfxsc.ComboBox[File] = _
|
||||||
|
|
||||||
|
val animations = ObservableBuffer[ObsAnimation]()
|
||||||
|
val imgFiles = ObservableBuffer[File]()
|
||||||
|
|
||||||
|
val imgdir = raw"C:\Users\dalyj\Documents\Design\Images\TimmahLexusX reduced\Reduced Animations"
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
def initialize(): Unit = {
|
||||||
|
fileChooser.items = imgFiles
|
||||||
|
fileChooser.selectionModel().selectedIndex.onChange(selectImageFile)
|
||||||
|
|
||||||
|
fileChooser.converter = FilenameConverter
|
||||||
|
val dir = new File(imgdir)
|
||||||
|
imgFiles ++= dir.listFiles()
|
||||||
|
|
||||||
|
animationList.items = animations
|
||||||
|
animationList.selectionModel().selectedIndex.onChange(selectionChange)
|
||||||
|
frameList.selectionModel().selectedIndex.onChange(selectFrame)
|
||||||
|
//for (x <- 0 until palette.numAcross; y <- 0 until palette.numDown) {
|
||||||
|
|
||||||
|
//addAnimation()
|
||||||
|
animationList.selectionModel().selectFirst()
|
||||||
|
fileChooser.selectionModel().selectFirst()
|
||||||
|
}
|
||||||
|
|
||||||
|
def currFile = fileChooser.selectionModel().getSelectedItem
|
||||||
|
def palette = Palette.square(new FileInputStream(currFile), 5)
|
||||||
|
|
||||||
|
def selectImageFile(): Unit = {
|
||||||
|
val images = palette.images
|
||||||
|
val children = images.zipWithIndex.map{case (img, i) => {
|
||||||
|
val imgView = new ImageView() {
|
||||||
|
scaleX = 0.5
|
||||||
|
scaleY = 0.5
|
||||||
|
image = img.croppedImage()
|
||||||
|
|
||||||
|
}
|
||||||
|
imgView.handleEvent(MouseEvent.Any) {
|
||||||
|
e: MouseEvent => if (e.clickCount == 2 && e.eventType == MouseEvent.MouseClicked) {
|
||||||
|
addFrame(img, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
imgView.delegate
|
||||||
|
}}
|
||||||
|
tiles.children.clear()
|
||||||
|
tiles.children ++= children
|
||||||
|
tiles.prefTileWidth = 96 // TODO : I think this has to do with how the image is scaled
|
||||||
|
tiles.prefTileHeight = 96
|
||||||
|
}
|
||||||
|
|
||||||
|
def addAnimation(): Unit = {
|
||||||
|
addAnimation("Animation")
|
||||||
|
}
|
||||||
|
|
||||||
|
def addAnimation(name: String): Unit = {
|
||||||
|
animations += new ObsAnimation(name)
|
||||||
|
animationList.selectionModel().selectLast()
|
||||||
|
}
|
||||||
|
|
||||||
|
def quickBuildAnimation(): Unit = {
|
||||||
|
val filename = currFile.getName
|
||||||
|
val name = filename.split('.')(0)
|
||||||
|
animations += new ObsAnimation(name)
|
||||||
|
animationList.selectionModel().selectLast()
|
||||||
|
palette.images.zipWithIndex.foreach{case (img, i) => addFrame(img, i)}
|
||||||
|
}
|
||||||
|
|
||||||
|
def selectionChange(): Unit = {
|
||||||
|
frameList.items = selected.frames
|
||||||
|
}
|
||||||
|
|
||||||
|
def playAnimation(): Unit = {
|
||||||
|
val images = frameList.items().map(f => f.img).toIndexedSeq
|
||||||
|
val animated = new AnimatedImage(images)
|
||||||
|
val duration = new Duration(50 * images.size)
|
||||||
|
val animator = new SpriteAnimation(viewpane, animated, duration)
|
||||||
|
animator.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
def addFrame(): Unit = {
|
||||||
|
//println("New frame")
|
||||||
|
//selected.frames += new FrameToken
|
||||||
|
}
|
||||||
|
|
||||||
|
def addFrame(img: StillImg, index: Int): Unit = {
|
||||||
|
selected.frames += new Frame(img, currFile, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
def removeFrame(): Unit = {
|
||||||
|
frameList.items() --= frameList.selectionModel().getSelectedItems
|
||||||
|
}
|
||||||
|
|
||||||
|
def selectFrame(): Unit = {
|
||||||
|
val frame = frameList.selectionModel().getSelectedItem
|
||||||
|
frame.img.draw(viewpane, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def saveTo(file: File): Unit = {
|
||||||
|
val aniMap = animations.map(a => (a.name(), a.toToken)).toMap
|
||||||
|
YamlHelper.writeMap(new FileOutputStream(file), aniMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def openFrom(file: File): Unit = {
|
||||||
|
val aniMap = YamlHelper.extractMap[AnimationToken](new FileInputStream(file))
|
||||||
|
aniMap.foreach{case (name, token) => {
|
||||||
|
addAnimation(name)
|
||||||
|
val frames = AnimationBuilder.toFrames(token, new File(imgdir))
|
||||||
|
animationList.selectionModel().getSelectedItem.frames ++= frames
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def selected: ObsAnimation = {
|
||||||
|
animationList.selectionModel().getSelectedItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object AnimationBuilder {
|
||||||
|
class ABA extends Application {
|
||||||
|
override def start(primaryStage: Stage): Unit = {
|
||||||
|
val frameLoader = new FXMLLoader(getClass.getResource("App.fxml"))
|
||||||
|
val root: Parent = frameLoader.load()
|
||||||
|
val controller = frameLoader.getController[App]()
|
||||||
|
|
||||||
|
val builderLoader = new FXMLLoader(getClass.getResource("AnimationBuilder.fxml"))
|
||||||
|
val builder: Parent = builderLoader.load()
|
||||||
|
val builderController = builderLoader.getController[AnimationBuilder]()
|
||||||
|
|
||||||
|
controller.pane.children = builder
|
||||||
|
controller.builder = builderController
|
||||||
|
|
||||||
|
val scene: Scene = new Scene(root)
|
||||||
|
primaryStage.setScene(scene)
|
||||||
|
primaryStage.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def main(args: Array[String]): Unit = {
|
||||||
|
Application.launch(classOf[ABA], args: _*)
|
||||||
|
}
|
||||||
|
|
||||||
|
def toFrames(token: AnimationToken, dir: File) = {
|
||||||
|
val file = new File(s"${dir.getPath}/${token.filename}")
|
||||||
|
val palette = Palette.square(new FileInputStream(file), 5)
|
||||||
|
val images = palette.images
|
||||||
|
token.indices.map(i => new Frame(images(i), file, i))
|
||||||
|
}
|
||||||
|
}
|
||||||
63
Builder/src/fmon/builder/App.fxml
Normal file
63
Builder/src/fmon/builder/App.fxml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.Menu?>
|
||||||
|
<?import javafx.scene.control.MenuBar?>
|
||||||
|
<?import javafx.scene.control.MenuItem?>
|
||||||
|
<?import javafx.scene.control.SeparatorMenuItem?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
<VBox prefHeight="400.0" prefWidth="640.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fmon.builder.App">
|
||||||
|
<children>
|
||||||
|
<MenuBar VBox.vgrow="NEVER">
|
||||||
|
<menus>
|
||||||
|
<Menu mnemonicParsing="false" text="File">
|
||||||
|
<items>
|
||||||
|
<MenuItem mnemonicParsing="false" text="New" />
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#open" text="Open…" />
|
||||||
|
<Menu mnemonicParsing="false" text="Open Recent" />
|
||||||
|
<SeparatorMenuItem mnemonicParsing="false" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Close" />
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#save" text="Save" />
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#saveAs" text="Save As…" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Revert" />
|
||||||
|
<SeparatorMenuItem mnemonicParsing="false" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Preferences…" />
|
||||||
|
<SeparatorMenuItem mnemonicParsing="false" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Quit" />
|
||||||
|
</items>
|
||||||
|
</Menu>
|
||||||
|
<Menu mnemonicParsing="false" text="Edit">
|
||||||
|
<items>
|
||||||
|
<MenuItem mnemonicParsing="false" text="Undo" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Redo" />
|
||||||
|
<SeparatorMenuItem mnemonicParsing="false" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Cut" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Copy" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Paste" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Delete" />
|
||||||
|
<SeparatorMenuItem mnemonicParsing="false" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Select All" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Unselect All" />
|
||||||
|
</items>
|
||||||
|
</Menu>
|
||||||
|
<Menu mnemonicParsing="false" text="Help">
|
||||||
|
<items>
|
||||||
|
<MenuItem mnemonicParsing="false" text="About MyHelloApp" />
|
||||||
|
</items>
|
||||||
|
</Menu>
|
||||||
|
</menus>
|
||||||
|
</MenuBar>
|
||||||
|
<AnchorPane fx:id="pane" maxHeight="-1.0" maxWidth="-1.0" prefHeight="-1.0" prefWidth="-1.0" VBox.vgrow="ALWAYS">
|
||||||
|
<children>
|
||||||
|
<Label alignment="CENTER" layoutX="155.0" layoutY="177.0" style=" " text="Drag components from Library here…" textAlignment="CENTER" textFill="#9f9f9f" wrapText="false">
|
||||||
|
<font>
|
||||||
|
<Font size="18.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
84
Builder/src/fmon/builder/App.scala
Normal file
84
Builder/src/fmon/builder/App.scala
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
package fmon.builder
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.util.prefs.Preferences
|
||||||
|
|
||||||
|
import scala.util.Properties
|
||||||
|
|
||||||
|
import javafx.fxml.FXML
|
||||||
|
import javafx.fxml.FXMLLoader
|
||||||
|
import javafx.fxml.JavaFXBuilderFactory
|
||||||
|
import javafx.scene.{control => jfxsc, layout => jfxsl}
|
||||||
|
|
||||||
|
import scalafx.Includes._
|
||||||
|
import scalafx.stage.FileChooser
|
||||||
|
import FileChooser.ExtensionFilter
|
||||||
|
|
||||||
|
trait Savable {
|
||||||
|
def saveTo(file: File): Unit
|
||||||
|
def openFrom(file: File): Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
class App {
|
||||||
|
@FXML var pane: jfxsl.Pane = _
|
||||||
|
|
||||||
|
var builder: Savable = _
|
||||||
|
var lastFile: File = _
|
||||||
|
|
||||||
|
def lastDir(): String = {
|
||||||
|
val prefs = Preferences.userNodeForPackage(classOf[App])
|
||||||
|
prefs.get("dir", Properties.userDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
def rememberDir(file: File): Unit = {
|
||||||
|
val dir = if (file.isDirectory()) file.getPath else file.getParent
|
||||||
|
if (dir != lastDir) {
|
||||||
|
val prefs = Preferences.userNodeForPackage(classOf[App])
|
||||||
|
prefs.put("dir", dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def open(): Unit = {
|
||||||
|
val fileChooser = new FileChooser {
|
||||||
|
title = "Open Resource File"
|
||||||
|
initialDirectory = new File(lastDir())
|
||||||
|
extensionFilters ++= Seq(
|
||||||
|
new ExtensionFilter("YAML Files", "*.yaml"),
|
||||||
|
new ExtensionFilter("All Files", "*.*")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val selectedFile = fileChooser.showOpenDialog(null)
|
||||||
|
if (selectedFile != null) {
|
||||||
|
rememberDir(selectedFile)
|
||||||
|
builder openFrom selectedFile
|
||||||
|
lastFile = selectedFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def save(): Unit = {
|
||||||
|
if (lastFile != null) {
|
||||||
|
builder saveTo lastFile
|
||||||
|
} else {
|
||||||
|
saveAs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def saveAs(): Unit = {
|
||||||
|
val fileChooser = new FileChooser {
|
||||||
|
title = "Open Resource File"
|
||||||
|
initialDirectory = new File(lastDir())
|
||||||
|
extensionFilters ++= Seq(
|
||||||
|
new ExtensionFilter("YAML Files", "*.yaml"),
|
||||||
|
new ExtensionFilter("All Files", "*.*")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val selectedFile = fileChooser.showSaveDialog(null)
|
||||||
|
if (selectedFile != null) {
|
||||||
|
rememberDir(selectedFile)
|
||||||
|
builder saveTo selectedFile
|
||||||
|
lastFile = selectedFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
93
Builder/src/fmon/builder/ElementBuilder.fxml
Normal file
93
Builder/src/fmon/builder/ElementBuilder.fxml
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ColorPicker?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.ListView?>
|
||||||
|
<?import javafx.scene.control.Separator?>
|
||||||
|
<?import javafx.scene.control.TextField?>
|
||||||
|
<?import javafx.scene.layout.BorderPane?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
|
||||||
|
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fmon.builder.ElementBuilder">
|
||||||
|
<left>
|
||||||
|
<VBox BorderPane.alignment="CENTER">
|
||||||
|
<children>
|
||||||
|
<ListView fx:id="elements" prefWidth="200.0" />
|
||||||
|
<HBox prefHeight="0.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<Button fx:id="add" mnemonicParsing="false" onAction="#addElement" text="+" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</left>
|
||||||
|
<center>
|
||||||
|
<VBox prefHeight="200.0" prefWidth="100.0" BorderPane.alignment="CENTER">
|
||||||
|
<children>
|
||||||
|
<GridPane>
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Label text="Element Name:" />
|
||||||
|
<ColorPicker fx:id="bgColor" onAction="#updateBg" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||||
|
<Label text="Background Color:" GridPane.rowIndex="1" />
|
||||||
|
<Label text="Text Color:" GridPane.rowIndex="2" />
|
||||||
|
<ColorPicker fx:id="textColor" onAction="#updateFont" GridPane.columnIndex="1" GridPane.rowIndex="2" />
|
||||||
|
<Label text="Symbol:" GridPane.rowIndex="3" />
|
||||||
|
<Label text="Example" GridPane.rowIndex="4" />
|
||||||
|
<Button fx:id="button" mnemonicParsing="false" text="Button" GridPane.columnIndex="1" GridPane.rowIndex="4" />
|
||||||
|
<TextField fx:id="name" onAction="#updateName" promptText="Element" GridPane.columnIndex="1" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
<Separator prefHeight="0.0" prefWidth="400.0" />
|
||||||
|
<GridPane prefHeight="252.0" prefWidth="400.0">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints maxHeight="155.0" minHeight="10.0" prefHeight="38.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="186.0" minHeight="10.0" prefHeight="186.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="26.0" minHeight="0.0" prefHeight="26.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Label text="Immune" />
|
||||||
|
<Label text="Resist" GridPane.columnIndex="2" />
|
||||||
|
<Label text="Normal" GridPane.columnIndex="4" />
|
||||||
|
<Label text="Weak" GridPane.columnIndex="6" />
|
||||||
|
<ListView fx:id="immuneView" prefHeight="200.0" prefWidth="200.0" GridPane.columnSpan="2" GridPane.rowIndex="1" />
|
||||||
|
<ListView fx:id="resistView" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.columnSpan="2" GridPane.rowIndex="1" />
|
||||||
|
<ListView fx:id="normalView" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="4" GridPane.columnSpan="2" GridPane.rowIndex="1" />
|
||||||
|
<ListView fx:id="weakView" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="6" GridPane.columnSpan="2" GridPane.rowIndex="1" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#immune2Resist" text="->" GridPane.columnIndex="1" GridPane.rowIndex="2" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#resist2Immune" text="<-" GridPane.columnIndex="2" GridPane.rowIndex="2" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#resist2Normal" text="->" GridPane.columnIndex="3" GridPane.rowIndex="2" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#normal2Resist" text="<-" GridPane.columnIndex="4" GridPane.rowIndex="2" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#normal2Weak" text="->" GridPane.columnIndex="5" GridPane.rowIndex="2" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#weak2Normal" text="<-" GridPane.columnIndex="6" GridPane.rowIndex="2" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</center>
|
||||||
|
</BorderPane>
|
||||||
225
Builder/src/fmon/builder/ElementBuilder.scala
Normal file
225
Builder/src/fmon/builder/ElementBuilder.scala
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
package fmon.builder
|
||||||
|
|
||||||
|
import java.io._
|
||||||
|
|
||||||
|
import javafx.application.Application
|
||||||
|
import javafx.fxml.FXML
|
||||||
|
import javafx.fxml.FXMLLoader
|
||||||
|
import javafx.fxml.JavaFXBuilderFactory
|
||||||
|
import javafx.scene.{control => jfxsc, Parent, Scene}
|
||||||
|
import javafx.stage.Stage
|
||||||
|
|
||||||
|
import scalafx.Includes._
|
||||||
|
import scalafx.beans.property._
|
||||||
|
import scalafx.collections.ObservableBuffer
|
||||||
|
import scalafx.scene.control._
|
||||||
|
import scalafx.scene.paint.Color
|
||||||
|
import scalafx.scene.layout._
|
||||||
|
|
||||||
|
import org.yaml.snakeyaml.nodes._
|
||||||
|
import org.yaml.snakeyaml.nodes.Tag
|
||||||
|
import org.yaml.snakeyaml.constructor._
|
||||||
|
import org.yaml.snakeyaml.representer._
|
||||||
|
|
||||||
|
import fmon.stat.{Effectiveness, Element}
|
||||||
|
import fmon.util.YamlHelper
|
||||||
|
import Effectiveness._
|
||||||
|
|
||||||
|
import TableColumn._
|
||||||
|
|
||||||
|
class ObsElement(name_ : String, bgColor_ : Color = Color.Gray, fontColor_ : Color = Color.Black) {
|
||||||
|
val name = new StringProperty(this, "name", name_)
|
||||||
|
val bgColor = new ObjectProperty[Color](this, "bgColor", bgColor_)
|
||||||
|
val fontColor = new ObjectProperty[Color](this, "fontColor", fontColor_)
|
||||||
|
|
||||||
|
val immune = new ObservableBuffer[ObsElement]()
|
||||||
|
val resist = new ObservableBuffer[ObsElement]()
|
||||||
|
val normal = new ObservableBuffer[ObsElement]()
|
||||||
|
val weak = new ObservableBuffer[ObsElement]()
|
||||||
|
|
||||||
|
def asElement = {
|
||||||
|
def toMap(buffer: ObservableBuffer[ObsElement], mult: Effectiveness) = {
|
||||||
|
buffer.map(e => (e.name(), mult)).toMap
|
||||||
|
}
|
||||||
|
val effect = toMap(immune, Immune) ++ toMap(resist, Resist) ++ toMap(normal, Regular) ++ toMap(weak, Weak)
|
||||||
|
new Element(name(), bgColor(), fontColor(), effect)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def toString = name()
|
||||||
|
}
|
||||||
|
|
||||||
|
class ColorRepresenter extends Representer {
|
||||||
|
representers.put(classOf[Color], new ColorRepresent)
|
||||||
|
|
||||||
|
class ColorRepresent extends Represent {
|
||||||
|
override def representData(data: AnyRef): Node = {
|
||||||
|
val color = data.asInstanceOf[Color]
|
||||||
|
representScalar(new Tag("!color"), color.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ColorConstructor extends Constructor {
|
||||||
|
yamlConstructors.put(new Tag("!color"), new ConstructColor)
|
||||||
|
|
||||||
|
class ConstructColor extends AbstractConstruct {
|
||||||
|
override def construct(node: Node): AnyRef = {
|
||||||
|
val string = constructScalar(node.asInstanceOf[ScalarNode])
|
||||||
|
return Color.valueOf(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ElementBuilder extends Savable {
|
||||||
|
@FXML var elements: jfxsc.ListView[ObsElement] = _
|
||||||
|
@FXML var add: jfxsc.Button = _
|
||||||
|
//@FXML var matchups: jfxsc.TableView[ObsElement] = _
|
||||||
|
var elementList = ObservableBuffer[ObsElement]()
|
||||||
|
|
||||||
|
@FXML var immuneView: jfxsc.ListView[ObsElement] = _
|
||||||
|
@FXML var resistView: jfxsc.ListView[ObsElement] = _
|
||||||
|
@FXML var normalView: jfxsc.ListView[ObsElement] = _
|
||||||
|
@FXML var weakView: jfxsc.ListView[ObsElement] = _
|
||||||
|
|
||||||
|
@FXML var name: jfxsc.TextField = _
|
||||||
|
@FXML var bgColor: jfxsc.ColorPicker = _
|
||||||
|
@FXML var textColor: jfxsc.ColorPicker = _
|
||||||
|
@FXML var button: jfxsc.Button = _
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
def initialize(): Unit = {
|
||||||
|
elements.items = elementList
|
||||||
|
elementList ++= Seq(new ObsElement("Fire"), new ObsElement("Water"), new ObsElement("Leaf"))
|
||||||
|
elements.selectionModel().selectedIndex.onChange(selectionChange)
|
||||||
|
elements.selectionModel().selectFirst()
|
||||||
|
|
||||||
|
elementList.foreach(e => elementList.foreach(f => e.normal += f))
|
||||||
|
}
|
||||||
|
|
||||||
|
def selectionChange(): Unit = {
|
||||||
|
val element = elementList(elements.selectionModel().selectedIndex())
|
||||||
|
name.text = element.name()
|
||||||
|
bgColor.value = element.bgColor()
|
||||||
|
textColor.value = element.fontColor()
|
||||||
|
|
||||||
|
immuneView.items = element.immune
|
||||||
|
resistView.items = element.resist
|
||||||
|
normalView.items = element.normal
|
||||||
|
weakView.items = element.weak
|
||||||
|
updateFont()
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateName(): Unit = {
|
||||||
|
button.text = name.text()
|
||||||
|
setChanges()
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateBg(): Unit = {
|
||||||
|
updateFont()
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateFont(): Unit = {
|
||||||
|
val bg = bgColor.value().toString().replaceFirst("0x", "#")
|
||||||
|
val color = textColor.value().toString().replaceFirst("0x", "#")
|
||||||
|
button.style = s"-fx-background-color: ${bg}; -fx-text-fill: ${color}"
|
||||||
|
setChanges()
|
||||||
|
}
|
||||||
|
|
||||||
|
def setChanges(): Unit = {
|
||||||
|
val element = elementList(elements.selectionModel().selectedIndex())
|
||||||
|
element.name.value = name.text()
|
||||||
|
element.bgColor.value = bgColor.value()
|
||||||
|
element.fontColor.value = textColor.value()
|
||||||
|
elements.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
def addElement(): Unit = {
|
||||||
|
elementList += new ObsElement("New Element")
|
||||||
|
println(elements.items().size())
|
||||||
|
}
|
||||||
|
|
||||||
|
def immune2Resist(): Unit = {
|
||||||
|
val items = immuneView.selectionModel().getSelectedItems
|
||||||
|
resistView.items() ++= items
|
||||||
|
immuneView.items() --= items
|
||||||
|
}
|
||||||
|
|
||||||
|
def resist2Immune(): Unit = {
|
||||||
|
val items = resistView.selectionModel().getSelectedItems
|
||||||
|
immuneView.items() ++= items
|
||||||
|
resistView.items() --= items
|
||||||
|
}
|
||||||
|
|
||||||
|
def resist2Normal(): Unit = {
|
||||||
|
val items = resistView.selectionModel().getSelectedItems
|
||||||
|
normalView.items() ++= items
|
||||||
|
resistView.items() --= items
|
||||||
|
}
|
||||||
|
|
||||||
|
def normal2Resist(): Unit = {
|
||||||
|
val items = normalView.selectionModel().getSelectedItems
|
||||||
|
resistView.items() ++= items
|
||||||
|
normalView.items() --= items
|
||||||
|
}
|
||||||
|
|
||||||
|
def normal2Weak(): Unit = {
|
||||||
|
val items = normalView.selectionModel().getSelectedItems
|
||||||
|
weakView.items() ++= items
|
||||||
|
normalView.items() --= items
|
||||||
|
}
|
||||||
|
|
||||||
|
def weak2Normal(): Unit = {
|
||||||
|
val items = weakView.selectionModel().getSelectedItems
|
||||||
|
normalView.items() ++= items
|
||||||
|
weakView.items() --= items
|
||||||
|
}
|
||||||
|
|
||||||
|
override def saveTo(file: File): Unit = {
|
||||||
|
val elementMap = elementList.map(e => (e.name(), e.asElement)).toMap
|
||||||
|
YamlHelper.writeMap(new FileOutputStream(file), elementMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def openFrom(file: File): Unit = {
|
||||||
|
val elementMap = YamlHelper.extractMap[Element](new FileInputStream(file))
|
||||||
|
val es = elementMap.values.map(e => new ObsElement(e.name, e.bgColor, e.fontColor)).toSeq
|
||||||
|
es.foreach(eo => es.foreach(fo => {
|
||||||
|
val e = elementMap(eo.name())
|
||||||
|
val f = elementMap(fo.name())
|
||||||
|
e.effect.getOrElse(f.name, Regular) match {
|
||||||
|
case Immune => eo.immune += fo
|
||||||
|
case Resist => eo.resist += fo
|
||||||
|
case Regular => eo.normal += fo
|
||||||
|
case Weak => eo.weak += fo
|
||||||
|
case _ => throw new IllegalArgumentException
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
elementList.clear()
|
||||||
|
elementList ++= es
|
||||||
|
elements.selectionModel().selectFirst()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object ElementBuilder {
|
||||||
|
class EBA extends Application {
|
||||||
|
override def start(primaryStage: Stage): Unit = {
|
||||||
|
val frameLoader = new FXMLLoader(getClass.getResource("App.fxml"))
|
||||||
|
val root: Parent = frameLoader.load()
|
||||||
|
val controller = frameLoader.getController[App]()
|
||||||
|
|
||||||
|
val builderLoader = new FXMLLoader(getClass.getResource("ElementBuilder.fxml"))
|
||||||
|
val builder: Parent = builderLoader.load()
|
||||||
|
val builderController = builderLoader.getController[ElementBuilder]()
|
||||||
|
|
||||||
|
controller.pane.children = builder
|
||||||
|
controller.builder = builderController
|
||||||
|
|
||||||
|
val scene: Scene = new Scene(root)
|
||||||
|
primaryStage.setScene(scene)
|
||||||
|
primaryStage.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def main(args: Array[String]): Unit = {
|
||||||
|
Application.launch(classOf[EBA], args: _*)
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Builder/src/fmon/builder/FilenameConverter.scala
Normal file
9
Builder/src/fmon/builder/FilenameConverter.scala
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package fmon.builder
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import scalafx.util.StringConverter
|
||||||
|
|
||||||
|
object FilenameConverter extends StringConverter[File] {
|
||||||
|
override def toString(f: File) = if (f != null) f.getName else "<none>"
|
||||||
|
override def fromString(s: String) = if (s == "<none>") null else new File(s)
|
||||||
|
}
|
||||||
112
Builder/src/fmon/builder/FormeBuilder.fxml
Normal file
112
Builder/src/fmon/builder/FormeBuilder.fxml
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ComboBox?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.ListView?>
|
||||||
|
<?import javafx.scene.control.Spinner?>
|
||||||
|
<?import javafx.scene.control.TextField?>
|
||||||
|
<?import javafx.scene.layout.BorderPane?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
|
||||||
|
|
||||||
|
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fmon.builder.FormeBuilder">
|
||||||
|
<left>
|
||||||
|
<VBox prefHeight="200.0" prefWidth="100.0" BorderPane.alignment="CENTER">
|
||||||
|
<children>
|
||||||
|
<ListView fx:id="formes" prefWidth="200.0" />
|
||||||
|
<HBox prefHeight="16.0" prefWidth="100.0">
|
||||||
|
<children>
|
||||||
|
<Button mnemonicParsing="false" onAction="#addForme" text="+" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</left>
|
||||||
|
<center>
|
||||||
|
<GridPane BorderPane.alignment="CENTER">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="95.0" minWidth="10.0" prefWidth="78.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="135.0" minWidth="10.0" prefWidth="98.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="127.0" minWidth="10.0" prefWidth="62.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="162.0" minWidth="10.0" prefWidth="125.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="137.0" minWidth="10.0" prefWidth="137.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Label text="Name:" />
|
||||||
|
<TextField fx:id="name" promptText="Name" GridPane.columnIndex="1" />
|
||||||
|
<TextField fx:id="height" promptText="Height (m)" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||||
|
<TextField fx:id="weight" promptText="Weight (kg)" GridPane.columnIndex="1" GridPane.rowIndex="2" />
|
||||||
|
<Label text="Height:" GridPane.rowIndex="1" />
|
||||||
|
<Label text="Weight:" GridPane.rowIndex="2" />
|
||||||
|
<Label text="Level Rate:" GridPane.rowIndex="3" />
|
||||||
|
<ComboBox fx:id="levelRate" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="3" />
|
||||||
|
<Label text="Catch Rate:" GridPane.rowIndex="4" />
|
||||||
|
<ComboBox fx:id="genderRatio" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="5" />
|
||||||
|
<Label text="Gender Ratio:" GridPane.rowIndex="5" />
|
||||||
|
<ListView fx:id="abilities" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="3" />
|
||||||
|
<Label text="Abilities:" GridPane.columnIndex="2" />
|
||||||
|
<HBox prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="3" GridPane.rowIndex="1">
|
||||||
|
<children>
|
||||||
|
<ComboBox fx:id="abilityList" prefHeight="25.0" prefWidth="100.0" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#addAbility" text="+" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<HBox prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="3" GridPane.rowIndex="3">
|
||||||
|
<children>
|
||||||
|
<ComboBox fx:id="hiddenAbilityList" prefHeight="25.0" prefWidth="100.0" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#addHiddenAbility" text="+" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<ListView fx:id="hiddenAbilities" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="3" GridPane.rowIndex="2" />
|
||||||
|
<Label text="Hidden:" GridPane.columnIndex="2" GridPane.rowIndex="2" />
|
||||||
|
<Label text="Egg Group:" GridPane.columnIndex="2" GridPane.rowIndex="4" />
|
||||||
|
<ListView fx:id="eggGroup" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="3" GridPane.rowIndex="4" />
|
||||||
|
<HBox prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="3" GridPane.rowIndex="5">
|
||||||
|
<children>
|
||||||
|
<ComboBox fx:id="eggGroupList" prefHeight="25.0" prefWidth="100.0" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#addEggGroup" text="+" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<ListView fx:id="movePool" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="4" GridPane.rowIndex="3" GridPane.rowSpan="8" />
|
||||||
|
<HBox prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="4" GridPane.rowIndex="11">
|
||||||
|
<children>
|
||||||
|
<ComboBox fx:id="moveList" prefHeight="25.0" prefWidth="110.0" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#addMove" text="+" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<Spinner fx:id="catchRate" GridPane.columnIndex="1" GridPane.rowIndex="4" />
|
||||||
|
<Spinner fx:id="hp" GridPane.columnIndex="1" GridPane.rowIndex="6" />
|
||||||
|
<Spinner fx:id="spd" GridPane.columnIndex="1" GridPane.rowIndex="11" />
|
||||||
|
<Spinner fx:id="patk" GridPane.columnIndex="1" GridPane.rowIndex="7" />
|
||||||
|
<Spinner fx:id="mdef" GridPane.columnIndex="1" GridPane.rowIndex="10" />
|
||||||
|
<Spinner fx:id="pdef" GridPane.columnIndex="1" GridPane.rowIndex="8" />
|
||||||
|
<Spinner fx:id="matk" GridPane.columnIndex="1" GridPane.rowIndex="9" />
|
||||||
|
<Label text="Hp" GridPane.rowIndex="6" />
|
||||||
|
<Label text="Phys Atk" GridPane.rowIndex="7" />
|
||||||
|
<Label text="Phys Def" GridPane.rowIndex="8" />
|
||||||
|
<Label text="Mag Atk" GridPane.rowIndex="9" />
|
||||||
|
<Label text="Mag Def" GridPane.rowIndex="10" />
|
||||||
|
<Label text="Speed" GridPane.rowIndex="11" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
</center>
|
||||||
|
</BorderPane>
|
||||||
98
Builder/src/fmon/builder/FormeBuilder.scala
Normal file
98
Builder/src/fmon/builder/FormeBuilder.scala
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package fmon.builder
|
||||||
|
|
||||||
|
import java.io._
|
||||||
|
|
||||||
|
import javafx.application.Application
|
||||||
|
import javafx.fxml.FXML
|
||||||
|
import javafx.fxml.FXMLLoader
|
||||||
|
import javafx.fxml.JavaFXBuilderFactory
|
||||||
|
import javafx.scene.{control => jfxsc, Parent, Scene}
|
||||||
|
import javafx.stage.Stage
|
||||||
|
|
||||||
|
import scalafx.Includes._
|
||||||
|
import scalafx.beans.property._
|
||||||
|
import scalafx.collections.ObservableBuffer
|
||||||
|
import scalafx.scene.control._
|
||||||
|
import scalafx.scene.paint.Color
|
||||||
|
import scalafx.scene.layout._
|
||||||
|
|
||||||
|
import fmon.stat.FormToken
|
||||||
|
|
||||||
|
class FormeBuilder extends Savable {
|
||||||
|
@FXML var formes: jfxsc.ListView[FormToken] = _
|
||||||
|
@FXML var name: jfxsc.TextField = _
|
||||||
|
@FXML var height: jfxsc.TextField = _
|
||||||
|
@FXML var weight: jfxsc.TextField = _
|
||||||
|
@FXML var levelRate: jfxsc.ComboBox[_] = _
|
||||||
|
@FXML var catchRate: jfxsc.Spinner[Int] = _
|
||||||
|
@FXML var genderRatio: jfxsc.ComboBox[_] = _
|
||||||
|
|
||||||
|
|
||||||
|
@FXML var abilities: jfxsc.ListView[_] = _
|
||||||
|
@FXML var abilityList: jfxsc.ComboBox[_] = _
|
||||||
|
@FXML var hiddenAbilities: jfxsc.ListView[_] = _
|
||||||
|
@FXML var hiddenAbilityList: jfxsc.ComboBox[_] = _
|
||||||
|
@FXML var eggGroup: jfxsc.ListView[_] = _
|
||||||
|
@FXML var eggGroupList: jfxsc.ComboBox[_] = _
|
||||||
|
|
||||||
|
@FXML var movePool: jfxsc.ListView[_] = _
|
||||||
|
@FXML var moveList: jfxsc.ComboBox[_] = _
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
def initialize(): Unit = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def addForme(): Unit = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def addAbility(): Unit = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def addHiddenAbility(): Unit = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def addEggGroup(): Unit = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def addMove(): Unit = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override def saveTo(file: File): Unit = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override def openFrom(file: File): Unit = {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object FormeBuilder {
|
||||||
|
class FBA extends Application {
|
||||||
|
override def start(primaryStage: Stage): Unit = {
|
||||||
|
val frameLoader = new FXMLLoader(getClass.getResource("App.fxml"))
|
||||||
|
val root: Parent = frameLoader.load()
|
||||||
|
val controller = frameLoader.getController[App]()
|
||||||
|
|
||||||
|
val builderLoader = new FXMLLoader(getClass.getResource("FormeBuilder.fxml"))
|
||||||
|
val builder: Parent = builderLoader.load()
|
||||||
|
val builderController = builderLoader.getController[FormeBuilder]()
|
||||||
|
|
||||||
|
controller.pane.children = builder
|
||||||
|
controller.builder = builderController
|
||||||
|
|
||||||
|
val scene: Scene = new Scene(root)
|
||||||
|
primaryStage.setScene(scene)
|
||||||
|
primaryStage.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def main(args: Array[String]): Unit = {
|
||||||
|
Application.launch(classOf[FBA], args: _*)
|
||||||
|
}
|
||||||
|
}
|
||||||
137
Builder/src/fmon/builder/MapBuilder.fxml
Normal file
137
Builder/src/fmon/builder/MapBuilder.fxml
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.Menu?>
|
||||||
|
<?import javafx.scene.control.MenuBar?>
|
||||||
|
<?import javafx.scene.control.MenuItem?>
|
||||||
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
|
<?import javafx.scene.control.SeparatorMenuItem?>
|
||||||
|
<?import javafx.scene.control.SplitPane?>
|
||||||
|
<?import javafx.scene.control.Tab?>
|
||||||
|
<?import javafx.scene.control.TabPane?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.Pane?>
|
||||||
|
<?import javafx.scene.layout.TilePane?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.paint.Color?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
<VBox prefHeight="600.0" prefWidth="900.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fmon.builder.MapBuilder">
|
||||||
|
<children>
|
||||||
|
<MenuBar VBox.vgrow="NEVER">
|
||||||
|
<menus>
|
||||||
|
<Menu mnemonicParsing="false" text="File">
|
||||||
|
<items>
|
||||||
|
<MenuItem mnemonicParsing="false" text="New" />
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#open" text="Open…" />
|
||||||
|
<Menu mnemonicParsing="false" text="Open Recent" />
|
||||||
|
<SeparatorMenuItem mnemonicParsing="false" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Close" />
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#save" text="Save" />
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#saveAs" text="Save As…" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Revert" />
|
||||||
|
<SeparatorMenuItem mnemonicParsing="false" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Preferences…" />
|
||||||
|
<SeparatorMenuItem mnemonicParsing="false" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Quit" />
|
||||||
|
</items>
|
||||||
|
</Menu>
|
||||||
|
<Menu mnemonicParsing="false" text="Edit">
|
||||||
|
<items>
|
||||||
|
<MenuItem mnemonicParsing="false" text="Undo" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Redo" />
|
||||||
|
<SeparatorMenuItem mnemonicParsing="false" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Cut" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Copy" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Paste" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Delete" />
|
||||||
|
<SeparatorMenuItem mnemonicParsing="false" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Select All" />
|
||||||
|
<MenuItem mnemonicParsing="false" text="Unselect All" />
|
||||||
|
<SeparatorMenuItem mnemonicParsing="false" />
|
||||||
|
<MenuItem mnemonicParsing="false" onAction="#onPropertiesDialog" text="Properties ..." />
|
||||||
|
</items>
|
||||||
|
</Menu>
|
||||||
|
<Menu mnemonicParsing="false" text="Help">
|
||||||
|
<items>
|
||||||
|
<MenuItem mnemonicParsing="false" text="About MyHelloApp" />
|
||||||
|
</items>
|
||||||
|
</Menu>
|
||||||
|
</menus>
|
||||||
|
</MenuBar>
|
||||||
|
<SplitPane dividerPositions="0.2505567928730512, 0.7505567928730512" focusTraversable="true" prefHeight="-1.0" prefWidth="-1.0" VBox.vgrow="ALWAYS">
|
||||||
|
<items>
|
||||||
|
<TabPane>
|
||||||
|
<tabs>
|
||||||
|
<Tab closable="false" text="A">
|
||||||
|
<content>
|
||||||
|
<ScrollPane>
|
||||||
|
<content>
|
||||||
|
<AnchorPane>
|
||||||
|
<children>
|
||||||
|
<TilePane fx:id="tileSelector" hgap="2.0" prefColumns="8" vgap="2.0" />
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</content>
|
||||||
|
</ScrollPane>
|
||||||
|
</content>
|
||||||
|
</Tab>
|
||||||
|
<Tab closable="false" text="B">
|
||||||
|
<content>
|
||||||
|
<ScrollPane>
|
||||||
|
<content>
|
||||||
|
<AnchorPane>
|
||||||
|
<children>
|
||||||
|
<TilePane fx:id="doodadSelector" hgap="2.0" prefColumns="8" vgap="2.0" />
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</content>
|
||||||
|
</ScrollPane>
|
||||||
|
</content>
|
||||||
|
</Tab>
|
||||||
|
</tabs>
|
||||||
|
</TabPane>
|
||||||
|
<ScrollPane prefHeight="-1.0" prefWidth="-1.0">
|
||||||
|
<content>
|
||||||
|
<AnchorPane id="Content" minHeight="-1.0" minWidth="-1.0" prefHeight="545.0" prefWidth="430.0">
|
||||||
|
<children>
|
||||||
|
<TilePane fx:id="gameMap" hgap="1.0" layoutX="14.0" layoutY="28.0" prefColumns="10" prefRows="10" vgap="1.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0" />
|
||||||
|
<TilePane fx:id="doodadMap" hgap="1.0" vgap="1.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0" />
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</content>
|
||||||
|
</ScrollPane>
|
||||||
|
<AnchorPane>
|
||||||
|
<children>
|
||||||
|
<Label alignment="CENTER" layoutX="14.0" layoutY="14.0" style=" " text="Details" textAlignment="CENTER" wrapText="false">
|
||||||
|
<font>
|
||||||
|
<Font size="18.0" fx:id="x1" />
|
||||||
|
</font>
|
||||||
|
<textFill>
|
||||||
|
<Color blue="0.624" green="0.624" red="0.624" fx:id="x2" />
|
||||||
|
</textFill></Label>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</items>
|
||||||
|
</SplitPane>
|
||||||
|
<HBox id="HBox" alignment="CENTER_LEFT" spacing="5.0" VBox.vgrow="NEVER">
|
||||||
|
<children>
|
||||||
|
<Label maxHeight="1.7976931348623157E308" maxWidth="-1.0" text="Left status" HBox.hgrow="ALWAYS">
|
||||||
|
<font>
|
||||||
|
<Font size="11.0" fx:id="x3" />
|
||||||
|
</font>
|
||||||
|
<textFill>
|
||||||
|
<Color blue="0.625" green="0.625" red="0.625" fx:id="x4" />
|
||||||
|
</textFill>
|
||||||
|
</Label>
|
||||||
|
<Pane prefHeight="-1.0" prefWidth="-1.0" HBox.hgrow="ALWAYS" />
|
||||||
|
<Label font="$x3" maxWidth="-1.0" text="Right status" textFill="$x4" HBox.hgrow="NEVER" />
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
|
||||||
|
</padding>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
276
Builder/src/fmon/builder/MapBuilder.scala
Normal file
276
Builder/src/fmon/builder/MapBuilder.scala
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
package fmon.builder
|
||||||
|
|
||||||
|
import java.io._
|
||||||
|
import java.util.prefs.Preferences
|
||||||
|
|
||||||
|
import scala.util.Properties
|
||||||
|
|
||||||
|
import javafx.application.Application
|
||||||
|
import javafx.fxml.FXML
|
||||||
|
import javafx.fxml.FXMLLoader
|
||||||
|
import javafx.fxml.JavaFXBuilderFactory
|
||||||
|
import javafx.scene.{control => jfxsc, layout => jfxsl, Parent, Scene}
|
||||||
|
import javafx.stage.Stage
|
||||||
|
|
||||||
|
import scalafx.Includes._
|
||||||
|
import scalafx.beans.property._
|
||||||
|
import scalafx.collections.ObservableBuffer
|
||||||
|
import scalafx.scene.control._
|
||||||
|
import scalafx.scene.image.ImageView
|
||||||
|
import scalafx.scene.input.MouseEvent
|
||||||
|
import scalafx.scene.paint.Color
|
||||||
|
import scalafx.scene.layout._
|
||||||
|
import scalafx.stage.FileChooser
|
||||||
|
import scalafx.stage.Modality
|
||||||
|
import FileChooser.ExtensionFilter
|
||||||
|
|
||||||
|
import fmon._
|
||||||
|
import fmon.builder.dialog.MapProperties
|
||||||
|
import fmon.draw.Palette
|
||||||
|
import fmon.draw.tile._
|
||||||
|
import fmon.util.{Direction, YamlHelper}
|
||||||
|
import fmon.world._
|
||||||
|
|
||||||
|
import Direction._
|
||||||
|
import Alert.AlertType
|
||||||
|
|
||||||
|
import scala.math.{min, max}
|
||||||
|
|
||||||
|
sealed trait MapBuilderState
|
||||||
|
|
||||||
|
case object GroundTile extends MapBuilderState
|
||||||
|
case object DoodadTile extends MapBuilderState
|
||||||
|
|
||||||
|
class MapBuilder {
|
||||||
|
@FXML var tileSelector: jfxsl.TilePane = _
|
||||||
|
@FXML var doodadSelector: jfxsl.TilePane = _
|
||||||
|
@FXML var gameMap: jfxsl.TilePane = _
|
||||||
|
@FXML var doodadMap: jfxsl.TilePane = _
|
||||||
|
|
||||||
|
var imgSeq: IndexedSeq[ImageView] = _
|
||||||
|
var doodadSeq: IndexedSeq[ImageView] = _
|
||||||
|
|
||||||
|
val tilesets = {
|
||||||
|
val dir = new File(Config.homedir + raw"Progena\Data")
|
||||||
|
YamlHelper.extractSeq[TilesetToken](new FileInputStream(new File(dir, "Tilesets.yaml"))).map(t => (t.name, t)).toMap
|
||||||
|
}
|
||||||
|
var tileset: Tileset = _
|
||||||
|
var currTile: AutoTile = _
|
||||||
|
var currState: MapBuilderState = GroundTile
|
||||||
|
|
||||||
|
var level: GameMap = _
|
||||||
|
|
||||||
|
var lastFile: File = _
|
||||||
|
|
||||||
|
def lastDir(): String = {
|
||||||
|
val prefs = Preferences.userNodeForPackage(classOf[MapBuilder])
|
||||||
|
prefs.get("dir", Properties.userDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
def rememberDir(file: File): Unit = {
|
||||||
|
val dir = if (file.isDirectory()) file.getPath else file.getParent
|
||||||
|
if (dir != lastDir) {
|
||||||
|
val prefs = Preferences.userNodeForPackage(classOf[MapBuilder])
|
||||||
|
prefs.put("dir", dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
def initialize(): Unit = {
|
||||||
|
val resourceDir = new File(Config.homedir + raw"Progena\Resources\tilesets")
|
||||||
|
tileset = tilesets("Outside").load(resourceDir)
|
||||||
|
level = new GameMap(10, 10, tileset)
|
||||||
|
setup()
|
||||||
|
}
|
||||||
|
|
||||||
|
def setup(): Unit = {
|
||||||
|
|
||||||
|
val icons = tileset.groundTiles.map(t => {
|
||||||
|
val view = new ImageView(t.icon.croppedImage())
|
||||||
|
view.handleEvent(MouseEvent.Any) {
|
||||||
|
e: MouseEvent => if (e.eventType == MouseEvent.MouseClicked) {
|
||||||
|
currTile = t
|
||||||
|
currState = GroundTile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
view.delegate
|
||||||
|
})
|
||||||
|
tileSelector.children.clear()
|
||||||
|
tileSelector.children ++= icons
|
||||||
|
currTile = tileset.groundTiles.head
|
||||||
|
|
||||||
|
val doodads = tileset.doodadTiles.map(d => {
|
||||||
|
val view = new ImageView(d.icon.croppedImage())
|
||||||
|
view.handleEvent(MouseEvent.Any) {
|
||||||
|
e: MouseEvent => if (e.eventType == MouseEvent.MouseClicked) {
|
||||||
|
currTile = d
|
||||||
|
currState = DoodadTile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
view.delegate
|
||||||
|
})
|
||||||
|
doodadSelector.children.clear()
|
||||||
|
doodadSelector.children ++= doodads
|
||||||
|
|
||||||
|
imgSeq = for (y <- 0 until level.height; x <- 0 until level.width) yield {
|
||||||
|
val view = new ImageView(level(x, y).icon.croppedImage()) // TODO Tile with adjacent
|
||||||
|
view.handleEvent(MouseEvent.Any) {
|
||||||
|
e: MouseEvent => if (e.eventType == MouseEvent.MouseClicked) {
|
||||||
|
val index = point2index(x, y)
|
||||||
|
currState match {
|
||||||
|
case GroundTile => {
|
||||||
|
level.tiles = level.tiles.updated(index, currTile)
|
||||||
|
autotile(x, y)
|
||||||
|
}
|
||||||
|
case DoodadTile => {
|
||||||
|
level.doodads = level.doodads.updated(index, currTile)
|
||||||
|
doodadSeq(index).image = level.doodads(index).icon.croppedImage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
view
|
||||||
|
}
|
||||||
|
doodadSeq = for (y <- 0 until level.height; x <- 0 until level.width) yield {
|
||||||
|
val view = new ImageView(level.doodad(x, y).icon.croppedImage())
|
||||||
|
view
|
||||||
|
}
|
||||||
|
gameMap.children.clear()
|
||||||
|
gameMap.prefColumns = level.width
|
||||||
|
gameMap.prefRows = level.height
|
||||||
|
gameMap.children ++= imgSeq.map(_.delegate)
|
||||||
|
doodadMap.children.clear()
|
||||||
|
doodadMap.prefColumns = level.width
|
||||||
|
doodadMap.prefRows = level.height
|
||||||
|
doodadMap.children ++= doodadSeq.map(_.delegate)
|
||||||
|
doodadMap.mouseTransparent = true
|
||||||
|
|
||||||
|
for (y <- 0 until level.height; x <- 0 until level.width) {
|
||||||
|
smoothTile(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def autotile(x: Int, y: Int) = {
|
||||||
|
for (yy <- max(y - 1, 0) to min(y + 1, level.height - 1);
|
||||||
|
xx <- max(x - 1, 0) to min(x + 1, level.width - 1)) {
|
||||||
|
smoothTile(xx, yy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def smoothTile(x: Int, y: Int) = {
|
||||||
|
imgSeq(level.pt2index(x, y)).image = level.smoothed(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
def onPropertiesDialog: Unit = {
|
||||||
|
val fxmlLoader = new FXMLLoader(getClass().getResource("dialog/MapProperties.fxml"))
|
||||||
|
val parent: Parent = fxmlLoader.load()
|
||||||
|
val dialogController = fxmlLoader.getController[MapProperties]();
|
||||||
|
//dialogController.setAppMainObservableList(tvObservableList);
|
||||||
|
dialogController.init(level)
|
||||||
|
|
||||||
|
val dialog = new Alert(AlertType.Confirmation) {
|
||||||
|
title = "Map Properties"
|
||||||
|
dialogPane().content = parent
|
||||||
|
headerText = ""
|
||||||
|
graphic = null
|
||||||
|
}
|
||||||
|
val result = dialog.showAndWait()
|
||||||
|
println(result)
|
||||||
|
result match {
|
||||||
|
case Some(ButtonType.OK) => {
|
||||||
|
println(dialogController.width, dialogController.height)
|
||||||
|
level = new GameMap(dialogController.width, dialogController.height, tileset)
|
||||||
|
level.tiles = level.tiles.map(_ => currTile)
|
||||||
|
setup()
|
||||||
|
}
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
|
/*val scene = new Scene(parent, 300, 200);
|
||||||
|
val stage = new Stage();
|
||||||
|
stage.initModality(Modality.ApplicationModal);
|
||||||
|
stage.setScene(scene);
|
||||||
|
stage.showAndWait();*/
|
||||||
|
}
|
||||||
|
|
||||||
|
def save() = {
|
||||||
|
if (lastFile != null) {
|
||||||
|
level saveTo lastFile
|
||||||
|
} else {
|
||||||
|
saveAs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def saveAs() = {
|
||||||
|
val fileChooser = new FileChooser {
|
||||||
|
title = "Open Resource File"
|
||||||
|
initialDirectory = new File(lastDir())
|
||||||
|
extensionFilters ++= Seq(
|
||||||
|
new ExtensionFilter("MAP Files", "*.map"),
|
||||||
|
new ExtensionFilter("All Files", "*.*")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val selectedFile = fileChooser.showSaveDialog(null)
|
||||||
|
if (selectedFile != null) {
|
||||||
|
rememberDir(selectedFile)
|
||||||
|
level saveTo selectedFile
|
||||||
|
lastFile = selectedFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def open() = {
|
||||||
|
val fileChooser = new FileChooser {
|
||||||
|
title = "Open Resource File"
|
||||||
|
initialDirectory = new File(lastDir())
|
||||||
|
extensionFilters ++= Seq(
|
||||||
|
new ExtensionFilter("MAP Files", "*.map"),
|
||||||
|
new ExtensionFilter("All Files", "*.*")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val selectedFile = fileChooser.showOpenDialog(null)
|
||||||
|
if (selectedFile != null) {
|
||||||
|
rememberDir(selectedFile)
|
||||||
|
level = GameMap loadFrom selectedFile
|
||||||
|
tileset = level.tileset
|
||||||
|
setup()
|
||||||
|
lastFile = selectedFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def point2index(x: Int, y: Int) = level.pt2index(x, y)
|
||||||
|
|
||||||
|
def left(i: Int) = i - 1
|
||||||
|
def right(i: Int) = i + 1
|
||||||
|
def up(i: Int) = i - level.width
|
||||||
|
def down(i: Int) = i + level.width
|
||||||
|
}
|
||||||
|
|
||||||
|
object MapBuilder {
|
||||||
|
class MBA extends Application {
|
||||||
|
override def start(primaryStage: Stage): Unit = {
|
||||||
|
val frameLoader = new FXMLLoader(getClass.getResource("MapBuilder.fxml"))
|
||||||
|
val root: Parent = frameLoader.load()
|
||||||
|
val controller = frameLoader.getController[MapBuilder]()
|
||||||
|
|
||||||
|
//val builderLoader = new FXMLLoader(getClass.getResource("ElementBuilder.fxml"))
|
||||||
|
//val builder: Parent = builderLoader.load()
|
||||||
|
//val builderController = builderLoader.getController[ElementBuilder]()
|
||||||
|
|
||||||
|
//controller.pane.children = builder
|
||||||
|
//controller.builder = builderController
|
||||||
|
|
||||||
|
val scene: Scene = new Scene(root)
|
||||||
|
primaryStage.setScene(scene)
|
||||||
|
primaryStage.width = 1280
|
||||||
|
primaryStage.height = 800
|
||||||
|
primaryStage.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def main(args: Array[String]): Unit = {
|
||||||
|
Application.launch(classOf[MBA], args: _*)
|
||||||
|
}
|
||||||
|
}
|
||||||
92
Builder/src/fmon/builder/TilesetBuilder.fxml
Normal file
92
Builder/src/fmon/builder/TilesetBuilder.fxml
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.CheckBox?>
|
||||||
|
<?import javafx.scene.control.ComboBox?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.ListView?>
|
||||||
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
|
<?import javafx.scene.control.SplitPane?>
|
||||||
|
<?import javafx.scene.control.TextField?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.layout.TilePane?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
|
||||||
|
<SplitPane dividerPositions="0.3110367892976589, 0.5953177257525084" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fmon.builder.TilesetBuilder">
|
||||||
|
<items>
|
||||||
|
<VBox>
|
||||||
|
<children>
|
||||||
|
<ListView fx:id="tilesetList" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#addTileset" text="+" />
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
<GridPane hgap="2.0" vgap="2.0">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="72.0" minWidth="10.0" prefWidth="54.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="105.0" minWidth="10.0" prefWidth="105.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<ComboBox fx:id="a1Chooser" onAction="#setA1" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||||
|
<Label text="Tiles A1" GridPane.rowIndex="1" />
|
||||||
|
<Label text="Tiles A2" GridPane.rowIndex="2" />
|
||||||
|
<Label text="Tiles A3" GridPane.rowIndex="3" />
|
||||||
|
<Label text="Tiles A4" GridPane.rowIndex="4" />
|
||||||
|
<Label text="Tiles A5" GridPane.rowIndex="5" />
|
||||||
|
<Label text="Tiles B" GridPane.rowIndex="6" />
|
||||||
|
<Label text="Tiles C" GridPane.rowIndex="7" />
|
||||||
|
<ComboBox fx:id="a2Chooser" onAction="#setA2" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="2" />
|
||||||
|
<ComboBox fx:id="a3Chooser" onAction="#setA3" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="3" />
|
||||||
|
<ComboBox fx:id="a4Chooser" onAction="#setA4" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="4" />
|
||||||
|
<ComboBox fx:id="a5Chooser" onAction="#setA5" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="5" />
|
||||||
|
<ComboBox fx:id="bChooser" onAction="#setB" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="6" />
|
||||||
|
<ComboBox fx:id="cChooser" onAction="#setC" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="7" />
|
||||||
|
<Label text="Name" />
|
||||||
|
<TextField fx:id="nameField" onAction="#setName" promptText="Tileset Name" GridPane.columnIndex="1" />
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||||
|
</padding>
|
||||||
|
</GridPane>
|
||||||
|
<SplitPane dividerPositions="0.5" orientation="VERTICAL" prefWidth="160.0">
|
||||||
|
<items>
|
||||||
|
<ScrollPane>
|
||||||
|
<content>
|
||||||
|
<TilePane fx:id="tileView" hgap="1.0" prefColumns="8" vgap="1.0">
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="1.0" left="1.0" right="1.0" top="1.0" />
|
||||||
|
</padding></TilePane>
|
||||||
|
</content>
|
||||||
|
</ScrollPane>
|
||||||
|
<ScrollPane>
|
||||||
|
<content>
|
||||||
|
<VBox spacing="2.0">
|
||||||
|
<children>
|
||||||
|
<CheckBox fx:id="doesPassNorth" mnemonicParsing="false" onAction="#updateTile" text="Pass North" />
|
||||||
|
<CheckBox fx:id="doesPassEast" mnemonicParsing="false" onAction="#updateTile" text="Pass East" />
|
||||||
|
<CheckBox fx:id="doesPassSouth" mnemonicParsing="false" onAction="#updateTile" text="Pass South" />
|
||||||
|
<CheckBox fx:id="doesPassWest" mnemonicParsing="false" onAction="#updateTile" text="Pass West" />
|
||||||
|
<CheckBox fx:id="isCounter" mnemonicParsing="false" onAction="#updateTile" text="Talk Throuh (Counter)" />
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||||
|
</padding>
|
||||||
|
</VBox>
|
||||||
|
</content>
|
||||||
|
</ScrollPane>
|
||||||
|
</items>
|
||||||
|
</SplitPane>
|
||||||
|
</items>
|
||||||
|
</SplitPane>
|
||||||
323
Builder/src/fmon/builder/TilesetBuilder.scala
Normal file
323
Builder/src/fmon/builder/TilesetBuilder.scala
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
package fmon.builder
|
||||||
|
|
||||||
|
import java.io._
|
||||||
|
|
||||||
|
import javafx.application.Application
|
||||||
|
import javafx.fxml.FXML
|
||||||
|
import javafx.fxml.FXMLLoader
|
||||||
|
import javafx.fxml.JavaFXBuilderFactory
|
||||||
|
import javafx.scene.{control => jfxsc, layout => jfxsl, Parent, Scene}
|
||||||
|
import javafx.stage.Stage
|
||||||
|
|
||||||
|
import scalafx.Includes._
|
||||||
|
import scalafx.beans.property._
|
||||||
|
import scalafx.collections.ObservableBuffer
|
||||||
|
import scalafx.scene.control._
|
||||||
|
import scalafx.scene.image.ImageView
|
||||||
|
import scalafx.scene.input.MouseEvent
|
||||||
|
import scalafx.scene.paint.Color
|
||||||
|
import scalafx.scene.layout._
|
||||||
|
|
||||||
|
import fmon.Config
|
||||||
|
import fmon.draw.tile.{AutoTile, AutoTilePalette}
|
||||||
|
import fmon.util.YamlHelper
|
||||||
|
import fmon.world.{Tileset, TileInfo, TilesetToken}
|
||||||
|
|
||||||
|
class ObsTileset(name_ : String) {
|
||||||
|
val name = new StringProperty(this, "name", name_)
|
||||||
|
val a1 = new ObjectProperty[File](this, "a1", null)
|
||||||
|
val a2 = new ObjectProperty[File](this, "a2", null)
|
||||||
|
val a3 = new ObjectProperty[File](this, "a3", null)
|
||||||
|
val a4 = new ObjectProperty[File](this, "a4", null)
|
||||||
|
val a5 = new ObjectProperty[File](this, "a5", null)
|
||||||
|
|
||||||
|
val b = new ObjectProperty[File](this, "b", null)
|
||||||
|
val c = new ObjectProperty[File](this, "c", null)
|
||||||
|
|
||||||
|
val a1Size = new IntegerProperty(this, "a1size", 0)
|
||||||
|
val a2Size = new IntegerProperty(this, "a2size", 0)
|
||||||
|
val a3Size = new IntegerProperty(this, "a3size", 0)
|
||||||
|
val a4Size = new IntegerProperty(this, "a4size", 0)
|
||||||
|
val a5Size = new IntegerProperty(this, "a5size", 0)
|
||||||
|
val bSize = new IntegerProperty(this, "bsize", 0)
|
||||||
|
val cSize = new IntegerProperty(this, "csize", 0)
|
||||||
|
|
||||||
|
val tileInfo = ObservableBuffer[TileInfo]()
|
||||||
|
|
||||||
|
def toToken = {
|
||||||
|
def nm(file: File) = if (file != null) file.getName else null
|
||||||
|
println(tileInfo.size)
|
||||||
|
TilesetToken(name(), nm(a1()), nm(a2()), nm(a3()), nm(a4()), nm(a5()), nm(b()), nm(c()), IndexedSeq(tileInfo: _*))
|
||||||
|
}
|
||||||
|
|
||||||
|
override def toString = name()
|
||||||
|
}
|
||||||
|
|
||||||
|
class TilesetBuilder extends Savable {
|
||||||
|
@FXML var tilesetList: jfxsc.ListView[ObsTileset] = _
|
||||||
|
|
||||||
|
@FXML var nameField: jfxsc.TextField = _
|
||||||
|
@FXML var a1Chooser: jfxsc.ComboBox[File] = _
|
||||||
|
@FXML var a2Chooser: jfxsc.ComboBox[File] = _
|
||||||
|
@FXML var a3Chooser: jfxsc.ComboBox[File] = _
|
||||||
|
@FXML var a4Chooser: jfxsc.ComboBox[File] = _
|
||||||
|
@FXML var a5Chooser: jfxsc.ComboBox[File] = _
|
||||||
|
@FXML var bChooser: jfxsc.ComboBox[File] = _
|
||||||
|
@FXML var cChooser: jfxsc.ComboBox[File] = _
|
||||||
|
|
||||||
|
@FXML var tileView: jfxsl.TilePane = _
|
||||||
|
|
||||||
|
@FXML var doesPassNorth: jfxsc.CheckBox = _
|
||||||
|
@FXML var doesPassEast: jfxsc.CheckBox = _
|
||||||
|
@FXML var doesPassSouth: jfxsc.CheckBox = _
|
||||||
|
@FXML var doesPassWest: jfxsc.CheckBox = _
|
||||||
|
@FXML var isCounter: jfxsc.CheckBox = _
|
||||||
|
|
||||||
|
val tilesets = ObservableBuffer[ObsTileset]()
|
||||||
|
|
||||||
|
var a1Palette = IndexedSeq[AutoTile]()
|
||||||
|
var a2Palette = IndexedSeq[AutoTile]()
|
||||||
|
var a3Palette = IndexedSeq[AutoTile]()
|
||||||
|
var a4Palette = IndexedSeq[AutoTile]()
|
||||||
|
var a5Palette = IndexedSeq[AutoTile]()
|
||||||
|
var bPalette = IndexedSeq[AutoTile]()
|
||||||
|
var cPalette = IndexedSeq[AutoTile]()
|
||||||
|
|
||||||
|
val resourceDir = new File(Config.homedir + raw"Progena\Resources\tilesets")
|
||||||
|
val files = resourceDir.listFiles()
|
||||||
|
val filenames = files.map(f => (f.getName(), f)).toMap
|
||||||
|
|
||||||
|
var currTileIndex = 0
|
||||||
|
|
||||||
|
def currTileset = getSelected(tilesetList)
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
def initialize(): Unit = {
|
||||||
|
tilesetList.items = tilesets
|
||||||
|
tilesetList.selectionModel().selectedIndex.onChange(selectionChange)
|
||||||
|
a1Chooser.converter = FilenameConverter
|
||||||
|
a2Chooser.converter = FilenameConverter
|
||||||
|
a3Chooser.converter = FilenameConverter
|
||||||
|
a4Chooser.converter = FilenameConverter
|
||||||
|
a5Chooser.converter = FilenameConverter
|
||||||
|
bChooser.converter = FilenameConverter
|
||||||
|
cChooser.converter = FilenameConverter
|
||||||
|
|
||||||
|
a1Chooser.items() ++= null +: files.filter(f => f.getName.endsWith("A1.png"))
|
||||||
|
a2Chooser.items() ++= null +: files.filter(f => f.getName.endsWith("A2.png"))
|
||||||
|
a3Chooser.items() ++= null +: files.filter(f => f.getName.endsWith("A3.png"))
|
||||||
|
a4Chooser.items() ++= null +: files.filter(f => f.getName.endsWith("A4.png"))
|
||||||
|
a5Chooser.items() ++= null +: files.filter(f => f.getName.endsWith("A5.png"))
|
||||||
|
bChooser.items() ++= null +: files.filter(f => f.getName.endsWith("B.png"))
|
||||||
|
cChooser.items() ++= null +: files.filter(f => f.getName.endsWith("C.png"))
|
||||||
|
|
||||||
|
println(files.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
def addTileset(): Unit = {
|
||||||
|
tilesets += new ObsTileset("Tileset")
|
||||||
|
tilesetList.selectionModel().selectLast()
|
||||||
|
}
|
||||||
|
|
||||||
|
def selectionChange(): Unit = {
|
||||||
|
val ts = getSelected(tilesetList)
|
||||||
|
nameField.text = ts.name()
|
||||||
|
|
||||||
|
def safeSelect(box: ComboBox[File], file: File) = {
|
||||||
|
box.selectionModel().clearSelection()
|
||||||
|
box.selectionModel().select(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
safeSelect(a1Chooser, ts.a1())
|
||||||
|
safeSelect(a2Chooser, ts.a2())
|
||||||
|
safeSelect(a3Chooser, ts.a3())
|
||||||
|
safeSelect(a4Chooser, ts.a4())
|
||||||
|
safeSelect(a5Chooser, ts.a5())
|
||||||
|
safeSelect(bChooser, ts.b())
|
||||||
|
safeSelect(cChooser, ts.c())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def setName(): Unit = {
|
||||||
|
getSelected(tilesetList).name.value = nameField.text()
|
||||||
|
tilesetList.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
def setA1(): Unit = {
|
||||||
|
currTileset.a1.value = getSelected(a1Chooser)
|
||||||
|
a1Palette = if (getSelected(a1Chooser) == null) IndexedSeq() else AutoTilePalette.a2(getSelected(a1Chooser), Config.tileSize).tiles
|
||||||
|
if (a1Palette.size != currTileset.a1Size()) {
|
||||||
|
currTileset.tileInfo.removeRange(0, currTileset.a1Size())
|
||||||
|
currTileset.a1Size() = a1Palette.size
|
||||||
|
val info = a1Palette.map(_ => noPassInfo)
|
||||||
|
currTileset.tileInfo.insert(0, info : _*)
|
||||||
|
}
|
||||||
|
updateTileView()
|
||||||
|
}
|
||||||
|
|
||||||
|
def setA2(): Unit = {
|
||||||
|
currTileset.a2.value = getSelected(a2Chooser)
|
||||||
|
a2Palette = if (getSelected(a2Chooser) == null) IndexedSeq() else AutoTilePalette.a2(getSelected(a2Chooser), Config.tileSize).tiles
|
||||||
|
if (a2Palette.size != currTileset.a2Size()) {
|
||||||
|
val priorSize = currTileset.a1Size()
|
||||||
|
currTileset.tileInfo.removeRange(priorSize, priorSize + currTileset.a2Size())
|
||||||
|
currTileset.a2Size() = a1Palette.size
|
||||||
|
val info = a2Palette.map(_ => allPassInfo)
|
||||||
|
currTileset.tileInfo.insert(priorSize, info : _*)
|
||||||
|
}
|
||||||
|
updateTileView()
|
||||||
|
}
|
||||||
|
|
||||||
|
def setA3(): Unit = {
|
||||||
|
currTileset.a3.value = getSelected(a3Chooser)
|
||||||
|
a3Palette = if (getSelected(a3Chooser) == null) IndexedSeq() else AutoTilePalette.a3(getSelected(a3Chooser), Config.tileSize).tiles
|
||||||
|
if (a3Palette.size != currTileset.a3Size()) {
|
||||||
|
val priorSize = currTileset.a1Size() + currTileset.a2Size()
|
||||||
|
currTileset.tileInfo.removeRange(priorSize, priorSize + currTileset.a3Size())
|
||||||
|
currTileset.a3Size() = a1Palette.size
|
||||||
|
val info = a3Palette.map(_ => noPassInfo)
|
||||||
|
currTileset.tileInfo.insert(priorSize, info : _*)
|
||||||
|
}
|
||||||
|
updateTileView()
|
||||||
|
}
|
||||||
|
|
||||||
|
def setA4(): Unit = {
|
||||||
|
currTileset.a4.value = getSelected(a4Chooser)
|
||||||
|
a4Palette = if (getSelected(a4Chooser) == null) IndexedSeq() else AutoTilePalette.a4(getSelected(a4Chooser), Config.tileSize).tiles
|
||||||
|
if (a4Palette.size != currTileset.a4Size()) {
|
||||||
|
val priorSize = currTileset.a1Size() + currTileset.a2Size() + currTileset.a3Size()
|
||||||
|
currTileset.tileInfo.removeRange(priorSize, priorSize + currTileset.a4Size())
|
||||||
|
currTileset.a4Size() = a4Palette.size
|
||||||
|
val info = a4Palette.zipWithIndex.map{case (_, i) => if ((i / 8) % 2 == 0) allPassInfo else noPassInfo}
|
||||||
|
currTileset.tileInfo.insert(priorSize, info : _*)
|
||||||
|
}
|
||||||
|
updateTileView()
|
||||||
|
}
|
||||||
|
|
||||||
|
def setA5(): Unit = {
|
||||||
|
currTileset.a5.value = getSelected(a5Chooser)
|
||||||
|
a5Palette = if (getSelected(a5Chooser) == null) IndexedSeq() else AutoTilePalette.basic(getSelected(a5Chooser), Config.tileSize).tiles
|
||||||
|
if (a5Palette.size != currTileset.a5Size()) {
|
||||||
|
val priorSize = currTileset.a1Size() + currTileset.a2Size() + currTileset.a3Size() + currTileset.a4Size()
|
||||||
|
currTileset.tileInfo.removeRange(priorSize, priorSize + currTileset.a5Size())
|
||||||
|
currTileset.a5Size() = a5Palette.size
|
||||||
|
val info = a5Palette.map(_ => allPassInfo)
|
||||||
|
currTileset.tileInfo.insert(priorSize, info : _*)
|
||||||
|
}
|
||||||
|
updateTileView()
|
||||||
|
}
|
||||||
|
|
||||||
|
def setB(): Unit = {
|
||||||
|
currTileset.b.value = getSelected(bChooser)
|
||||||
|
bPalette = if (getSelected(bChooser) == null) IndexedSeq() else AutoTilePalette.partitioned(getSelected(bChooser), 2, Config.tileSize).tiles
|
||||||
|
if (bPalette.size != currTileset.bSize()) {
|
||||||
|
val priorSize = currTileset.a1Size() + currTileset.a2Size() + currTileset.a3Size() + currTileset.a4Size() + currTileset.a5Size()
|
||||||
|
currTileset.tileInfo.removeRange(priorSize, priorSize + currTileset.bSize())
|
||||||
|
currTileset.bSize() = bPalette.size
|
||||||
|
val info = bPalette.zipWithIndex.map{case (_, i) => if (i != 0) doodadInfo else allPassInfo}
|
||||||
|
currTileset.tileInfo.insert(priorSize, info : _*)
|
||||||
|
}
|
||||||
|
updateTileView()
|
||||||
|
}
|
||||||
|
|
||||||
|
def setC(): Unit = {
|
||||||
|
currTileset.c.value = getSelected(cChooser)
|
||||||
|
cPalette = if (getSelected(cChooser) == null) IndexedSeq() else AutoTilePalette.partitioned(getSelected(cChooser), 2, Config.tileSize).tiles
|
||||||
|
if (cPalette.size != currTileset.cSize()) {
|
||||||
|
val priorSize = currTileset.a1Size() + currTileset.a2Size() + currTileset.a3Size() + currTileset.a4Size() + currTileset.a5Size() + currTileset.bSize()
|
||||||
|
currTileset.tileInfo.removeRange(priorSize, priorSize + currTileset.cSize())
|
||||||
|
currTileset.cSize() = cPalette.size
|
||||||
|
val info = cPalette.map(_ => doodadInfo)
|
||||||
|
currTileset.tileInfo.insert(priorSize, info : _*)
|
||||||
|
}
|
||||||
|
updateTileView()
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateTileView(): Unit = {
|
||||||
|
tileView.children.clear()
|
||||||
|
val tiles = a1Palette ++ a2Palette ++ a3Palette ++ a4Palette ++ a5Palette ++ bPalette ++ cPalette
|
||||||
|
tileView.children ++= tiles.zipWithIndex.map{case (t, i) => new ImageView(t.icon.croppedImage()){
|
||||||
|
handleEvent(MouseEvent.Any) {
|
||||||
|
e: MouseEvent => if (e.eventType == MouseEvent.MouseClicked) {
|
||||||
|
currTileIndex = i
|
||||||
|
setTile(currTileset.tileInfo(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.delegate}
|
||||||
|
}
|
||||||
|
|
||||||
|
def setTile(info: TileInfo): Unit = {
|
||||||
|
doesPassNorth.selected = info.doesPassNorth
|
||||||
|
doesPassEast.selected = info.doesPassEast
|
||||||
|
doesPassSouth.selected = info.doesPassSouth
|
||||||
|
doesPassWest.selected = info.doesPassWest
|
||||||
|
isCounter.selected = info.isCounter
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateTile(): Unit = {
|
||||||
|
val northFlag = if (doesPassNorth.selected()) TileInfo.PassNorth else TileInfo.NoPass
|
||||||
|
val eastFlag = if (doesPassEast.selected()) TileInfo.PassEast else TileInfo.NoPass
|
||||||
|
val southFlag = if (doesPassSouth.selected()) TileInfo.PassSouth else TileInfo.NoPass
|
||||||
|
val westFlag = if (doesPassWest.selected()) TileInfo.PassWest else TileInfo.NoPass
|
||||||
|
val counterFlag = if (isCounter.selected()) TileInfo.IsCounter else TileInfo.NoPass
|
||||||
|
val overwriteFlag = currTileset.tileInfo(currTileIndex).flags & TileInfo.Overwrite
|
||||||
|
val flags = northFlag | eastFlag | southFlag | westFlag | counterFlag | overwriteFlag
|
||||||
|
currTileset.tileInfo(currTileIndex) = TileInfo(flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
def saveTo(file: File): Unit = {
|
||||||
|
val tileSeq = tilesets.map(a => a.toToken)
|
||||||
|
YamlHelper.writeSeq(new FileOutputStream(file), tileSeq)
|
||||||
|
}
|
||||||
|
|
||||||
|
def openFrom(file: File): Unit = {
|
||||||
|
def f(name: String) = if (name != null) new File(resourceDir, name) else null
|
||||||
|
|
||||||
|
val tokens = YamlHelper.extractSeq[TilesetToken](new FileInputStream(file))
|
||||||
|
tilesets.clear()
|
||||||
|
tilesets ++= tokens.map(t=> {
|
||||||
|
val ts = new ObsTileset(t.name)
|
||||||
|
ts.a1.value = f(t.a1)
|
||||||
|
ts.a2.value = f(t.a2)
|
||||||
|
ts.a3.value = f(t.a3)
|
||||||
|
ts.a4.value = f(t.a4)
|
||||||
|
ts.a5.value = f(t.a5)
|
||||||
|
ts.b.value = f(t.b)
|
||||||
|
ts.c.value = f(t.c)
|
||||||
|
// TODO : Individual sizes
|
||||||
|
ts.tileInfo ++= t.info
|
||||||
|
ts
|
||||||
|
})
|
||||||
|
tilesetList.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
private def allPassInfo = TileInfo(TileInfo.PassAll)
|
||||||
|
private def noPassInfo = TileInfo(TileInfo.NoPass)
|
||||||
|
private def doodadInfo = TileInfo(TileInfo.NoPass | TileInfo.Overwrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
object TilesetBuilder {
|
||||||
|
class TBA extends Application {
|
||||||
|
override def start(primaryStage: Stage): Unit = {
|
||||||
|
val frameLoader = new FXMLLoader(getClass.getResource("App.fxml"))
|
||||||
|
val root: Parent = frameLoader.load()
|
||||||
|
val controller = frameLoader.getController[App]()
|
||||||
|
|
||||||
|
val builderLoader = new FXMLLoader(getClass.getResource("TilesetBuilder.fxml"))
|
||||||
|
val builder: Parent = builderLoader.load()
|
||||||
|
val builderController = builderLoader.getController[TilesetBuilder]()
|
||||||
|
|
||||||
|
controller.pane.children = builder
|
||||||
|
controller.builder = builderController
|
||||||
|
|
||||||
|
val scene: Scene = new Scene(root)
|
||||||
|
primaryStage.setScene(scene)
|
||||||
|
primaryStage.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def main(args: Array[String]): Unit = {
|
||||||
|
Application.launch(classOf[TBA], args: _*)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
28
Builder/src/fmon/builder/dialog/MapProperties.fxml
Normal file
28
Builder/src/fmon/builder/dialog/MapProperties.fxml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.ComboBox?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.Spinner?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
|
||||||
|
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fmon.builder.dialog.MapProperties">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="294.0" minWidth="10.0" prefWidth="74.0" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Label text="Width" />
|
||||||
|
<Label text="Height" GridPane.rowIndex="1" />
|
||||||
|
<Label text="Tileset" GridPane.rowIndex="2" />
|
||||||
|
<Spinner fx:id="widthSpin" editable="true" initialValue="10" max="100" min="10" GridPane.columnIndex="1" />
|
||||||
|
<Spinner fx:id="heightSpin" editable="true" initialValue="10" max="100" min="10" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||||
|
<ComboBox fx:id="tilesetChooser" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="2" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
26
Builder/src/fmon/builder/dialog/MapProperties.scala
Normal file
26
Builder/src/fmon/builder/dialog/MapProperties.scala
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package fmon.builder.dialog
|
||||||
|
|
||||||
|
import javafx.fxml.FXML
|
||||||
|
import javafx.scene.{control => jfxsc, layout => jfxsl, Parent, Scene}
|
||||||
|
|
||||||
|
import scalafx.Includes._
|
||||||
|
import scalafx.scene.control.SpinnerValueFactory
|
||||||
|
|
||||||
|
import fmon.world.GameMap
|
||||||
|
|
||||||
|
class MapProperties {
|
||||||
|
@FXML var widthSpin: jfxsc.Spinner[Int] = _
|
||||||
|
@FXML var heightSpin: jfxsc.Spinner[Int] = _
|
||||||
|
|
||||||
|
@FXML def initialize(): Unit = {
|
||||||
|
println(widthSpin)
|
||||||
|
}
|
||||||
|
|
||||||
|
def init(level: GameMap): Unit = {
|
||||||
|
widthSpin.valueFactory().value = level.width
|
||||||
|
heightSpin.valueFactory().value = level.height
|
||||||
|
}
|
||||||
|
|
||||||
|
def width = widthSpin.value()
|
||||||
|
def height = heightSpin.value()
|
||||||
|
}
|
||||||
14
Builder/src/fmon/builder/package.scala
Normal file
14
Builder/src/fmon/builder/package.scala
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package fmon
|
||||||
|
|
||||||
|
import scalafx.Includes._
|
||||||
|
import scalafx.scene.control._
|
||||||
|
|
||||||
|
package object builder {
|
||||||
|
def getSelected[T](view: ListView[T]): T = {
|
||||||
|
view.selectionModel().selectedItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
def getSelected[T](view: ComboBox[T]): T = {
|
||||||
|
view.selectionModel().selectedItem()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,13 @@ case class Config(
|
|||||||
val title: String,
|
val title: String,
|
||||||
val crit: Double,
|
val crit: Double,
|
||||||
val stab: Double,
|
val stab: Double,
|
||||||
val maxBoost: Int
|
val resistMult: Double,
|
||||||
|
val weakMult: Double,
|
||||||
|
val immuneMult: Double,
|
||||||
|
val maxBoost: Int,
|
||||||
|
val tileSize: Int,
|
||||||
|
val moveSpeed: Double,
|
||||||
|
val yOffset: Int
|
||||||
) {
|
) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -17,5 +23,13 @@ object Config {
|
|||||||
def title = config.title
|
def title = config.title
|
||||||
def crit = config.crit
|
def crit = config.crit
|
||||||
def stab = config.stab
|
def stab = config.stab
|
||||||
|
def resistMult = config.resistMult
|
||||||
|
def weakMult = config.weakMult
|
||||||
|
def immuneMult = config.immuneMult
|
||||||
def maxBoost = config.maxBoost
|
def maxBoost = config.maxBoost
|
||||||
|
def tileSize = config.tileSize
|
||||||
|
def moveSpeed = config.moveSpeed
|
||||||
|
def yOffset = config.yOffset
|
||||||
|
|
||||||
|
def homedir = raw"C:\Users\dalyj\Documents\Design\Project\"
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,7 @@ object Game {
|
|||||||
|
|
||||||
class Game extends Application {
|
class Game extends Application {
|
||||||
override def start(primaryStage: Stage): Unit = {
|
override def start(primaryStage: Stage): Unit = {
|
||||||
|
/*
|
||||||
val url = getClass.getResource("battle/battle.fxml")
|
val url = getClass.getResource("battle/battle.fxml")
|
||||||
val loader = new FXMLLoader(url)
|
val loader = new FXMLLoader(url)
|
||||||
val root: Parent = loader.load()
|
val root: Parent = loader.load()
|
||||||
@@ -29,8 +30,8 @@ class Game extends Application {
|
|||||||
implicit val rng = new scala.util.Random()
|
implicit val rng = new scala.util.Random()
|
||||||
val form1 = Form("Diabolo")
|
val form1 = Form("Diabolo")
|
||||||
val form2 = Form("Chanilla")
|
val form2 = Form("Chanilla")
|
||||||
val movepool1 = IndexedSeq(Move("eruption"), Move("willowisp"), Move("thunderbolt"), Move("thunderwave"), Move("blastburn"))
|
val movepool1 = IndexedSeq(Move("eruption"), Move("willowisp"), Move("thunderbolt"), Move("thunderwave"), Move("thundershock"))
|
||||||
val movepool2 = IndexedSeq(Move("blastburn"))
|
val movepool2 = IndexedSeq(Move("tackle"))
|
||||||
val p1 = TrainerID("Jaeda", Gender.Female, 0)
|
val p1 = TrainerID("Jaeda", Gender.Female, 0)
|
||||||
val p2 = TrainerID("Wild Monster", Gender.Male, 0)
|
val p2 = TrainerID("Wild Monster", Gender.Male, 0)
|
||||||
val party1 = new Party(p1, new MonsterPtr(Monster.generate("Allied Mon", p1, 500, form1, movepool1)), IndexedSeq())
|
val party1 = new Party(p1, new MonsterPtr(Monster.generate("Allied Mon", p1, 500, form1, movepool1)), IndexedSeq())
|
||||||
@@ -43,5 +44,6 @@ class Game extends Application {
|
|||||||
primaryStage.setTitle(Config.title)
|
primaryStage.setTitle(Config.title)
|
||||||
primaryStage.setScene(scene)
|
primaryStage.setScene(scene)
|
||||||
primaryStage.show()
|
primaryStage.show()
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
31
FakeMon/src/fmon/GameManager.scala
Normal file
31
FakeMon/src/fmon/GameManager.scala
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package fmon
|
||||||
|
|
||||||
|
import scalafx.scene.Scene
|
||||||
|
|
||||||
|
import scalafx.Includes._
|
||||||
|
import scalafx.scene.Node
|
||||||
|
import scalafx.scene.Parent
|
||||||
|
import scalafx.stage.Stage
|
||||||
|
|
||||||
|
import fmon.world.ui.GameView
|
||||||
|
|
||||||
|
object GameManager {
|
||||||
|
var currLevel: Parent = _
|
||||||
|
var currController: GameView = _
|
||||||
|
|
||||||
|
var currView: Parent = _
|
||||||
|
|
||||||
|
var root: Stage = _
|
||||||
|
|
||||||
|
def setView(view: Parent): Unit = {
|
||||||
|
currController.timer.stop()
|
||||||
|
currView = view
|
||||||
|
root.scene().root = view
|
||||||
|
}
|
||||||
|
|
||||||
|
def restoreMap(): Unit = {
|
||||||
|
currView = currLevel
|
||||||
|
root.scene().root = currView
|
||||||
|
currController.timer.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package fmon.battle
|
|||||||
|
|
||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
|
|
||||||
|
import fmon._
|
||||||
import fmon.battle.msg._
|
import fmon.battle.msg._
|
||||||
import fmon.stat._
|
import fmon.stat._
|
||||||
import fmon.stat.Statistic._
|
import fmon.stat.Statistic._
|
||||||
@@ -43,6 +44,7 @@ class BattleEngine(val player: Party, val enemy: Party)(implicit val reader: Sig
|
|||||||
player.switchIn(replace)
|
player.switchIn(replace)
|
||||||
} else {
|
} else {
|
||||||
this ! Message(s"${player.trainer} has lost!")
|
this ! Message(s"${player.trainer} has lost!")
|
||||||
|
GameManager.restoreMap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!enemy.lead.isAlive) {
|
if (!enemy.lead.isAlive) {
|
||||||
@@ -51,6 +53,7 @@ class BattleEngine(val player: Party, val enemy: Party)(implicit val reader: Sig
|
|||||||
enemy.switchIn(replace)
|
enemy.switchIn(replace)
|
||||||
} else {
|
} else {
|
||||||
this ! Message(s"${enemy.trainer} has lost!")
|
this ! Message(s"${enemy.trainer} has lost!")
|
||||||
|
GameManager.restoreMap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
package fmon.battle
|
package fmon.battle
|
||||||
|
|
||||||
|
import java.io._
|
||||||
|
import java.util.concurrent.locks.{Condition, ReentrantLock}
|
||||||
|
|
||||||
|
import scala.util.Random
|
||||||
|
|
||||||
|
import javafx.application.Application
|
||||||
|
import javafx.application.Platform
|
||||||
import javafx.fxml.FXML
|
import javafx.fxml.FXML
|
||||||
import javafx.fxml.FXMLLoader
|
import javafx.fxml.FXMLLoader
|
||||||
import javafx.fxml.JavaFXBuilderFactory
|
import javafx.fxml.JavaFXBuilderFactory
|
||||||
import javafx.scene.{control => jfxsc}
|
import javafx.scene.{control => jfxsc}
|
||||||
import javafx.scene.image.ImageView
|
import javafx.scene.image.ImageView
|
||||||
|
import javafx.stage.Stage
|
||||||
|
|
||||||
import scalafx.Includes._
|
import scalafx.Includes._
|
||||||
import scalafx.animation._
|
import scalafx.animation._
|
||||||
|
import scalafx.concurrent.Task
|
||||||
import scalafx.geometry.{Point2D, Pos}
|
import scalafx.geometry.{Point2D, Pos}
|
||||||
import scalafx.scene.{Group, Parent}
|
import scalafx.scene.{Group, Parent}
|
||||||
import scalafx.scene.control._
|
import scalafx.scene.control._
|
||||||
@@ -18,7 +27,9 @@ import scalafx.scene.text.Font
|
|||||||
import scalafx.scene.transform.Rotate
|
import scalafx.scene.transform.Rotate
|
||||||
import scalafx.util.Duration
|
import scalafx.util.Duration
|
||||||
|
|
||||||
|
import fmon.GameManager
|
||||||
import fmon.battle.msg._
|
import fmon.battle.msg._
|
||||||
|
import fmon.draw._
|
||||||
import fmon.stat._
|
import fmon.stat._
|
||||||
import fmon.stat.Statistic._
|
import fmon.stat.Statistic._
|
||||||
|
|
||||||
@@ -36,7 +47,28 @@ class BattleUI extends SignalConsumer {
|
|||||||
|
|
||||||
private var engine: BattleEngine = _
|
private var engine: BattleEngine = _
|
||||||
|
|
||||||
|
private var numPlaying = 0
|
||||||
|
private val lock = new ReentrantLock()
|
||||||
|
private val finishedPlaying = lock.newCondition()
|
||||||
|
|
||||||
|
def beginWildBattle(player: Party, wild: Monster)(implicit rng: Random) {
|
||||||
|
implicit val consumer = this
|
||||||
|
setEngine(new BattleEngine(
|
||||||
|
player,
|
||||||
|
new Party(BattleUI.WildTrainer, new MonsterPtr(wild), IndexedSeq())
|
||||||
|
))
|
||||||
|
this ! Message(s"A wild ${wild.name} appeared!")
|
||||||
|
}
|
||||||
|
|
||||||
|
def beginTrainerBattle(player: Party, trainer: Party)(implicit rng: Random) {
|
||||||
|
implicit val consumer = this
|
||||||
|
setEngine(new BattleEngine(player, trainer))
|
||||||
|
this ! Message(s"${trainer.trainer} wants to fight!")
|
||||||
|
this ! Message(s"${trainer.trainer} sent out ${trainer.lead}")
|
||||||
|
}
|
||||||
|
|
||||||
def setEngine(engine: BattleEngine): Unit = {
|
def setEngine(engine: BattleEngine): Unit = {
|
||||||
|
messages.clear()
|
||||||
this.engine = engine
|
this.engine = engine
|
||||||
updateUI()
|
updateUI()
|
||||||
}
|
}
|
||||||
@@ -51,24 +83,69 @@ class BattleUI extends SignalConsumer {
|
|||||||
enemyMonController.update(enemy)
|
enemyMonController.update(enemy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def requestUpdateUI(): Unit = {
|
||||||
|
val task = Task(updateUI)
|
||||||
|
Platform.runLater(task)
|
||||||
|
}
|
||||||
|
|
||||||
def onMove(move: Move): Unit = {
|
def onMove(move: Move): Unit = {
|
||||||
implicit val rng = engine.rng
|
implicit val rng = engine.rng
|
||||||
implicit val reader = engine
|
implicit val reader = engine
|
||||||
val action = Action(engine.player.lead, move, engine.player.pollTarget(move, engine.player.lead, engine.enemy))
|
val action = Action(engine.player.lead, move, engine.player.pollTarget(move, engine.player.lead, engine.enemy))
|
||||||
engine.playTurn(action)
|
val task = Task(engine.playTurn(action))
|
||||||
updateUI()
|
val thread = new Thread(task)
|
||||||
|
thread.setDaemon(true)
|
||||||
|
thread.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
def !(msg: Signal): Unit = {
|
def !(msg: Signal): Unit = {
|
||||||
|
lock.lock()
|
||||||
|
try {
|
||||||
msg match {
|
msg match {
|
||||||
|
case AnimateMsg(ani, target) => playAnimation(ani, target)
|
||||||
case Message(text) => messages.text = s"${messages.text()}${text}\n"
|
case Message(text) => messages.text = s"${messages.text()}${text}\n"
|
||||||
case DamageMsg(dmg, target, element) => playDamage(dmg, target, element)
|
case DamageMsg(dmg, target, element) => playDamage(dmg, target, element)
|
||||||
}
|
}
|
||||||
|
while (numPlaying > 0) {
|
||||||
|
finishedPlaying.await()
|
||||||
|
}
|
||||||
|
requestUpdateUI()
|
||||||
|
} catch {
|
||||||
|
case e: Exception => e.printStackTrace()
|
||||||
|
} finally {
|
||||||
|
lock.unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private def playAnimation(aniname: String, target: Monster): Unit = {
|
||||||
|
numPlaying += 1
|
||||||
|
def helper = {
|
||||||
|
val animation = AnimationToken(aniname).load(new File(raw"C:\Users\dalyj\Documents\Design\Images\TimmahLexusX reduced\Reduced Animations"))
|
||||||
|
val imgView = new ImageView
|
||||||
|
animationPane.children += imgView
|
||||||
|
|
||||||
|
val center = findCenter(target)
|
||||||
|
imgView.translateX = center.x - 96 // TODO : Compute result
|
||||||
|
imgView.translateY = center.y - 96
|
||||||
|
|
||||||
|
val duration = Duration(animation.frames.size * 50)
|
||||||
|
val transition = new SpriteAnimation(imgView, animation, duration)
|
||||||
|
transition.onFinished = handle{
|
||||||
|
animationPane.children -= imgView
|
||||||
|
finishPlay()
|
||||||
|
}
|
||||||
|
transition.play()
|
||||||
|
}
|
||||||
|
val task = Task(helper)
|
||||||
|
Platform.runLater(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def playDamage(damage: Int, target: Monster, element: Element): Unit = {
|
private def playDamage(damage: Int, target: Monster, element: Element): Unit = {
|
||||||
|
numPlaying += 1
|
||||||
|
def helper = {
|
||||||
val r = 25
|
val r = 25
|
||||||
val color = Color.AliceBlue
|
val color = element.bgColor
|
||||||
val center = findCenter(target)
|
val center = findCenter(target)
|
||||||
|
|
||||||
val circle = new Circle {
|
val circle = new Circle {
|
||||||
@@ -106,10 +183,25 @@ class BattleUI extends SignalConsumer {
|
|||||||
))
|
))
|
||||||
animation.onFinished = handle{
|
animation.onFinished = handle{
|
||||||
animationPane.children -= group
|
animationPane.children -= group
|
||||||
|
finishPlay()
|
||||||
}
|
}
|
||||||
animation.play()
|
animation.play()
|
||||||
animationPane.children += group
|
animationPane.children += group
|
||||||
}
|
}
|
||||||
|
val task = Task(helper)
|
||||||
|
Platform.runLater(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def finishPlay(): Unit = {
|
||||||
|
lock.lock()
|
||||||
|
try {
|
||||||
|
numPlaying -= 1
|
||||||
|
updateUI()
|
||||||
|
finishedPlaying.signal()
|
||||||
|
} finally {
|
||||||
|
lock.unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private def findCenter(monster: Monster): Point2D = {
|
private def findCenter(monster: Monster): Point2D = {
|
||||||
if (monster == engine.player.lead.mon) {
|
if (monster == engine.player.lead.mon) {
|
||||||
@@ -135,3 +227,61 @@ class BattleUI extends SignalConsumer {
|
|||||||
button
|
button
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import javafx.scene.Scene
|
||||||
|
|
||||||
|
import fmon.Config
|
||||||
|
import fmon.stat.Gender
|
||||||
|
|
||||||
|
object BattleUI {
|
||||||
|
final val WildTrainer = TrainerID("Wild", Gender.Neuter, 0)
|
||||||
|
|
||||||
|
def startWildBattle(): Unit = {
|
||||||
|
val url = getClass.getResource("battle.fxml")
|
||||||
|
val loader = new FXMLLoader(url)
|
||||||
|
val root: javafx.scene.Parent = loader.load()
|
||||||
|
implicit val controller = loader.getController[BattleUI]()
|
||||||
|
|
||||||
|
implicit val rng = new scala.util.Random()
|
||||||
|
val form1 = Form("Diabolo")
|
||||||
|
val form2 = Form("Chanilla")
|
||||||
|
val movepool1 = IndexedSeq(Move("eruption"), Move("willowisp"), Move("thunderbolt"), Move("thunderwave"), Move("thundershock"))
|
||||||
|
val movepool2 = IndexedSeq(Move("tackle"))
|
||||||
|
val p1 = TrainerID("Jaeda", Gender.Female, 0)
|
||||||
|
val party1 = new Party(p1, new MonsterPtr(Monster.generate("Allied Mon", p1, 500, form1, movepool1)), IndexedSeq())
|
||||||
|
val monster = Monster.generate(null, WildTrainer, 500, form2, movepool2)
|
||||||
|
controller.beginWildBattle(party1, monster)
|
||||||
|
GameManager.setView(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
class GameApp extends Application {
|
||||||
|
override def start(primaryStage: Stage): Unit = {
|
||||||
|
val url = getClass.getResource("battle.fxml")
|
||||||
|
val loader = new FXMLLoader(url)
|
||||||
|
val root: javafx.scene.Parent = loader.load()
|
||||||
|
implicit val controller = loader.getController[BattleUI]()
|
||||||
|
val scene: Scene = new Scene(root)
|
||||||
|
|
||||||
|
implicit val rng = new scala.util.Random()
|
||||||
|
val form1 = Form("Diabolo")
|
||||||
|
val form2 = Form("Chanilla")
|
||||||
|
val movepool1 = IndexedSeq(Move("eruption"), Move("willowisp"), Move("thunderbolt"), Move("thunderwave"), Move("thundershock"))
|
||||||
|
val movepool2 = IndexedSeq(Move("tackle"))
|
||||||
|
val p1 = TrainerID("Jaeda", Gender.Female, 0)
|
||||||
|
val p2 = TrainerID("Wild Monster", Gender.Male, 0)
|
||||||
|
val party1 = new Party(p1, new MonsterPtr(Monster.generate("Allied Mon", p1, 500, form1, movepool1)), IndexedSeq())
|
||||||
|
val party2 = new Party(p2, new MonsterPtr(Monster.generate("Wild Mon", p2, 500, form2, movepool2)), IndexedSeq(
|
||||||
|
Monster.generate("Sideboard Mon", p2, 500, form2, movepool2)
|
||||||
|
))
|
||||||
|
controller.beginTrainerBattle(party1, party2)
|
||||||
|
|
||||||
|
primaryStage.setTitle(Config.title)
|
||||||
|
primaryStage.setScene(scene)
|
||||||
|
primaryStage.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def main(args: Array[String]): Unit = {
|
||||||
|
Application.launch(classOf[GameApp], args: _*)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import javafx.scene.{control => jfxsc}
|
|||||||
|
|
||||||
import scalafx.Includes._
|
import scalafx.Includes._
|
||||||
import scalafx.scene.control._
|
import scalafx.scene.control._
|
||||||
|
import scalafx.scene.paint.Color
|
||||||
|
|
||||||
import fmon.stat.Move
|
import fmon.stat.Move
|
||||||
|
|
||||||
@@ -19,10 +20,16 @@ class MoveButton {
|
|||||||
move = mv
|
move = mv
|
||||||
moveName.text = mv.name
|
moveName.text = mv.name
|
||||||
moveUses.text = s"${move.pp}/${move.pp}"
|
moveUses.text = s"${move.pp}/${move.pp}"
|
||||||
|
moveBtn.style = s"-fx-background-color: ${colorString(mv.element.bgColor)}; -fx-text-fill: ${colorString(mv.element.fontColor)}"
|
||||||
this.callback = callback
|
this.callback = callback
|
||||||
}
|
}
|
||||||
|
|
||||||
def moveActivate(): Unit = {
|
def moveActivate(): Unit = {
|
||||||
callback(move)
|
callback(move)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def colorString(color: Color): String = {
|
||||||
|
val s = color.toString()
|
||||||
|
s"#${s.split("x")(1)}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<?import javafx.scene.layout.Pane?>
|
<?import javafx.scene.layout.Pane?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
|
||||||
<VBox prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fmon.battle.BattleUI">
|
<VBox prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fmon.battle.BattleUI">
|
||||||
<children>
|
<children>
|
||||||
<MenuBar VBox.vgrow="NEVER">
|
<MenuBar VBox.vgrow="NEVER">
|
||||||
<menus>
|
<menus>
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
</Menu>
|
</Menu>
|
||||||
</menus>
|
</menus>
|
||||||
</MenuBar>
|
</MenuBar>
|
||||||
<AnchorPane maxHeight="-1.0" maxWidth="-1.0" prefHeight="-1.0" prefWidth="-1.0" VBox.vgrow="ALWAYS">
|
<AnchorPane maxHeight="-1.0" maxWidth="-1.0" VBox.vgrow="ALWAYS">
|
||||||
<children>
|
<children>
|
||||||
<ImageView fx:id="backgroundImg" fitHeight="575.0" fitWidth="856.0" pickOnBounds="true">
|
<ImageView fx:id="backgroundImg" fitHeight="575.0" fitWidth="856.0" pickOnBounds="true">
|
||||||
<image>
|
<image>
|
||||||
|
|||||||
25
FakeMon/src/fmon/battle/msg/AnimateMsg.scala
Normal file
25
FakeMon/src/fmon/battle/msg/AnimateMsg.scala
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package fmon.battle.msg
|
||||||
|
|
||||||
|
import fmon.stat.{Element, Monster}
|
||||||
|
|
||||||
|
case class AnimateMsg(val animation: String, target: Monster) extends Signal {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object AnimateMsg {
|
||||||
|
def apply(element: Element, target: Monster): AnimateMsg = {
|
||||||
|
val ani = element.name match {
|
||||||
|
case "Poison" => "Poison Bubble"
|
||||||
|
case "Electric" => "Lightning Lines"
|
||||||
|
case "Fire" => "Rising Fire 1"
|
||||||
|
case "Rock" => "Rock Blast"
|
||||||
|
case "Ground" => "Stalagmites"
|
||||||
|
case "Water" => "Rain"
|
||||||
|
case "Flying" => "Gale"
|
||||||
|
case "Ice" => "Glacier"
|
||||||
|
case "Fairy" => "Light and Glass"
|
||||||
|
case _ => "Buster"
|
||||||
|
}
|
||||||
|
AnimateMsg(ani, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
title: FakeMon Engine Demo
|
title: FakeMon Engine Demo
|
||||||
crit: 1.5
|
crit: 1.5
|
||||||
stab: 1.5
|
stab: 1.5
|
||||||
|
weakMult: 2.0
|
||||||
|
resistMult: 0.5
|
||||||
|
immuneMult: 0.0
|
||||||
maxBoost: 6
|
maxBoost: 6
|
||||||
newday: 02:00
|
newday: 02:00
|
||||||
|
tileSize: 48
|
||||||
|
yOffset: -4
|
||||||
|
moveSpeed: 3.0
|
||||||
|
|||||||
47
FakeMon/src/fmon/draw/AnimatedImage.scala
Normal file
47
FakeMon/src/fmon/draw/AnimatedImage.scala
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package fmon.draw
|
||||||
|
|
||||||
|
import java.io.{File, FileInputStream}
|
||||||
|
|
||||||
|
import javafx.animation.{Transition => JTransition}
|
||||||
|
|
||||||
|
import scalafx.Includes._
|
||||||
|
import scalafx.animation._
|
||||||
|
import scalafx.scene.image.ImageView
|
||||||
|
import scalafx.util.Duration
|
||||||
|
|
||||||
|
import fmon.util.YamlHelper
|
||||||
|
|
||||||
|
class SpriteAnimation(val view: ImageView, val image: Drawable, val duration: Duration) extends JTransition {
|
||||||
|
setCycleDuration(duration)
|
||||||
|
setInterpolator(Interpolator.Linear)
|
||||||
|
setAutoReverse(false)
|
||||||
|
|
||||||
|
def interpolate(t: Double): Unit = {
|
||||||
|
image.draw(view, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimatedImage(val frames: IndexedSeq[Drawable]) extends Drawable {
|
||||||
|
val lastIndex = -1
|
||||||
|
|
||||||
|
def draw(view: ImageView, t: Double): Unit = {
|
||||||
|
val index = Math.min((frames.size * t).toInt, frames.size - 1)
|
||||||
|
frames(index).draw(view, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class AnimationToken(val filename: String, val indices: IndexedSeq[Int]) {
|
||||||
|
def load(dir: File) = {
|
||||||
|
val file = new File(s"${dir.getPath}/${filename}")
|
||||||
|
val palette = Palette.square(new FileInputStream(file), 5)
|
||||||
|
val images = palette.images
|
||||||
|
val frames = indices.map(i => images(i))
|
||||||
|
new AnimatedImage(frames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object AnimationToken {
|
||||||
|
val tokens = YamlHelper.extractMap[AnimationToken](new FileInputStream(raw"C:\Users\dalyj\Documents\Design\animations.yaml"))
|
||||||
|
|
||||||
|
def apply(name: String) = tokens(name)
|
||||||
|
}
|
||||||
35
FakeMon/src/fmon/draw/AnimatedImageView.scala
Normal file
35
FakeMon/src/fmon/draw/AnimatedImageView.scala
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package fmon.draw
|
||||||
|
|
||||||
|
import scalafx.scene.image.ImageView
|
||||||
|
|
||||||
|
import scalafx.Includes._
|
||||||
|
import scalafx.animation.Transition
|
||||||
|
import scalafx.util.Duration
|
||||||
|
|
||||||
|
class AnimatedImageView extends ImageView {
|
||||||
|
def this(img: Drawable, dur: Duration) {
|
||||||
|
this()
|
||||||
|
animate(img, dur)
|
||||||
|
}
|
||||||
|
|
||||||
|
var drawable: Drawable = _
|
||||||
|
var duration: Duration = _
|
||||||
|
|
||||||
|
private var animation: SpriteAnimation = _
|
||||||
|
|
||||||
|
def animate(img: Drawable, dur: Duration) {
|
||||||
|
drawable = img
|
||||||
|
duration = dur
|
||||||
|
animation = new SpriteAnimation(this, drawable, duration)
|
||||||
|
animation.cycleCount = Transition.Indefinite
|
||||||
|
animation.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
def pause(): Unit = animation.pause()
|
||||||
|
def play(): Unit = animation.play()
|
||||||
|
def playFromStart(): Unit = animation.playFromStart()
|
||||||
|
def stop(): Unit = {
|
||||||
|
animation.stop()
|
||||||
|
animation.interpolate(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
67
FakeMon/src/fmon/draw/Battler.scala
Normal file
67
FakeMon/src/fmon/draw/Battler.scala
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package fmon.draw
|
||||||
|
|
||||||
|
import java.io._
|
||||||
|
|
||||||
|
object Battler {
|
||||||
|
final val FramesPerPose = 3
|
||||||
|
|
||||||
|
def apply(filename: String): Sprite = {
|
||||||
|
this(new File(filename))
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply(file: File): Sprite = {
|
||||||
|
val palette = new Palette(new FileInputStream(file), 9, 6)
|
||||||
|
def pose(x: Int, y: Int): AnimatedImage = {
|
||||||
|
val xx = x * FramesPerPose
|
||||||
|
val frames = IndexedSeq(palette(xx, y), palette(xx + 1, y), palette(xx + 2, y), palette(xx + 1, y))
|
||||||
|
new AnimatedImage(frames)
|
||||||
|
}
|
||||||
|
val poses = Map(
|
||||||
|
"Idle" -> pose(0, 0),
|
||||||
|
"ReadyPhys" -> pose(0, 1),
|
||||||
|
"ReadyMag" -> pose(0, 2),
|
||||||
|
"Guard" -> pose(0, 3),
|
||||||
|
"Damage" -> pose(0, 4),
|
||||||
|
"Evade" -> pose(0, 5),
|
||||||
|
"Stab" -> pose(1, 0),
|
||||||
|
"Swing" -> pose(1, 1),
|
||||||
|
"Shoot" -> pose(1, 2),
|
||||||
|
"PhysSkill" -> pose(1, 3),
|
||||||
|
"MagSkill" -> pose(1, 4),
|
||||||
|
"Item" -> pose(1, 5),
|
||||||
|
"Escape" -> pose(2, 0),
|
||||||
|
"Victory" -> pose(2, 1),
|
||||||
|
"Crisis" -> pose(2, 2),
|
||||||
|
"Abnormal" -> pose(2, 3),
|
||||||
|
"Sleep" -> pose(2, 4),
|
||||||
|
"Dead" -> pose(2, 5)
|
||||||
|
)
|
||||||
|
new Sprite("Idle", poses)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
import scalafx.application.JFXApp
|
||||||
|
import scalafx.scene._
|
||||||
|
import scalafx.scene.control._
|
||||||
|
import scalafx.scene.image._
|
||||||
|
import scalafx.scene.paint.Color._
|
||||||
|
import scalafx.util.Duration
|
||||||
|
|
||||||
|
object DrawingDemo extends JFXApp {
|
||||||
|
val view = new ImageView
|
||||||
|
stage = new JFXApp.PrimaryStage {
|
||||||
|
title.value = "Hello Stage"
|
||||||
|
width = 600
|
||||||
|
height = 450
|
||||||
|
scene = new Scene {
|
||||||
|
fill = LightGreen
|
||||||
|
content = view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val battler = Battler(raw"C:\Users\James\Documents\Design\fakemon\images\Aekashics Librarium MV Format Animated Battlers\Aekashics Librarium MV Format Animated Battlers\RPG Maker MV format Animated Sideview Battlers\Bat\Bat1_1.png")
|
||||||
|
val animation = new SpriteAnimation(view, battler, new Duration(1000)) {
|
||||||
|
setCycleCount(10)
|
||||||
|
}
|
||||||
|
animation.play()
|
||||||
|
|
||||||
|
}
|
||||||
52
FakeMon/src/fmon/draw/CharSet.scala
Normal file
52
FakeMon/src/fmon/draw/CharSet.scala
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package fmon.draw
|
||||||
|
|
||||||
|
import java.io._
|
||||||
|
|
||||||
|
object CharSet {
|
||||||
|
final val FramesPerPose = 3
|
||||||
|
final val Poses = IndexedSeq("South", "West", "East", "North")
|
||||||
|
final val NumAcross = 4
|
||||||
|
final val NumDown = 2
|
||||||
|
|
||||||
|
def apply(file: File, index: Int): Sprite = {
|
||||||
|
val na = NumAcross * FramesPerPose
|
||||||
|
val nd = NumDown * Poses.size
|
||||||
|
val palette = new Palette(new FileInputStream(file), na, nd)
|
||||||
|
val x = index % NumAcross
|
||||||
|
val y = index / NumAcross
|
||||||
|
val xx = x * FramesPerPose + 1
|
||||||
|
def pose(i: Int): AnimatedImage = {
|
||||||
|
val yy = y * Poses.size + i
|
||||||
|
val images = IndexedSeq(palette(xx, yy), palette(xx - 1, yy), palette(xx, yy), palette(xx + 1, yy))
|
||||||
|
new AnimatedImage(images)
|
||||||
|
}
|
||||||
|
val poses = Poses.zipWithIndex.map{case (p, i) => (p, pose(i))}.toMap
|
||||||
|
new Sprite("South", poses)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
import scalafx.application.JFXApp
|
||||||
|
import scalafx.scene._
|
||||||
|
import scalafx.scene.control._
|
||||||
|
import scalafx.scene.image._
|
||||||
|
import scalafx.scene.paint.Color._
|
||||||
|
import scalafx.util.Duration
|
||||||
|
|
||||||
|
object CharSetDemo extends JFXApp {
|
||||||
|
val view = new ImageView
|
||||||
|
stage = new JFXApp.PrimaryStage {
|
||||||
|
title.value = "Hello Stage"
|
||||||
|
width = 600
|
||||||
|
height = 450
|
||||||
|
scene = new Scene {
|
||||||
|
fill = LightGreen
|
||||||
|
content = view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val filename = raw"C:\Users\dalyj\Documents\Design\Images\CharSets\remakertp01.png"
|
||||||
|
val walker = CharSet(new File(filename), 1)
|
||||||
|
val animation = new SpriteAnimation(view, walker, new Duration(600)) {
|
||||||
|
setCycleCount(scalafx.animation.Transition.Indefinite)
|
||||||
|
}
|
||||||
|
animation.play()
|
||||||
|
}
|
||||||
7
FakeMon/src/fmon/draw/Drawable.scala
Normal file
7
FakeMon/src/fmon/draw/Drawable.scala
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package fmon.draw
|
||||||
|
|
||||||
|
import scalafx.scene.image.ImageView
|
||||||
|
|
||||||
|
trait Drawable {
|
||||||
|
def draw(view: ImageView, t: Double): Unit
|
||||||
|
}
|
||||||
71
FakeMon/src/fmon/draw/Palette.scala
Normal file
71
FakeMon/src/fmon/draw/Palette.scala
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package fmon.draw
|
||||||
|
|
||||||
|
import java.io._
|
||||||
|
|
||||||
|
import scalafx.geometry.Rectangle2D
|
||||||
|
import scalafx.scene.image._
|
||||||
|
|
||||||
|
case class Box(val x: Int, val y: Int, val width: Int, val height: Int)
|
||||||
|
|
||||||
|
case class StillImg(val image: Image, val x: Double, val y: Double, val width: Double, val height: Double) extends Drawable {
|
||||||
|
def draw(view: ImageView, t: Double = 0): Unit = {
|
||||||
|
if (view.image() != image.delegate) {
|
||||||
|
view.image = image
|
||||||
|
}
|
||||||
|
view.viewport = new Rectangle2D(x, y, width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
def croppedImage(): Image = {
|
||||||
|
new WritableImage(image.pixelReader.get, x.toInt, y.toInt, width.toInt, height.toInt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Palette(val image: Image, val numAcross: Int, val numDown: Int) {
|
||||||
|
def this(url: String, numAcross: Int, numDown: Int) = this(new Image(url), numAcross, numDown)
|
||||||
|
def this(stream: InputStream, numAcross: Int, numDown: Int) = this(new Image(stream), numAcross, numDown)
|
||||||
|
def this(file: File, numAcross: Int, numDown: Int) = this(new FileInputStream(file), numAcross, numDown)
|
||||||
|
|
||||||
|
val imgWidth = image.width() / numAcross
|
||||||
|
val imgHeight = image.height() / numDown
|
||||||
|
|
||||||
|
def apply(x: Int, y: Int): StillImg = {
|
||||||
|
StillImg(image, x * imgWidth, y * imgHeight, imgWidth, imgHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply(x : Int, y : Int, w : Int, h : Int): StillImg = {
|
||||||
|
StillImg(image, x * imgWidth, y * imgHeight, w * imgWidth, h * imgHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a sub-image that spans multiple cells
|
||||||
|
*/
|
||||||
|
def apply(cells : Box): StillImg = {
|
||||||
|
this(cells.x, cells.y, cells.width, cells.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
def images = {
|
||||||
|
for (y <- 0 until numDown; x <- 0 until numAcross) yield {
|
||||||
|
this(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Palette {
|
||||||
|
def square(stream: InputStream, numAcross: Int) = {
|
||||||
|
val img = new Image(stream)
|
||||||
|
val size = img.width() / numAcross
|
||||||
|
val numDown = img.height() / size
|
||||||
|
new Palette(img, numAcross, numDown.toInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
def bySize(stream: InputStream, width: Double, height: Double): Palette = {
|
||||||
|
val img = new Image(stream)
|
||||||
|
val numAcross = img.width() / width
|
||||||
|
val numDown = img.height() / height
|
||||||
|
new Palette(img, numAcross.toInt, numDown.toInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
def bySize(file: File, width: Double, height: Double): Palette = {
|
||||||
|
bySize(new FileInputStream(file), width, height)
|
||||||
|
}
|
||||||
|
}
|
||||||
9
FakeMon/src/fmon/draw/Sprite.scala
Normal file
9
FakeMon/src/fmon/draw/Sprite.scala
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package fmon.draw
|
||||||
|
|
||||||
|
import scalafx.scene.image.{Image, ImageView}
|
||||||
|
|
||||||
|
class Sprite(var pose: String, val poses: Map[String, Drawable]) extends Drawable {
|
||||||
|
def draw(view: ImageView, t: Double): Unit = {
|
||||||
|
poses(pose).draw(view, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
307
FakeMon/src/fmon/draw/tile/AutoFloorTile.scala
Normal file
307
FakeMon/src/fmon/draw/tile/AutoFloorTile.scala
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
package fmon.draw.tile
|
||||||
|
|
||||||
|
import java.io.{File, FileInputStream}
|
||||||
|
|
||||||
|
import scalafx.scene.SnapshotParameters
|
||||||
|
import scalafx.scene.canvas.Canvas
|
||||||
|
import scalafx.scene.image._
|
||||||
|
import scalafx.scene.paint.Color
|
||||||
|
|
||||||
|
import fmon._
|
||||||
|
import fmon.draw._
|
||||||
|
import fmon.util._
|
||||||
|
|
||||||
|
import Direction._
|
||||||
|
|
||||||
|
trait AutoTile {
|
||||||
|
def icon: StillImg
|
||||||
|
def build(dirs: Set[Direction]): StillImg
|
||||||
|
}
|
||||||
|
|
||||||
|
class BasicTile(val icon: StillImg) extends AutoTile {
|
||||||
|
def build(dirs: Set[Direction]) = icon
|
||||||
|
}
|
||||||
|
|
||||||
|
class AutoFloorTile(val palette: Palette, val size: Int = 48) extends AutoTile {
|
||||||
|
|
||||||
|
def icon: StillImg = palette.apply(0, 0, 2, 2)
|
||||||
|
|
||||||
|
def build(dirs: Set[Direction]) = {
|
||||||
|
val canvas = new scalafx.scene.canvas.Canvas(size, size)
|
||||||
|
val g = canvas.graphicsContext2D
|
||||||
|
|
||||||
|
val ne = getNE(dirs)
|
||||||
|
val nw = getNW(dirs)
|
||||||
|
val se = getSE(dirs)
|
||||||
|
val sw = getSW(dirs)
|
||||||
|
|
||||||
|
canvas.graphicsContext2D.drawImage(nw, 0, 0)
|
||||||
|
canvas.graphicsContext2D.drawImage(ne, size / 2, 0)
|
||||||
|
canvas.graphicsContext2D.drawImage(sw, 0, size / 2)
|
||||||
|
canvas.graphicsContext2D.drawImage(se, size / 2, size / 2)
|
||||||
|
|
||||||
|
val wi = new WritableImage(size, size)
|
||||||
|
val parameters = new SnapshotParameters(){
|
||||||
|
fill = Color.Transparent
|
||||||
|
}
|
||||||
|
val img = canvas.snapshot(parameters, wi)
|
||||||
|
StillImg(img, 0, 0, size, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def getNE(dirs: Set[Direction]): Image = {
|
||||||
|
if (!dirs(Northeast) && dirs(North) && dirs(East)) {
|
||||||
|
palette(3, 0).croppedImage()
|
||||||
|
} else {
|
||||||
|
val x = if (dirs(East)) 1 else 3
|
||||||
|
val y = if (dirs(North)) 4 else 2
|
||||||
|
palette(x, y).croppedImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def getNW(dirs: Set[Direction]): Image = {
|
||||||
|
if (!dirs(Northwest) && dirs(North) && dirs(West)) {
|
||||||
|
palette(2, 0).croppedImage()
|
||||||
|
} else {
|
||||||
|
val x = if (dirs(West)) 2 else 0
|
||||||
|
val y = if (dirs(North)) 4 else 2
|
||||||
|
palette(x, y).croppedImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def getSE(dirs: Set[Direction]): Image = {
|
||||||
|
if (!dirs(Southeast) && dirs(South) && dirs(East)) {
|
||||||
|
palette(3, 1).croppedImage()
|
||||||
|
} else {
|
||||||
|
val x = if (dirs(East)) 1 else 3
|
||||||
|
val y = if (dirs(South)) 3 else 5
|
||||||
|
palette(x, y).croppedImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def getSW(dirs: Set[Direction]): Image = {
|
||||||
|
if (!dirs(Southwest) && dirs(South) && dirs(West)) {
|
||||||
|
palette(2, 1).croppedImage()
|
||||||
|
} else {
|
||||||
|
val x = if (dirs(West)) 2 else 0
|
||||||
|
val y = if (dirs(South)) 3 else 5
|
||||||
|
palette(x, y).croppedImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AutoWallTile(val palette: Palette, val size: Int = 48) extends AutoTile {
|
||||||
|
def icon: StillImg = {
|
||||||
|
val canvas = new scalafx.scene.canvas.Canvas(size, size)
|
||||||
|
val g = canvas.graphicsContext2D
|
||||||
|
|
||||||
|
val ul = palette.apply(0, 0)
|
||||||
|
val ur = palette.apply(3, 0)
|
||||||
|
val ll = palette.apply(0, 3)
|
||||||
|
val lr = palette.apply(3, 3)
|
||||||
|
|
||||||
|
canvas.graphicsContext2D.drawImage(ul.croppedImage(), 0, 0)
|
||||||
|
canvas.graphicsContext2D.drawImage(ur.croppedImage(), size / 2, 0)
|
||||||
|
canvas.graphicsContext2D.drawImage(ll.croppedImage(), 0, size / 2)
|
||||||
|
canvas.graphicsContext2D.drawImage(lr.croppedImage(), size / 2, size / 2)
|
||||||
|
|
||||||
|
val wi = new WritableImage(size, size)
|
||||||
|
val parameters = new SnapshotParameters(){
|
||||||
|
fill = Color.Transparent
|
||||||
|
}
|
||||||
|
val img = canvas.snapshot(parameters, wi)
|
||||||
|
StillImg(img, 0, 0, size, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
def build(dirs: Set[Direction]) = {
|
||||||
|
val canvas = new scalafx.scene.canvas.Canvas(size, size)
|
||||||
|
val g = canvas.graphicsContext2D
|
||||||
|
|
||||||
|
val ul = buildUL(dirs)
|
||||||
|
val ur = buildUR(dirs)
|
||||||
|
val ll = buildLL(dirs)
|
||||||
|
val lr = buildLR(dirs)
|
||||||
|
|
||||||
|
canvas.graphicsContext2D.drawImage(ul.croppedImage(), 0, 0)
|
||||||
|
canvas.graphicsContext2D.drawImage(ur.croppedImage(), size / 2, 0)
|
||||||
|
canvas.graphicsContext2D.drawImage(ll.croppedImage(), 0, size / 2)
|
||||||
|
canvas.graphicsContext2D.drawImage(lr.croppedImage(), size / 2, size / 2)
|
||||||
|
|
||||||
|
val wi = new WritableImage(size, size)
|
||||||
|
val parameters = new SnapshotParameters(){
|
||||||
|
fill = Color.Transparent
|
||||||
|
}
|
||||||
|
val img = canvas.snapshot(parameters, wi)
|
||||||
|
StillImg(img, 0, 0, size, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def buildUL(dirs: Set[Direction]) = {
|
||||||
|
val x = if (dirs(West)) 2 else 0
|
||||||
|
val y = if (dirs(North)) 2 else 0
|
||||||
|
palette(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def buildUR(dirs: Set[Direction]) = {
|
||||||
|
val x = if (dirs(East)) 1 else 3
|
||||||
|
val y = if (dirs(North)) 2 else 0
|
||||||
|
palette(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def buildLL(dirs: Set[Direction]) = {
|
||||||
|
val x = if (dirs(West)) 2 else 0
|
||||||
|
val y = if (dirs(South)) 1 else 3
|
||||||
|
palette(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def buildLR(dirs: Set[Direction]) = {
|
||||||
|
val x = if (dirs(East)) 1 else 3
|
||||||
|
val y = if (dirs(South)) 1 else 3
|
||||||
|
palette(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait AutoTilePalette {
|
||||||
|
def apply(x: Int, y: Int): AutoTile
|
||||||
|
def tiles: IndexedSeq[AutoTile]
|
||||||
|
}
|
||||||
|
|
||||||
|
class AutoFloorTilePalette(val palette: Palette, val size: Int) extends AutoTilePalette {
|
||||||
|
final val TileWidth = 2
|
||||||
|
final val TileHeight = 3
|
||||||
|
|
||||||
|
def this(file: File, size: Int = 48) = this(Palette.bySize(new FileInputStream(file), size, size), size)
|
||||||
|
|
||||||
|
def apply(x: Int, y: Int) = {
|
||||||
|
val img = palette.apply(x * TileWidth, y * TileHeight, TileWidth, TileHeight).croppedImage()
|
||||||
|
new AutoFloorTile(new Palette(img, 2 * TileWidth, 2 * TileHeight), size)
|
||||||
|
}
|
||||||
|
|
||||||
|
def tiles = for (y <- 0 until palette.numDown / TileHeight; x <- 0 until palette.numAcross / TileWidth) yield {
|
||||||
|
this(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AutoWallTilePalette(val palette: Palette, val size: Int) extends AutoTilePalette {
|
||||||
|
final val TileWidth = 2
|
||||||
|
final val TileHeight = 2
|
||||||
|
|
||||||
|
def this(file: File, size: Int = 48) = this(Palette.bySize(new FileInputStream(file), size, size), size)
|
||||||
|
|
||||||
|
def apply(x: Int, y: Int) = {
|
||||||
|
val img = palette.apply(x * TileWidth, y * TileHeight, TileWidth, TileHeight).croppedImage()
|
||||||
|
new AutoWallTile(new Palette(img, 2 * TileWidth, 2 * TileHeight), size)
|
||||||
|
}
|
||||||
|
|
||||||
|
def tiles = for (y <- 0 until palette.numDown / TileHeight; x <- 0 until palette.numAcross / TileWidth) yield {
|
||||||
|
this(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AutoComboTilePalette(val palette: Palette, val size: Int) extends AutoTilePalette {
|
||||||
|
final val TileWidth = 2
|
||||||
|
final val CeilHeight = 3
|
||||||
|
final val WallHeight = 2
|
||||||
|
final val TotalHeight = CeilHeight + WallHeight
|
||||||
|
|
||||||
|
def this(file: File, size: Int = 48) = this(Palette.bySize(new FileInputStream(file), size, size), size)
|
||||||
|
|
||||||
|
def apply(x: Int, y: Int) = {
|
||||||
|
val yy = y / 2
|
||||||
|
if (y % 2 == 0) {
|
||||||
|
// Ceiling
|
||||||
|
val img = palette.apply(x * TileWidth, yy * TotalHeight, TileWidth, CeilHeight).croppedImage()
|
||||||
|
new AutoFloorTile(new Palette(img, 2 * TileWidth, 2 * CeilHeight), size)
|
||||||
|
} else {
|
||||||
|
// Wall
|
||||||
|
val img = palette.apply(x * TileWidth, yy * TotalHeight + CeilHeight, TileWidth, WallHeight).croppedImage()
|
||||||
|
new AutoWallTile(new Palette(img, 2 * TileWidth, 2 * WallHeight), size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def tiles = for (y <- 0 until 2 * palette.numDown / TotalHeight; x <- 0 until palette.numAcross / TileWidth) yield {
|
||||||
|
this(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BasicTilePalette(val palette: Palette, val size: Int) extends AutoTilePalette {
|
||||||
|
def this(file: File, size: Int = 48) = this(Palette.bySize(new FileInputStream(file), size, size), size)
|
||||||
|
|
||||||
|
def apply(x: Int, y: Int) = new BasicTile(palette(x, y))
|
||||||
|
def tiles = palette.images.map(new BasicTile(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
class PartitionedTilePalette(val palette: Palette, val parts: Int, val size: Int) extends AutoTilePalette {
|
||||||
|
val subwidth = palette.numAcross / parts
|
||||||
|
|
||||||
|
def this(file: File, parts: Int, size: Int = 48) = this(Palette.bySize(new FileInputStream(file), size, size), parts, size)
|
||||||
|
|
||||||
|
def apply(x: Int, y: Int) = {
|
||||||
|
val yy = y % palette.numDown
|
||||||
|
val xx = (y / palette.numDown) * subwidth
|
||||||
|
new BasicTile(palette(xx, yy))
|
||||||
|
}
|
||||||
|
def tiles = {
|
||||||
|
val tiles = palette.images.map(new BasicTile(_))
|
||||||
|
val windows = tiles.sliding(subwidth, subwidth).toSeq
|
||||||
|
(0 until parts).flatMap(i => windows.drop(i).sliding(1, parts).flatten).flatten
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object AutoTilePalette {
|
||||||
|
def a2(file: File, size: Int = 48) = new AutoFloorTilePalette(file, size)
|
||||||
|
def a3(file: File, size: Int = 48) = new AutoWallTilePalette(file, size)
|
||||||
|
def a4(file: File, size: Int = 48) = new AutoComboTilePalette(file, size)
|
||||||
|
def basic(file: File, size: Int = 48) = new BasicTilePalette(file, size)
|
||||||
|
def partitioned(file: File, parts: Int = 2, size: Int = 48) = new PartitionedTilePalette(file, parts, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
import scalafx.Includes._
|
||||||
|
import scalafx.application.JFXApp
|
||||||
|
import scalafx.scene._
|
||||||
|
import scalafx.scene.image.ImageView
|
||||||
|
import scalafx.scene.layout.TilePane
|
||||||
|
import scalafx.scene.paint.Color
|
||||||
|
|
||||||
|
import Color._
|
||||||
|
|
||||||
|
object AutoTileDemo extends JFXApp {
|
||||||
|
final val MaxX = 3
|
||||||
|
final val MaxY = 3
|
||||||
|
|
||||||
|
val file = raw"C:\Users\dalyj\Documents\Design\Images\AutoTiles\tilea2.png"
|
||||||
|
val palette = new AutoFloorTilePalette(new File(file), 32)
|
||||||
|
val tile = palette(2, 0)
|
||||||
|
val imgs = for (y <- 0 to MaxY; x <- 0 to MaxX) yield {
|
||||||
|
val dirs = (x, y) match {
|
||||||
|
case (0, 0) => Set(East, South)
|
||||||
|
case (MaxX, MaxY) => Set(West, North)
|
||||||
|
case (0, MaxY) => Set(East, North)
|
||||||
|
case (MaxX, 0) => Set(West, South)
|
||||||
|
case (0, _) => Set(East, North, South)
|
||||||
|
case (MaxX, _) => Set(West, North, South)
|
||||||
|
case (_, 0) => Set(East, West, South)
|
||||||
|
case (_, MaxY) => Set(East, West, North)
|
||||||
|
case (_, _) => Set(East, West, North, South)
|
||||||
|
}
|
||||||
|
tile.build(dirs)
|
||||||
|
}
|
||||||
|
val tiles = imgs.map(i => new ImageView(i.croppedImage()).delegate)
|
||||||
|
|
||||||
|
|
||||||
|
val pane = new TilePane(){
|
||||||
|
prefColumns = MaxX + 1
|
||||||
|
prefRows = MaxY + 1
|
||||||
|
}
|
||||||
|
pane.children ++= tiles
|
||||||
|
pane.children += new ImageView(tile.icon.croppedImage())
|
||||||
|
|
||||||
|
stage = new JFXApp.PrimaryStage {
|
||||||
|
title.value = "Hello Stage"
|
||||||
|
width = 600
|
||||||
|
height = 450
|
||||||
|
scene = new Scene {
|
||||||
|
fill = LightGreen
|
||||||
|
content = pane
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import fmon.stat._
|
|||||||
import fmon.util.{Dice, IntFraction}
|
import fmon.util.{Dice, IntFraction}
|
||||||
|
|
||||||
package object fmon {
|
package object fmon {
|
||||||
|
type Direction = fmon.util.Direction.Val
|
||||||
|
|
||||||
implicit def rngDice(rng : Random) = new Dice(rng)
|
implicit def rngDice(rng : Random) = new Dice(rng)
|
||||||
implicit def int2Helper(x : Int) = new IntFraction(x)
|
implicit def int2Helper(x : Int) = new IntFraction(x)
|
||||||
|
|||||||
5
FakeMon/src/fmon/script/Action.scala
Normal file
5
FakeMon/src/fmon/script/Action.scala
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package fmon.script
|
||||||
|
|
||||||
|
trait Action[A] {
|
||||||
|
def apply(env: A): Unit
|
||||||
|
}
|
||||||
5
FakeMon/src/fmon/script/Condition.scala
Normal file
5
FakeMon/src/fmon/script/Condition.scala
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package fmon.script
|
||||||
|
|
||||||
|
trait Condition[A] {
|
||||||
|
def apply(t: A): Boolean
|
||||||
|
}
|
||||||
7
FakeMon/src/fmon/script/action/MessageAction.scala
Normal file
7
FakeMon/src/fmon/script/action/MessageAction.scala
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package fmon.script.action
|
||||||
|
|
||||||
|
import fmon.script.Action
|
||||||
|
|
||||||
|
class MessageAction[A](val msg: String) extends Action[A] {
|
||||||
|
override def apply(env: A) = println(msg)
|
||||||
|
}
|
||||||
7
FakeMon/src/fmon/script/condition/AndCondition.scala
Normal file
7
FakeMon/src/fmon/script/condition/AndCondition.scala
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package fmon.script.condition
|
||||||
|
|
||||||
|
import fmon.script.Condition
|
||||||
|
|
||||||
|
class AndCondition[A](seq: Seq[Condition[A]]) extends Condition[A] {
|
||||||
|
def apply(env: A): Boolean = seq.forall(_(env))
|
||||||
|
}
|
||||||
7
FakeMon/src/fmon/script/condition/OrCondition.scala
Normal file
7
FakeMon/src/fmon/script/condition/OrCondition.scala
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package fmon.script.condition
|
||||||
|
|
||||||
|
import fmon.script.Condition
|
||||||
|
|
||||||
|
class OrCondition[A](seq: Seq[Condition[A]]) extends Condition[A] {
|
||||||
|
def apply(env: A): Boolean = seq.exists(_(env))
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ abstract class Ability extends Effect {
|
|||||||
|
|
||||||
class AbilityToken(
|
class AbilityToken(
|
||||||
val name: String,
|
val name: String,
|
||||||
|
val id: String,
|
||||||
val shortDesc: String,
|
val shortDesc: String,
|
||||||
val onAfterDamage: String
|
val onAfterDamage: String
|
||||||
) {
|
) {
|
||||||
@@ -32,6 +33,7 @@ class AbilityToken(
|
|||||||
val self = this
|
val self = this
|
||||||
new Ability {
|
new Ability {
|
||||||
val name = self.name
|
val name = self.name
|
||||||
|
val id = self.id
|
||||||
val desc = self.shortDesc
|
val desc = self.shortDesc
|
||||||
val onAfterDamageImp = Ability.compileOnAfterDamage(self.onAfterDamage)
|
val onAfterDamageImp = Ability.compileOnAfterDamage(self.onAfterDamage)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,29 @@
|
|||||||
package fmon.stat
|
package fmon.stat
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.module.scala.JsonScalaEnumeration
|
||||||
|
|
||||||
import scala.io.Source
|
import scala.io.Source
|
||||||
|
|
||||||
import fmon.util.YamlHelper
|
import scalafx.scene.paint.Color
|
||||||
|
|
||||||
case class Element(val name : String, val effect : Map[String, Double]) {
|
import fmon._
|
||||||
|
import fmon.util._
|
||||||
|
|
||||||
def -->(other : Element) = {
|
object Effectiveness extends Enumeration {
|
||||||
effect.getOrElse(other.name, 1.0)
|
case class Val protected(val mult: Double) extends super.Val
|
||||||
}
|
|
||||||
|
|
||||||
def <--(other : Element) = other --> this
|
val Immune = Val(Config.immuneMult)
|
||||||
|
val Resist = Val(Config.resistMult)
|
||||||
|
val Regular = Val(1.0)
|
||||||
|
val Weak = Val(Config.weakMult)
|
||||||
|
}
|
||||||
|
class EffectivenessType extends TypeReference[Effectiveness.type]
|
||||||
|
|
||||||
|
case class Element(val name : String, val bgColor: Color, val fontColor: Color, @JsonScalaEnumeration(classOf[EffectivenessType]) val effect : Map[String, Effectiveness]) {
|
||||||
|
|
||||||
|
def -->(other : Element) = other <-- this
|
||||||
|
|
||||||
|
def <--(other : Element) = effect.getOrElse(other.name, Effectiveness.Regular).mult
|
||||||
|
|
||||||
override def toString = name
|
override def toString = name
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,18 @@ package fmon.stat
|
|||||||
|
|
||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
|
|
||||||
|
import fmon._
|
||||||
|
|
||||||
case class Gene(val gender : Gender, val nature : Nature, val ivs : Map[Stat, Int], ot : TrainerID) {
|
case class Gene(val gender : Gender, val nature : Nature, val ivs : Map[Stat, Int], ot : TrainerID) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Gene {
|
object Gene {
|
||||||
final val MaxIV = 31
|
final val MaxIV = 31
|
||||||
def randomGene(ot : TrainerID, form : Form)(implicit rng : Random) = {
|
|
||||||
|
def randomGene(ot : TrainerID, form : Form)(implicit rng : Random): Gene = {
|
||||||
val gender = Gender.Neuter // TODO
|
val gender = Gender.Neuter // TODO
|
||||||
val ivs = Statistic.values.map(s => (s, rng.nextInt(MaxIV + 1))).toMap
|
val ivs = Statistic.values.toSeq.map(s => (s, rng.nextInt(MaxIV + 1))).toMap
|
||||||
Gene(gender, Nature.randomNature, ivs, ot)
|
Gene(gender, Nature.randomNature, ivs, ot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,6 +36,7 @@ trait MoveTurn extends Effect {
|
|||||||
|
|
||||||
class SwitchOut(val party: Party, val replacement: Monster) extends MoveTurn {
|
class SwitchOut(val party: Party, val replacement: Monster) extends MoveTurn {
|
||||||
def name = "Swap"
|
def name = "Swap"
|
||||||
|
def id = "swap"
|
||||||
def flags = Set()
|
def flags = Set()
|
||||||
def prior = +6
|
def prior = +6
|
||||||
def target = Target.Self
|
def target = Target.Self
|
||||||
@@ -52,11 +53,13 @@ class SwitchOut(val party: Party, val replacement: Monster) extends MoveTurn {
|
|||||||
|
|
||||||
abstract class Move extends MoveTurn {
|
abstract class Move extends MoveTurn {
|
||||||
val name: String
|
val name: String
|
||||||
|
val id: String
|
||||||
val desc: String
|
val desc: String
|
||||||
val category: MoveType
|
val category: MoveType
|
||||||
val pow: Int
|
val pow: Int
|
||||||
val powCallback: (Monster, Move, Monster, SignalConsumer, Random) => Int
|
val powCallback: (Monster, Move, Monster, SignalConsumer, Random) => Int
|
||||||
val damageCallback: (Monster, Move, Monster, SignalConsumer, Random) => Int
|
val damageCallback: (Monster, Move, Monster, SignalConsumer, Random) => Int
|
||||||
|
val onTryMove: (Monster, Move, Monster, SignalConsumer, Random) => Boolean
|
||||||
val prior: Int
|
val prior: Int
|
||||||
val accuracy: Int
|
val accuracy: Int
|
||||||
val pp: Int
|
val pp: Int
|
||||||
@@ -78,7 +81,9 @@ abstract class Move extends MoveTurn {
|
|||||||
|
|
||||||
override def useMove(user: Monster, target: Monster)(implicit reader: SignalConsumer, rng: Random) = {
|
override def useMove(user: Monster, target: Monster)(implicit reader: SignalConsumer, rng: Random) = {
|
||||||
reader ! Message(s"$user used $name.")
|
reader ! Message(s"$user used $name.")
|
||||||
|
if (onTryMove(user, this, target, reader, rng)) {
|
||||||
if (attackRoll(user, target)) {
|
if (attackRoll(user, target)) {
|
||||||
|
reader ! AnimateMsg(element, target)
|
||||||
if (pow > 0 || powCallback != null || damageCallback != null) {
|
if (pow > 0 || powCallback != null || damageCallback != null) {
|
||||||
val dmg = damageRoll(user, target)
|
val dmg = damageRoll(user, target)
|
||||||
val actualDmg = target.takeDamage(dmg)
|
val actualDmg = target.takeDamage(dmg)
|
||||||
@@ -127,6 +132,8 @@ abstract class Move extends MoveTurn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
def attackRoll(user: Monster, target: Monster)(implicit reader: SignalConsumer, rng: Random) = {
|
def attackRoll(user: Monster, target: Monster)(implicit reader: SignalConsumer, rng: Random) = {
|
||||||
if (accuracy == 0) {
|
if (accuracy == 0) {
|
||||||
true
|
true
|
||||||
@@ -142,12 +149,17 @@ abstract class Move extends MoveTurn {
|
|||||||
if (damageCallback != null) {
|
if (damageCallback != null) {
|
||||||
damageCallback(user, this, target, reader, rng)
|
damageCallback(user, this, target, reader, rng)
|
||||||
} else {
|
} else {
|
||||||
|
val crit = critMultiplier(user, target)
|
||||||
|
println(crit)
|
||||||
val atkStat = if (category == MoveType.Physical) PAtk else MAtk
|
val atkStat = if (category == MoveType.Physical) PAtk else MAtk
|
||||||
val defStat = if (category == MoveType.Physical) PDef else MDef
|
val defStat = if (category == MoveType.Physical) PDef else MDef
|
||||||
|
val atk = if (crit > 1.0) Math.max(user(atkStat), user.stats(atkStat)) else user(atkStat)
|
||||||
|
val defense = if (crit > 1.0) Math.min(target(defStat), target.stats(defStat)) else user(defStat)
|
||||||
|
println(target(defStat), target.stats(defStat), defense)
|
||||||
val actualPow = if (powCallback != null) powCallback(user, this, target, reader, rng) else pow
|
val actualPow = if (powCallback != null) powCallback(user, this, target, reader, rng) else pow
|
||||||
val baseDmg = (2 * user.level / 5 + 2) * actualPow * user(atkStat) / (target(defStat) * 50) + 2
|
val baseDmg = (2 * user.level / 5 + 2) * actualPow * atk / (defense * 50) + 2
|
||||||
val effectiveness = target.effectiveness(element)
|
val effectiveness = target.effectiveness(element)
|
||||||
val multiplier = effectiveness * critMultiplier(user, target) * stabMultiplier(user)
|
val multiplier = effectiveness * crit * stabMultiplier(user)
|
||||||
if (effectiveness > 1.0) {
|
if (effectiveness > 1.0) {
|
||||||
reader ! Message("It's super effective!")
|
reader ! Message("It's super effective!")
|
||||||
} else if (effectiveness < 1.0) {
|
} else if (effectiveness < 1.0) {
|
||||||
@@ -208,10 +220,12 @@ abstract class Move extends MoveTurn {
|
|||||||
|
|
||||||
case class MoveToken(
|
case class MoveToken(
|
||||||
val name: String,
|
val name: String,
|
||||||
|
val id: String,
|
||||||
val shortDesc: String,
|
val shortDesc: String,
|
||||||
@JsonScalaEnumeration(classOf[MoveTypeType]) val category: MoveType,
|
@JsonScalaEnumeration(classOf[MoveTypeType]) val category: MoveType,
|
||||||
val basePower: Option[Int],
|
val basePower: Option[Int],
|
||||||
val basePowerCallback: String,
|
val basePowerCallback: String,
|
||||||
|
val onTryMove: String,
|
||||||
val damage: String,
|
val damage: String,
|
||||||
val priority: Int,
|
val priority: Int,
|
||||||
val accuracy: Option[Int],
|
val accuracy: Option[Int],
|
||||||
@@ -233,11 +247,13 @@ case class MoveToken(
|
|||||||
val effectToken = SecondaryToken(100, Map(), null, token.volatileStatus, null)
|
val effectToken = SecondaryToken(100, Map(), null, token.volatileStatus, null)
|
||||||
new Move {
|
new Move {
|
||||||
val name = token.name
|
val name = token.name
|
||||||
|
val id = token.id
|
||||||
val desc = token.shortDesc
|
val desc = token.shortDesc
|
||||||
val category = token.category
|
val category = token.category
|
||||||
val pow = token.basePower.getOrElse(0)
|
val pow = token.basePower.getOrElse(0)
|
||||||
val powCallback = Move.compilePowCallback(token.basePowerCallback)
|
val powCallback = Move.compilePowCallback(token.basePowerCallback)
|
||||||
val damageCallback = Move.compileDamageCallback(token.damage)
|
val damageCallback = Move.compileDamageCallback(token.damage)
|
||||||
|
val onTryMove = Move.compileOnTryMove(token.onTryMove)
|
||||||
val prior = token.priority
|
val prior = token.priority
|
||||||
val pp = token.pp
|
val pp = token.pp
|
||||||
val element = Element(token.`type`)
|
val element = Element(token.`type`)
|
||||||
@@ -266,6 +282,14 @@ object Move {
|
|||||||
moves(name)
|
moves(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val helpers = """
|
||||||
|
implicit val dice = rng
|
||||||
|
implicit val consumer = reader
|
||||||
|
def msg(text: String): Unit = {
|
||||||
|
reader ! Message(text)
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
def compilePowCallback(code: String): (Monster, Move, Monster, SignalConsumer, Random) => Int = {
|
def compilePowCallback(code: String): (Monster, Move, Monster, SignalConsumer, Random) => Int = {
|
||||||
if (code != null) {
|
if (code != null) {
|
||||||
val tb = runtimeMirror(getClass.getClassLoader).mkToolBox()
|
val tb = runtimeMirror(getClass.getClassLoader).mkToolBox()
|
||||||
@@ -294,4 +318,29 @@ object Move {
|
|||||||
def compileDamageCallback(code: String): (Monster, Move, Monster, SignalConsumer, Random) => Int = {
|
def compileDamageCallback(code: String): (Monster, Move, Monster, SignalConsumer, Random) => Int = {
|
||||||
compilePowCallback(code)
|
compilePowCallback(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def compileOnTryMove(code: String): (Monster, Move, Monster, SignalConsumer, Random) => Boolean = {
|
||||||
|
if (code == null) {
|
||||||
|
(_, _, _, _, _) => true
|
||||||
|
} else {
|
||||||
|
val tb = runtimeMirror(getClass.getClassLoader).mkToolBox()
|
||||||
|
val tree = tb.parse(
|
||||||
|
s"""
|
||||||
|
|import scala.util.Random
|
||||||
|
|import fmon._
|
||||||
|
|import fmon.battle.msg._
|
||||||
|
|import fmon.stat._
|
||||||
|
|import fmon.stat.Statistic._
|
||||||
|
|def callback(user : Monster, move: Move, target : Monster, reader: SignalConsumer, rng: Random): Boolean = {
|
||||||
|
| $helpers
|
||||||
|
| $code
|
||||||
|
|}
|
||||||
|
|callback _
|
||||||
|
""".stripMargin)
|
||||||
|
val f = tb.compile(tree)
|
||||||
|
val wrapper = f()
|
||||||
|
|
||||||
|
wrapper.asInstanceOf[(Monster, Move, Monster, SignalConsumer, Random) => Boolean]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,12 +11,12 @@ object Statistic extends Enumeration {
|
|||||||
val Evasion = Value("Evasion")
|
val Evasion = Value("Evasion")
|
||||||
|
|
||||||
def apply(s : String) = s match {
|
def apply(s : String) = s match {
|
||||||
case "hp" => Hp
|
case "hp" | "Hp" => Hp
|
||||||
case "patk" => PAtk
|
case "patk" | "PAtk" => PAtk
|
||||||
case "pdef" => PDef
|
case "pdef" | "PDef" => PDef
|
||||||
case "matk" => MAtk
|
case "matk" | "MAtk" => MAtk
|
||||||
case "mdef" => MDef
|
case "mdef" | "MDef" => MDef
|
||||||
case "spd" => Speed
|
case "spd" | "Spd" => Speed
|
||||||
case "acc" => Accuracy
|
case "acc" => Accuracy
|
||||||
case "evd" => Evasion
|
case "evd" => Evasion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ object EffectType extends Enumeration {
|
|||||||
|
|
||||||
trait Effect {
|
trait Effect {
|
||||||
def name: String
|
def name: String
|
||||||
|
def id: String
|
||||||
def effectType: EffectType
|
def effectType: EffectType
|
||||||
|
|
||||||
def isItem = effectType == EffectType.ItemEffect
|
def isItem = effectType == EffectType.ItemEffect
|
||||||
@@ -37,6 +38,9 @@ trait Effect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case class EffectSource(mon: Monster, effect: Effect) {
|
case class EffectSource(mon: Monster, effect: Effect) {
|
||||||
|
def name = effect.name
|
||||||
|
def id = effect.id
|
||||||
|
|
||||||
def isItem = effect.isItem
|
def isItem = effect.isItem
|
||||||
def isMove = effect.isMove
|
def isMove = effect.isMove
|
||||||
def isAbility = effect.isAbility
|
def isAbility = effect.isAbility
|
||||||
@@ -47,7 +51,7 @@ case class EffectSource(mon: Monster, effect: Effect) {
|
|||||||
|
|
||||||
class Status(template: StatusTemplate) extends Effect {
|
class Status(template: StatusTemplate) extends Effect {
|
||||||
def name = template.name
|
def name = template.name
|
||||||
// val id
|
def id = template.id
|
||||||
def effectType = template.effectType
|
def effectType = template.effectType
|
||||||
def onStart(mon: Monster, source: EffectSource)(implicit reader: SignalConsumer, rng: Random) = template.onStart(this, mon, source, reader, rng)
|
def onStart(mon: Monster, source: EffectSource)(implicit reader: SignalConsumer, rng: Random) = template.onStart(this, mon, source, reader, rng)
|
||||||
def onEnd(mon: Monster)(implicit reader: SignalConsumer, rng: Random) = template.onEnd(this, mon, reader, rng)
|
def onEnd(mon: Monster)(implicit reader: SignalConsumer, rng: Random) = template.onEnd(this, mon, reader, rng)
|
||||||
@@ -62,14 +66,14 @@ class Status(template: StatusTemplate) extends Effect {
|
|||||||
def onLockMove(mon: Monster)(implicit reader: SignalConsumer, rng: Random) = template.onLockMove(this, mon, reader, rng)
|
def onLockMove(mon: Monster)(implicit reader: SignalConsumer, rng: Random) = template.onLockMove(this, mon, reader, rng)
|
||||||
|
|
||||||
val intData: MutMap[String, Int] = MutMap[String, Int]()
|
val intData: MutMap[String, Int] = MutMap[String, Int]()
|
||||||
val stringData = MutMap[String, String]()
|
val strData = MutMap[String, String]()
|
||||||
|
|
||||||
override def toString = name
|
override def toString = name
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class StatusTemplate {
|
abstract class StatusTemplate {
|
||||||
val name: String
|
val name: String
|
||||||
// val id
|
val id: String
|
||||||
val effectType: EffectType
|
val effectType: EffectType
|
||||||
val onStart: (Status, Monster, EffectSource, SignalConsumer, Random) => Unit
|
val onStart: (Status, Monster, EffectSource, SignalConsumer, Random) => Unit
|
||||||
val onEnd: (Status, Monster, SignalConsumer, Random) => Unit
|
val onEnd: (Status, Monster, SignalConsumer, Random) => Unit
|
||||||
@@ -90,6 +94,7 @@ abstract class StatusTemplate {
|
|||||||
|
|
||||||
case class StatusToken(
|
case class StatusToken(
|
||||||
val name: String,
|
val name: String,
|
||||||
|
val id: String,
|
||||||
val effectType: String,
|
val effectType: String,
|
||||||
val onStart: String,
|
val onStart: String,
|
||||||
val onEnd: String,
|
val onEnd: String,
|
||||||
@@ -102,6 +107,7 @@ case class StatusToken(
|
|||||||
val self = this
|
val self = this
|
||||||
new StatusTemplate {
|
new StatusTemplate {
|
||||||
val name = self.name
|
val name = self.name
|
||||||
|
val id = self.id
|
||||||
val effectType = EffectType.parse(self.effectType, Volatile)
|
val effectType = EffectType.parse(self.effectType, Volatile)
|
||||||
val onStart = Status.compileOnStart(self.onStart)
|
val onStart = Status.compileOnStart(self.onStart)
|
||||||
val onEnd = Status.compileOnEnd(self.onEnd)
|
val onEnd = Status.compileOnEnd(self.onEnd)
|
||||||
|
|||||||
@@ -1,174 +1,523 @@
|
|||||||
Bug:
|
---
|
||||||
effect:
|
|
||||||
Dark: 2.0
|
|
||||||
Fairy: 0.5
|
|
||||||
Fighting: 0.5
|
|
||||||
Fire: 0.5
|
|
||||||
Flying: 0.5
|
|
||||||
Ghost: 0.5
|
|
||||||
Grass: 2.0
|
|
||||||
Poison: 0.5
|
|
||||||
Psychic: 2.0
|
|
||||||
Steel: 0.5
|
|
||||||
name: Bug
|
|
||||||
Dark:
|
|
||||||
effect:
|
|
||||||
Dark: 0.5
|
|
||||||
Fairy: 0.5
|
|
||||||
Fighting: 0.5
|
|
||||||
Ghost: 2.0
|
|
||||||
Psychic: 2.0
|
|
||||||
name: Dark
|
|
||||||
Dragon:
|
|
||||||
effect:
|
|
||||||
Dragon: 2.0
|
|
||||||
Fairy: 0.0
|
|
||||||
Steel: 0.5
|
|
||||||
name: Dragon
|
|
||||||
Electric:
|
|
||||||
effect:
|
|
||||||
Dragon: 0.5
|
|
||||||
Electric: 0.5
|
|
||||||
Flying: 2.0
|
|
||||||
Grass: 0.5
|
|
||||||
Ground: 0.0
|
|
||||||
Water: 2.0
|
|
||||||
name: Electric
|
|
||||||
Fairy:
|
|
||||||
effect:
|
|
||||||
Dark: 2.0
|
|
||||||
Dragon: 2.0
|
|
||||||
Fighting: 2.0
|
|
||||||
Fire: 0.5
|
|
||||||
Poison: 0.5
|
|
||||||
Steel: 0.5
|
|
||||||
name: Fairy
|
|
||||||
Fighting:
|
|
||||||
effect:
|
|
||||||
Bug: 0.5
|
|
||||||
Dark: 2.0
|
|
||||||
Fairy: 0.5
|
|
||||||
Flying: 0.5
|
|
||||||
Ghost: 0.0
|
|
||||||
Ice: 2.0
|
|
||||||
Normal: 2.0
|
|
||||||
Poison: 0.5
|
|
||||||
Psychic: 0.5
|
|
||||||
Rock: 2.0
|
|
||||||
Steel: 2.0
|
|
||||||
name: Fighting
|
|
||||||
Fire:
|
|
||||||
effect:
|
|
||||||
Bug: 2.0
|
|
||||||
Dragon: 0.5
|
|
||||||
Fire: 0.5
|
|
||||||
Grass: 2.0
|
|
||||||
Ice: 2.0
|
|
||||||
Rock: 0.5
|
|
||||||
Steel: 2.0
|
|
||||||
Water: 0.5
|
|
||||||
name: Fire
|
|
||||||
Flying:
|
|
||||||
effect:
|
|
||||||
Bug: 2.0
|
|
||||||
Electric: 0.5
|
|
||||||
Fighting: 2.0
|
|
||||||
Grass: 2.0
|
|
||||||
Rock: 0.5
|
|
||||||
Steel: 0.5
|
|
||||||
name: Flying
|
|
||||||
Ghost:
|
|
||||||
effect:
|
|
||||||
Dark: 0.5
|
|
||||||
Ghost: 2.0
|
|
||||||
Normal: 0.0
|
|
||||||
Psychic: 2.0
|
|
||||||
name: Ghost
|
|
||||||
Grass:
|
|
||||||
effect:
|
|
||||||
Bug: 0.5
|
|
||||||
Dragon: 0.5
|
|
||||||
Fire: 0.5
|
|
||||||
Flying: 0.5
|
|
||||||
Grass: 0.5
|
|
||||||
Ground: 2.0
|
|
||||||
Poison: 0.5
|
|
||||||
Rock: 2.0
|
|
||||||
Steel: 0.5
|
|
||||||
Water: 2.0
|
|
||||||
name: Grass
|
|
||||||
Ground:
|
|
||||||
effect:
|
|
||||||
Bug: 0.5
|
|
||||||
Electric: 2.0
|
|
||||||
Fire: 2.0
|
|
||||||
Flying: 0.0
|
|
||||||
Grass: 0.5
|
|
||||||
Poison: 2.0
|
|
||||||
Rock: 2.0
|
|
||||||
Steel: 2.0
|
|
||||||
name: Ground
|
|
||||||
Ice:
|
|
||||||
effect:
|
|
||||||
Dragon: 2.0
|
|
||||||
Fire: 0.5
|
|
||||||
Flying: 2.0
|
|
||||||
Grass: 2.0
|
|
||||||
Ground: 2.0
|
|
||||||
Ice: 0.5
|
|
||||||
Steel: 0.5
|
|
||||||
Water: 0.5
|
|
||||||
name: Ice
|
|
||||||
Normal:
|
Normal:
|
||||||
|
name: "Normal"
|
||||||
|
bgColor:
|
||||||
|
red: 0.501960813999176
|
||||||
|
green: 0.501960813999176
|
||||||
|
blue: 0.501960813999176
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
effect:
|
effect:
|
||||||
Ghost: 0.0
|
Normal: Regular
|
||||||
Rock: 0.5
|
Electric: Regular
|
||||||
Steel: 0.5
|
Poison: Regular
|
||||||
name: Normal
|
Rock: Regular
|
||||||
|
Flying: Regular
|
||||||
|
Ghost: Immune
|
||||||
|
Ice: Regular
|
||||||
|
Water: Regular
|
||||||
|
Fighting: Weak
|
||||||
|
Ground: Regular
|
||||||
|
Steel: Regular
|
||||||
|
Bug: Regular
|
||||||
|
Fire: Regular
|
||||||
|
Fairy: Regular
|
||||||
|
Dragon: Regular
|
||||||
|
Psychic: Regular
|
||||||
|
Grass: Regular
|
||||||
|
Dark: Regular
|
||||||
|
Electric:
|
||||||
|
name: "Electric"
|
||||||
|
bgColor:
|
||||||
|
red: 0.9019607901573181
|
||||||
|
green: 0.9019607901573181
|
||||||
|
blue: 0.3019607961177826
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
|
effect:
|
||||||
|
Normal: Regular
|
||||||
|
Electric: Resist
|
||||||
|
Poison: Regular
|
||||||
|
Rock: Regular
|
||||||
|
Flying: Resist
|
||||||
|
Ghost: Regular
|
||||||
|
Ice: Regular
|
||||||
|
Water: Regular
|
||||||
|
Fighting: Regular
|
||||||
|
Ground: Weak
|
||||||
|
Steel: Resist
|
||||||
|
Bug: Regular
|
||||||
|
Fire: Regular
|
||||||
|
Fairy: Regular
|
||||||
|
Dragon: Regular
|
||||||
|
Psychic: Regular
|
||||||
|
Grass: Regular
|
||||||
|
Dark: Regular
|
||||||
Poison:
|
Poison:
|
||||||
|
name: "Poison"
|
||||||
|
bgColor:
|
||||||
|
red: 0.6000000238418579
|
||||||
|
green: 0.4000000059604645
|
||||||
|
blue: 0.6000000238418579
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
effect:
|
effect:
|
||||||
Fairy: 2.0
|
Normal: Regular
|
||||||
Ghost: 0.5
|
Electric: Regular
|
||||||
Grass: 2.0
|
Poison: Resist
|
||||||
Ground: 0.5
|
Rock: Regular
|
||||||
Poison: 0.5
|
Flying: Regular
|
||||||
Rock: 0.5
|
Ghost: Regular
|
||||||
Steel: 0.0
|
Ice: Regular
|
||||||
name: Poison
|
Water: Regular
|
||||||
Psychic:
|
Fighting: Resist
|
||||||
effect:
|
Ground: Weak
|
||||||
Dark: 0.0
|
Steel: Regular
|
||||||
Fighting: 2.0
|
Bug: Resist
|
||||||
Poison: 2.0
|
Fire: Regular
|
||||||
Psychic: 0.5
|
Fairy: Resist
|
||||||
Steel: 0.5
|
Dragon: Regular
|
||||||
name: Psychic
|
Psychic: Weak
|
||||||
|
Grass: Resist
|
||||||
|
Dark: Regular
|
||||||
Rock:
|
Rock:
|
||||||
|
name: "Rock"
|
||||||
|
bgColor:
|
||||||
|
red: 0.6000000238418579
|
||||||
|
green: 0.4000000059604645
|
||||||
|
blue: 0.0
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
effect:
|
effect:
|
||||||
Bug: 2.0
|
Normal: Resist
|
||||||
Fighting: 0.5
|
Electric: Regular
|
||||||
Fire: 2.0
|
Poison: Resist
|
||||||
Flying: 2.0
|
Rock: Regular
|
||||||
Ground: 0.5
|
Flying: Resist
|
||||||
Ice: 2.0
|
Ghost: Regular
|
||||||
Steel: 0.5
|
Ice: Regular
|
||||||
name: Rock
|
Water: Weak
|
||||||
Steel:
|
Fighting: Weak
|
||||||
|
Ground: Weak
|
||||||
|
Steel: Weak
|
||||||
|
Bug: Regular
|
||||||
|
Fire: Resist
|
||||||
|
Fairy: Regular
|
||||||
|
Dragon: Regular
|
||||||
|
Psychic: Regular
|
||||||
|
Grass: Weak
|
||||||
|
Dark: Regular
|
||||||
|
Flying:
|
||||||
|
name: "Flying"
|
||||||
|
bgColor:
|
||||||
|
red: 0.7019608020782471
|
||||||
|
green: 0.6000000238418579
|
||||||
|
blue: 1.0
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
effect:
|
effect:
|
||||||
Electric: 0.5
|
Normal: Regular
|
||||||
Fairy: 2.0
|
Electric: Weak
|
||||||
Fire: 0.5
|
Poison: Regular
|
||||||
Ice: 2.0
|
Rock: Weak
|
||||||
Rock: 2.0
|
Flying: Regular
|
||||||
Steel: 0.5
|
Ghost: Regular
|
||||||
Water: 0.5
|
Ice: Weak
|
||||||
name: Steel
|
Water: Regular
|
||||||
|
Fighting: Resist
|
||||||
|
Ground: Immune
|
||||||
|
Steel: Regular
|
||||||
|
Bug: Resist
|
||||||
|
Fire: Regular
|
||||||
|
Fairy: Regular
|
||||||
|
Dragon: Regular
|
||||||
|
Psychic: Regular
|
||||||
|
Grass: Resist
|
||||||
|
Dark: Regular
|
||||||
|
Ghost:
|
||||||
|
name: "Ghost"
|
||||||
|
bgColor:
|
||||||
|
red: 0.501960813999176
|
||||||
|
green: 0.3019607961177826
|
||||||
|
blue: 0.501960813999176
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
|
effect:
|
||||||
|
Normal: Immune
|
||||||
|
Electric: Regular
|
||||||
|
Poison: Resist
|
||||||
|
Rock: Regular
|
||||||
|
Flying: Regular
|
||||||
|
Ghost: Weak
|
||||||
|
Ice: Regular
|
||||||
|
Water: Regular
|
||||||
|
Fighting: Immune
|
||||||
|
Ground: Regular
|
||||||
|
Steel: Regular
|
||||||
|
Bug: Resist
|
||||||
|
Fire: Regular
|
||||||
|
Fairy: Regular
|
||||||
|
Dragon: Regular
|
||||||
|
Psychic: Regular
|
||||||
|
Grass: Regular
|
||||||
|
Dark: Weak
|
||||||
|
Ice:
|
||||||
|
name: "Ice"
|
||||||
|
bgColor:
|
||||||
|
red: 0.7019608020782471
|
||||||
|
green: 0.9019607901573181
|
||||||
|
blue: 0.9019607901573181
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
|
effect:
|
||||||
|
Normal: Regular
|
||||||
|
Electric: Regular
|
||||||
|
Poison: Regular
|
||||||
|
Rock: Weak
|
||||||
|
Flying: Regular
|
||||||
|
Ghost: Regular
|
||||||
|
Ice: Resist
|
||||||
|
Water: Regular
|
||||||
|
Fighting: Weak
|
||||||
|
Ground: Regular
|
||||||
|
Steel: Weak
|
||||||
|
Bug: Regular
|
||||||
|
Fire: Weak
|
||||||
|
Fairy: Regular
|
||||||
|
Dragon: Regular
|
||||||
|
Psychic: Regular
|
||||||
|
Grass: Regular
|
||||||
|
Dark: Regular
|
||||||
Water:
|
Water:
|
||||||
|
name: "Water"
|
||||||
|
bgColor:
|
||||||
|
red: 0.501960813999176
|
||||||
|
green: 0.6000000238418579
|
||||||
|
blue: 1.0
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
effect:
|
effect:
|
||||||
Dragon: 0.5
|
Normal: Regular
|
||||||
Fire: 2.0
|
Electric: Weak
|
||||||
Grass: 0.5
|
Poison: Regular
|
||||||
Ground: 2.0
|
Rock: Regular
|
||||||
Rock: 2.0
|
Flying: Regular
|
||||||
Water: 0.5
|
Ghost: Regular
|
||||||
name: Water
|
Ice: Resist
|
||||||
|
Water: Resist
|
||||||
|
Fighting: Regular
|
||||||
|
Ground: Regular
|
||||||
|
Steel: Resist
|
||||||
|
Bug: Regular
|
||||||
|
Fire: Resist
|
||||||
|
Fairy: Regular
|
||||||
|
Dragon: Regular
|
||||||
|
Psychic: Regular
|
||||||
|
Grass: Weak
|
||||||
|
Dark: Regular
|
||||||
|
Fighting:
|
||||||
|
name: "Fighting"
|
||||||
|
bgColor:
|
||||||
|
red: 0.800000011920929
|
||||||
|
green: 0.20000000298023224
|
||||||
|
blue: 0.20000000298023224
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
|
effect:
|
||||||
|
Normal: Regular
|
||||||
|
Electric: Regular
|
||||||
|
Poison: Regular
|
||||||
|
Rock: Resist
|
||||||
|
Flying: Weak
|
||||||
|
Ghost: Regular
|
||||||
|
Ice: Regular
|
||||||
|
Water: Regular
|
||||||
|
Fighting: Regular
|
||||||
|
Ground: Regular
|
||||||
|
Steel: Regular
|
||||||
|
Bug: Resist
|
||||||
|
Fire: Regular
|
||||||
|
Fairy: Weak
|
||||||
|
Dragon: Regular
|
||||||
|
Psychic: Weak
|
||||||
|
Grass: Regular
|
||||||
|
Dark: Resist
|
||||||
|
Ground:
|
||||||
|
name: "Ground"
|
||||||
|
bgColor:
|
||||||
|
red: 0.9019607901573181
|
||||||
|
green: 0.7019608020782471
|
||||||
|
blue: 0.3019607961177826
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
|
effect:
|
||||||
|
Normal: Regular
|
||||||
|
Electric: Immune
|
||||||
|
Poison: Resist
|
||||||
|
Rock: Resist
|
||||||
|
Flying: Regular
|
||||||
|
Ghost: Regular
|
||||||
|
Ice: Weak
|
||||||
|
Water: Weak
|
||||||
|
Fighting: Regular
|
||||||
|
Ground: Regular
|
||||||
|
Steel: Regular
|
||||||
|
Bug: Regular
|
||||||
|
Fire: Regular
|
||||||
|
Fairy: Regular
|
||||||
|
Dragon: Regular
|
||||||
|
Psychic: Regular
|
||||||
|
Grass: Weak
|
||||||
|
Dark: Regular
|
||||||
|
Steel:
|
||||||
|
name: "Steel"
|
||||||
|
bgColor:
|
||||||
|
red: 0.800000011920929
|
||||||
|
green: 0.800000011920929
|
||||||
|
blue: 0.800000011920929
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
|
effect:
|
||||||
|
Normal: Resist
|
||||||
|
Electric: Regular
|
||||||
|
Poison: Immune
|
||||||
|
Rock: Resist
|
||||||
|
Flying: Resist
|
||||||
|
Ghost: Regular
|
||||||
|
Ice: Resist
|
||||||
|
Water: Regular
|
||||||
|
Fighting: Weak
|
||||||
|
Ground: Weak
|
||||||
|
Steel: Resist
|
||||||
|
Bug: Resist
|
||||||
|
Fire: Weak
|
||||||
|
Fairy: Resist
|
||||||
|
Dragon: Resist
|
||||||
|
Psychic: Resist
|
||||||
|
Grass: Resist
|
||||||
|
Dark: Regular
|
||||||
|
Bug:
|
||||||
|
name: "Bug"
|
||||||
|
bgColor:
|
||||||
|
red: 0.7019608020782471
|
||||||
|
green: 0.9019607901573181
|
||||||
|
blue: 0.7019608020782471
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
|
effect:
|
||||||
|
Normal: Regular
|
||||||
|
Electric: Regular
|
||||||
|
Poison: Regular
|
||||||
|
Rock: Weak
|
||||||
|
Flying: Weak
|
||||||
|
Ghost: Regular
|
||||||
|
Ice: Regular
|
||||||
|
Water: Regular
|
||||||
|
Fighting: Resist
|
||||||
|
Ground: Resist
|
||||||
|
Steel: Regular
|
||||||
|
Bug: Regular
|
||||||
|
Fire: Weak
|
||||||
|
Fairy: Regular
|
||||||
|
Dragon: Regular
|
||||||
|
Psychic: Regular
|
||||||
|
Grass: Resist
|
||||||
|
Dark: Regular
|
||||||
|
Fire:
|
||||||
|
name: "Fire"
|
||||||
|
bgColor:
|
||||||
|
red: 0.9019607901573181
|
||||||
|
green: 0.501960813999176
|
||||||
|
blue: 0.3019607961177826
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
|
effect:
|
||||||
|
Normal: Regular
|
||||||
|
Electric: Regular
|
||||||
|
Poison: Regular
|
||||||
|
Rock: Weak
|
||||||
|
Flying: Regular
|
||||||
|
Ghost: Regular
|
||||||
|
Ice: Resist
|
||||||
|
Water: Weak
|
||||||
|
Fighting: Regular
|
||||||
|
Ground: Weak
|
||||||
|
Steel: Resist
|
||||||
|
Bug: Resist
|
||||||
|
Fire: Resist
|
||||||
|
Fairy: Resist
|
||||||
|
Dragon: Regular
|
||||||
|
Psychic: Regular
|
||||||
|
Grass: Resist
|
||||||
|
Dark: Regular
|
||||||
|
Fairy:
|
||||||
|
name: "Fairy"
|
||||||
|
bgColor:
|
||||||
|
red: 0.9019607901573181
|
||||||
|
green: 0.7019608020782471
|
||||||
|
blue: 0.800000011920929
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
|
effect:
|
||||||
|
Normal: Regular
|
||||||
|
Electric: Regular
|
||||||
|
Poison: Weak
|
||||||
|
Rock: Regular
|
||||||
|
Flying: Regular
|
||||||
|
Ghost: Regular
|
||||||
|
Ice: Regular
|
||||||
|
Water: Regular
|
||||||
|
Fighting: Resist
|
||||||
|
Ground: Regular
|
||||||
|
Steel: Weak
|
||||||
|
Bug: Resist
|
||||||
|
Fire: Regular
|
||||||
|
Fairy: Regular
|
||||||
|
Dragon: Immune
|
||||||
|
Psychic: Regular
|
||||||
|
Grass: Regular
|
||||||
|
Dark: Resist
|
||||||
|
Dragon:
|
||||||
|
name: "Dragon"
|
||||||
|
bgColor:
|
||||||
|
red: 0.4000000059604645
|
||||||
|
green: 0.20000000298023224
|
||||||
|
blue: 0.4000000059604645
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
|
effect:
|
||||||
|
Normal: Regular
|
||||||
|
Electric: Resist
|
||||||
|
Poison: Regular
|
||||||
|
Rock: Regular
|
||||||
|
Flying: Regular
|
||||||
|
Ghost: Regular
|
||||||
|
Ice: Weak
|
||||||
|
Water: Resist
|
||||||
|
Fighting: Regular
|
||||||
|
Ground: Regular
|
||||||
|
Steel: Regular
|
||||||
|
Bug: Regular
|
||||||
|
Fire: Resist
|
||||||
|
Fairy: Weak
|
||||||
|
Dragon: Weak
|
||||||
|
Psychic: Regular
|
||||||
|
Grass: Resist
|
||||||
|
Dark: Regular
|
||||||
|
Psychic:
|
||||||
|
name: "Psychic"
|
||||||
|
bgColor:
|
||||||
|
red: 1.0
|
||||||
|
green: 0.501960813999176
|
||||||
|
blue: 0.501960813999176
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
|
effect:
|
||||||
|
Normal: Regular
|
||||||
|
Electric: Regular
|
||||||
|
Poison: Regular
|
||||||
|
Rock: Regular
|
||||||
|
Flying: Regular
|
||||||
|
Ghost: Weak
|
||||||
|
Ice: Regular
|
||||||
|
Water: Regular
|
||||||
|
Fighting: Resist
|
||||||
|
Ground: Regular
|
||||||
|
Steel: Regular
|
||||||
|
Bug: Weak
|
||||||
|
Fire: Regular
|
||||||
|
Fairy: Regular
|
||||||
|
Dragon: Regular
|
||||||
|
Psychic: Resist
|
||||||
|
Grass: Regular
|
||||||
|
Dark: Weak
|
||||||
|
Grass:
|
||||||
|
name: "Grass"
|
||||||
|
bgColor:
|
||||||
|
red: 0.6000000238418579
|
||||||
|
green: 0.800000011920929
|
||||||
|
blue: 0.6000000238418579
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
|
effect:
|
||||||
|
Normal: Regular
|
||||||
|
Electric: Resist
|
||||||
|
Poison: Weak
|
||||||
|
Rock: Regular
|
||||||
|
Flying: Weak
|
||||||
|
Ghost: Regular
|
||||||
|
Ice: Weak
|
||||||
|
Water: Resist
|
||||||
|
Fighting: Regular
|
||||||
|
Ground: Resist
|
||||||
|
Steel: Regular
|
||||||
|
Bug: Weak
|
||||||
|
Fire: Weak
|
||||||
|
Fairy: Regular
|
||||||
|
Dragon: Regular
|
||||||
|
Psychic: Regular
|
||||||
|
Grass: Resist
|
||||||
|
Dark: Regular
|
||||||
|
Dark:
|
||||||
|
name: "Dark"
|
||||||
|
bgColor:
|
||||||
|
red: 0.3019607961177826
|
||||||
|
green: 0.3019607961177826
|
||||||
|
blue: 0.3019607961177826
|
||||||
|
fontColor:
|
||||||
|
red: 0.0
|
||||||
|
green: 0.0
|
||||||
|
blue: 0.0
|
||||||
|
effect:
|
||||||
|
Normal: Regular
|
||||||
|
Electric: Regular
|
||||||
|
Poison: Regular
|
||||||
|
Rock: Regular
|
||||||
|
Flying: Regular
|
||||||
|
Ghost: Resist
|
||||||
|
Ice: Regular
|
||||||
|
Water: Regular
|
||||||
|
Fighting: Weak
|
||||||
|
Ground: Regular
|
||||||
|
Steel: Regular
|
||||||
|
Bug: Weak
|
||||||
|
Fire: Regular
|
||||||
|
Fairy: Weak
|
||||||
|
Dragon: Regular
|
||||||
|
Psychic: Immune
|
||||||
|
Grass: Regular
|
||||||
|
Dark: Resist
|
||||||
|
|||||||
@@ -5139,6 +5139,42 @@ razorshell:
|
|||||||
target: Normal
|
target: Normal
|
||||||
type: Water
|
type: Water
|
||||||
zMovePower: 140
|
zMovePower: 140
|
||||||
|
|
||||||
|
razorwind:
|
||||||
|
accuracy: 100
|
||||||
|
basePower: 80
|
||||||
|
category: Special
|
||||||
|
contestType: Cool
|
||||||
|
critRatio: 2
|
||||||
|
desc: Has a higher chance for a critical hit. This attack charges on the first turn
|
||||||
|
and executes on the second. If the user is holding a Power Herb, the move completes
|
||||||
|
in one turn.
|
||||||
|
flags:
|
||||||
|
charge: 1
|
||||||
|
mirror: 1
|
||||||
|
protect: 1
|
||||||
|
id: razorwind
|
||||||
|
name: Razor Wind
|
||||||
|
num: 13
|
||||||
|
onTryMove: |-
|
||||||
|
if (user.statuses.exists(_.id == "twoturnmove")) {
|
||||||
|
user -= Status("twoturnmove")
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
msg(s"${user} drew in power!")
|
||||||
|
/*if (!this.runEvent('ChargeMove', attacker, defender, move)) {
|
||||||
|
return;
|
||||||
|
}*/
|
||||||
|
user.addStatus(Status("twoturnmove"), EffectSource(user, move))
|
||||||
|
false
|
||||||
|
}
|
||||||
|
pp: 10
|
||||||
|
priority: 0
|
||||||
|
secondary: null
|
||||||
|
shortDesc: Charges, then hits foe(s) turn 2. High crit ratio.
|
||||||
|
target: AllAdjacentFoes
|
||||||
|
type: Normal
|
||||||
|
zMovePower: 160
|
||||||
recharge:
|
recharge:
|
||||||
accuracy: 100
|
accuracy: 100
|
||||||
basePower: 0
|
basePower: 0
|
||||||
|
|||||||
@@ -180,3 +180,16 @@ tox:
|
|||||||
}
|
}
|
||||||
mon.takeDamage(self.intData("stage") \\ 16 * mon(Hp));
|
mon.takeDamage(self.intData("stage") \\ 16 * mon(Hp));
|
||||||
msg(s"${mon} was damaged by poison!")
|
msg(s"${mon} was damaged by poison!")
|
||||||
|
|
||||||
|
twoturnmove:
|
||||||
|
duration: 2
|
||||||
|
id: twoturnmove
|
||||||
|
name: twoturnmove
|
||||||
|
num: 0
|
||||||
|
onLockMove: self.strData("move")
|
||||||
|
onStart: |-
|
||||||
|
self.strData("move") = source.id
|
||||||
|
//target.addVolatile(effect.id, source)
|
||||||
|
//this.attrLastMove("[still]")
|
||||||
|
onMoveAborted: |-
|
||||||
|
mon -= self
|
||||||
@@ -8,6 +8,7 @@ import fmon.util.Dice
|
|||||||
|
|
||||||
package object stat {
|
package object stat {
|
||||||
type Stat = Statistic.Value
|
type Stat = Statistic.Value
|
||||||
|
type Effectiveness = Effectiveness.Val
|
||||||
type EffectType = EffectType.Value
|
type EffectType = EffectType.Value
|
||||||
type MoveType = MoveType.Value
|
type MoveType = MoveType.Value
|
||||||
type Gender = Gender.Value
|
type Gender = Gender.Value
|
||||||
|
|||||||
22
FakeMon/src/fmon/util/Direction.scala
Normal file
22
FakeMon/src/fmon/util/Direction.scala
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package fmon.util
|
||||||
|
|
||||||
|
import fmon.Direction
|
||||||
|
|
||||||
|
object Direction extends Enumeration {
|
||||||
|
case class Val protected(x: Int = 0, y: Int = 0) extends super.Val {
|
||||||
|
}
|
||||||
|
|
||||||
|
val Stationary = Val()
|
||||||
|
val North = Val(y = -1)
|
||||||
|
val Northeast = Val(x = 1, y = -1)
|
||||||
|
val East = Val(x = 1)
|
||||||
|
val Southeast = Val(x = 1, y = 1)
|
||||||
|
val South = Val(y = 1)
|
||||||
|
val Southwest = Val(x = -1, y = 1)
|
||||||
|
val West = Val(x = -1)
|
||||||
|
val Northwest = Val(x = -1, y = -1)
|
||||||
|
|
||||||
|
val cardinals = Set(North, East, South, West)
|
||||||
|
|
||||||
|
def byName(s: String): Direction = super.withName(s).asInstanceOf[Direction]
|
||||||
|
}
|
||||||
@@ -1,20 +1,55 @@
|
|||||||
package fmon.util
|
package fmon.util
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
import com.fasterxml.jackson.core._
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind._
|
||||||
|
import com.fasterxml.jackson.databind.ser._
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.StdSerializer
|
||||||
|
import com.fasterxml.jackson.databind.deser._
|
||||||
|
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode
|
||||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
||||||
import com.fasterxml.jackson.module.scala.DefaultScalaModule
|
import com.fasterxml.jackson.module.scala.DefaultScalaModule
|
||||||
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
|
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
|
||||||
|
|
||||||
import java.io.InputStream
|
import scalafx.scene.paint.Color
|
||||||
|
|
||||||
|
import java.io.{InputStream, OutputStream}
|
||||||
|
|
||||||
import scala.io.Source
|
import scala.io.Source
|
||||||
|
|
||||||
import fmon.util._
|
import fmon.util._
|
||||||
|
|
||||||
|
class ColorSerializer extends StdSerializer[Color](classOf[Color]) {
|
||||||
|
override def serialize(color: Color, jgen: JsonGenerator, serializer: SerializerProvider): Unit = {
|
||||||
|
jgen.writeStartObject()
|
||||||
|
jgen.writeFieldName("red")
|
||||||
|
jgen.writeNumber(color.red)
|
||||||
|
jgen.writeFieldName("green")
|
||||||
|
jgen.writeNumber(color.green)
|
||||||
|
jgen.writeFieldName("blue")
|
||||||
|
jgen.writeNumber(color.blue)
|
||||||
|
jgen.writeEndObject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ColorDeserializer extends StdDeserializer[Color](classOf[Color]) {
|
||||||
|
override def deserialize(jp: JsonParser, ctxt: DeserializationContext): Color = {
|
||||||
|
val node = jp.getCodec.readTree[JsonNode](jp)
|
||||||
|
val red = node.get("red").asDouble()
|
||||||
|
val green = node.get("green").asDouble()
|
||||||
|
val blue = node.get("blue").asDouble()
|
||||||
|
Color.color(red, green, blue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
object YamlHelper {
|
object YamlHelper {
|
||||||
val mapper = new ObjectMapper(new YAMLFactory()) with ScalaObjectMapper
|
val mapper = new ObjectMapper(new YAMLFactory()) with ScalaObjectMapper
|
||||||
mapper.registerModule(DefaultScalaModule)
|
mapper.registerModule(DefaultScalaModule)
|
||||||
|
val module = new SimpleModule()
|
||||||
|
module.addSerializer(classOf[Color], new ColorSerializer)
|
||||||
|
module.addDeserializer(classOf[Color], new ColorDeserializer)
|
||||||
|
mapper.registerModule(module)
|
||||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||||
|
|
||||||
def extract[T](text : String)(implicit m : Manifest[T]) : T = {
|
def extract[T](text : String)(implicit m : Manifest[T]) : T = {
|
||||||
@@ -50,4 +85,16 @@ object YamlHelper {
|
|||||||
def extractMap[T](source : InputStream)(implicit m: Manifest[T]) : Map[String, T] = {
|
def extractMap[T](source : InputStream)(implicit m: Manifest[T]) : Map[String, T] = {
|
||||||
mapper.readValue[Map[String, T]](source)
|
mapper.readValue[Map[String, T]](source)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def write[T](source: OutputStream, item: T) = {
|
||||||
|
mapper.writeValue(source, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
def writeSeq[T](source: OutputStream, items: Seq[T]) = {
|
||||||
|
mapper.writeValue(source, items)
|
||||||
|
}
|
||||||
|
|
||||||
|
def writeMap[T](source: OutputStream, items: Map[String, T])(implicit m: Manifest[T]) = {
|
||||||
|
mapper.writeValue(source, items)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
45
FakeMon/src/fmon/world/Actor.scala
Normal file
45
FakeMon/src/fmon/world/Actor.scala
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package fmon.world
|
||||||
|
|
||||||
|
import scalafx.util.Duration
|
||||||
|
|
||||||
|
import fmon._
|
||||||
|
import fmon.draw._
|
||||||
|
import fmon.util.Direction
|
||||||
|
|
||||||
|
abstract class Actor(val sprite: Sprite, var pos: Position, var move: Movement) {
|
||||||
|
def x: Int = pos.x
|
||||||
|
def y: Int = pos.y
|
||||||
|
def xreal: Double = x - move.direction.x * move.completion
|
||||||
|
def yreal: Double = y - move.direction.y * move.completion
|
||||||
|
|
||||||
|
val imgView = new AnimatedImageView(sprite, new Duration(600)) {
|
||||||
|
x = pos.x * Config.tileSize
|
||||||
|
y = pos.y * Config.tileSize + Config.yOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
def pose: String = sprite.pose
|
||||||
|
def pose_=(p: String) = {
|
||||||
|
sprite.pose = p
|
||||||
|
}
|
||||||
|
|
||||||
|
def slide(dt: Duration): Unit = {
|
||||||
|
move.completion -= dt.toSeconds() * Config.moveSpeed
|
||||||
|
if (move.completion <= 0) {
|
||||||
|
move = selectNextMove
|
||||||
|
pos += move.direction
|
||||||
|
if (move.direction != Direction.Stationary) {
|
||||||
|
imgView.play()
|
||||||
|
} else {
|
||||||
|
imgView.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
imgView.x = xreal * Config.tileSize
|
||||||
|
imgView.y = yreal * Config.tileSize + Config.yOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
def selectNextMove: Movement
|
||||||
|
}
|
||||||
|
|
||||||
|
class Hero(sprite: Sprite, pos: Position) extends Actor(sprite, pos, new Movement(Direction.Stationary, 0)) {
|
||||||
|
def selectNextMove = new Movement(Direction.Stationary, 0)
|
||||||
|
}
|
||||||
27
FakeMon/src/fmon/world/EventPage.scala
Normal file
27
FakeMon/src/fmon/world/EventPage.scala
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package fmon.world
|
||||||
|
|
||||||
|
import fmon.draw.Sprite
|
||||||
|
import fmon.script.Action
|
||||||
|
|
||||||
|
class EventPage (
|
||||||
|
// Conditions
|
||||||
|
// Switch
|
||||||
|
// Variable
|
||||||
|
// Self-Switch
|
||||||
|
// Item
|
||||||
|
// Actor
|
||||||
|
val sprite : Sprite,
|
||||||
|
// Priority - Above or below
|
||||||
|
// Trigger - Action button / On contact
|
||||||
|
// Movement
|
||||||
|
// Type
|
||||||
|
// Speed
|
||||||
|
// Frequency
|
||||||
|
// Options
|
||||||
|
// Walking Animation
|
||||||
|
// Stepping Animation
|
||||||
|
// Direction Fix
|
||||||
|
// Pass through (no-collide)
|
||||||
|
val action: Action[Actor]
|
||||||
|
) {
|
||||||
|
}
|
||||||
18
FakeMon/src/fmon/world/GameEvent.scala
Normal file
18
FakeMon/src/fmon/world/GameEvent.scala
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package fmon.world
|
||||||
|
|
||||||
|
import fmon._
|
||||||
|
import fmon.util.Direction
|
||||||
|
|
||||||
|
class GameEvent(val name: String, val pages: IndexedSeq[EventPage]) {
|
||||||
|
def currPage = pages.head // TODO : Conditions
|
||||||
|
// Event Name
|
||||||
|
// Event Pages
|
||||||
|
}
|
||||||
|
|
||||||
|
class NPC(val event: GameEvent, pos: Position)
|
||||||
|
extends Actor(event.currPage.sprite, pos, new Movement(Direction.Stationary, 0.0)) {
|
||||||
|
|
||||||
|
def selectNextMove: Movement = {
|
||||||
|
new Movement(Direction.Stationary, 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
97
FakeMon/src/fmon/world/GameMap.scala
Normal file
97
FakeMon/src/fmon/world/GameMap.scala
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package fmon.world
|
||||||
|
|
||||||
|
import scalafx.scene.image.Image
|
||||||
|
|
||||||
|
import java.io._
|
||||||
|
|
||||||
|
import fmon._
|
||||||
|
import fmon.Config
|
||||||
|
import fmon.draw._
|
||||||
|
import fmon.draw.tile._
|
||||||
|
import fmon.util._
|
||||||
|
|
||||||
|
import Direction._
|
||||||
|
|
||||||
|
class GameMap(val width: Int, val height: Int, val tileset: Tileset) {
|
||||||
|
|
||||||
|
var tiles: IndexedSeq[AutoTile] = for (y <- 0 until height; x <- 0 until width) yield tileset.groundTiles.head
|
||||||
|
var doodads: IndexedSeq[AutoTile] = for (y <- 0 until height; x <- 0 until width) yield tileset.doodadTiles.head
|
||||||
|
|
||||||
|
def tileInfo(index: Int) = tileset.infoOf(tiles(index))
|
||||||
|
def tileInfo(pos: Position) = tileset.infoOf(this(pos))
|
||||||
|
def compositeInfo(pos: Position) = {
|
||||||
|
val gi = tileset.infoOf(this(pos))
|
||||||
|
val di = tileset.infoOf(doodad(pos))
|
||||||
|
if (di.doesOverwrite) di else gi
|
||||||
|
}
|
||||||
|
|
||||||
|
def saveTo(file: File): Unit = {
|
||||||
|
val ostream = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file)))
|
||||||
|
ostream.writeInt(width)
|
||||||
|
ostream.writeInt(height)
|
||||||
|
//ostream.writeObject(tileset.token.a2)// Tileset
|
||||||
|
val tileIndices = tileset.groundTiles.zipWithIndex.toMap
|
||||||
|
tiles.foreach(t => ostream.writeInt(tileIndices(t)))
|
||||||
|
//ostream.writeObject(tiles.map(tileIndices(_)).mkString(" "))
|
||||||
|
val doodadIndices = tileset.doodadTiles.zipWithIndex.toMap
|
||||||
|
doodads.foreach(t => ostream.writeInt(doodadIndices(t)))
|
||||||
|
ostream.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply(x: Int, y: Int): AutoTile = tiles(pt2index(x, y))
|
||||||
|
def doodad(x: Int, y: Int): AutoTile = doodads(pt2index(x, y))
|
||||||
|
def apply(pos: Position): AutoTile = tiles(pt2index(pos.x, pos.y))
|
||||||
|
def doodad(pos: Position): AutoTile = doodads(pt2index(pos.x, pos.y))
|
||||||
|
|
||||||
|
def smoothed(x: Int, y: Int): Image = {
|
||||||
|
val i = pt2index(x, y)
|
||||||
|
val dirs = scala.collection.mutable.Set[Direction]()
|
||||||
|
|
||||||
|
val onLeft = x == 0
|
||||||
|
val onRight = x + 1 == width
|
||||||
|
val onTop = y == 0
|
||||||
|
val onBottom = y + 1 == height
|
||||||
|
|
||||||
|
if (onLeft || tiles(left(i)) == tiles(i)) dirs += West
|
||||||
|
if (onRight || tiles(right(i)) == tiles(i)) dirs += East
|
||||||
|
if (onTop || tiles(up(i)) == tiles(i)) dirs += North
|
||||||
|
if (onBottom || tiles(down(i)) == tiles(i)) dirs += South
|
||||||
|
if (onLeft || onTop || tiles(left(up(i))) == tiles(i)) dirs += Northwest
|
||||||
|
if (onLeft || onBottom || tiles(down(left(i))) == tiles(i)) dirs += Southwest
|
||||||
|
if (onTop || onRight || tiles(right(up(i))) == tiles(i)) dirs += Northeast
|
||||||
|
if (onBottom || onRight || tiles(right(down(i))) == tiles(i)) dirs += Southeast
|
||||||
|
val index = pt2index(x, y)
|
||||||
|
tiles(index).build(dirs.toSet).croppedImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
def pt2index(x: Int, y: Int): Int = y * width + x
|
||||||
|
def pt2index(pos: Position): Int = pt2index(pos.x, pos.y)
|
||||||
|
def index2x(i: Int) = i % width
|
||||||
|
def index2y(i: Int) = i / width
|
||||||
|
|
||||||
|
def left(i: Int) = i - 1
|
||||||
|
def right(i: Int) = i + 1
|
||||||
|
def up(i: Int) = i - width
|
||||||
|
def down(i: Int) = i + width
|
||||||
|
}
|
||||||
|
|
||||||
|
object GameMap {
|
||||||
|
def loadFrom(file: File): GameMap = {
|
||||||
|
//val istream = new BufferedReader(new FileReader(file))
|
||||||
|
val istream = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))
|
||||||
|
val width = istream.readInt()
|
||||||
|
val height = istream.readInt()
|
||||||
|
val dir = new File(Config.homedir + raw"Progena\Data")
|
||||||
|
val resourceDir = new File(Config.homedir + raw"Progena\Resources\tilesets")
|
||||||
|
val tokens = YamlHelper.extractSeq[TilesetToken](new FileInputStream(new File(dir, "Tilesets.yaml"))).map(t => (t.name, t)).toMap
|
||||||
|
val tileset = tokens("Outside").load(resourceDir)
|
||||||
|
val indices = for (y <- 0 until height; x <- 0 until width) yield istream.readInt()
|
||||||
|
val dindices = for (y <- 0 until height; x <- 0 until width) yield istream.readInt()
|
||||||
|
val map = new GameMap(width, height, tileset)
|
||||||
|
val tiles = indices.map(i => tileset.groundTiles(i))
|
||||||
|
val doodads = dindices.map(i => tileset.doodadTiles(i))
|
||||||
|
map.tiles = tiles
|
||||||
|
map.doodads = doodads
|
||||||
|
map
|
||||||
|
}
|
||||||
|
}
|
||||||
15
FakeMon/src/fmon/world/Position.scala
Normal file
15
FakeMon/src/fmon/world/Position.scala
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package fmon.world
|
||||||
|
|
||||||
|
import fmon._
|
||||||
|
import fmon.util.Direction._
|
||||||
|
|
||||||
|
case class Position(val x: Int, val y: Int) {
|
||||||
|
def +(d: Direction): Position = {
|
||||||
|
Position(x + d.x, y + d.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Movement(val direction: Direction, var completion: Double) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
67
FakeMon/src/fmon/world/Tileset.scala
Normal file
67
FakeMon/src/fmon/world/Tileset.scala
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package fmon.world
|
||||||
|
|
||||||
|
import java.io._
|
||||||
|
|
||||||
|
import fmon.Config
|
||||||
|
import fmon.draw.tile._
|
||||||
|
|
||||||
|
class Tileset(val groundTiles: IndexedSeq[AutoTile], val doodadTiles: IndexedSeq[AutoTile], val info: IndexedSeq[TileInfo]) {
|
||||||
|
def groundInfo(index: Int) = info(index)
|
||||||
|
def doodadInfo(index: Int) = info(index + groundTiles.size)
|
||||||
|
|
||||||
|
val infoOf = (groundTiles.zipWithIndex.map{case (t, i) => (t, groundInfo(i))} ++ doodadTiles.zipWithIndex.map{case (t, i) => (t, doodadInfo(i))}).toMap
|
||||||
|
}
|
||||||
|
|
||||||
|
case class TileInfo(val flags: Int = TileInfo.PassAll) {
|
||||||
|
def doesPassNorth = (flags & TileInfo.PassNorth) != 0
|
||||||
|
def doesPassEast = (flags & TileInfo.PassEast) != 0
|
||||||
|
def doesPassSouth = (flags & TileInfo.PassSouth) != 0
|
||||||
|
def doesPassWest = (flags & TileInfo.PassWest) != 0
|
||||||
|
|
||||||
|
def doesOverwrite = (flags & TileInfo.Overwrite) != 0
|
||||||
|
|
||||||
|
def isCounter = (flags & TileInfo.IsCounter) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
object TileInfo {
|
||||||
|
val PassNorth = 0x0001
|
||||||
|
val PassEast = 0x0002
|
||||||
|
val PassSouth = 0x0004
|
||||||
|
val PassWest = 0x0008
|
||||||
|
val PassAll = PassNorth | PassEast | PassSouth | PassWest
|
||||||
|
val NoPass = 0
|
||||||
|
|
||||||
|
val Overwrite = 0x0010
|
||||||
|
val Underfoot = 0x0020
|
||||||
|
val Overhead = 0x0040
|
||||||
|
|
||||||
|
val IsCounter = 0x0100
|
||||||
|
}
|
||||||
|
|
||||||
|
case class TilesetToken(
|
||||||
|
val name: String,
|
||||||
|
val a1: String,
|
||||||
|
val a2: String,
|
||||||
|
val a3: String,
|
||||||
|
val a4: String,
|
||||||
|
val a5: String,
|
||||||
|
val b: String,
|
||||||
|
val c: String,
|
||||||
|
val info: IndexedSeq[TileInfo]) {
|
||||||
|
|
||||||
|
def load(dir: File): Tileset = {
|
||||||
|
val a1Tiles = AutoTilePalette.a2(new File(dir, a1), Config.tileSize).tiles
|
||||||
|
val a2Tiles = AutoTilePalette.a2(new File(dir, a2), Config.tileSize).tiles
|
||||||
|
val a3Tiles = AutoTilePalette.a3(new File(dir, a3), Config.tileSize).tiles
|
||||||
|
val a4Tiles = AutoTilePalette.a4(new File(dir, a4), Config.tileSize).tiles
|
||||||
|
val a5Tiles = AutoTilePalette.basic(new File(dir, a5), Config.tileSize).tiles
|
||||||
|
val groundTiles = a1Tiles ++ a2Tiles ++ a3Tiles ++ a4Tiles ++ a5Tiles
|
||||||
|
val bTiles = AutoTilePalette.partitioned(new File(dir, b), 2, Config.tileSize).tiles
|
||||||
|
val cTiles = AutoTilePalette.partitioned(new File(dir, c), 2, Config.tileSize).tiles
|
||||||
|
val doodadTiles = bTiles ++ cTiles
|
||||||
|
new Tileset(groundTiles, doodadTiles, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def toString = name
|
||||||
|
}
|
||||||
18
FakeMon/src/fmon/world/ui/GameView.fxml
Normal file
18
FakeMon/src/fmon/world/ui/GameView.fxml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.StackPane?>
|
||||||
|
<?import javafx.scene.layout.TilePane?>
|
||||||
|
|
||||||
|
<ScrollPane fx:id="scroller" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" onKeyPressed="#onKeyDown" onKeyReleased="#onKeyUp" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fmon.world.ui.GameView">
|
||||||
|
<content>
|
||||||
|
<StackPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308">
|
||||||
|
<children>
|
||||||
|
<TilePane fx:id="background" />
|
||||||
|
<TilePane fx:id="doodads" />
|
||||||
|
<AnchorPane fx:id="characterPane" />
|
||||||
|
</children>
|
||||||
|
</StackPane>
|
||||||
|
</content>
|
||||||
|
</ScrollPane>
|
||||||
310
FakeMon/src/fmon/world/ui/GameView.scala
Normal file
310
FakeMon/src/fmon/world/ui/GameView.scala
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
package fmon.world.ui
|
||||||
|
|
||||||
|
import java.io._
|
||||||
|
|
||||||
|
import javafx.application.Application
|
||||||
|
import javafx.fxml.FXML
|
||||||
|
import javafx.fxml.FXMLLoader
|
||||||
|
import javafx.fxml.JavaFXBuilderFactory
|
||||||
|
import javafx.scene.{control => jfxsc, layout => jfxsl, Parent, Scene}
|
||||||
|
import javafx.scene.input.KeyEvent
|
||||||
|
import javafx.stage.Stage
|
||||||
|
|
||||||
|
import scalafx.Includes._
|
||||||
|
import scalafx.animation.AnimationTimer
|
||||||
|
import scalafx.beans.property._
|
||||||
|
import scalafx.collections.ObservableBuffer
|
||||||
|
import scalafx.geometry.Point2D
|
||||||
|
import scalafx.scene.control._
|
||||||
|
import scalafx.scene.image.ImageView
|
||||||
|
import scalafx.scene.input.{KeyCode, MouseEvent}
|
||||||
|
import scalafx.scene.paint.Color
|
||||||
|
import scalafx.scene.layout._
|
||||||
|
import scalafx.stage.FileChooser
|
||||||
|
import scalafx.stage.Modality
|
||||||
|
import scalafx.util.Duration
|
||||||
|
import FileChooser.ExtensionFilter
|
||||||
|
|
||||||
|
import fmon._
|
||||||
|
import fmon.battle.BattleUI
|
||||||
|
import fmon.draw._
|
||||||
|
import fmon.script.action.MessageAction
|
||||||
|
import fmon.util.Direction
|
||||||
|
import fmon.world._
|
||||||
|
import fmon.util.YamlHelper
|
||||||
|
import Direction._
|
||||||
|
|
||||||
|
class GameView {
|
||||||
|
@FXML var scroller: jfxsc.ScrollPane = _
|
||||||
|
@FXML var background: jfxsl.TilePane = _
|
||||||
|
@FXML var doodads: jfxsl.TilePane = _
|
||||||
|
@FXML var characterPane: jfxsl.AnchorPane = _
|
||||||
|
|
||||||
|
var hero: Actor = _
|
||||||
|
var npcs = Seq[NPC]()
|
||||||
|
def actors = hero +: npcs
|
||||||
|
|
||||||
|
var heroImg: AnimatedImageView = _
|
||||||
|
var currDir: Option[Direction] = None
|
||||||
|
|
||||||
|
var level: GameMap = _
|
||||||
|
|
||||||
|
var lastTime = 0L
|
||||||
|
val timer = AnimationTimer(t => {
|
||||||
|
if (lastTime != 0L) {
|
||||||
|
act(new Duration((t - lastTime) * 0.000001))
|
||||||
|
}
|
||||||
|
lastTime = t
|
||||||
|
})
|
||||||
|
|
||||||
|
def loadLevel(level: GameMap): Unit = {
|
||||||
|
this.level = level
|
||||||
|
background.prefColumns = level.width
|
||||||
|
val tiles = for (y <- 0 until level.height; x <- 0 until level.width) yield {
|
||||||
|
new ImageView(level.smoothed(x, y)).delegate
|
||||||
|
}
|
||||||
|
|
||||||
|
background.children ++= tiles
|
||||||
|
|
||||||
|
doodads.prefColumns = level.width
|
||||||
|
val dtiles = for (y <- 0 until level.height; x <- 0 until level.width) yield {
|
||||||
|
new ImageView(level.doodad(x, y).icon.croppedImage()).delegate
|
||||||
|
}
|
||||||
|
doodads.children ++= dtiles
|
||||||
|
|
||||||
|
val heroFile = Config.homedir + raw"Progena\Resources\characters\Actor1.png"
|
||||||
|
hero = new Hero(CharSet(new File(heroFile), 1), Position(5, 4))
|
||||||
|
|
||||||
|
heroImg = hero.imgView
|
||||||
|
|
||||||
|
npcs = (0 until 8).map(i => {
|
||||||
|
val event = new GameEvent(s"NPC $i", IndexedSeq(new EventPage(
|
||||||
|
CharSet(new File(heroFile), i),
|
||||||
|
new MessageAction(s"I am NPC $i")
|
||||||
|
)))
|
||||||
|
new NPC(event, Position(7 + i, 3))
|
||||||
|
})
|
||||||
|
|
||||||
|
characterPane.children += heroImg
|
||||||
|
val npcImgs = npcs.map(npc => npc.imgView)
|
||||||
|
npcImgs.foreach(characterPane.children += _)
|
||||||
|
|
||||||
|
timer.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
def onKeyDown(k: KeyEvent): Unit = {
|
||||||
|
k.code match {
|
||||||
|
case KeyCode.W | KeyCode.Up => setDir(North)
|
||||||
|
case KeyCode.D | KeyCode.Right => setDir(East)
|
||||||
|
case KeyCode.A | KeyCode.Left => setDir(West)
|
||||||
|
case KeyCode.S | KeyCode.Down => setDir(South)
|
||||||
|
case KeyCode.Space => activate()
|
||||||
|
case KeyCode.BackQuote => beginWildBattle()
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def onKeyUp(k: KeyEvent): Unit = {
|
||||||
|
k.code match {
|
||||||
|
case KeyCode.W | KeyCode.Up => clearDir(North)
|
||||||
|
case KeyCode.D | KeyCode.Right => clearDir(East)
|
||||||
|
case KeyCode.A | KeyCode.Left => clearDir(West)
|
||||||
|
case KeyCode.S | KeyCode.Down => clearDir(South)
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def beginWildBattle(): Unit = {
|
||||||
|
BattleUI.startWildBattle()
|
||||||
|
}
|
||||||
|
|
||||||
|
def activate(): Unit = {
|
||||||
|
if (hero.move.direction == Direction.Stationary) {
|
||||||
|
val dir = Direction.byName(hero.sprite.pose)
|
||||||
|
val np = hero.pos + dir
|
||||||
|
npcs.filter(npc => npc.pos == np).foreach(npc => npc.event.currPage.action(npc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def setDir(dir: Direction): Unit = {
|
||||||
|
currDir = Some(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
def clearDir(dir: Direction): Unit = {
|
||||||
|
if (currDir == Some(dir)) {
|
||||||
|
currDir = None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def act(dur: Duration): Unit = {
|
||||||
|
/*currDir match {
|
||||||
|
case Some(North) => slide(hero, 0, -dur.toSeconds() * Config.moveSpeed)
|
||||||
|
case Some(East) => slide(hero, dur.toSeconds() * Config.moveSpeed, 0)
|
||||||
|
case Some(West) => slide(hero, -dur.toSeconds() * Config.moveSpeed, 0)
|
||||||
|
case Some(South) => slide(hero, 0, +dur.toSeconds() * Config.moveSpeed)
|
||||||
|
case _ => ()
|
||||||
|
}*/
|
||||||
|
if (hero.move.completion <= 0 && currDir.isDefined) {
|
||||||
|
val dir = currDir.get
|
||||||
|
|
||||||
|
if (hero.pose != dir.toString()) {
|
||||||
|
hero.pose = dir.toString()
|
||||||
|
} else {
|
||||||
|
val dest = hero.pos + dir
|
||||||
|
val info = level.compositeInfo(hero.pos + dir)
|
||||||
|
val canPass = (dir.x < 0 && info.doesPassEast) || (dir.x > 0 && info.doesPassWest) ||
|
||||||
|
(dir.y < 0 && info.doesPassSouth) || (dir.y > 0 && info.doesPassNorth)
|
||||||
|
val unoccupied = !actors.exists(a => a.pos == dest && a != hero)
|
||||||
|
|
||||||
|
if (canPass && unoccupied) {
|
||||||
|
hero.pos += dir
|
||||||
|
hero.move = new Movement(dir, 1.0)
|
||||||
|
hero.imgView.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hero.slide(dur)
|
||||||
|
npcs.foreach(_.slide(dur))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
def slide(actor: Actor, dx: Double, dy: Double): Unit = {
|
||||||
|
// Need to do all corners
|
||||||
|
val px = if (dx < 0) actor.x else actor.x + 1
|
||||||
|
val py = if (dy < 0) actor.y else actor.y + 1
|
||||||
|
val tiles = tilesOnLine(new Point2D(px, py), new Point2D(px + dx, py + dy))
|
||||||
|
|
||||||
|
val condition = if (dx < 0 && dy < 0) {
|
||||||
|
info:TileInfo => info.doesPassSouth && info.doesPassEast
|
||||||
|
} else if (dx < 0) {
|
||||||
|
info: TileInfo => info.doesPassNorth && info.doesPassEast
|
||||||
|
} else if (dy < 0) {
|
||||||
|
info: TileInfo => info.doesPassSouth && info.doesPassWest
|
||||||
|
} else {
|
||||||
|
info: TileInfo => info.doesPassNorth && info.doesPassWest
|
||||||
|
}
|
||||||
|
|
||||||
|
val collisions = tiles.filterNot{case (i, _) => condition(level.tileInfo(i))}
|
||||||
|
if (!collisions.isEmpty) {
|
||||||
|
val (i, pt) = collisions.head
|
||||||
|
println(actor.y, pt.y)
|
||||||
|
actor.x += (pt.x - px)
|
||||||
|
actor.y += (pt.y - py)
|
||||||
|
//println(actor.x, actor.y)
|
||||||
|
} else {
|
||||||
|
actor.x += dx
|
||||||
|
actor.y += dy
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
def lineIntersectionIndices(p: Point2D, q: Point2D): Seq[Int] = {
|
||||||
|
def p2i(pt: Point2D) = level.pt2index(pt.x.toInt, pt.y.toInt)
|
||||||
|
val dx = q.x - p.x
|
||||||
|
val dy = q.y - p.y
|
||||||
|
val nx = Math.abs(dx).toInt
|
||||||
|
val ny = Math.abs(dy).toInt
|
||||||
|
val sx = Math.signum(dx)
|
||||||
|
val sy = Math.signum(dy)
|
||||||
|
|
||||||
|
var pts = Seq(p2i(p))
|
||||||
|
var pt = p
|
||||||
|
var ix = 0
|
||||||
|
var iy = 0
|
||||||
|
while (ix < nx || iy < ny) {
|
||||||
|
if ((0.5+ix) * ny < (0.5+iy) * nx) {
|
||||||
|
// next step is horizontal
|
||||||
|
pt = new Point2D(pt.x + sx, pt.y)
|
||||||
|
ix += 1
|
||||||
|
} else {
|
||||||
|
// next step is vertical
|
||||||
|
pt = new Point2D(pt.x, pt.y + sy)
|
||||||
|
ix += 1
|
||||||
|
}
|
||||||
|
pts :+= p2i(pt)
|
||||||
|
}
|
||||||
|
pts
|
||||||
|
}
|
||||||
|
|
||||||
|
def lineIntersectsGrid(p: Point2D, q: Point2D, gi: Int): Point2D = {
|
||||||
|
val gx = level.index2x(gi)
|
||||||
|
val gy = level.index2y(gi)
|
||||||
|
val dx = q.x - p.x
|
||||||
|
val dy = q.y - p.y
|
||||||
|
if (dx > 0) {
|
||||||
|
new Point2D(gx, p.y)
|
||||||
|
} else if (dx < 0) {
|
||||||
|
new Point2D(gx + 1.0, p.y)
|
||||||
|
} else if (dy > 0) {
|
||||||
|
new Point2D(p.x, gy)
|
||||||
|
} else if (dy < 0) {
|
||||||
|
new Point2D(p.x, gy + 1)
|
||||||
|
} else {
|
||||||
|
new Point2D(q.x, q.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def tilesOnLine(p: Point2D, q: Point2D): Seq[(Int, Point2D)] = {
|
||||||
|
val tileIndices = lineIntersectionIndices(p, q)
|
||||||
|
tileIndices.map(i => (i, lineIntersectsGrid(p, q, i)))
|
||||||
|
}
|
||||||
|
|
||||||
|
def tilesOnLine_(p: Point2D, q: Point2D): Seq[(Int, Point2D)] = {
|
||||||
|
val dx = q.x - p.x
|
||||||
|
val dy = q.y - p.y
|
||||||
|
val nx = Math.abs(dx).toInt
|
||||||
|
val ny = Math.abs(dy).toInt
|
||||||
|
val sx = Math.signum(dx)
|
||||||
|
val sy = Math.signum(dy)
|
||||||
|
|
||||||
|
var pts = Seq(p)
|
||||||
|
var pt = p
|
||||||
|
var ix = 0
|
||||||
|
var iy = 0
|
||||||
|
while (ix < nx || iy < ny) {
|
||||||
|
if ((0.5+ix) * ny < (0.5+iy) * nx) {
|
||||||
|
// next step is horizontal
|
||||||
|
pt = new Point2D(pt.x + sx, pt.y)
|
||||||
|
ix += 1
|
||||||
|
} else {
|
||||||
|
// next step is vertical
|
||||||
|
pt = new Point2D(pt.x, pt.y + sy)
|
||||||
|
ix += 1
|
||||||
|
}
|
||||||
|
pts :+= pt
|
||||||
|
}
|
||||||
|
|
||||||
|
pts.map(pt => (level.pt2index(pt.x.toInt, pt.y.toInt), pt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object GameView {
|
||||||
|
class GameApp extends Application {
|
||||||
|
override def start(primaryStage: Stage): Unit = {
|
||||||
|
val frameLoader = new FXMLLoader(getClass.getResource("GameView.fxml"))
|
||||||
|
val root: Parent = frameLoader.load()
|
||||||
|
val controller = frameLoader.getController[GameView]()
|
||||||
|
|
||||||
|
val stageFile = Config.homedir + raw"Progena\Data\outside.map"
|
||||||
|
val level = GameMap.loadFrom(new File(stageFile))
|
||||||
|
controller.loadLevel(level)
|
||||||
|
|
||||||
|
val scene: Scene = new Scene(root)
|
||||||
|
|
||||||
|
GameManager.root = primaryStage
|
||||||
|
GameManager.currLevel = root
|
||||||
|
GameManager.currController = controller
|
||||||
|
GameManager.currView = root
|
||||||
|
|
||||||
|
primaryStage.setScene(scene)
|
||||||
|
primaryStage.width = 1280
|
||||||
|
primaryStage.height = 800
|
||||||
|
//primaryStage.maximized = true
|
||||||
|
primaryStage.show()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def main(args: Array[String]): Unit = {
|
||||||
|
Application.launch(classOf[GameApp], args: _*)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user