public class AudioManager implements Disposable {
    private static final Map<SoundId, Sound> _soundMap = new HashMap<SoundId, Sound>();
    private static final Map<MusicId, Music> _musicMap = new HashMap<MusicId, Music>();

    private static MusicId _currentSong = MusicId.NONE;

    public static void initSounds() {
        _soundMap.put(SoundId.DOOR_CREAK, Gdx.audio.newSound(Gdx.files.internal("audio/effects/door.mp3")));
        _soundMap.put(SoundId.DOOR_LOCKED, Gdx.audio.newSound(Gdx.files.internal("audio/effects/door_locked.mp3")));
        _soundMap.put(SoundId.SNOW_CRUNCH, Gdx.audio.newSound(Gdx.files.internal("audio/effects/snowsteps_2.mp3")));
        _soundMap.put(SoundId.FLOOR_STEP, Gdx.audio.newSound(Gdx.files.internal("audio/effects/footsteps_floor.mp3")));
        _soundMap.put(SoundId.CLICK, Gdx.audio.newSound(Gdx.files.internal("audio/effects/caclick.mp3")));
        _soundMap.put(SoundId.UNCLE_GASP, Gdx.audio.newSound(Gdx.files.internal("audio/effects/uncleNoise.mp3")));
        _soundMap.put(SoundId.ENGINE_START, Gdx.audio.newSound(Gdx.files.internal("audio/effects/car_ignition.mp3")));
        _soundMap.put(SoundId.SCARE, Gdx.audio.newSound(Gdx.files.internal("audio/effects/jump-scare.mp3")));


        _musicMap.put(MusicId.MAIN_MENU, Gdx.audio.newMusic(Gdx.files.internal("audio/music/bensound-betterdays.mp3")));

        Music dark = Gdx.audio.newMusic(Gdx.files.internal("audio/music/244961__patricklieberkind__dark-ambience.mp3"));
        dark.setLooping(true);
        _musicMap.put(MusicId.DARK, dark);

        Music drone = Gdx.audio.newMusic(Gdx.files.internal("audio/music/144921__thesoundcatcher__deepdrone.mp3"));
        drone.setLooping(true);
        _musicMap.put(MusicId.DRONE, drone);

        Music bedroom = Gdx.audio.newMusic(Gdx.files.internal("audio/music/simpleloops/bedroom.mp3"));
        bedroom.setLooping(true);
        _musicMap.put(MusicId.BEDROOM, bedroom);

        Music cozy = Gdx.audio.newMusic(Gdx.files.internal("audio/music/fireplace.mp3"));
        cozy.setLooping(true);
        _musicMap.put(MusicId.COZY, cozy);

        Music crawlspace = Gdx.audio.newMusic(Gdx.files.internal("audio/music/simpleloops/crawlspace.mp3"));
        crawlspace.setLooping(true);
        _musicMap.put(MusicId.CRAWLSPACE, crawlspace);

        Music howl = Gdx.audio.newMusic(Gdx.files.internal("audio/music/simpleloops/howl.mp3"));
        howl.setLooping(true);
        _musicMap.put(MusicId.HOWL, howl);

        Music wind = Gdx.audio.newMusic(Gdx.files.internal("audio/music/simpleloops/wind.mp3"));
        wind.setLooping(true);
        _musicMap.put(MusicId.WIND, wind);
    }

    public static void playSound(SoundId id) {
        if (id == null) {
            return;
        }

        if (_soundMap.containsKey(id)) {
            _soundMap.get(id).play();
        }
    }

    public static void playMusic(MusicId id, boolean restart) {
        if (id == null) {
            return;
        }
        if (_currentSong == id && !restart) {
            return;
        }

        stopSong(_currentSong);
        _musicMap.get(id).play();
        _currentSong = id;
    }

    public static void playMusic(MusicId id) {
        playMusic(id, true);
    }

    public static void stopSong(MusicId id) {
        if (_currentSong != MusicId.NONE) {
            _musicMap.get(id).stop();
            _currentSong = MusicId.NONE;
        }
    }

    public static void stopMusic() {
        for (MusicId id : _musicMap.keySet()) {
            if (_musicMap.get(id).isPlaying()) {
                _musicMap.get(id).stop();
                _currentSong = MusicId.NONE;
                return;
            }
        }
    }

