diff --git a/assets/map/RPGScreenshot.png b/assets/map/RPGScreenshot.png new file mode 100644 index 0000000..33e468f Binary files /dev/null and b/assets/map/RPGScreenshot.png differ diff --git a/assets/map/RPGTiles.png b/assets/map/RPGTiles.png new file mode 100644 index 0000000..6b7107a Binary files /dev/null and b/assets/map/RPGTiles.png differ diff --git a/assets/map/boss_chest.png b/assets/map/boss_chest.png new file mode 100644 index 0000000..333cecb Binary files /dev/null and b/assets/map/boss_chest.png differ diff --git a/assets/map/grassland.png b/assets/map/grassland.png new file mode 100644 index 0000000..0a56056 Binary files /dev/null and b/assets/map/grassland.png differ diff --git a/assets/map/grassland_structures.png b/assets/map/grassland_structures.png new file mode 100644 index 0000000..d24fb92 Binary files /dev/null and b/assets/map/grassland_structures.png differ diff --git a/assets/map/grassland_trees.png b/assets/map/grassland_trees.png new file mode 100644 index 0000000..1b87de6 Binary files /dev/null and b/assets/map/grassland_trees.png differ diff --git a/assets/map/grassland_water.png b/assets/map/grassland_water.png new file mode 100644 index 0000000..d99a40f Binary files /dev/null and b/assets/map/grassland_water.png differ diff --git a/assets/map/rottentower.png b/assets/map/rottentower.png new file mode 100644 index 0000000..3c3085f Binary files /dev/null and b/assets/map/rottentower.png differ diff --git a/assets/map/set_rules.png b/assets/map/set_rules.png new file mode 100644 index 0000000..dbbea97 Binary files /dev/null and b/assets/map/set_rules.png differ diff --git a/assets/map/tiled_collision.png b/assets/map/tiled_collision.png new file mode 100644 index 0000000..30c5d9b Binary files /dev/null and b/assets/map/tiled_collision.png differ diff --git a/assets/map/tiled_grassland_2x2.png b/assets/map/tiled_grassland_2x2.png new file mode 100644 index 0000000..bd5c09a Binary files /dev/null and b/assets/map/tiled_grassland_2x2.png differ diff --git a/assets/terrain.png b/assets/terrain.png new file mode 100644 index 0000000..3411f5d Binary files /dev/null and b/assets/terrain.png differ diff --git a/core/src/com/cpa/project/Camera/OrthographicCamera.java b/core/src/com/cpa/project/Camera/OrthographicCamera.java index e83c710..808fb7d 100644 --- a/core/src/com/cpa/project/Camera/OrthographicCamera.java +++ b/core/src/com/cpa/project/Camera/OrthographicCamera.java @@ -1,6 +1,7 @@ package com.cpa.project.Camera; import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.viewport.ScreenViewport; import com.badlogic.gdx.utils.viewport.Viewport; @@ -9,6 +10,9 @@ public class OrthographicCamera extends com.badlogic.gdx.graphics.OrthographicCamera { Entity target; float lerp = 0.1f; + private float zoom = 1.0f; + + Viewport viewport; @@ -23,6 +27,12 @@ public void update(float dt) { position.x = target.getSprite().getX() + target.getSprite().getWidth() / 2; position.y = target.getSprite().getY() + target.getSprite().getHeight() / 2; } + + position.x += zoom * 0.5f; + position.y -= zoom * 0.25f; + + position.lerp(new Vector3(position.x, position.y, 0), lerp); + viewport.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); } @@ -41,4 +51,13 @@ public Viewport getViewport() { public void setViewport(Viewport viewport) { this.viewport = viewport; } + + + public void setZoom(float zoom) { + this.zoom = zoom; + } + + public float getZoom() { + return zoom; + } } \ No newline at end of file diff --git a/core/src/com/cpa/project/Survivors.java b/core/src/com/cpa/project/Survivors.java index 7bf13f6..85d6ae3 100644 --- a/core/src/com/cpa/project/Survivors.java +++ b/core/src/com/cpa/project/Survivors.java @@ -2,7 +2,6 @@ import com.badlogic.gdx.Game; import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.Input; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.Sprite; import com.badlogic.gdx.graphics.g2d.SpriteBatch; @@ -13,7 +12,7 @@ import com.cpa.project.Entities.Actors.Mobs.Skeleton; import com.cpa.project.Entities.Actors.Player; import com.cpa.project.Entities.Entity; -import com.cpa.project.World.MapManager; +import com.cpa.project.World.Map; import com.cpa.project.World.World; import java.util.HashSet; @@ -22,15 +21,18 @@ public class Survivors extends Game { SpriteBatch batch; World world; - MapManager mapManager; ShapeRenderer shapeRenderer; OrthographicCamera camera; + Player player ; + + Map map; + @Override public void create() { batch = new SpriteBatch(); - Player player = new Player(new Vector2(800, 240), new Sprite(new Texture("threeformsPrev.png"))); - player.setSpeed(50); + player = new Player(new Vector2(800, 240), new Sprite(new Texture("threeformsPrev.png"))); + player.setSpeed(25); player.setHealth(10); Set entities = new HashSet<>(); entities.add(player); @@ -39,21 +41,22 @@ public void create() { camera = new OrthographicCamera(); camera.setTarget(player); camera.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); - mapManager = new MapManager(camera ,1); + map = new Map(batch, camera, player.getPosition()); world = new World(player, entities, camera ); shapeRenderer = new ShapeRenderer(); + // change the player position to the center of the map + player.setPosition(new Vector2((float) (map.getWidth() * 48) / 2, (float) (map.getHeight() * 48) / 2)); } @Override public void render() { ScreenUtils.clear(0, 1, 0, 0.2f); this.camera.update(Gdx.graphics.getDeltaTime()); - - mapManager.update(Gdx.graphics.getDeltaTime(),batch, shapeRenderer, world.getPlayer().getPosition()); - + map.render( this.player.getPosition()); batch.setProjectionMatrix(this.world.getCamera().combined); batch.begin(); + world.update(Gdx.graphics.getDeltaTime()); world.getPlayer().getSprite().draw(batch); for (Entity entity : world.getEntities()) { diff --git a/core/src/com/cpa/project/Tiles/Tile.java b/core/src/com/cpa/project/Tiles/Tile.java new file mode 100644 index 0000000..b8fb537 --- /dev/null +++ b/core/src/com/cpa/project/Tiles/Tile.java @@ -0,0 +1,203 @@ +package com.cpa.project.Tiles; + +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.maps.MapProperties; +import com.badlogic.gdx.maps.tiled.tiles.StaticTiledMapTile; +import com.badlogic.gdx.math.Vector2; + +import java.util.Arrays; + +public class Tile extends StaticTiledMapTile { + private int id ; + private BlendMode blendMode = BlendMode.ALPHA; // on utilise le mode alpha par défaut + private MapProperties properties ; + private TextureRegion textureRegion ; + private boolean isReacheable ; + + private Vector2 position; + + public Tile(int id , TextureRegion textureRegion) { + super(textureRegion); + this.id = id ; + this.textureRegion = textureRegion ; + this.properties = new MapProperties(); + this.isReacheable = true ; + } + + public Tile(int id , TextureRegion textureRegion , Boolean isReacheable) { + super(textureRegion); + this.id = id ; + this.textureRegion = textureRegion ; + this.properties = new MapProperties(); + this.isReacheable = isReacheable ; + } + + public void setPosition(Vector2 position) { + this.position = position; + } + + public Vector2 getPosition() { + return this.position; + } + + + public void setIsReachable(boolean value) + { + this.isReacheable = value; + } + + public boolean isReachable() + { + return this.isReacheable; + } + + @Override + public int getId() + { + return this.id; + } + + @Override + public void setId(int id) + { + this.id = id; + + } + + @Override + public BlendMode getBlendMode() + { + return this.blendMode; + } + + @Override + public void setBlendMode(BlendMode blendMode) + { + this.blendMode = blendMode; + + } + + @Override + public TextureRegion getTextureRegion() + { + return this.textureRegion; + } + + @Override + public MapProperties getProperties() + { + return this.properties; + } + + public void update(float dt , SpriteBatch batch , float x , float y ) { + // draw the tile + batch.draw(textureRegion, x, y); + } + + public Tile clone(int id) { + return new Tile(id, this.textureRegion); + } + + public boolean isIn(int[] ids) { + for (int i = 0; i < ids.length; i++) { + if (this.id == ids[i]) { + return true; + } + } + return false; + } + + public TileType getTileType() { + //System.out.println("this.id : " + this.id); + //System.out.println("List.of(terrainFloorTiles.SAND) : " ); + //System.out.println(Arrays.toString(terrainFloorTiles.SAND)); + if (isIn(terrainFloorTiles.SAND)) { + return TileType.Sand; + } + if (isIn(terrainFloorTiles.GRASS)) { + return TileType.Grass; + } + if (isIn(terrainFloorTiles.ROCK)) { + return TileType.Rock; + } + if (isIn(terrainFloorTiles.WATER)) { + return TileType.Water; + } + + return TileType.None; + } + + public Tile getTransition(TileType top, TileType right, TileType bottom, TileType left, + TileType topLeft, TileType topRight, TileType bottomLeft, TileType bottomRight) { + TileType centerType = getTileType(); + if (centerType == TileType.Sand) { + if (top == TileType.Grass && left == TileType.Grass) { + return terrainFloorTiles.sandCenterWGrass[0][0]; + } + if (top == TileType.Grass && right == TileType.Grass) { + return terrainFloorTiles.sandCenterWGrass[0][2]; + } + if (bottom == TileType.Grass && left == TileType.Grass) { + return terrainFloorTiles.sandCenterWGrass[2][0]; + } + if (bottom == TileType.Grass && right == TileType.Grass) { + return terrainFloorTiles.sandCenterWGrass[2][2]; + } + if (left == TileType.Grass && right == TileType.Grass){ + if (bottom == TileType.Sand && top == TileType.Sand){ + return terrainFloorTiles.sandCenterWGrass[1][1]; + } + return terrainFloorTiles.sandCenterWGrass[1][1]; + } + + if (top == TileType.Grass) { + return terrainFloorTiles.sandCenterWGrass[0][1]; + } + if (right == TileType.Grass) { + return terrainFloorTiles.sandCenterWGrass[1][2]; + } + if (bottom == TileType.Grass) { + return terrainFloorTiles.sandCenterWGrass[2][1]; + } + if (left == TileType.Grass) { + return terrainFloorTiles.sandCenterWGrass[1][0]; + } + + } + + if (centerType == TileType.Rock) { + if (top == TileType.Sand && left == TileType.Sand) { + return terrainFloorTiles.rockCenterWSand[0][0]; + } + if (top == TileType.Sand && right == TileType.Sand) { + return terrainFloorTiles.rockCenterWSand[0][2]; + } + if (bottom == TileType.Sand && left == TileType.Sand) { + return terrainFloorTiles.rockCenterWSand[2][0]; + } + if (bottom == TileType.Sand && right == TileType.Sand) { + return terrainFloorTiles.rockCenterWSand[2][2]; + } + if (top == TileType.Sand) { + return terrainFloorTiles.rockCenterWSand[0][1]; + } + if (right == TileType.Sand) { + return terrainFloorTiles.rockCenterWSand[1][2]; + } + if (bottom == TileType.Sand) { + return terrainFloorTiles.rockCenterWSand[2][1]; + } + if (left == TileType.Sand) { + return terrainFloorTiles.rockCenterWSand[1][0]; + } + } + if (centerType == TileType.Water) { + return terrainFloorTiles.waterTiles[1][1]; + } + + return this; + } + + +} diff --git a/core/src/com/cpa/project/Tiles/TileType.java b/core/src/com/cpa/project/Tiles/TileType.java new file mode 100644 index 0000000..d1694b5 --- /dev/null +++ b/core/src/com/cpa/project/Tiles/TileType.java @@ -0,0 +1,5 @@ +package com.cpa.project.Tiles; + +public enum TileType { + Sand, Grass, Rock, Water, None // etc. +} diff --git a/core/src/com/cpa/project/Tiles/terrainFloorTiles.java b/core/src/com/cpa/project/Tiles/terrainFloorTiles.java new file mode 100644 index 0000000..ef9e6b3 --- /dev/null +++ b/core/src/com/cpa/project/Tiles/terrainFloorTiles.java @@ -0,0 +1,116 @@ +package com.cpa.project.Tiles; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.TextureRegion; + +import java.util.HashMap; +import java.util.Map; + +public class terrainFloorTiles { + // we define the ids of the tiles in the terrain tileset + public static final int[] GRASS = {0, 1, 2, 3, 4, 5, 6, 7, 8}; + public static final int[] SAND = { 10, 11, 12, 13, 14, 15, 16, 17, 18}; + public static final int[] ROCK = {20, 21, 22, 23, 24, 25, 26, 27, 28}; + public static final int[] VOID = {30, 31, 32, 33, 34, 35, 36, 37, 38}; + public static final int[] WATER = {40, 41, 42, 43, 44, 45, 46, 47, 48}; + + // our terrain tileset is "terrain.png" + public static Texture terrainTiles ; + + + + public static TextureRegion[][] terrainTilesSplit ; + + // we load the tree tile + public static Texture treeTexture ; + public static TextureRegion treeTile; + + public static Tile tree1Tile ; + + + + // -- these are from the terrain tiles + // we define each box as a 3x3 matrix of tiles + // we define the center tile of each box as the main tile + // and the surrounding tiles as the surrounding tiles + public static Tile[][] sandCenterWGrass ; + public static Tile[][] grassCenterWSand ; + public static Tile[][] rockCenterWSand ; + public static Tile[][] voidCenterWRock ; + public static Tile[][] waterTiles; + public static Tile[][] flowerTiles; + + + + public static void init() { + + terrainTiles = new Texture(Gdx.files.internal("terrain.png")); + terrainTilesSplit = TextureRegion.split(terrainTiles, 48, 48); + treeTexture = new Texture(Gdx.files.internal("map/grassland_trees.png")); // pas le meme tileset + int treeTileWidth = treeTexture.getWidth() / 8; + int treeTileHeight = treeTexture.getHeight() / 2; + treeTile = new TextureRegion(treeTexture, 0, 0, treeTileWidth, treeTileHeight); + tree1Tile = new Tile(50, treeTile); + sandCenterWGrass = new Tile[][] { + {new Tile(SAND[0], terrainTilesSplit[0][0]), new Tile(SAND[1], terrainTilesSplit[0][1]), new Tile(SAND[2], terrainTilesSplit[0][2])}, + {new Tile(SAND[3], terrainTilesSplit[1][0]), new Tile(SAND[4], terrainTilesSplit[1][1]), new Tile(SAND[5], terrainTilesSplit[1][2])}, + {new Tile(SAND[6], terrainTilesSplit[2][0]), new Tile(SAND[7], terrainTilesSplit[2][1]), new Tile(SAND[8], terrainTilesSplit[2][2])} + }; + + grassCenterWSand = new Tile[][] { + {new Tile(GRASS[0], terrainTilesSplit[0][3]), new Tile(GRASS[1], terrainTilesSplit[0][4]), new Tile(GRASS[2], terrainTilesSplit[0][5])}, + {new Tile(GRASS[3], terrainTilesSplit[1][3]), new Tile(GRASS[4], terrainTilesSplit[1][4]), new Tile(GRASS[5], terrainTilesSplit[1][5])}, + {new Tile(GRASS[6], terrainTilesSplit[2][3]), new Tile(GRASS[7], terrainTilesSplit[2][4]), new Tile(GRASS[8], terrainTilesSplit[2][5])} + }; + + rockCenterWSand = new Tile[][] { + {new Tile(ROCK[0], terrainTilesSplit[0][7]), new Tile(ROCK[1], terrainTilesSplit[0][8]), new Tile(ROCK[2], terrainTilesSplit[0][9])}, + {new Tile(ROCK[3], terrainTilesSplit[1][7]), new Tile(ROCK[4], terrainTilesSplit[1][8]), new Tile(ROCK[5], terrainTilesSplit[1][9])}, + {new Tile(ROCK[6], terrainTilesSplit[2][7]), new Tile(ROCK[7], terrainTilesSplit[2][8]), new Tile(ROCK[8], terrainTilesSplit[2][9])} + }; + + voidCenterWRock = new Tile[][] { + {new Tile(VOID[0], terrainTilesSplit[0][9]), new Tile(VOID[1], terrainTilesSplit[0][10]), new Tile(VOID[2], terrainTilesSplit[0][11])}, + {new Tile(VOID[3], terrainTilesSplit[1][9]), new Tile(VOID[4], terrainTilesSplit[1][10]), new Tile(VOID[5], terrainTilesSplit[1][11])}, + {new Tile(VOID[6], terrainTilesSplit[2][9]), new Tile(VOID[7], terrainTilesSplit[2][10]), new Tile(VOID[8], terrainTilesSplit[2][11])} + }; + + waterTiles = new Tile[][] { + {new Tile(WATER[0], terrainTilesSplit[11][3] , false), new Tile(WATER[1], terrainTilesSplit[11][4],false), new Tile(WATER[2], terrainTilesSplit[11][5],false)}, + {new Tile(WATER[3], terrainTilesSplit[11][6], false), new Tile(WATER[4], terrainTilesSplit[11][7],false), new Tile(WATER[5], terrainTilesSplit[11][8],false)}, + {new Tile(WATER[6], terrainTilesSplit[11][9], false), new Tile(WATER[7], terrainTilesSplit[11][10],false), new Tile(WATER[8], terrainTilesSplit[11][11],false)} + }; + + flowerTiles = new Tile[][] { + {new Tile(50, terrainTilesSplit[3][0]), new Tile(50, terrainTilesSplit[3][1]), new Tile(50, terrainTilesSplit[3][2])}, + {new Tile(50, terrainTilesSplit[4][0]), new Tile(50, terrainTilesSplit[4][1]), new Tile(50, terrainTilesSplit[4][2])} + }; + + + } + + public Tile[][] getTileBox(int id) { + switch (id) { + case 0, 1, 2, 3, 4, 5, 6, 7, 8: + return sandCenterWGrass; + case 10, 11, 12, 13, 14, 15, 16, 17, 18: + return grassCenterWSand; + case 20, 21, 22, 23, 24, 25, 26, 27, 28: + return rockCenterWSand; + case 30, 31, 32, 33, 34, 35, 36, 37, 38: + return voidCenterWRock; + case 40, 41, 42, 43, 44, 45, 46, 47, 48: + return waterTiles; + default: + return null; + } + } + public terrainFloorTiles() { + init(); + } + + + + +} diff --git a/core/src/com/cpa/project/Utils/Direction.java b/core/src/com/cpa/project/Utils/Direction.java new file mode 100644 index 0000000..9c4adc0 --- /dev/null +++ b/core/src/com/cpa/project/Utils/Direction.java @@ -0,0 +1,12 @@ +package com.cpa.project.Utils; + +public enum Direction { + TOP, + DOWN, + LEFT, + RIGHT, + TOP_LEFT, + TOP_RIGHT, + DOWN_LEFT, + DOWN_RIGHT +} diff --git a/core/src/com/cpa/project/Utils/PerlinNoiseGenerator.java b/core/src/com/cpa/project/Utils/PerlinNoiseGenerator.java new file mode 100644 index 0000000..b24c122 --- /dev/null +++ b/core/src/com/cpa/project/Utils/PerlinNoiseGenerator.java @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.cpa.project.Utils; + +import com.badlogic.gdx.graphics.Pixmap; +import com.badlogic.gdx.graphics.Pixmap.Format; +import com.badlogic.gdx.math.MathUtils; + +/** Adapted from http://devmag.org.za/2009/04/25/perlin-noise/ + * @author badlogic */ +public class PerlinNoiseGenerator { + public static float[][] generateWhiteNoise (int width, int height) { + float[][] noise = new float[width][height]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + noise[x][y] = MathUtils.random(); + } + } + return noise; + } + + public static float interpolate (float x0, float x1, float alpha) { + return x0 * (1 - alpha) + alpha * x1; + } + + public static float[][] generateSmoothNoise (float[][] baseNoise, int octave) { + int width = baseNoise.length; + int height = baseNoise[0].length; + float[][] smoothNoise = new float[width][height]; + + int samplePeriod = 1 << octave; // calculates 2 ^ k + float sampleFrequency = 1.0f / samplePeriod; + for (int i = 0; i < width; i++) { + int sample_i0 = (i / samplePeriod) * samplePeriod; + int sample_i1 = (sample_i0 + samplePeriod) % width; // wrap around + float horizontal_blend = (i - sample_i0) * sampleFrequency; + + for (int j = 0; j < height; j++) { + int sample_j0 = (j / samplePeriod) * samplePeriod; + int sample_j1 = (sample_j0 + samplePeriod) % height; // wrap around + float vertical_blend = (j - sample_j0) * sampleFrequency; + float top = interpolate(baseNoise[sample_i0][sample_j0], baseNoise[sample_i1][sample_j0], horizontal_blend); + float bottom = interpolate(baseNoise[sample_i0][sample_j1], baseNoise[sample_i1][sample_j1], horizontal_blend); + smoothNoise[i][j] = interpolate(top, bottom, vertical_blend); + } + } + + return smoothNoise; + } + + public static float[][] generatePerlinNoise (float[][] baseNoise, int octaveCount) { + int width = baseNoise.length; + int height = baseNoise[0].length; + float[][][] smoothNoise = new float[octaveCount][][]; // an array of 2D arrays containing + float persistance = (float) SimplexNoise.noise(width, height); // a float between 0 and 1 + + for (int i = 0; i < octaveCount; i++) { + smoothNoise[i] = + generateSmoothNoise(baseNoise, i); + } + + float[][] perlinNoise = new float[width][height]; // an array of floats initialised to 0 + + float amplitude = 1.0f; + float totalAmplitude = 0.0f; + + for (int octave = octaveCount - 1; octave >= 0; octave--) { + amplitude *= persistance; + totalAmplitude += amplitude; + + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + perlinNoise[i][j] += smoothNoise[octave][i][j] * amplitude; + } + } + } + + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + perlinNoise[i][j] /= totalAmplitude; + } + } + + return perlinNoise; + } + + public static float[][] generatePerlinNoise (int width, int height, int octaveCount) { + float[][] baseNoise = generateWhiteNoise(width, height); + return generatePerlinNoise(baseNoise, octaveCount); + } + + public static byte[] generateHeightMap (int width, int height, int min, int max, int octaveCount) { + float[][] baseNoise = generateWhiteNoise(width, height); + float[][] noise = generatePerlinNoise(baseNoise, octaveCount); + byte[] bytes = new byte[baseNoise.length * baseNoise[0].length]; + int idx = 0; + int range = max - min; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + bytes[idx++] = (byte)(noise[x][y] * range + min); + } + } + return bytes; + } + + public static Pixmap generatePixmap (int width, int height, int min, int max, int octaveCount) { + byte[] bytes = generateHeightMap(width, height, min, max, octaveCount); + Pixmap pixmap = new Pixmap(width, height, Format.RGBA8888); + for (int i = 0, idx = 0; i < bytes.length; i++) { + byte val = bytes[i]; + pixmap.getPixels().put(idx++, val); + pixmap.getPixels().put(idx++, val); + pixmap.getPixels().put(idx++, val); + pixmap.getPixels().put(idx++, (byte)255); + } + return pixmap; + } + +} diff --git a/core/src/com/cpa/project/Utils/SimplexNoise.java b/core/src/com/cpa/project/Utils/SimplexNoise.java new file mode 100644 index 0000000..981e491 --- /dev/null +++ b/core/src/com/cpa/project/Utils/SimplexNoise.java @@ -0,0 +1,357 @@ +package com.cpa.project.Utils;/* + * A speed-improved simplex noise algorithm for 2D, 3D and 4D in Java. + * + * Based on example code by Stefan Gustavson (stegu@itn.liu.se). + * Optimisations by Peter Eastman (peastman@drizzle.stanford.edu). + * Better rank ordering method by Stefan Gustavson in 2012. + * + * This could be speeded up even further, but it's useful as it is. + * + * Version 2012-03-09 + * + * This code was placed in the public domain by its original author, + * Stefan Gustavson. You may use it as you see fit, but + * attribution is appreciated. + * + * code trouvé dans : https://github.com/SRombauts/SimplexNoise/blob/master/references/SimplexNoise.java + */ + +public class SimplexNoise { // Simplex noise in 2D, 3D and 4D + private static Grad grad3[] = {new Grad(1,1,0),new Grad(-1,1,0),new Grad(1,-1,0),new Grad(-1,-1,0), + new Grad(1,0,1),new Grad(-1,0,1),new Grad(1,0,-1),new Grad(-1,0,-1), + new Grad(0,1,1),new Grad(0,-1,1),new Grad(0,1,-1),new Grad(0,-1,-1)}; + + private static Grad grad4[]= {new Grad(0,1,1,1),new Grad(0,1,1,-1),new Grad(0,1,-1,1),new Grad(0,1,-1,-1), + new Grad(0,-1,1,1),new Grad(0,-1,1,-1),new Grad(0,-1,-1,1),new Grad(0,-1,-1,-1), + new Grad(1,0,1,1),new Grad(1,0,1,-1),new Grad(1,0,-1,1),new Grad(1,0,-1,-1), + new Grad(-1,0,1,1),new Grad(-1,0,1,-1),new Grad(-1,0,-1,1),new Grad(-1,0,-1,-1), + new Grad(1,1,0,1),new Grad(1,1,0,-1),new Grad(1,-1,0,1),new Grad(1,-1,0,-1), + new Grad(-1,1,0,1),new Grad(-1,1,0,-1),new Grad(-1,-1,0,1),new Grad(-1,-1,0,-1), + new Grad(1,1,1,0),new Grad(1,1,-1,0),new Grad(1,-1,1,0),new Grad(1,-1,-1,0), + new Grad(-1,1,1,0),new Grad(-1,1,-1,0),new Grad(-1,-1,1,0),new Grad(-1,-1,-1,0)}; + + private static short p[] = {151,160,137,91,90,15, + 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, + 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, + 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, + 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, + 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, + 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, + 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, + 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, + 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, + 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, + 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, + 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180}; + // To remove the need for index wrapping, double the permutation table length + private static short perm[] = new short[512]; + private static short permMod12[] = new short[512]; + static { + for(int i=0; i<512; i++) + { + perm[i]=p[i & 255]; + permMod12[i] = (short)(perm[i] % 12); + } + } + + // Skewing and unskewing factors for 2, 3, and 4 dimensions + private static final double F2 = 0.5*(Math.sqrt(3.0)-1.0); + private static final double G2 = (3.0-Math.sqrt(3.0))/6.0; + private static final double F3 = 1.0/3.0; + private static final double G3 = 1.0/6.0; + private static final double F4 = (Math.sqrt(5.0)-1.0)/4.0; + private static final double G4 = (5.0-Math.sqrt(5.0))/20.0; + + // This method is a *lot* faster than using (int)Math.floor(x) + private static int fastfloor(double x) { + int xi = (int)x; + return xy0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1) + else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1) + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + double y1 = y0 - j1 + G2; + double x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords + double y2 = y0 - 1.0 + 2.0 * G2; + // Work out the hashed gradient indices of the three simplex corners + int ii = i & 255; + int jj = j & 255; + int gi0 = permMod12[ii+perm[jj]]; + int gi1 = permMod12[ii+i1+perm[jj+j1]]; + int gi2 = permMod12[ii+1+perm[jj+1]]; + // Calculate the contribution from the three corners + double t0 = 0.5 - x0*x0-y0*y0; + if(t0<0) n0 = 0.0; + else { + t0 *= t0; + n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient + } + double t1 = 0.5 - x1*x1-y1*y1; + if(t1<0) n1 = 0.0; + else { + t1 *= t1; + n1 = t1 * t1 * dot(grad3[gi1], x1, y1); + } + double t2 = 0.5 - x2*x2-y2*y2; + if(t2<0) n2 = 0.0; + else { + t2 *= t2; + n2 = t2 * t2 * dot(grad3[gi2], x2, y2); + } + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 70.0 * (n0 + n1 + n2); + } + + + // 3D simplex noise + public static double noise(double xin, double yin, double zin) { + double n0, n1, n2, n3; // Noise contributions from the four corners + // Skew the input space to determine which simplex cell we're in + double s = (xin+yin+zin)*F3; // Very nice and simple skew factor for 3D + int i = fastfloor(xin+s); + int j = fastfloor(yin+s); + int k = fastfloor(zin+s); + double t = (i+j+k)*G3; + double X0 = i-t; // Unskew the cell origin back to (x,y,z) space + double Y0 = j-t; + double Z0 = k-t; + double x0 = xin-X0; // The x,y,z distances from the cell origin + double y0 = yin-Y0; + double z0 = zin-Z0; + // For the 3D case, the simplex shape is a slightly irregular tetrahedron. + // Determine which simplex we are in. + int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords + int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords + if(x0>=y0) { + if(y0>=z0) + { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order + else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order + else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order + } + else { // x0 y0) rankx++; else ranky++; + if(x0 > z0) rankx++; else rankz++; + if(x0 > w0) rankx++; else rankw++; + if(y0 > z0) ranky++; else rankz++; + if(y0 > w0) ranky++; else rankw++; + if(z0 > w0) rankz++; else rankw++; + int i1, j1, k1, l1; // The integer offsets for the second simplex corner + int i2, j2, k2, l2; // The integer offsets for the third simplex corner + int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner + // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. + // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0; + j1 = ranky >= 3 ? 1 : 0; + k1 = rankz >= 3 ? 1 : 0; + l1 = rankw >= 3 ? 1 : 0; + // Rank 2 denotes the second largest coordinate. + i2 = rankx >= 2 ? 1 : 0; + j2 = ranky >= 2 ? 1 : 0; + k2 = rankz >= 2 ? 1 : 0; + l2 = rankw >= 2 ? 1 : 0; + // Rank 1 denotes the second smallest coordinate. + i3 = rankx >= 1 ? 1 : 0; + j3 = ranky >= 1 ? 1 : 0; + k3 = rankz >= 1 ? 1 : 0; + l3 = rankw >= 1 ? 1 : 0; + // The fifth corner has all coordinate offsets = 1, so no need to compute that. + double x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords + double y1 = y0 - j1 + G4; + double z1 = z0 - k1 + G4; + double w1 = w0 - l1 + G4; + double x2 = x0 - i2 + 2.0*G4; // Offsets for third corner in (x,y,z,w) coords + double y2 = y0 - j2 + 2.0*G4; + double z2 = z0 - k2 + 2.0*G4; + double w2 = w0 - l2 + 2.0*G4; + double x3 = x0 - i3 + 3.0*G4; // Offsets for fourth corner in (x,y,z,w) coords + double y3 = y0 - j3 + 3.0*G4; + double z3 = z0 - k3 + 3.0*G4; + double w3 = w0 - l3 + 3.0*G4; + double x4 = x0 - 1.0 + 4.0*G4; // Offsets for last corner in (x,y,z,w) coords + double y4 = y0 - 1.0 + 4.0*G4; + double z4 = z0 - 1.0 + 4.0*G4; + double w4 = w0 - 1.0 + 4.0*G4; + // Work out the hashed gradient indices of the five simplex corners + int ii = i & 255; + int jj = j & 255; + int kk = k & 255; + int ll = l & 255; + int gi0 = perm[ii+perm[jj+perm[kk+perm[ll]]]] % 32; + int gi1 = perm[ii+i1+perm[jj+j1+perm[kk+k1+perm[ll+l1]]]] % 32; + int gi2 = perm[ii+i2+perm[jj+j2+perm[kk+k2+perm[ll+l2]]]] % 32; + int gi3 = perm[ii+i3+perm[jj+j3+perm[kk+k3+perm[ll+l3]]]] % 32; + int gi4 = perm[ii+1+perm[jj+1+perm[kk+1+perm[ll+1]]]] % 32; + // Calculate the contribution from the five corners + double t0 = 0.6 - x0*x0 - y0*y0 - z0*z0 - w0*w0; + if(t0<0) n0 = 0.0; + else { + t0 *= t0; + n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0); + } + double t1 = 0.6 - x1*x1 - y1*y1 - z1*z1 - w1*w1; + if(t1<0) n1 = 0.0; + else { + t1 *= t1; + n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1); + } + double t2 = 0.6 - x2*x2 - y2*y2 - z2*z2 - w2*w2; + if(t2<0) n2 = 0.0; + else { + t2 *= t2; + n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2); + } + double t3 = 0.6 - x3*x3 - y3*y3 - z3*z3 - w3*w3; + if(t3<0) n3 = 0.0; + else { + t3 *= t3; + n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3); + } + double t4 = 0.6 - x4*x4 - y4*y4 - z4*z4 - w4*w4; + if(t4<0) n4 = 0.0; + else { + t4 *= t4; + n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4); + } + // Sum up and scale the result to cover the range [-1,1] + return 27.0 * (n0 + n1 + n2 + n3 + n4); + } + + // Inner class to speed upp gradient computations + // (array access is a lot slower than member access) + private static class Grad + { + double x, y, z, w; + + Grad(double x, double y, double z) + { + this.x = x; + this.y = y; + this.z = z; + } + + Grad(double x, double y, double z, double w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + } +} diff --git a/core/src/com/cpa/project/Utils/TileSplitter.java b/core/src/com/cpa/project/Utils/TileSplitter.java deleted file mode 100644 index 6410cd7..0000000 --- a/core/src/com/cpa/project/Utils/TileSplitter.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.cpa.project.Utils; - -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.TextureRegion; - -public class TileSplitter { - - private String tileset = "grassland_tiles.png"; - TextureRegion[][] tiles; - private TextureRegion[][] splitTiles(Texture tileset, int tileWidth, int tileHeight) { - TextureRegion[][] tiles = new TextureRegion[tileset.getWidth() / tileWidth][tileset.getHeight() / tileHeight]; - - for (int y = 0; y < tileset.getHeight() / tileHeight; y++) { - for (int x = 0; x < tileset.getWidth() / tileWidth; x++) { - tiles[x][y] = new TextureRegion(tileset, x * tileWidth, y * tileHeight, tileWidth, tileHeight); - } - } - - return tiles; - } - - public TextureRegion[][] getTiles(Texture tileset, int tileWidth, int tileHeight) { - return splitTiles(tileset, tileWidth, tileHeight); - } - - public TileSplitter() { - tiles = getTiles(new Texture(tileset), 32, 32); - } - - - - -} diff --git a/core/src/com/cpa/project/World/Map.java b/core/src/com/cpa/project/World/Map.java index 09f9fe2..1b17329 100644 --- a/core/src/com/cpa/project/World/Map.java +++ b/core/src/com/cpa/project/World/Map.java @@ -1,90 +1,124 @@ package com.cpa.project.World; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.Colors; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; -import com.badlogic.gdx.maps.tiled.TiledMapTile; +import com.badlogic.gdx.maps.tiled.TiledMap; +import com.badlogic.gdx.maps.tiled.TiledMapRenderer; +import com.badlogic.gdx.maps.tiled.TiledMapTileLayer; +import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer; import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.math.Vector3; import com.cpa.project.Camera.OrthographicCamera; +import com.cpa.project.Tiles.Tile; +import com.cpa.project.Tiles.terrainFloorTiles; +import com.cpa.project.World.procGen.NoiseProceduralGen; +import com.cpa.project.World.procGen.WFC; -public class Map { - - final static int mapSize = 4 ; - final static int tileSize = 64; +import java.util.ArrayList; - Color color; - TiledMapTile[][] tiles; - - Vector2 position; - Color [] [] colors ; +public class Map { + private final static int tileSize = 48; // Size of a tile in pixels + private final static int chunkSize = 64; // Size of a chunk in tiles + + public static TiledMap tiledMap; + private OrthographicCamera camera; + private SpriteBatch batch; + + private Tile[][] sandCenterWGrass; + + private terrainFloorTiles terrainFloorTiles; + private static OrthogonalTiledMapRenderer renderer; + + private WFC wfc; + private ArrayList WFCoutput; + BitmapFont font; + + NoiseProceduralGen noiseProceduralGen; + Tile[][] outputNoiseMap; + + public Map(SpriteBatch batch, OrthographicCamera camera , Vector2 playerPos) { + this.batch = batch; + this.camera = camera; + this.terrainFloorTiles = new terrainFloorTiles(); + this.sandCenterWGrass = terrainFloorTiles.getTileBox(0); // Get sand tile box + this.tiledMap = new TiledMap(); + this.renderer = new OrthogonalTiledMapRenderer(tiledMap); +// this.wfc = new WFC( 48, 48); +// this.WFCoutput = wfc.getOutputTiles(); + this.font = new BitmapFont(); + this.noiseProceduralGen = new NoiseProceduralGen(5, 1448, 1448); + this.noiseProceduralGen.generateMap(); + this.outputNoiseMap = noiseProceduralGen.getMap(); + addNoiseMapToTiledMap(playerPos); + } + // take the output of the WFC algorithm and add it to the tiled map for rendering + // it's a terrain so add it to the terrain layer + public void addWFCtoTiledMap() { + TiledMapTileLayer terrainLayer = new TiledMapTileLayer(wfc.getWidth(), wfc.getHeight(), tileSize, tileSize); - public Map(Color color , Vector2 position) { - this.color = color; - this.tiles = new TiledMapTile[mapSize][mapSize]; - this.colors = new Color[mapSize][mapSize]; - // fill colors with random colors - for (int i = 0; i < mapSize; i++) { - for (int j = 0; j < mapSize; j++) { - colors[i][j] = new Color((float)Math.random(), (float)Math.random(), (float)Math.random(), 1); - } + // declare a new layer for the objects (e.g. trees, rocks, etc.) and add it to the tiled map + TiledMapTileLayer objectLayer = new TiledMapTileLayer(wfc.getWidth(), wfc.getHeight(), tileSize, tileSize); + for (int i = 0; i < WFCoutput.size(); i++) { + Tile tile = WFCoutput.get(i); + terrainLayer.setCell((int) tile.getPosition().x / tileSize, (int) tile.getPosition().y / tileSize, new TiledMapTileLayer.Cell().setTile(tile)); } - this.position = position; + tiledMap.getLayers().add(terrainLayer); + tiledMap.getLayers().add(objectLayer); } - public void render(ShapeRenderer shapeRenderer , OrthographicCamera camera) { - shapeRenderer.begin(ShapeRenderer.ShapeType.Filled); - shapeRenderer.setColor(color); - - // calculate the offset to position the map in world-space - float offsetX = -(mapSize * tileSize) / 2f + position.x; - float offsetY = -(mapSize * tileSize) / 2 + position.y; - - for (int i = 0; i < mapSize; i++) { - for (int j = 0; j < mapSize; j++) { -// shapeRenderer.setColor(colors[i][j]); - // draw the tile - shapeRenderer.rect(offsetX + i * tileSize, offsetY + j * tileSize, tileSize, tileSize); + // take the output of the noise procedural generation and add it to the tiled map for rendering + // it's a terrain so add it to the terrain layer + public void addNoiseMapToTiledMap(Vector2 playerposition) { + TiledMapTileLayer terrainLayer = new TiledMapTileLayer(noiseProceduralGen.getWidth(), noiseProceduralGen.getHeight(), tileSize, tileSize); + + // declare a new layer for the objects (e.g. trees, rocks, etc.) and add it to the tiled map + TiledMapTileLayer objectLayer = new TiledMapTileLayer(noiseProceduralGen.getWidth(), noiseProceduralGen.getHeight(), tileSize, tileSize); + for (int i = 0; i < outputNoiseMap.length; i++) { + for (int j = 0; j < outputNoiseMap[i].length; j++) { + Tile tile = outputNoiseMap[i][j]; + + // i need the tiles to be positioned relative to the player ( player needs to spawn in the middle of the map to not encounter edges ) + int poxX = (int) playerposition.x - (outputNoiseMap.length / 2) * tileSize; + int poxY = (int) playerposition.y - (outputNoiseMap[i].length / 2) * tileSize; + tile.setPosition(new Vector2(poxX + i * tileSize, poxY + j * tileSize)); + terrainLayer.setCell(i, j, new TiledMapTileLayer.Cell().setTile(tile)); } } - shapeRenderer.end(); + tiledMap.getLayers().add(terrainLayer); + tiledMap.getLayers().add(objectLayer); } - public void update(float dt , ShapeRenderer shapeRenderer , OrthographicCamera camera) { - render(shapeRenderer , camera); - } - public Vector2 getPosition() { - return position; - } - public void setPosition(Vector2 position) { - this.position = position; - } + public void render( Vector2 playerPos ) { + // Render the WFC output tiles + renderer.setView(camera); + renderer.render(); - public int getMapSize() { - return mapSize; - } + // access the terrain layer + TiledMapTileLayer terrainLayer = (TiledMapTileLayer) tiledMap.getLayers().get(0); + + // access the tile at the player's position + TiledMapTileLayer.Cell cell = terrainLayer.getCell((int) playerPos.x / tileSize, (int) playerPos.y / tileSize); + Tile tile = (Tile) cell.getTile(); + System.out.println("Tile at player position: " + tile.isReachable()); - public int getTileSize() { - return tileSize; } - public boolean isPlayerInMap(Vector2 playerPos) { - float mapStartX = position.x - (mapSize * tileSize) / 2f; - float mapEndX = position.x + (mapSize * tileSize) / 2f; - float mapStartY = position.y - (mapSize * tileSize) / 2f; - float mapEndY = position.y + (mapSize * tileSize) / 2f; - return playerPos.x >= mapStartX && playerPos.x <= mapEndX - && playerPos.y >= mapStartY && playerPos.y <= mapEndY; + public void update(float dt, SpriteBatch batch, ShapeRenderer shapeRenderer, OrthographicCamera camera, TiledMapRenderer tiledMapRenderer) { + this.camera = camera; + this.batch = batch; +// this.render(); } - public void dispose() { - // dispose of the tiles + public int getWidth() { + return this.noiseProceduralGen.getWidth(); + } + public int getHeight() { + return this.noiseProceduralGen.getHeight(); } -} +} \ No newline at end of file diff --git a/core/src/com/cpa/project/World/MapManager.java b/core/src/com/cpa/project/World/MapManager.java deleted file mode 100644 index d7160f1..0000000 --- a/core/src/com/cpa/project/World/MapManager.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.cpa.project.World; - -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.g2d.Batch; -import com.badlogic.gdx.graphics.g2d.BitmapFont; -import com.badlogic.gdx.graphics.g2d.SpriteBatch; -import com.badlogic.gdx.graphics.glutils.ShapeRenderer; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.math.Vector3; -import com.cpa.project.Camera.OrthographicCamera; -import com.sun.tools.javac.util.Pair; - -import java.util.HashMap; -import java.util.ArrayList; -import java.util.List; - - - -public class MapManager { - - final static int[][] DIRECTIONS = { - {-1, 1}, {0, 1}, {1, 1}, - {-1, 0}, {1, 0}, - {-1,-1}, {0,-1}, {1,-1} - }; - - int renderDistance; - Vector2 playerMapPos; - HashMap maps; - OrthographicCamera camera; - BitmapFont font; - - Map currentMap; - - public MapManager(OrthographicCamera camera, int renderDistance) { - this.camera = camera; - this.renderDistance = renderDistance; - this.playerMapPos = new Vector2(0, 0); - this.maps = new HashMap<>(); - this.font = new BitmapFont(); - this.currentMap = new Map(new Color((float)Math.random(), (float)Math.random(), (float)Math.random(), 1), - new Vector2(camera.position.x, camera.position.y)); - createSurroundingMaps((int) playerMapPos.x, (int) playerMapPos.y); - } - - public void update(float dt,SpriteBatch bacth, ShapeRenderer shapeRenderer, Vector2 playerPos) { - shapeRenderer.setProjectionMatrix(camera.combined); - currentMap.update(dt, shapeRenderer, camera); - for (Map map : maps.values()) { - map.update(dt, shapeRenderer, camera); - } - - // check if the player is in a new map and update the current map - for (Map map : maps.values()) { - if (map.isPlayerInMap(playerPos)) { - currentMap = map; - } - } - - // draw the current map - bacth.begin(); - font.draw(bacth, "Current Map", currentMap.getPosition().x, currentMap.getPosition().y); - - bacth.end(); - - Vector2 newPlayerMapPos = getMapPosForPlayer(playerPos); - if(!newPlayerMapPos.equals(playerMapPos)) { - playerMapPos = newPlayerMapPos; - updateMaps((int) playerMapPos.x, (int) playerMapPos.y); - } - } - - public void addMap(Vector2 position, Map map) { - maps.put(position, map); - } - - - - private Vector2 getMapPosForPlayer(Vector2 playerPos) { - return new Vector2((int) Math.floor(playerPos.x / (currentMap.getMapSize() * currentMap.getTileSize())), - (int) Math.floor(playerPos.y / (currentMap.getMapSize() * currentMap.getTileSize()))); - } - - private void createSurroundingMaps(int playerX, int playerY) { - // Calculate offsets to prioritize maps around the player ---- this here is kinda approximate - int offsetX = playerX - renderDistance; - int offsetY = playerY - renderDistance; - - for(int x = 0; x < renderDistance * 2 + 1; x++) { - for(int y = 0; y < renderDistance * 2 + 1; y++) { - int newX = offsetX + x; - int newY = offsetY + y; - Vector2 newPosition = new Vector2(newX, newY); - if(!maps.containsKey(newPosition)) { - createNewMapAt(newX, newY); - } - } - } - } - - - private void createNewMapAt(int x, int y) { - int mapSize = currentMap.getMapSize(); - int tileSize = currentMap.getTileSize(); - Vector2 mapPosition = new Vector2(x * mapSize * tileSize, y * mapSize * tileSize); - Map newMap = new Map(new Color((float)Math.random(), (float)Math.random(), (float)Math.random(), 1), mapPosition); - addMap(new Vector2(x, y), newMap); - } - - private void updateMaps(int centerX, int centerY) { - List positionsToRemove = new ArrayList<>(); - for(Vector2 position : maps.keySet()) { - if(Math.abs(centerX - position.x) > renderDistance || Math.abs(centerY - position.y) > renderDistance) { - positionsToRemove.add(position); - } - } - - for(Vector2 positionToRemove : positionsToRemove) { - Map mapToRemove = maps.remove(positionToRemove); - mapToRemove.dispose(); - } - - createSurroundingMaps(centerX, centerY); - - System.err.println("maps to remove : " + positionsToRemove.size()); - System.err.println("number of maps : " + maps.size()); - } - -} \ No newline at end of file diff --git a/core/src/com/cpa/project/World/World.java b/core/src/com/cpa/project/World/World.java index b7b704d..705bf06 100644 --- a/core/src/com/cpa/project/World/World.java +++ b/core/src/com/cpa/project/World/World.java @@ -25,7 +25,7 @@ public class World { float fireRate = 0.1f; - public World(Player player, Set entities, OrthographicCamera camera) { + public World(Player player, Set entities, OrthographicCamera camera ) { this.player = player; this.entities = entities; this.camera = camera; @@ -34,6 +34,7 @@ public World(Player player, Set entities, OrthographicCamera camera) { public void update(float dt) { // camera.update(dt); // TODO: DELEGATE THIS TO THE PLAYER CLASS OR SOMETHING + fireRate -= dt; if (fireRate <= 0) { Vector3 cursorPos3D = camera.unproject(new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0)); diff --git a/core/src/com/cpa/project/World/procGen/NoiseProceduralGen.java b/core/src/com/cpa/project/World/procGen/NoiseProceduralGen.java new file mode 100644 index 0000000..e09c045 --- /dev/null +++ b/core/src/com/cpa/project/World/procGen/NoiseProceduralGen.java @@ -0,0 +1,118 @@ +package com.cpa.project.World.procGen; + +import com.cpa.project.Tiles.Tile; +import com.badlogic.gdx.math.MathUtils; +import com.cpa.project.Tiles.TileType; +import com.cpa.project.Tiles.terrainFloorTiles; +import com.cpa.project.Utils.PerlinNoiseGenerator; +import com.cpa.project.Utils.SimplexNoise; + +public class NoiseProceduralGen { + private final int scale; // Scale of the noise map , octave count + private final int width; + private final int height; // Width and height of the map in tiles + private final Tile[][] map; // The map array that will hold the final tiles + + public NoiseProceduralGen( int scale, int width, int height) { + this.scale = scale; + this.width = width; + this.height = height; + this.map = new Tile[width][height]; + } + + public int getWidth() { + return this.width; + } + + public int getHeight() { + return this.height; + } + + public void generateMap() { + // Generate Perlin noise map with appropriate octave count + // The octave count can be adjusted to control detail level + float[][] noiseMap = PerlinNoiseGenerator.generatePerlinNoise(width, height, scale); + + // Map the noise values to terrain tiles + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + map[x][y] = chooseTileBasedOnNoise(noiseMap[x][y]); + +// double noiseValue = SimplexNoise.noise( scale * x / (double) width, scale * y / (double) height); +// // Normalize the value to be between 0 and 1 +// double normalizedValue = (noiseValue + 1) / 2; +// // Choose the tile based on the noise value +// map[x][y] = chooseTileBasedOnNoise((float) normalizedValue); + } + } + + // Apply transitions for tiles + applyTransitions(); + } + + + + private Tile chooseTileBasedOnNoise(float noiseValue) { + if (noiseValue<0.2){ + return terrainFloorTiles.waterTiles[1][1].clone(44); // Example center tile of water + } + else if (noiseValue < 0.3) { + return terrainFloorTiles.rockCenterWSand[1][1].clone(24); // Example center tile of rock + } else if (noiseValue < 0.6) { + return terrainFloorTiles.sandCenterWGrass[1][1].clone(14); // Example center tile of sand + } else if (noiseValue < 0.8) { + return terrainFloorTiles.grassCenterWSand[1][1].clone(4); // Example center tile of grass + }else{ + // choose randomly between flowerTiles + int random = MathUtils.random(0, terrainFloorTiles.flowerTiles.length - 1); + return terrainFloorTiles.flowerTiles[random][0].clone(54); // Example flower tile + } + } + + public void applyTransitions() { + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + map[x][y] = getTransitionTile(x, y); + } + } + } + + private Tile getTransitionTile(int x, int y) { + Tile centerTile = map[x][y]; + + // Get the main tile type for this position + TileType centerType = getTileType(x, y); + + // Identify the types of neighboring tiles + TileType topType = getTileType(x, y + 1); + TileType rightType = getTileType(x + 1, y); + TileType bottomType = getTileType(x, y - 1); + TileType leftType = getTileType(x - 1, y); + + // Check corners + TileType topLeftType = getTileType(x - 1, y + 1); + TileType topRightType = getTileType(x + 1, y + 1); + TileType bottomLeftType = getTileType(x - 1, y - 1); + TileType bottomRightType = getTileType(x + 1, y - 1); + + return centerTile.getTransition(topType, rightType, bottomType, leftType, topLeftType, topRightType, bottomLeftType, bottomRightType); + } + private TileType getTileType(int x, int y) { + // Check if the position is within bounds + if (x < 0 || x >= width || y < 0 || y >= height) { + return TileType.None; + } + TileType res = map[x][y].getTileType(); +// System.out.println("TileType : " + res); + return res; + } + + + // Accessor for the map for rendering or other purposes + public Tile[][] getMap() { + return map; + } + + +} + diff --git a/core/src/com/cpa/project/World/procGen/WFC.java b/core/src/com/cpa/project/World/procGen/WFC.java new file mode 100644 index 0000000..5868a08 --- /dev/null +++ b/core/src/com/cpa/project/World/procGen/WFC.java @@ -0,0 +1,811 @@ + +package com.cpa.project.World.procGen; + + +import com.badlogic.gdx.math.Vector2; +import com.cpa.project.Tiles.Tile; +import com.cpa.project.Tiles.terrainFloorTiles; + +import java.util.*; +import java.util.Map; + + +class WFCNode { + + String name; + Tile tile; + WFCConnection Top; // les connections du haut de la tile qui sont possibles + WFCConnection Bottom; + WFCConnection Left; + WFCConnection Right; + + double entropy; + + + public WFCNode(String name, Tile tile , double entropy ) { + this.name = name; + this.tile = tile; + this.entropy = entropy; + this.Top = new WFCConnection(); + this.Bottom = new WFCConnection(); + this.Left = new WFCConnection(); + this.Right = new WFCConnection(); + + } + + public WFCNode addTop(WFCNode node){ + Top.addNode(node); +// node.Bottom.addNode(this); + return this; + } + + public WFCNode addBottom(WFCNode node){ + Bottom.addNode(node); +// node.Top.addNode(this); + return this; + } + + public WFCNode addLeft(WFCNode node){ + Left.addNode(node); +// node.Right.addNode(this); + return this; + } + + public WFCNode addRight(WFCNode node){ + Right.addNode(node); +// node.Left.addNode(this); + return this; + } + + public void setEntropy(double entropy){ + this.entropy = entropy; + } + + public double getEntropy(){ + return this.entropy; + } + + +} + +class WFCConnection { + ArrayList compatibleNodes ; + public WFCConnection() { + compatibleNodes = new ArrayList<>(); + } + + public void addNode(WFCNode node){ + if (!compatibleNodes.contains(node)) + compatibleNodes.add(node); + } + + public void removeNode(WFCNode node){ + compatibleNodes.remove(node); + } + + // print the compatible nodes + public void printCompatibleNodes(){ + for (int i = 0; i < compatibleNodes.size(); i++) { + System.err.println(compatibleNodes.get(i).name); + } + } +} + +/** + * classe qui permet de gérer la map et les tiles de la map + * Algorithme de Wave Function Collapse : https://robertheaton.com/2018/12/17/wavefunction-collapse-algorithm/ + * ATTEMPT AT IMPLEMENTING WAVE FUNCTION COLLAPSE with tileset of 3x3 of multiple types ... + * HOURS WASTED ON THIS : 14 + * IT WORKS BUT IT'S NOT EFFICIENT SO I WILL NOT USE IT AND I WILL USE THE NOISE PROCEDURAL GENERATION INSTEAD ... + */ +public class WFC { + // the Size of the map : width and height + private int width,Height; + + // a 2D array of WFCNode that will store our collapsed tiles so we can reference them later + private WFCNode[][] map; + + // the list of all the nodes that we have + public ArrayList nodes = new ArrayList<>(); + + // the list of all the tiles that we will output + ArrayList outputTiles = new ArrayList<>(); + + // a list of the vector2 that we will use to collapse the tiles + private ArrayList toCollapse = new ArrayList<>(); + + WFCNode[][][] chunks; + + // an array of the offsets that we will use to get the neighbors of a tile + private Vector2[] offsets = new Vector2[]{ + new Vector2(0,1), // top + new Vector2(0,-1), // bottom + new Vector2(1,0), // right + new Vector2(-1,0) // left +// ,new Vector2(1,1), // top right +// new Vector2(-1,1), // top left +// new Vector2(1,-1), // bottom right +// new Vector2(-1,-1) // bottom left + }; + + terrainFloorTiles terrainFloorTiles; + + Tile[][] sandCenterWGrass; + Tile[][] grassCenterWSand; + Tile[][] rockCenterWSand; + Tile[][] voidCenterWRock; + Tile[][] waterTiles; + + + WFCNode centerStart ; + + int chunkSize = 16; + + // the constructor of the WFC + public WFC(int width, int height) { + this.width = width; + this.Height = height; + this.map = new WFCNode[width][height]; + this.terrainFloorTiles = new terrainFloorTiles(); + this.sandCenterWGrass = terrainFloorTiles.getTileBox(0); + this.grassCenterWSand = terrainFloorTiles.getTileBox(10); + this.rockCenterWSand = terrainFloorTiles.getTileBox(20); + this.voidCenterWRock = terrainFloorTiles.getTileBox(30); + this.waterTiles = terrainFloorTiles.getTileBox(40); + + initNodes(); + collapseWorld(); + + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return Height; + } + + // this function will initialize the nodes that we have in the world + // and the connections between them + private void initNodes() { + // we will start with sandCenterWGrass + double outerEntropy = 3.5; + double innerEntropy =28.1; + double edgeEntropy = 1.14; + + Map allNodes = new HashMap<>(); + allNodes.put(0, new WFCNode("sandCenterWGrass[0][0]", sandCenterWGrass[0][0] , edgeEntropy)); + allNodes.put(1, new WFCNode("sandCenterWGrass[0][1]", sandCenterWGrass[0][1] , outerEntropy)); + allNodes.put(2, new WFCNode("sandCenterWGrass[0][2]", sandCenterWGrass[0][2] , edgeEntropy)); + allNodes.put(3, new WFCNode("sandCenterWGrass[1][0]", sandCenterWGrass[1][0] , outerEntropy)); + allNodes.put(4, new WFCNode("sandCenterWGrass[1][1]", sandCenterWGrass[1][1] , innerEntropy)); + allNodes.put(5, new WFCNode("sandCenterWGrass[1][2]", sandCenterWGrass[1][2] , outerEntropy)); + allNodes.put(6, new WFCNode("sandCenterWGrass[2][0]", sandCenterWGrass[2][0] , edgeEntropy)); + allNodes.put(7, new WFCNode("sandCenterWGrass[2][1]", sandCenterWGrass[2][1] , outerEntropy)); + allNodes.put(8, new WFCNode("sandCenterWGrass[2][2]", sandCenterWGrass[2][2] , edgeEntropy)); + + innerEntropy = 22.9; + edgeEntropy = 1.14; + outerEntropy = 3.5; + allNodes.put(10, new WFCNode("grassCenterWSand[0][0]", grassCenterWSand[0][0] , edgeEntropy)); + allNodes.put(11, new WFCNode("grassCenterWSand[0][1]", grassCenterWSand[0][1] , outerEntropy)); + allNodes.put(12, new WFCNode("grassCenterWSand[0][2]", grassCenterWSand[0][2] , edgeEntropy)); + allNodes.put(13, new WFCNode("grassCenterWSand[1][0]", grassCenterWSand[1][0] , outerEntropy)); + allNodes.put(14, new WFCNode("grassCenterWSand[1][1]", grassCenterWSand[1][1] , innerEntropy)); + allNodes.put(15, new WFCNode("grassCenterWSand[1][2]", grassCenterWSand[1][2] , outerEntropy)); + allNodes.put(16, new WFCNode("grassCenterWSand[2][0]", grassCenterWSand[2][0] , edgeEntropy)); + allNodes.put(17, new WFCNode("grassCenterWSand[2][1]", grassCenterWSand[2][1] , outerEntropy)); + allNodes.put(18, new WFCNode("grassCenterWSand[2][2]", grassCenterWSand[2][2] , edgeEntropy)); + + innerEntropy = 9.1; + edgeEntropy = 0.95; + outerEntropy = 0.5; + allNodes.put(20, new WFCNode("rockCenterWSand[0][0]", rockCenterWSand[0][0] , edgeEntropy)); + allNodes.put(21, new WFCNode("rockCenterWSand[0][1]", rockCenterWSand[0][1] , outerEntropy)); + allNodes.put(22, new WFCNode("rockCenterWSand[0][2]", rockCenterWSand[0][2] , edgeEntropy)); + allNodes.put(23, new WFCNode("rockCenterWSand[1][0]", rockCenterWSand[1][0] , outerEntropy)); + allNodes.put(24, new WFCNode("rockCenterWSand[1][1]", rockCenterWSand[1][1] , innerEntropy)); + allNodes.put(25, new WFCNode("rockCenterWSand[1][2]", rockCenterWSand[1][2] , outerEntropy)); + allNodes.put(26, new WFCNode("rockCenterWSand[2][0]", rockCenterWSand[2][0] , edgeEntropy)); + allNodes.put(27, new WFCNode("rockCenterWSand[2][1]", rockCenterWSand[2][1] , outerEntropy)); + allNodes.put(28, new WFCNode("rockCenterWSand[2][2]", rockCenterWSand[2][2] , edgeEntropy)); + + + innerEntropy = 0.1; + edgeEntropy = 0.1; + outerEntropy = 0.1; + allNodes.put(30, new WFCNode("voidCenterWRock[0][0]", voidCenterWRock[0][0] , edgeEntropy)); + allNodes.put(31, new WFCNode("voidCenterWRock[0][1]", voidCenterWRock[0][1] , outerEntropy)); + allNodes.put(32, new WFCNode("voidCenterWRock[0][2]", voidCenterWRock[0][2] , edgeEntropy)); + allNodes.put(33, new WFCNode("voidCenterWRock[1][0]", voidCenterWRock[1][0] , outerEntropy)); + allNodes.put(34, new WFCNode("voidCenterWRock[1][1]", rockCenterWSand[1][1] , innerEntropy)); + allNodes.put(35, new WFCNode("voidCenterWRock[1][2]", voidCenterWRock[1][2] , outerEntropy)); + allNodes.put(36, new WFCNode("voidCenterWRock[2][0]", voidCenterWRock[2][0] , edgeEntropy)); + allNodes.put(37, new WFCNode("voidCenterWRock[2][1]", voidCenterWRock[2][1] , outerEntropy)); + allNodes.put(38, new WFCNode("voidCenterWRock[2][2]", voidCenterWRock[2][2] , edgeEntropy)); +// +// allNodes.put(40, new WFCNode("waterTiles[0][0]", waterTiles[0][0] , 0.1)); +// allNodes.put(41, new WFCNode("waterTiles[0][1]", waterTiles[0][1] , 0.1)); +// allNodes.put(42, new WFCNode("waterTiles[0][2]", waterTiles[0][2] , 0.1)); +// allNodes.put(43, new WFCNode("waterTiles[1][0]", waterTiles[1][0] , 0.1)); +// allNodes.put(44, new WFCNode("waterTiles[1][1]", waterTiles[1][1] , 1.4)); +// allNodes.put(45, new WFCNode("waterTiles[1][2]", waterTiles[1][2] , 0.1)); +// allNodes.put(46, new WFCNode("waterTiles[2][0]", waterTiles[2][0] , 0.1)); +// allNodes.put(47, new WFCNode("waterTiles[2][1]", waterTiles[2][1] , 0.1)); +// allNodes.put(48, new WFCNode("waterTiles[2][2]", waterTiles[2][2] , 0.1)); + + + + + + + // we add the nodes to the nodes list + nodes.addAll(allNodes.values()); + + centerStart = allNodes.get(4); + + + setConnections(allNodes, 0); + setConnections(allNodes, 10); + setConnections(allNodes, 20); + setConnections(allNodes, 30); +// setConnections(allNodes, 40); + +// we set the outer connections + setOuterConnections(allNodes, 0, 10); + setOuterConnections(allNodes , 10, 0); + setOuterConnections(allNodes, 10, 20); + setOuterConnections(allNodes, 20, 10); + setOuterConnections(allNodes, 20, 30); + setOuterConnections(allNodes, 30, 20); +// setOuterConnections(allNodes, 40, 20); +// setOuterConnections(allNodes, 40, 0); +// setOuterConnections(allNodes, 0, 40); +// setOuterConnections(allNodes, 10, 40); +// setOuterConnections(allNodes, 40, 10); +// setOuterConnections(allNodes, 20, 40); +// setOuterConnections(allNodes, 40, 30); + + System.err.println("Top" ) ; + allNodes.get(4).Top.printCompatibleNodes(); + System.err.println("Bottom" ) ; + allNodes.get(4).Bottom.printCompatibleNodes(); + System.err.println("Left" ) ; + allNodes.get(4).Left.printCompatibleNodes(); + System.err.println("Right" ) ; + allNodes.get(4).Right.printCompatibleNodes(); + + } + + void setConnections(Map allNodes, int baseIndex) { + allNodes.get(baseIndex+4).addRight(allNodes.get(baseIndex+4)) + .addBottom(allNodes.get(baseIndex+4)).addLeft(allNodes.get(baseIndex+4)) + .addTop(allNodes.get(baseIndex+4)); + + allNodes.get(baseIndex).addRight(allNodes.get(baseIndex + 1)). + addBottom(allNodes.get(baseIndex + 3)); + allNodes.get(baseIndex + 1).addLeft(allNodes.get(baseIndex)). + addRight(allNodes.get(baseIndex + 2)). + addBottom(allNodes.get(baseIndex + 4)) +// .addLeft(allNodes.get(baseIndex + 1)).addRight(allNodes.get(baseIndex + 1)) + ; + allNodes.get(baseIndex + 2).addLeft(allNodes.get(baseIndex + 1)). + addBottom(allNodes.get(baseIndex + 5)); + + allNodes.get(baseIndex + 3).addTop(allNodes.get(baseIndex)). + addRight(allNodes.get(baseIndex + 4)). + addBottom(allNodes.get(baseIndex + 6)) +// .addTop(allNodes.get(baseIndex + 3)).addBottom(allNodes.get(baseIndex + 3)) + ; + allNodes.get(baseIndex + 4).addTop(allNodes.get(baseIndex + 1)) + .addLeft(allNodes.get(baseIndex + 3)) + .addRight(allNodes.get(baseIndex + 5)) + .addBottom(allNodes.get(baseIndex + 7)); + allNodes.get(baseIndex + 5).addTop(allNodes.get(baseIndex + 2)) + .addLeft(allNodes.get(baseIndex + 4)) + .addBottom(allNodes.get(baseIndex + 8)) +// .addTop(allNodes.get(baseIndex + 5)).addBottom(allNodes.get(baseIndex + 5)) + ; + + allNodes.get(baseIndex + 6).addTop(allNodes.get(baseIndex + 3)) + .addRight(allNodes.get(baseIndex + 7)); + allNodes.get(baseIndex + 7).addTop(allNodes.get(baseIndex + 4)) + .addLeft(allNodes.get(baseIndex + 6)) + .addRight(allNodes.get(baseIndex + 8)) +// .addRight(allNodes.get(baseIndex + 7)).addLeft(allNodes.get(baseIndex + 7)) + ; + allNodes.get(baseIndex + 8).addTop(allNodes.get(baseIndex + 5)) + .addLeft(allNodes.get(baseIndex + 7)); + + } + + + public void setOuterConnections(Map allNodes, int baseIndex1, int baseIndex2) { +// // top left + allNodes.get(baseIndex1).addLeft(allNodes.get(baseIndex2+4)).addTop(allNodes.get(baseIndex2+4)) + .addTop(allNodes.get(baseIndex2+1)).addLeft(allNodes.get(baseIndex2+3)) + ; + + // top right + allNodes.get(baseIndex1+2).addRight(allNodes.get(baseIndex2+4)).addTop(allNodes.get(baseIndex2+4)) + .addTop(allNodes.get(baseIndex2+1)).addRight(allNodes.get(baseIndex2+5)) + ; + + // top + allNodes.get(baseIndex1+1).addTop(allNodes.get(baseIndex2+4)) + .addTop(allNodes.get(baseIndex2+1)) + ; + + // bottom left + allNodes.get(baseIndex1+6).addLeft(allNodes.get(baseIndex2+4)).addBottom(allNodes.get(baseIndex2+4)) + .addBottom(allNodes.get(baseIndex2+7)).addLeft(allNodes.get(baseIndex2+3)) + ; + + // bottom right + allNodes.get(baseIndex1+8).addRight(allNodes.get(baseIndex2+4)).addBottom(allNodes.get(baseIndex2+4)) + .addBottom(allNodes.get(baseIndex2+7)) + .addRight(allNodes.get(baseIndex2+5)) + ; + + // bottom + allNodes.get(baseIndex1+7).addBottom(allNodes.get(baseIndex2+4)) + .addBottom(allNodes.get(baseIndex2+7)) + ; + + // left + allNodes.get(baseIndex1+3).addLeft(allNodes.get(baseIndex2+4)) + .addLeft(allNodes.get(baseIndex2+3)) + ; + + // right + allNodes.get(baseIndex1+5).addRight(allNodes.get(baseIndex2+4)) + .addRight(allNodes.get(baseIndex2+5)) + ; + + } + + // divide the world into chunks and initialize the chunks arrays + private WFCNode[][][] divideIntoChunks() { + int chunksX = width / chunkSize; + int chunksY = Height / chunkSize; + WFCNode[][][] chunks = new WFCNode[chunksX][chunksY][]; + + for (int cx = 0; cx < chunksX; cx++) { + for (int cy = 0; cy < chunksY; cy++) { + chunks[cx][cy] = new WFCNode[chunkSize * chunkSize]; + // Initialize with nulls + Arrays.fill(chunks[cx][cy], null); + } + } + return chunks; + } + + private void collapseChunk(WFCNode[] chunk, int chunkX, int chunkY) { + // Here we process the 1D array as if it was 2D by calculating indices + + for (int i = 0; i < chunkSize; i++) { + for (int j = 0; j < chunkSize; j++) { + + boolean chunkDone = false; +// do { + + int globalX = chunkX * chunkSize + i; + int globalY = chunkY * chunkSize + j; + ArrayList potentialNodes = getPotentialNodes(globalX, globalY); + + if (!potentialNodes.isEmpty()) { + WFCNode selectedNode = getRandomNode(potentialNodes); + int indexInChunk = i * chunkSize + j; + chunk[indexInChunk] = selectedNode; + map[globalX][globalY] = selectedNode; // Update the global map + }else { + // If no potential nodes are found, it means ther is a configuration issue. + // Incrementally expand the recalcZone and try again. + checkAndResolveBoundaries(globalX, globalY); + } +// } while (!chunkDone); + } + } + } + + + // check a potential empty tile and resolve the boundaries + private void checkAndResolveBoundaries(int emptyTileX, int emptyTileY) { + Zone recalcZone = calculateRecalculationZone(emptyTileX, emptyTileY); + + boolean isValid; + do { + for (int x = recalcZone.startX; x <= recalcZone.endX; x++) { + for (int y = recalcZone.startY; y <= recalcZone.endY; y++) { + if (map[x][y] == null) { + ArrayList potentialNodes = getPotentialNodesConsideringAllNeighbors(x, y); + if (!potentialNodes.isEmpty()) { + map[x][y] = getRandomNode(potentialNodes); + } + } + } + } + isValid = validateRecalculationZone(recalcZone); + if (!isValid) { + recalcZone = expandRecalculationZone(recalcZone); + } + } while (!isValid && (recalcZone.startX > 0 || recalcZone.startY > 0 || recalcZone.endX < width - 1 || recalcZone.endY < Height - 1)); + + if (!isValid) { + System.err.println("Failed to resolve with maximum allowable zone."); + } + } + + // Get a list of potential nodes for a given position + private ArrayList getPotentialNodesConsideringAllNeighbors(int x, int y) { + ArrayList potentialNodes = new ArrayList<>(nodes); // Start with all possible nodes + + // Iterate over each direction to check the neighbors + for (Vector2 offset : offsets) { + int neighborX = x + (int) offset.x; + int neighborY = y + (int) offset.y; + + // Ensure the neighbor is within grid boundaries + if (IsInGrid(new Vector2(neighborX, neighborY))) { + WFCNode neighborNode = map[neighborX][neighborY]; + + if (neighborNode != null) { + // Reduce potential nodes based on the neighbor's compatible nodes + filterPotentialNodes(potentialNodes, neighborNode, offset); + } + } + } + + return potentialNodes; + } + + // filter potential nodes based on compatibility with a given neighbor + private void filterPotentialNodes(ArrayList potentialNodes, WFCNode neighborNode, Vector2 offset) { + potentialNodes.removeIf(node -> !isCompatible(node, neighborNode, offset)); + } + + // Determines if two nodes are compatible based on their position relative to each other + private boolean isCompatible(WFCNode node, WFCNode neighborNode, Vector2 offset) { + if (offset.x == 0 && offset.y == 1) // Top neighbor + return node.Top.compatibleNodes.contains(neighborNode); + else if (offset.x == 0 && offset.y == -1) // Bottom neighbor + return node.Bottom.compatibleNodes.contains(neighborNode); + else if (offset.x == 1 && offset.y == 0) // Right neighbor + return node.Right.compatibleNodes.contains(neighborNode); + else if (offset.x == -1 && offset.y == 0) // Left neighbor + return node.Left.compatibleNodes.contains(neighborNode); + + return false; + } + + + // get the zone around the empty tile that needs to be recalculated + private Zone calculateRecalculationZone(int emptyTileX, int emptyTileY) { + // Start by defining a basic zone around the empty tile + int startX = Math.max(0, emptyTileX - 1); + int startY = Math.max(0, emptyTileY - 1); + int endX = Math.min(width - 1, emptyTileX + 1); + int endY = Math.min(Height - 1, emptyTileY + 1); + + // Expand the zone until it encounters tiles or reaches the grid boundaries + while (startX > 0 && isRowEmpty(startX - 1, startY, endY)) --startX; + while (endX < width - 1 && isRowEmpty(endX + 1, startY, endY)) ++endX; + while (startY > 0 && isColumnEmpty(startY - 1, startX, endX)) --startY; + while (endY < Height - 1 && isColumnEmpty(endY + 1, startX, endX)) ++endY; + + return new Zone(startX, startY, endX, endY); + } + + // Checks if a row is entirely empty within a specified range + private boolean isRowEmpty(int row, int startCol, int endCol) { + for (int i = startCol; i <= endCol; i++) { + if (map[row][i] != null) return false; + } + return true; + } + + // Checks if a column is entirely empty within a specified range + private boolean isColumnEmpty(int col, int startRow, int endRow) { + for (int i = startRow; i <= endRow; i++) { + if (map[i][col] != null) return false; + } + return true; + } + + + // returns an expanded zone for recalculation + private Zone expandRecalculationZone(Zone zone) { + // Check if expansion is possible or makes sense + if (zone.startX == 0 && zone.startY == 0 && zone.endX == width - 1 && zone.endY == Height - 1) { + // Already at maximum size, no further expansion should happen + return zone; + } + + zone.startX = Math.max(0, zone.startX - 1); + zone.startY = Math.max(0, zone.startY - 1); + zone.endX = Math.min(width - 1, zone.endX + 1); + zone.endY = Math.min(Height - 1, zone.endY + 1); + + System.out.printf("Expanded zone from (%d, %d)-(%d, %d)\n", zone.startX, zone.startY, zone.endX, zone.endY); + return zone; + } + + // chekcs if a zone is valid ( all the tiles are compatible with their neighbors) + private boolean validateRecalculationZone(Zone zone) { + for (int x = zone.startX; x <= zone.endX; x++) { + for (int y = zone.startY; y <= zone.endY; y++) { + WFCNode node = map[x][y]; + if (!isNodeCompatible(node, x, y)) { + System.err.println("Incompatibility found at (" + x + ", " + y + ")"); + return false; // Configuration not valid + } + } + } + return true; // Configuration is valid + } + + // takes a node and it's position and checks if it's compatible with it's neighbors + private boolean isNodeCompatible(WFCNode node, int x, int y) { + if (node == null) return false; // Empty node is not compatible with anything + for (Vector2 offset : offsets) { + + int neighborX = x + (int) offset.x; + int neighborY = y + (int) offset.y; + if (IsInGrid(new Vector2(neighborX, neighborY))) { + WFCNode neighborNode = map[neighborX][neighborY]; + if (neighborNode == null) return false; + + // Depending on the offset, check the compatibility in the corresponding direction + if (offset.equals(new Vector2(0, 1)) && !node.Top.compatibleNodes.contains(neighborNode) || + offset.equals(new Vector2(0, -1)) && !node.Bottom.compatibleNodes.contains(neighborNode) || + offset.equals(new Vector2(1, 0)) && !node.Right.compatibleNodes.contains(neighborNode) || + offset.equals(new Vector2(-1, 0)) && !node.Left.compatibleNodes.contains(neighborNode)) { + return false; // Found incompatibility + } + } + } + return true; + } + + + // helper class Zone + private class Zone { + public int startX, startY, endX, endY; + + public Zone(int startX, int startY, int endX, int endY) { + this.startX = startX; + this.startY = startY; + this.endX = endX; + this.endY = endY; + } + } + + + + // Main function to collapse the world + // This function will divide the world into chunks and collapse each chunk + // and then resolve the boundaries ( inefficient af ) + private void collapseWorld() { + chunks = divideIntoChunks(); + for (int chunkX = 0; chunkX < width / chunkSize; chunkX++) { + for (int chunkY = 0; chunkY < Height / chunkSize; chunkY++) { + collapseChunk(chunks[chunkX][chunkY], chunkX, chunkY); + } + } + + for (int chunkX = 0; chunkX < width / chunkSize; chunkX++) { + for (int chunkY = 0; chunkY < Height / chunkSize; chunkY++) { + checkAndResolveBoundaries(chunkX, chunkY); + } + } + + + // Final pass to check for any empty spaces and resolve if needed and add to outputTiles + for (int x = 0; x < width; x++) { + for (int y = 0; y < Height; y++) { + if (map[x][y] != null) { + Tile tile = map[x][y].tile.clone((int) Math.round(map[x][y].tile.getId() + x + y * Math.random())); + tile.setIsReachable(!Objects.equals(map[x][y].name, "sandCenterWGrass[2][1]")); // Adjust reachability condition + tile.setPosition(new Vector2(x * width, y * Height)); + outputTiles.add(tile); + } + else { + // this means that there + recalculateZoneAroundEmptyTile(x, y); + } + } +} + } + + // Recalculate the zone around an empty tile and resolve it + private void recalculateZoneAroundEmptyTile(int emptyTileX, int emptyTileY) { + int zoneSize = 1; // Start with the immediate neighbors + boolean resolved = false; + + while (!resolved && zoneSize <= Math.max(width, Height)) { + // Determine the zone to clear and recalculate + int startX = Math.max(0, emptyTileX - zoneSize); + int endX = Math.min(width - 1, emptyTileX + zoneSize); + int startY = Math.max(0, emptyTileY - zoneSize); + int endY = Math.min(Height - 1, emptyTileY + zoneSize); + + // Clear out the zone in the map and the outputTiles + clearZone(startX, startY, endX, endY); + + // Try to resolve the zone + resolved = fillZone(startX, startY, endX, endY); + + if (!resolved) { + // If not resolved, expand the zone + zoneSize++; + } else { + updateOutputTiles(startX, startY, endX, endY); + } + } + + if (!resolved) { + System.err.println("Failed to resolve even after expanding the zone to maximum size."); + // At this point, we have exhausted all possibilities and the world cannot be resolved + // we handle this by choosing a random tile for the empty space + // and we will choose the tile that is the most repeated + // if there are no repeated tiles we will choose a random tile + Map repeatedTiles = new HashMap<>(); + for (int i = 0; i < offsets.length; i++) { + Vector2 neighbor = new Vector2(emptyTileX + offsets[i].x, emptyTileY + offsets[i].y); + if (IsInGrid(neighbor)) { + WFCNode neighborNode = map[(int) neighbor.x][(int) neighbor.y]; + if (neighborNode != null) { + int tileId = neighborNode.tile.getId(); + if (repeatedTiles.containsKey(tileId)) { + repeatedTiles.put(tileId, repeatedTiles.get(tileId) + 1); + } else { + repeatedTiles.put(tileId, 1); + } + } + } + } + + // if there are no repeated tiles we will choose a random tile + if (repeatedTiles.isEmpty()) { + WFCNode newNode = getRandomNode(nodes); + map[emptyTileX][emptyTileY] = newNode; + // chose according to the entropy + Tile tile = newNode.tile.clone((int) Math.round(newNode.tile.getId() + emptyTileX + emptyTileY * Math.random())); + tile.setIsReachable(!Objects.equals(newNode.name, "sandCenterWGrass[2][1]")); // Adjust reachability condition + tile.setPosition(new Vector2(emptyTileX * width, emptyTileY * Height)); + outputTiles.add(tile); + } else { + // we will choose the tile that is the most repeated + int max = 0; + int tileId = 0; + for (Map.Entry entry : repeatedTiles.entrySet()) { + if (entry.getValue() > max) { + max = entry.getValue(); + tileId = entry.getKey(); + } + } + int finalTileId = tileId; + WFCNode newNode = nodes.stream().filter(node -> node.tile.getId() == finalTileId).findFirst().orElse(null); + map[emptyTileX][emptyTileY] = newNode; + Tile tile = newNode.tile.clone((int) Math.round(newNode.tile.getId() + emptyTileX + emptyTileY * Math.random())); + tile.setIsReachable(!Objects.equals(newNode.name, "sandCenterWGrass[2][1]")); // Adjust reachability condition + tile.setPosition(new Vector2(emptyTileX * width, emptyTileY * Height)); + outputTiles.add(tile); + } + + } + } + + private void clearZone(int startX, int startY, int endX, int endY) { + for (int x = startX; x <= endX; x++) { + for (int y = startY; y <= endY; y++) { + map[x][y] = null; + } + } + outputTiles.removeIf(tile -> + tile.getPosition().x >= startX && tile.getPosition().x <= endX && + tile.getPosition().y >= startY && tile.getPosition().y <= endY + ); + } + + private boolean fillZone(int startX, int startY, int endX, int endY) { + for (int x = startX; x <= endX; x++) { + for (int y = startY; y <= endY; y++) { + ArrayList potentialNodes = getPotentialNodesConsideringAllNeighbors(x, y); + if (!potentialNodes.isEmpty()) { + WFCNode selectedNode = getRandomNode(potentialNodes); + map[x][y] = selectedNode; // Update the map with the selected node + } else { + return false; // Return false if no compatible nodes found, indicating unresolved zone + } + } + } + return true; // Return true if the entire zone is filled successfully + } + + private void updateOutputTiles(int startX, int startY, int endX, int endY) { + for (int x = startX; x <= endX; x++) { + for (int y = startY; y <= endY; y++) { + if (map[x][y] != null) { + Tile tile = map[x][y].tile.clone((int) Math.round(map[x][y].tile.getId() + x + y * Math.random())); + tile.setIsReachable(!Objects.equals(map[x][y].name, "sandCenterWGrass[2][1]")); + tile.setPosition(new Vector2(x * width, y * Height)); + outputTiles.add(tile); // Add the tile to the output list + } + } + } + } + + // Method to get potential nodes based on the neighbors and their neighbors + private ArrayList getPotentialNodes(int x, int y) { + ArrayList potentialNodes = new ArrayList<>(nodes); + + // Iterate through neighbors and update potentialNodes + for (int i = 0; i < offsets.length; i++) { + Vector2 neighbor = new Vector2(x + offsets[i].x, y + offsets[i].y); + if (IsInGrid(neighbor)) { + WFCNode neighborNode = map[(int) neighbor.x][(int) neighbor.y]; + if (neighborNode != null) { + switch (i) { + case 0: + whittleNodes(potentialNodes, neighborNode.Bottom.compatibleNodes); + break; + case 1: + whittleNodes(potentialNodes, neighborNode.Top.compatibleNodes); + break; + case 2: + whittleNodes(potentialNodes, neighborNode.Left.compatibleNodes); + break; + case 3: + whittleNodes(potentialNodes, neighborNode.Right.compatibleNodes); + break; + } + } + } + } + + return potentialNodes; + } + + + // Method to get a random node from a list of nodes based on entropy + private WFCNode getRandomNode(ArrayList nodes) { + double totalEntropy = nodes.stream().mapToDouble(node -> node.entropy).sum(); + double randomValue = Math.random() * totalEntropy; + + for (WFCNode node : nodes) { + randomValue -= node.entropy; + if (randomValue <= 0) { + return node; + } + } + + return nodes.get((int) (Math.random() * nodes.size())); + } + + // Method to check if coordinates are within the grid bounds + private boolean IsInGrid(Vector2 coords) { + return coords.x >= 0 && coords.x < width && coords.y >= 0 && coords.y < Height; + } + + + // get the output tiles + public ArrayList getOutputTiles() { + return outputTiles; + } + + + // compares a list of potential nodes to a lmist of a valid nodes and remove + // all non valid nodes from the potential nodes + private void whittleNodes(ArrayList potentialNodes, ArrayList validNodes ) { + for (int i = 0; i < potentialNodes.size(); i++) { + if (!validNodes.contains(potentialNodes.get(i))) { + potentialNodes.remove(i); + i--; + } + } + } + + +} \ No newline at end of file diff --git a/desktop/out/production/resources/Brick.png b/desktop/out/production/resources/Brick.png new file mode 100644 index 0000000..d81eb5c Binary files /dev/null and b/desktop/out/production/resources/Brick.png differ diff --git a/desktop/out/production/resources/badlogic.jpg b/desktop/out/production/resources/badlogic.jpg new file mode 100644 index 0000000..4390da6 Binary files /dev/null and b/desktop/out/production/resources/badlogic.jpg differ diff --git a/desktop/out/production/resources/firebullet.png b/desktop/out/production/resources/firebullet.png new file mode 100644 index 0000000..25d9720 Binary files /dev/null and b/desktop/out/production/resources/firebullet.png differ diff --git a/desktop/out/production/resources/grassland_tiles.png b/desktop/out/production/resources/grassland_tiles.png new file mode 100644 index 0000000..003b1f5 Binary files /dev/null and b/desktop/out/production/resources/grassland_tiles.png differ diff --git a/desktop/out/production/resources/map/RPGScreenshot.png b/desktop/out/production/resources/map/RPGScreenshot.png new file mode 100644 index 0000000..33e468f Binary files /dev/null and b/desktop/out/production/resources/map/RPGScreenshot.png differ diff --git a/desktop/out/production/resources/map/RPGTiles.png b/desktop/out/production/resources/map/RPGTiles.png new file mode 100644 index 0000000..6b7107a Binary files /dev/null and b/desktop/out/production/resources/map/RPGTiles.png differ diff --git a/desktop/out/production/resources/map/boss_chest.png b/desktop/out/production/resources/map/boss_chest.png new file mode 100644 index 0000000..333cecb Binary files /dev/null and b/desktop/out/production/resources/map/boss_chest.png differ diff --git a/desktop/out/production/resources/map/grassland.png b/desktop/out/production/resources/map/grassland.png new file mode 100644 index 0000000..0a56056 Binary files /dev/null and b/desktop/out/production/resources/map/grassland.png differ diff --git a/desktop/out/production/resources/map/grassland_structures.png b/desktop/out/production/resources/map/grassland_structures.png new file mode 100644 index 0000000..d24fb92 Binary files /dev/null and b/desktop/out/production/resources/map/grassland_structures.png differ diff --git a/desktop/out/production/resources/map/grassland_trees.png b/desktop/out/production/resources/map/grassland_trees.png new file mode 100644 index 0000000..1b87de6 Binary files /dev/null and b/desktop/out/production/resources/map/grassland_trees.png differ diff --git a/desktop/out/production/resources/map/grassland_water.png b/desktop/out/production/resources/map/grassland_water.png new file mode 100644 index 0000000..d99a40f Binary files /dev/null and b/desktop/out/production/resources/map/grassland_water.png differ diff --git a/desktop/out/production/resources/map/rottentower.png b/desktop/out/production/resources/map/rottentower.png new file mode 100644 index 0000000..3c3085f Binary files /dev/null and b/desktop/out/production/resources/map/rottentower.png differ diff --git a/desktop/out/production/resources/map/set_rules.png b/desktop/out/production/resources/map/set_rules.png new file mode 100644 index 0000000..dbbea97 Binary files /dev/null and b/desktop/out/production/resources/map/set_rules.png differ diff --git a/desktop/out/production/resources/map/tiled_collision.png b/desktop/out/production/resources/map/tiled_collision.png new file mode 100644 index 0000000..30c5d9b Binary files /dev/null and b/desktop/out/production/resources/map/tiled_collision.png differ diff --git a/desktop/out/production/resources/map/tiled_grassland_2x2.png b/desktop/out/production/resources/map/tiled_grassland_2x2.png new file mode 100644 index 0000000..bd5c09a Binary files /dev/null and b/desktop/out/production/resources/map/tiled_grassland_2x2.png differ diff --git a/desktop/out/production/resources/sc_map.png b/desktop/out/production/resources/sc_map.png new file mode 100644 index 0000000..1ae81b0 Binary files /dev/null and b/desktop/out/production/resources/sc_map.png differ diff --git a/desktop/out/production/resources/skeleton.png b/desktop/out/production/resources/skeleton.png new file mode 100644 index 0000000..292bba7 Binary files /dev/null and b/desktop/out/production/resources/skeleton.png differ diff --git a/desktop/out/production/resources/terrain.png b/desktop/out/production/resources/terrain.png new file mode 100644 index 0000000..3411f5d Binary files /dev/null and b/desktop/out/production/resources/terrain.png differ diff --git a/desktop/out/production/resources/threeformsPrev.png b/desktop/out/production/resources/threeformsPrev.png new file mode 100644 index 0000000..616a50c Binary files /dev/null and b/desktop/out/production/resources/threeformsPrev.png differ diff --git a/desktop/src/com/cpa/project/DesktopLauncher.java b/desktop/src/com/cpa/project/DesktopLauncher.java index 3e9fc59..1a6813c 100644 --- a/desktop/src/com/cpa/project/DesktopLauncher.java +++ b/desktop/src/com/cpa/project/DesktopLauncher.java @@ -2,6 +2,11 @@ import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; +import com.badlogic.gdx.math.Vector2; +import com.cpa.project.Tiles.Tile; +import com.cpa.project.Tiles.terrainFloorTiles; + +import java.util.*; // Please note that on macOS your application needs to be started with the -XstartOnFirstThread JVM argument public class DesktopLauncher { @@ -15,3 +20,765 @@ public static void main(String[] arg) { new Lwjgl3Application(new Survivors(), config); } } + + + + +// +//package com.cpa.project.World; +// +// +//import com.badlogic.gdx.graphics.g2d.BitmapFont; +//import com.badlogic.gdx.maps.MapProperties; +//import com.badlogic.gdx.math.Vector2; +//import com.cpa.project.Tiles.Tile; +//import com.cpa.project.Tiles.terrainFloorTiles; +// +//import java.util.*; +// import java.util.Map; +//import java.util.stream.StreamSupport; +// +// +//class WFCNode { +// +// String name; +// Tile tile; +// com.cpa.project.World.WFCConnection Top; // les connections du haut de la tile qui sont possibles +// com.cpa.project.World.WFCConnection Bottom; +// com.cpa.project.World.WFCConnection Left; +// com.cpa.project.World.WFCConnection Right; +// +// double entropy; +// +//// WFCConnection TopRight; +//// WFCConnection TopLeft; +//// WFCConnection BottomRight; +//// WFCConnection BottomLeft; +// +// +// public WFCNode(String name, Tile tile , double entropy ) { +// this.name = name; +// this.tile = tile; +// this.entropy = entropy; +// this.Top = new com.cpa.project.World.WFCConnection(); +// this.Bottom = new com.cpa.project.World.WFCConnection(); +// this.Left = new com.cpa.project.World.WFCConnection(); +// this.Right = new com.cpa.project.World.WFCConnection(); +// +//// this.TopRight = new WFCConnection(); +//// this.TopLeft = new WFCConnection(); +//// this.BottomRight = new WFCConnection(); +//// this.BottomLeft = new WFCConnection(); +// +// } +// +// public com.cpa.project.World.WFCNode addTop(com.cpa.project.World.WFCNode node){ +// Top.addNode(node); +// node.Bottom.addNode(this); +// return this; +// } +// +// public com.cpa.project.World.WFCNode addBottom(com.cpa.project.World.WFCNode node){ +// Bottom.addNode(node); +// node.Top.addNode(this); +// return this; +// } +// +// public com.cpa.project.World.WFCNode addLeft(com.cpa.project.World.WFCNode node){ +// Left.addNode(node); +// node.Right.addNode(this); +// return this; +// } +// +// public com.cpa.project.World.WFCNode addRight(com.cpa.project.World.WFCNode node){ +// Right.addNode(node); +// node.Left.addNode(this); +// return this; +// } +// +// public void setEntropy(double entropy){ +// this.entropy = entropy; +// } +// +// public double getEntropy(){ +// return this.entropy; +// } +// +// public ArrayList getCompatibleNodes(int i) { +// switch (i) { +// case 0: +// return Top.compatibleNodes; +// case 1: +// return Bottom.compatibleNodes; +// case 2: +// return Left.compatibleNodes; +// case 3: +// return Right.compatibleNodes; +// default: +// return new ArrayList<>(); +// } +// } +//// +//// public WFCNode addTopRight(WFCNode node){ +//// TopRight.addNode(node); +//// node.BottomLeft.addNode(this); +//// return this; +//// } +//// +//// public WFCNode addTopLeft(WFCNode node){ +//// TopLeft.addNode(node); +//// node.BottomRight.addNode(this); +//// return this; +//// } +//// +//// public WFCNode addBottomRight(WFCNode node){ +//// BottomRight.addNode(node); +//// node.TopLeft.addNode(this); +//// return this; +//// } +//// +//// public WFCNode addBottomLeft(WFCNode node){ +//// BottomLeft.addNode(node); +//// node.TopRight.addNode(this); +//// return this; +//// } +// +// +// +// +//} +// +//class WFCConnection { +// ArrayList compatibleNodes ; +// public WFCConnection() { +// compatibleNodes = new ArrayList<>(); +// } +// +// public void addNode(com.cpa.project.World.WFCNode node){ +// if (!compatibleNodes.contains(node)) +// compatibleNodes.add(node); +// } +// +// public void removeNode(com.cpa.project.World.WFCNode node){ +// compatibleNodes.remove(node); +// } +// +// // print the compatible nodes +// public void printCompatibleNodes(){ +// for (int i = 0; i < compatibleNodes.size(); i++) { +// System.err.println(compatibleNodes.get(i).name); +// } +// } +//} +// +///** +// * classe qui permet de gérer la map et les tiles de la map +// * Algorithme de Wave Function Collapse : https://robertheaton.com/2018/12/17/wavefunction-collapse-algorithm/ +// * this is the Simple Tiled Implementation of the Wave Function Collapse Algorithm +// */ +//public class WFC { +// // the Size of the map : width and height +// private int width,Height; +// // a 2D array of WFCNode that will store our collapsed tiles so we can reference them later +// private com.cpa.project.World.WFCNode[][] map; +// +// // the list of all the nodes that we have +// public ArrayList nodes = new ArrayList<>(); +// +// // the list of all the tiles that we will output +// ArrayList outputTiles = new ArrayList<>(); +// +// // a list of the vector2 that we will use to collapse the tiles +// private ArrayList toCollapse = new ArrayList<>(); +// +// +// // an array of the offsets that we will use to get the neighbors of a tile +// private Vector2[] offsets = new Vector2[]{ +// new Vector2(0,1), // top +// new Vector2(0,-1), // bottom +// new Vector2(1,0), // right +// new Vector2(-1,0) // left +//// new Vector2(1,1), // top right +//// new Vector2(-1,1), // top left +//// new Vector2(1,-1), // bottom right +//// new Vector2(-1,-1) // bottom left +// }; +// +// com.cpa.project.Tiles.terrainFloorTiles terrainFloorTiles; +// +// Tile[][] sandCenterWGrass; +// Tile[][] grassCenterWSand; +// Tile[][] rockCenterWSand; +// Tile[][] voidCenterWRock; +// Tile[][] waterTiles; +// +// +// com.cpa.project.World.WFCNode centerStart ; +// +// // the constructor of the WFC +// public WFC(int width, int height) { +// this.width = width; +// this.Height = height; +// this.map = new com.cpa.project.World.WFCNode[width][height]; +// this.terrainFloorTiles = new terrainFloorTiles(); +// this.sandCenterWGrass = terrainFloorTiles.getTileBox(0); +// this.grassCenterWSand = terrainFloorTiles.getTileBox(10); +// this.rockCenterWSand = terrainFloorTiles.getTileBox(20); +// this.voidCenterWRock = terrainFloorTiles.getTileBox(30); +// this.waterTiles = terrainFloorTiles.getTileBox(40); +// +// initNodes(); +// collapseWorld(); +// +// } +// +// // this function will initialize the nodes that we have in the world +// // and the connections between them +// private void initNodes() { +// // we will start with sandCenterWGrass +// Map allNodes = new HashMap<>(); +// allNodes.put(0, new com.cpa.project.World.WFCNode("sandCenterWGrass[0][0]", sandCenterWGrass[0][0] , 0.1)); +// allNodes.put(1, new com.cpa.project.World.WFCNode("sandCenterWGrass[0][1]", sandCenterWGrass[0][1] , 0.1)); +// allNodes.put(2, new com.cpa.project.World.WFCNode("sandCenterWGrass[0][2]", sandCenterWGrass[0][2] , 0.1)); +// allNodes.put(3, new com.cpa.project.World.WFCNode("sandCenterWGrass[1][0]", sandCenterWGrass[1][0] , 0.1)); +// allNodes.put(4, new com.cpa.project.World.WFCNode("sandCenterWGrass[1][1]", sandCenterWGrass[1][1] , 0.9)); +// allNodes.put(5, new com.cpa.project.World.WFCNode("sandCenterWGrass[1][2]", sandCenterWGrass[1][2] , 0.1)); +// allNodes.put(6, new com.cpa.project.World.WFCNode("sandCenterWGrass[2][0]", sandCenterWGrass[2][0] , 0.1)); +// allNodes.put(7, new com.cpa.project.World.WFCNode("sandCenterWGrass[2][1]", sandCenterWGrass[2][1] , 0.1)); +// allNodes.put(8, new com.cpa.project.World.WFCNode("sandCenterWGrass[2][2]", sandCenterWGrass[2][2] , 0.1)); +// +// +// allNodes.put(10, new com.cpa.project.World.WFCNode("grassCenterWSand[0][0]", grassCenterWSand[0][0] , 0.1)); +// allNodes.put(11, new com.cpa.project.World.WFCNode("grassCenterWSand[0][1]", grassCenterWSand[0][1] , 0.1)); +// allNodes.put(12, new com.cpa.project.World.WFCNode("grassCenterWSand[0][2]", grassCenterWSand[0][2] , 0.1)); +// allNodes.put(13, new com.cpa.project.World.WFCNode("grassCenterWSand[1][0]", grassCenterWSand[1][0] , 0.1)); +// allNodes.put(14, new com.cpa.project.World.WFCNode("grassCenterWSand[1][1]", grassCenterWSand[1][1] , 0.9)); +// allNodes.put(15, new com.cpa.project.World.WFCNode("grassCenterWSand[1][2]", grassCenterWSand[1][2] , 0.1)); +// allNodes.put(16, new com.cpa.project.World.WFCNode("grassCenterWSand[2][0]", grassCenterWSand[2][0] , 0.1)); +// allNodes.put(17, new com.cpa.project.World.WFCNode("grassCenterWSand[2][1]", grassCenterWSand[2][1] , 0.1)); +// allNodes.put(18, new com.cpa.project.World.WFCNode("grassCenterWSand[2][2]", grassCenterWSand[2][2] , 0.1)); +// +// +// +// +// +// +// // we add the nodes to the nodes list +// nodes.addAll(allNodes.values()); +// +// centerStart = allNodes.get(4); +// +// +// setConnections(allNodes, 0); +// setConnections(allNodes, 10); +// +//// we set the outer connections +// setOuterConnections(allNodes, 0, 10); +// setOuterConnections(allNodes , 10, 0); +// +// +// // check compatibilitis of 0 +// System.err.println("Top" ) ; +// allNodes.get(7).Top.printCompatibleNodes(); +// System.err.println("Bottom" ) ; +// allNodes.get(7).Bottom.printCompatibleNodes(); +// System.err.println("Left" ) ; +// allNodes.get(7).Left.printCompatibleNodes(); +// System.err.println("Right" ) ; +// allNodes.get(7).Right.printCompatibleNodes(); +// +// } +// +// +// +// void setConnections(Map allNodes, int baseIndex) { +// allNodes.get(baseIndex+4).addRight(allNodes.get(baseIndex+4)) +// .addBottom(allNodes.get(baseIndex+4)).addLeft(allNodes.get(baseIndex+4)) +// .addTop(allNodes.get(baseIndex+4)); +// +// allNodes.get(baseIndex).addRight(allNodes.get(baseIndex + 1)). +// addBottom(allNodes.get(baseIndex + 3)); +// allNodes.get(baseIndex + 1).addLeft(allNodes.get(baseIndex)). +// addRight(allNodes.get(baseIndex + 2)). +// addBottom(allNodes.get(baseIndex + 4)).addLeft(allNodes.get(baseIndex + 1)).addRight(allNodes.get(baseIndex + 1)); +// allNodes.get(baseIndex + 2).addLeft(allNodes.get(baseIndex + 1)). +// addBottom(allNodes.get(baseIndex + 5)); +// +// allNodes.get(baseIndex + 3).addTop(allNodes.get(baseIndex)). +// addRight(allNodes.get(baseIndex + 4)). +// addBottom(allNodes.get(baseIndex + 6)).addTop(allNodes.get(baseIndex + 3)).addBottom(allNodes.get(baseIndex + 3)); +// allNodes.get(baseIndex + 4).addTop(allNodes.get(baseIndex + 1)) +// .addLeft(allNodes.get(baseIndex + 3)) +// .addRight(allNodes.get(baseIndex + 5)) +// .addBottom(allNodes.get(baseIndex + 7)); +// allNodes.get(baseIndex + 5).addTop(allNodes.get(baseIndex + 2)) +// .addLeft(allNodes.get(baseIndex + 4)) +// .addBottom(allNodes.get(baseIndex + 8)) +// .addTop(allNodes.get(baseIndex + 5)). +// addBottom(allNodes.get(baseIndex + 5)); +// +// allNodes.get(baseIndex + 6).addTop(allNodes.get(baseIndex + 3)) +// .addRight(allNodes.get(baseIndex + 7)); +// allNodes.get(baseIndex + 7).addTop(allNodes.get(baseIndex + 4)) +// .addLeft(allNodes.get(baseIndex + 6)) +// .addRight(allNodes.get(baseIndex + 8)).addRight(allNodes.get(baseIndex + 6)).addLeft(allNodes.get(baseIndex + 6)); +// allNodes.get(baseIndex + 8).addTop(allNodes.get(baseIndex + 5)) +// .addLeft(allNodes.get(baseIndex + 7)); +// +// } +// +// +// // i'm adding the outer connection from one box tile to another +// // example : sandCenterWGrass to grassCenterWSand +// // sandCenterWGrass[0][0] to grassCenterWSand[1][1] in the top direction and left direction +// // sandCenterWGrass[0][2] to grassCenterWSand[1][1] in the top direction and right direction +// // sandCenterWGrass[1][0] to grassCenterWSand[1][1] in the left direction +// // sandCenterWGrass[1][2] to grassCenterWSand[1][1] in the right direction +// // sandCenterWGrass[2][0] to grassCenterWSand[1][1] in the bottom direction and left direction +// // sandCenterWGrass[2][2] to grassCenterWSand[1][1] in the bottom direction and right direction +// public void setOuterConnections(Map allNodes, int baseIndex1, int baseIndex2) { +// // top left +// allNodes.get(baseIndex1).addLeft(allNodes.get(baseIndex2+4)).addTop(allNodes.get(baseIndex2+4)); +// +// // top right +// allNodes.get(baseIndex1+2).addRight(allNodes.get(baseIndex2+4)).addTop(allNodes.get(baseIndex2+4)); +// +// // top +// allNodes.get(baseIndex1+1).addTop(allNodes.get(baseIndex2+4)); +// +// // bottom left +// allNodes.get(baseIndex1+6).addLeft(allNodes.get(baseIndex2+4)).addBottom(allNodes.get(baseIndex2+4)); +// +// // bottom right +// allNodes.get(baseIndex1+8).addRight(allNodes.get(baseIndex2+4)).addBottom(allNodes.get(baseIndex2+4)); +// +// // bottom +// allNodes.get(baseIndex1+7).addBottom(allNodes.get(baseIndex2+4)); +// +// // left +// allNodes.get(baseIndex1+3).addLeft(allNodes.get(baseIndex2+4)); +// +// // right +// allNodes.get(baseIndex1+5).addRight(allNodes.get(baseIndex2+4)); +// +// +// } +// +// +// // this function will collapse the world +//// private void collapseWorld() { +//// toCollapse.clear(); +//// toCollapse.add(new Vector2(0, 0)); // Starting from the center +//// +//// boolean first = false; +//// while (!toCollapse.isEmpty()) { +//// int x = (int) toCollapse.get(0).x; +//// int y = (int) toCollapse.get(0).y; +//// +//// ArrayList potentialNodes = new ArrayList<>(nodes); +//// +//// for (int i = 0; i < offsets.length; i++) { +//// Vector2 neighbor = new Vector2(x + offsets[i].x, y + offsets[i].y); +//// if (IsInGrid(neighbor)) { +//// WFCNode neighborNode = map[(int) neighbor.x][(int) neighbor.y]; +//// if (neighborNode != null) { +//// switch (i) { +//// case 0: +//// whittleNodes(potentialNodes, neighborNode.Bottom.compatibleNodes ); +//// break; +//// case 1: +//// whittleNodes(potentialNodes, neighborNode.Top.compatibleNodes ); +//// break; +//// case 2: +//// whittleNodes(potentialNodes, neighborNode.Left.compatibleNodes ); +//// break; +//// case 3: +//// whittleNodes(potentialNodes, neighborNode.Right.compatibleNodes); +//// break; +//// } +//// } else { +//// if (!toCollapse.contains(neighbor)) { +//// toCollapse.add(neighbor); +//// } +//// } +//// } +//// } +//// +//// // Now we have whittled down the potential nodes based on neighbors +//// if (potentialNodes.isEmpty()) { +//// // If no compatible nodes, select a random one +//// map[x][y] = nodes.get(0); +//// } else { +//// if (!first) { +//// map[x][y] = centerStart; +//// first = true; +//// } else { +//// map[x][y] = getRandomNode(potentialNodes); +//// } +//// } +//// +//// // Add the tile to the output tiles +//// Tile tile = map[x][y].tile.clone((int) Math.round(map[x][y].tile.getId() + x + y * Math.random())); +//// tile.setIsReachable(!Objects.equals(map[x][y].name, "sandCenterWGrass[2][1]")); // Adjust reachability condition +//// tile.setPosition(new Vector2(x * width, y * Height)); +//// outputTiles.add(tile); +//// +//// // Remove the tile from the toCollapse list +//// toCollapse.remove(0); +//// } +//// } +// +//// +// private void collapseWorld() { +// toCollapse.clear(); +// toCollapse.add(new Vector2(width / 2, Height / 2)); // Starting from the center +// +// while (!toCollapse.isEmpty()) { +// +// int x = (int) toCollapse.get(0).x; +// int y = (int) toCollapse.get(0).y; +// +// ArrayList potentialNodes = new ArrayList<>(nodes); +// +// for (int i = 0; i < offsets.length; i++) { +// Vector2 neighbor = new Vector2(x + offsets[i].x, y + offsets[i].y); +// if (IsInGrid(neighbor)) { +// WFCNode neighborNode = map[(int) neighbor.x][(int) neighbor.y]; +// if (neighborNode != null) { +// switch (i) { +// case 0: +// whittleNodes(potentialNodes, neighborNode.Bottom.compatibleNodes); +// break; +// case 1: +// whittleNodes(potentialNodes, neighborNode.Top.compatibleNodes); +// break; +// case 2: +// whittleNodes(potentialNodes, neighborNode.Left.compatibleNodes); +// break; +// case 3: +// whittleNodes(potentialNodes, neighborNode.Right.compatibleNodes); +// break; +// } +// } else { +// if (!toCollapse.contains(neighbor)) { +// toCollapse.add(neighbor); +// } +// } +// } +// } +// +// // Now we have whittled down the potential nodes based on neighbors +// if (!potentialNodes.isEmpty()) { +// // If there are compatible nodes, select a random one +// WFCNode selectedNode = getRandomNode(potentialNodes); +// map[x][y] = selectedNode; +// +// // Check if the current configuration is valid +// if (isValidConfiguration(x, y)) { +// // Add the tile to the output tiles +// Tile tile = map[x][y].tile.clone((int) Math.round(map[x][y].tile.getId() + x + y * Math.random())); +// tile.setIsReachable(!Objects.equals(map[x][y].name, "sandCenterWGrass[2][1]")); // Adjust reachability condition +// tile.setPosition(new Vector2(x * width, y * Height)); +// outputTiles.add(tile); +// +// // Remove the tile from the toCollapse list +// toCollapse.remove(0); +// } else { +// // If the current configuration is not valid, backtrack +// map[x][y] = null; +// +// // add the tile to the end of the toCollapse list +// toCollapse.add(new Vector2(x, y)); +// Collections.shuffle(toCollapse); +// } +// } else { +// // If no compatible nodes found, backtrack +// toCollapse.remove(0); +// } +// } +// } +// +// private void collapseWorld() { +// // Use a queue for BFS +// Queue queue = new LinkedList<>(toCollapse); +// +// queue.add(new Vector2(width / 2, Height / 2)); // Starting from the center +// +// while (!queue.isEmpty()) { +// Vector2 currentPos = queue.poll(); +// int x = (int) currentPos.x; +// int y = (int) currentPos.y; +// +// ArrayList potentialNodes = new ArrayList<>(nodes); +// +// // Iterate through neighbors and update potentialNodes +// for (int i = 0; i < offsets.length; i++) { +// Vector2 neighbor = new Vector2(x + offsets[i].x, y + offsets[i].y); +// if (IsInGrid(neighbor)) { +// com.cpa.project.World.WFCNode neighborNode = map[(int) neighbor.x][(int) neighbor.y]; +// if (neighborNode != null) { +// switch (i) { +// case 0: +// whittleNodes(potentialNodes, neighborNode.Bottom.compatibleNodes); +// break; +// case 1: +// whittleNodes(potentialNodes, neighborNode.Top.compatibleNodes); +// break; +// case 2: +// whittleNodes(potentialNodes, neighborNode.Left.compatibleNodes); +// break; +// case 3: +// whittleNodes(potentialNodes, neighborNode.Right.compatibleNodes); +// break; +// } +// } else { +// if (!queue.contains(neighbor)) { +// queue.add(neighbor); +// } +// } +// } +// +// } +// +// // Now we have whittled down the potential nodes based on neighbors +// if (!potentialNodes.isEmpty()) { +// // If there are compatible nodes, select a random one +// com.cpa.project.World.WFCNode selectedNode = getRandomNode(potentialNodes); +// map[x][y] = selectedNode; +// +// // Check if the current configuration is valid +// if (isValidConfiguration(x, y)) { +// // Add the tile to the output tiles +// Tile tile = map[x][y].tile.clone((int) Math.round(map[x][y].tile.getId() + x + y * Math.random())); +// tile.setIsReachable(!Objects.equals(map[x][y].name, "sandCenterWGrass[2][1]")); // Adjust reachability condition +// tile.setPosition(new Vector2(x * width, y * Height)); +// outputTiles.add(tile); +// } else { +// // If the current configuration is not valid, backtrack +// map[x][y] = null; +// } +// } +// else{ +// +// // this means that there are no compatible nodes +// // so we need to remove the current node from the queue +// // and also remove the neighbors from the queue +// // and redo the whole zone +// +// } +// } +// +// +// } +// +// +//// private void collapseWorld() { +//// // Use a queue for BFS +//// Queue queue = new LinkedList<>(toCollapse); +//// +//// queue.add(new Vector2(width / 2, Height / 2)); // Starting from the center +//// +//// +//// while (!queue.isEmpty()) { +//// Vector2 currentPos = queue.poll(); +//// int x = (int) currentPos.x; +//// int y = (int) currentPos.y; +//// +//// ArrayList potentialNodes = new ArrayList<>(nodes); +//// +//// // Iterate through neighbors and update potentialNodes +//// for (int i = 0; i < offsets.length; i++) { +//// Vector2 neighbor = new Vector2(x + offsets[i].x, y + offsets[i].y); +//// if (IsInGrid(neighbor)) { +//// WFCNode neighborNode = map[(int) neighbor.x][(int) neighbor.y]; +//// if (neighborNode != null) { +//// switch (i) { +//// case 0: +//// whittleNodes(potentialNodes, neighborNode.Bottom.compatibleNodes); +//// break; +//// case 1: +//// whittleNodes(potentialNodes, neighborNode.Top.compatibleNodes); +//// break; +//// case 2: +//// whittleNodes(potentialNodes, neighborNode.Left.compatibleNodes); +//// break; +//// case 3: +//// whittleNodes(potentialNodes, neighborNode.Right.compatibleNodes); +//// break; +//// } +//// } else { +//// if (!queue.contains(neighbor)) { +//// queue.add(neighbor); +//// } +//// } +//// } +//// +//// } +//// +//// // Now we have whittled down the potential nodes based on neighbors +//// if (!potentialNodes.isEmpty()) { +//// // If there are compatible nodes, select a random one +//// WFCNode selectedNode = getRandomNode(potentialNodes); +//// map[x][y] = selectedNode; +//// +//// // Check if the current configuration is valid +//// if (isValidConfiguration(x, y)) { +//// // Add the tile to the output tiles +//// Tile tile = map[x][y].tile.clone((int) Math.round(map[x][y].tile.getId() + x + y * Math.random())); +//// tile.setIsReachable(!Objects.equals(map[x][y].name, "sandCenterWGrass[2][1]")); // Adjust reachability condition +//// tile.setPosition(new Vector2(x * width, y * Height)); +//// outputTiles.add(tile); +//// } else { +//// // If the current configuration is not valid, backtrack +//// map[x][y] = null; +//// +//// // Remove the current tile and its neighbors from the queue +//// queue.remove(currentPos); +//// for (Vector2 offset : offsets) { +//// Vector2 neighbor = new Vector2(x + offset.x, y + offset.y); +//// if (IsInGrid(neighbor)) { +//// queue.remove(neighbor); +//// } +//// } +//// +//// // Retry collapsing the affected zone +//// queue.add(currentPos); +//// } +//// +//// +//// } else { +//// // No compatible nodes found, handle as needed +//// +//// } +//// } +//// +//// } +//// +// +// // Method to get potential nodes based on the neighbors and their neighbors +// private ArrayList getPotentialNodes(int x, int y) { +// ArrayList potentialNodes = new ArrayList<>(nodes); +// +// // Iterate through neighbors and update potentialNodes +// for (int i = 0; i < offsets.length; i++) { +// Vector2 neighbor = new Vector2(x + offsets[i].x, y + offsets[i].y); +// if (IsInGrid(neighbor)) { +// com.cpa.project.World.WFCNode neighborNode = map[(int) neighbor.x][(int) neighbor.y]; +// if (neighborNode != null) { +// switch (i) { +// case 0: +// whittleNodes(potentialNodes, neighborNode.Bottom.compatibleNodes); +// break; +// case 1: +// whittleNodes(potentialNodes, neighborNode.Top.compatibleNodes); +// break; +// case 2: +// whittleNodes(potentialNodes, neighborNode.Left.compatibleNodes); +// break; +// case 3: +// whittleNodes(potentialNodes, neighborNode.Right.compatibleNodes); +// break; +// } +// } +// } +// } +// +// return potentialNodes; +// } +// +// +// +// +// // Check if the current configuration is valid +//// private boolean isValidConfiguration(int x, int y) { +//// if (x > 0 && map[x - 1][y] != null && !map[x][y].Left.compatibleNodes.contains(map[x - 1][y])) { +//// return false; +//// } +//// if (x < width - 1 && map[x + 1][y] != null && !map[x][y].Right.compatibleNodes.contains(map[x + 1][y])) { +//// return false; +//// } +//// if (y > 0 && map[x][y - 1] != null && !map[x][y].Bottom.compatibleNodes.contains(map[x][y - 1])) { +//// return false; +//// } +//// if (y < Height - 1 && map[x][y + 1] != null && !map[x][y].Top.compatibleNodes.contains(map[x][y + 1])) { +//// return false; +//// } +//// return true; +//// } +// +// private boolean isValidConfiguration(int x, int y) { +// com.cpa.project.World.WFCNode currentNode = map[x][y]; +// for (int i = 0; i < offsets.length; i++) { +// Vector2 neighbor = new Vector2(x + offsets[i].x, y + offsets[i].y); +// if (IsInGrid(neighbor)) { +// com.cpa.project.World.WFCNode neighborNode = map[(int) neighbor.x][(int) neighbor.y]; +// if (neighborNode != null) { +// switch (i) { +// case 0: +// if (!currentNode.Top.compatibleNodes.contains(neighborNode)) { +// return false; +// } +// break; +// case 1: +// if (!currentNode.Bottom.compatibleNodes.contains(neighborNode)) { +// return false; +// } +// break; +// case 2: +// if (!currentNode.Right.compatibleNodes.contains(neighborNode)) { +// return false; +// } +// break; +// case 3: +// if (!currentNode.Left.compatibleNodes.contains(neighborNode)) { +// return false; +// } +// break; +// } +// } +// } +// } +// return true; +// } +// // Method to get a random node from a list of nodes +// private com.cpa.project.World.WFCNode getRandomNode(ArrayList nodes) { +// // take into account the entropy of the nodes +// double totalEntropy = nodes.stream().mapToDouble(node -> node.entropy).sum(); +// double randomValue = Math.random() * totalEntropy; +// +// for (com.cpa.project.World.WFCNode node : nodes) { +// randomValue -= node.entropy; +// if (randomValue <= 0) { +// return node; +// } +// } +// +// return nodes.get((int) (Math.random() * nodes.size())); +// } +// +// // Method to check if coordinates are within the grid bounds +// private boolean IsInGrid(Vector2 coords) { +// return coords.x >= 0 && coords.x < width && coords.y >= 0 && coords.y < Height; +// } +// +// +// +// // get the output tiles +// public ArrayList getOutputTiles() { +// return outputTiles; +// } +// +// +// // compares a list of potential nodes to a lmist of a valid nodes and remove +// // all non valid nodes from the potential nodes +// private void whittleNodes(ArrayList potentialNodes, ArrayList validNodes ) { +// for (int i = 0; i < potentialNodes.size(); i++) { +// if (!validNodes.contains(potentialNodes.get(i))) { +// potentialNodes.remove(i); +// i--; +// } +// } +// } +// +// +//} \ No newline at end of file