Skip to content

Commit 09c4420

Browse files
committed
Example quest, functional from start to finish
1 parent efd8d77 commit 09c4420

12 files changed

Lines changed: 422 additions & 135 deletions

File tree

modules/src/main/java/parallelmc/parallelutils/modules/parallelquests/ParallelQuests.java

Lines changed: 55 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
package parallelmc.parallelutils.modules.parallelquests;
22

33
import org.bukkit.Bukkit;
4-
import org.bukkit.entity.Player;
54
import org.bukkit.plugin.Plugin;
65
import org.bukkit.plugin.PluginManager;
7-
import org.checkerframework.checker.units.qual.C;
86
import org.jetbrains.annotations.NotNull;
97
import org.jetbrains.annotations.Nullable;
108
import parallelmc.parallelutils.Constants;
119
import parallelmc.parallelutils.ParallelClassLoader;
1210
import parallelmc.parallelutils.ParallelModule;
1311
import parallelmc.parallelutils.ParallelUtils;
14-
import parallelmc.parallelutils.modules.parallelquests.commands.ExampleConversation;
1512
import parallelmc.parallelutils.modules.parallelquests.commands.QuestTracker;
16-
import parallelmc.parallelutils.modules.parallelquests.dialogue.Conversation;
17-
import parallelmc.parallelutils.modules.parallelquests.dialogue.Dialogue;
13+
import parallelmc.parallelutils.modules.parallelquests.dialogue.ConversationManager;
14+
import parallelmc.parallelutils.modules.parallelquests.events.OnCommand;
1815
import parallelmc.parallelutils.modules.parallelquests.events.OnJoinLeave;
19-
import parallelmc.parallelutils.modules.parallelquests.events.OnSlotUpdated;
16+
import parallelmc.parallelutils.modules.parallelquests.quests.Quest;
17+
import parallelmc.parallelutils.modules.parallelquests.quests.TheBakersBeastQuest;
2018

