diff --git a/pswgcommon b/pswgcommon
index cfd1d3e64..aec2c464d 160000
--- a/pswgcommon
+++ b/pswgcommon
@@ -1 +1 @@
-Subproject commit cfd1d3e6442dd2915ceb59d65d4cfe7392038147
+Subproject commit aec2c464dec92e5864302d3b56411ce981c2396a
diff --git a/serverdata/nge/conversation/rebel_recruiter/change_status.json b/serverdata/nge/conversation/rebel_recruiter/change_status.json
new file mode 100644
index 000000000..49779d2a6
--- /dev/null
+++ b/serverdata/nge/conversation/rebel_recruiter/change_status.json
@@ -0,0 +1,33 @@
+{
+ "npcMessage": {
+ "file": "conversation/faction_recruiter_rebel",
+ "key": "s_50"
+ },
+ "playerResponses": [
+ {
+ "playerMessage": {
+ "file": "conversation/faction_recruiter_rebel",
+ "key": "s_74"
+ },
+ "next": "rebel_recruiter/resign"
+ }
+ ],
+ "requirements": [
+ {
+ "type": "faction_name",
+ "args": {
+ "faction": "rebel"
+ }
+ },
+ {
+ "type": "faction_status",
+ "args": {
+ "statuses": [
+ "combatant",
+ "special_forces"
+ ]
+ }
+ }
+ ],
+ "events": []
+}
\ No newline at end of file
diff --git a/serverdata/nge/conversation/rebel_recruiter/imperial_root.json b/serverdata/nge/conversation/rebel_recruiter/imperial_root.json
new file mode 100644
index 000000000..944e44434
--- /dev/null
+++ b/serverdata/nge/conversation/rebel_recruiter/imperial_root.json
@@ -0,0 +1,16 @@
+{
+ "npcMessage": {
+ "file": "conversation/faction_recruiter_rebel",
+ "key": "s_230"
+ },
+ "playerResponses": [],
+ "requirements": [
+ {
+ "type": "faction_name",
+ "args": {
+ "faction": "imperial"
+ }
+ }
+ ],
+ "events": []
+}
\ No newline at end of file
diff --git a/serverdata/nge/conversation/rebel_recruiter/neutral_join.json b/serverdata/nge/conversation/rebel_recruiter/neutral_join.json
new file mode 100644
index 000000000..00f2bf062
--- /dev/null
+++ b/serverdata/nge/conversation/rebel_recruiter/neutral_join.json
@@ -0,0 +1,31 @@
+{
+ "npcMessage": {
+ "file": "conversation/faction_recruiter_rebel",
+ "key": "s_352"
+ },
+ "playerResponses": [
+ {
+ "playerMessage": {
+ "file": "conversation/faction_recruiter_rebel",
+ "key": "s_354"
+ },
+ "next": "rebel_recruiter/neutral_join_accept"
+ },
+ {
+ "playerMessage": {
+ "file": "conversation/faction_recruiter_rebel",
+ "key": "s_360"
+ },
+ "next": "rebel_recruiter/neutral_join_decline"
+ }
+ ],
+ "requirements": [
+ {
+ "type": "faction_name",
+ "args": {
+ "faction": "neutral"
+ }
+ }
+ ],
+ "events": []
+}
\ No newline at end of file
diff --git a/serverdata/nge/conversation/rebel_recruiter/neutral_join_accept.json b/serverdata/nge/conversation/rebel_recruiter/neutral_join_accept.json
new file mode 100644
index 000000000..8a37ae4c5
--- /dev/null
+++ b/serverdata/nge/conversation/rebel_recruiter/neutral_join_accept.json
@@ -0,0 +1,23 @@
+{
+ "npcMessage": {
+ "file": "conversation/faction_recruiter_rebel",
+ "key": "s_99"
+ },
+ "playerResponses": [],
+ "requirements": [
+ {
+ "type": "faction_name",
+ "args": {
+ "faction": "neutral"
+ }
+ }
+ ],
+ "events": [
+ {
+ "type": "faction_change",
+ "args": {
+ "faction": "rebel"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/serverdata/nge/conversation/rebel_recruiter/neutral_join_decline.json b/serverdata/nge/conversation/rebel_recruiter/neutral_join_decline.json
new file mode 100644
index 000000000..62a66987f
--- /dev/null
+++ b/serverdata/nge/conversation/rebel_recruiter/neutral_join_decline.json
@@ -0,0 +1,16 @@
+{
+ "npcMessage": {
+ "file": "conversation/faction_recruiter_rebel",
+ "key": "s_362"
+ },
+ "playerResponses": [],
+ "requirements": [
+ {
+ "type": "faction_name",
+ "args": {
+ "faction": "neutral"
+ }
+ }
+ ],
+ "events": []
+}
\ No newline at end of file
diff --git a/serverdata/nge/conversation/rebel_recruiter/neutral_root.json b/serverdata/nge/conversation/rebel_recruiter/neutral_root.json
new file mode 100644
index 000000000..2e4a0160b
--- /dev/null
+++ b/serverdata/nge/conversation/rebel_recruiter/neutral_root.json
@@ -0,0 +1,24 @@
+{
+ "npcMessage": {
+ "file": "conversation/faction_recruiter_rebel",
+ "key": "s_334"
+ },
+ "playerResponses": [
+ {
+ "playerMessage": {
+ "file": "conversation/faction_recruiter_rebel",
+ "key": "s_348"
+ },
+ "next": "rebel_recruiter/neutral_join"
+ }
+ ],
+ "requirements": [
+ {
+ "type": "faction_name",
+ "args": {
+ "faction": "neutral"
+ }
+ }
+ ],
+ "events": []
+}
\ No newline at end of file
diff --git a/serverdata/nge/conversation/rebel_recruiter/normal_root.json b/serverdata/nge/conversation/rebel_recruiter/normal_root.json
new file mode 100644
index 000000000..17c144b1a
--- /dev/null
+++ b/serverdata/nge/conversation/rebel_recruiter/normal_root.json
@@ -0,0 +1,33 @@
+{
+ "npcMessage": {
+ "file": "conversation/faction_recruiter_rebel",
+ "key": "s_216"
+ },
+ "playerResponses": [
+ {
+ "playerMessage": {
+ "file": "conversation/faction_recruiter_rebel",
+ "key": "s_49"
+ },
+ "next": "rebel_recruiter/change_status"
+ }
+ ],
+ "requirements": [
+ {
+ "type": "faction_name",
+ "args": {
+ "faction": "rebel"
+ }
+ },
+ {
+ "type": "faction_status",
+ "args": {
+ "statuses": [
+ "combatant",
+ "special_forces"
+ ]
+ }
+ }
+ ],
+ "events": []
+}
\ No newline at end of file
diff --git a/serverdata/nge/conversation/rebel_recruiter/resign.json b/serverdata/nge/conversation/rebel_recruiter/resign.json
new file mode 100644
index 000000000..8f6c30ac0
--- /dev/null
+++ b/serverdata/nge/conversation/rebel_recruiter/resign.json
@@ -0,0 +1,24 @@
+{
+ "npcMessage": {
+ "file": "conversation/faction_recruiter_rebel",
+ "key": "s_76"
+ },
+ "playerResponses": [
+ {
+ "playerMessage": {
+ "file": "conversation/faction_recruiter_rebel",
+ "key": "s_78"
+ },
+ "next": "rebel_recruiter/resign_accept"
+ }
+ ],
+ "requirements": [
+ {
+ "type": "faction_name",
+ "args": {
+ "faction": "rebel"
+ }
+ }
+ ],
+ "events": []
+}
\ No newline at end of file
diff --git a/serverdata/nge/conversation/rebel_recruiter/resign_accept.json b/serverdata/nge/conversation/rebel_recruiter/resign_accept.json
new file mode 100644
index 000000000..b6e28d6b1
--- /dev/null
+++ b/serverdata/nge/conversation/rebel_recruiter/resign_accept.json
@@ -0,0 +1,23 @@
+{
+ "npcMessage": {
+ "file": "conversation/faction_recruiter_rebel",
+ "key": "s_80"
+ },
+ "playerResponses": [],
+ "requirements": [
+ {
+ "type": "faction_name",
+ "args": {
+ "faction": "rebel"
+ }
+ }
+ ],
+ "events": [
+ {
+ "type": "faction_change",
+ "args": {
+ "faction": "neutral"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/serverdata/nge/conversation/spawn_conversation_map.msdb b/serverdata/nge/conversation/spawn_conversation_map.msdb
new file mode 100644
index 000000000..0eeb2dd64
--- /dev/null
+++ b/serverdata/nge/conversation/spawn_conversation_map.msdb
@@ -0,0 +1,3 @@
+file enabled
+TEXT BOOLEAN
+spawn_tat_3.sdb TRUE
\ No newline at end of file
diff --git a/serverdata/nge/conversation/spawn_tat_3.sdb b/serverdata/nge/conversation/spawn_tat_3.sdb
new file mode 100644
index 000000000..4a2bb9cca
--- /dev/null
+++ b/serverdata/nge/conversation/spawn_tat_3.sdb
@@ -0,0 +1,5 @@
+spawn_id conversation_id
+TEXT TEXT
+spawn_tat_3 rebel_recruiter/imperial_root
+spawn_tat_3 rebel_recruiter/neutral_root
+spawn_tat_3 rebel_recruiter/normal_root
\ No newline at end of file
diff --git a/src/main/java/com/projectswg/holocore/intents/gameplay/conversation/ConversationIntents.kt b/src/main/java/com/projectswg/holocore/intents/gameplay/conversation/ConversationIntents.kt
new file mode 100644
index 000000000..c305c3fc9
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/intents/gameplay/conversation/ConversationIntents.kt
@@ -0,0 +1,51 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+
+@file:Suppress("NOTHING_TO_INLINE")
+package com.projectswg.holocore.intents.gameplay.conversation
+
+import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject
+import com.projectswg.holocore.resources.support.objects.swg.custom.AIObject
+import me.joshlarson.jlcommon.control.Intent
+
+data class StartConversationIntent(val starter: CreatureObject, val npc: AIObject): Intent() {
+ companion object {
+ @JvmStatic inline fun broadcast(starter: CreatureObject, npc: AIObject) = StartConversationIntent(starter, npc).broadcast()
+ }
+}
+
+data class ProgressConversationIntent(val starter: CreatureObject, val selection: Int): Intent() {
+ companion object {
+ @JvmStatic inline fun broadcast(starter: CreatureObject, selection: Int) = ProgressConversationIntent(starter, selection).broadcast()
+ }
+}
+
+data class StopConversationIntent(val creatureObject: CreatureObject): Intent() {
+ companion object {
+ @JvmStatic inline fun broadcast(creatureObject: CreatureObject) = StopConversationIntent(creatureObject).broadcast()
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/events/ChangePlayerFactionEvent.java b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/events/ChangePlayerFactionEvent.java
new file mode 100644
index 000000000..8c7a030ec
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/events/ChangePlayerFactionEvent.java
@@ -0,0 +1,52 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.gameplay.conversation.events;
+
+import com.projectswg.common.data.encodables.tangible.PvpStatus;
+import com.projectswg.holocore.intents.gameplay.gcw.faction.FactionIntent;
+import com.projectswg.holocore.resources.gameplay.conversation.model.Event;
+import com.projectswg.holocore.resources.support.data.server_info.loader.ServerData;
+import com.projectswg.holocore.resources.support.data.server_info.loader.combat.FactionLoader;
+import com.projectswg.holocore.resources.support.global.player.Player;
+import com.projectswg.holocore.resources.support.objects.swg.custom.AIObject;
+
+public class ChangePlayerFactionEvent implements Event {
+
+ private final String factionName;
+
+ public ChangePlayerFactionEvent(String factionName) {
+ this.factionName = factionName;
+ }
+
+ @Override
+ public void trigger(Player player, AIObject npc) {
+ FactionLoader.Faction faction = ServerData.INSTANCE.getFactions().getFaction(factionName);
+
+ FactionIntent.broadcastUpdateFaction(player.getCreatureObject(), faction);
+ FactionIntent.broadcastUpdateStatus(player.getCreatureObject(), PvpStatus.COMBATANT);
+ }
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/model/Conversation.java b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/model/Conversation.java
new file mode 100644
index 000000000..9b3aa6f34
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/model/Conversation.java
@@ -0,0 +1,98 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.gameplay.conversation.model;
+
+import com.projectswg.common.data.encodables.oob.ProsePackage;
+import com.projectswg.holocore.resources.support.global.player.Player;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Conversation {
+
+ private final String id;
+ private ProsePackage npcMessage;
+ private final List playerResponses;
+ private final List events;
+ private final List requirements;
+
+ public Conversation(String id) {
+ this.id = id;
+ playerResponses = new ArrayList<>();
+ events = new ArrayList<>();
+ requirements = new ArrayList<>();
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public ProsePackage getNpcMessage() {
+ return npcMessage;
+ }
+
+ public void setNpcMessage(ProsePackage npcMessage) {
+ this.npcMessage = npcMessage;
+ }
+
+ public void addPlayerResponse(PlayerResponse playerResponse) {
+ playerResponses.add(playerResponse);
+ }
+
+ public void addEvent(Event event) {
+ events.add(event);
+ }
+
+ public void addRequirement(Requirement requirement) {
+ requirements.add(requirement);
+ }
+
+ public List getPlayerResponses() {
+ return playerResponses;
+ }
+
+ public boolean isAllowed(Player player) {
+ for (Requirement requirement : requirements) {
+ boolean available = requirement.test(player);
+
+ if (!available) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public List getEvents() {
+ return events;
+ }
+
+ @Override
+ public String toString() {
+ return "Conversation{" + "id='" + id + '\'' + '}';
+ }
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/model/Event.java b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/model/Event.java
new file mode 100644
index 000000000..a70fb6444
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/model/Event.java
@@ -0,0 +1,34 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.gameplay.conversation.model;
+
+import com.projectswg.holocore.resources.support.global.player.Player;
+import com.projectswg.holocore.resources.support.objects.swg.custom.AIObject;
+
+public interface Event {
+ void trigger(Player player, AIObject npc);
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/model/PlayerResponse.java b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/model/PlayerResponse.java
new file mode 100644
index 000000000..1a2c55149
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/model/PlayerResponse.java
@@ -0,0 +1,47 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.gameplay.conversation.model;
+
+import com.projectswg.common.data.encodables.oob.ProsePackage;
+
+public class PlayerResponse {
+ private final ProsePackage prosePackage;
+ private final String next;
+
+ public PlayerResponse(ProsePackage prosePackage, String next) {
+ this.prosePackage = prosePackage;
+ this.next = next;
+ }
+
+ public ProsePackage getProsePackage() {
+ return prosePackage;
+ }
+
+ public String getNext() {
+ return next;
+ }
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/model/Requirement.java b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/model/Requirement.java
new file mode 100644
index 000000000..a947118e8
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/model/Requirement.java
@@ -0,0 +1,35 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.gameplay.conversation.model;
+
+import com.projectswg.holocore.resources.support.global.player.Player;
+
+import java.util.function.Predicate;
+
+public interface Requirement extends Predicate {
+
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/requirements/FactionNameRequirement.java b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/requirements/FactionNameRequirement.java
new file mode 100644
index 000000000..d10a4d096
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/requirements/FactionNameRequirement.java
@@ -0,0 +1,53 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.gameplay.conversation.requirements;
+
+import com.projectswg.holocore.resources.gameplay.conversation.model.Requirement;
+import com.projectswg.holocore.resources.support.data.server_info.loader.ServerData;
+import com.projectswg.holocore.resources.support.data.server_info.loader.combat.FactionLoader;
+import com.projectswg.holocore.resources.support.global.player.Player;
+import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject;
+
+import java.util.Objects;
+
+public class FactionNameRequirement implements Requirement {
+
+ private final String factionName;
+
+ public FactionNameRequirement(String factionName) {
+ this.factionName = factionName;
+ }
+
+ @Override
+ public boolean test(Player player) {
+ CreatureObject creatureObject = player.getCreatureObject();
+ FactionLoader.Faction currentFaction = creatureObject.getFaction();
+ FactionLoader.Faction requiredFaction = ServerData.INSTANCE.getFactions().getFaction(factionName);
+
+ return Objects.equals(currentFaction, requiredFaction);
+ }
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/requirements/FactionStatusRequirement.java b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/requirements/FactionStatusRequirement.java
new file mode 100644
index 000000000..2943b435c
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/gameplay/conversation/requirements/FactionStatusRequirement.java
@@ -0,0 +1,51 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.gameplay.conversation.requirements;
+
+import com.projectswg.common.data.encodables.tangible.PvpStatus;
+import com.projectswg.holocore.resources.gameplay.conversation.model.Requirement;
+import com.projectswg.holocore.resources.support.global.player.Player;
+import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject;
+
+import java.util.Collection;
+
+public class FactionStatusRequirement implements Requirement {
+
+ private final Collection allowedPvpStatuses;
+
+ public FactionStatusRequirement(Collection allowedPvpStatuses) {
+ this.allowedPvpStatuses = allowedPvpStatuses;
+ }
+
+ @Override
+ public boolean test(Player player) {
+ CreatureObject creatureObject = player.getCreatureObject();
+ PvpStatus pvpStatus = creatureObject.getPvpStatus();
+
+ return allowedPvpStatuses.contains(pvpStatus);
+ }
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/DataLoader.kt b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/DataLoader.kt
index 03c53761e..e314b0fc7 100644
--- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/DataLoader.kt
+++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/DataLoader.kt
@@ -1,15 +1,34 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
package com.projectswg.holocore.resources.support.data.server_info.loader
+import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.ConversationLoader
import com.projectswg.holocore.resources.support.data.server_info.loader.npc.*
-import me.joshlarson.jlcommon.log.Log
import java.io.IOException
-import java.lang.ref.Reference
-import java.lang.ref.SoftReference
-import java.lang.ref.WeakReference
-import java.util.concurrent.ConcurrentHashMap
-import java.util.function.Function
-import java.util.function.Supplier
-import kotlin.reflect.KProperty
abstract class DataLoader {
@@ -61,6 +80,7 @@ abstract class DataLoader {
fun terrainLevels(): TerrainLevelLoader = ServerData.terrainLevels
fun noSpawnZones(): NoSpawnZoneLoader = ServerData.noSpawnZones
fun gcwRegionLoader(): GcwRegionLoader = ServerData.gcwRegionLoader
+ fun conversationLoader(): ConversationLoader = ServerData.conversationLoader
}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/ServerData.kt b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/ServerData.kt
index 99959b775..080bd109a 100644
--- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/ServerData.kt
+++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/ServerData.kt
@@ -28,6 +28,7 @@
package com.projectswg.holocore.resources.support.data.server_info.loader
import com.projectswg.holocore.resources.support.data.server_info.loader.combat.FactionLoader
+import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.ConversationLoader
import com.projectswg.holocore.resources.support.data.server_info.loader.npc.*
import me.joshlarson.jlcommon.log.Log
import java.io.IOException
@@ -97,6 +98,7 @@ object ServerData {
val terrainLevels by SoftDataLoaderDelegate(::TerrainLevelLoader)
val noSpawnZones by SoftDataLoaderDelegate(::NoSpawnZoneLoader)
val gcwRegionLoader by SoftDataLoaderDelegate(::GcwRegionLoader)
+ val conversationLoader by SoftDataLoaderDelegate(::ConversationLoader)
private class WeakDataLoaderDelegate(loaderCreator: () -> T): DataLoaderDelegate(::WeakReference, loaderCreator)
private class SoftDataLoaderDelegate(loaderCreator: () -> T): DataLoaderDelegate(::SoftReference, loaderCreator)
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/ConversationLoader.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/ConversationLoader.java
new file mode 100644
index 000000000..33b568348
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/ConversationLoader.java
@@ -0,0 +1,210 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.support.data.server_info.loader.conversation;
+
+import com.projectswg.common.data.encodables.oob.ProsePackage;
+import com.projectswg.holocore.resources.gameplay.conversation.model.Conversation;
+import com.projectswg.holocore.resources.gameplay.conversation.model.Event;
+import com.projectswg.holocore.resources.gameplay.conversation.model.PlayerResponse;
+import com.projectswg.holocore.resources.gameplay.conversation.model.Requirement;
+import com.projectswg.holocore.resources.support.data.server_info.SdbLoader;
+import com.projectswg.holocore.resources.support.data.server_info.loader.DataLoader;
+import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.events.ChangePlayerFactionEventParser;
+import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.requirements.FactionNameRequirementParser;
+import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.requirements.FactionStatusRequirementParser;
+import me.joshlarson.jlcommon.log.Log;
+import me.joshlarson.json.JSON;
+import me.joshlarson.json.JSONObject;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class ConversationLoader extends DataLoader {
+
+ private final Map> spawnConversationsMap;
+ private final Map> requirementParserMap;
+ private final Map> eventParserMap;
+
+ public ConversationLoader() {
+ spawnConversationsMap = new HashMap<>();
+ requirementParserMap = new HashMap<>();
+ eventParserMap = new HashMap<>();
+ }
+
+ @Nullable
+ public Conversation getConversation(String conversationId) {
+ try {
+ InputStream inputStream = new FileInputStream("serverdata/nge/conversation/" + conversationId + ".json");
+ JSONObject jsonObject = JSON.readObject(inputStream);
+
+ return readConversation(conversationId, jsonObject);
+ } catch (Throwable t) {
+ Log.e("Unable to load conversation by ID %s", conversationId);
+ Log.e(t);
+ return null;
+ }
+ }
+
+ @NotNull
+ public List getInitialConversations(String spawnId) {
+ if (!spawnConversationsMap.containsKey(spawnId)) {
+ return Collections.emptyList();
+ }
+
+ Collection conversationIds = spawnConversationsMap.get(spawnId);
+
+ return conversationIds.stream()
+ .map(this::getConversation)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ @NotNull
+ public Collection getConversationIds(String spawnId) {
+ return spawnConversationsMap.getOrDefault(spawnId, Collections.emptyList());
+ }
+
+ private Conversation readConversation(String conversationId, JSONObject jsonObject) {
+ Conversation conversation = new Conversation(conversationId);
+
+ Map npcMessageObj = (Map) jsonObject.get("npcMessage");
+
+ ProsePackage npcMessage = readProsePackage(npcMessageObj);
+ conversation.setNpcMessage(npcMessage);
+
+ List playerResponseObjs = (List) jsonObject.get("playerResponses");
+ List playerResponses = readPlayerResponses(playerResponseObjs);
+
+ for (PlayerResponse playerResponse : playerResponses) {
+ conversation.addPlayerResponse(playerResponse);
+ }
+
+ List requirementObjs = (List) jsonObject.get("requirements");
+ List requirements = readRequirements(requirementObjs);
+
+ for (Requirement requirement : requirements) {
+ conversation.addRequirement(requirement);
+ }
+
+ List eventObjs = (List) jsonObject.get("events");
+ List events = readEvents(eventObjs);
+
+ for (Event event : events) {
+ conversation.addEvent(event);
+ }
+
+ return conversation;
+ }
+
+ private ProsePackage readProsePackage(Map object) {
+ String file = object.get("file").toString();
+ String key = object.get("key").toString();
+
+ return new ProsePackage(file, key);
+ }
+
+ private List readPlayerResponses(List playerResponseObjs) {
+ List playerResponses = new ArrayList<>();
+
+ for (JSONObject playerResponseObj : playerResponseObjs) {
+ Map playerMessageRaw = (Map) playerResponseObj.get("playerMessage");
+ ProsePackage prosePackage = readProsePackage(playerMessageRaw);
+
+ String nextConversationId = (String) playerResponseObj.get("next");
+
+ PlayerResponse playerResponse = new PlayerResponse(prosePackage, nextConversationId);
+
+ playerResponses.add(playerResponse);
+ }
+
+ return playerResponses;
+ }
+
+ private List readRequirements(List requirementObjs) {
+ List requirements = new ArrayList<>();
+
+ for (JSONObject requirementObj : requirementObjs) {
+ String type = (String) requirementObj.get("type");
+ Map args = (Map) requirementObj.get("args");
+ RequirementParser extends Requirement> requirementParser = requirementParserMap.get(type);
+ Requirement requirement = requirementParser.parse(args);
+
+ requirements.add(requirement);
+ }
+
+ return requirements;
+ }
+
+ private List readEvents(List eventObjs) {
+ List events = new ArrayList<>();
+
+ for (JSONObject eventObj : eventObjs) {
+ String type = (String) eventObj.get("type");
+ Map args = (Map) eventObj.get("args");
+ EventParser extends Event> eventParser = eventParserMap.get(type);
+ Event event = eventParser.parse(args);
+
+ events.add(event);
+ }
+
+ return events;
+ }
+
+ @Override
+ public void load() throws IOException {
+ initRequirementParsers();
+ initEventParsers();
+ loadSpawnToConversations();
+ }
+
+ private void initRequirementParsers() {
+ requirementParserMap.put("faction_name", new FactionNameRequirementParser());
+ requirementParserMap.put("faction_status", new FactionStatusRequirementParser());
+ }
+
+ private void initEventParsers() {
+ eventParserMap.put("faction_change", new ChangePlayerFactionEventParser());
+ }
+
+ private void loadSpawnToConversations() throws IOException {
+ try (SdbLoader.SdbResultSet set = SdbLoader.load(new File("serverdata/nge/conversation/spawn_conversation_map.msdb"))) {
+ while (set.next()) {
+ String spawnId = set.getText("spawn_id");
+ String conversationId = set.getText("conversation_id");
+
+ spawnConversationsMap.computeIfAbsent(spawnId, a -> new ArrayList<>()).add(conversationId);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/EventParser.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/EventParser.java
new file mode 100644
index 000000000..c886a325c
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/EventParser.java
@@ -0,0 +1,35 @@
+/***********************************************************************************
+ * Copyright (c) 2018 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.support.data.server_info.loader.conversation;
+
+import com.projectswg.holocore.resources.gameplay.conversation.model.Event;
+
+import java.util.Map;
+
+public interface EventParser {
+ T parse(Map args);
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/RequirementParser.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/RequirementParser.java
new file mode 100644
index 000000000..9a2857d32
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/RequirementParser.java
@@ -0,0 +1,35 @@
+/***********************************************************************************
+ * Copyright (c) 2018 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.support.data.server_info.loader.conversation;
+
+import com.projectswg.holocore.resources.gameplay.conversation.model.Requirement;
+
+import java.util.Map;
+
+public interface RequirementParser {
+ T parse(Map args);
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/events/ChangePlayerFactionEventParser.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/events/ChangePlayerFactionEventParser.java
new file mode 100644
index 000000000..c84103c44
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/events/ChangePlayerFactionEventParser.java
@@ -0,0 +1,42 @@
+/***********************************************************************************
+ * Copyright (c) 2018 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.support.data.server_info.loader.conversation.events;
+
+import com.projectswg.holocore.resources.gameplay.conversation.events.ChangePlayerFactionEvent;
+import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.EventParser;
+
+import java.util.Map;
+
+public class ChangePlayerFactionEventParser implements EventParser {
+
+ @Override
+ public ChangePlayerFactionEvent parse(Map args) {
+ String factionName = (String) args.get("faction");
+
+ return new ChangePlayerFactionEvent(factionName);
+ }
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/requirements/FactionNameRequirementParser.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/requirements/FactionNameRequirementParser.java
new file mode 100644
index 000000000..2df8b5e06
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/requirements/FactionNameRequirementParser.java
@@ -0,0 +1,42 @@
+/***********************************************************************************
+ * Copyright (c) 2018 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.support.data.server_info.loader.conversation.requirements;
+
+import com.projectswg.holocore.resources.gameplay.conversation.requirements.FactionNameRequirement;
+import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.RequirementParser;
+
+import java.util.Map;
+
+public class FactionNameRequirementParser implements RequirementParser {
+
+ @Override
+ public FactionNameRequirement parse(Map args) {
+ String factionName = (String) args.get("faction");
+
+ return new FactionNameRequirement(factionName);
+ }
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/requirements/FactionStatusRequirementParser.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/requirements/FactionStatusRequirementParser.java
new file mode 100644
index 000000000..de88d0ad6
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/conversation/requirements/FactionStatusRequirementParser.java
@@ -0,0 +1,70 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.support.data.server_info.loader.conversation.requirements;
+
+import com.projectswg.common.data.encodables.tangible.PvpStatus;
+import com.projectswg.holocore.resources.gameplay.conversation.requirements.FactionStatusRequirement;
+import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.RequirementParser;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class FactionStatusRequirementParser implements RequirementParser {
+
+ private static final Map STATUS_MAP = Map.of(
+ "on_leave", PvpStatus.ONLEAVE,
+ "combatant", PvpStatus.COMBATANT,
+ "special_forces", PvpStatus.SPECIALFORCES
+ );
+
+ public FactionStatusRequirementParser() {
+
+ }
+
+ @Override
+ public FactionStatusRequirement parse(Map args) {
+ Collection statuses = (List) args.get("statuses");
+
+ Collection allowedPvpStatuses = statuses.stream()
+ .map(this::getStatusByName)
+ .collect(Collectors.toList());
+
+ return new FactionStatusRequirement(allowedPvpStatuses);
+ }
+
+ private PvpStatus getStatusByName(String status) {
+ PvpStatus pvpStatus = STATUS_MAP.get(status);
+
+ if (pvpStatus != null) {
+ return pvpStatus;
+ }
+
+ throw new RuntimeException("Unknown status name " + status + ". Valid status names: " + STATUS_MAP.keySet());
+ }
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/conversation/NpcConversationSelectCmdCallback.java b/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/conversation/NpcConversationSelectCmdCallback.java
new file mode 100644
index 000000000..81c377c80
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/conversation/NpcConversationSelectCmdCallback.java
@@ -0,0 +1,42 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.support.global.commands.callbacks.conversation;
+
+import com.projectswg.holocore.intents.gameplay.conversation.ProgressConversationIntent;
+import com.projectswg.holocore.resources.support.global.commands.ICmdCallback;
+import com.projectswg.holocore.resources.support.global.player.Player;
+import com.projectswg.holocore.resources.support.objects.swg.SWGObject;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class NpcConversationSelectCmdCallback implements ICmdCallback {
+
+ @Override
+ public void execute(@NotNull Player player, @Nullable SWGObject target, @NotNull String args) {
+ ProgressConversationIntent.broadcast(player.getCreatureObject(), Integer.parseInt(args));
+ }
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/conversation/NpcConversationStartCmdCallback.java b/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/conversation/NpcConversationStartCmdCallback.java
new file mode 100644
index 000000000..c8e074ed5
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/conversation/NpcConversationStartCmdCallback.java
@@ -0,0 +1,43 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.support.global.commands.callbacks.conversation;
+
+import com.projectswg.holocore.intents.gameplay.conversation.StartConversationIntent;
+import com.projectswg.holocore.resources.support.global.commands.ICmdCallback;
+import com.projectswg.holocore.resources.support.global.player.Player;
+import com.projectswg.holocore.resources.support.objects.swg.SWGObject;
+import com.projectswg.holocore.resources.support.objects.swg.custom.AIObject;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class NpcConversationStartCmdCallback implements ICmdCallback {
+
+ @Override
+ public void execute(@NotNull Player player, @Nullable SWGObject target, @NotNull String args) {
+ StartConversationIntent.broadcast(player.getCreatureObject(), (AIObject) target);
+ }
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/conversation/NpcConversationStopCmdCallback.java b/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/conversation/NpcConversationStopCmdCallback.java
new file mode 100644
index 000000000..7738a4df3
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/conversation/NpcConversationStopCmdCallback.java
@@ -0,0 +1,42 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.resources.support.global.commands.callbacks.conversation;
+
+import com.projectswg.holocore.intents.gameplay.conversation.StopConversationIntent;
+import com.projectswg.holocore.resources.support.global.commands.ICmdCallback;
+import com.projectswg.holocore.resources.support.global.player.Player;
+import com.projectswg.holocore.resources.support.objects.swg.SWGObject;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class NpcConversationStopCmdCallback implements ICmdCallback {
+
+ @Override
+ public void execute(@NotNull Player player, @Nullable SWGObject target, @NotNull String args) {
+ StopConversationIntent.broadcast(player.getCreatureObject());
+ }
+}
diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/GameplayManager.java b/src/main/java/com/projectswg/holocore/services/gameplay/GameplayManager.java
index 326f855a7..28005fadd 100644
--- a/src/main/java/com/projectswg/holocore/services/gameplay/GameplayManager.java
+++ b/src/main/java/com/projectswg/holocore/services/gameplay/GameplayManager.java
@@ -1,6 +1,7 @@
package com.projectswg.holocore.services.gameplay;
import com.projectswg.holocore.services.gameplay.combat.CombatManager;
+import com.projectswg.holocore.services.gameplay.conversation.ConversationService;
import com.projectswg.holocore.services.gameplay.crafting.CraftingManager;
import com.projectswg.holocore.services.gameplay.entertainment.EntertainmentManager;
import com.projectswg.holocore.services.gameplay.faction.FactionManager;
@@ -14,6 +15,7 @@ import me.joshlarson.jlcommon.control.ManagerStructure;
@ManagerStructure(children = {
CombatManager.class,
+ ConversationService.class,
CraftingManager.class,
EntertainmentManager.class,
FactionManager.class,
diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/conversation/ConversationService.java b/src/main/java/com/projectswg/holocore/services/gameplay/conversation/ConversationService.java
new file mode 100644
index 000000000..f534a840f
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/services/gameplay/conversation/ConversationService.java
@@ -0,0 +1,338 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.services.gameplay.conversation;
+
+import com.projectswg.common.data.encodables.oob.OutOfBandPackage;
+import com.projectswg.common.data.encodables.oob.ProsePackage;
+import com.projectswg.common.data.encodables.oob.StringId;
+import com.projectswg.common.data.location.Location;
+import com.projectswg.common.data.location.Point3D;
+import com.projectswg.common.data.location.Terrain;
+import com.projectswg.common.network.packets.swg.zone.object_controller.conversation.NpcConversationMessage;
+import com.projectswg.common.network.packets.swg.zone.object_controller.conversation.NpcConversationOptions;
+import com.projectswg.common.network.packets.swg.zone.object_controller.conversation.StartNpcConversation;
+import com.projectswg.common.network.packets.swg.zone.object_controller.conversation.StopNpcConversation;
+import com.projectswg.holocore.intents.gameplay.combat.EnterCombatIntent;
+import com.projectswg.holocore.intents.gameplay.conversation.ProgressConversationIntent;
+import com.projectswg.holocore.intents.gameplay.conversation.StartConversationIntent;
+import com.projectswg.holocore.intents.gameplay.conversation.StopConversationIntent;
+import com.projectswg.holocore.intents.support.objects.swg.MoveObjectIntent;
+import com.projectswg.holocore.intents.support.objects.swg.ObjectCreatedIntent;
+import com.projectswg.holocore.resources.gameplay.conversation.model.Conversation;
+import com.projectswg.holocore.resources.gameplay.conversation.model.Event;
+import com.projectswg.holocore.resources.gameplay.conversation.model.PlayerResponse;
+import com.projectswg.holocore.resources.support.data.server_info.StandardLog;
+import com.projectswg.holocore.resources.support.data.server_info.loader.conversation.ConversationLoader;
+import com.projectswg.holocore.resources.support.data.server_info.loader.ServerData;
+import com.projectswg.holocore.resources.support.global.player.Player;
+import com.projectswg.holocore.resources.support.npc.spawn.Spawner;
+import com.projectswg.holocore.resources.support.objects.swg.SWGObject;
+import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject;
+import com.projectswg.holocore.resources.support.objects.swg.custom.AIObject;
+import com.projectswg.holocore.resources.support.objects.swg.tangible.OptionFlag;
+import me.joshlarson.jlcommon.control.IntentHandler;
+import me.joshlarson.jlcommon.control.Service;
+import me.joshlarson.jlcommon.log.Log;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class ConversationService extends Service {
+
+ private static final double ALLOWED_DISTANCE = 7.0; // Max allowed amount of distance between player and NPC before conversation is interrupted
+
+ private final Map sessions;
+
+ public ConversationService() {
+ sessions = new HashMap<>();
+ }
+
+ @IntentHandler
+ private void handleCombatStartedIntent(EnterCombatIntent intent) {
+ CreatureObject source = intent.getSource();
+ CreatureObject target = intent.getTarget();
+
+ if (isConversing(source)) {
+ abortConversation(source);
+ }
+
+ if (isConversing(target)) {
+ abortConversation(target);
+ }
+ }
+
+ @IntentHandler
+ private void handleMoveObjectIntent(MoveObjectIntent intent) {
+ SWGObject object = intent.getObject();
+
+ if (!(object instanceof CreatureObject)) {
+ return;
+ }
+
+ CreatureObject creatureObject = (CreatureObject) object;
+
+ if (!isConversing(creatureObject)) {
+ return;
+ }
+
+ Session session = sessions.get(creatureObject);
+ AIObject npc = session.getNpc();
+
+ if (isWithinRange(creatureObject, npc)) {
+ return;
+ }
+
+ // At this point, the conversation should be interrupted
+ abortConversation(creatureObject);
+ }
+
+ @IntentHandler
+ private void handleObjectCreatedIntent(ObjectCreatedIntent intent) {
+ SWGObject object = intent.getObject();
+
+ if (!(object instanceof AIObject)) {
+ return;
+ }
+
+ AIObject npc = (AIObject) object;
+
+ if (!isConversableNpc(npc)) {
+ return;
+ }
+
+ npc.addOptionFlags(OptionFlag.CONVERSABLE);
+ }
+
+ @IntentHandler
+ private void handleStartConversationIntent(StartConversationIntent intent) {
+ ConversationLoader conversationLoader = ServerData.INSTANCE.getConversationLoader();
+ AIObject npc = intent.getNpc();
+ CreatureObject starter = intent.getStarter();
+
+ Spawner spawner = npc.getSpawner();
+ String spawnId = spawner.getId();
+
+ List conversations = conversationLoader.getInitialConversations(spawnId);
+
+ Conversation conversation = reduce(conversations, starter.getOwner());
+
+ if (conversation == null) {
+ StandardLog.onPlayerEvent(this, starter, "No eligible conversations for spawnId %s", spawnId);
+ return;
+ }
+
+ progressConversation(starter, npc, conversation);
+
+ rotateNpc(npc, starter);
+ }
+
+ @IntentHandler
+ private void handleProgressConversationIntent(ProgressConversationIntent intent) {
+ int selection = intent.getSelection();
+ CreatureObject starter = intent.getStarter();
+
+ Session session = sessions.get(starter);
+ Conversation currentConversation = session.getConversation();
+ AIObject npc = session.getNpc();
+
+ List filteredResponses = getFilteredResponses(starter, currentConversation);
+ PlayerResponse selectedResponse = filteredResponses.get(selection);
+ String nextConversationId = selectedResponse.getNext();
+ ConversationLoader conversationLoader = ServerData.INSTANCE.getConversationLoader();
+
+ Conversation nextConversation = conversationLoader.getConversation(nextConversationId);
+
+ if (nextConversation == null) {
+ StandardLog.onPlayerError(this, starter, "Unable to progress conversation, conversation by ID %s does not exist", nextConversationId);
+ return;
+ }
+
+ progressConversation(starter, npc, nextConversation);
+ }
+
+ @IntentHandler
+ private void handleStopConversationIntent(StopConversationIntent intent) {
+ CreatureObject creatureObject = intent.getCreatureObject();
+
+ if (isConversing(creatureObject)) {
+ abortConversation(creatureObject);
+ }
+ }
+
+ private boolean isConversableNpc(AIObject npc) {
+ ConversationLoader conversationLoader = ServerData.INSTANCE.getConversationLoader();
+ Spawner spawner = npc.getSpawner();
+ String spawnId = spawner.getId();
+
+ Collection spawnConversationIds = conversationLoader.getConversationIds(spawnId);
+
+ return !spawnConversationIds.isEmpty();
+ }
+
+ @Nullable
+ private Conversation reduce(List conversations, Player player) {
+ Conversation conversation = null;
+
+ for (Conversation candidate : conversations) {
+ if (candidate.isAllowed(player)) {
+ conversation = candidate;
+ break;
+ }
+ }
+
+ return conversation;
+ }
+
+ private List getFilteredResponses(CreatureObject starter, Conversation conversation) {
+ List playerResponses = conversation.getPlayerResponses();
+ ConversationLoader conversationLoader = ServerData.INSTANCE.getConversationLoader();
+
+ return playerResponses.stream()
+ .filter(playerResponse -> {
+ String nextConversationId = playerResponse.getNext();
+
+ if (nextConversationId == null) {
+ return false;
+ }
+
+ Conversation nextConversation = conversationLoader.getConversation(nextConversationId);
+
+ if (nextConversation == null) {
+ return false;
+ }
+
+ return nextConversation.isAllowed(starter.getOwner());
+ })
+ .collect(Collectors.toList());
+ }
+
+ private void progressConversation(CreatureObject starter, AIObject npc, Conversation conversation) {
+ ProsePackage npcMessageProse = conversation.getNpcMessage();
+
+ List filteredResponses = getFilteredResponses(starter, conversation);
+ List replies = filteredResponses.stream()
+ .map(PlayerResponse::getProsePackage)
+ .map(OutOfBandPackage::new)
+ .peek(outOfBandPackage -> outOfBandPackage.setConversation(true))
+ .collect(Collectors.toList());
+
+ if (!sessions.containsKey(starter)) {
+ starter.sendSelf(new StartNpcConversation(npc.getObjectId(), starter.getObjectId()));
+ }
+
+ if (filteredResponses.isEmpty()) {
+ sessions.remove(starter);
+ starter.sendSelf(new StopNpcConversation(starter.getObjectId(), npc.getObjectId(), npcMessageProse.getBase()));
+ } else {
+ OutOfBandPackage npcMessage = new OutOfBandPackage(npcMessageProse);
+ npcMessage.setConversation(true);
+ Session session = new Session(conversation, npc);
+
+ sessions.put(starter, session);
+ starter.sendSelf(new NpcConversationMessage(starter.getObjectId(), npcMessage));
+ starter.sendSelf(new NpcConversationOptions(starter.getObjectId(), replies));
+ }
+
+ List events = conversation.getEvents();
+
+ for (Event event : events) {
+ try {
+ event.trigger(starter.getOwner(), npc);
+ } catch (Throwable t) {
+ StandardLog.onPlayerError(this, starter, "Error while triggering conversation event of type %s", event.getClass().getSimpleName());
+ Log.e(t);
+ }
+ }
+ }
+
+ private void rotateNpc(AIObject npc, CreatureObject starter) {
+ Location oldNpcWorldLocation = npc.getLocation();
+ Point3D starterWorldLocation = starter.getLocation().getPosition();
+
+ double headingTo = oldNpcWorldLocation.getHeadingTo(starterWorldLocation);
+
+ Location newNpcWorldLocation = new Location.LocationBuilder(oldNpcWorldLocation)
+ .setHeading(headingTo)
+ .build();
+
+ MoveObjectIntent.broadcast(npc, npc.getParent(), newNpcWorldLocation, 0);
+ }
+
+ private void abortConversation(CreatureObject creatureObject) {
+ Session session = sessions.remove(creatureObject);
+ Conversation conversation = session.getConversation();
+ AIObject npc = session.getNpc();
+ ProsePackage npcMessage = conversation.getNpcMessage();
+ StringId npcMessageBase = npcMessage.getBase();
+
+ StopNpcConversation stopNpcConversation = new StopNpcConversation(creatureObject.getObjectId(), npc.getObjectId(), npcMessageBase);
+
+ creatureObject.sendSelf(stopNpcConversation);
+ }
+
+ private boolean isConversing(CreatureObject creatureObject) {
+ return sessions.containsKey(creatureObject);
+ }
+
+ private boolean isWithinRange(CreatureObject creature, AIObject npc) {
+ Location playerWorldLocation = creature.getWorldLocation();
+ Location npcWorldLocation = npc.getWorldLocation();
+
+ Terrain playerTerrain = playerWorldLocation.getTerrain();
+ Terrain npcTerrain = npcWorldLocation.getTerrain();
+
+ if (playerTerrain != npcTerrain) {
+ return false;
+ }
+
+ double distance = playerWorldLocation.distanceTo(npcWorldLocation);
+
+ return distance <= ALLOWED_DISTANCE;
+ }
+
+ private static class Session {
+ private final Conversation conversation;
+ private final AIObject npc;
+
+ public Session(Conversation conversation, AIObject npc) {
+ this.conversation = conversation;
+ this.npc = npc;
+ }
+
+ public Conversation getConversation() {
+ return conversation;
+ }
+
+ public AIObject getNpc() {
+ return npc;
+ }
+ }
+}
diff --git a/src/main/java/com/projectswg/holocore/services/support/global/commands/CommandExecutionService.java b/src/main/java/com/projectswg/holocore/services/support/global/commands/CommandExecutionService.java
index b8a07bf74..7dc85706d 100644
--- a/src/main/java/com/projectswg/holocore/services/support/global/commands/CommandExecutionService.java
+++ b/src/main/java/com/projectswg/holocore/services/support/global/commands/CommandExecutionService.java
@@ -1,3 +1,29 @@
+/***********************************************************************************
+ * Copyright (c) 2021 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see . *
+ ***********************************************************************************/
package com.projectswg.holocore.services.support.global.commands;
import com.projectswg.holocore.intents.support.global.command.ExecuteCommandIntent;
@@ -13,6 +39,7 @@ import com.projectswg.holocore.resources.support.global.commands.callbacks.comba
import com.projectswg.holocore.resources.support.global.commands.callbacks.combat.CmdDuel;
import com.projectswg.holocore.resources.support.global.commands.callbacks.combat.CmdEndDuel;
import com.projectswg.holocore.resources.support.global.commands.callbacks.combat.CmdPVP;
+import com.projectswg.holocore.resources.support.global.commands.callbacks.conversation.*;
import com.projectswg.holocore.resources.support.global.commands.callbacks.flags.*;
import com.projectswg.holocore.resources.support.global.commands.callbacks.generic.*;
import com.projectswg.holocore.resources.support.global.commands.callbacks.group.*;
@@ -94,6 +121,7 @@ public class CommandExecutionService extends Service {
addGenericScripts();
addGroupScripts();
addSurveyScripts();
+ addConversationScripts();
}
private void addAdminScripts() {
@@ -188,4 +216,10 @@ public class CommandExecutionService extends Service {
registerCallback("requestSurvey", CmdRequestSurvey::new);
}
+ private void addConversationScripts() {
+ registerCallback("npcconversationstart", NpcConversationStartCmdCallback::new);
+ registerCallback("npcconversationselect", NpcConversationSelectCmdCallback::new);
+ registerCallback("npcconversationstop", NpcConversationStopCmdCallback::new);
+ }
+
}