    @Override
    public void dispose() {
        for (SoundId id : _soundMap.keySet()) {
            _soundMap.get(id).dispose();
        }
    }
} public enum MusicId {
    NONE, MAIN_MENU, DARK, DRONE, BEDROOM, COZY, CRAWLSPACE, HOWL, WIND
} public enum SoundId {
    DOOR_CREAK, DOOR_LOCKED, SNOW_CRUNCH, FLOOR_STEP, CLICK, UNCLE_GASP,
    ENGINE_START, SCARE
} public class FireAnimation {
    private static final int FIRE_HEIGHT = 10;
    private static final int MAX_SPEED = 6;

    private float _originX;
    private float _originY;

    private List<Flame> _flameList;

    private static final Color[] colours = {Color.RED, Color.FIREBRICK, Color.YELLOW};

    public FireAnimation(float originX, float originY, int intensity) {
        _originX = originX;
        _originY = originY;
        _flameList = createFlameList(intensity);

        for (int i = 0; i < 20; i++) {
            if (i % 2 != 1) {
                update(0.05f);
            }
        }
    }

    public void draw(ShapeRenderer renderer) {
        for (Flame flame : _flameList) {
            Color colour = colours[flame.getColour()];
            renderer.setColor(colour.r, colour.g, colour.b, flame.getOpacity());
            Vector2 position = flame.getPosition();
            int size = flame.getSize();
            renderer.rect(position.x, position.y, size, size);
        }
    }

    private List<Flame> createFlameList(int intensity) {
        List<Flame> flameList = new LinkedList<Flame>();
        Random random = new Random();
        for (int i = 0; i < intensity; i++) {
            Vector2 position = new Vector2(_originX, _originY);
            float speed = random.nextInt(MAX_SPEED) + 2;
            float sway = (random.nextInt(2) == 1 ? random.nextFloat() + random.nextInt(2) : -random.nextFloat() - random.nextInt(2));
            int size = random.nextInt(2) + 1;
            int colour = random.nextInt(3);
            flameList.add(new Flame(position, speed, sway, size, colour));
        }
        return flameList;
    }

    public void update(float delta) {
        for (Flame flame : _flameList) {
            flame.animate(delta);
        }
    }

    private class Flame {
        private Vector2 _position;
        private float _speed;
        private float _sway;
        private float _animationOpacity;
        private float _animationLength;
        private float _currentLength;
        private int _size;
        private int _colour;

        Flame(Vector2 position, float speed, float sway, int size, int colour) {
            _position = position;
            _speed = speed;
            _sway = sway;
            _size = size;
            _animationLength = FIRE_HEIGHT / _speed;
            _animationOpacity = 1;
            _colour = colour;
        }

        Vector2 getPosition() {
            return _position;
        }

        int getSize() {
            return _size;
        }

        int getColour() {
            return _colour;
        }

        float getOpacity() {
            return _animationOpacity;
        }

        void animate(float delta) {
            _position.add(delta * _sway, _speed * delta);
            _currentLength += delta;

            _animationOpacity = 1 - (_currentLength / _animationLength);

            if (_position.y > _originY + FIRE_HEIGHT + _size || _animationOpacity < 0.05f) {
                _position.set(_originX, _originY);
                _animationOpacity = 1;
                _currentLength = 0;
            }
        }
    }
} public enum ItemId {
    KEY_HOUSE,
    GLASS,
    GLASS_SNOW,
    GLASS_WATER,
    SHOVEL,
    HAMMER,
    STICK,
    STICK_RAGS,
    STICK_RAGS_ALCOHOL,
    TORCH,
    PRYBAR,
    CAR_KEYS
} public class PickupableItem implements Item {
    private LinkedList<InteractionListener> _listeners = new LinkedList<InteractionListener>();
    private Rectangle _interactionBox;
    private Sprite _sprite;
    private Sprite _invSprite;
    private ItemId _id;

    private SoundId _soundId;

    private String _pickupText;

    private boolean _pickedUp;

    private Map<ItemId, String> _itemInteractionMessages;

    public PickupableItem(Sprite sprite, Rectangle interactionBox, ItemId id) {
        _interactionBox = interactionBox;
        _sprite = sprite;
        _invSprite = sprite;
        _sprite.setSize(_interactionBox.getWidth(), _interactionBox.getHeight());
        _sprite.setPosition(_interactionBox.getX(), _interactionBox.getY());
        _id = id;

        _pickedUp = false;

        _itemInteractionMessages = new HashMap<ItemId, String>();
    }

    public PickupableItem(Sprite worldSprite, Sprite invSprite, Rectangle interactionBox, ItemId id) {
        _interactionBox = interactionBox;
        _sprite = worldSprite;
        _invSprite = invSprite;
        _sprite.setPosition(0, 0);
        _sprite.setSize(PlayGame.GAME_WIDTH, PlayGame.GAME_HEIGHT);
        _id = id;
    }

    public void setPickupText(String pickupText) {
        _pickupText = pickupText;
    }

    // Draw debug areas
    public void draw(ShapeRenderer renderer) {
        if (PlayGame.getDebug()) {
            renderer.setColor(0.5f, 0.3f, 0.1f, 0.25f);
            renderer.rect(_interactionBox.x, _interactionBox.y, _interactionBox.width, _interactionBox.height);
        }
    }

    // Draw the world sprite
    public void draw(SpriteBatch batch) {
        _sprite.draw(batch);
    }

    // Draw the inventory sprite
    public Sprite getInventorySprite() {
        return _invSprite;
    }

    public boolean isPickedUp() {
        return _pickedUp;
    }

    @Override
    public Rectangle getInteractionBox() {
        return _interactionBox;
    }

    @Override
    public boolean click(float curX, float curY) {
        if (_interactionBox.contains(curX, curY)) {
            notifyListeners();
            AudioManager.playSound(_soundId);
            if (_pickupText != null) {
                PlayGame.getPlayer().think(_pickupText);
                _pickedUp = true;
            }
            return true;
        }
        return false;
    }

    @Override
    public void update(float delta) {

    }

    @Override
    public void beautifyCursor(float curX, float curY) {
        if (_interactionBox.contains(curX, curY)) {
            PlayGame.getPlayer().setCursor(Player.CursorType.PICKUP);
        }
    }

    @Override
    public void registerListener(InteractionListener listener) {
        _listeners.push(listener);
    }

    @Override
    public void setSoundEffect(SoundId soundId) {
        _soundId = soundId;
    }

    @Override
    public boolean isItemInteractable() {
        return false;
    }

    @Override
    public boolean itemInteract(ItemId id) {
        return false;
    }

    @Override
    public String getItemInteractionMessage(ItemId id) {
        if (_itemInteractionMessages.containsKey(id)) {
            return _itemInteractionMessages.get(id);
        }
        return "";
    }

    @Override
    public void setItemInteractionMessage(ItemId id, String message) {
        _itemInteractionMessages.put(id, message);
    }

    public void notifyListeners() {
        for (InteractionListener listener : _listeners) {
            listener.notifyListener();
        }
    }

    @Override
    public ItemId getItemId() {
        return _id;
    }

    @Override
    public boolean checkInteraction(float curX, float curY) {
        return _interactionBox.contains(curX, curY);
    }
} public class DirectionalPassage implements Passage {
    private LinkedList<InteractionListener> _listeners = new LinkedList<InteractionListener>();
    protected Rectangle _interactionBox;
    protected Rectangle _silentInteractionBox;
    private RoomId _source, _destination;
    protected Direction _direction;
    private boolean _release = true;

    private FlagId _travelFlag;

    private SoundId _soundId;

    private Sprite _transitionSprite;

    private float _travelSpeed = SlideTransition.DEFAULT_SPEED;

    private Map<ItemId, String> _itemInteractionMessages;

    public DirectionalPassage(RoomId src, RoomId dest, Rectangle interactionBox, Direction direction) {
        _source = src;
        _destination = dest;
        _interactionBox = interactionBox;
        _silentInteractionBox = interactionBox;
        _direction = direction;

        _itemInteractionMessages = new HashMap<ItemId, String>();
    }

    public DirectionalPassage(RoomId src, RoomId dest, Rectangle interactionBox, Rectangle silentInteractionBox, Direction direction) {
        _source = src;
        _destination = dest;
        _interactionBox = interactionBox;
        _silentInteractionBox = silentInteractionBox;
        _direction = direction;

        _itemInteractionMessages = new HashMap<ItemId, String>();
    }

    @Override
    public RoomId getDestination() {
        return _destination;
    }

    @Override
    public Rectangle getInteractionBox() {
        return _interactionBox;
    }

    @Override
    public void roomTransition() {
        PlayGame.getPlayer().enterRoom(_destination);
    }

    @Override
    public void transitionComplete() {
        if (PlayGame.getFlagManager().getFlag(FlagId.FLAG_KEYS_MISSING).getState()) {
            Timer.schedule(new Timer.Task() {
                               @Override
                               public void run() {
                                   if (_release) {
                                       PlayGame.getPlayer().setInteractionMode(Player.InteractionMode.NORMAL);
                                   }
                               }
                           }
                    , 0.65f // Initial delay
                    , 0 // Fire every X seconds
                    , 1 // Number of times to fire
            );
        } else {
            Timer.schedule(new Timer.Task() {
                               @Override
                               public void run() {
                                   if (_release) {
                                       PlayGame.getPlayer().setInteractionMode(Player.InteractionMode.NORMAL);
                                   }
                               }
                           }
                    , 0.4f // Initial delay
                    , 0 // Fire every X seconds
                    , 1 // Number of times to fire
            );
        }
    }

    @Override
    public void setTransitionImage(Sprite sprite) {
        _transitionSprite = sprite;
    }

    @Override
    public void setTravelFlag(FlagId flag) {
        _travelFlag = flag;
    }

    @Override
    public void setTravelSpeed(float speed) {
        _travelSpeed = speed;
    }

    @Override
    public void draw(SpriteBatch batch) {
    }

    public void setAutoRelease(boolean release) {
        _release = release;
    }

    @Override
    public void draw(ShapeRenderer renderer) {
        if (PlayGame.getDebug()) {
            if (PlayGame.getPlayer().getInteractionMode() == Player.InteractionMode.NORMAL) {
                renderer.setColor(0, 1, 0, 0.25f);
            } else if (PlayGame.getPlayer().getInteractionMode() == Player.InteractionMode.ITEM_INTERACTION) {
                renderer.setColor(0, 0, 1, 0.25f);
            } else {
                renderer.setColor(1, 0, 0, 0.25f);
            }
            renderer.rect(_interactionBox.x, _interactionBox.y, _interactionBox.width, _interactionBox.height);
        }
    }

    @Override
    public boolean click(float curX, float curY) {
        if (_interactionBox.contains(curX, curY) || _silentInteractionBox.contains(curX, curY)) {
            notifyListeners();

            PlayGame.getPlayer().setInteractionMode(Player.InteractionMode.NONE);
            PlayGame.playTransition(_transitionSprite == null ?
                    new SlideTransition(this, _direction) :
                    new SlideTransition(this, _direction, _transitionSprite, _travelSpeed));

            AudioManager.playSound(_soundId);

            if (_travelFlag != null && !PlayGame.getFlagManager().getFlag(_travelFlag).getState()) {
                PlayGame.getFlagManager().toggleFlag(_travelFlag);
            }

            return true;
        }
        return false;
    }

    @Override
    public void beautifyCursor(float curX, float curY) {
        if (_interactionBox.contains(curX, curY)) {
            switch (_direction) {
                case UP:
                    PlayGame.getPlayer().setCursor(Player.CursorType.UP_ARROW);
                    break;
                case DOWN:
                    PlayGame.getPlayer().setCursor(Player.CursorType.DOWN_ARROW);
                    break;
                case LEFT:
                    PlayGame.getPlayer().setCursor(Player.CursorType.LEFT_ARROW);
                    break;
                case RIGHT:
                    PlayGame.getPlayer().setCursor(Player.CursorType.RIGHT_ARROW);
                    break;
            }
        }
    }

    @Override
    public void update(float delta) {
    }

    @Override
    public void registerListener(InteractionListener lis) {
        _listeners.push(lis);
    }

    @Override
    public void setSoundEffect(SoundId soundId) {
        _soundId = soundId;
    }

    @Override
    public String getItemInteractionMessage(ItemId id) {
        if (_itemInteractionMessages.containsKey(id)) {
            return _itemInteractionMessages.get(id);
        }
        return "";
    }

    @Override
    public void setItemInteractionMessage(ItemId id, String message) {
        _itemInteractionMessages.put(id, message);
    }

    @Override
    public boolean isItemInteractable() {
        return false;
    }

    @Override
    public boolean itemInteract(ItemId id) {
        return false;
    }

    private void notifyListeners() {
        for (InteractionListener listener : _listeners) {
            listener.notifyListener();
        }
    }

    @Override
    public boolean checkInteraction(float curX, float curY) {
        return _interactionBox.contains(curX, curY);
    }
} public interface Passage extends Interactable {
    public RoomId getDestination();

    public void roomTransition();

    public void transitionComplete();

    public void setTransitionImage(Sprite sprite);

    public void setTravelFlag(FlagId flag);

    public void setTravelSpeed(float speed);
} public interface Describable extends Interactable {

    public abstract void setDescription(String description);

    public abstract String getDescription();

    public abstract void addDescription(String description);

    public abstract void removeAllDescriptions();
} public class GeneralDescribable implements Describable {

    private Rectangle _interactionBox;
    private String _description;
    private Queue<String> _descriptionList;
    private static final float HOLD_LENGTH = 2.0f;
    private Map<ItemId, String> _itemInteractionMessages;

    public GeneralDescribable(String description, Rectangle interactionBox) {
        _interactionBox = interactionBox;

        _descriptionList = new LinkedList<String>();
        _descriptionList.add(description);
        _description = description;

        _itemInteractionMessages = new HashMap<ItemId, String>();
    }

    public void addDescription(String description) {
        _descriptionList.add(description);
    }

    @Override
    public void removeAllDescriptions() {
        _descriptionList = new LinkedList<String>();
        _description = "";
    }

    @Override
    public void setDescription(String description) {
        _description = description;
        _descriptionList.clear();
        _descriptionList.add(description);
    }

    @Override
    public String getDescription() {
        if (_descriptionList.size() > 1) {
            String description = _descriptionList.poll();
            _descriptionList.add(description);
            return description;
        } else {
            return _description;
        }
    }

    @Override
    public Rectangle getInteractionBox() {
        return _interactionBox;
    }

    @Override
    public void draw(SpriteBatch batch) {
    }

    //Debug drawing
    @Override
    public void draw(ShapeRenderer renderer) {
        if (PlayGame.getDebug()) {
            renderer.setColor(0.5f, 0f, 0.5f, 0.25f);
            renderer.rect(_interactionBox.x, _interactionBox.y, _interactionBox.width, _interactionBox.height);
        }
    }

    @Override
    public boolean click(float curX, float curY) {
        if (_interactionBox.contains(curX, curY)) {
            PlayGame.getPlayer().think(getDescription(), HOLD_LENGTH);
            return true;
        }
        return false;
    }

    @Override
    public void beautifyCursor(float curX, float curY) {
        if (_interactionBox.contains(curX, curY)) {
            PlayGame.getPlayer().setCursor(Player.CursorType.EXAMINE);
        }
    }

    @Override
    public String getItemInteractionMessage(ItemId id) {
        if (_itemInteractionMessages.containsKey(id)) {
            return _itemInteractionMessages.get(id);
        }
        return "";
    }

    @Override
    public void setItemInteractionMessage(ItemId id, String message) {
        _itemInteractionMessages.put(id, message);
    }

    @Override
    public void update(float delta) {
    }

    @Override
    public void registerListener(InteractionListener listener) {
    }

    @Override
    public void setSoundEffect(SoundId soundId) {
    }

    @Override
    public boolean isItemInteractable() {
        return false;
    }

    @Override
    public boolean itemInteract(ItemId id) {
        return false;
    }

    @Override
    public boolean checkInteraction(float curX, float curY) {
        return _interactionBox.contains(curX, curY);
    }
} public class ItemInteractableScenic extends GeneralDescribable {

    private ItemId _interactableItem;
    private PickupableItem _returnItem;

    private String _interactThought;

    public ItemInteractableScenic(String description,
                                  String interactThought,
                                  Rectangle interactionBox,
                                  ItemId interactableItem,
                                  PickupableItem newItem) {
        super(description, interactionBox);
        _interactableItem = interactableItem;
        _returnItem = newItem;
        _interactThought = interactThought;
    }

    @Override
    public boolean click(float curX, float curY) {
        return super.click(curX, curY);
    }

    @Override
    public boolean isItemInteractable() {
        return true;
    }

    @Override
    public boolean itemInteract(ItemId id) {

        if (id == _interactableItem) {
            PlayGame.getPlayer().getInventory().addItem(_returnItem);
            PlayGame.getPlayer().getInventory().removeItem(id);
            PlayGame.getPlayer().think(_interactThought);
            return true;
        }

        return false;
    }

    @Override
    public void beautifyCursor(float curX, float curY) {
        super.beautifyCursor(curX, curY);
        if (PlayGame.getPlayer().getInteractionMode() != Player.InteractionMode.ITEM_INTERACTION) {
            return;
        }
        if (getInteractionBox().contains(curX, curY)) {
            PlayGame.getPlayer().setCursor(Player.CursorType.PICKUP);
        }
    }
} public class Messages {

    private static final Properties _messages = init();

    private static Properties init() {
        Properties properties = new Properties();
        try {
            InputStream in = Gdx.files.internal("properties/descriptions.properties").read();
            properties.load(in);
            in.close();
        } catch (IOException e) {
            Gdx.app.log("ERROR", e.getMessage());
        }
        return properties;
    }

    public static Properties getProperties() {
        return _messages;
    }

    public static String getText(String key) {
        if (_messages.containsKey(key)) {
            return _messages.getProperty(key);
        }
        return "<NO TEXT>";
    }
} public class PlayGame implements Screen {
    // 640x360
    public static final float GAME_WIDTH = 160;
    public static final float GAME_HEIGHT = 90;

    private OrthographicCamera _camera;
    private Viewport _viewport;
    private SpriteBatch _batch;
    private ShapeRenderer _shapeRenderer;
    private static boolean _debug = false;
    private static boolean _placing = false;
    private static Transition _transition;

    // Global state
    private static Player _player;
    private static RoomManager _roomManager;
    private static FlagManager _flagManager;
    private static boolean _paused;

    public static Player getPlayer() {
        return _player;
    }

    public static RoomManager getRoomManager() {
        return _roomManager;
    }

    public static FlagManager getFlagManager() {
        return _flagManager;
    }

    @Override
    public void show() {
        _roomManager = new RoomManager();
        _flagManager = new FlagManager();

        SlideTransition.setTransitionSpeed(SlideTransition.DEFAULT_SPEED);

        PlayerInventory inventory = new PlayerInventory();

        _batch = new SpriteBatch();
        _shapeRenderer = new ShapeRenderer();
        _camera = new OrthographicCamera();
        _camera.translate(GAME_WIDTH / 2, GAME_HEIGHT / 2);
        _viewport = new StretchViewport(GAME_WIDTH, GAME_HEIGHT, _camera);

        _player = new Player(this, inventory);
        _player.startGame(RoomId.CAR);

        Gdx.input.setInputProcessor(_player);
    }

    public static void setPaused(boolean paused) {
        _paused = paused;
    }

    public static boolean getPaused() {
        return _paused;
    }

    public void renderPauseDialog() {
        // Fade to black
        Gdx.gl.glEnable(GL20.GL_BLEND);
        Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
        _shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
        _shapeRenderer.setColor(0.05f, 0.05f, 0.05f, 0.5f);
        _shapeRenderer.rect(0, 0, GAME_WIDTH, GAME_HEIGHT);
        _shapeRenderer.end();
        Gdx.gl.glDisable(GL20.GL_BLEND);

        _batch.begin();
        // TODO: Factor this out (at the very least)
        FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("font/raleway/Raleway-Medium.ttf"));
        FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
        parameter.size = 32;
        BitmapFont bfont = generator.generateFont(parameter);
        GlyphLayout layout = new GlyphLayout();
        bfont.getData().setScale(0.12f, 0.12f);

        float x_pos = GAME_WIDTH / 2;
        float y_pos = 20 / 2;

        bfont.setColor(1.0f, 1.0f, 1.0f, 0.5f);

        layout.setText(bfont, Messages.getText("system.exit"));

        x_pos -= layout.width / 2;
        y_pos += layout.height / 2;

        bfont.draw(_batch, layout, x_pos, y_pos);
        _batch.end();
    }

    @Override
    public void render(float delta) {
        _camera.update();
        _batch.setProjectionMatrix(_camera.combined);
        _shapeRenderer.setProjectionMatrix(_camera.combined);

        if (_paused) {
            renderPauseDialog();
            return;
        }

        updateGame(delta);
        renderGame();
    }

    private void updateGame(float delta) {
        Vector3 position = screenToWorldPosition(Gdx.input.getX(), Gdx.input.getY());
        getPlayer().updateCursor(position);
        getPlayer().update(delta);

        if (_transition != null) {
            _transition.update(delta);
        }
    }

    private void renderCurrentRoom() {
        Gdx.gl.glClearColor(1, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        Gdx.gl.glEnable(GL20.GL_BLEND);
        Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);

        // Draw room sprites
        _batch.begin();
        getPlayer().getCurrentRoom().draw(_batch);
        _batch.end();
        Gdx.gl.glDisable(GL20.GL_BLEND);

        // Draw room shapes
        Gdx.gl.glEnable(GL20.GL_BLEND);
        Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
        _shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
        getPlayer().getCurrentRoom().draw(_shapeRenderer);
        _shapeRenderer.end();
        Gdx.gl.glDisable(GL20.GL_BLEND);
    }

    private void renderFX() {
        // Draw room FX
        Gdx.gl.glEnable(GL20.GL_BLEND);
        Gdx.gl.glBlendFunc(GL20.GL_DST_COLOR, GL20.GL_ONE);
        _batch.begin();
        getPlayer().getCurrentRoom().drawFx(_batch);
        _batch.end();
        Gdx.gl.glDisable(GL20.GL_BLEND);
    }

    private void renderInventory() {
        Gdx.gl.glEnable(GL20.GL_BLEND);
        Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);

        // Draw inventory bag
        _batch.begin();
        getPlayer().getInventory().drawInventory(_batch);
        _batch.end();
        Gdx.gl.glDisable(GL20.GL_BLEND);
    }

    private void renderSelectedItem() {
        _batch.begin();
        getPlayer().getInventory().drawSelectedItemSprite(_batch);
        _batch.end();
    }

    private void renderTransitions() {
        // Draw transitions, if applicable
        if (_transition == null) {
            return;
        }

        Gdx.gl.glEnable(GL20.GL_BLEND);
        Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
        _shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
        _transition.draw(_shapeRenderer);
        _shapeRenderer.end();
        _batch.begin();
        _transition.draw(_batch);
        _batch.end();
        Gdx.gl.glDisable(GL20.GL_BLEND);
    }

    private void renderHUD() {
        // Draw player thoughts over everything
        Gdx.gl.glEnable(GL20.GL_BLEND);
        Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
        _shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
        getPlayer().getThought().draw(_shapeRenderer);
        _shapeRenderer.end();
        _batch.begin();
        getPlayer().getThought().draw(_batch);
        _batch.end();
        Gdx.gl.glDisable(GL20.GL_BLEND);
    }

    private void renderPlacementHelper() {
        Gdx.gl.glEnable(GL20.GL_BLEND);
        Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
        _shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
        _player.getPlacementRectangle().draw(_shapeRenderer);
        _shapeRenderer.end();
        Gdx.gl.glDisable(GL20.GL_BLEND);
    }

    /**
     * Breaks down rendering of game world into different layers
     */
    private void renderGame() {
        renderCurrentRoom();
        renderFX();
        renderInventory();
        renderPlacementHelper();

        // Draw inventory, if applicable
        if (getPlayer().getInteractionMode() == ITEM_INTERACTION) {
            renderSelectedItem();
        }

        renderTransitions();
        renderHUD();
    }

    @Override
    public void resize(int width, int height) {
        _viewport.update(width, height);
    }

    @Override
    public void pause() {
        // TODO Auto-generated method stub

    }

    @Override
    public void resume() {
        // TODO Auto-generated method stub

    }

    @Override
    public void hide() {
        // TODO Auto-generated method stub
    }

    @Override
    public void dispose() {
    }

    public Vector3 screenToWorldPosition(int screenX, int screenY) {
        Vector3 vecCursorPos = new Vector3(screenX, screenY, 0);
        _camera.unproject(vecCursorPos);
        return vecCursorPos;
    }

    /**
     * Play transition animation on transition layer
     *
     * @param t The transition animation to play
     */
    public static void playTransition(Transition t) {
        _transition = t;
        _transition.play();
    }

    public static void setDebug(boolean debug) {
        _debug = debug;
    }

    public static boolean getDebug() {
        return _debug;
    }

    public static void setPlacementMode(boolean mode) {
        _player.think("Placement mode: " + (mode ? "ON" : "OFF"));
        _placing = mode;
    }

    public static boolean getPlacementMode() {
        return _placing;
    }
} public class GameUtil {
    public enum Direction {
        UP, DOWN, LEFT, RIGHT
    }

    public static boolean isNotNullOrEmpty(String s) {
        return !(s == null || "".equals(s));
    }
} public enum FlagId {
    FLAG_BODY_FOUND,
    FLAG_CAR_FOUND,
    FLAG_CAR_SNOWREMOVED,
    FLAG_SHED_OPENED,
    FLAG_NIGHT_TIME,
    FLAG_BOARDS_REMOVED,
    FLAG_TORCH_PLACED,
    FLAG_ENTERED_BATHROOM,
    FLAG_KEYS_MISSING,
    FLAG_KEYS_APPEARED,
    FLAG_KEYS_FOUND,
    FLAG_HALLWAY_SCARE,
    FLAG_HALLWAY_SCARE_OVER
}
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Rectangle;
import com.kowaisugoi.game.audio.AudioManager;
import com.kowaisugoi.game.audio.MusicId;
import com.kowaisugoi.game.audio.SoundId;
import com.kowaisugoi.game.graphics.FireAnimation;
import com.kowaisugoi.game.interactables.objects.ItemId;
import com.kowaisugoi.game.interactables.objects.PickupableItem;
import com.kowaisugoi.game.interactables.passages.DirectionalPassage;
import com.kowaisugoi.game.interactables.passages.Passage;
import com.kowaisugoi.game.interactables.scenic.Describable;
import com.kowaisugoi.game.interactables.scenic.GeneralDescribable;
import com.kowaisugoi.game.interactables.scenic.ItemInteractableScenic;
import com.kowaisugoi.game.messages.Messages;
import com.kowaisugoi.game.screens.PlayGame;
import com.kowaisugoi.game.system.GameUtil;
import java.util.LinkedList;
import java.util.List;
import static com.kowaisugoi.game.control.flags.FlagId.*;
package com.kowaisugoi.game.rooms;




