fix quest content and condition triggers (#2283)

* Add param3 to EVENT_GADGET_STATE_CHANGE

* Slight cleanup for DungeonManager

* Fix ContentTriggerFire

* Rework and fix talk content and conditions.

* redo item content and conditions, swap out getItemByGuid with getItemById, and make count handling consistent.

* Don't need to check if checkItem is null

* add this. to DungeonManager.java

* add this to Inventory.java

* Update src/main/java/emu/grasscutter/game/quest/QuestManager.java

Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com>

* add spaces to ContentCompleteAnyTalk.java

* Update src/main/java/emu/grasscutter/game/quest/content/ContentCompleteAnyTalk.java

Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com>

---------

Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com>
This commit is contained in:
Nazrin 2023-08-07 15:03:13 -07:00 committed by GitHub
parent 8200607a15
commit b9a493d424
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 119 additions and 67 deletions

View File

@ -286,14 +286,18 @@ public final class DungeonManager {
}
public void finishDungeon() {
// Mark the dungeon has completed for the players.
var dungeonId = this.getDungeonData().getId();
this.getScene()
.getPlayers()
.forEach(player -> player.getPlayerProgress().markDungeonAsComplete(dungeonId));
this.notifyEndDungeon(true);
this.endDungeon(BaseDungeonResult.DungeonEndReason.COMPLETED);
}
notifyEndDungeon(true);
endDungeon(BaseDungeonResult.DungeonEndReason.COMPLETED);
public void quitDungeon() {
this.notifyEndDungeon(false);
this.endDungeon(BaseDungeonResult.DungeonEndReason.QUIT);
}
public void failDungeon() {
this.notifyEndDungeon(false);
this.endDungeon(BaseDungeonResult.DungeonEndReason.FAILED);
}
public void notifyEndDungeon(boolean successfully) {
@ -301,8 +305,11 @@ public final class DungeonManager {
.getPlayers()
.forEach(
p -> {
// Trigger the fail event if needed.
if (!successfully) {
// Trigger the fail and success event.
if (successfully) {
var dungeonId = this.getDungeonData().getId();
p.getPlayerProgress().markDungeonAsComplete(dungeonId);
} else {
p.getQuestManager()
.queueEvent(QuestContent.QUEST_CONTENT_FAIL_DUNGEON, dungeonData.getId());
}
@ -317,16 +324,6 @@ public final class DungeonManager {
.callEvent(new ScriptArgs(0, EventType.EVENT_DUNGEON_SETTLE, successfully ? 1 : 0));
}
public void quitDungeon() {
notifyEndDungeon(false);
endDungeon(BaseDungeonResult.DungeonEndReason.QUIT);
}
public void failDungeon() {
notifyEndDungeon(false);
endDungeon(BaseDungeonResult.DungeonEndReason.FAILED);
}
public void endDungeon(BaseDungeonResult.DungeonEndReason endReason) {
if (scene.getDungeonSettleListeners() != null) {
scene.getDungeonSettleListeners().forEach(o -> o.onDungeonSettle(this, endReason));

View File

@ -150,6 +150,7 @@ public class EntityGadget extends EntityBaseGadget {
public void updateState(int state) {
if (state == this.getState()) return; // Don't triggers events
var oldState = this.getState();
this.setState(state);
ticksSinceChange = getScene().getSceneTimeSeconds();
this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state));
@ -157,7 +158,7 @@ public class EntityGadget extends EntityBaseGadget {
.getScriptManager()
.callEvent(
new ScriptArgs(
this.getGroupId(), EventType.EVENT_GADGET_STATE_CHANGE, state, this.getConfigId()));
this.getGroupId(), EventType.EVENT_GADGET_STATE_CHANGE, state, this.getConfigId()).setParam3(oldState));
}
@Deprecated(forRemoval = true) // Dont use!

View File

@ -36,4 +36,9 @@ public class EquipInventoryTab implements InventoryTab {
public int getMaxCapacity() {
return this.maxCapacity;
}
@Override
public int getItemCountById(int itemId) {
return (int) items.stream().filter(item -> item.getItemId() == itemId).count();
}
}

View File

@ -129,7 +129,7 @@ public class GameItem {
public void checkIsNew(Inventory inventory) {
// display notification when player obtain new item
if (inventory.getItemByGuid(this.itemId) == null) {
if (inventory.getItemById(this.itemId) == null) {
this.newItem = true;
}
}

View File

@ -17,6 +17,9 @@ import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.longs.*;
import lombok.val;
import javax.annotation.Nullable;
import java.util.*;
public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
@ -62,6 +65,26 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
return this.getItems().get(id);
}
@Nullable
public InventoryTab getInventoryTabByItemId(int itemId) {
val itemData = GameData.getItemDataMap().get(itemId);
if (itemData == null || itemData.getItemType() == null) {
return null;
}
return getInventoryTab(itemData.getItemType());
}
@Nullable
public GameItem getItemById(int itemId) {
val inventoryTab = this.getInventoryTabByItemId(itemId);
return inventoryTab != null ? inventoryTab.getItemById(itemId) : null;
}
public int getItemCountById(int itemId) {
val inventoryTab = this.getInventoryTabByItemId(itemId);
return inventoryTab != null ? inventoryTab.getItemCountById(itemId) : 0;
}
public boolean addItem(int itemId) {
return addItem(itemId, 1);
}
@ -562,7 +585,7 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
tab = getInventoryTab(item.getItemData().getItemType());
}
putItem(item, tab);
this.putItem(item, tab);
// Equip to a character if possible
if (item.isEquipped()) {

View File

@ -10,4 +10,6 @@ public interface InventoryTab {
int getSize();
int getMaxCapacity();
int getItemCountById(int itemId);
}

View File

@ -2,6 +2,7 @@ package emu.grasscutter.game.inventory;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.val;
public class MaterialInventoryTab implements InventoryTab {
private final Int2ObjectMap<GameItem> items;
@ -36,4 +37,10 @@ public class MaterialInventoryTab implements InventoryTab {
public int getMaxCapacity() {
return this.maxCapacity;
}
@Override
public int getItemCountById(int itemId) {
val item = getItemById(itemId);
return item != null ? item.getCount() : 0;
}
}

View File

@ -172,6 +172,8 @@ public class GameQuest {
this.failProgressList = new int[questData.getFailCond().size()];
}
this.getMainQuest().getTalks().values().removeIf(talk -> talk.getId() == this.getSubQuestId());
this.getOwner().getPlayerProgress().resetCurrentProgress(String.valueOf(this.subQuestId));
setState(QuestState.QUEST_STATE_UNSTARTED);

View File

@ -279,7 +279,7 @@ public class QuestManager extends BasePlayerManager {
}
public GameMainQuest getMainQuestByTalkId(int talkId) {
int mainQuestId = GameData.getQuestTalkMap().getOrDefault(talkId, talkId / 100);
var mainQuestId = GameData.getQuestTalkMap().getOrDefault(talkId, 0);
return getMainQuestById(mainQuestId);
}

View File

@ -2,8 +2,6 @@ package emu.grasscutter.game.quest.conditions;
import static emu.grasscutter.game.quest.enums.QuestCond.QUEST_COND_COMPLETE_TALK;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.quest.QuestData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.QuestValueCond;
@ -20,16 +18,12 @@ public class ConditionCompleteTalk extends BaseCondition {
String paramStr,
int... params) {
val talkId = condition.getParam()[0];
val unknownParam = condition.getParam()[1]; // e.g. 3 for 7081601
val checkMainQuest = owner.getQuestManager().getMainQuestByTalkId(talkId);
if (checkMainQuest == null
|| GameData.getMainQuestDataMap().get(checkMainQuest.getParentQuestId()).getTalks()
== null) {
Grasscutter.getLogger()
.debug("Warning: mainQuest {} hasn't been started yet, or has no talks", talkId / 100);
return false;
if (checkMainQuest == null) {
return talkId == params[0];
}
val talkData = checkMainQuest.getTalks().get(talkId);
return talkData != null || checkMainQuest.getChildQuestById(talkId) != null;
return talkData != null;
}
}

View File

@ -18,8 +18,8 @@ public class ConditionItemNumLessThan extends BaseCondition {
String paramStr,
int... params) {
val itemId = condition.getParam()[0];
val amount = condition.getParam()[1];
val checkItem = owner.getInventory().getItemByGuid(itemId);
return checkItem == null || checkItem.getCount() < amount;
val targetAmount = condition.getParam()[1];
val amount = owner.getInventory().getItemCountById(itemId);
return amount < targetAmount;
}
}

View File

@ -18,8 +18,11 @@ public class ConditionPackHaveItem extends BaseCondition {
String paramStr,
int... params) {
val itemId = condition.getParam()[0];
val targetAmount = condition.getParam()[1];
val checkItem = owner.getInventory().getItemByGuid(itemId);
return checkItem != null && checkItem.getCount() >= targetAmount;
var targetAmount = condition.getParam()[1];
if (targetAmount == 0) {
targetAmount = 1;
}
val amount = owner.getInventory().getItemCountById(itemId);
return amount >= targetAmount;
}
}

View File

@ -17,8 +17,12 @@ public class ContentAddQuestProgress extends BaseContent {
val currentCount =
quest.getOwner().getPlayerProgress().getCurrentProgress(String.valueOf(progressId));
var targetAmount = condition.getCount();
if (targetAmount == 0) {
targetAmount = 1;
}
// if the condition count is 0 I think it is safe to assume that the
// condition count from EXEC only needs to be 1
return currentCount >= condition.getCount();
return currentCount >= targetAmount;
}
}

View File

@ -2,11 +2,10 @@ package emu.grasscutter.game.quest.content;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_COMPLETE_ANY_TALK;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.quest.QuestData;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import java.util.stream.Stream;
import emu.grasscutter.game.quest.*;
import lombok.val;
import java.util.Arrays;
@QuestValueContent(QUEST_CONTENT_COMPLETE_ANY_TALK)
public class ContentCompleteAnyTalk extends BaseContent {
@ -14,10 +13,22 @@ public class ContentCompleteAnyTalk extends BaseContent {
@Override
public boolean execute(
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
return Stream.of(condition.getParamStr().split(","))
var conditionTalk = Arrays.stream(condition.getParamStr().split(","))
.mapToInt(Integer::parseInt)
.anyMatch(
talkId ->
GameData.getTalkConfigDataMap().get(params[0]) != null && talkId == params[0]);
.toArray();
for (var talkId : conditionTalk) {
val checkMainQuest = quest.getOwner().getQuestManager().getMainQuestByTalkId(talkId);
if (checkMainQuest == null) {
if (talkId == params[0]) return true;
continue;
}
val talkData = checkMainQuest.getTalks().get(talkId);
if (talkData != null) {
return true;
}
}
return false;
}
}

View File

@ -12,10 +12,9 @@ public class ContentCompleteTalk extends BaseContent {
public boolean execute(
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
val talkId = condition.getParam()[0];
if (talkId != params[0]) return false;
val checkMainQuest = quest.getOwner().getQuestManager().getMainQuestByTalkId(talkId);
if (checkMainQuest == null) {
return false;
return talkId == params[0];
}
val talkData = checkMainQuest.getTalks().get(talkId);

View File

@ -5,12 +5,16 @@ import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_ITEM_L
import emu.grasscutter.data.excels.quest.QuestData;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import lombok.val;
@QuestValueContent(QUEST_CONTENT_ITEM_LESS_THAN)
public class ContentItemLessThan extends BaseContent {
@Override
public boolean execute(
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0] && condition.getCount() > params[1];
val itemId = condition.getParam()[0];
val targetAmount = condition.getParam()[1];
val amount = quest.getOwner().getInventory().getItemCountById(itemId);
return amount < targetAmount;
}
}

View File

@ -12,8 +12,10 @@ public class ContentLuaNotify extends BaseContent {
@Override
public boolean execute(
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
return condition.getParamStr().equals(paramStr)
&& condition.getCount()
<= quest.getOwner().getPlayerProgress().getCurrentProgress(paramStr);
var targetAmount = condition.getCount();
if (targetAmount == 0) {
targetAmount = 1;
}
return targetAmount <= quest.getOwner().getPlayerProgress().getCurrentProgress(condition.getParamStr());
}
}

View File

@ -5,16 +5,19 @@ import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_OBTAIN
import emu.grasscutter.data.excels.quest.QuestData;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import lombok.val;
@QuestValueContent(QUEST_CONTENT_OBTAIN_ITEM)
public class ContentObtainItem extends BaseContent {
@Override
public boolean execute(
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
var targetCount = condition.getCount();
if (targetCount == 0) {
targetCount = 1;
val itemId = condition.getParam()[0];
var targetAmount = condition.getCount();
if (targetAmount == 0) {
targetAmount = 1;
}
return condition.getParam()[0] == params[0] && targetCount <= params[1];
val amount = quest.getOwner().getInventory().getItemCountById(itemId);
return amount >= targetAmount;
}
}

View File

@ -2,23 +2,18 @@ package emu.grasscutter.game.quest.content;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_TRIGGER_FIRE;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.excels.quest.QuestData;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import lombok.val;
@QuestValueContent(QUEST_CONTENT_TRIGGER_FIRE)
public class ContentTriggerFire extends BaseContent {
@Override
public boolean execute(
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
if (quest.getTriggers().containsKey(quest.getTriggerNameById(params[0]))) {
// We don't want to put a new key here
return quest.getTriggers().get(quest.getTriggerNameById(params[0]));
} else {
Grasscutter.getLogger()
.debug("Quest {} doesn't have trigger {} registered.", quest.getSubQuestId(), params[0]);
return false;
}
val triggerId = condition.getParam()[0];
val triggerName = quest.getTriggerNameById(triggerId);
return quest.getTriggers().getOrDefault(triggerName, false);
}
}