From 1167094c6a987043248c4cc4fe45d8a7cd6f5d38 Mon Sep 17 00:00:00 2001
From: Chris Kearney <chris.kearney@urbanairship.com>
Date: Sat, 23 Aug 2014 07:45:59 -0700
Subject: [PATCH] started the very basics of NPCs

---
 .../java/com/comandante/creeper/Main.java     |  5 ++
 .../creeper/command/DefaultCommandType.java   |  8 +-
 .../creeper/managers/GameManager.java         | 48 ++++++-----
 .../creeper/managers/HelpManager.java         |  2 +-
 .../creeper/managers/NPCManager.java          | 82 +++++++++++++++++++
 .../creeper/managers/PlayerManager.java       |  5 ++
 .../creeper/managers/PlayerManagerMapDB.java  | 14 ++++
 .../com/comandante/creeper/model/Npc.java     | 33 ++++++++
 .../com/comandante/creeper/model/NpcType.java | 36 ++++++++
 .../com/comandante/creeper/model/Room.java    | 13 +++
 10 files changed, 220 insertions(+), 26 deletions(-)
 create mode 100644 src/main/java/com/comandante/creeper/managers/NPCManager.java
 create mode 100644 src/main/java/com/comandante/creeper/model/Npc.java
 create mode 100644 src/main/java/com/comandante/creeper/model/NpcType.java

diff --git a/src/main/java/com/comandante/creeper/Main.java b/src/main/java/com/comandante/creeper/Main.java
index 87a6b65e..71c4306a 100644
--- a/src/main/java/com/comandante/creeper/Main.java
+++ b/src/main/java/com/comandante/creeper/Main.java
@@ -4,6 +4,8 @@ import com.comandante.creeper.managers.GameManager;
 import com.comandante.creeper.managers.PlayerManager;
 import com.comandante.creeper.managers.PlayerManagerMapDB;
 import com.comandante.creeper.managers.RoomManager;
+import com.comandante.creeper.model.Npc;
+import com.comandante.creeper.model.NpcType;
 import com.comandante.creeper.model.Player;
 import com.comandante.creeper.model.PlayerMetadata;
 import com.comandante.creeper.model.Room;