public class RoomMainHall extends StandardRoom {

    private static final String ROOM_URL = "rooms/mainhall/mainhall.png";
    private static final String ROOM_URL2 = "rooms/mainhall/mainhall_dark.png";

    private static final Sprite _roomSprite1 = new Sprite(new Texture(ROOM_URL));
    private static final Sprite _roomSprite2 = new Sprite(new Texture(ROOM_URL2));

    private FireAnimation _fireAnimation;

    private PickupableItem _carkeys;

    private List<Describable> _describableList2;

    public RoomMainHall() {
        super(new Sprite(new Texture(ROOM_URL)));

        _describableList2 = new LinkedList<Describable>();

        Passage hallDoor = new DirectionalPassage(RoomId.MAIN_HALL,
                RoomId.HALLWAY,
                new Rectangle(0, 3, 29, 75),
                GameUtil.Direction.LEFT);
        Passage turnAround = new DirectionalPassage(RoomId.MAIN_HALL,
                RoomId.FRONTYARD,
                new Rectangle(55, 0, 50, 10),
                GameUtil.Direction.DOWN);
        Passage crawlDoor = new DirectionalPassage(RoomId.MAIN_HALL,
                RoomId.CRAWLSPACE,
                new Rectangle(120, 10, 27, 27),
                GameUtil.Direction.UP);

        Describable paintingDescription = new GeneralDescribable(Messages.getText("mainhall.painting.thought"),