ConversationManager.java

package com.jadventure.game.conversation;

import com.jadventure.game.GameBeans;
import com.jadventure.game.entities.NPC;
import com.jadventure.game.entities.Player;
import com.jadventure.game.items.Item;
import com.jadventure.game.repository.ItemRepository;
import com.jadventure.game.QueueProvider;
import com.jadventure.game.DeathException;
import com.jadventure.game.Trading;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.jadventure.game.repository.NpcRepository;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ConversationManager {
    private static NpcRepository npcRepository = NpcRepository.createRepo();
    private static ConversationManager instance = null;
    private Map<NPC, List<Line>> lines = new HashMap<NPC, List<Line>>();
    private static final Map<String, ActionType> ACTION_TYPE_MAP = new HashMap<>();
    private static final Map<String, ConditionType> CONDITION_TYPE_MAP = new HashMap<>();

    static {
        ACTION_TYPE_MAP.put("none", ActionType.NONE);
        ACTION_TYPE_MAP.put("attack", ActionType.ATTACK);
        ACTION_TYPE_MAP.put("buy", ActionType.BUY);
        ACTION_TYPE_MAP.put("sell", ActionType.SELL);
        ACTION_TYPE_MAP.put("trade", ActionType.TRADE);
        ACTION_TYPE_MAP.put("give", ActionType.GIVE);
        ACTION_TYPE_MAP.put("take", ActionType.TAKE);
        CONDITION_TYPE_MAP.put("none", ConditionType.NONE);
        CONDITION_TYPE_MAP.put("ally", ConditionType.ALLY);
        CONDITION_TYPE_MAP.put("enemy", ConditionType.ENEMY);
        CONDITION_TYPE_MAP.put("level", ConditionType.LEVEL);
        CONDITION_TYPE_MAP.put("item", ConditionType.ITEM);
        CONDITION_TYPE_MAP.put("char type", ConditionType.CHAR_TYPE);
    }

    public ConversationManager() {
       load(); 
    } 

    public static ConversationManager getInstance() {
        if (instance == null) {
            instance = new ConversationManager();
        }
        return instance;
    }

    private void load() {
        String fileName = "json/original_data/npcs.json";
        JsonParser parser = new JsonParser();
        File f = new File(fileName);
        try {
            Reader reader = new FileReader(fileName);
            JsonObject json = parser.parse(reader).getAsJsonObject();
            json = json.get("npcs").getAsJsonObject();
            Set<Map.Entry<String, JsonElement>> entries = json.entrySet();
            for (Map.Entry<String, JsonElement> entry : entries) {
                NPC npc = npcRepository.getNpc(entry.getKey());
                JsonObject details = entry.getValue().getAsJsonObject();
                if (details.get("conversations") != null) {
                    JsonArray conversation = details.get("conversations").getAsJsonArray();
                    addConversation(npc, conversation);
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    private void addConversation(NPC npc, JsonArray conversation) {
        List<Line> start = new ArrayList<>();
        int i = 0;
        for (JsonElement entry : conversation) {
            JsonObject details = entry.getAsJsonObject();
            start.add(getLine(i++, conversation));
        }
        lines.put(npc, start);
    }

    private Line getLine(int index, JsonArray conversation) {
        JsonObject line = conversation.get(index).getAsJsonObject();
        List<Integer> responses = new ArrayList<>();
        if (line.get("response") != null) {
            for (JsonElement i : line.get("response").getAsJsonArray()) {
                responses.add(i.getAsInt());
            }
        }
        String playerPrompt = line.get("player").getAsString();
        String text = line.get("text").getAsString();
        String[] con = line.get("condition").getAsString().split("=");
        ConditionType condition = CONDITION_TYPE_MAP.get(con[0]);
        String conditionParameter = (con.length == 1) ? "" : con[1];
        ActionType action = ACTION_TYPE_MAP.get(line.get("action").getAsString());
        return new Line(index, playerPrompt, text, condition, conditionParameter, responses, action);
    }

    public void startConversation(NPC npc, Player player) throws DeathException {
        List<Line> conversation = null;
        //Workaround as <code>lines.get(npc)</code> is not working.
        Iterator it = lines.entrySet().iterator();
        while (it.hasNext()) {
            @SuppressWarnings("unchecked")
            Map.Entry<NPC, List<Line>> entry = (Map.Entry<NPC, List<Line>>) it.next();
            if (entry.getKey().equals(npc)) {
                conversation = entry.getValue();
            }
            it.remove();
        }
        if (conversation != null) {
            Line start = null;
            for (Line l : conversation) {
                if ("".equals(l.getPlayerPrompt()) && 
                            ConversationManager.matchesConditions(npc, player, l)) {
                    start = l;
                    break;
                }
            }
            if (start != null) {
                QueueProvider.offer(start.getText());
                Line response = start.display(npc, player, conversation);
                triggerAction(start, npc, player);
                while (response != null) {
                   QueueProvider.offer(response.getText());
                   triggerAction(response, npc, player);
                   Line temp_response = response.display(npc, player, conversation);
                   response = temp_response;
               }
            }
        }
    }

    private void triggerAction(Line line, NPC npc, Player player) throws DeathException {
        switch (line.getAction()) {
            case ATTACK:
                QueueProvider.offer("\n" + npc.getName() + " is now attacking you!\n");
                player.attack(npc.getName());
                break;
            case TRADE:
                Trading t = new Trading(npc, player);
                t.trade(true, true);
                break;
        }     
    }

    public static boolean matchesConditions(NPC npc, Player player, Line line) {
        switch(line.getCondition()) {
            case ALLY:
                return npc.getAllies().contains(player.getCurrentCharacterType());
            case ENEMY:
                return npc.getEnemies().contains(player.getCurrentCharacterType());
            case LEVEL:
                int requiredLevel = Integer.parseInt(line.getConditionParameter());
                return player.getLevel() >= requiredLevel;
            case ITEM:
                ItemRepository itemRepo = GameBeans.getItemRepository();
                Item requiredItem = itemRepo.getItem(line.getConditionParameter());
                return player.hasItem(requiredItem);
            case CHAR_TYPE:
                String charType = line.getConditionParameter();
                return charType.equals(player.getCurrentCharacterType());
            default: // No condition
                return true;
        }
    }
}