2119
import java.sql.*;
2220
import java.util.*;
@@ -28,11 +26,11 @@ public class ParallelQuests extends ParallelModule {
2826

2927
public ParallelQuests(ParallelClassLoader classLoader, List<String> dependents) { super(classLoader, dependents); }
3028

31-
// TODO: populate
32-
private final HashSet<String> ValidQuests = new HashSet<>();
29+
private final HashMap<String, Quest> RegisteredQuests = new HashMap<>();
3330

3431
private final ConcurrentHashMap<UUID, List<QuestStatus>> PlayerQuestStatuses = new ConcurrentHashMap<>();
35-
private final HashMap<UUID, Conversation> ActiveConversations = new HashMap<>();
32+
33+
private static final ConversationManager ConversationManager = new ConversationManager();
3634

3735
private static ParallelQuests Instance;
3836

@@ -59,10 +57,17 @@ public void onEnable() {
5957
}
6058

6159
manager.registerEvents(new OnJoinLeave(), puPlugin);
62-
manager.registerEvents(new OnSlotUpdated(), puPlugin);
60+
manager.registerEvents(new OnCommand(), puPlugin);
6361

6462
puPlugin.getCommand("questtracker").setExecutor(new QuestTracker());
65-
puPlugin.getCommand("exampleconversation").setExecutor(new ExampleConversation());
63+
64+
RegisteredQuests.put("thebakersbeast", new TheBakersBeastQuest());
65+
66+
for (Quest quest : RegisteredQuests.values()) {
67+
quest.init();
68+
}
69+
70+
ParallelUtils.log(Level.WARNING, "Initialized " + RegisteredQuests.size() + " quests.");
6671

6772
try (Connection conn = puPlugin.getDbConn()) {
6873
if (conn == null) throw new SQLException("Unable to establish connection!");
@@ -74,7 +79,7 @@ public void onEnable() {
7479
Id int not null auto_increment,
7580
UUID varchar(36) not null,
7681
QuestId varchar(128) not null,
77-
Completed tinyint not null,
82+
QuestStage varchar(128) not null,
7883
constraint Quests_Id_uindex
7984
unique (Id),
8085
PRIMARY KEY (Id)
@@ -87,7 +92,7 @@ PRIMARY KEY (Id)
8792
}
8893

8994
Bukkit.getScheduler().runTaskTimer(puPlugin, () -> {
90-
PlayerQuestStatuses.keySet().forEach(this::savePlayerQuestStatus);
95+
PlayerQuestStatuses.keySet().forEach(u -> savePlayerQuestStatus(u, false));
9196
}, 6000L, 6000L); // 5 minutes
9297

9398
Instance = this;
@@ -108,46 +113,49 @@ public static ParallelQuests get() {
108113
return Instance;
109114
}
110115

111-
public void startConversation(Player player, Dialogue dialogue) {
112-
if (ActiveConversations.containsKey(player.getUniqueId())) {
113-
ParallelUtils.log(Level.WARNING, "Tried to add " + player.getName() + " to a conversation when they are already in one!");
114-
return;
115-
}
116-
Conversation c = new Conversation(dialogue);
117-
c.enter(player);
118-
ActiveConversations.put(player.getUniqueId(), c);
119-
}
116+
public static ConversationManager getConversationManager() { return ConversationManager; }
120117

121-
public void endConversation(UUID uuid) {
122-
ActiveConversations.remove(uuid);
118+
@Nullable
119+
public Quest getQuest(String questId) {
120+
return RegisteredQuests.get(questId);
123121
}
124122

125-
public @Nullable Conversation getActiveConversation(UUID uuid) {
126-
return ActiveConversations.getOrDefault(uuid, null);
123+
public Optional<QuestStatus> getQuestStatus(UUID uuid, String questId) {
124+
return getAllQuestStatuses(uuid).stream()
125+
.filter(x -> x.getQuestId().equals(questId))
126+
.findFirst();
127127
}
128128

129129
/**
130130
* Returns a list of quests a player currently has active, as well as if they are completed.
131131
* If a quest ID is not in this list, the player has not accepted it yet.
132132
* @param uuid The player UUID to search
133133
*/
134-
public List<QuestStatus> getQuestStatus(UUID uuid) {
135-
return PlayerQuestStatuses.getOrDefault(uuid, List.of());
134+
public List<QuestStatus> getAllQuestStatuses(UUID uuid) {
135+
return PlayerQuestStatuses.getOrDefault(uuid, new ArrayList<>());
136136
}
137137

138-
public boolean markQuestCompleted(UUID uuid, String questId) {
139-
if (!ValidQuests.contains(questId)) {
140-
ParallelUtils.log(Level.SEVERE, "Invalid Quest ID " + questId + " provided for quest completion!");
141-
return false;
138+
public void startQuest(UUID uuid, String questId, String initialStage) {
139+
List<QuestStatus> statuses = getAllQuestStatuses(uuid);
140+
if (statuses.isEmpty()) {
141+
statuses.add(new QuestStatus(questId, initialStage));
142+
PlayerQuestStatuses.put(uuid, statuses);
143+
return;
144+
}
145+
if (statuses.stream().anyMatch(x -> x.getQuestId().equals(questId))) {
146+
ParallelUtils.log(Level.WARNING, "Tried to start already active quest " + questId + " for UUID " + uuid);
147+
return;
142148
}
149+
statuses.add(new QuestStatus(questId, initialStage));
150+
}
143151

144-
Optional<QuestStatus> status = getQuestStatus(uuid).stream().filter(x -> x.getQuestId().equals(questId)).findFirst();
152+
public void setQuestStage(UUID uuid, String questId, String newStage) {
153+
var status = getQuestStatus(uuid, questId);
145154
if (status.isEmpty()) {
146-
return false;
155+
ParallelUtils.log(Level.WARNING, "Tried to update status of quest " + questId + " for UUID " + uuid + " that has not accepted said quest!");
156+
return;
147157
}
148-
149-
status.get().markCompleted();
150-
return false;
158+
status.get().setQuestStage(newStage);
151159
}
152160

153161
/**
@@ -165,8 +173,8 @@ public void loadPlayerQuestStatus(UUID uuid) {
165173
ResultSet results = statement.executeQuery("select * from Quests where UUID = '" + uuid + "'");
166174
while (results.next()) {
167175
String questId = results.getString("QuestId");
168-
boolean completed = results.getBoolean("Completed");
169-
result.add(new QuestStatus(questId, completed));
176+
String questStage = results.getString("QuestStage");
177+
result.add(new QuestStatus(questId, questStage));
170178
}
171179
conn.commit();
172180
statement.close();
@@ -182,34 +190,24 @@ public void loadPlayerQuestStatus(UUID uuid) {
182190

183191
/**
184192
* Asynchronously saves a player's quest data to the database.
185-
* The player's data WILL be removed from the cache, see savePlayerQuestStatus to avoid this.
186-
* @param uuid The Player UUID to save
187-
*/
188-
public void saveAndRemovePlayerQuestStatus(UUID uuid) {
189-
savePlayerQuestStatus(uuid);
190-
PlayerQuestStatuses.remove(uuid);
191-
}
192-
193-
/**
194-
* Asynchronously saves a player's quest data to the database.
195-
* The player's data will NOT be removed from the cache, see saveAndRemovePlayerQuestStatus
196193
* @param uuid The Player UUID to save
194+
* @param removeFromCache If true, the data will also be removed from the local cache
197195
*/
198-
public void savePlayerQuestStatus(UUID uuid) {
196+
public void savePlayerQuestStatus(UUID uuid, boolean removeFromCache) {
199197
Bukkit.getScheduler().runTaskAsynchronously(puPlugin, () -> {
200-
List<QuestStatus> status = getQuestStatus(uuid);
198+
List<QuestStatus> status = getAllQuestStatuses(uuid);
201199
try (Connection conn = puPlugin.getDbConn()) {
202200
if (conn == null) throw new SQLException("Unable to establish connection!");
203201
Statement statement = conn.createStatement();
204202
statement.setQueryTimeout(10);
205203
statement.execute("delete from Quests where UUID = '" + uuid + "'");
206-
PreparedStatement prepared = conn.prepareStatement("insert into Quests (UUID, QuestId, Completed) values (?, ?, ?)");
204+
PreparedStatement prepared = conn.prepareStatement("insert into Quests (UUID, QuestId, QuestStage) values (?, ?, ?)");
207205
prepared.setQueryTimeout(30);
208206
status.forEach(s -> {
209207
try {
210208
prepared.setString(1, uuid.toString());
211209
prepared.setString(2, s.getQuestId());
212-
prepared.setBoolean(3, s.isCompleted());
210+
prepared.setString(3, s.getQuestStage());
213211
prepared.addBatch();
214212
}
215213
catch (SQLException e){
@@ -222,6 +220,9 @@ public void savePlayerQuestStatus(UUID uuid) {
222220
prepared.close();
223221
} catch (SQLException e) {
224222
e.printStackTrace();
223+
} finally {
224+
if (removeFromCache)
225+
PlayerQuestStatuses.remove(uuid);
225226
}
226227
});
227228
}
Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,20 @@
11
package parallelmc.parallelutils.modules.parallelquests;
22

3+
34
public class QuestStatus {
45
private final String questId;
5-
private boolean completed;
6+
private String questStage;
67

7-
public QuestStatus(String questId, boolean completed) {
8+
public QuestStatus(String questId, String questStage) {
89
this.questId = questId;
9-
this.completed = completed;
10+
this.questStage = questStage;
1011
}
1112

12-
public String getQuestId() {
13-
return questId;
14-
}
13+
public String getQuestId() { return questId; }
1514

16-
public boolean isCompleted() {
17-
return completed;
18-
}
15+
public String getQuestStage() { return questStage; }
1916

20-
public void markCompleted() {
21-
completed = true;
22-
}
17+
public void setQuestStage(String stage) { this.questStage = stage; }
18+
19+
public boolean isCompleted() { return questStage.equalsIgnoreCase("completed"); }
2320
}

modules/src/main/java/parallelmc/parallelutils/modules/parallelquests/commands/ExampleConversation.java

Lines changed: 0 additions & 35 deletions
This file was deleted.

modules/src/main/java/parallelmc/parallelutils/modules/parallelquests/dialogue/Conversation.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
package parallelmc.parallelutils.modules.parallelquests.dialogue;
22

33
import net.kyori.adventure.text.Component;
4+
import net.kyori.adventure.text.event.ClickEvent;
5+
import net.kyori.adventure.text.event.HoverEvent;
46
import net.kyori.adventure.text.format.NamedTextColor;
57
import org.bukkit.entity.Player;
8+
import org.bukkit.map.MinecraftFont;
69

710
public class Conversation {
811
private final Dialogue dialogue;
912
private DialogueNode current;
13+
private static final int CENTER = 120;
14+
private static final int SPACE_WIDTH = 3;
1015

1116
public Conversation(Dialogue dialogue) {
1217
this.dialogue = dialogue;
@@ -24,11 +29,26 @@ public void choose(Player player, int index) {
2429
}
2530

2631
private void display(Player player) {
27-
player.sendMessage(Component.text(dialogue.getSpeaker() + ": " + current.getText()));
32+
Component message = Component
33+
.text(centerText(dialogue.getSpeaker()), NamedTextColor.YELLOW)
34+
.appendNewline()
35+
.append(Component.text(current.getText(), NamedTextColor.WHITE))
36+
.appendNewline();
2837
for (int i = 0; i < current.getOptions().size(); i++) {
29-
player.sendMessage(Component.text("[" + current.getOptions().get(i).getText() + "]", NamedTextColor.AQUA));
38+
DialogueOption option = current.getOptions().get(i);
39+
message = message.append(Component.text(" ".repeat(5) + option.getText(), NamedTextColor.AQUA)
40+
.clickEvent(ClickEvent.runCommand("/dialogueoption " + i))
41+
.hoverEvent(HoverEvent.showText(Component.text("Click to Select Option " + (i + 1), NamedTextColor.YELLOW)))
42+
).appendNewline();
3043
}
44+
player.sendMessage(message);
3145
}
3246

3347
public boolean isFinished() { return !current.hasNext(); }
48+
49+
private String centerText(String input) {
50+
int messageSize = MinecraftFont.Font.getWidth(input);
51+
int padding = (CENTER - messageSize / 2) / SPACE_WIDTH;
52+
return " ".repeat(padding) + input;
53+
}
3454
}

0 commit comments

Comments
 (0)