Browse Source

Merge branch 'master' of http://git.famrupp.de/schule/tiny_settlement_schule

# Conflicts:
#	src/main/java/controller/GameController.java
Oskar Berger 5 months ago
parent
commit
af4ee8e30d
74 changed files with 2340 additions and 378 deletions
  1. 346 80
      src/main/java/controller/GameController.java
  2. 0 86
      src/main/java/controller/GameMouseListener.java
  3. 0 4
      src/main/java/controller/InputHandler.java
  4. 2 0
      src/main/java/controller/InteractiveController.java
  5. 0 4
      src/main/java/controller/MenuController.java
  6. 58 0
      src/main/java/controller/RessourceManager.java
  7. 6 1
      src/main/java/controller/entity/EntityController.java
  8. 3 1
      src/main/java/controller/entity/VillagerController.java
  9. 19 0
      src/main/java/controller/factories/EntityFactory.java
  10. 23 0
      src/main/java/controller/factories/InteractiveTileFactory.java
  11. 107 0
      src/main/java/controller/input/GameMouseListener.java
  12. 5 2
      src/main/java/controller/input/KeyHandler.java
  13. 3 1
      src/main/java/controller/input/MouseWheelZoom.java
  14. 12 4
      src/main/java/controller/tiles/interactive/InteractiveTileController.java
  15. 5 2
      src/main/java/controller/tiles/interactive/upgradeable/IronMineController.java
  16. 2 5
      src/main/java/controller/tiles/interactive/upgradeable/MineController.java
  17. 6 3
      src/main/java/controller/tiles/interactive/upgradeable/SawmillController.java
  18. 48 0
      src/main/java/controller/ui/ShopController.java
  19. 10 3
      src/main/java/main/Main.java
  20. 9 0
      src/main/java/model/GameModel.java
  21. 0 1
      src/main/java/model/GameSaver.java
  22. 12 5
      src/main/java/model/Inventory.java
  23. 9 2
      src/main/java/model/entity/EntityModel.java
  24. 5 0
      src/main/java/model/entity/EntityType.java
  25. 1 1
      src/main/java/model/items/ITEM_NAME.java
  26. 3 1
      src/main/java/model/items/Item.java
  27. 68 3
      src/main/java/model/tiles/InteractiveTileModel.java
  28. 10 0
      src/main/java/model/tiles/InteractiveTileType.java
  29. 1 1
      src/main/java/util/GAMESTATE.java
  30. 31 0
      src/main/java/util/Settings.java
  31. 15 1
      src/main/java/util/Translator.java
  32. 39 0
      src/main/java/util/economy/EconomyData.java
  33. 226 0
      src/main/java/util/economy/EconomyInfo.java
  34. 15 12
      src/main/java/view/Camera.java
  35. 66 11
      src/main/java/view/GamePanel.java
  36. 0 65
      src/main/java/view/InventoryView.java
  37. 3 0
      src/main/java/view/components/Button.java
  38. 3 0
      src/main/java/view/entity/EntityManager.java
  39. 11 6
      src/main/java/view/popUpMenu/UpgradeMenu.java
  40. 83 0
      src/main/java/view/sound/MusicManager.java
  41. 111 0
      src/main/java/view/sound/SoundManager.java
  42. 177 9
      src/main/java/view/tile/TileManager.java
  43. 1 1
      src/main/java/view/tile/interactive/AnimalEnclosureView.java
  44. 16 3
      src/main/java/view/tile/interactive/InteractiveTileView.java
  45. 1 1
      src/main/java/view/tile/interactive/IronMineView.java
  46. 1 1
      src/main/java/view/tile/interactive/MineView.java
  47. 2 2
      src/main/java/view/tile/interactive/SawmillView.java
  48. 90 0
      src/main/java/view/ui/InventoryView.java
  49. 153 0
      src/main/java/view/ui/ShopView.java
  50. 11 55
      src/main/java/view/ui/UI.java
  51. 34 0
      src/main/java/view/ui/menu/AbstractMenu.java
  52. 21 0
      src/main/java/view/ui/menu/MainMenu.java
  53. 82 0
      src/main/java/view/ui/menu/PauseMenu.java
  54. 82 0
      src/main/java/view/ui/menu/SettingsMenu.java
  55. 5 1
      src/main/java/view/util/RenderingManager.java
  56. 114 0
      src/main/resources/economy/economy.json
  57. BIN
      src/main/resources/items/iron.png
  58. BIN
      src/main/resources/items/stone.png
  59. BIN
      src/main/resources/items/wood.png
  60. 8 0
      src/main/resources/lang/de_de.json
  61. 8 0
      src/main/resources/lang/en_us.json
  62. 0 0
      src/main/resources/lang/translation.json
  63. BIN
      src/main/resources/sound/background/background.wav
  64. BIN
      src/main/resources/sound/click.wav
  65. BIN
      src/main/resources/sound/moving_bush.wav
  66. BIN
      src/main/resources/sound/villager.wav
  67. 0 0
      src/main/resources/tiles/animal_enclosure/animal_enclosureTest.png
  68. 0 0
      src/main/resources/tiles/iron_mine/iron_mineTest.png
  69. BIN
      src/main/resources/tiles/iron_mine/iron_mine_lvl1.png
  70. BIN
      src/main/resources/tiles/iron_mine/iron_mine_lvl2.png
  71. BIN
      src/main/resources/tiles/mine/mine_lvl1.png
  72. BIN
      src/main/resources/tiles/mine/mine_lvl2.png
  73. 44 0
      src/test/java/EconomyInfoTest.java
  74. 114 0
      src/test/resources/economy/economy.json

+ 346 - 80
src/main/java/controller/GameController.java

@@ -2,23 +2,33 @@ package controller;
 
 import controller.entity.EntityController;
 import controller.entity.VillagerController;
+import controller.factories.EntityFactory;
+import controller.factories.InteractiveTileFactory;
+import controller.input.KeyHandler;
 import controller.tiles.interactive.*;
 import controller.tiles.interactive.upgradeable.*;
+import controller.ui.ShopController;
 import model.GameModel;
 import model.GameSaver;
+import model.Inventory;
 import model.entity.EntityModel;
+import model.entity.EntityType;
 import model.items.Item;
 import model.items.ITEM_NAME;
 import model.tiles.InteractiveTileModel;
+import model.tiles.InteractiveTileType;
 import util.GAMESTATE;
 import view.GamePanel;
-import view.tile.interactive.*;
+import view.sound.SoundManager;
 
-import java.awt.*;
 import java.io.Serializable;
 import java.util.ArrayList;
 
