Skip to content
This repository was archived by the owner on Mar 8, 2022. It is now read-only.

Commit 2308427

Browse files
committed
quest basic claim & submit #31
1 parent 9632774 commit 2308427

File tree

8 files changed

+336
-58
lines changed

8 files changed

+336
-58
lines changed

src/main/java/cat/nyaa/HamsterEcoHelper/quest/QuestCommands.java

+5
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ public void postQuest(CommandSender sender, Arguments args) {
4141
new QuestWizard(station.id, asPlayer(sender), 30);
4242
}
4343

44+
@SubCommand(value = "finish", permission = "heh.quest.claim")
45+
public void submitQuest(CommandSender sender, Arguments args) {
46+
QuestCommon.submitQuest(asPlayer(sender));
47+
}
48+
4449
public Sign getSignLookat(CommandSender sender) {
4550
Player p = asPlayer(sender);
4651
Block b = p.getTargetBlock((Set<Material>) null, 5);// TODO use nms rayTrace

src/main/java/cat/nyaa/HamsterEcoHelper/quest/QuestCommon.java

+175
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
11
package cat.nyaa.HamsterEcoHelper.quest;
22

33
import cat.nyaa.HamsterEcoHelper.HamsterEcoHelper;
4+
import cat.nyaa.HamsterEcoHelper.I18n;
5+
import cat.nyaa.HamsterEcoHelper.utils.Utils;
6+
import cat.nyaa.HamsterEcoHelper.utils.database.Database;
7+
import cat.nyaa.HamsterEcoHelper.utils.database.tables.quest.QuestEntry;
48
import cat.nyaa.HamsterEcoHelper.utils.database.tables.quest.QuestInstance;
59
import cat.nyaa.HamsterEcoHelper.utils.database.tables.quest.QuestStation;
10+
import cat.nyaa.nyaacore.database.BaseDatabase;
11+
import cat.nyaa.nyaacore.utils.InventoryUtils;
12+
import net.milkbowl.vault.item.Items;
13+
import org.bukkit.Bukkit;
614
import org.bukkit.Location;
15+
import org.bukkit.entity.Player;
16+
import org.bukkit.inventory.Inventory;
17+
import org.bukkit.inventory.ItemStack;
18+
19+
import java.time.ZonedDateTime;
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import java.util.UUID;
23+
24+
import static cat.nyaa.HamsterEcoHelper.utils.database.tables.quest.QuestInstance.QuestStatus.*;
725

826

927
public class QuestCommon {
@@ -37,4 +55,161 @@ public static void removeStation(Location loc) {
3755
.whereEq("z", loc.getBlockZ())
3856
.delete();
3957
}
58+
59+
public static void claimQuest(Player player, String questId) {
60+
if (!player.isOnline()) throw new RuntimeException("user.quest.menu.unexpected_exception");
61+
String playerId = player.getUniqueId().toString();
62+
QuestEntry quest;
63+
Database db = HamsterEcoHelper.instance.database;
64+
try {
65+
quest = db.query(QuestEntry.class)
66+
.whereEq("id", questId)
67+
.selectUnique();
68+
} catch (RuntimeException ex) {
69+
throw new RuntimeException("user.quest.menu.unexpected_exception");
70+
}
71+
72+
if (!db.query(QuestInstance.class)
73+
.whereEq("claimer", player.getUniqueId())
74+
.whereEq("status", IN_PROGRESS.name())
75+
.select().isEmpty()) {
76+
throw new RuntimeException("user.quest.menu.reject_unfinished");
77+
}
78+
79+
if (!quest.claimable) throw new RuntimeException("user.quest.menu.reject_unavailable");
80+
if (quest.isExpired()) throw new RuntimeException("user.quest.menu.reject_expired");
81+
// TODO prereq etc.
82+
83+
else {
84+
QuestInstance q = new QuestInstance();
85+
q.id = UUID.randomUUID().toString();
86+
q.questId = questId;
87+
q.claimer = playerId;
88+
q.status = IN_PROGRESS;
89+
q.startTime = ZonedDateTime.now();
90+
91+
quest.claimable = false;
92+
93+
db.query(QuestEntry.class).whereEq("id", questId).update(quest, "claimable");
94+
db.query(QuestInstance.class).insert(q);
95+
}
96+
}
97+
98+
public static void submitQuest(Player player) {
99+
String playerId = player.getUniqueId().toString();
100+
Database db = HamsterEcoHelper.instance.database;
101+
List<QuestInstance> quests = db.query(QuestInstance.class)
102+
.whereEq("claimer", player.getUniqueId())
103+
.whereEq("status", IN_PROGRESS.name())
104+
.select();
105+
for (QuestInstance q : quests) {
106+
String questId = q.questId;
107+
QuestEntry e = selectUniqueUnchecked(db.query(QuestEntry.class).whereEq("id", questId));
108+
// TODO reset quest claimable
109+
if (e == null) {
110+
player.sendMessage(I18n.format("user.quest.submit.no_entry", questId));
111+
q.status = CANCELLED;
112+
db.query(QuestInstance.class).update(q, "status");
113+
continue;
114+
}
115+
116+
if (!e.completedInTime(q.startTime)) {
117+
player.sendMessage(I18n.format("user.quest.submit.timeout"));
118+
q.status = TIMEOUT;
119+
db.query(QuestInstance.class).update(q, "status");
120+
continue;
121+
}
122+
123+
if (e.targetType == QuestEntry.QuestType.OTHER) {
124+
player.sendMessage(I18n.format("user.quest.submit.need_verification"));
125+
q.status = UNVERIFIED;
126+
db.query(QuestInstance.class).update(q, "status");
127+
} else if (e.targetType == QuestEntry.QuestType.ITEM) {
128+
List<ItemStack> ret = withdrawInventoryAtomic(player.getInventory(), e.targetItems);
129+
if (ret == null) {
130+
// TODO return target item to publisher notification.
131+
//for (ItemStack item : e.targetItems) Utils.giveItem(Bukkit.getOfflinePlayer(UUID.fromString(e.publisher)), item);
132+
switch (e.rewardType) {
133+
case ITEM: for (ItemStack i : e.rewardItem) Utils.giveItem(player, i); break;
134+
case MONEY: HamsterEcoHelper.instance.eco.deposit(player, e.rewardMoney); break;
135+
default: break;
136+
}
137+
q.status = COMPLETED;
138+
// TODO update endTime
139+
db.query(QuestInstance.class).update(q, "status");
140+
player.sendMessage(I18n.format("user.quest.submit.quest_complete", e.id));
141+
} else {
142+
player.sendMessage(I18n.format("user.quest.submit.target_not_satisfy", e.id));
143+
}
144+
} else {
145+
player.sendMessage(I18n.format("user.quest.submit.bad_target_type", e.id));
146+
q.status = CANCELLED;
147+
db.query(QuestInstance.class).update(q, "status");
148+
}
149+
150+
151+
152+
}
153+
}
154+
155+
// TODO move to NC
156+
private static <T> T selectUniqueUnchecked(BaseDatabase.Query<T> q) {
157+
try {
158+
return q.selectUnique();
159+
} catch (RuntimeException ex) {
160+
return null;
161+
}
162+
}
163+
164+
/**
165+
* Remove items from inventory.
166+
* Either all removed or none removed.
167+
* @param inv the inventory
168+
* @param itemToBeTaken items to be removed
169+
* @return If null, then all designated items are removed. If not null, it contains the items missing
170+
* TODO move to NC
171+
*/
172+
private static List<ItemStack> withdrawInventoryAtomic(Inventory inv, List<ItemStack> itemToBeTaken) {
173+
ItemStack[] itemStacks = inv.getContents();
174+
ItemStack[] cloneStacks = new ItemStack[itemStacks.length];
175+
for (int i = 0; i < itemStacks.length; i++) {
176+
cloneStacks[i] = itemStacks[i] == null ? null : itemStacks[i].clone();
177+
}
178+
179+
List<ItemStack> ret = new ArrayList<>();
180+
181+
for (ItemStack item : itemToBeTaken) {
182+
int sizeReq = item.getAmount();
183+
184+
for (int i = 0; i < cloneStacks.length;i++) {
185+
if (cloneStacks[i] == null) continue;
186+
if (cloneStacks[i].isSimilar(item)) {
187+
int sizeSupp = cloneStacks[i].getAmount();
188+
if (sizeSupp > sizeReq) {
189+
cloneStacks[i].setAmount(sizeSupp - sizeReq);
190+
sizeReq = 0;
191+
break;
192+
} else {
193+
cloneStacks[i] = null;
194+
sizeReq -= sizeSupp;
195+
if (sizeReq == 0) break;
196+
}
197+
}
198+
}
199+
200+
if (sizeReq > 0) {
201+
ItemStack n = item.clone();
202+
item.setAmount(sizeReq);
203+
ret.add(n);
204+
}
205+
}
206+
207+
if (ret.size() == 0) {
208+
inv.setContents(cloneStacks);
209+
return null;
210+
} else {
211+
return ret;
212+
}
213+
}
214+
40215
}

src/main/java/cat/nyaa/HamsterEcoHelper/quest/QuestWizard.java

+65-12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import cat.nyaa.HamsterEcoHelper.utils.Utils;
66
import cat.nyaa.HamsterEcoHelper.utils.database.tables.quest.QuestEntry;
77
import org.bukkit.Bukkit;
8+
import org.bukkit.ChatColor;
89
import org.bukkit.Material;
910
import org.bukkit.entity.Player;
1011
import org.bukkit.event.EventHandler;
@@ -124,22 +125,29 @@ public void onPlayerChat(PlayerChatEvent ev) {
124125
return;
125126
}
126127

128+
input = ChatColor.translateAlternateColorCodes('&', input);
129+
127130
switch (state) {
128131
case WAITING_NAME:
132+
player.sendMessage(input);
129133
entry.questName = input;
130134
state = State.WAITING_DESCRIPTION;
131135
break;
132136
case WAITING_DESCRIPTION:
137+
player.sendMessage(input);
133138
entry.questDescription = input;
134139
state = State.WAITING_TARGET_TYPE;
135140
break;
136141
case WAITING_TARGET_TYPE:
142+
player.sendMessage(input);
137143
if ("item".equalsIgnoreCase(input)) {
138144
entry.targetType = QuestEntry.QuestType.ITEM;
139145
state = State.WAITING_TARGET_ITEM;
140-
} else {
146+
} else if ("other".equalsIgnoreCase(input)) {
141147
entry.targetType = QuestEntry.QuestType.OTHER;
142148
state = State.WAITING_REWARD_TYPE;
149+
} else {
150+
player.sendMessage(I18n.format("user.quest.wizard.invalid_option"));
143151
}
144152
break;
145153
case WAITING_TARGET_ITEM:
@@ -155,6 +163,7 @@ public void onPlayerChat(PlayerChatEvent ev) {
155163
player.sendMessage(I18n.format("user.quest.wizard.hold_item_plz"));
156164
} else {
157165
entry.targetItems.add(stack.clone());
166+
player.sendMessage(I18n.format("user.quest.wizard.item_added"));
158167
}
159168
}
160169
break;
@@ -165,9 +174,11 @@ public void onPlayerChat(PlayerChatEvent ev) {
165174
} else if ("money".equalsIgnoreCase(input)) {
166175
entry.rewardType = QuestEntry.QuestType.MONEY;
167176
state = State.WAITING_REWARD_MONEY;
168-
} else {
177+
} else if ("none".equalsIgnoreCase(input)) {
169178
entry.rewardType = QuestEntry.QuestType.NONE;
170179
state = WAITING_TIME_LIMIT;
180+
} else {
181+
player.sendMessage(I18n.format("user.quest.wizard.invalid_option"));
171182
}
172183
break;
173184
case WAITING_REWARD_ITEM:
@@ -183,6 +194,7 @@ public void onPlayerChat(PlayerChatEvent ev) {
183194
player.sendMessage(I18n.format("user.quest.wizard.hold_item_plz"));
184195
} else {
185196
entry.rewardItem.add(stack.clone());
197+
player.sendMessage(I18n.format("user.quest.wizard.item_added"));
186198
if (reallyTakeItem) {
187199
player.getInventory().setItemInMainHand(new ItemStack(Material.AIR));
188200
}
@@ -191,6 +203,7 @@ public void onPlayerChat(PlayerChatEvent ev) {
191203
break;
192204
case WAITING_REWARD_MONEY:
193205
try {
206+
player.sendMessage(input);
194207
double money = Double.parseDouble(input);
195208
entry.rewardMoney = money;
196209
state = WAITING_TIME_LIMIT;
@@ -200,21 +213,30 @@ public void onPlayerChat(PlayerChatEvent ev) {
200213
}
201214
break;
202215
case WAITING_TIME_LIMIT:
203-
try {
204-
entry.questTimeLimit = Duration.parse(input);
205-
state = WAITING_EXPIRE_IN;
206-
} catch (DateTimeParseException ex) {
207-
player.sendMessage(I18n.format("user.quest.wizard.invalid_time"));
216+
if ("unlimited".equalsIgnoreCase(input)) {
217+
entry.questTimeLimit = QuestEntry.NO_TIME_LIMIT;
218+
} else {
219+
Duration dur = parseDuration(input);
220+
if (dur == null) {
221+
player.sendMessage(I18n.format("user.quest.wizard.invalid_time"));
222+
break;
223+
}
224+
entry.questTimeLimit = dur;
208225
}
226+
state = WAITING_EXPIRE_IN;
209227
break;
210228
case WAITING_EXPIRE_IN:
211-
try {
212-
Duration dur = Duration.parse(input);
229+
if ("never".equalsIgnoreCase(input)) {
230+
entry.questExpire = QuestEntry.NEVER_EXPIRE;
231+
} else {
232+
Duration dur = parseDuration(input);
233+
if (dur == null) {
234+
player.sendMessage(I18n.format("user.quest.wizard.invalid_time"));
235+
break;
236+
}
213237
entry.questExpire = ZonedDateTime.now().plus(dur);
214-
state = FINISH;
215-
} catch (DateTimeParseException ex) {
216-
player.sendMessage(I18n.format("user.quest.wizard.invalid_time"));
217238
}
239+
state = FINISH;
218240
break;
219241
case FINISH:
220242
entry.claimable = true;
@@ -229,4 +251,35 @@ public void onPlayerChat(PlayerChatEvent ev) {
229251
player.sendMessage(I18n.format("user.quest.wizard." + state.name().toLowerCase()));
230252
timer = new Timer(timeout);
231253
}
254+
255+
/**
256+
* format = 1d1h1m1s
257+
* any combination of [dhms] works
258+
* @param input
259+
* @return
260+
*/
261+
private static Duration parseDuration(String input) {
262+
String num = "";
263+
Duration dur = Duration.ZERO;
264+
for (char ch : input.toCharArray()) {
265+
if (ch == 'd' || ch == 'h' || ch == 'm'||ch == 's') {
266+
try {
267+
int n = Integer.parseInt(num);
268+
if (n < 0) return null;
269+
switch (ch) {
270+
case 'd': dur = dur.plusDays(n); break;
271+
case 'h': dur = dur.plusHours(n); break;
272+
case 'm': dur = dur.plusMinutes(n); break;
273+
case 's': dur = dur.plusSeconds(n); break;
274+
}
275+
num = "";
276+
} catch (NumberFormatException ex) {
277+
return null;
278+
}
279+
} else {
280+
num += ch;
281+
}
282+
}
283+
return dur;
284+
}
232285
}

0 commit comments

Comments
 (0)