diff --git a/README.md b/README.md index e7dea83..0ea12c2 100644 --- a/README.md +++ b/README.md @@ -50,32 +50,33 @@ There are also some special commands: ## Screenshots -This is how the game looks when run (**very** preliminary GUI): +This is how the game looks when run: ![](screenshots/gameplay.gif) ### Map -![](screenshots/orig_map.png) +After selecting a new game you have to generate the world, just before choosing the attributes. -Thanks to the map generator from [Red Blob Games](https://www.redblobgames.com/), I produced the map above. The one in -the game is fundamentally based on this (eg, it uses this heightmap) but it then simplifies the terrains into less -groups - so my map is much less coloured and it has less water mass. +NB: trees cannot be seen here, but they are generated (as individual obstacles) with varying probability +depending on terrain type. -The rivers and lakes are also currently not present. The map is **2048x2048 pixels**, and it's pixel is one "cell" in -game, so it's pretty big. +The rivers and lakes are upcoming in release `0.3.0`. The terrain types are currently fixed. Ordered by +height, they are: -In game, if it could be zoomed out it would look more like this: +* Deep water +* Shallow water +* Coast +* Grass-less ground +* Grass plain +* Grassy Hill +* Rocky Hill +* Mountain +* High Mountain -![](screenshots/map.png) +Some terrains are harder to move through: more stamina will be consumed, and a higher delay is to be expected. -The elevation thresholds are simplified to reduce the amount of colours (and thus, terrains) shown. - -Later the terrain will be configurable by the user. - -You can also see some rivers (still in-progress, not in the game yet) flowing from high to low places. - -Note: this is only temporary. In the final game the terrain will be randomised. +All this should be completely configurable by the user by release `0.4.0`, hopefully. ## Features diff --git a/alone-rl.iml b/alone-rl.iml index a202107..79edaeb 100644 --- a/alone-rl.iml +++ b/alone-rl.iml @@ -13,18 +13,19 @@ - - + + + - - - - + + + + - + \ No newline at end of file diff --git a/data/items.yml b/data/items.yml index 44e4576..c12de3b 100644 --- a/data/items.yml +++ b/data/items.yml @@ -62,9 +62,9 @@ tree: sprite: c: T col: - red: 0 - green: 205 - blue: 113 + red: 77 + green: 168 + blue: 59 shadowView: true obstacle: {} cuttable: {} diff --git a/data/map/elevation.data b/data/map/elevation.data index 7461bad..9a78b4f 100644 Binary files a/data/map/elevation.data and b/data/map/elevation.data differ diff --git a/data/map/map.png b/data/map/map.png deleted file mode 100644 index 37008b2..0000000 Binary files a/data/map/map.png and /dev/null differ diff --git a/data/map/map.yml b/data/map/map.yml deleted file mode 100644 index e69de29..0000000 diff --git a/data/map/terrain.yml b/data/map/terrain.yml new file mode 100644 index 0000000..a0adcad --- /dev/null +++ b/data/map/terrain.yml @@ -0,0 +1,81 @@ + +deep-water: + c: 247 + col: + red: 51 + green: 102 + blue: 153 + theight: 0.01 + type: WATER + +shallow-water: + c: '~' + col: + red: 20 + green: 152 + blue: 204 + theight: 0.05 + type: WATER + +sand: + c: 250 + col: + red: 170 + green: 170 + blue: 0 + theight: 0.08 + type: LAND + +ground: + c: 250 + col: + red: 184 + green: 134 + blue: 11 + theight: 0.1 + type: LAND + +grass: + c: 250 + col: + red: 31 + green: 138 + blue: 19 + theight: 0.4 + type: GRASS + +hill-grass: + c: '^' + col: + red: 74 + green: 105 + blue: 4 + theight: 0.7 + type: GRASS + +hill: + c: '^' + col: + red: 119 + green: 93 + blue: 61 + theight: 0.8 + type: LAND + +mountain: + c: '^' + col: + red: 76 + green: 70 + blue: 50 + theight: 0.9 + type: LAND + +high-mountain: + c: '^' + col: + red: 255 + green: 240 + blue: 220 + theight: 1.1 # must be strictly greater than 1 + type: ICE diff --git a/data/palette.yml b/data/palette.yml new file mode 100644 index 0000000..9c4a014 --- /dev/null +++ b/data/palette.yml @@ -0,0 +1,46 @@ + +## Arne64 from http://www.gridsagegames.com/rexpaint/palettes/Arne64.txt +# +#gray: [{0,0,0},{21,21,21},{52,52,52},{123,123,123},{168,168,168},{215,215,215},{255,255,255}] +#brown: [{ 35, 23, 18},{92,60,13},{174,108,55},{197,151,130},{226,215,181}] +#grayblue: [{13,32,48},{65,93,102},{113,166,161}] +#ochra: [{173,78,26},{246,143,55}] +#cyan: [{0,82,128},{10,152,172},{37,226,205},{189,255,202}] +#red: [{79,21,7},{224,60,40}] +#aquamarine: [{0,96,75},{32,181,98}] +#rosee: [{130,60,61},{218,101,94},{225,130,137},{245,183,132},{255,233,197}] +#green: [{0,78,0},{19,157,8},{88,211,50}] +#pink: [{135,22,70},{207, 60,113},{255,130,206}] +#green2: [{23,40,8},{55,109,3},{106,180,23},{140,214,18},{190,235,113},{238,255,169}] +#magenta: [{163,40,179},{204,105,228},{213,156,252},{254,201,237}] +#acidgreen: [{147,151,23},{182,193,33}] +#purple: [{33,22,64},{90,25,145},{106,49,202},{166,117,254},{226,201,255}] +#yellow: [{204,143, 21},{255,187,49},{255,231,55}] +#indigo: [{61,52,165},{98,100,220},{155,160,239}] +#blue: [{0,23,125},{2,74,202},{0,132,255},{91,168,255},{152,220,255}] + +# from libtcod + +red: 255,0,0 +flame: 255,63,0 +orange: 255,127,0 +amber: 255,191,0 +yellow: 255,255,0, +lime: 191,255,0 +chartreuse: 127,255,0 +green: 0,255,0 +sea: 0,255,127 +turquoise: 0,255,191 +cyan: 0,255,255 +sky: 0,191,255 +azure: 0,127,255 +blue: 0,0,255 +han: 63,0,255 +violet: 127,0,255 +purple: 191,0,255 +fuchsia: 255,0,255 +magenta: 255,0,191 +pink: 255,0,127 +crimson: 255,0,63 +sepia: 127,101,63 +gray: 127,127,127 diff --git a/make_pkg.sh b/make_pkg.sh index fb53c7d..07a57e6 100755 --- a/make_pkg.sh +++ b/make_pkg.sh @@ -1,9 +1,9 @@ #!/bin/bash -v=`ls target/alone-rl-*-jar-with-dependencies.jar | awk -F"-" '{print $3}'` - mvn clean package -Dmaven.test.skip=true +v=`ls target/alone-rl-*-jar-with-dependencies.jar | awk -F"-" '{print $3}'` + cp target/alone-rl-*-jar-with-dependencies.jar alone-rl-$v.jar 7z a -tzip alone-rl-$v.zip data/ alone-rl-$v.jar diff --git a/pom.xml b/pom.xml index bfc9bf8..556fc16 100644 --- a/pom.xml +++ b/pom.xml @@ -20,9 +20,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - com.github.fabioticconi + com.github.fabio-t alone-rl - 0.2.0 + 0.2.5 jar AloneRL @@ -41,48 +41,56 @@ net.onedaybeard.artemis artemis-odb - 2.1.0 + [2,3) net.mostlyoriginal.artemis-odb contrib-core - 2.2.0 + [2,3) com.github.fabioticconi rlforj-alt - 0.2.0 + 0.3.0 - ch.qos.logback - logback-classic - 1.2.3 + com.github.fabioticconi + terrain-generator + 0.0.1 - com.fasterxml.jackson.dataformat - jackson-dataformat-yaml - 2.9.1 + ch.qos.logback + logback-classic + [1,2) junit junit - 4.12 + [4,5) test + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.9.3 + + com.fasterxml.jackson.core jackson-databind + 2.9.3 com.fasterxml.jackson.module jackson-module-parameter-names + 2.9.3 @@ -92,7 +100,7 @@ com.fasterxml.jackson jackson-bom - 2.9.0 + 2.9.3 import pom @@ -113,7 +121,7 @@ org.apache.maven.plugins maven-release-plugin - 2.5.3 + [2,3) @{project.version} @@ -161,9 +169,9 @@ - scm:git:git@fabioticconi/alone-rl.git - scm:git:git@fabioticconi/alone-rl.git - scm:git:git@fabioticconi/alone-rl.git + scm:git:git@fabio-t/alone-rl.git + scm:git:git@fabio-t/alone-rl.git + scm:git:git@fabio-t/alone-rl.git diff --git a/screenshots/full_colours.png b/screenshots/full_colours.png deleted file mode 100644 index 4e56c4a..0000000 Binary files a/screenshots/full_colours.png and /dev/null differ diff --git a/screenshots/gameplay.gif b/screenshots/gameplay.gif index 1e0b896..7e999c4 100644 Binary files a/screenshots/gameplay.gif and b/screenshots/gameplay.gif differ diff --git a/screenshots/map.png b/screenshots/map.png deleted file mode 100644 index 927e979..0000000 Binary files a/screenshots/map.png and /dev/null differ diff --git a/screenshots/orig_map.png b/screenshots/orig_map.png deleted file mode 100644 index 15fdcd5..0000000 Binary files a/screenshots/orig_map.png and /dev/null differ diff --git a/screenshots/screenshot.png b/screenshots/screenshot.png deleted file mode 100644 index 68ea9a2..0000000 Binary files a/screenshots/screenshot.png and /dev/null differ diff --git a/src/main/java/com/github/fabioticconi/alone/Main.java b/src/main/java/com/github/fabioticconi/alone/Main.java index 426d95c..5085bc3 100644 --- a/src/main/java/com/github/fabioticconi/alone/Main.java +++ b/src/main/java/com/github/fabioticconi/alone/Main.java @@ -79,6 +79,7 @@ public Main() throws IOException final WorldConfiguration config; config = new WorldConfiguration(); // first thing to be loaded + config.setSystem(MapScreen.class); config.setSystem(MapSystem.class); // POJO config.register(new Random()); @@ -127,6 +128,7 @@ public Main() throws IOException config.setSystem(EquipScreen.class); config.setSystem(CraftScreen.class); config.setSystem(CraftItemScreen.class); + config.setSystem(CharScreen.class); // last systems config.setSystem(DeadSystem.class); @@ -174,6 +176,8 @@ public void loop() long repaintCooldown = 1L; long actionCooldown = 0L; + world.process(); + while (keepRunning) { currentTime = System.nanoTime(); diff --git a/src/main/java/com/github/fabioticconi/alone/behaviours/GrazeBehaviour.java b/src/main/java/com/github/fabioticconi/alone/behaviours/GrazeBehaviour.java index 8dab748..3d181a2 100644 --- a/src/main/java/com/github/fabioticconi/alone/behaviours/GrazeBehaviour.java +++ b/src/main/java/com/github/fabioticconi/alone/behaviours/GrazeBehaviour.java @@ -23,7 +23,7 @@ import com.github.fabioticconi.alone.components.Position; import com.github.fabioticconi.alone.components.Speed; import com.github.fabioticconi.alone.components.attributes.Sight; -import com.github.fabioticconi.alone.constants.Cell; +import com.github.fabioticconi.alone.constants.TerrainType; import com.github.fabioticconi.alone.systems.ActionSystem; import com.github.fabioticconi.alone.systems.BumpSystem; import com.github.fabioticconi.alone.systems.HungerSystem; @@ -53,7 +53,7 @@ public class GrazeBehaviour extends AbstractBehaviour // FIXME: this should be in a Context of sort private Hunger hunger; - private EnumSet validCells = EnumSet.of(Cell.GRASS, Cell.HILL_GRASS); + final private EnumSet validCells = EnumSet.of(TerrainType.GRASS); @Override protected void initialize() diff --git a/src/main/java/com/github/fabioticconi/alone/behaviours/UnderwaterBehaviour.java b/src/main/java/com/github/fabioticconi/alone/behaviours/UnderwaterBehaviour.java index 393690f..8d4ffe2 100644 --- a/src/main/java/com/github/fabioticconi/alone/behaviours/UnderwaterBehaviour.java +++ b/src/main/java/com/github/fabioticconi/alone/behaviours/UnderwaterBehaviour.java @@ -24,8 +24,8 @@ import com.github.fabioticconi.alone.components.Position; import com.github.fabioticconi.alone.components.Speed; import com.github.fabioticconi.alone.components.Underwater; -import com.github.fabioticconi.alone.constants.Cell; import com.github.fabioticconi.alone.constants.Side; +import com.github.fabioticconi.alone.constants.TerrainType; import com.github.fabioticconi.alone.systems.BumpSystem; import com.github.fabioticconi.alone.systems.MapSystem; @@ -39,14 +39,17 @@ */ public class UnderwaterBehaviour extends AbstractBehaviour { - final EnumSet validCells = EnumSet.of(Cell.WATER, Cell.DEEP_WATER); ComponentMapper mPos; MapSystem map; BumpSystem sBump; + @Wire Random r; + private Position curPos; + private final EnumSet validCells = EnumSet.of(TerrainType.WATER); + @Override protected void initialize() { @@ -63,9 +66,9 @@ public float evaluate(final int entityId) curPos = mPos.get(entityId); - final Cell c = map.get(curPos.x, curPos.y); + final MapSystem.Cell c = map.get(curPos.x, curPos.y); - if (c != Cell.DEEP_WATER && c != Cell.WATER) + if (c.type != TerrainType.WATER) { // can't move at all if on solid ground diff --git a/src/main/java/com/github/fabioticconi/alone/constants/Cell.java b/src/main/java/com/github/fabioticconi/alone/constants/Cell.java deleted file mode 100644 index d0324fd..0000000 --- a/src/main/java/com/github/fabioticconi/alone/constants/Cell.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2017 Fabio Ticconi - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -package com.github.fabioticconi.alone.constants; - -import com.github.fabioticconi.alone.utils.Util; - -import java.awt.*; - -/** - * @author Fabio Ticconi - */ -public enum Cell -{ - EMPTY(), - DEEP_WATER('=', Color.BLUE.darker().darker().darker()), - WATER('~', Color.BLUE.brighter()), - SAND((char) 250, Color.ORANGE), - GROUND((char) 250, Color.ORANGE.darker().darker()), - GRASS((char) 250, Color.GREEN.darker().darker().darker()), - HILL_GRASS('^', Color.GREEN.darker().darker().darker()), - HILL('^', Util.BROWN), - MOUNTAIN('^', Util.BROWN.darker()), - HIGH_MOUNTAIN('^', Color.GRAY); - - public final char c; - public final Color col; - public final Color bg; - - Cell() - { - this.c = ' '; - this.col = Color.BLACK; - this.bg = Color.BLACK; - } - - Cell(final char c) - { - this.c = c; - this.col = Color.BLACK; - this.bg = Color.BLACK; - } - - Cell(final char c, final Color color) - { - this.c = c; - // this.col = color; - // this.bg = Color.BLACK; - this.col = color.darker(); - this.bg = color; - } -} diff --git a/src/main/java/com/github/fabioticconi/alone/constants/Options.java b/src/main/java/com/github/fabioticconi/alone/constants/Options.java index 63ae8dd..a5a6e4d 100644 --- a/src/main/java/com/github/fabioticconi/alone/constants/Options.java +++ b/src/main/java/com/github/fabioticconi/alone/constants/Options.java @@ -24,6 +24,6 @@ public class Options { public static final int OUTPUT_SIZE_X = 55; public static final int OUTPUT_SIZE_Y = 55; - public static int MAP_SIZE_X = 1000; - public static int MAP_SIZE_Y = 1000; + public static int MAP_SIZE_X = 1024; + public static int MAP_SIZE_Y = 1024; } diff --git a/src/main/java/com/github/fabioticconi/alone/constants/TerrainType.java b/src/main/java/com/github/fabioticconi/alone/constants/TerrainType.java new file mode 100644 index 0000000..f9b2d21 --- /dev/null +++ b/src/main/java/com/github/fabioticconi/alone/constants/TerrainType.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 Fabio Ticconi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +package com.github.fabioticconi.alone.constants; + +/** + * Author: Fabio Ticconi + * Date: 27/11/17 + */ +public enum TerrainType +{ + WATER, + GRASS, + LAND, + ICE +} diff --git a/src/main/java/com/github/fabioticconi/alone/screens/AbstractScreen.java b/src/main/java/com/github/fabioticconi/alone/screens/AbstractScreen.java index f5854d5..b87a4d5 100644 --- a/src/main/java/com/github/fabioticconi/alone/screens/AbstractScreen.java +++ b/src/main/java/com/github/fabioticconi/alone/screens/AbstractScreen.java @@ -21,6 +21,8 @@ import asciiPanel.AsciiPanel; import com.artemis.managers.PlayerManager; import com.artemis.utils.BitVector; +import com.github.fabioticconi.alone.systems.MapSystem; +import com.github.fabioticconi.alone.systems.ScreenSystem; import com.github.fabioticconi.alone.utils.Util; import net.mostlyoriginal.api.system.core.PassiveSystem; @@ -38,6 +40,8 @@ public abstract class AbstractScreen extends PassiveSystem implements Screen { public static final Letter[] ALL = Letter.values(); + ScreenSystem screen; + MapSystem map; PlayerManager pManager; public abstract String header(); diff --git a/src/main/java/com/github/fabioticconi/alone/screens/CharScreen.java b/src/main/java/com/github/fabioticconi/alone/screens/CharScreen.java new file mode 100644 index 0000000..fe2374d --- /dev/null +++ b/src/main/java/com/github/fabioticconi/alone/screens/CharScreen.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2017 Fabio Ticconi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +package com.github.fabioticconi.alone.screens; + +import asciiPanel.AsciiPanel; +import com.artemis.ComponentMapper; +import com.artemis.utils.BitVector; +import com.github.fabioticconi.alone.components.attributes.Agility; +import com.github.fabioticconi.alone.components.attributes.Constitution; +import com.github.fabioticconi.alone.components.attributes.Strength; +import com.github.fabioticconi.alone.systems.BootstrapSystem; + +import java.awt.*; +import java.awt.event.KeyEvent; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Collections; + +/** + * Author: Fabio Ticconi + * Date: 21/11/17 + */ +public class CharScreen extends AbstractScreen +{ + BootstrapSystem sBoot; + + ComponentMapper mStr; + ComponentMapper mAgi; + ComponentMapper mCon; + + byte[] stats = new byte[3]; + int curStat = 0; + int points = 1; + + @Override + public String header() + { + return "Character Generation"; + } + + @Override + public float handleKeys(final BitVector keys) + { + if (keys.get(KeyEvent.VK_ESCAPE)) + { + screen.select(MapScreen.class); + } + else if (keys.get(KeyEvent.VK_DOWN)) + { + if (curStat < 2) + curStat++; + } + else if (keys.get(KeyEvent.VK_UP)) + { + if (curStat > 0) + curStat--; + } + else if (keys.get(KeyEvent.VK_LEFT)) + { + if (stats[curStat] > -2) + { + stats[curStat]--; + points++; + } + } + else if (keys.get(KeyEvent.VK_RIGHT)) + { + if (stats[curStat] < 2 && points > 0) + { + stats[curStat]++; + points--; + } + } + else if (keys.get(KeyEvent.VK_ENTER)) + { + // confirmed, let's play! + + final int playerId = pManager.getEntitiesOfPlayer("player").get(0).getId(); + + mStr.create(playerId).value = stats[0]; + mAgi.create(playerId).value = stats[1]; + mCon.create(playerId).value = stats[2]; + + sBoot.makeDerivative(playerId); + + screen.select(PlayScreen.class); + } + + keys.clear(); + + return 0f; + } + + @Override + public void display(final AsciiPanel terminal) + { + terminal.clear(); + + drawHeader(terminal); + + terminal.writeCenter("Available Attribute Points: [" + points + "]", 20); + + int yoff = terminal.getHeightInCharacters() / 2 - 1; + final int xoff = terminal.getWidthInCharacters() / 2 - 12; + + final String str = "[" + String.join("", Collections.nCopies(stats[0]+3, "=")) + "]"; + final String agi = "[" + String.join("", Collections.nCopies(stats[1]+3, "=")) + "]"; + final String con = "[" + String.join("", Collections.nCopies(stats[2]+3, "=")) + "]"; + + terminal.write("Strength: " + str, xoff, yoff, curStat==0?Color.YELLOW:Color.WHITE); + yoff += 3; + terminal.write("Agility: " + agi, xoff, yoff, curStat==1?Color.YELLOW:Color.WHITE); + yoff += 3; + terminal.write("Constitution: " + con, xoff, yoff, curStat==2?Color.YELLOW:Color.WHITE); + + terminal.writeCenter("[ENTER] to play, [ESC] to go back", terminal.getHeightInCharacters() - 2); + } +} diff --git a/src/main/java/com/github/fabioticconi/alone/screens/LookScreen.java b/src/main/java/com/github/fabioticconi/alone/screens/LookScreen.java index 198d9d3..2d9b92e 100644 --- a/src/main/java/com/github/fabioticconi/alone/screens/LookScreen.java +++ b/src/main/java/com/github/fabioticconi/alone/screens/LookScreen.java @@ -141,12 +141,13 @@ public void display(final AsciiPanel terminal) if (t == null) return; - final int xmax = terminal.getWidthInCharacters(); - final int ymax = terminal.getHeightInCharacters(); + final int ymin = 6; + final int xmax = terminal.getWidthInCharacters(); + final int ymax = terminal.getHeightInCharacters(); final int panelSize = 8; final int playerX = xmax / 2; - final int playerY = (ymax - panelSize) / 2; + final int playerY = (ymax - panelSize + ymin) / 2; terminal.write('@', playerX, playerY, Color.BLACK, Color.LIGHT_GRAY); diff --git a/src/main/java/com/github/fabioticconi/alone/screens/MapScreen.java b/src/main/java/com/github/fabioticconi/alone/screens/MapScreen.java new file mode 100644 index 0000000..8ad0df0 --- /dev/null +++ b/src/main/java/com/github/fabioticconi/alone/screens/MapScreen.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2017 Fabio Ticconi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +package com.github.fabioticconi.alone.screens; + +import asciiPanel.AsciiPanel; +import com.artemis.utils.BitVector; +import com.github.fabioticconi.alone.constants.Options; +import com.github.fabioticconi.alone.systems.MapSystem; +import com.github.fabioticconi.tergen.HeightMap; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.image.BufferedImage; +import java.io.*; +import java.util.ArrayList; + +/** + * Author: Fabio Ticconi + * Date: 21/11/17 + */ +public class MapScreen extends AbstractScreen +{ + float heightmap[][]; + + @Override + protected void initialize() + { + try + { + map.loadTemplates(); + } catch (IOException e) + { + e.printStackTrace(); + } + + regenerate(); + } + + void regenerate() + { + final int seed = (int)(System.currentTimeMillis() / 1000); + final float freq = 3f / Math.max(Options.MAP_SIZE_X, Options.MAP_SIZE_Y); + + final HeightMap heightMap = new HeightMap() + .size(Options.MAP_SIZE_X, Options.MAP_SIZE_Y, seed) + .island(0.85f) + .noise(16, 0.5f, freq, 1f); + + heightmap = heightMap.build(); + + map.terrainFromHeightmap(heightmap); + } + + @Override + public String header() + { + return "World Generation:"; + } + + @Override + public float handleKeys(final BitVector keys) + { + if (keys.get(KeyEvent.VK_ESCAPE)) + screen.select(StartScreen.class); + else if (keys.get(KeyEvent.VK_R)) + { + regenerate(); + } + else if (keys.get(KeyEvent.VK_ENTER)) + { + try + { + map.saveTerrain(heightmap); + } catch (IOException e) + { + e.printStackTrace(); + System.exit(1); + } + + screen.select(CharScreen.class); + } + + keys.clear(); + + return 0f; + } + + @Override + public void display(final AsciiPanel terminal) + { + terminal.clear(); + + // title: + drawHeader(terminal); + + final int xmin = 1; + final int xmax = terminal.getWidthInCharacters() - 1; + final int ymin = 4; + final int ymax = terminal.getHeightInCharacters() - 5; + + final int tileWidth = Math.floorDiv(Options.MAP_SIZE_X, xmax - xmin); + final int tileHeight = Math.floorDiv(Options.MAP_SIZE_Y, ymax - ymin); + + final ArrayList cells = new ArrayList<>(tileWidth * tileHeight); + + for (int x = xmin; x < xmax; x++) + { + for (int y = ymin; y < ymax; y++) + { + for (int tileX = 0; tileX < tileWidth; tileX++) + { + for (int tileY = 0; tileY < tileHeight; tileY++) + { + final int posX = x * tileWidth + tileX; + final int posY = y * tileHeight + tileY; + + // render terrain + final MapSystem.Cell cell = map.get(posX, posY); + + cells.add(cell); + } + } + + float r=0,g=0,b=0; + for (final MapSystem.Cell cell : cells) + { + r += cell.col.getRed()*cell.col.getRed(); + g += cell.col.getGreen()*cell.col.getGreen(); + b += cell.col.getBlue()*cell.col.getBlue(); + } + + r = (float)Math.sqrt(r / cells.size()); + g = (float)Math.sqrt(g / cells.size()); + b = (float)Math.sqrt(b / cells.size()); + + final Color col = new Color(Math.round(r), Math.round(g), Math.round(b)); + terminal.write(' ', x, y, Color.WHITE, col); + + // final Cell cell = cells.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).entrySet() + // .stream().max(Comparator.comparing(Map.Entry::getValue)).get().getKey(); + // + // terminal.write(cell.c, x, y, cell.col, cell.bg); + + cells.clear(); + } + } + + // TODO must take the full map, downscaled a lot (maybe take average colour per tile? or most common colour?) + // and show it in a square/rectangle leaving some little black margin + + terminal.writeCenter("[R]egenerate", terminal.getHeightInCharacters() - 4); + terminal.writeCenter("[ENTER] to confirm, [ESC] to go back", terminal.getHeightInCharacters() - 2); + } +} diff --git a/src/main/java/com/github/fabioticconi/alone/screens/PlayScreen.java b/src/main/java/com/github/fabioticconi/alone/screens/PlayScreen.java index 06a486f..8801839 100644 --- a/src/main/java/com/github/fabioticconi/alone/screens/PlayScreen.java +++ b/src/main/java/com/github/fabioticconi/alone/screens/PlayScreen.java @@ -26,10 +26,9 @@ import com.github.fabioticconi.alone.Main; import com.github.fabioticconi.alone.components.*; import com.github.fabioticconi.alone.components.attributes.Sight; -import com.github.fabioticconi.alone.constants.Cell; import com.github.fabioticconi.alone.constants.Side; import com.github.fabioticconi.alone.constants.WeaponType; -import com.github.fabioticconi.alone.map.SingleGrid; +import com.github.fabioticconi.alone.utils.SingleGrid; import com.github.fabioticconi.alone.messages.AbstractMessage; import com.github.fabioticconi.alone.messages.CannotMsg; import com.github.fabioticconi.alone.messages.Msg; @@ -294,13 +293,15 @@ public void display(final AsciiPanel terminal) final Position p = mPosition.get(playerId); final int sight = mSight.get(playerId).value; + final int xmin = 0; + final int ymin = 6; final int xmax = terminal.getWidthInCharacters(); final int ymax = terminal.getHeightInCharacters(); final int panelSize = 8; final int halfcols = xmax / 2; - final int halfrows = (ymax - panelSize) / 2; + final int halfrows = (ymax - panelSize + ymin) / 2; int posX; int posY; @@ -313,9 +314,9 @@ public void display(final AsciiPanel terminal) final LongBag cells = map.getVisibleCells(p.x, p.y, sight); - for (int x = 0; x < xmax; x++) + for (int x = xmin; x < xmax; x++) { - for (int y = 0; y < ymax - panelSize; y++) + for (int y = ymin; y < ymax - panelSize; y++) { posX = p.x + x - halfcols; posY = p.y + y - halfrows; @@ -325,19 +326,19 @@ public void display(final AsciiPanel terminal) if (map.contains(posX, posY)) { // render terrain - final Cell cell = map.get(posX, posY); - Color tileFg; - final Color tileBg; + final MapSystem.Cell cell = map.get(posX, posY); + Color tileFg; + final Color tileBg; if (cells.contains(key)) { - tileFg = cell.col; - tileBg = cell.bg; + tileFg = cell.col.darker(); + tileBg = cell.col; } else { - tileFg = cell.col.darker().darker().darker(); - tileBg = cell.bg.darker().darker().darker(); + tileFg = cell.col.darker().darker().darker().darker(); + tileBg = cell.col.darker().darker().darker(); } terminal.write(cell.c, x, y, tileFg, tileBg); @@ -400,6 +401,8 @@ else if (sprite.shadowView) } } + terminal.clear(' ', 0, 0, terminal.getWidthInCharacters(), ymin-1); + // title: drawHeader(terminal); @@ -409,7 +412,7 @@ else if (sprite.shadowView) int x; - final int yoff = 3; + final int yoff = ymin-3; // health bar terminal.write('[', 0, yoff, Color.RED); diff --git a/src/main/java/com/github/fabioticconi/alone/screens/StartScreen.java b/src/main/java/com/github/fabioticconi/alone/screens/StartScreen.java index 2071f27..fd128df 100644 --- a/src/main/java/com/github/fabioticconi/alone/screens/StartScreen.java +++ b/src/main/java/com/github/fabioticconi/alone/screens/StartScreen.java @@ -24,6 +24,7 @@ import com.github.fabioticconi.alone.Main; import com.github.fabioticconi.alone.systems.ScreenSystem; +import java.awt.*; import java.awt.event.KeyEvent; import java.util.Collections; import java.util.Properties; @@ -42,10 +43,16 @@ public class StartScreen extends AbstractScreen @Override public float handleKeys(final BitVector keys) { - // TODO: eventually, "N" should re-run the world to start from scratch, - // while "C" either selects the PlayScreen, or (at first start) loads the save file + // TODO: [C] should load a saved game (if any), or if the world is already loaded + // (eg, if the player types ESC while playing), it must simply jump to the PlayScreen - if (keys.get(KeyEvent.VK_N) || keys.get(KeyEvent.VK_C)) + if (keys.get(KeyEvent.VK_N)) + { + keys.clear(); + + screen.select(MapScreen.class); + } + else if (keys.get(KeyEvent.VK_C)) { keys.clear(); @@ -70,13 +77,13 @@ public void display(final AsciiPanel terminal) terminal.writeCenter(String.join("", Collections.nCopies(header.length(), "-")), 3); int y = terminal.getHeightInCharacters() / 2 - 4 * 2; - terminal.writeCenter("(N)ew", y); + terminal.writeCenter("[N]ew", y); y = y + 2; - terminal.writeCenter("(C)ontinue", y); + terminal.writeCenter("[C]ontinue", y, Color.GRAY); y = y + 2; - terminal.writeCenter("(O)ptions [inactive]", y); + terminal.writeCenter("[O]ptions [inactive]", y); y = y + 2; - terminal.writeCenter("Save & (Q)uit", y); + terminal.writeCenter("Save & [Q]uit", y); y = terminal.getHeightInCharacters() - 2; terminal.writeCenter("by Fabio Ticconi", y); diff --git a/src/main/java/com/github/fabioticconi/alone/systems/AISystem.java b/src/main/java/com/github/fabioticconi/alone/systems/AISystem.java index d5fd918..297bcea 100644 --- a/src/main/java/com/github/fabioticconi/alone/systems/AISystem.java +++ b/src/main/java/com/github/fabioticconi/alone/systems/AISystem.java @@ -132,7 +132,7 @@ protected void processExpired(final int entityId) // run the new behaviour and update the context if (bestBehaviour != null && maxScore > 0f) { - System.out.println(entityId + ": " + bestBehaviour.getClass().getSimpleName() + " (" + maxScore + ")"); + // System.out.println(entityId + ": " + bestBehaviour.getClass().getSimpleName() + " (" + maxScore + ")"); ai.time = bestBehaviour.update(); ai.activeBehaviour = bestBehaviour; diff --git a/src/main/java/com/github/fabioticconi/alone/systems/BootstrapSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/BootstrapSystem.java index c25b4c8..878c69f 100644 --- a/src/main/java/com/github/fabioticconi/alone/systems/BootstrapSystem.java +++ b/src/main/java/com/github/fabioticconi/alone/systems/BootstrapSystem.java @@ -17,6 +17,8 @@ */ package com.github.fabioticconi.alone.systems; +import com.artemis.BaseSystem; +import com.artemis.ComponentMapper; import com.artemis.EntityEdit; import com.artemis.annotations.Wire; import com.artemis.managers.PlayerManager; @@ -26,10 +28,12 @@ import com.github.fabioticconi.alone.behaviours.*; import com.github.fabioticconi.alone.components.*; import com.github.fabioticconi.alone.components.attributes.*; -import com.github.fabioticconi.alone.constants.Cell; import com.github.fabioticconi.alone.constants.Options; +import com.github.fabioticconi.alone.constants.TerrainType; import com.github.fabioticconi.alone.utils.Util; import net.mostlyoriginal.api.system.core.PassiveSystem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.awt.*; import java.io.FileInputStream; @@ -40,9 +44,15 @@ /** * @author Fabio Ticconi */ -public class BootstrapSystem extends PassiveSystem +public class BootstrapSystem extends BaseSystem { - private final ClassLoader loader = getClass().getClassLoader(); + static final Logger log = LoggerFactory.getLogger(BootstrapSystem.class); + + ComponentMapper mStr; + ComponentMapper mAgi; + ComponentMapper mCon; + ComponentMapper mHerbivore; + ComponentMapper mCarnivore; @Wire Random r; @@ -57,10 +67,10 @@ public class BootstrapSystem extends PassiveSystem PlayerManager pManager; @Override - protected void initialize() + protected void processSystem() { // this must be only run once - // setEnabled(false); + setEnabled(false); int x; int y; @@ -247,12 +257,9 @@ protected void initialize() { for (y = 0; y < Options.MAP_SIZE_Y; y++) { - final Cell cell = sMap.get(x, y); + final MapSystem.Cell cell = sMap.get(x, y); - // if ((cell.equals(Cell.DEEP_WATER) && r.nextGaussian() > 3f) || - // (cell.equals(Cell.WATER) && r.nextGaussian() > 2.5f)) - if ((cell.equals(Cell.DEEP_WATER) && r.nextGaussian() > 5f) || - (cell.equals(Cell.WATER) && r.nextGaussian() > 5f)) + if (cell.type.equals(TerrainType.WATER) && r.nextGaussian() > 5f) { if (!map.obstacles.isEmpty(x, y)) continue; @@ -288,11 +295,10 @@ protected void initialize() { for (y = 0; y < Options.MAP_SIZE_Y; y++) { - final Cell cell = sMap.get(x, y); + final MapSystem.Cell cell = sMap.get(x, y); - if ((cell.equals(Cell.GRASS) && r.nextGaussian() > 3f) || - (cell.equals(Cell.HILL_GRASS) && r.nextGaussian() > 2f) || - (cell.equals(Cell.HILL) && r.nextGaussian() > 3f)) + if ((cell.type.equals(TerrainType.GRASS) && r.nextGaussian() > 2.5f) || + (cell.type.equals(TerrainType.LAND) && r.nextGaussian() > 3f)) { if (!map.obstacles.isEmpty(x, y)) continue; @@ -307,14 +313,10 @@ protected void initialize() { for (y = 0; y < Options.MAP_SIZE_Y; y++) { - final Cell cell = sMap.get(x, y); - - if (((cell.equals(Cell.HIGH_MOUNTAIN)) && r.nextGaussian() > 3f) || - (cell.equals(Cell.MOUNTAIN) && r.nextGaussian() > 2.5f) || - (cell.equals(Cell.HILL) && r.nextGaussian() > 3f) || - (cell.equals(Cell.HILL_GRASS) && r.nextGaussian() > 3.5f) || - (cell.equals(Cell.GRASS) && r.nextGaussian() > 4f) || - (cell.equals(Cell.GROUND) && r.nextGaussian() > 4f)) + final MapSystem.Cell cell = sMap.get(x, y); + + if ((cell.type.equals(TerrainType.GRASS) && r.nextGaussian() > 3.5f) || + (cell.type.equals(TerrainType.LAND) && r.nextGaussian() > 3f)) { if (!map.obstacles.isEmpty(x, y)) continue; @@ -329,14 +331,10 @@ protected void initialize() { for (y = 0; y < Options.MAP_SIZE_Y; y++) { - final Cell cell = sMap.get(x, y); - - if (((cell.equals(Cell.HIGH_MOUNTAIN)) && r.nextGaussian() > 3f) || - (cell.equals(Cell.MOUNTAIN) && r.nextGaussian() > 2.5f) || - (cell.equals(Cell.HILL) && r.nextGaussian() > 3f) || - (cell.equals(Cell.HILL_GRASS) && r.nextGaussian() > 3.5f) || - (cell.equals(Cell.GRASS) && r.nextGaussian() > 4f) || - (cell.equals(Cell.GROUND) && r.nextGaussian() > 2.5f)) + final MapSystem.Cell cell = sMap.get(x, y); + + if ((cell.type.equals(TerrainType.GRASS) && r.nextGaussian() > 3f) || + (cell.type.equals(TerrainType.LAND) && r.nextGaussian() > 2.5f)) { if (!map.items.isEmpty(x, y)) continue; @@ -351,11 +349,10 @@ protected void initialize() { for (y = 0; y < Options.MAP_SIZE_Y; y++) { - final Cell cell = sMap.get(x, y); + final MapSystem.Cell cell = sMap.get(x, y); - if ((cell.equals(Cell.GRASS) && r.nextGaussian() > 4f) || - (cell.equals(Cell.HILL_GRASS) && r.nextGaussian() > 3f) || - (cell.equals(Cell.HILL) && r.nextGaussian() > 4f)) + if ((cell.type.equals(TerrainType.GRASS) && r.nextGaussian() > 3f) || + (cell.type.equals(TerrainType.LAND) && r.nextGaussian() > 3.5f)) { if (!map.items.isEmpty(x, y)) continue; @@ -367,7 +364,7 @@ protected void initialize() } } - System.out.println("Bootstrap done"); + log.info("initialised"); } public void loadBody(final String filename, final EntityEdit edit) throws IOException @@ -445,4 +442,26 @@ public void loadBody(final String filename, final EntityEdit edit) throws IOExce if (herbivore || carnivore) edit.create(Hunger.class).set(0f, (size / 2f) + 2f); } + + public void makeDerivative(final int id) + { + final Strength str = mStr.get(id); + final Agility agi = mAgi.get(id); + final Constitution con = mCon.get(id); + + final EntityEdit edit = world.edit(id); + + // Secondary Attributes + final int size = Math.round((con.value - agi.value) / 2f); + edit.create(Size.class).set(size); + edit.create(Stamina.class).set((5 + str.value + con.value) * 100); // FIXME for debug, reduce/tweak later + edit.create(Speed.class).set((con.value - str.value - agi.value + 6) / 12f); + edit.create(Health.class).set((con.value + 3) * 10); + + // Tertiary Attributes + + // a fish does not need to eat, for now + if (mHerbivore.has(id) || mCarnivore.has(id)) + edit.create(Hunger.class).set(0f, (size / 2f) + 2f); + } } diff --git a/src/main/java/com/github/fabioticconi/alone/systems/ItemSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/ItemSystem.java index 8629a5d..60a5142 100644 --- a/src/main/java/com/github/fabioticconi/alone/systems/ItemSystem.java +++ b/src/main/java/com/github/fabioticconi/alone/systems/ItemSystem.java @@ -148,46 +148,36 @@ public int makeItem(final String tag, final int x, final int y) public int makeItem(final String tag) { - try - { - loadTemplates(); + final ItemTemplate template = templates.get(tag); - final ItemTemplate template = templates.get(tag); - - if (template == null) - { - log.warn("Item named {} doesn't exist", tag); - return -1; - } - - final int id = world.create(); + if (template == null) + { + log.warn("Item named {} doesn't exist", tag); + return -1; + } - final EntityEdit edit = world.edit(id); + final int id = world.create(); - edit.add(new Name(template.name, tag)); + final EntityEdit edit = world.edit(id); - // TODO find a way to do this dynamically. Right now I cannot figure out a way - // of deserialising an array of Components, because it's an abstract class that I cannot annotate. - if (template.wearable != null) - edit.add(template.wearable); - if (template.weapon != null) - edit.add(template.weapon); - if (template.sprite != null) - edit.add(template.sprite); - if (template.obstacle != null) - edit.add(template.obstacle); - if (template.crushable != null) - edit.add(template.crushable); - if (template.cuttable != null) - edit.add(template.cuttable); + edit.add(new Name(template.name, tag)); - return id; - } catch (final IOException e) - { - e.printStackTrace(); + // TODO find a way to do this dynamically. Right now I cannot figure out a way + // of deserialising an array of Components, because it's an abstract class that I cannot annotate. + if (template.wearable != null) + edit.add(template.wearable); + if (template.weapon != null) + edit.add(template.weapon); + if (template.sprite != null) + edit.add(template.sprite); + if (template.obstacle != null) + edit.add(template.obstacle); + if (template.crushable != null) + edit.add(template.crushable); + if (template.cuttable != null) + edit.add(template.cuttable); - return -1; - } + return id; } public GetAction get(final int actorId) diff --git a/src/main/java/com/github/fabioticconi/alone/systems/MapSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/MapSystem.java index 02def06..86deabb 100644 --- a/src/main/java/com/github/fabioticconi/alone/systems/MapSystem.java +++ b/src/main/java/com/github/fabioticconi/alone/systems/MapSystem.java @@ -18,13 +18,17 @@ package com.github.fabioticconi.alone.systems; import com.artemis.ComponentMapper; +import com.artemis.annotations.Wire; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ser.std.StdArraySerializers; import com.github.fabioticconi.alone.components.Obstacle; -import com.github.fabioticconi.alone.constants.Cell; import com.github.fabioticconi.alone.constants.Options; import com.github.fabioticconi.alone.constants.Side; -import com.github.fabioticconi.alone.map.SingleGrid; +import com.github.fabioticconi.alone.constants.TerrainType; import com.github.fabioticconi.alone.utils.Coords; import com.github.fabioticconi.alone.utils.LongBag; +import com.github.fabioticconi.alone.utils.SingleGrid; import com.github.fabioticconi.alone.utils.Util; import net.mostlyoriginal.api.system.core.PassiveSystem; import org.slf4j.Logger; @@ -36,16 +40,15 @@ import rlforj.los.ShadowCasting; import rlforj.math.Point; import rlforj.pathfinding.AStar; +import rlforj.pathfinding.IPathAlgorithm; import javax.imageio.ImageIO; +import java.awt.*; import java.awt.image.BufferedImage; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.EnumSet; -import java.util.LinkedHashSet; +import java.io.*; +import java.nio.ByteBuffer; +import java.util.*; import java.util.List; -import java.util.Set; /** * @author Fabio Ticconi @@ -54,35 +57,78 @@ public class MapSystem extends PassiveSystem implements IBoard { static final Logger log = LoggerFactory.getLogger(MapSystem.class); - final Cell terrain[][]; + Cell terrain[][]; - /* FOV/LOS stuff */ - final LongBag lastVisited; - final AStar astar; - final IFovAlgorithm fov; - final ILosAlgorithm los; - final SingleGrid obstacles; - final SingleGrid items; + /* FOV/LOS stuff */ LongBag lastVisited; + IPathAlgorithm path; + IFovAlgorithm fov; + ILosAlgorithm los; + SingleGrid obstacles; + SingleGrid items; ComponentMapper mObstacle; - public MapSystem() throws IOException + @Wire + ObjectMapper mapper; + + Map templates; + TreeMap cellAtHeight; + + public MapSystem() + { + + } + + @Override + protected void initialize() { lastVisited = new LongBag(256); fov = new ShadowCasting(); los = new BresLos(true); - final InputStream mapStream = new FileInputStream("data/map/map.png"); + try + { + loadTerrain(); + + obstacles = new SingleGrid(Options.MAP_SIZE_X, Options.MAP_SIZE_Y); + items = new SingleGrid(Options.MAP_SIZE_X, Options.MAP_SIZE_Y); + + path = new AStar(this, Options.MAP_SIZE_X, Options.MAP_SIZE_Y, true); + } catch (final IOException e) + { + e.printStackTrace(); + System.exit(1); + } + + log.info("initialised"); + } + + public void loadTemplates() throws IOException + { + final InputStream fileStream = new FileInputStream("data/map/terrain.yml"); + + templates = mapper.readValue(fileStream, new TypeReference>() + { + }); + + cellAtHeight = new TreeMap<>(); + + for (final Map.Entry entry : templates.entrySet()) + { + final Cell temp = entry.getValue(); + temp.tag = entry.getKey(); + cellAtHeight.put(temp.theight, temp); + } + } + + public void loadTerrain() throws IOException + { + loadTemplates(); + final InputStream elevationStream = new FileInputStream("data/map/elevation.data"); - final BufferedImage img = ImageIO.read(mapStream); final byte[] elevation = elevationStream.readAllBytes(); - Options.MAP_SIZE_X = img.getWidth(); - Options.MAP_SIZE_Y = img.getHeight(); - terrain = new Cell[Options.MAP_SIZE_X][Options.MAP_SIZE_Y]; - obstacles = new SingleGrid(Options.MAP_SIZE_X, Options.MAP_SIZE_Y); - items = new SingleGrid(Options.MAP_SIZE_X, Options.MAP_SIZE_Y); // TODO: we should make Cell a class, and obviously use a pool. // This way we can load the cells from a yaml file, with their thresholds, colours, characters etc, @@ -90,56 +136,56 @@ public MapSystem() throws IOException // for example. // movement costs should also be part of this cell. - float value; - for (int x = 0; x < Options.MAP_SIZE_X; x++) { for (int y = 0; y < Options.MAP_SIZE_Y; y++) { - final byte h = elevation[x * Options.MAP_SIZE_X + y]; - final int uns_h = Byte.toUnsignedInt(h); - value = (float) (uns_h) / 255f; + final byte h = elevation[x * Options.MAP_SIZE_X + y]; + final int uns_h = Byte.toUnsignedInt(h); + final float value = (float) (uns_h) / 255f; - if (value < 0.01) - { - terrain[x][y] = Cell.DEEP_WATER; - } - else if (value < 0.05f) - { - terrain[x][y] = Cell.WATER; - } - else if (value < 0.08f) - { - terrain[x][y] = Cell.SAND; - } - else if (value < 0.1f) - { - terrain[x][y] = Cell.GROUND; - } - else if (value < 0.4f) - { - terrain[x][y] = Cell.GRASS; - } - else if (value < 0.7f) - { - terrain[x][y] = Cell.HILL_GRASS; - } - else if (value < 0.8f) - { - terrain[x][y] = Cell.HILL; - } - else if (value < 0.9f) - { - terrain[x][y] = Cell.MOUNTAIN; - } - else - { - terrain[x][y] = Cell.HIGH_MOUNTAIN; - } + final float key = cellAtHeight.higherKey(value); + final Cell cell = cellAtHeight.get(key); + + terrain[x][y] = cell; + } + } + } + + public void saveTerrain(final float[][] heightmap) throws IOException + { + final OutputStream elevationStream = new FileOutputStream("data/map/elevation.data"); + + final byte[] elevation = new byte[heightmap.length * heightmap[0].length]; + + for (int x = 0; x < Options.MAP_SIZE_X; x++) + { + for (int y = 0; y < Options.MAP_SIZE_Y; y++) + { + elevation[x * Options.MAP_SIZE_X + y] = (byte)(heightmap[x][y]*255f); } } - astar = new AStar(this, Options.MAP_SIZE_X, Options.MAP_SIZE_Y, true); + elevationStream.write(elevation); + + elevationStream.close(); + } + + public void terrainFromHeightmap(final float[][] heightmap) + { + if (terrain == null || terrain.length != Options.MAP_SIZE_X || terrain[0].length != Options.MAP_SIZE_Y) + terrain = new Cell[Options.MAP_SIZE_X][Options.MAP_SIZE_Y]; + + for (int x = 0; x < Options.MAP_SIZE_X; x++) + { + for (int y = 0; y < Options.MAP_SIZE_Y; y++) + { + final float key = cellAtHeight.higherKey(heightmap[x][y]); + final Cell cell = cellAtHeight.get(key); + + terrain[x][y] = cell; + } + } } /** @@ -178,7 +224,7 @@ public Set getFreeExits(final int x, final int y) * @param y * @return A set of free exits, or HERE if none is available */ - public Set getFreeExits(final int x, final int y, final EnumSet set) + public Set getFreeExits(final int x, final int y, final EnumSet set) { int xn, yn; @@ -192,7 +238,7 @@ public Set getFreeExits(final int x, final int y, final EnumSet set) xn = x + side.x; yn = y + side.y; - if (contains(xn, yn) && obstacles.get(xn, yn) < 0 && set.contains(terrain[x][y])) + if (contains(xn, yn) && obstacles.get(xn, yn) < 0 && set.contains(terrain[x][y].type)) exits.add(side); } @@ -325,14 +371,14 @@ else if (maxRadius == 0) * * @return null if none could be found, the coordinate array otherwise */ - public int[] getFirstOfType(final int x, final int y, final int r, final EnumSet set) + public int[] getFirstOfType(final int x, final int y, final int r, final EnumSet set) { - if (set.contains(terrain[x][y])) + if (set.contains(terrain[x][y].type)) return new int[] { x, y }; lastVisited.clear(); - fov.visitFieldOfView(this, x, y, r); + fov.visitFoV(this, x, y, r); int[] coords; Cell cell; @@ -343,7 +389,7 @@ public int[] getFirstOfType(final int x, final int y, final int r, final EnumSet coords = Coords.unpackCoords(key); cell = terrain[coords[0]][coords[1]]; - if (set.contains(cell)) + if (set.contains(cell.type)) return coords; } @@ -443,25 +489,61 @@ public LongBag getVisibleCells(final int x, final int y, final int r) { lastVisited.clear(); - fov.visitFieldOfView(this, x, y, r); + fov.visitFoV(this, x, y, r); return lastVisited; } public List getLineOfSight(final int startX, final int startY, final int endX, final int endY) { - final boolean exists = los.existsLineOfSight(this, startX, startY, endX, endY, true); + final boolean exists = los.exists(this, startX, startY, endX, endY, true); // FIXME: rlforj-alt should either always return a list, or always an array if (exists) - return los.getProjectPath(); + return los.getPath(); return null; } public Point[] getPath(final int startX, final int startY, final int endX, final int endY, final int radius) { - return astar.findPath(startX, startY, endX, endY, radius); + return path.findPath(startX, startY, endX, endY, radius); + } + + /** + * @author Fabio Ticconi + */ + public static class Cell implements Comparable + { + public final static Cell EMPTY = new Cell("empty", ' ', Color.BLACK, null, 0f); + + public String tag; + + public char c; + public Color col; + public TerrainType type; + + public float theight; + + public Cell() + { + + } + + public Cell(final String tag, final char c, final Color col, final TerrainType type, final float theight) + { + this.tag = tag; + this.c = c; + this.col = col; + this.type = type; + this.theight = theight; + } + + @Override + public int compareTo(final Float o) + { + return Float.compare(theight, o); + } } } diff --git a/src/main/java/com/github/fabioticconi/alone/systems/MovementSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/MovementSystem.java index 2b6da60..61fdfff 100644 --- a/src/main/java/com/github/fabioticconi/alone/systems/MovementSystem.java +++ b/src/main/java/com/github/fabioticconi/alone/systems/MovementSystem.java @@ -23,8 +23,9 @@ import com.github.fabioticconi.alone.components.Speed; import com.github.fabioticconi.alone.components.Underwater; import com.github.fabioticconi.alone.components.actions.ActionContext; -import com.github.fabioticconi.alone.constants.Cell; import com.github.fabioticconi.alone.constants.Side; +import com.github.fabioticconi.alone.constants.TerrainType; +import com.github.fabioticconi.alone.utils.Util; import net.mostlyoriginal.api.system.core.PassiveSystem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -76,7 +77,7 @@ public boolean tryAction() if (!map.isFree(x2, y2)) return false; - final Cell cell = map.get(x2, y2); + final MapSystem.Cell cell = map.get(x2, y2); if (mUnderWater.has(actorId)) { @@ -95,33 +96,10 @@ public boolean tryAction() return true; } - switch (cell) - { - case HILL: - case HILL_GRASS: - cost = 1.5f; - - break; - - case MOUNTAIN: - cost = 2f; - break; - - case HIGH_MOUNTAIN: - cost = 3f; - break; - - case WATER: - cost = 3f; - break; - - case DEEP_WATER: - cost = 4f; - break; - - default: - cost = 1f; - } + if (cell.type == TerrainType.WATER) + cost = 2f - Util.bias(cell.theight, 0.97f); + else + cost = 1f + Util.bias(cell.theight, 0.75f); delay = speed.value * cost; diff --git a/src/main/java/com/github/fabioticconi/alone/systems/UnderwaterSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/UnderwaterSystem.java index 2cba61a..e1700c4 100644 --- a/src/main/java/com/github/fabioticconi/alone/systems/UnderwaterSystem.java +++ b/src/main/java/com/github/fabioticconi/alone/systems/UnderwaterSystem.java @@ -24,7 +24,7 @@ import com.github.fabioticconi.alone.components.Dead; import com.github.fabioticconi.alone.components.Position; import com.github.fabioticconi.alone.components.Underwater; -import com.github.fabioticconi.alone.constants.Cell; +import com.github.fabioticconi.alone.constants.TerrainType; import java.util.EnumSet; @@ -34,11 +34,12 @@ */ public class UnderwaterSystem extends IntervalIteratingSystem { - private final EnumSet validCells = EnumSet.of(Cell.WATER, Cell.DEEP_WATER); ComponentMapper mPos; HealthSystem sHealth; MapSystem map; + private final EnumSet validCells = EnumSet.of(TerrainType.WATER); + public UnderwaterSystem(final float interval) { super(Aspect.all(Underwater.class, Position.class).exclude(Dead.class), interval); @@ -49,7 +50,7 @@ protected void process(final int entityId) { final Position p = mPos.get(entityId); - final Cell c = map.get(p.x, p.y); + final MapSystem.Cell c = map.get(p.x, p.y); // if inside water, no problem if (validCells.contains(c)) diff --git a/src/main/java/com/github/fabioticconi/alone/map/SingleGrid.java b/src/main/java/com/github/fabioticconi/alone/utils/SingleGrid.java similarity index 99% rename from src/main/java/com/github/fabioticconi/alone/map/SingleGrid.java rename to src/main/java/com/github/fabioticconi/alone/utils/SingleGrid.java index a5abef5..98ada18 100644 --- a/src/main/java/com/github/fabioticconi/alone/map/SingleGrid.java +++ b/src/main/java/com/github/fabioticconi/alone/utils/SingleGrid.java @@ -16,7 +16,7 @@ * */ -package com.github.fabioticconi.alone.map; +package com.github.fabioticconi.alone.utils; import com.artemis.utils.IntBag; import com.github.fabioticconi.alone.utils.Coords;