+/**
+ * Controls the main game loop, input handling, entities, and interactive tiles.
+ */
 public class GameController implements Runnable, Serializable {
+
     public final String worldPath = "gamefiles/worlds/world_0.txt";
     private GameModel gameModel;
     private boolean running = true;
@@ -27,188 +37,444 @@ public class GameController implements Runnable, Serializable {
 
     private transient KeyHandler keyHandler;
     private transient Thread gameThread;
+    private transient RessourceManager ressourceManager;
     private transient GamePanel view;
+    private transient ShopController shopController;
     public transient ArrayList<EntityController> entityControllers;
     public transient ArrayList<InteractiveTileController> interactiveTileControllers;
+    private transient boolean gameActive;
 
+    /**
+     * Constructs a GameController, sets up model, view, input, inventory, entities, and interactive tiles.
+     */
     public GameController() {
-        this.keyHandler = new KeyHandler(this);
-        this.gameModel = new GameModel();
-        this.view = new GamePanel(this);
-        entityControllers = new ArrayList<>();
-        interactiveTileControllers = new ArrayList<>();
-        setStartInventory();
-        setupInteractiveTiles();
-        setupEntities();
+        initModel();
+        initView();
+        initInputHandler();
+        initControllerLists();
+        initializeStartInventory();
+        initializeInteractiveTiles();
+        initializeEntities();
+        initResourceManager();
+        initShopController();
         view.loadMap(worldPath);
     }
 
-    private void setupInteractiveTiles() {
-        LighthouseController lighthouse = new LighthouseController(this, setupInteractiveTileModel(20, 20));
-        addInteractiveTile(lighthouse);
+    /**
+     * Creates a new ShopController
+     */
+    private void initShopController() {
+        shopController = new ShopController(this);
+    }
 
-        MineController mineController = new MineController(this, setupInteractiveTileModel(25,20));
-        addInteractiveTile(mineController);
+    /**
+     * Creates a new ResourceManager
+     */
+    private void initResourceManager() {
+        ressourceManager = new RessourceManager(this);
+        ressourceManager.start();
+    }
 
-        BarracksController barracksController = new BarracksController(this, setupInteractiveTileModel(26, 20));
-        addInteractiveTile(barracksController);
+    /**
+     * Initializes the GameModel.
+     */
+    private void initModel() {
+        this.gameModel = new GameModel();
+    }
 
-        IronMineController ironMineController = new IronMineController(this, setupInteractiveTileModel(27, 20));
-        addInteractiveTile(ironMineController);
+    /**
+     * Initializes the GamePanel view.
+     */
+    private void initView() {
+        this.view = new GamePanel(this);
+    }
 
-        AnimalEnclosureController animal = new AnimalEnclosureController(this, setupInteractiveTileModel(28, 20));
-        addInteractiveTile(animal);
+    /**
+     * Initializes the key handler.
+     */
+    private void initInputHandler() {
+        this.keyHandler = new KeyHandler(this);
+    }
 
-        SawmillController sawmillController = new SawmillController(this, setupInteractiveTileModel(29, 20));
-        addInteractiveTile(sawmillController);
+    /**
+     * Initializes lists for entity and interactive tile controllers.
+     */
+    private void initControllerLists() {
+        entityControllers = new ArrayList<>();
+        interactiveTileControllers = new ArrayList<>();
     }
 
-    private void setupEntities(){
-        VillagerController villager = new VillagerController(this, setupEntityModel(22,20));
-        addEntity(villager);
+    /**
+     * Populates the starting inventory with basic items.
+     */
+    private void initializeStartInventory() {
+        Inventory inv = getModel().getInventory();
+        inv.addToInventory(new Item(ITEM_NAME.stone));
+        inv.addToInventory(new Item(ITEM_NAME.wood));
     }
 
-    private void addEntity(EntityController entityController) {
+    /**
+     * Sets up predefined interactive tiles in the world.
+     */
+    private void initializeInteractiveTiles() {
+        addInteractiveTileController(new LighthouseController(this,
+                createInteractiveTileModel(20, 20, InteractiveTileType.LIGHTHOUSE)));
+        addInteractiveTileController(new MineController(this,
+                createInteractiveTileModel(25, 20, InteractiveTileType.MINE)));
+        addInteractiveTileController(new BarracksController(this,
+                createInteractiveTileModel(26, 20, InteractiveTileType.BARRACKS)));
+        addInteractiveTileController(new IronMineController(this,
+                createInteractiveTileModel(27, 20, InteractiveTileType.IRON_MINE)));
+        addInteractiveTileController(new AnimalEnclosureController(this,
+                createInteractiveTileModel(28, 20, InteractiveTileType.ANIMAL_ENCLOSURE)));
+        addInteractiveTileController(new SawmillController(this,
+                createInteractiveTileModel(29, 20, InteractiveTileType.SAWMILL)));
+    }
+
+    /**
+     * Sets up predefined entities in the world.
+     */
+    private void initializeEntities() {
+        addEntityController(new VillagerController(this,
+                createEntityModel(22, 20, EntityType.VILLAGER)));
+    }
+
+    /**
+     * Adds an EntityController to the controller list.
+     *
+     * @param entityController the entity controller to add
+     */
+    private void addEntityController(EntityController entityController) {
         entityControllers.add(entityController);
     }
 
-    private void addInteractiveTile(InteractiveTileController controller) {
-        interactiveTileControllers.add(controller);
+    /**
+     * Adds an InteractiveTileController to the controller list.
+     *
+     * @param tileController the interactive tile controller to add
+     */
+    public void addInteractiveTileController(InteractiveTileController tileController) {
+        interactiveTileControllers.add(tileController);
     }
 
-    private EntityModel setupEntityModel(int worldX, int worldY) {
-        EntityModel model = new EntityModel(worldX, worldY, getView().tileSize, getView().tileSize);
-        gameModel.addEntityModel(model);
+    /**
+     * Creates and registers an EntityModel in the GameModel.
+     *
+     * @param worldX X position in world grid
+     * @param worldY Y position in world grid
+     * @param type   type of entity
+     * @return the created EntityModel
+     */
+    private EntityModel createEntityModel(int worldX, int worldY, EntityType type) {
+        int size = getView().tileSize;
+        EntityModel model = new EntityModel(worldX, worldY, size, size, type);
+        getModel().addEntityModel(model);
         return model;
     }
 
-    public InteractiveTileModel setupInteractiveTileModel(int worldGridX, int worldGridY){
-        InteractiveTileModel model = new InteractiveTileModel(worldGridX, worldGridY);
-        gameModel.addInteractiveTileModel(model);
+    /**
+     * Creates and registers an InteractiveTileModel in the GameModel.
+     *
+     * @param worldGridX X grid coordinate
+     * @param worldGridY Y grid coordinate
+     * @param type       type of interactive tile
+     * @return the created InteractiveTileModel
+     */
+    public InteractiveTileModel createInteractiveTileModel(int worldGridX, int worldGridY, InteractiveTileType type) {
+        InteractiveTileModel model = new InteractiveTileModel(worldGridX, worldGridY, type);
+        getModel().addInteractiveTileModel(model);
         return model;
     }
-    private void setStartInventory() {
-        gameModel.getInventory().addToInventory(new Item(ITEM_NAME.stone));
-        gameModel.getInventory().addToInventory(new Item(ITEM_NAME.water));
-        gameModel.getInventory().addToInventory(new Item(ITEM_NAME.wood));
-    }
 
-    public void setView(GamePanel gp){
-        view = gp;
+    /**
+     * Sets the GamePanel view.
+     *
+     * @param gp the GamePanel to set
+     */
+    public void setView(GamePanel gp) {
+        this.view = gp;
     }
 
+    /**
+     * Handles a click on the inventory UI.
+     *
+     * @param x screen x-coordinate of click
+     * @param y screen y-coordinate of click
+     */
     public void handleInventoryClick(int x, int y) {
+        SoundManager.getInstance().playSound(SoundManager.SOUNDS.CLICK);
         int slot = view.getInventoryView().getClickedInventorySlot(x, y);
-        gameModel.getInventory().select(slot);
+        getModel().getInventory().select(slot);
     }
 
+    /**
+     * Handles a click on the shop UI.
+     *  @param x screen x-coordinate of click
+     * @param y screen y-coordinate of click
+     * @return
+     */
+    public void handleShopClick(int x, int y) {
+        InteractiveTileController controller = view.getShopView().getClickedOffer(x, y);
+        if(controller != null) SoundManager.getInstance().loopSound(SoundManager.SOUNDS.MOVING_BUSH);
+        shopController.setDraggingOffer(controller);
+    }
+
+    /**
+     * Starts the main game loop and resource manager thread.
+     */
     public void startGameLoop() {
-        gameThread = new Thread(this);
+        gameActive = true;
+        gameThread = new Thread(this, "GameLoopThread");
         gameThread.start();
     }
 
+    /**
+     * Stops the main game loop.
+     */
     public void stopGameLoop() {
-        running = false;
+        gameActive = false;
     }
 
+    /**
+     * Performs update logic once per frame.
+     * Updates camera or handles save state depending on game state.
+     */
     public void update() {
-        if (view.gameState == GAMESTATE.PLAY || view.gameState == GAMESTATE.INVENTORY) {
-            view.camera.update();
-        }
-        if(view.gameState == GAMESTATE.SAVE){
-            GameSaver.saveGame(this);
-            view.gameState = GAMESTATE.PLAY;
+        GAMESTATE state = view.gameState;
+        if (state == GAMESTATE.PLAY || state == GAMESTATE.INVENTORY) {
+            updateCamera();
+        } else if (state == GAMESTATE.SAVE) {
+            performSaveAndResume();
         }
+    }
+
+    /**
+     * Updates the camera position.
+     */
+    private void updateCamera() {
+        view.camera.update();
+    }
 
+    /**
+     * Saves the game and returns to PLAY state.
+     */
+    private void performSaveAndResume() {
+        GameSaver.saveGame(this);
+        view.gameState = GAMESTATE.PLAY;
     }
 
+    /**
+     * The main run loop implementing Runnable.
+     * Controls timing for updates and rendering.
+     */
     @Override
     public void run() {
+        runGameLoop();
+    }
+
+    /**
+     * Implements the game loop timing logic based on fps.
+     */
+    private void runGameLoop() {
         double drawInterval = 1_000_000_000.0 / fps;
         double delta = 0;
         long lastTime = System.nanoTime();
-        long currentTime;
         long timer = 0;
         int drawCount = 0;
 
         while (running) {
-            currentTime = System.nanoTime();
+            long currentTime = System.nanoTime();
             delta += (currentTime - lastTime) / drawInterval;
             timer += (currentTime - lastTime);
             lastTime = currentTime;
 
             if (delta >= 1) {
-                update();           // Logic update
-                view.repaint();     // Triggers paintComponent in view
-                moveEntities();
+                if(gameActive){
+                    update();
+                    moveAllEntities();
+                }
+                view.repaint();
                 delta--;
                 drawCount++;
             }
 
             if (timer >= 1_000_000_000) {
+                // Could log fps: drawCount
                 drawCount = 0;
                 timer = 0;
             }
         }
     }
 
-    private void moveEntities() {
-        for(EntityController entityController : entityControllers){
+    /**
+     * Invokes movement logic on all entity controllers.
+     */
+    private void moveAllEntities() {
+        if (entityControllers == null) return;
+        for (EntityController entityController : entityControllers) {
             entityController.convertRepaintSpeedToMovementLogic();
         }
     }
 
-    public void zoomInOut(int i) {
-        view.zoomInOut(i);
+    /**
+     * Zooms camera in or out and closes any open menus.
+     *
+     * @param amount positive to zoom in, negative to zoom out
+     */
+    public void zoomInOut(int amount) {
+        view.zoomInOut(amount);
         view.closeMenus();
     }
 
-    public void togglePauseOrExitInventory() {
-        if (view.gameState == GAMESTATE.INVENTORY) {
+    /**
+     * Toggles between PLAY, PAUSED or exits other menus.
+     */
+    public void handleEscape() {
+        GAMESTATE state = view.gameState;
+        if (    state == GAMESTATE.INVENTORY
+                || state == GAMESTATE.PAUSED
+                || state == GAMESTATE.SHOP) {
+
             view.gameState = GAMESTATE.PLAY;
-        } else if (view.gameState == GAMESTATE.PLAY) {
+
+        } else if (state == GAMESTATE.PLAY) {
             view.gameState = GAMESTATE.PAUSED;
-        } else if (view.gameState == GAMESTATE.PAUSED) {
-            view.gameState = GAMESTATE.PLAY;
         }
     }
 
+    /**
+     * Toggles the inventory UI: enters or exits INVENTORY state.
+     * Deselects inventory item when closing.
+     */
     public void toggleInventory() {
         if (view.gameState == GAMESTATE.INVENTORY) {
             view.gameState = GAMESTATE.PLAY;
-            gameModel.getInventory().deselect();
-        } else if (view.gameState == GAMESTATE.PLAY) {
+            getModel().getInventory().deselectAll();
+        } else
             view.gameState = GAMESTATE.INVENTORY;
-        }
     }
-    public GamePanel getView(){
-        if(view == null){
-            setView(new GamePanel(this));
+
+    /**
+     * Returns the GamePanel view, creating if necessary.
+     *
+     * @return the GamePanel
+     */
+    public GamePanel getView() {
+        if (view == null) {
+            view = new GamePanel(this);
         }
         return view;
     }
 
+    /**
+     * Returns the GameModel, creating if necessary.
+     *
+     * @return the GameModel
+     */
     public GameModel getModel() {
-        if(gameModel == null) gameModel = new GameModel();
+        if (gameModel == null) {
+            gameModel = new GameModel();
+        }
         return gameModel;
     }
 
+    /**
+     * Returns the KeyHandler, creating if necessary.
+     *
+     * @return the KeyHandler
+     */
     public KeyHandler getKeyHandler() {
-        if(keyHandler == null) keyHandler = new KeyHandler(this);
+        if (keyHandler == null) {
+            keyHandler = new KeyHandler(this);
+        }
         return keyHandler;
     }
 
+    /**
+     * Loads the world from the default worldPath.
+     */
+    public void loadWorld() {
+        if (view == null) {
+            view = new GamePanel(this);
+        }
+        view.loadMap(worldPath);
+    }
+
+    /**
+     * Asks the view to generate a new world.
+     */
+    public void generateNewWorld() {
+        getView().generateNewWorld();
+    }
+
+    /**
+     * Entry point for dragging movement: called from GameMouseListener.mouseDragged when right mouse button.
+     * dx, dy are screen pixel deltas.
+     */
+    public void handleTileShift(int dx, int dy) {
+        getView().tileManager.handleTileShift(dx, dy);
+    }
+
+    /**
+     * Called from GameMouseListener.mouseReleased when ending a drag.
+     * Snaps the tile to grid, validates position, reverts if invalid.
+     */
+    public void handleTileRelease() {
+        getView().tileManager.handleTileRelease();
+
+    }
+
+    /**
+     * Loads interactive tiles and entities from the saved GameModel.
+     */
+    public void loadGame() {
+        loadInteractiveTilesFromSave();
+        loadEntitiesFromSave();
+        initResourceManager();
+        initShopController();
+    }
 
-    public void loadInteractiveTiles() {
+    /**
+     * Re-initializes interactiveTileControllers from models in gameModel.
+     */
+    private void loadInteractiveTilesFromSave() {
         interactiveTileControllers = new ArrayList<>();
-        setupInteractiveTiles();
+        for (InteractiveTileModel model : getModel().getInteractiveTileModels()) {
+            InteractiveTileController controller = InteractiveTileFactory.createTile(this, model);
+            if (controller != null) {
+                addInteractiveTileController(controller);
+            } else {
+                System.err.println("Unknown tile type: " + model.getTileType());
+            }
+        }
     }
 
-    public void loadWorld(){
-        if(view == null) this.view = new GamePanel(this);
-        view.loadMap(worldPath);
+    /**
+     * Re-initializes entityControllers from models in gameModel.
+     */
+    private void loadEntitiesFromSave() {
+        entityControllers = new ArrayList<>();
+        for (EntityModel model : getModel().getEntityModels()) {
+            EntityController controller = EntityFactory.createEntity(this, model);
+            if (controller != null) {
+                addEntityController(controller);
+            } else {
+                System.err.println("Unknown entity type: " + model.getEntityType());
+            }
+        }
     }
-    public void generateNewWorld() {
-        view.generateNewWorld();
+
+    public boolean isRunning() {
+        return running;
+    }
+
+    public void addToInventory(Item collected) {
+        getModel().getInventory().addToInventory(collected);
+    }
+
+
+    public void toggleShop() {
+        getView().toggleShop();
     }
 }
+

+ 0 - 86
src/main/java/controller/GameMouseListener.java

@@ -1,86 +0,0 @@
-package controller;
-
-import util.GAMESTATE;
-import view.Camera;
-import view.GamePanel;
-
-import javax.imageio.ImageIO;
-import javax.tools.Tool;
-import java.awt.*;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.awt.event.MouseMotionListener;
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-
-public class GameMouseListener implements MouseListener, MouseMotionListener {
-    private int startX;
-    private int startY;
-
-    Camera camera;
-    GamePanel gamePanel;
-    GameController controller;
-
-    public GameMouseListener(GameController controller, GamePanel gamePanel, Camera camera) {
-        this.controller = controller;
-        this.gamePanel = gamePanel;
-        this.camera = camera;
-    }
-    @Override
-    public void mouseDragged(MouseEvent e) {
-        if(gamePanel.gameState == GAMESTATE.PAUSED) return;
-        if(gamePanel.gameState == GAMESTATE.INVENTORY){
-
-        }else{
-
-            int diffY = startY - e.getY();
-            int diffX = startX - e.getX();
-
-            camera.move(diffX, diffY);
-            gamePanel.closeMenus();
-
-            startX = e.getX();
-            startY = e.getY();
-        }
-    }
-
-    @Override
-    public void mouseMoved(MouseEvent e) {
-
-    }
-
-    @Override
-    public void mouseClicked(MouseEvent e) {
-        if(gamePanel.gameState == GAMESTATE.PLAY){
-            gamePanel.ui.handleClick(e.getX(), e.getY());
-        }else if (gamePanel.gameState == GAMESTATE.INVENTORY && e.getButton() == MouseEvent.BUTTON1) {
-            controller.handleInventoryClick(e.getX(), e.getY());
-        }else if(gamePanel.gameState == GAMESTATE.INVENTORY && e.getButton() == 1){
-            controller.handleInventoryClick(e.getX(), e.getY());
-        }else if(gamePanel.gameState == GAMESTATE.PAUSED){
-            gamePanel.ui.handleMenuClick(e.getX(), e.getY());
-        }
-    }
-
-    @Override
-    public void mousePressed(MouseEvent e) {
-        startX = e.getX();
-        startY = e.getY();
-    }
-
-    @Override
-    public void mouseReleased(MouseEvent e) {
-
-    }
-
-    @Override
-    public void mouseEntered(MouseEvent e) {
-
-    }
-
-    @Override
-    public void mouseExited(MouseEvent e) {
-
-    }
-}

+ 0 - 4
src/main/java/controller/InputHandler.java

@@ -1,4 +0,0 @@
-package controller;
-
-public class InputHandler {
-}

+ 2 - 0
src/main/java/controller/InteractiveController.java

@@ -19,4 +19,6 @@ public interface InteractiveController {
     void click(int screenX, int screenY);
 
     void resize(int newTileSize);
+
+    void move(int screenX, int screenY);
 }

+ 0 - 4
src/main/java/controller/MenuController.java

@@ -1,4 +0,0 @@
-package controller;
-
-public class MenuController {
-}

+ 58 - 0
src/main/java/controller/RessourceManager.java

@@ -0,0 +1,58 @@
+package controller;
+
+import controller.tiles.interactive.InteractiveTileController;
+import util.GAMESTATE;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+
+public class RessourceManager {
+    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
+    private final GameController gameController;
+
+    public RessourceManager(GameController gameController) {
+        this.gameController = gameController;
+    }
+
+    public void start() {
+        scheduler.scheduleAtFixedRate(() -> {
+            if (!gameController.isRunning()) {
+                scheduler.shutdown();
+                return;
+            }
+
+            if(gameController.getView().gameState == GAMESTATE.INVENTORY || gameController.getView().gameState == GAMESTATE.PLAY)
+            calculateResources();
+        }, 0, 1, TimeUnit.SECONDS);
+    }
+
+    public void stop() {
+        scheduler.shutdownNow();
+    }
+
+    private void calculateResources() {
+        // If interactiveTileControllers can change concurrently, consider making a snapshot:
+        List<InteractiveTileController> snapshot;
+
+        synchronized(gameController.interactiveTileControllers) {
+            snapshot = new ArrayList<>(gameController.interactiveTileControllers);
+        }
+        for (InteractiveTileController c : snapshot) {
+            int spI = c.getModel().getSecondsPerItem();
+            // Avoid division by zero or spI <= 0:
+            if (spI > 0) {
+                // You need to track elapsed seconds per controller or use a shared seconds counter:
+                // For simplicity, you could store a counter in each model:
+                c.getModel().incrementSecondCounter();
+                if (c.getModel().getSecondCounter() % spI == 0) {
+                    c.getModel().addItem();
+                    System.out.println("New amount: " + c.getModel().getItemAmount());
+                }
+            }
+        }
+    }
+}

+ 6 - 1
src/main/java/controller/entity/EntityController.java

@@ -3,9 +3,11 @@ package controller.entity;
 import controller.GameController;
 import controller.InteractiveController;
 import model.entity.EntityModel;
+import util.GAMESTATE;
 import view.Camera;
 import view.entity.EntityView;
 import view.popUpMenu.PopupMenu;
+import view.sound.SoundManager;
 import view.tile.ONCLICKTYPE;
 
 import java.awt.*;
@@ -67,6 +69,7 @@ public abstract class EntityController implements InteractiveController {
     }
 
     public void click(int screenX, int screenY) {
+        SoundManager.getInstance().playSound(SoundManager.SOUNDS.VILLAGER);
         if(onclicktype == ONCLICKTYPE.RUNNABLE){
             view.runOnClick();
         }else{
@@ -119,7 +122,9 @@ public abstract class EntityController implements InteractiveController {
     }
     public void convertRepaintSpeedToMovementLogic() {
         if(updateCount >= gameController.fps){
-            findPath();
+            if(gameController.getView().gameState == GAMESTATE.PLAY || gameController.getView().gameState == GAMESTATE.INVENTORY){
+                findPath();
+            }
             updateCount = 0;
         }else{
             updateCount++;

+ 3 - 1
src/main/java/controller/entity/VillagerController.java

@@ -7,7 +7,9 @@ import view.entity.VillagerView;
 import view.popUpMenu.EntityMenu;
 import view.popUpMenu.PopupMenu;
 
-public class VillagerController extends EntityController{
+import java.io.Serializable;
+
+public class VillagerController extends EntityController {
     private int amount = 1;
     public VillagerController(GameController controller, EntityModel model){
         super(model, null, controller);

+ 19 - 0
src/main/java/controller/factories/EntityFactory.java

@@ -0,0 +1,19 @@
+package controller.factories;
+
+import controller.GameController;
+import controller.entity.EntityController;
+import controller.entity.VillagerController;
+import controller.tiles.interactive.upgradeable.*;
+import model.entity.EntityModel;
+import model.entity.EntityType;
+
+public class EntityFactory {
+    public static EntityController createEntity(GameController controller, EntityModel model) {
+        EntityType type = model.getEntityType();
+
+        return switch (type) {
+            case VILLAGER -> new VillagerController(controller, model);
+            default -> null;
+        };
+    }
+}

+ 23 - 0
src/main/java/controller/factories/InteractiveTileFactory.java

@@ -0,0 +1,23 @@
+package controller.factories;
+
+import controller.GameController;
+import controller.tiles.interactive.InteractiveTileController;
+import controller.tiles.interactive.upgradeable.*;
+import model.tiles.InteractiveTileModel;
+import model.tiles.InteractiveTileType;
+
+public class InteractiveTileFactory {
+    public static InteractiveTileController createTile(GameController controller, InteractiveTileModel model) {
+        InteractiveTileType type = model.getTileType();
+
+        return switch (type) {
+            case LIGHTHOUSE -> new LighthouseController(controller, model);
+            case MINE -> new MineController(controller, model);
+            case BARRACKS -> new BarracksController(controller, model);
+            case IRON_MINE -> new IronMineController(controller, model);
+            case ANIMAL_ENCLOSURE -> new AnimalEnclosureController(controller, model);
+            case SAWMILL -> new SawmillController(controller, model);
+            default -> null;
+        };
+    }
+}

+ 107 - 0
src/main/java/controller/input/GameMouseListener.java

@@ -0,0 +1,107 @@
+package controller.input;
+
+import controller.GameController;
+import controller.tiles.interactive.InteractiveTileController;
+import util.GAMESTATE;
+import view.Camera;
+import view.GamePanel;
+import view.sound.SoundManager;
+import view.tile.TileManager;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+
+public class GameMouseListener implements MouseListener, MouseMotionListener {
+    private int lastY;
+    private int lastX;
+    Camera camera;
+    GamePanel gamePanel;
+    GameController controller;
+
+    public GameMouseListener(GameController controller, GamePanel gamePanel, Camera camera) {
+        this.controller = controller;
+        this.gamePanel = gamePanel;
+        this.camera = camera;
+    }
+    @Override
+    public void mouseDragged(MouseEvent e) {
+        if(gamePanel.gameState == GAMESTATE.INVENTORY || gamePanel.gameState == GAMESTATE.PLAY){
+            int diffY = e.getY() - lastX;
+            int diffX = e.getX() - lastY;
+
+            if(SwingUtilities.isLeftMouseButton(e)){
+
+                camera.move(diffX, diffY);
+                gamePanel.closeMenus();
+
+            }else if(SwingUtilities.isRightMouseButton(e)){
+                controller.handleTileShift(diffX, diffY);
+                gamePanel.closeMenus();
+            }
+            lastY = e.getX();
+            lastX = e.getY();
+        }
+
+    }
+
+    @Override
+    public void mouseMoved(MouseEvent e) {
+
+    }
+
+    @Override
+    public void mouseClicked(MouseEvent e) {
+        if(gamePanel.gameState == GAMESTATE.PLAY || gamePanel.gameState == GAMESTATE.INVENTORY){
+            gamePanel.ui.handleClick(e.getX(), e.getY());
+        }
+        if (gamePanel.gameState == GAMESTATE.INVENTORY && e.getButton() == MouseEvent.BUTTON1) {
+            controller.handleInventoryClick(e.getX(), e.getY());
+        }else{
+            gamePanel.handleMenusClicks(e.getX(), e.getY());
+        }
+    }
+
+    @Override
+    public void mousePressed(MouseEvent e) {
+        if (SwingUtilities.isRightMouseButton(e)) {
+            TileManager tm = controller.getView().tileManager;
+
+            int worldX = tm.screenToWorldX(e.getX());
+            int worldY = tm.screenToWorldY(e.getY());
+            InteractiveTileController controller = tm.getTileAt(worldX, worldY);
+
+            if(controller != null){
+                SoundManager.getInstance().loopSound(SoundManager.SOUNDS.MOVING_BUSH);
+            }
+
+            tm.setDraggingTile(controller);
+        }
+
+        if (gamePanel.gameState == GAMESTATE.SHOP && SwingUtilities.isRightMouseButton(e)) {
+            controller.handleShopClick(e.getX(), e.getY());
+        }
+
+        lastY = e.getX();
+        lastX = e.getY();
+    }
+
+    @Override
+    public void mouseReleased(MouseEvent e) {
+        if (SwingUtilities.isRightMouseButton(e)) {
+            controller.handleTileRelease();
+        }
+    }
+
+    @Override
+    public void mouseEntered(MouseEvent e) {
+
+    }
+
+    @Override
+    public void mouseExited(MouseEvent e) {
+
+    }
+}

+ 5 - 2
src/main/java/controller/KeyHandler.java → src/main/java/controller/input/KeyHandler.java

@@ -1,6 +1,8 @@
-package controller;
+package controller.input;
 
 
+import controller.GameController;
+
 import java.awt.event.KeyEvent;
 import java.awt.event.KeyListener;
 
@@ -27,8 +29,9 @@ public class KeyHandler implements KeyListener {
             case KeyEvent.VK_D -> rightPressed = true;
             case KeyEvent.VK_UP -> controller.zoomInOut(1);
             case KeyEvent.VK_DOWN -> controller.zoomInOut(-1);
-            case KeyEvent.VK_ESCAPE -> controller.togglePauseOrExitInventory();
+            case KeyEvent.VK_ESCAPE -> controller.handleEscape();
             case KeyEvent.VK_E -> controller.toggleInventory();
+            case KeyEvent.VK_R -> controller.toggleShop();
         }
     }
 

+ 3 - 1
src/main/java/controller/MouseWheelZoom.java → src/main/java/controller/input/MouseWheelZoom.java

@@ -1,4 +1,6 @@
-package controller;
+package controller.input;
+
+import controller.GameController;
 
 import java.awt.event.MouseWheelEvent;
 import java.awt.event.MouseWheelListener;

+ 12 - 4
src/main/java/controller/tiles/interactive/InteractiveTileController.java

@@ -5,13 +5,14 @@ import controller.InteractiveController;
 import model.tiles.InteractiveTileModel;
 import view.Camera;
 import view.popUpMenu.PopupMenu;
+import view.sound.SoundManager;
 import view.tile.ONCLICKTYPE;
 import view.tile.interactive.InteractiveTileView;
 
 import java.awt.*;
 
 public abstract class InteractiveTileController implements InteractiveController {
-    private GameController gameController;
+    protected GameController gameController;
     private InteractiveTileView view;
     private InteractiveTileModel model;
     protected ONCLICKTYPE onclicktype;
@@ -22,7 +23,7 @@ public abstract class InteractiveTileController implements InteractiveController
     public InteractiveTileController(InteractiveTileModel model, InteractiveTileView view, GameController gameController){
         this.view = view;
         this.model = model;
-        this.gameController =  gameController;
+        this.gameController = gameController;
     }
 
     public void setModel(InteractiveTileModel model){
@@ -58,10 +59,10 @@ public abstract class InteractiveTileController implements InteractiveController
     }
 
     public void click(int screenX, int screenY) {
+        SoundManager.getInstance().playSound(SoundManager.SOUNDS.CLICK);
         if(onclicktype == ONCLICKTYPE.RUNNABLE){
             view.runOnClick();
         }else{
-
             screenXClick = screenX;
             screenYClick = screenY;
             clicked = true;
@@ -88,6 +89,7 @@ public abstract class InteractiveTileController implements InteractiveController
     public int getTileSize(){
         return gameController.getView().tileSize;
     }
+
     public boolean getClicked(){
         return clicked;
     }
@@ -103,7 +105,13 @@ public abstract class InteractiveTileController implements InteractiveController
     public int getLevel() {
         return model.getLevel();
     }
-
+    public InteractiveTileView getView(){
+        return view;
+    }
+    public void move(int x, int y){
+        model.setWorldGridX(x);
+        model.setWorldGridY(y);
+    }
     @Override
     public void resize(int newTileSize) {
         view.resize(newTileSize);

+ 5 - 2
src/main/java/controller/tiles/interactive/upgradeable/IronMineController.java

@@ -20,11 +20,14 @@ public class IronMineController extends InteractiveTileController {
 
     @Override
     public void upgrade() {
-
+        boolean condition = false;
+        if(condition){
+            getModel().upgrade();
+        }
     }
 
     @Override
     public void collect() {
-
+        gameController.addToInventory(getModel().collect());
     }
 }

+ 2 - 5
src/main/java/controller/tiles/interactive/upgradeable/MineController.java

@@ -12,7 +12,7 @@ public class MineController extends InteractiveTileController {
 
     public MineController(GameController gc, InteractiveTileModel model) {
         super(model, null, gc);
-        setView(new MineView(1, 2,gc, this));
+        setView(new MineView(2, 2,gc, this));
         PopupMenu popup = new UpgradeMenu(100, 100, Translator.translate("popup.title.mine"), this);
         setPopupOnClick(popup);
     }
@@ -27,9 +27,6 @@ public class MineController extends InteractiveTileController {
 
     @Override
     public void collect() {
-        boolean condition = false;
-        if(condition){
-            getModel().collect();
-        }
+        gameController.addToInventory(getModel().collect());
     }
 }

+ 6 - 3
src/main/java/controller/tiles/interactive/upgradeable/SawmillController.java

@@ -12,7 +12,7 @@ public class SawmillController extends InteractiveTileController {
 
     public SawmillController(GameController gc, InteractiveTileModel model) {
         super(model, null, gc);
-        setView(new SawmillView(gc, this));
+        setView(new SawmillView(2,2,gc, this));
         PopupMenu popup = new UpgradeMenu(100, 100, Translator.translate("popup.title.sawmill"), this);
 
         setPopupOnClick(popup);
@@ -20,11 +20,14 @@ public class SawmillController extends InteractiveTileController {
 
     @Override
     public void upgrade() {
-
+        boolean condition = false;
+        if(condition){
+            getModel().upgrade();
+        }
     }
 
     @Override
     public void collect() {
-
+        gameController.addToInventory(getModel().collect());
     }
 }

+ 48 - 0
src/main/java/controller/ui/ShopController.java

@@ -0,0 +1,48 @@
+package controller.ui;
+
+import controller.GameController;
+import controller.factories.InteractiveTileFactory;
+import controller.tiles.interactive.InteractiveTileController;
+import model.Inventory;
+import model.items.ITEM_NAME;
+import model.tiles.InteractiveTileModel;
+import model.tiles.InteractiveTileType;
+import util.Translator;
+import util.economy.EconomyData;
+import util.economy.EconomyInfo;
+import view.tile.TileManager;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class ShopController {
+    private GameController gameController;
+    public ShopController(GameController gameController){
+        this.gameController = gameController;
+    }
+
+    public void setDraggingOffer(InteractiveTileController controller) {
+        EconomyData data = EconomyInfo.infoOf(controller.getModel().getTileType());
+        List<Map.Entry<ITEM_NAME, Integer>> entries = new ArrayList<>(data.getCosts().get(1).entrySet());
+
+        for(Map.Entry<ITEM_NAME, Integer> entry : entries){
+            Inventory inventory = gameController.getModel().getInventory();
+
+            if(!inventory.hasEnough(entry.getKey(), entry.getValue())){
+                return;
+            }else{
+                inventory.spend(entry.getKey(), entry.getValue());
+            }
+        }
+        if(controller != null){
+
+            gameController.addInteractiveTileController(controller);
+            gameController.getView().tileManager.setDraggingTile(controller);
+            gameController.getView().toggleShop();
+            //gameController.createInteractiveTileModel(1,1, InteractiveTileType.SAWMILL);
+            System.out.println(controller.getModel().getWorldGridX());
+        }
+    }
+}

+ 10 - 3
src/main/java/main/Main.java

@@ -3,7 +3,10 @@ package main;
 import controller.GameController;
 import model.GameSaver;
 import util.Translator;
+import util.economy.EconomyInfo;
 import view.GamePanel;
+import view.sound.MusicManager;
+import view.sound.SoundManager;
 
 import javax.swing.*;
 import java.io.IOException;
@@ -12,7 +15,12 @@ public class Main {
 
     public static void main(String[] args) throws IOException {
 
-        Translator.load("lang/de_de.json");
+        Translator.set_de_de();
+
+        EconomyInfo.load();
+        SoundManager.getInstance().loadAllSounds();
+        MusicManager mm = MusicManager.getInstance();
+        mm.playBackgroundMusic("sound/background/background.wav", true);
 
         GameController gameController = GameSaver.loadGame();
 
@@ -20,9 +28,8 @@ public class Main {
 
         GamePanel gamePanel = gameController.getView();
 
-        gameController.loadInteractiveTiles();
+        gameController.loadGame();
 
-        System.out.println("Starte Spiel...");
         JFrame window = new JFrame();
         window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
         window.setResizable(false);

+ 9 - 0
src/main/java/model/GameModel.java

@@ -10,6 +10,7 @@ public class GameModel implements Serializable {
     private Inventory inventory;
     private ArrayList<InteractiveTileModel> interactiveTileModels = new ArrayList<>();
     private ArrayList<EntityModel> entityModels = new ArrayList<>();
+
     public GameModel() {
         inventory = new Inventory();
     }
@@ -22,6 +23,14 @@ public class GameModel implements Serializable {
         interactiveTileModels.add(model);
     }
 
+    public ArrayList<InteractiveTileModel> getInteractiveTileModels() {
+        return interactiveTileModels;
+    }
+
+    public ArrayList<EntityModel> getEntityModels(){
+        return entityModels;
+    }
+
     public void addEntityModel(EntityModel model) {
         entityModels.add(model);
     }

+ 0 - 1
src/main/java/model/GameSaver.java

@@ -33,7 +33,6 @@ public class GameSaver {
             return controller;
         } catch (Exception e) {
             System.out.println("Failed to load save file. Starting a new game...");
-            e.printStackTrace();
             return new GameController();
         }
     }

+ 12 - 5
src/main/java/model/Inventory.java

@@ -45,16 +45,23 @@ public class Inventory implements Serializable {
 
     public void select(int clickedSlot) {
         if(clickedSlot != -1){
-            getItemList().forEach((item) -> {
-                item.setSelected(false);
-            });
-            getItemList().get(clickedSlot).setSelected();
+            boolean selected = getItemList().get(clickedSlot).isSelected();
+            deselectAll();
+            getItemList().get(clickedSlot).setSelected(!selected);
         }
     }
 
-    public void deselect() {
+    public void deselectAll() {
         getItemList().forEach((item) -> {
             item.setSelected(false);
         });
     }
+
+    public boolean hasEnough(ITEM_NAME key, int comparedTo) {
+        return itemHashMap.get(key).getCount() >= comparedTo;
+    }
+
+    public void spend(ITEM_NAME key, int value) {
+        itemHashMap.get(key).takeItems(value);
+    }
 }

+ 9 - 2
src/main/java/model/entity/EntityModel.java

@@ -3,8 +3,10 @@ package model.entity;
 import view.GamePanel;
 
 import java.awt.*;
+import java.io.Serializable;
 
-public class EntityModel {
+public class EntityModel implements Serializable {
+    private EntityType type;
     public GamePanel gp;
 
     private Image optionalImage;
@@ -16,11 +18,12 @@ public class EntityModel {
     private boolean clicked;
     private int height, width;
 
-    public EntityModel(double worldX, double worldY, int width, int height){
+    public EntityModel(double worldX, double worldY, int width, int height, EntityType type){
         this.worldX = (int) worldX;
         this.worldY = (int) worldY;
         this.width = width;
         this.height = height;
+        this.type = type;
     }
 
     public double getWorldGridX() {
@@ -63,4 +66,8 @@ public class EntityModel {
     public void updateWidthAndHeight(int newTileSize) {
         height = newTileSize;
     }
+
+    public EntityType getEntityType() {
+        return type;
+    }
 }

+ 5 - 0
src/main/java/model/entity/EntityType.java

@@ -0,0 +1,5 @@
+package model.entity;
+
+public enum EntityType {
+    VILLAGER
+}

+ 1 - 1
src/main/java/model/items/ITEM_NAME.java

@@ -5,5 +5,5 @@ import java.io.Serializable;
 public enum ITEM_NAME implements Serializable {
     wood,
     stone,
-    water,
+    water, iron,
 }

+ 3 - 1
src/main/java/model/items/Item.java

@@ -1,5 +1,7 @@
 package model.items;
 
+import util.Translator;
+
 import java.io.Serializable;
 
 public class Item implements Serializable {
@@ -32,7 +34,7 @@ public class Item implements Serializable {
         return itemName;
     }
     public String getItemNamePlusCount(){
-        return itemName + ": " + count;
+        return Translator.translate("item." + itemName) + ": " + count;
     }
     public void addCount(int count){
         this.count += count;

+ 68 - 3
src/main/java/model/tiles/InteractiveTileModel.java

@@ -1,21 +1,56 @@
 package model.tiles;
 
+import model.items.ITEM_NAME;
 import model.items.Item;
 
 import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
 
 public class InteractiveTileModel implements Serializable {
     protected int worldGridX, worldGridY;
     private boolean clicked;
     private int level;
-    private Item items;
+    private Item item;
+    private InteractiveTileType type;
+    private static final Map<ITEM_NAME, List<Integer>> secondsPerItem = Map.of(
+            ITEM_NAME.stone, List.of(2, 22, 18, 12),
+            ITEM_NAME.wood, List.of(2, 22, 18, 12),
+            ITEM_NAME.iron, List.of(2, 22, 18, 12)
+    );
+    private int secondsPassed;
 
-    public InteractiveTileModel(int worldGridX, int worldGridY) {
+    public InteractiveTileModel(int worldGridX, int worldGridY, InteractiveTileType type) {
         this.worldGridX = worldGridX;
         this.worldGridY = worldGridY;
         this.clicked = false;
         this.level = 1;
+        this.type = type;
+        setupItem();
     }
+    public String getItemNamePlusCount(){
+        return item.getItemNamePlusCount();
+    }
+
+    private void setupItem(){
+        switch (type){
+            case MINE -> item = new Item(ITEM_NAME.stone);
+            case IRON_MINE -> item = new Item(ITEM_NAME.iron);
+            case SAWMILL -> item = new Item(ITEM_NAME.wood);
+            default -> item = new Item(ITEM_NAME.water);
+        }
+    }
+    public int getSecondsPerItem(){
+        List<Integer> secondsPerItem = null;
+        switch (type){
+            case MINE -> secondsPerItem = InteractiveTileModel.secondsPerItem.get(ITEM_NAME.stone);
+            case IRON_MINE -> secondsPerItem = InteractiveTileModel.secondsPerItem.get(ITEM_NAME.iron);
+            case SAWMILL -> secondsPerItem = InteractiveTileModel.secondsPerItem.get(ITEM_NAME.wood);
+            default -> secondsPerItem = List.of(50,50,50,50,50);
+        }
+        return secondsPerItem.get(level-1);
+    }
+
     public int getLevel(){
         return level;
     }
@@ -45,7 +80,37 @@ public class InteractiveTileModel implements Serializable {
     }
 
     public Item collect() {
-        return items.takeItems(items.getCount());
+        return item.takeItems(item.getCount());
+    }
+
+    public void setWorldGridX(int snappedX) {
+        worldGridX = snappedX;
+    }
+
+    public void setWorldGridY(int snappedY) {
+        worldGridY = snappedY;
     }
 
+    public InteractiveTileType getTileType() {
+        return type;
+    }
+
+    public void addItem() {
+        item.addCount(1);
+    }
+
+    public int getItemAmount() {
+        return item.getCount();
+    }
+
+    public void incrementSecondCounter() {
+        if(secondsPassed >= getSecondsPerItem()){
+            secondsPassed = 0;
+        }
+        secondsPassed ++;
+    }
+
+    public int getSecondCounter() {
+        return secondsPassed;
+    }
 }

+ 10 - 0
src/main/java/model/tiles/InteractiveTileType.java

@@ -0,0 +1,10 @@
+package model.tiles;
+
+public enum InteractiveTileType {
+    ANIMAL_ENCLOSURE,
+    BARRACKS,
+    IRON_MINE,
+    LIGHTHOUSE,
+    MINE,
+    SAWMILL,
+}

+ 1 - 1
src/main/java/util/GAMESTATE.java

@@ -5,5 +5,5 @@ public enum GAMESTATE {
     PAUSED,
     INVENTORY,
     QUIT,
-    SAVE
+    MAIN_MENU, SHOP, SETTINGS, SAVE
 }

+ 31 - 0
src/main/java/util/Settings.java

@@ -0,0 +1,31 @@
+package util;
+
+import view.sound.MusicManager;
+import view.sound.SoundManager;
+
+public class Settings {
+    private static float volume = 100;
+    private static String language = "DE";
+    private static boolean fullscreen = true;
+
+    public static float getVolume() { return volume; }
+
+    public static void toggleVolume() {
+        volume = (volume + 10) % 110; // 0–100
+        MusicManager.getInstance().setVolume(volume);
+        SoundManager.getInstance().setVolume(volume);
+    }
+
+    public static String getLanguage() { return language; }
+    public static void toggleLanguage() {
+
+        language = language.equals("DE") ? "EN" : "DE";
+        Translator.toggleLang(language);
+
+    }
+
+    public static boolean isFullscreen() { return fullscreen; }
+    public static void toggleFullscreen() {
+        fullscreen = !fullscreen;
+    }
+}

+ 15 - 1
src/main/java/util/Translator.java

@@ -6,9 +6,16 @@ import java.util.Map;
 
 public class Translator {
     private Translator(){};
-
     private static Map<String, String> translations = new HashMap<>();
 
+    public static void toggleLang(String language) {
+        if(language == "DE"){
+            set_de_de();
+        }else{
+            set_en_us();
+        }
+    }
+
     public static void load(String filePath){
         translations.clear();
         try (BufferedReader reader = new BufferedReader(new InputStreamReader(Translator.class.getClassLoader().getResourceAsStream(filePath)))) {
@@ -26,6 +33,13 @@ public class Translator {
             e.printStackTrace();
         }
     }
+
+    public static void set_de_de(){
+        load("lang/de_de.json");
+    }
+    public static void set_en_us(){
+        load("lang/en_us.json");
+    }
     public static String translate(String key) {
         return translations.getOrDefault(key, key); // fallback to key if missing
     }

+ 39 - 0
src/main/java/util/economy/EconomyData.java

@@ -0,0 +1,39 @@
+package util.economy;
+
+import model.items.ITEM_NAME;
+
+import java.util.Map;
+
+public class EconomyData {
+    private final Map<Integer, Map<ITEM_NAME, Integer>> levelCosts;
+    private final int maxLevel;
+    private final Map<Integer, Integer> lighthouseLevels;
+    private final Map<Integer, Integer> productionTimePerLevel;
+
+    public EconomyData(Map<Integer, Map<ITEM_NAME, Integer>> costs, int maxLevel, Map<Integer, Integer> lighthouseLevels, Map<Integer, Integer> productionTimePerLevel) {
+        this.levelCosts = costs;
+        this.maxLevel = maxLevel;
+        this.lighthouseLevels = lighthouseLevels;
+        this.productionTimePerLevel = productionTimePerLevel;
+    }
+
+    public Map<Integer, Map<ITEM_NAME, Integer>> getCosts() {
+        return levelCosts;
+    }
+
+    public int getMaxLevel() {
+        return maxLevel;
+    }
+
+    public Map<Integer, Integer> getRequiredLighthouseLevels() {
+        return lighthouseLevels;
+    }
+
+    public Map<Integer, Integer> getProductionTimePerLevel() {
+        return productionTimePerLevel;
+    }
+
+    public int getProductionTime(int level) {
+        return productionTimePerLevel.getOrDefault(level, -1);
+    }
+}

+ 226 - 0
src/main/java/util/economy/EconomyInfo.java

@@ -0,0 +1,226 @@
+package util.economy;
+
+import model.items.ITEM_NAME;
+import model.tiles.InteractiveTileType;
+
+import java.io.*;
+import java.util.*;
+
+public class EconomyInfo {
+    private static final String FILE_PATH = "/economy/economy.json";
+    private static final Map<InteractiveTileType, EconomyData> economyHashmap = new HashMap<>();
+
+    private EconomyInfo(){};
+
+    public static void load() {
+        economyHashmap.clear();
+        try (InputStream is = EconomyInfo.class.getResourceAsStream(FILE_PATH);
+             BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
+
+            StringBuilder jsonBuilder = new StringBuilder();
+            String line;
+            while ((line = reader.readLine()) != null) {
+                jsonBuilder.append(line);
+            }
+
+            String json = jsonBuilder.toString().trim();
+            if (json.startsWith("{") && json.endsWith("}")) {
+                json = json.substring(1, json.length() - 1);
+            }
+
+            parseTopLevel(json);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+    public static List<InteractiveTileType> getOffers(){
+        return economyHashmap.keySet().stream().toList();
+    }
+
+    private static void parseTopLevel(String json) {
+        int index = 0;
+        while (index < json.length()) {
+            int keyStart = json.indexOf('"', index);
+            if (keyStart == -1) break;
+
+            int keyEnd = json.indexOf('"', keyStart + 1);
+            String typeName = json.substring(keyStart + 1, keyEnd);
+
+            int dataStart = json.indexOf('{', keyEnd);
+            if (dataStart == -1) break;
+
+            int depth = 1;
+            int dataEnd = dataStart + 1;
+            while (dataEnd < json.length() && depth > 0) {
+                if (json.charAt(dataEnd) == '{') depth++;
+                else if (json.charAt(dataEnd) == '}') depth--;
+                dataEnd++;
+            }
+
+            if (depth != 0) break;
+
+            String dataJson = json.substring(dataStart, dataEnd);
+            parseTileData(InteractiveTileType.valueOf(typeName), dataJson);
+
+            index = dataEnd;
+        }
+    }
+
+    private static void parseTileData(InteractiveTileType tileType, String json) {
+        int maxLevel = extractIntValue(json, "maxLevel");
+        Map<Integer, Map<ITEM_NAME, Integer>> costs = extractNestedMap(json, "costs");
+        Map<Integer, Integer> lighthouseLevels = extractFlatMap(json, "lighthouseLevels");
+        Map<Integer, Integer> productionTimes = extractFlatMap(json, "productionTimeInSec");
+
+        economyHashmap.put(tileType, new EconomyData(
+                costs,
+                maxLevel,
+                lighthouseLevels,
+                productionTimes
+        ));
+    }
+
+    private static int extractIntValue(String json, String key) {
+        int keyIndex = json.indexOf("\"" + key + "\":");
+        if (keyIndex == -1) return -1;
+
+        int valueStart = json.indexOf(':', keyIndex) + 1;
+        int valueEnd = findValueEnd(json, valueStart);
+        String value = json.substring(valueStart, valueEnd).trim();
+
+        try {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+            return -1;
+        }
+    }
+
+    private static Map<Integer, Map<ITEM_NAME, Integer>> extractNestedMap(String json, String key) {
+        Map<Integer, Map<ITEM_NAME, Integer>> result = new HashMap<>();
+        String mapJson = extractSubJson(json, key);
+        if (mapJson == null) return result;
+
+        int index = 0;
+        while (index < mapJson.length()) {
+            int levelStart = mapJson.indexOf('"', index);
+            if (levelStart == -1) break;
+
+            int levelEnd = mapJson.indexOf('"', levelStart + 1);
+            int level = Integer.parseInt(mapJson.substring(levelStart + 1, levelEnd));
+
+            int innerStart = mapJson.indexOf('{', levelEnd);
+            if (innerStart == -1) break;
+
+            int innerDepth = 1;
+            int innerEnd = innerStart + 1;
+            while (innerEnd < mapJson.length() && innerDepth > 0) {
+                if (mapJson.charAt(innerEnd) == '{') innerDepth++;
+                else if (mapJson.charAt(innerEnd) == '}') innerDepth--;
+                innerEnd++;
+            }
+
+            if (innerDepth != 0) break;
+
+            String innerJson = mapJson.substring(innerStart + 1, innerEnd - 1);
+            Map<ITEM_NAME, Integer> innerMap = parseInnerMap(innerJson);
+            result.put(level, innerMap);
+
+            index = innerEnd;
+        }
+
+        return result;
+    }
+
+    private static Map<ITEM_NAME, Integer> parseInnerMap(String innerJson) {
+        Map<ITEM_NAME, Integer> map = new HashMap<>();
+        int index = 0;
+
+        while (index < innerJson.length()) {
+            int keyStart = innerJson.indexOf('"', index);
+            if (keyStart == -1) break;
+
+            int keyEnd = innerJson.indexOf('"', keyStart + 1);
+            String itemName = innerJson.substring(keyStart + 1, keyEnd);
+
+            int valueStart = innerJson.indexOf(':', keyEnd) + 1;
+            int valueEnd = findValueEnd(innerJson, valueStart);
+            String valueStr = innerJson.substring(valueStart, valueEnd).trim();
+
+            try {
+                ITEM_NAME item = ITEM_NAME.valueOf(itemName);
+                int amount = Integer.parseInt(valueStr);
+                map.put(item, amount);
+            } catch (IllegalArgumentException ignored) {
+            }
+
+            index = valueEnd + 1;
+        }
+
+        return map;
+    }
+
+    private static Map<Integer, Integer> extractFlatMap(String json, String key) {
+        Map<Integer, Integer> result = new HashMap<>();
+        String mapJson = extractSubJson(json, key);
+        if (mapJson == null) return result;
+
+        int index = 0;
+        while (index < mapJson.length()) {
+            int keyStart = mapJson.indexOf('"', index);
+            if (keyStart == -1) break;
+
+            int keyEnd = mapJson.indexOf('"', keyStart + 1);
+            int level = Integer.parseInt(mapJson.substring(keyStart + 1, keyEnd));
+
+            int valueStart = mapJson.indexOf(':', keyEnd) + 1;
+            int valueEnd = findValueEnd(mapJson, valueStart);
+            String valueStr = mapJson.substring(valueStart, valueEnd).trim();
+
+            try {
+                int value = Integer.parseInt(valueStr);
+                result.put(level, value);
+            } catch (NumberFormatException ignored) {
+            }
+
+            index = valueEnd + 1;
+        }
+
+        return result;
+    }
+
+    private static String extractSubJson(String json, String key) {
+        String searchKey = "\"" + key + "\":";
+        int keyIndex = json.indexOf(searchKey);
+        if (keyIndex == -1) return null;
+
+        int start = json.indexOf('{', keyIndex + searchKey.length());
+        if (start == -1) return null;
+
+        int depth = 1;
+        int end = start + 1;
+        while (end < json.length() && depth > 0) {
+            if (json.charAt(end) == '{') depth++;
+            else if (json.charAt(end) == '}') depth--;
+            end++;
+        }
+
+        if (depth != 0) return null;
+        return json.substring(start, end);
+    }
+
+    private static int findValueEnd(String json, int start) {
+        int end = start;
+        while (end < json.length()) {
+            char c = json.charAt(end);
+            if (c == ',' || c == '}' || c == ']') break;
+            end++;
+        }
+        return end;
+    }
+
+    public static EconomyData infoOf(InteractiveTileType type) {
+        return economyHashmap.get(type);
+    }
+}
+

+ 15 - 12
src/main/java/view/Camera.java

@@ -1,11 +1,6 @@
 package view;
 
-import controller.KeyHandler;
-import view.entity.EntityView;
-
-import javax.imageio.ImageIO;
-import java.awt.*;
-import java.io.IOException;
+import controller.input.KeyHandler;
 
 public class Camera{
     private GamePanel gamePanel;
@@ -45,8 +40,8 @@ public class Camera{
         float halfViewHeight = viewHeight / 2f;
 
         // 4. Proposed new camera center position
-        float newWorldX = (float) (worldX + x);
-        float newWorldY = (float) (worldY + y);
+        float newWorldX = (float) (worldX - x);
+        float newWorldY = (float) (worldY - y);
 
         // 5. Clamp the camera to world bounds so no outside area is visible
         newWorldX = clamp(newWorldX, halfViewWidth, worldWidth - halfViewWidth);
@@ -68,16 +63,24 @@ public class Camera{
 
     public void update(){
         if(keyH.upPressed){
-            move(0, -speed);
+            move(0, speed);
         }
         if(keyH.downPressed){
-            move(0, speed);
+            move(0, -speed);
         }
         if(keyH.leftPressed){
-            move(-speed, 0);
+            move(speed, 0);
         }
         if(keyH.rightPressed){
-            move(speed, 0);
+            move(-speed, 0);
         }
     }
+
+    public int getX() {
+        return worldX;
+    }
+
+    public int getY() {
+        return worldY;
+    }
 }

+ 66 - 11
src/main/java/view/GamePanel.java

@@ -1,14 +1,21 @@
 package view;
 
 import controller.GameController;
-import controller.GameMouseListener;
-import controller.MouseWheelZoom;
+import controller.input.GameMouseListener;
+import controller.input.MouseWheelZoom;
 import model.GameSaver;
 import model.Inventory;
 import view.entity.EntityManager;
 import view.popUpMenu.PopupManager;
 import view.tile.TileManager;
 import util.GAMESTATE;
+import view.ui.InventoryView;
+import view.ui.menu.AbstractMenu;
+import view.ui.menu.MainMenu;
+import view.ui.ShopView;
+import view.ui.UI;
+import view.ui.menu.PauseMenu;
+import view.ui.menu.SettingsMenu;
 
 import javax.swing.*;
 import java.awt.*;
@@ -18,7 +25,7 @@ import java.util.Arrays;
 public class GamePanel extends JPanel{
 
     //Screen Settings
-    final static int originalTileSize = 16;
+    public final int originalTileSize = 16;
     public final static int scale = 4;
 
     int fps = 60;
@@ -35,9 +42,6 @@ public class GamePanel extends JPanel{
     public final static int maxWorldCol = 100;
     public final static int maxWorldRow = 100;
 
-    public final int worldWidth = tileSize * maxWorldCol;
-    public final int worldHeight = tileSize * maxWorldRow;
-
     //System
     public UI ui = new UI(this);
     private Graphics2D g2;
@@ -45,23 +49,28 @@ public class GamePanel extends JPanel{
     public GAMESTATE gameState;
 
 
-
     public TileManager tileManager;
     public EntityManager entityManager;
     private PopupManager popupManager;
     private InventoryView inventoryView;
+    private ShopView shopView;
     public Camera camera;
-
+    private MainMenu mainMenu;
+    private SettingsMenu settingsMenu;
+    private PauseMenu pauseMenu;
 
 
     public GamePanel(GameController controller) {
 
         this.gameController = controller; // get from controller
         this.camera = new Camera(this, controller.getKeyHandler());
-
+        this.settingsMenu = new SettingsMenu(this);
         this.tileManager = new TileManager(this);
         this.entityManager = new EntityManager(this);
         this.popupManager = new PopupManager(this, new ArrayList<>(Arrays.asList(tileManager, entityManager)));
+        this.mainMenu = new MainMenu(this);
+        this.pauseMenu = new PauseMenu(this);
+        this.shopView = new ShopView(this);
 
         Inventory inventory = gameController.getModel().getInventory();
         this.inventoryView = new InventoryView(inventory);
@@ -100,7 +109,6 @@ public class GamePanel extends JPanel{
         if (newTileSize < minTileSize || newTileSize > maxTileSize) {
             return;
         }
-
         tileSize = newTileSize;
 
         int newWorldWidth = tileSize * maxWorldCol;
@@ -114,6 +122,7 @@ public class GamePanel extends JPanel{
 
         camera.worldX = (int)(camera.worldX * widthMultiplier);
         camera.worldY = (int)(camera.worldY * heightMultiplier);
+        camera.move(0,0);
     }
     public void closeMenus(){
         ui.closeMenus();
@@ -122,15 +131,21 @@ public class GamePanel extends JPanel{
     public InventoryView getInventoryView(){
         return inventoryView;
     }
+
     @Override
     public void paintComponent(Graphics g) {
         super.paintComponent(g);
         g2 = (Graphics2D) g;
 
+
+
         tileManager.draw(g2);
         entityManager.draw(g2);
         popupManager.drawPopups(g2);
 
+        if(gameState == GAMESTATE.SHOP){
+            shopView.drawOverlay(g2);
+        }
         if (gameState == GAMESTATE.INVENTORY) {
             inventoryView.drawInventoryOverlay(g2);
         }
@@ -139,11 +154,51 @@ public class GamePanel extends JPanel{
             GameSaver.saveGame(gameController);
             System.exit(0);
         }
-        ui.draw(g2);
+
+
+        if(gameState == GAMESTATE.PAUSED){
+            pauseMenu.draw(g2);
+        }else if(gameState == GAMESTATE.MAIN_MENU){
+            drawMenu(mainMenu, g2);
+        }else if(gameState == GAMESTATE.SETTINGS){
+            drawMenu(settingsMenu, g2);
+        }
         g2.dispose();
     }
 
+    private void drawMenu(AbstractMenu menu, Graphics2D g2){
+        menu.draw(g2);
+    }
+
     public void generateNewWorld() {
         tileManager.generateNewWorld();
     }
+
+    public double getZoom() {
+        return originalTileSize/tileSize;
+    }
+
+    public void toggleShop() {
+        closeMenus();
+        if(gameState == GAMESTATE.SHOP){
+            gameState = GAMESTATE.PLAY;
+        }else gameState = GAMESTATE.SHOP;
+    }
+
+    public ShopView getShopView() {
+        return shopView;
+    }
+
+    public void handleMenusClicks(int x, int y) {
+        if(gameState == GAMESTATE.MAIN_MENU){
+            mainMenu.handleClick(x,y);
+        }else if(gameState == GAMESTATE.SETTINGS){
+            settingsMenu.handleClick(x,y);
+        }else if(gameState == GAMESTATE.PAUSED){
+            pauseMenu.handleClick(x,y);
+        }
+
+    }
+
+
 }

+ 0 - 65
src/main/java/view/InventoryView.java

@@ -1,65 +0,0 @@
-package view;
-
-import model.Inventory;
-import util.TextUtil;
-import util.Translator;
-
-import java.awt.*;
-
-public class InventoryView {
-    private Inventory inventory;
-    int overlayX = 20;
-    int overlayY = 20;
-    int slotSize = 48;
-    int slotSpacing = 8;
-    int slotCount = 0;
-    public InventoryView(Inventory inventory){
-        this.inventory = inventory;
-    }
-    public void drawInventoryOverlay(Graphics2D g2) {
-        slotCount = inventory.getUniqueItemsCount();
-
-        int overlayWidth = slotSize + 2* slotSpacing;
-        int overlayHeight = (slotSize + slotSpacing) * slotCount + slotSpacing;
-
-        // Background panel
-        g2.setColor(new Color(0, 0, 0, 160)); // semi-transparent black
-        g2.fillRoundRect(overlayX, overlayY, overlayWidth, overlayHeight, 15, 15);
-
-        // Draw slots
-        for (int i = 0; i < slotCount; i++) {
-            if(inventory.getItemList().get(i).isSelected()){
-                g2.setColor(Color.WHITE);
-            }else{
-                g2.setColor(Color.LIGHT_GRAY);
-            }
-            int x = overlayX + slotSpacing;
-            int y = overlayY + slotSpacing + i * (slotSize + slotSpacing);
-            g2.fillRoundRect(x, y, slotSize, slotSize, 10, 10);
-
-            g2.setColor(Color.DARK_GRAY);
-
-            String itemName = inventory.getItemList().get(i).getItemName().toString();
-
-            TextUtil.setFontAppropriateToSize(itemName, slotSize - slotSpacing/2, g2);
-
-            g2.drawString(Translator.translate("item."+itemName), x + slotSpacing/4, y + slotSize / 2 + 5);
-            g2.drawString( "" + inventory.getItemList().get(i).getCount() , x + slotSize - slotSize/5, y + slotSize -2);
-
-            g2.setColor(Color.LIGHT_GRAY); // reset color for next slot
-        }
-    }
-    public int getClickedInventorySlot(int mouseX, int mouseY) {
-        for (int i = 0; i < slotCount; i++) {
-            int x = overlayX + slotSpacing;
-            int y = overlayY + slotSpacing + i * (slotSize + slotSpacing);
-            int width = slotSize;
-            int height = slotSize;
-
-            if (mouseX >= x && mouseX <= x + width && mouseY >= y && mouseY <= y + height) {
-                return i; // Slot i was clicked
-            }
-        }
-        return -1; // No slot was clicked
-    }
-}

+ 3 - 0
src/main/java/view/components/Button.java

@@ -1,5 +1,7 @@
 package view.components;
 
+import view.sound.SoundManager;
+
 import java.awt.*;
 import java.io.Serializable;
 
@@ -28,6 +30,7 @@ public class Button implements Serializable {
     }
 
     public void click(){
+        SoundManager.getInstance().playSound(SoundManager.SOUNDS.CLICK);
         runnable.run();
     }
     public void setOffset(int x, int y){

+ 3 - 0
src/main/java/view/entity/EntityManager.java

@@ -2,6 +2,7 @@ package view.entity;
 
 import controller.entity.EntityController;
 import model.entity.EntityModel;
+import util.GAMESTATE;
 import view.GamePanel;
 import view.util.RenderingManager;
 
@@ -19,12 +20,14 @@ public class EntityManager implements RenderingManager {
     }
 
     public void draw(Graphics2D g2){
+
         for (EntityController entity : gamePanel.gameController.entityControllers) {
             EntityModel model = entity.getModel();
             entity.updateCoordinates(worldColToScreenX(model.getWorldX()), worldRowToScreenY(model.getWorldY()));
             entity.drawSprite(g2);
         }
     }
+
     public int getTileSize(){
         return gamePanel.tileSize;
     }

+ 11 - 6
src/main/java/view/popUpMenu/UpgradeMenu.java

@@ -14,12 +14,14 @@ public class UpgradeMenu extends PopupMenu {
         super(width, height, title);
         this.controller = controller;
         addButton( new Button(80, 25, Translator.translate("popup.upgrade"), this::upgrade), 10, 40 );
-        addButton( new Button(80,25, Translator.translate("popup.collect"),this::collect), 10, 70);
+        addButton( new Button(80,25, Translator.translate("popup.collect"), this::collect), 10, 70);
     }
+
     @Override
     public void draw(int x, int y, Graphics2D g2){
         FontMetrics fm = g2.getFontMetrics();
-        int msgWidth = fm.stringWidth(title);
+        String title_with_level = title + " (" + controller.getLevel() + ")";
+        int msgWidth = fm.stringWidth(title_with_level);
         if(overlayWidth < msgWidth) overlayWidth = msgWidth + 20;
 
         centerButtons();
@@ -29,11 +31,14 @@ public class UpgradeMenu extends PopupMenu {
         g2.setColor(Color.WHITE);
         g2.drawRoundRect(x,y,overlayWidth,overlayHeight, 15, 15);
 
-        g2.drawString(title, x + ((overlayWidth - msgWidth )/2), y+ 20);
+        g2.drawString(title_with_level, x + ((overlayWidth - msgWidth )/2), y+ 20);
+
+        String amountStr = controller.getModel().getItemNamePlusCount();
+
+        int amountStrWidth = fm.stringWidth(amountStr);
+
+        g2.drawString(amountStr, x + ((overlayWidth - amountStrWidth )/2), y + 35);
 
-        String levelStr = "Level: " + controller.getLevel();
-        int lvlStrWidth = fm.stringWidth(levelStr);
-        g2.drawString(levelStr, x + ((overlayWidth - lvlStrWidth )/2), y+ 35);
         for (Button button : buttonArrayList){
             button.setRelativeScreenCoordinates(x,y);
             button.draw(g2);

+ 83 - 0
src/main/java/view/sound/MusicManager.java

@@ -0,0 +1,83 @@
+package view.sound;
+
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.Clip;
+import javax.sound.sampled.FloatControl;
+import java.net.URL;
+import java.util.EnumMap;
+
+public class MusicManager {
+    private static MusicManager instance;
+    private Clip musicClip;
+
+
+    private MusicManager() {
+    }
+
+    public static MusicManager getInstance() {
+        if (instance == null) {
+            instance = new MusicManager();
+        }
+        return instance;
+    }
+
+    public void playBackgroundMusic(String resourcePath, boolean loop) {
+        stop();
+        if(loop){
+            getBackgroundMusic(resourcePath).loop(Clip.LOOP_CONTINUOUSLY);
+        }else{
+            getBackgroundMusic(resourcePath).start();
+        }
+    }
+
+    private Clip getBackgroundMusic(String resourcePath){
+        try {
+            URL musicURL = getClass().getClassLoader().getResource(resourcePath);
+            if (musicURL == null) {
+                System.err.println("Music file not found: " + resourcePath);
+                return null;
+            }
+
+            AudioInputStream audioStream = AudioSystem.getAudioInputStream(musicURL);
+            musicClip = AudioSystem.getClip();
+            musicClip.open(audioStream);
+
+            return musicClip;
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public void setVolume(Clip clip, float percent) {
+        if (clip != null && clip.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
+
+            FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
+
+            float min = gainControl.getMinimum(); // usually -80.0f
+            float max = gainControl.getMaximum(); // usually 0.0f
+
+            // Clamp percent to 0–100
+            percent = Math.max(0, Math.min(100, percent));
+
+            // Convert percent to gain value
+            float range = max - min;
+            float gain = (range * percent / 100f) + min;
+            System.out.println("Set volume: " + gain + " dB for " + percent + "%");
+            gainControl.setValue(gain); // Safe
+        }
+    }
+
+    public void stop() {
+        if (musicClip != null) {
+            musicClip.stop();
+            musicClip.close();
+        }
+    }
+
+    public void setVolume(float volume) {
+        setVolume(musicClip, volume);
+    }
+}

+ 111 - 0
src/main/java/view/sound/SoundManager.java

@@ -0,0 +1,111 @@
+package view.sound;
+
+import javax.sound.sampled.*;
+import java.io.IOException;
+import java.net.URL;
+import java.util.EnumMap;
+
+public class SoundManager{
+    private static SoundManager instance;
+    private EnumMap<SOUNDS, Clip> soundMap;
+
+    public void stopLoopSound(SOUNDS sound) {
+        soundMap.get(sound).stop();
+    }
+
+    public void setVolume(Clip clip, float percent) {
+        if (clip.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
+            FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
+
+            float min = gainControl.getMinimum(); // usually -80.0f
+            float max = gainControl.getMaximum(); // usually 6.0f or 0.0f
+
+            // Convert percent (0–100) to decibel scale
+            float range = max - min;
+            float gain = (range * percent / 100f) + min;
+
+            gainControl.setValue(gain);
+        } else {
+            System.err.println("Volume control not supported for this clip.");
+        }
+    }
+    public void setVolume(float volume) {
+        soundMap.forEach((sound, clip) -> {
+            setVolume(clip, volume);
+        });
+    }
+
+    public enum SOUNDS {
+        CLICK,
+        MOVING_BUSH,
+        VILLAGER
+    }
+
+
+    private SoundManager() {
+        soundMap = new EnumMap<>(SOUNDS.class);
+    }
+
+    public void loadAllSounds(){
+        SoundManager.getInstance().loadSound(SoundManager.SOUNDS.CLICK, "sound/click.wav");
+        SoundManager.getInstance().loadSound(SoundManager.SOUNDS.VILLAGER, "sound/villager.wav");
+        SoundManager.getInstance().loadSound(SoundManager.SOUNDS.MOVING_BUSH, "sound/moving_bush.wav");
+    }
+
+    public void loopSound(SOUNDS sound){
+        Clip clip = soundMap.get(sound);
+        if(clip != null){
+            if(clip.isRunning()){
+                clip.stop();
+            }else{
+                clip.loop(Clip.LOOP_CONTINUOUSLY);
+            }
+        }
+
+    }
+
+    public static SoundManager getInstance() {
+        if (instance == null) {
+            instance = new SoundManager();
+        }
+        return instance;
+    }
+
+    public void loadSound(SOUNDS sound, String resourcePath) {
+        try {
+            URL soundURL = getClass().getClassLoader().getResource(resourcePath);
+            if (soundURL == null) {
+                System.err.println("Resource not found: " + resourcePath);
+                return;
+            }
+
+            AudioInputStream audioStream = AudioSystem.getAudioInputStream(soundURL);
+            Clip clip = AudioSystem.getClip();
+            clip.open(audioStream);
+            soundMap.put(sound, clip);
+        } catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
+            System.err.println("Failed to load sound " + sound + ": " + e.getMessage());
+        }
+    }
+
+    public void playSound(SOUNDS sound) {
+
+        Clip clip = soundMap.get(sound);
+        if (clip != null) {
+            if (clip.isRunning()) {
+                clip.stop();
+            }
+            clip.setFramePosition(0);
+            clip.start();
+        } else {
+            System.err.println("Sound " + sound + " wurde nicht geladen.");
+        }
+    }
+
+    public void unloadAllSounds() {
+        for (Clip clip : soundMap.values()) {
+            clip.close();
+        }
+        soundMap.clear();
+    }
+}

+ 177 - 9
src/main/java/view/tile/TileManager.java

@@ -1,13 +1,13 @@
 package view.tile;
 
-import controller.entity.EntityController;
 import controller.tiles.interactive.InteractiveTileController;
 import model.Tile;
-import model.entity.EntityModel;
 import model.tiles.BackgroundTile;
 import model.tiles.InteractiveTileModel;
 import util.WorldGenerator;
 import view.GamePanel;
+import view.sound.SoundManager;
+import view.tile.interactive.InteractiveTileView;
 import view.util.RenderingManager;
 
 import java.awt.*;
@@ -15,10 +15,12 @@ import java.io.*;
 
 public class TileManager implements RenderingManager {
 
-    GamePanel gamePanel;
-    public Tile[] tile;
-    public int[][] mapTileNum;
-    public int mapTileOverflow = 1;
+    private GamePanel gamePanel;
+    private Tile[] tile;
+    private int[][] mapTileNum;
+    private int mapTileOverflow = 1;
+    private InteractiveTileController draggingTile;
+    private int originalGridX, originalGridY;
 
     public TileManager(GamePanel gp){
         this.gamePanel = gp;
@@ -97,12 +99,42 @@ public class TileManager implements RenderingManager {
                 worldRow++;
             }
         }
+        drawAllTiles(g2);
+    }
+    /**
+     * Entry point for dragging movement: called from GameMouseListener.mouseDragged when right mouse button.
+     * dx, dy are screen pixel deltas.
+     */
+    public void handleTileShift(int dx, int dy) {
+
+        if (draggingTile == null) {
+            return;
+        }
+        // Smooth pixel-based move: update view's screen coordinates by delta
+        InteractiveTileView view = draggingTile.getView();
+        view.setScreenCoordinates(view.getScreenX() + dx, view.getScreenY() + dy);
+    }
+
+    /**
+     * Drawing loop: update view coords from model for non-dragged tiles; draw all.
+     */
+    public void drawAllTiles(Graphics2D g2) {
+        // Optionally draw non-dragged first, then dragged to render on top
         for (InteractiveTileController tile : gamePanel.gameController.interactiveTileControllers) {
-            InteractiveTileModel model = tile.getModel();
-            tile.updateCoordinates(worldColToScreenX(model.getWorldGridX()), worldRowToScreenY(model.getWorldGridY()));
-            tile.drawTile(g2);
+            if (!isTileDragged(tile)) {
+                InteractiveTileModel model = tile.getModel();
+                int sx = worldColToScreenX(model.getWorldGridX());
+                int sy = worldRowToScreenY(model.getWorldGridY());
+                tile.getView().setScreenCoordinates(sx, sy);
+                tile.drawTile(g2);
+            }
+        }
+        if (getDraggedTile() != null) {
+            // Draw dragged tile on top
+            getDraggedTile().drawTile(g2);
         }
     }
+
     public int worldColToScreenX(int worldCol) {
         double worldX = worldCol * gamePanel.tileSize; // Reactively use tileSize
         return (int) (worldX - gamePanel.camera.worldX + gamePanel.camera.screenX);
@@ -112,6 +144,7 @@ public class TileManager implements RenderingManager {
         double worldY = worldRow * gamePanel.tileSize; // Reactively use tileSize
         return (int) (worldY - gamePanel.camera.worldY + gamePanel.camera.screenY);
     }
+
     public void getTileImage(){
         try{
             setupTile(0, "grass");
@@ -155,5 +188,140 @@ public class TileManager implements RenderingManager {
             tile.resize(newTileSize);
         }
     }
+
+    public void handleTileRelease() {
+        SoundManager.getInstance().stopLoopSound(SoundManager.SOUNDS.MOVING_BUSH);
+
+        if (draggingTile == null) {
+            return;
+        }
+        InteractiveTileView view = draggingTile.getView();
+        InteractiveTileModel model = draggingTile.getModel();
+
+        int tileSize = gamePanel.tileSize;
+        int widthTiles = view.getScaleX();
+        int heightTiles = view.getScaleY();
+
+        // Compute the screen coordinates of the tile's top-left
+        int dropScreenX = view.getScreenX();
+        int dropScreenY = view.getScreenY();
+        // Compute the center pixel position of the tile
+        int centerScreenX = dropScreenX + (widthTiles * tileSize) / 2;
+        int centerScreenY = dropScreenY + (heightTiles * tileSize) / 2;
+        // Convert center to world grid coordinate (floor)
+        int centerGridX = screenToWorldX(centerScreenX);
+        int centerGridY = screenToWorldY(centerScreenY);
+        // Compute top-left grid position so that tile center aligns to centerGrid
+        int targetGridX = centerGridX - (widthTiles / 2);
+        int targetGridY = centerGridY - (heightTiles / 2);
+
+        if (isValidPosition(targetGridX, targetGridY)) {
+            model.setWorldGridX(targetGridX);
+            model.setWorldGridY(targetGridY);
+        } else {
+            // Revert to original grid position
+            model.setWorldGridX(originalGridX);
+            model.setWorldGridY(originalGridY);
+        }
+        // After updating model, reset view’s screen coords to the snapped grid position
+        int snappedScreenX = worldColToScreenX(model.getWorldGridX());
+        int snappedScreenY = worldRowToScreenY(model.getWorldGridY());
+        view.setScreenCoordinates(snappedScreenX, snappedScreenY);
+
+        // Clear dragging state
+        draggingTile = null;
+    }
+    /**
+     * Checks if draggingTile can be placed at new grid coordinates without out-of-bounds or collision.
+     *
+     * @param newX target grid X
+     * @param newY target grid Y
+     * @return true if valid position
+     */
+    private boolean isValidPosition(int newX, int newY) {
+        if (draggingTile == null) {
+            return false;
+        }
+        InteractiveTileModel model = draggingTile.getModel();
+        InteractiveTileView tileView = draggingTile.getView();
+        int width = tileView.getScaleX();
+        int height = tileView.getScaleY();
+
+        // Check world bounds
+        if (newX < 0 || newY < 0
+                || newX + width > GamePanel.maxWorldCol
+                || newY + height > GamePanel.maxWorldRow) {
+            return false;
+        }
+        // Check collisions
+        for (int x = newX; x < newX + width; x++) {
+            for (int y = newY; y < newY + height; y++) {
+                InteractiveTileController other = getTileAt(x, y);
+                if (other != null && other != draggingTile) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+    /**
+     * Returns the InteractiveTileController at the specified grid coordinates, or null if none.
+     *
+     * @param worldX grid X coordinate
+     * @param worldY grid Y coordinate
+     * @return the tile controller at that position, or null
+     */
+    public InteractiveTileController getTileAt(int worldX, int worldY) {
+        if (gamePanel.gameController.interactiveTileControllers == null) {
+            return null;
+        }
+        for (InteractiveTileController tile : gamePanel.gameController.interactiveTileControllers) {
+            InteractiveTileModel model = tile.getModel();
+            int tileX = model.getWorldGridX();
+            int tileY = model.getWorldGridY();
+            int width = tile.getView().getScaleX();
+            int height = tile.getView().getScaleY();
+
+            boolean withinX = (worldX >= tileX && worldX < tileX + width);
+            boolean withinY = (worldY >= tileY && worldY < tileY + height);
+            if (withinX && withinY) {
+                return tile;
+            }
+        }
+        return null;
+    }
+
+    // screenToWorldX/Y assumed unchanged
+    public int screenToWorldX(int screenX) {
+        return (gamePanel.camera.worldX + screenX - gamePanel.camera.screenX) / gamePanel.tileSize;
+    }
+
+    public int screenToWorldY(int screenY) {
+        return (gamePanel.camera.worldY + screenY - gamePanel.camera.screenY) / gamePanel.tileSize;
+    }
+    /**
+     *
+     */
+    public boolean isTileDragged(InteractiveTileController controller){
+        return draggingTile == controller;
+    }
+
+    public InteractiveTileController getDraggedTile() {
+        return draggingTile;
+    }
+
+    public void setDraggingTile(InteractiveTileController tile) {
+        draggingTile = tile;
+        if (draggingTile != null) {
+            InteractiveTileModel model = draggingTile.getModel();
+            originalGridX = model.getWorldGridX();
+            originalGridY = model.getWorldGridY();
+            // Optionally, ensure view's screen coords are set initially
+            InteractiveTileView view = draggingTile.getView();
+            int sx = worldColToScreenX(originalGridX);
+            int sy = worldRowToScreenY(originalGridY);
+            view.setScreenCoordinates(sx, sy);
+        }
+    }
 }
 

+ 1 - 1
src/main/java/view/tile/interactive/AnimalEnclosureView.java

@@ -7,6 +7,6 @@ public class AnimalEnclosureView extends InteractiveTileView {
 
     public AnimalEnclosureView(GameController gc, InteractiveTileController controller) {
         super( 1, 1, controller, gc.getView().tileManager);
-        setImage("/tiles/animalEnclosure/animalEnclosureTest.png");
+        setImage("/tiles/animal_enclosure/animal_enclosureTest.png");
     }
 }

+ 16 - 3
src/main/java/view/tile/interactive/InteractiveTileView.java

@@ -5,10 +5,7 @@ import model.Tile;
 import java.awt.*;
 import java.io.Serializable;
 
-import controller.GameController;
-import view.Camera;
 import view.popUpMenu.PopupMenu;
-import view.tile.ONCLICKTYPE;
 import view.tile.TileManager;
 
 public abstract class InteractiveTileView extends Tile implements Serializable {
@@ -69,10 +66,26 @@ public abstract class InteractiveTileView extends Tile implements Serializable {
         width = newTileSize * scaleX;
     }
 
+    public int getScreenX(){
+        return screenX;
+    }
+    public int getScreenY(){
+        return screenY;
+    }
     public int getScaleX() {
         return scaleX;
     }
     public int getScaleY(){
         return scaleY;
     }
+
+    public void move(int x, int y) {
+        screenX += x;
+        screenY += y;
+    }
+
+    public void setScreenCoordinates(int screenX, int screenY) {
+        this.screenX = screenX;
+        this.screenY = screenY;
+    }
 }

+ 1 - 1
src/main/java/view/tile/interactive/IronMineView.java

@@ -7,6 +7,6 @@ public class IronMineView extends InteractiveTileView {
 
     public IronMineView(GameController gc, InteractiveTileController controller) {
         super( 1,1, controller, gc.getView().tileManager);
-        setImage("/tiles/mineIron/mineIronTest.png");
+        setImage("/tiles/iron_mine/iron_mine_lvl1.png");
     }
 }

+ 1 - 1
src/main/java/view/tile/interactive/MineView.java

@@ -7,7 +7,7 @@ public class MineView extends InteractiveTileView {
 
     public MineView(int scaleX, int scaleY,GameController gc, InteractiveTileController controller) {
         super( scaleX, scaleY, controller, gc.getView().tileManager );
-        setImage("/tiles/mine/mineTest.png");
+        setImage("/tiles/mine/mine_lvl1.png");
     }
 
 }

+ 2 - 2
src/main/java/view/tile/interactive/SawmillView.java

@@ -5,8 +5,8 @@ import controller.tiles.interactive.InteractiveTileController;
 
 public class SawmillView extends InteractiveTileView {
 
-    public SawmillView(GameController gc, InteractiveTileController controller) {
-        super( 1,1, controller, gc.getView().tileManager);
+    public SawmillView(int scaleX, int scaleY, GameController gc, InteractiveTileController controller) {
+        super( scaleX,scaleY, controller, gc.getView().tileManager);
         setImage("/tiles/sawmill/sawmillTest.png");
     }
 

+ 90 - 0
src/main/java/view/ui/InventoryView.java

@@ -0,0 +1,90 @@
+package view.ui;
+
+import model.Inventory;
+import model.items.Item;
+import util.TextUtil;
+import util.Translator;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+
+public class InventoryView {
+    private Inventory inventory;
+    int overlayX = 20;
+    int overlayY = 20;
+    int slotSize = 48;
+    int slotSpacing = 8;
+    int slotCount = 0;
+    public InventoryView(Inventory inventory){
+        this.inventory = inventory;
+    }
+
+    public void drawInventoryOverlay(Graphics2D g2) {
+        slotCount = inventory.getUniqueItemsCount();
+
+        int overlayWidth = slotSize + 2* slotSpacing;
+        int overlayHeight = (slotSize + slotSpacing) * slotCount + slotSpacing;
+
+        g2.setColor(new Color(0, 0, 0, 160));
+        g2.fillRoundRect(overlayX, overlayY, overlayWidth, overlayHeight, 15, 15);
+
+        for (int i = 0; i < slotCount; i++) {
+            drawSlot(g2, i);
+        }
+    }
+
+    private void drawSlot(Graphics2D g2, int slot){
+        Item item = inventory.getItemList().get(slot);
+
+        if(item.isSelected()){
+            g2.setColor(Color.WHITE);
+        }else{
+            g2.setColor(Color.LIGHT_GRAY);
+        }
+
+        int x = overlayX + slotSpacing;
+        int y = overlayY + slotSpacing + slot * (slotSize + slotSpacing);
+
+        g2.fillRoundRect(x, y, slotSize, slotSize, 10, 10);
+        g2.setColor(Color.DARK_GRAY);
+
+        String itemName = item.getItemName().toString();
+
+        int offset = 20;
+        g2.drawImage(getImage("/items/" + itemName + ".png"), x + offset/2, y + offset/2, slotSize - offset, slotSize -offset, null);
+
+        //g2.drawString(Translator.translate("item."+itemName), x + slotSpacing/4, y + slotSize / 2 + 5);
+
+        FontMetrics fm = g2.getFontMetrics();
+        String amount = "" + item.getCount();
+        int amountWidth = fm.stringWidth(amount);
+        g2.drawString( amount , x + slotSize - amountWidth - 3, y + slotSize - 3);
+
+        g2.setColor(Color.LIGHT_GRAY);
+    }
+
+    public BufferedImage getImage(String imagePath){
+        try {
+            return ImageIO.read(getClass().getResourceAsStream(imagePath));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public int getClickedInventorySlot(int mouseX, int mouseY) {
+        for (int i = 0; i < slotCount; i++) {
+            int x = overlayX + slotSpacing;
+            int y = overlayY + slotSpacing + i * (slotSize + slotSpacing);
+            int width = slotSize;
+            int height = slotSize;
+
+            if (mouseX >= x && mouseX <= x + width && mouseY >= y && mouseY <= y + height) {
+                return i; // Slot i was clicked
+            }
+        }
+        return -1; // No slot was clicked
+    }
+}

+ 153 - 0
src/main/java/view/ui/ShopView.java

@@ -0,0 +1,153 @@
+package view.ui;
+
+import controller.factories.InteractiveTileFactory;
+import controller.tiles.interactive.InteractiveTileController;
+import model.Inventory;
+import model.items.ITEM_NAME;
+import model.tiles.InteractiveTileModel;
+import model.tiles.InteractiveTileType;
+import util.Translator;
+import util.economy.EconomyData;
+import util.economy.EconomyInfo;
+import view.GamePanel;
+import view.tile.TileManager;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class ShopView {
+    private GamePanel gamePanel;
+    private List<InteractiveTileType> offers;
+
+    int overlayY = 80;
+    int slotSize = 128;
+    int spacing = 8;
+    int slotCount = 0;
+    int offsetX, offsetY;
+    public ShopView(GamePanel gamePanel){
+        this.gamePanel = gamePanel;
+        offers = EconomyInfo.getOffers();
+    }
+
+    public void drawOverlay(Graphics2D g2) {
+        drawShopOverlay(g2, Translator.translate("menu.shop"));
+
+        drawInventoryOverlay(g2);
+    }
+
+    private void drawInventoryOverlay(Graphics2D g2) {
+        gamePanel.getInventoryView().drawInventoryOverlay(g2);
+    }
+
+    public void drawShopOverlay(Graphics2D g2, String title){
+
+        slotCount = offers.size();
+
+        int overlayWidth = (slotSize + spacing) * slotCount + spacing;
+        int overlayHeight = slotSize + 2* spacing;
+
+        offsetX = gamePanel.getWidth()/2 - overlayWidth/2;
+        offsetY = overlayY + spacing;
+        g2.setColor(new Color(0, 0, 0, 160));
+        g2.fillRoundRect(offsetX, offsetY, overlayWidth, overlayHeight, 25,25);
+        drawTitle(g2, title);
+
+        offsetX += spacing;
+        offsetY += spacing;
+
+        for(InteractiveTileType type : offers){
+            drawOffer(g2, type);
+        }
+    }
+
+    public InteractiveTileController getClickedOffer(int x, int y) {
+        int offerCount = offers.size();
+
+        int overlayWidth = (slotSize + spacing) * offerCount + spacing;
+
+        int baseOffsetX = gamePanel.getWidth() / 2 - overlayWidth / 2;
+        int baseOffsetY = overlayY + spacing;
+
+        int offerX = baseOffsetX + spacing;
+        int offerY = baseOffsetY + spacing;
+
+        for (int i = 0; i < offerCount; i++) {
+            int currentX = offerX + i * (slotSize + spacing);
+            int currentY = offerY;
+
+            if (x >= currentX && x <= currentX + slotSize &&
+                    y >= currentY && y <= currentY + slotSize) {
+                TileManager tm = gamePanel.tileManager;
+
+                return InteractiveTileFactory.createTile(gamePanel.gameController, new InteractiveTileModel(tm.screenToWorldX(x), tm.screenToWorldY(y), offers.get(i)));
+            }
+        }
+        return null; // No offer clicked
+    }
+
+    private void drawOffer(Graphics2D g2, InteractiveTileType offer){
+        g2.setColor(Color.LIGHT_GRAY);
+        g2.fillRoundRect(offsetX, offsetY, slotSize, slotSize, 25, 25);
+
+        g2.setColor(Color.BLACK);
+        BufferedImage image = getImage("/tiles/" + offer.toString().toLowerCase() + "/" + offer.toString().toLowerCase() + "Test.png");
+        int imageSize = slotSize/2;
+        g2.drawImage(image, offsetX + (slotSize/2 - imageSize/2), offsetY, imageSize, imageSize,null);
+
+        EconomyData data = EconomyInfo.infoOf(offer);
+
+        int stringOffset = slotSize + imageSize;
+        List<Map.Entry<ITEM_NAME, Integer>> entries = new ArrayList<>(data.getCosts().get(1).entrySet());
+
+        for(Map.Entry<ITEM_NAME, Integer> entry : entries){
+            String needed = Translator.translate("item."+entry.getKey()) + ": " + entry.getValue();
+
+            int strWidth = g2.getFontMetrics().stringWidth(needed);
+            Inventory inventory = gamePanel.gameController.getModel().getInventory();
+
+            if(inventory.hasEnough(entry.getKey(), entry.getValue())){
+                g2.setColor(Color.BLACK);
+            }else{
+                g2.setColor(Color.RED);
+            }
+            g2.drawString(needed, offsetX + (slotSize/2 - strWidth/2), stringOffset);
+            stringOffset += g2.getFontMetrics().getHeight();
+        }
+        offsetX += slotSize + spacing;
+
+        if(offsetX >= gamePanel.getWidth() - slotSize*2){
+            offsetY += slotSize + spacing;
+            offsetX = slotSize + slotSize/2;
+        }
+    }
+
+
+    private void drawTitle(Graphics2D g2, String title){
+        Font defaultF = g2.getFont();
+        g2.setFont(new Font("Arial", Font.BOLD, 50));
+
+        FontMetrics fm = g2.getFontMetrics();
+        int msgWidth = fm.stringWidth(title);
+        g2.setColor(new Color(0, 0, 0, 100));
+        int offset = 100;
+
+        g2.fillRoundRect(gamePanel.screenWidth /2 - msgWidth /2 - offset/2, 40 - fm.getHeight()/2, msgWidth + offset, 50, 25, 25);
+        g2.setColor(Color.WHITE);
+        g2.drawString(title, gamePanel.screenWidth / 2 - msgWidth / 2, 50);
+        g2.setFont(defaultF);
+    }
+
+    private BufferedImage getImage(String imagePath){
+        try {
+            return ImageIO.read(getClass().getResourceAsStream(imagePath));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+}

+ 11 - 55
src/main/java/view/UI.java → src/main/java/view/ui/UI.java

@@ -1,13 +1,14 @@
-package view;
+package view.ui;
 
 import controller.InteractiveController;
 import controller.entity.EntityController;
 import controller.tiles.interactive.InteractiveTileController;
-import util.GAMESTATE;
 import util.Translator;
+import view.GamePanel;
 import view.components.Button;
 import view.popUpMenu.PopupMenu;
 import view.tile.ONCLICKTYPE;
+import view.ui.menu.PauseMenu;
 
 import java.awt.*;
 import java.util.ArrayList;
@@ -16,7 +17,7 @@ public class UI {
     private GamePanel gp;
     private Graphics2D g2;
     private Font arial_40;
-    private ArrayList<Button> activeButtons = new ArrayList<>();
+    private static ArrayList<Button> activeButtons = new ArrayList<>();
 
     public UI(GamePanel gp){
         this.gp = gp;
@@ -28,9 +29,6 @@ public class UI {
         g2.setFont(arial_40);
         g2.setColor(Color.white);
 
-        if(gp.gameState == GAMESTATE.PAUSED){
-            drawPauseScreen(g2, Translator.translate("menu.pause"));
-        }
     }
     public void handleMenuClick(int screenX, int screenY) {
         for (Button button : activeButtons) {
@@ -40,6 +38,7 @@ public class UI {
             }
         }
     }
+
     public void handleClick(int screenX, int screenY){
         for (InteractiveTileController tile : gp.gameController.interactiveTileControllers) {
             handleClickForController(tile, screenX, screenY);
@@ -48,6 +47,7 @@ public class UI {
             handleClickForController(controller, screenX, screenY);
         }
     }
+
     private void handleClickForController(InteractiveController interactiveController, int screenX, int screenY){
         if(interactiveController.getClicked() && interactiveController.getOnClickType() == ONCLICKTYPE.POPUP){
             PopupMenu popupMenu = interactiveController.getPopupMenu();
@@ -65,59 +65,12 @@ public class UI {
         }
     }
 
-    private void drawPauseScreen(Graphics2D g2, String message) {
-
-
-        // Background overlay
-        g2.setColor(new Color(0, 0, 0, 150));
-        g2.fillRect(0, 0, gp.screenWidth, gp.screenHeight);
-
-        // Dialog box
-        int boxWidth = gp.screenWidth / 2;
-        int boxHeight = gp.screenHeight / 2;
-        int boxX = gp.screenWidth / 2 - boxWidth / 2;
-        int boxY = gp.screenHeight / 4;
-
-        g2.setColor(Color.WHITE);
-        g2.fillRoundRect(boxX, boxY, boxWidth, boxHeight, 25, 25);
-
-        // Message text
-        g2.setColor(Color.BLACK);
-        g2.setFont(new Font("Arial", Font.BOLD, 28));
-        FontMetrics fm = g2.getFontMetrics();
-        int msgWidth = fm.stringWidth(message);
-        g2.drawString(message, gp.screenWidth / 2 - msgWidth / 2, boxY + 50);
-
-        // Buttons
-        int buttonWidth = 260;
-        int buttonHeight = 40;
-        int spacing = 20;
-
-        int buttonX = gp.screenWidth / 2 - buttonWidth / 2;
-        int resumeY = boxY + 100;
-        int saveY = resumeY + buttonHeight + spacing;
-        int exitY = resumeY + buttonHeight + buttonHeight + spacing + spacing;
-        Button resumeButton = new Button(buttonWidth, buttonHeight, Translator.translate("menu.resume"), () -> {
-            gp.gameState = GAMESTATE.PLAY;
-        });
-        drawAndRegisterButton(resumeButton, buttonX, resumeY);
-
-        Button exitButton = new Button(buttonWidth, buttonHeight, Translator.translate("menu.quit"), () -> {
-            gp.gameState = GAMESTATE.QUIT;
-        });
-        drawAndRegisterButton(exitButton, buttonX, exitY);
-
-        Button saveButton = new Button(buttonWidth, buttonHeight, Translator.translate("menu.save"), () -> {
-            gp.gameState = GAMESTATE.SAVE;
-        });
-        drawAndRegisterButton(saveButton, buttonX, saveY);
-    }
-
-    private void drawAndRegisterButton(Button button, int buttonX, int buttonY){
+    public static void drawAndRegisterButton(Graphics2D g2, Button button, int buttonX, int buttonY){
         button.setScreenCoordinates(buttonX, buttonY);
         button.draw(g2);
         activeButtons.add(button);
     }
+
     public int getXForCenteredText(String text){
         int length = (int)g2.getFontMetrics().getStringBounds(text, g2).getWidth();
         return gp.screenWidth/2 - length/2;
@@ -127,5 +80,8 @@ public class UI {
         for (InteractiveTileController tile : gp.gameController.interactiveTileControllers) {
             tile.unClick();
         }
+        for(EntityController e : gp.gameController.entityControllers){
+            e.unClick();
+        }
     }
 }

+ 34 - 0
src/main/java/view/ui/menu/AbstractMenu.java

@@ -0,0 +1,34 @@
+package view.ui.menu;
+
+import view.GamePanel;
+import view.components.Button;
+import view.ui.UI;
+
+import java.awt.*;
+import java.util.ArrayList;
+
+public abstract class AbstractMenu {
+    protected GamePanel gp;
+    private ArrayList<Button> activeButtons;
+    protected Graphics2D g2;
+
+    public AbstractMenu(GamePanel gp){
+        this.gp = gp;
+        activeButtons = new ArrayList<>();
+    }
+    public abstract void draw(Graphics2D g2);
+
+    public void handleClick(int screenX, int screenY) {
+        for (Button button : activeButtons) {
+            if (button.wasClicked(screenX, screenY)) {
+                button.click();
+                break;
+            }
+        }
+    }
+    protected void drawAndRegisterButton(Button button, int buttonX, int buttonY){
+        button.setScreenCoordinates(buttonX, buttonY);
+        button.draw(g2);
+        activeButtons.add(button);
+    }
+}

+ 21 - 0
src/main/java/view/ui/menu/MainMenu.java

@@ -0,0 +1,21 @@
+package view.ui.menu;
+
+import util.GAMESTATE;
+import view.GamePanel;
+import view.components.Button;
+
+import java.awt.*;
+
+public class MainMenu extends AbstractMenu {
+    public MainMenu(GamePanel gp){
+        super(gp);
+    }
+    @Override
+    public void draw(Graphics2D g2){
+        this.g2 = g2;
+        Button newGameButton = new Button(100, 100, "New Game", () -> {
+            gp.gameState = GAMESTATE.PLAY;
+        });
+        drawAndRegisterButton(newGameButton, 10, 10);
+    }
+}

+ 82 - 0
src/main/java/view/ui/menu/PauseMenu.java

@@ -0,0 +1,82 @@
+package view.ui.menu;
+
+import util.GAMESTATE;
+import util.Translator;
+import view.GamePanel;
+import view.components.Button;
+import view.ui.UI;
+
+import java.awt.*;
+
+public class PauseMenu extends AbstractMenu{
+
+    public PauseMenu(GamePanel gamePanel){
+        super(gamePanel);
+    }
+
+    public void draw(Graphics2D g2){
+        this.g2 = g2;
+        String message = Translator.translate("menu.pause");
+        // Background overlay
+        g2.setColor(new Color(0, 0, 0, 150));
+        g2.fillRect(0, 0, gp.screenWidth, gp.screenHeight);
+
+        // Dialog box
+        int boxWidth = gp.screenWidth / 2;
+        int boxHeight = gp.screenHeight / 2;
+        int boxX = gp.screenWidth / 2 - boxWidth / 2;
+        int boxY = gp.screenHeight / 4;
+
+        g2.setColor(Color.WHITE);
+        g2.fillRoundRect(boxX, boxY, boxWidth, boxHeight, 25, 25);
+
+        // Message text
+        g2.setColor(Color.BLACK);
+        g2.setFont(new Font("Arial", Font.BOLD, 28));
+        FontMetrics fm = g2.getFontMetrics();
+        int msgWidth = fm.stringWidth(message);
+        g2.drawString(message, gp.screenWidth / 2 - msgWidth / 2, boxY + 50);
+
+        // Buttons
+        int buttonWidth = 260;
+        int buttonHeight = 40;
+
+        int buttonX = gp.screenWidth / 2 - buttonWidth / 2;
+        int resumeY = boxY + 100;
+        int saveY = getYForNewButton(resumeY);
+        int exitY = getYForNewButton(saveY);
+        int menuY = getYForNewButton(exitY);
+        int settingsY = getYForNewButton(menuY);
+
+        view.components.Button resumeButton = new view.components.Button(buttonWidth, buttonHeight, Translator.translate("menu.resume"), () -> {
+            gp.gameState = GAMESTATE.PLAY;
+        });
+        drawAndRegisterButton(resumeButton, buttonX, resumeY);
+
+        view.components.Button exitButton = new view.components.Button(buttonWidth, buttonHeight, Translator.translate("menu.quit"), () -> {
+            gp.gameState = GAMESTATE.QUIT;
+        });
+        drawAndRegisterButton(exitButton, buttonX, exitY);
+
+        view.components.Button saveButton = new view.components.Button(buttonWidth, buttonHeight, Translator.translate("menu.save"), () -> {
+            gp.gameState = GAMESTATE.SAVE;
+        });
+        drawAndRegisterButton(saveButton, buttonX, saveY);
+
+        view.components.Button menuButton = new Button(buttonWidth, buttonHeight, Translator.translate("menu.main_menu"), () -> {
+            gp.gameState = GAMESTATE.MAIN_MENU;
+        });
+        drawAndRegisterButton(menuButton, buttonX, menuY);
+
+        view.components.Button settingsButton = new Button(buttonWidth, buttonHeight, Translator.translate("menu.settings"), () -> {
+            gp.gameState = GAMESTATE.SETTINGS;
+        });
+        drawAndRegisterButton(settingsButton, buttonX, settingsY);
+    }
+
+    private int getYForNewButton(int oldY){
+        int buttonHeight = 40;
+        int spacing = 20;
+        return oldY + buttonHeight + spacing;
+    }
+}

+ 82 - 0
src/main/java/view/ui/menu/SettingsMenu.java

@@ -0,0 +1,82 @@
+package view.ui.menu;
+
+import util.GAMESTATE;
+import util.Settings;
+import util.Translator;
+import view.GamePanel;
+import view.components.Button;
+
+import java.awt.*;
+
+public class SettingsMenu extends AbstractMenu {
+    public SettingsMenu(GamePanel gp) {
+        super(gp);
+    }
+
+    @Override
+    public void draw(Graphics2D g2) {
+        this.g2 = g2;
+
+        // Background overlay
+        g2.setColor(new Color(0, 0, 0, 150));
+        g2.fillRect(0, 0, gp.screenWidth, gp.screenHeight);
+
+        // Dialog box
+        int boxWidth = gp.screenWidth / 2;
+        int boxHeight = gp.screenHeight / 2;
+        int boxX = gp.screenWidth / 2 - boxWidth / 2;
+        int boxY = gp.screenHeight / 4;
+
+        g2.setColor(Color.WHITE);
+        g2.fillRoundRect(boxX, boxY, boxWidth, boxHeight, 25, 25);
+
+        // Title
+        g2.setColor(Color.BLACK);
+        g2.setFont(new Font("Arial", Font.BOLD, 28));
+        String title = Translator.translate("menu.settings");
+        int titleWidth = g2.getFontMetrics().stringWidth(title);
+        g2.drawString(title, gp.screenWidth / 2 - titleWidth / 2, boxY + 40);
+
+        // Buttons/Settings
+        int buttonWidth = 260;
+        int buttonHeight = 40;
+        int spacing = 15;
+
+        int buttonX = gp.screenWidth / 2 - buttonWidth / 2;
+        int audioY = boxY + 80;
+        int langY = audioY + buttonHeight + spacing;
+        int fullscreenY = langY + buttonHeight + spacing;
+        int backY = fullscreenY + buttonHeight + spacing;
+
+        // Music Volume Button (dummy increment/decrement toggle for now)
+        Button musicButton = new Button(buttonWidth, buttonHeight,
+                Translator.translate("settings.volume") + ": " + Settings.getVolume(),
+                () -> {
+                    Settings.toggleVolume();
+                });
+        drawAndRegisterButton(musicButton, buttonX, audioY);
+
+        // Language Selection
+        Button langButton = new Button(buttonWidth, buttonHeight,
+                Translator.translate("settings.language") + ": " + Settings.getLanguage(),
+                () -> {
+                    Settings.toggleLanguage();
+                });
+        drawAndRegisterButton(langButton, buttonX, langY);
+
+        // Fullscreen Toggle
+        Button fullscreenButton = new Button(buttonWidth, buttonHeight,
+                Translator.translate("settings.fullscreen") + ": " + (Settings.isFullscreen() ? "On" : "Off"),
+                () -> {
+                    Settings.toggleFullscreen();
+                });
+        drawAndRegisterButton(fullscreenButton, buttonX, fullscreenY);
+
+        // Back button
+        Button backButton = new Button(buttonWidth, buttonHeight,
+                Translator.translate("menu.back"), () -> {
+            gp.gameState = GAMESTATE.PAUSED;
+        });
+        drawAndRegisterButton(backButton, buttonX, backY);
+    }
+}

+ 5 - 1
src/main/java/view/util/RenderingManager.java

@@ -1,9 +1,13 @@
 package view.util;
 
-import java.awt.Graphics2D;
+import controller.InteractiveController;
+
+import java.awt.*;
 
 public interface RenderingManager {
     void drawPopup(Graphics2D g2);
 
     void resize(int newTileSize);
+
+
 }

+ 114 - 0
src/main/resources/economy/economy.json

@@ -0,0 +1,114 @@
+{
+  "IRON_MINE": {
+    "maxLevel": 10,
+    "costs": {
+      "1": { "stone": 40, "wood": 20 },
+      "2": { "stone": 5, "wood": 10 },
+      "3": { "stone": 50, "wood": 50 },
+      "4": { "stone": 500, "wood": 100 }
+    },
+    "lighthouseLevels": {
+      "1": 1,
+      "2": 4
+    },
+    "maxAmountPerLighthouseLevel": {
+      "1": 1,
+      "2": 2,
+      "3": 4
+    },
+    "productionTimeInSec": {
+      "1": 30,
+      "2": 28,
+      "3": 25,
+      "4": 20
+    }
+  },
+  "SAWMILL": {
+    "maxLevel": 5,
+    "costs": {
+      "1": { "wood": 1 },
+      "2": { "stone": 54, "wood": 40 }
+    },
+    "lighthouseLevels": {
+      "1": 1,
+      "2": 3
+    },
+    "maxAmountPerLighthouseLevel": {
+      "1": 1,
+      "2": 2,
+      "3": 4
+    },
+    "productionTimeInSec": {
+      "1": 30,
+      "2": 28,
+      "3": 25,
+      "4": 20
+    }
+  },
+  "BARRACKS": {
+    "maxLevel": 5,
+    "costs": {
+      "1": { "wood": 1 },
+      "2": { "stone": 54, "wood": 40 }
+    },
+    "lighthouseLevels": {
+      "1": 1,
+      "2": 3
+    },
+    "maxAmountPerLighthouseLevel": {
+      "1": 1,
+      "2": 2,
+      "3": 4
+    },
+    "productionTimeInSec": {
+      "1": 30,
+      "2": 28,
+      "3": 25,
+      "4": 20
+    }
+  },
+  "ANIMAL_ENCLOSURE": {
+    "maxLevel": 5,
+    "costs": {
+      "1": { "wood": 1 },
+      "2": { "stone": 54, "wood": 40 }
+    },
+    "lighthouseLevels": {
+      "1": 1,
+      "2": 3
+    },
+    "maxAmountPerLighthouseLevel": {
+      "1": 1,
+      "2": 2,
+      "3": 4
+    },
+    "productionTimeInSec": {
+      "1": 30,
+      "2": 28,
+      "3": 25,
+      "4": 20
+    }
+  },
+  "MINE": {
+    "maxLevel": 5,
+    "costs": {
+      "1": { "wood": 1 },
+      "2": { "stone": 54, "wood": 40 }
+    },
+    "lighthouseLevels": {
+      "1": 1,
+      "2": 3
+    },
+    "maxAmountPerLighthouseLevel": {
+      "1": 1,
+      "2": 2,
+      "3": 4
+    },
+    "productionTimeInSec": {
+      "1": 30,
+      "2": 28,
+      "3": 25,
+      "4": 20
+    }
+  }
+}

BIN
src/main/resources/items/iron.png


BIN
src/main/resources/items/stone.png


BIN
src/main/resources/items/wood.png


+ 8 - 0
src/main/resources/lang/de_de.json

@@ -5,11 +5,19 @@
   "menu.resume": "Fortsetzen",
   "menu.quit": "Beenden",
   "menu.save": "Speichern",
+  "menu.settings": "Einstellungen",
+  "settings.volume": "Lautstaerke",
+  "settings.language": "Sprache",
+  "settings.fullscreen": "Vollbildmodus",
+  "menu.back": "Zurueck",
+  "menu.main_menu": "Hauptmenu",
+  "menu.shop": "Laden",
   "popup.collect": "Sammeln",
   "popup.upgrade": "Verbessern",
   "item.wood": "Holz",
   "item.water": "Wasser",
   "item.stone": "Stein",
+  "item.iron": "Eisen",
   "popup.title.animal_enclosure": "Tierstall",
   "popup.title.barracks": "Kaserne",
   "popup.title.lighthouse": "Leuchtturm",

+ 8 - 0
src/main/resources/lang/en_us.json

@@ -5,11 +5,19 @@
   "menu.resume": "Resume",
   "menu.quit": "Quit",
   "menu.save": "Save",
+  "menu.settings": "Settings",
+  "settings.volume": "Volume",
+  "settings.language": "Language",
+  "settings.fullscreen": "Fullscreen",
+  "menu.back": "Back",
+  "menu.main_menu": "Main Menu",
+  "menu.shop": "Shop",
   "popup.collect": "Collect",
   "popup.upgrade": "Upgrade",
   "item.wood": "Wood",
   "item.water": "Water",
   "item.stone": "Stone",
+  "item.iron": "Iron",
   "popup.title.animal_enclosure": "Animal Enclosure",
   "popup.title.barracks": "Barracks",
   "popup.title.lighthouse": "Lighthouse",

+ 0 - 0
src/main/resources/lang/translation.json


BIN
src/main/resources/sound/background/background.wav


BIN
src/main/resources/sound/click.wav


BIN
src/main/resources/sound/moving_bush.wav


BIN
src/main/resources/sound/villager.wav


+ 0 - 0
src/main/resources/tiles/animalEnclosure/animalEnclosureTest.png → src/main/resources/tiles/animal_enclosure/animal_enclosureTest.png


+ 0 - 0
src/main/resources/tiles/mineIron/mineIronTest.png → src/main/resources/tiles/iron_mine/iron_mineTest.png


BIN
src/main/resources/tiles/iron_mine/iron_mine_lvl1.png


BIN
src/main/resources/tiles/iron_mine/iron_mine_lvl2.png


BIN
src/main/resources/tiles/mine/mine_lvl1.png


BIN
src/main/resources/tiles/mine/mine_lvl2.png


+ 44 - 0
src/test/java/EconomyInfoTest.java

@@ -0,0 +1,44 @@
+import model.items.ITEM_NAME;
+import model.tiles.InteractiveTileType;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import util.economy.EconomyData;
+import util.economy.EconomyInfo;
+
+import java.util.Map;
+
+
+public class EconomyInfoTest {
+    @Test
+    public void isReadingCorrectly(){
+        EconomyInfo.load();
+
+        EconomyData data = EconomyInfo.infoOf(InteractiveTileType.IRON_MINE);
+
+        Map<Integer, Map<ITEM_NAME, Integer>> costs = data.getCosts();
+
+        Map<ITEM_NAME, Integer> levelCosts2 = costs.get(1);
+
+        int max = data.getMaxLevel();
+
+        Integer stoneAmount = levelCosts2.get(ITEM_NAME.stone);
+        Integer woodAmount = levelCosts2.get(ITEM_NAME.wood);
+
+        Integer maxAmountLvl2 = data.getRequiredLighthouseLevels().get(1);
+        Integer maxAmountLvl3 = data.getRequiredLighthouseLevels().get(2);
+
+        Assertions.assertEquals(30, data.getProductionTime(1));
+        Assertions.assertEquals(28, data.getProductionTime(2));
+        Assertions.assertEquals(25, data.getProductionTime(3));
+        Assertions.assertEquals(20, data.getProductionTime(4));
+
+        Assertions.assertEquals(40, stoneAmount);
+        Assertions.assertEquals(20, woodAmount);
+
+        Assertions.assertEquals(10, max);
+
+        Assertions.assertEquals(1, maxAmountLvl2);
+
+        Assertions.assertEquals(4, maxAmountLvl3);
+    }
+}

+ 114 - 0
src/test/resources/economy/economy.json

@@ -0,0 +1,114 @@
+{
+  "IRON_MINE": {
+    "maxLevel": 10,
+    "costs": {
+      "1": { "stone": 40, "wood": 20 },
+      "2": { "stone": 5, "wood": 10 },
+      "3": { "stone": 50, "wood": 50 },
+      "4": { "stone": 500, "wood": 100 }
+    },
+    "lighthouseLevels": {
+      "1": 1,
+      "2": 4
+    },
+    "maxAmountPerLighthouseLevel": {
+      "1": 1,
+      "2": 2,
+      "3": 4
+    },
+    "productionTimeInSec": {
+      "1": 30,
+      "2": 28,
+      "3": 25,
+      "4": 20
+    }
+  },
+  "SAWMILL": {
+    "maxLevel": 5,
+    "costs": {
+      "1": { "wood": 1 },
+      "2": { "stone": 54, "wood": 40 }
+    },
+    "lighthouseLevels": {
+      "1": 1,
+      "2": 3
+    },
+    "maxAmountPerLighthouseLevel": {
+      "1": 1,
+      "2": 2,
+      "3": 4
+    },
+    "productionTimeInSec": {
+      "1": 30,
+      "2": 28,
+      "3": 25,
+      "4": 20
+    }
+  },
+  "BARRACKS": {
+    "maxLevel": 5,
+    "costs": {
+      "1": { "wood": 1 },
+      "2": { "stone": 54, "wood": 40 }
+    },
+    "lighthouseLevels": {
+      "1": 1,
+      "2": 3
+    },
+    "maxAmountPerLighthouseLevel": {
+      "1": 1,
+      "2": 2,
+      "3": 4
+    },
+    "productionTimeInSec": {
+      "1": 30,
+      "2": 28,
+      "3": 25,
+      "4": 20
+    }
+  },
+  "ANIMAL_ENCLOSURE": {
+    "maxLevel": 5,
+    "costs": {
+      "1": { "wood": 1 },
+      "2": { "stone": 54, "wood": 40 }
+    },
+    "lighthouseLevels": {
+      "1": 1,
+      "2": 3
+    },
+    "maxAmountPerLighthouseLevel": {
+      "1": 1,
+      "2": 2,
+      "3": 4
+    },
+    "productionTimeInSec": {
+      "1": 30,
+      "2": 28,
+      "3": 25,
+      "4": 20
+    }
+  },
+  "MINE": {
+    "maxLevel": 5,
+    "costs": {
+      "1": { "wood": 1 },
+      "2": { "stone": 54, "wood": 40 }
+    },
+    "lighthouseLevels": {
+      "1": 1,
+      "2": 3
+    },
+    "maxAmountPerLighthouseLevel": {
+      "1": 1,
+      "2": 2,
+      "3": 4
+    },
+    "productionTimeInSec": {
+      "1": 30,
+      "2": 28,
+      "3": 25,
+      "4": 20
+    }
+  }
+}