@@ -51,6 +53,9 @@ public class Main {
 
         GameManager gameManager = new GameManager(roomManager, playerManager);
 
+        Npc derper1 = new Npc(NpcType.DERPER);
+        gameManager.getNpcManager().saveNpc(derper1);
+        roomManager.getRoom(1).addPresentNpc(derper1.getNpcId());
         CreeperServer creeperServer = new CreeperServer(8080, db);
         creeperServer.run(gameManager);
 
diff --git a/src/main/java/com/comandante/creeper/command/DefaultCommandType.java b/src/main/java/com/comandante/creeper/command/DefaultCommandType.java
index 6ed5aa43..cbca6622 100644
--- a/src/main/java/com/comandante/creeper/command/DefaultCommandType.java
+++ b/src/main/java/com/comandante/creeper/command/DefaultCommandType.java
@@ -6,10 +6,10 @@ import java.util.HashSet;
 import java.util.Set;
 
 public enum DefaultCommandType implements CommandType {
-    MOVE_NORTH(new HashSet<String>(Arrays.asList("n", "North")), false, "Move north."),
-    MOVE_SOUTH(new HashSet<String>(Arrays.asList("s", "South")), false, "Move south."),
-    MOVE_EAST(new HashSet<String>(Arrays.asList("e", "East")), false, "Move east."),
-    MOVE_WEST(new HashSet<String>(Arrays.asList("w", "West")), false, "Move west."),
+    MOVE_NORTH(new HashSet<String>(Arrays.asList("n", "north")), false, "Move north."),
+    MOVE_SOUTH(new HashSet<String>(Arrays.asList("s", "south")), false, "Move south."),
+    MOVE_EAST(new HashSet<String>(Arrays.asList("e", "east")), false, "Move east."),
+    MOVE_WEST(new HashSet<String>(Arrays.asList("w", "west")), false, "Move west."),
     SAY(new HashSet<String>(Arrays.asList("say")), false, "Say something to the current room."),
     TELL(new HashSet<String>(Arrays.asList("tell", "t")), false, "Tell something to a player in private."),
     GOSSIP(new HashSet<String>(Arrays.asList("gossip")), false, "Gossip to the entire server."),
diff --git a/src/main/java/com/comandante/creeper/managers/GameManager.java b/src/main/java/com/comandante/creeper/managers/GameManager.java
index 54fa0a2f..21d7343a 100644
--- a/src/main/java/com/comandante/creeper/managers/GameManager.java
+++ b/src/main/java/com/comandante/creeper/managers/GameManager.java
@@ -3,16 +3,16 @@ package com.comandante.creeper.managers;
 
 import com.comandante.creeper.command.DefaultCommandType;
 import com.comandante.creeper.model.Movement;
+import com.comandante.creeper.model.Npc;
+import com.comandante.creeper.model.NpcType;
 import com.comandante.creeper.model.Player;
 import com.comandante.creeper.model.Room;
 import com.comandante.creeper.server.CreeperSession;
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Interners;
-import com.google.common.collect.Sets;
 import org.apache.commons.lang3.StringUtils;
 import org.fusesource.jansi.Ansi;
-import org.jboss.netty.channel.Channel;
 import org.jboss.netty.channel.MessageEvent;
 
 import java.util.ArrayList;
@@ -37,6 +37,7 @@ public class GameManager {
     private final PlayerManager playerManager;
     private final HelpManager helpManager;
     private final NewUserRegistrationManager newUserRegistrationManager;
+    private final NPCManager npcManager;
 
     public NewUserRegistrationManager getNewUserRegistrationManager() {
         return newUserRegistrationManager;
@@ -47,6 +48,11 @@ public class GameManager {
         this.playerManager = playerManager;
         this.newUserRegistrationManager = new NewUserRegistrationManager(playerManager);
         this.helpManager = new HelpManager();
+        this.npcManager = new NPCManager(roomManager, playerManager);
+    }
+
+    public NPCManager getNpcManager() {
+        return npcManager;
     }
 
     public HelpManager getHelpManager() {
@@ -77,14 +83,7 @@ public class GameManager {
         return Optional.absent();
     }
 
-    public Set<Player> getPresentPlayers(Room room) {
-        Set<String> presentPlayerIds = room.getPresentPlayerIds();
-        Set<Player> players = Sets.newHashSet();
-        for (String playerId: presentPlayerIds) {
-            players.add(playerManager.getPlayer(playerId));
-        }
-        return ImmutableSet.copyOf(players);
-    }
+
 
     public void who(Player player) {
         Set<Player> allPlayers = getAllPlayers();
@@ -142,7 +141,7 @@ public class GameManager {
         while (rooms.hasNext()) {
             Map.Entry<Integer, Room> next = rooms.next();
             Room room = next.getValue();
-            Set<Player> presentPlayers = getPresentPlayers(room);
+            Set<Player> presentPlayers = playerManager.getPresentPlayers(room);
             for (Player player : presentPlayers) {
                 builder.add(player);
             }
@@ -162,7 +161,7 @@ public class GameManager {
             Room sourceRoom = roomManager.getRoom(movement.getSourceRoomId());
             Room destinationRoom = roomManager.getRoom(movement.getDestinationRoomId());
             sourceRoom.removePresentPlayer(movement.getPlayer().getPlayerId());
-            for (Player next : getPresentPlayers(sourceRoom)) {
+            for (Player next : playerManager.getPresentPlayers(sourceRoom)) {
                 StringBuilder sb = new StringBuilder();
                 sb.append(movement.getPlayer().getPlayerName());
                 if (movement.getOriginalMovementCommand().equals(DefaultCommandType.MOVE_NORTH)) {
@@ -179,7 +178,7 @@ public class GameManager {
                 sb.append("\r\n");
             next.getChannel().write(sb.toString());
             }
-            for (Player next : getPresentPlayers(destinationRoom)) {
+            for (Player next : playerManager.getPresentPlayers(destinationRoom)) {
                 next.getChannel().write(movement.getPlayer().getPlayerName() + " arrived.\r\n");
             }
             destinationRoom.addPresentPlayer(movement.getPlayer().getPlayerId());
@@ -189,7 +188,7 @@ public class GameManager {
     public void placePlayerInLobby(Player player) {
         Room room = roomManager.getRoom(LOBBY_ID);
         room.addPresentPlayer(player.getPlayerId());
-        for (Player next : getPresentPlayers(room)) {
+        for (Player next : playerManager.getPresentPlayers(room)) {
             if (next.getPlayerId().equals(player.getPlayerId())) {
                 continue;
             }
@@ -204,7 +203,7 @@ public class GameManager {
         }
 
         Room playerCurrentRoom = playerCurrentRoomOpt.get();
-        Set<Player> presentPlayers = getPresentPlayers(playerCurrentRoom);
+        Set<Player> presentPlayers = playerManager.getPresentPlayers(playerCurrentRoom);
 
         for (Player presentPlayer : presentPlayers) {
             StringBuilder stringBuilder = new StringBuilder();
@@ -235,7 +234,7 @@ public class GameManager {
         }
     }
 
-    private void printExits(Room room, Channel channel) {
+    private String getExits(Room room) {
         StringBuilder stringBuilder = new StringBuilder();
         stringBuilder.append("exits: ");
         stringBuilder.append(new Ansi().fg(Ansi.Color.BLUE).toString());
@@ -251,22 +250,29 @@ public class GameManager {
         if (room.getWestId().isPresent()) {
             stringBuilder.append("west ");
         }
-        stringBuilder.append(new Ansi().reset().toString());
-        channel.write(stringBuilder.toString() + "\r\n");
+        stringBuilder.append("\r\n").append(new Ansi().reset().toString());
+        return stringBuilder.toString();
     }
 
     public void currentRoomLogic(CreeperSession creeperSession, MessageEvent e) {
         final Player player = playerManager.getPlayerByUsername(creeperSession.getUsername().get());
         final Room playerCurrentRoom = getPlayerCurrentRoom(player).get();
-        e.getChannel().write(playerCurrentRoom.getRoomDescription() + "\r\n");
+        StringBuilder sb = new StringBuilder();
+        sb.append(playerCurrentRoom.getRoomDescription()).append("\r\n");
         for (String searchPlayerId : playerCurrentRoom.getPresentPlayerIds()) {
             if (searchPlayerId.equals(player.getPlayerId())) {
                 continue;
             }
             Player searchPlayer = playerManager.getPlayer(searchPlayerId);
-            e.getChannel().write(searchPlayer.getPlayerName() + " is here.\r\n");
+            sb.append(searchPlayer.getPlayerName()).append(" is here.\r\n");
+        }
+        for (String npcId : playerCurrentRoom.getNpcIds()) {
+            Npc npc = npcManager.getNpc(npcId);
+            NpcType npcType = npc.getNpcType();
+            sb.append("A ").append(npcType.getNpcName()).append(" is here.\r\n");
         }
-        printExits(playerCurrentRoom, e.getChannel());
+        sb.append(getExits(playerCurrentRoom));
+        player.getChannel().write(sb.toString());
     }
 
 }
diff --git a/src/main/java/com/comandante/creeper/managers/HelpManager.java b/src/main/java/com/comandante/creeper/managers/HelpManager.java
index 2a5c4c97..a513b6bb 100644
--- a/src/main/java/com/comandante/creeper/managers/HelpManager.java
+++ b/src/main/java/com/comandante/creeper/managers/HelpManager.java
@@ -10,7 +10,7 @@ public class HelpManager {
     public void printHelp(Player player, String originalMessage) {
         StringBuilder sb = new StringBuilder();
         DefaultCommandType[] values = DefaultCommandType.values();
-        sb.append(new Ansi().bg(Ansi.Color.RED).toString());
+        sb.append(new Ansi().fg(Ansi.Color.RED).toString());
         for (DefaultCommandType defaultCommandType : values) {
             if (defaultCommandType.equals(DefaultCommandType.UNKNOWN)) {
                 continue;
diff --git a/src/main/java/com/comandante/creeper/managers/NPCManager.java b/src/main/java/com/comandante/creeper/managers/NPCManager.java
new file mode 100644
index 00000000..fe978792
--- /dev/null
+++ b/src/main/java/com/comandante/creeper/managers/NPCManager.java
@@ -0,0 +1,82 @@
+package com.comandante.creeper.managers;
+
+import com.comandante.creeper.model.Npc;
+import com.comandante.creeper.model.NpcType;
+import com.comandante.creeper.model.Player;
+import com.comandante.creeper.model.Room;
+import com.google.common.base.Optional;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class NPCManager {
+
+    private final ConcurrentHashMap<String, Npc> npcs = new ConcurrentHashMap<>();
+    private final RoomManager roomManager;
+    private final PlayerManager playerManager;
+    private final ExecutorService phraserService;
+
+    public NPCManager(RoomManager roomManager, PlayerManager playerManager) {
+        this.roomManager = roomManager;
+        this.playerManager = playerManager;
+        this.phraserService = Executors.newSingleThreadExecutor();
+        phraserService.submit(new Phraser());
+    }
+
+    public void saveNpc(Npc npc) {
+        npcs.put(npc.getNpcId(), npc);
+    }
+
+    public Npc getNpc(String npcId) {
+        return npcs.get(npcId);
+    }
+
+    public Optional<Room> getNpcCurrentRoom(String npcId) {
+        Iterator<Map.Entry<Integer, Room>> rooms = roomManager.getRooms();
+        while (rooms.hasNext()) {
+            Map.Entry<Integer, Room> next = rooms.next();
+            Room room = next.getValue();
+            if (room.getNpcIds().contains(npcId)) {
+                return Optional.of(room);
+            }
+        }
+        return Optional.absent();
+    }
+
+    class Phraser implements Runnable {
+        private Random randomGenerator = new Random();
+        @Override
+        public void run() {
+            while (true) {
+                try {
+                    Thread.sleep(5000);
+                    for (Map.Entry<String, Npc> next : npcs.entrySet()) {
+                        Npc npc = next.getValue();
+                        NpcType npcType = npc.getNpcType();
+                        long phraseTimestamp = npc.getPhraseTimestamp();
+                        long phraseInterval = npcType.getPhrasesIntervalMs();
+                        long now = System.currentTimeMillis();
+                        if (now - phraseTimestamp > phraseInterval) {
+                            String phrase = npcType.getPhrases().get(randomGenerator.nextInt(npcType.getPhrases().size()));
+                            Optional<Room> roomOpt = getNpcCurrentRoom(npc.getNpcId());
+                            if (roomOpt.isPresent()) {
+                                Set<Player> presentPlayers = playerManager.getPresentPlayers(roomOpt.get());
+                                for (Player player : presentPlayers) {
+                                    player.getChannel().write(npcType.getNpcName() + ": " + phrase + "\r\n");
+                                    npc.setPhraseTimestamp(System.currentTimeMillis());
+                                }
+                            }
+                        }
+                    }
+                } catch (Exception e) {
+                    System.out.println(e);
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/comandante/creeper/managers/PlayerManager.java b/src/main/java/com/comandante/creeper/managers/PlayerManager.java
index c147bce5..7c01d890 100644
--- a/src/main/java/com/comandante/creeper/managers/PlayerManager.java
+++ b/src/main/java/com/comandante/creeper/managers/PlayerManager.java
@@ -2,11 +2,16 @@ package com.comandante.creeper.managers;
 
 import com.comandante.creeper.model.Player;
 import com.comandante.creeper.model.PlayerMetadata;
+import com.comandante.creeper.model.Room;
 
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Set;
 
 public interface PlayerManager {
+
+    public Set<Player> getPresentPlayers(Room room);
+
     PlayerMetadata getPlayerMetadata(String playerId);
 
     void savePlayerMetadata(PlayerMetadata playerMetadata);
diff --git a/src/main/java/com/comandante/creeper/managers/PlayerManagerMapDB.java b/src/main/java/com/comandante/creeper/managers/PlayerManagerMapDB.java
index 2dc61e42..df4adb47 100644
--- a/src/main/java/com/comandante/creeper/managers/PlayerManagerMapDB.java
+++ b/src/main/java/com/comandante/creeper/managers/PlayerManagerMapDB.java
@@ -4,11 +4,15 @@ package com.comandante.creeper.managers;
 import com.comandante.creeper.model.PlayerMetadataSerializer;
 import com.comandante.creeper.model.Player;
 import com.comandante.creeper.model.PlayerMetadata;
+import com.comandante.creeper.model.Room;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
 import org.apache.commons.codec.binary.Base64;
 import org.mapdb.DB;
 import org.mapdb.HTreeMap;
 
 import java.util.Iterator;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 public class PlayerManagerMapDB implements PlayerManager {
@@ -26,6 +30,16 @@ public class PlayerManagerMapDB implements PlayerManager {
         }
     }
 
+    @Override
+    public Set<Player> getPresentPlayers(Room room) {
+        Set<String> presentPlayerIds = room.getPresentPlayerIds();
+        Set<Player> players = Sets.newHashSet();
+        for (String playerId: presentPlayerIds) {
+            players.add(getPlayer(playerId));
+        }
+        return ImmutableSet.copyOf(players);
+    }
+
     @Override
     public PlayerMetadata getPlayerMetadata(String playerId) {
         return playerMetadataStore.get(playerId);
diff --git a/src/main/java/com/comandante/creeper/model/Npc.java b/src/main/java/com/comandante/creeper/model/Npc.java
new file mode 100644
index 00000000..00bebb6a
--- /dev/null
+++ b/src/main/java/com/comandante/creeper/model/Npc.java
@@ -0,0 +1,33 @@
+package com.comandante.creeper.model;
+
+
+import java.util.UUID;
+
+public class Npc {
+
+    private final String npcId;
+    private final NpcType npcType;
+    private long phraseTimestamp;
+
+    public Npc(NpcType npcType) {
+        npcId = UUID.randomUUID().toString();
+        this.npcType = npcType;
+        this.phraseTimestamp = 0;
+    }
+
+    public String getNpcId() {
+        return npcId;
+    }
+
+    public NpcType getNpcType() {
+        return npcType;
+    }
+
+    public long getPhraseTimestamp() {
+        return phraseTimestamp;
+    }
+
+    public void setPhraseTimestamp(long phraseTimestamp) {
+        this.phraseTimestamp = phraseTimestamp;
+    }
+}
diff --git a/src/main/java/com/comandante/creeper/model/NpcType.java b/src/main/java/com/comandante/creeper/model/NpcType.java
new file mode 100644
index 00000000..d87d1239
--- /dev/null
+++ b/src/main/java/com/comandante/creeper/model/NpcType.java
@@ -0,0 +1,36 @@
+package com.comandante.creeper.model;
+
+import org.fusesource.jansi.Ansi;
+
+import java.util.Arrays;
+import java.util.List;
+
+
+public enum NpcType {
+
+    DERPER(Arrays.asList(
+            "Zug zug.",
+            "Don't provoke me."), 300000, new StringBuilder().append(new Ansi().fg(Ansi.Color.RED).toString()).append("derper").append(new Ansi().reset().toString()).toString());
+
+    private final List<String> phrases;
+    private final long phrasesIntervalMs;
+    private final String npcName;
+
+    NpcType(List<String> phrases, long phrasesIntervalMs, String npcName) {
+        this.phrases = phrases;
+        this.phrasesIntervalMs = phrasesIntervalMs;
+        this.npcName = npcName;
+    }
+
+    public List<String> getPhrases() {
+        return phrases;
+    }
+
+    public long getPhrasesIntervalMs() {
+        return phrasesIntervalMs;
+    }
+
+    public String getNpcName() {
+        return npcName;
+    }
+}
diff --git a/src/main/java/com/comandante/creeper/model/Room.java b/src/main/java/com/comandante/creeper/model/Room.java
index 5ff0cc33..04fa6c55 100644
--- a/src/main/java/com/comandante/creeper/model/Room.java
+++ b/src/main/java/com/comandante/creeper/model/Room.java
@@ -16,6 +16,7 @@ public class Room {
     public String roomDescription;
     private Set<String> presentPlayerIds = Sets.<String>newConcurrentHashSet();
     private Set<String> afkPlayerIds = Sets.<String>newConcurrentHashSet();
+    private Set<String> npcIds = Sets.newConcurrentHashSet();
 
     public Room(Integer roomId, Optional<Integer> northId, Optional<Integer> westId, Optional<Integer> eastId, Optional<Integer> southId, String roomDescription) {
         this.roomId = roomId;
@@ -26,6 +27,18 @@ public class Room {
         this.roomDescription = roomDescription;
     }
 
+    public void addPresentNpc(String npcId) {
+        npcIds.add(npcId);
+    }
+
+    public void removePresentNpc(String npcId) {
+        npcIds.remove(npcId);
+    }
+
+    public Set<String> getNpcIds() {
+        return npcIds;
+    }
+
     public java.util.Set<String> getPresentPlayerIds()  {
         return presentPlayerIds;
     }
-- 
GitLab