Merge remote-tracking branch 'origin/development' into development

This commit is contained in:
KingRainbow44 2023-08-13 16:20:11 -04:00
commit cfccb2d991
No known key found for this signature in database
GPG Key ID: FC2CB64B00D257BE
31 changed files with 376 additions and 75 deletions

View File

@ -404,6 +404,10 @@ public final class GameData {
private static final Int2ObjectMap<WeaponPromoteData> weaponPromoteDataMap =
new Int2ObjectOpenHashMap<>();
@Getter
private static final Int2ObjectMap<StatuePromoteData> statuePromoteDataMap =
new Int2ObjectOpenHashMap<>();
@Getter
private static final Int2ObjectMap<WeatherData> weatherDataMap = new Int2ObjectOpenHashMap<>();
@ -567,6 +571,10 @@ public final class GameData {
return weaponPromoteDataMap.get((promoteId << 8) + promoteLevel);
}
public static StatuePromoteData getStatuePromoteData(int cityId, int promoteLevel) {
return statuePromoteDataMap.get((cityId << 8) + promoteLevel);
}
public static ReliquaryLevelData getRelicLevelData(int rankLevel, int level) {
return reliquaryLevelDataMap.get((rankLevel << 8) + level);
}

View File

@ -0,0 +1,21 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemParamData;
import lombok.Getter;
import lombok.Setter;
@ResourceType(name = "StatuePromoteExcelConfigData.json")
public class StatuePromoteData extends GameResource {
@Getter @Setter private int level;
@Getter @Setter private int cityId;
@Getter @Setter private ItemParamData[] costItems;
@Getter @Setter private int[] rewardIdList;
@Getter @Setter private int stamina;
@Override
public int getId() {
return (cityId << 8) + level;
}
}

View File

@ -0,0 +1,28 @@
package emu.grasscutter.game.city;
import dev.morphia.annotations.Entity;
import emu.grasscutter.net.proto.CityInfoOuterClass.CityInfo;
import lombok.Getter;
import lombok.Setter;
@Entity
public class CityInfoData {
@Getter @Setter private int cityId;
@Getter @Setter
private int level = 1; // level of the city (include level SotS, level Frostbearing Trees, etc.)
@Getter @Setter private int numCrystal = 0; // number of crystals in the city
public CityInfoData(int cityId) {
this.cityId = cityId;
}
public CityInfo toProto() {
return CityInfo.newBuilder()
.setCityId(cityId)
.setLevel(level)
.setCrystalNum(numCrystal)
.build();
}
}

View File

@ -81,7 +81,7 @@ public final class DungeonManager {
}
public boolean isFinishedSuccessfully() {
if (passConfigData.getLogicType() == null) return false;
if (passConfigData.getConds() == null) return false;
return LogicType.calculate(passConfigData.getLogicType(), finishedConditions);
}
@ -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,11 @@ 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

@ -18,6 +18,8 @@ import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.longs.*;
import java.util.*;
import javax.annotation.Nullable;
import lombok.val;
public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
private final Long2ObjectMap<GameItem> store;
@ -62,6 +64,24 @@ 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 +582,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

@ -2,15 +2,24 @@ package emu.grasscutter.game.managers;
import ch.qos.logback.classic.Logger;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.CityData;
import emu.grasscutter.data.excels.RewardData;
import emu.grasscutter.game.city.CityInfoData;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketLevelupCityRsp;
import emu.grasscutter.server.packet.send.PacketSceneForceUnlockNotify;
import java.util.HashMap;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
@ -208,4 +217,91 @@ public class SotSManager extends BasePlayerManager {
}
}
}
public CityData getCityByAreaId(int areaId) {
return GameData.getCityDataMap().values().stream()
.filter(city -> city.getAreaIdVec().contains(areaId))
.findFirst()
.orElse(null);
}
public CityInfoData getCityInfo(int cityId) {
if (player.getCityInfoData() == null) player.setCityInfoData(new HashMap<>());
var cityInfo = player.getCityInfoData().get(cityId);
if (cityInfo == null) {
cityInfo = new CityInfoData(cityId);
player.getCityInfoData().put(cityId, cityInfo);
}
return cityInfo;
}
public void addCityInfo(CityInfoData cityInfoData) {
if (player.getCityInfoData() == null) player.setCityInfoData(new HashMap<>());
player.getCityInfoData().put(cityInfoData.getCityId(), cityInfoData);
}
public void levelUpSotS(int areaId, int sceneId, int itemNum) {
if (itemNum <= 0) return;
// search city by areaId
var city = this.getCityByAreaId(areaId);
if (city == null) return;
var cityId = city.getCityId();
// check data level up
var cityInfo = this.getCityInfo(cityId);
var nextStatuePromoteData = GameData.getStatuePromoteData(cityId, cityInfo.getLevel() + 1);
if (nextStatuePromoteData == null) return;
var nextLevelCrystal = nextStatuePromoteData.getCostItems()[0].getCount();
// delete item from inventory
var itemNumrequired = Math.min(itemNum, nextLevelCrystal - cityInfo.getNumCrystal());
player
.getInventory()
.removeItemById(nextStatuePromoteData.getCostItems()[0].getId(), itemNumrequired);
// update number oculi
cityInfo.setNumCrystal(cityInfo.getNumCrystal() + itemNumrequired);
// hanble quest
if (itemNumrequired >= 1)
player.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_CITY_LEVEL_UP, cityId, areaId);
// handle oculi overflow
if (cityInfo.getNumCrystal() >= nextLevelCrystal) {
cityInfo.setNumCrystal(cityInfo.getNumCrystal() - nextLevelCrystal);
cityInfo.setLevel(cityInfo.getLevel() + 1);
// update max stamina and notify client
player.setProperty(
PlayerProperty.PROP_MAX_STAMINA,
player.getProperty(PlayerProperty.PROP_MAX_STAMINA)
+ nextStatuePromoteData.getStamina() * 100,
true);
// Add items to inventory
if (nextStatuePromoteData.getRewardIdList() != null) {
for (var rewardId : nextStatuePromoteData.getRewardIdList()) {
RewardData rewardData = GameData.getRewardDataMap().get(rewardId);
if (rewardData == null) continue;
player
.getInventory()
.addItemParamDatas(rewardData.getRewardItemList(), ActionReason.CityLevelupReward);
}
}
// unlock forcescene
player.sendPacket(new PacketSceneForceUnlockNotify(1, true));
}
// update data
this.addCityInfo(cityInfo);
// Packets
player.sendPacket(
new PacketLevelupCityRsp(
sceneId, cityInfo.getLevel(), cityId, cityInfo.getNumCrystal(), areaId, 0));
}
}

View File

@ -389,6 +389,9 @@ public class StaminaManager extends BasePlayerManager {
return;
}
// Update previous motion state
this.previousState = currentState;
// Update the current state.
this.currentState = motionState;
// logger.trace(currentState + "\t" + (notifyEntityId == currentAvatarEntityId ? "character" : "vehicle"));
@ -417,6 +420,11 @@ public class StaminaManager extends BasePlayerManager {
// Internal handler
private void handleImmediateStamina(GameSession session, @NotNull MotionState motionState) {
// Don't double dip on sustained stamina start costs
if (previousState == currentState) {
return;
}
switch (motionState) {
case MOTION_STATE_CLIMB ->
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_START), true);

View File

@ -15,6 +15,7 @@ import emu.grasscutter.game.activity.ActivityManager;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.avatar.AvatarStorage;
import emu.grasscutter.game.battlepass.BattlePassManager;
import emu.grasscutter.game.city.CityInfoData;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.expedition.ExpeditionInfo;
import emu.grasscutter.game.friends.FriendsList;
@ -28,7 +29,6 @@ import emu.grasscutter.game.mail.MailHandler;
import emu.grasscutter.game.managers.FurnitureManager;
import emu.grasscutter.game.managers.ResinManager;
import emu.grasscutter.game.managers.SatiationManager;
import emu.grasscutter.game.managers.SotSManager;
import emu.grasscutter.game.managers.cooking.ActiveCookCompoundData;
import emu.grasscutter.game.managers.cooking.CookingCompoundManager;
import emu.grasscutter.game.managers.cooking.CookingManager;
@ -38,6 +38,7 @@ import emu.grasscutter.game.managers.forging.ActiveForgeData;
import emu.grasscutter.game.managers.forging.ForgingManager;
import emu.grasscutter.game.managers.mapmark.MapMark;
import emu.grasscutter.game.managers.mapmark.MapMarksManager;
import emu.grasscutter.game.managers.SotSManager;
import emu.grasscutter.game.managers.stamina.StaminaManager;
import emu.grasscutter.game.props.*;
import emu.grasscutter.game.quest.QuestManager;
@ -221,6 +222,8 @@ public class Player implements PlayerHook, FieldFetch {
@Getter @Setter private ElementType mainCharacterElement = ElementType.None;
@Getter @Setter private Map<Integer, CityInfoData> cityInfoData; // cityId -> CityData
@Deprecated
@SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only!
public Player() {
@ -267,6 +270,7 @@ public class Player implements PlayerHook, FieldFetch {
this.chatEmojiIdList = new ArrayList<>();
this.playerProgress = new PlayerProgress();
this.activeQuestTimers = new HashSet<>();
this.cityInfoData = new HashMap<>();
this.attackResults = new LinkedBlockingQueue<>();
this.coopRequests = new Int2ObjectOpenHashMap<>();
@ -1520,6 +1524,8 @@ public class Player implements PlayerHook, FieldFetch {
PropChangeReason.PROP_CHANGE_REASON_PLAYER_ADD_EXP));
case PROP_PLAYER_LEVEL -> this.sendPacket(new PacketPlayerPropChangeReasonNotify(this, prop, currentValue, value,
PropChangeReason.PROP_CHANGE_REASON_LEVELUP));
case PROP_MAX_STAMINA -> this.sendPacket(new PacketPlayerPropChangeReasonNotify(this, prop, currentValue, value,
PropChangeReason.PROP_CHANGE_REASON_CITY_LEVELUP));
// TODO: Handle world level changing.
// case PROP_PLAYER_WORLD_LEVEL -> this.sendPacket(new PacketPlayerPropChangeReasonNotify(this, prop, currentValue, value,

View File

@ -16,6 +16,7 @@ import emu.grasscutter.net.proto.ChildQuestOuterClass.ChildQuest;
import emu.grasscutter.net.proto.ParentQuestOuterClass.ParentQuest;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.ConversionUtils;
import emu.grasscutter.utils.Utils;
import java.util.*;
import java.util.stream.Collectors;
import lombok.*;
@ -120,6 +121,15 @@ public class GameMainQuest {
this.triggerQuestVarAction(i, this.questVars[i]);
}
public void randomQuestVar(int i, int low, int high) {
int previousValue = this.questVars[i];
this.questVars[i] = Utils.random.nextInt(low, high);
Grasscutter.getLogger()
.debug("questVar {} value randomized from {} to {}", i, previousValue, this.questVars[i]);
this.triggerQuestVarAction(i, this.questVars[i]);
}
public void triggerQuestVarAction(int index, int value) {
var questManager = this.getQuestManager();
questManager.queueEvent(QuestCond.QUEST_COND_QUEST_VAR_EQUAL, index, value);

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

@ -282,7 +282,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 java.util.Arrays;
import lombok.val;
@QuestValueContent(QUEST_CONTENT_COMPLETE_ANY_TALK)
public class ContentCompleteAnyTalk extends BaseContent {
@ -14,10 +13,21 @@ public class ContentCompleteAnyTalk extends BaseContent {
@Override
public boolean execute(
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
return Stream.of(condition.getParamStr().split(","))
.mapToInt(Integer::parseInt)
.anyMatch(
talkId ->
GameData.getTalkConfigDataMap().get(params[0]) != null && talkId == params[0]);
var conditionTalk =
Arrays.stream(condition.getParamStr().split(",")).mapToInt(Integer::parseInt).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,11 @@ 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);
}
}

View File

@ -59,7 +59,7 @@ public enum QuestExec implements QuestTrigger {
QUEST_EXEC_SET_QUEST_VAR(48),
QUEST_EXEC_INC_QUEST_VAR(49),
QUEST_EXEC_DEC_QUEST_VAR(50),
QUEST_EXEC_RANDOM_QUEST_VAR(51), // missing
QUEST_EXEC_RANDOM_QUEST_VAR(51),
QUEST_EXEC_ACTIVATE_SCANNING_PIC(52), // missing, currently unused
QUEST_EXEC_RELOAD_SCENE_TAG(53), // missing
QUEST_EXEC_REGISTER_DYNAMIC_GROUP_ONLY(54), // missing

View File

@ -0,0 +1,21 @@
package emu.grasscutter.game.quest.exec;
import emu.grasscutter.data.excels.quest.QuestData;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueExec;
import emu.grasscutter.game.quest.enums.QuestExec;
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
@QuestValueExec(QuestExec.QUEST_EXEC_RANDOM_QUEST_VAR)
public class ExecRandomQuestVar extends QuestExecHandler {
@Override
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
quest
.getMainQuest()
.randomQuestVar(
Integer.parseInt(paramStr[0]),
Integer.parseInt(paramStr[1]),
Integer.parseInt(paramStr[2]));
return true;
}
}

View File

@ -0,0 +1,22 @@
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.LevelupCityReqOuterClass.LevelupCityReq;
import emu.grasscutter.server.game.GameSession;
@Opcodes(PacketOpcodes.LevelupCityReq)
public class HandlerLevelupCityReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
LevelupCityReq req = LevelupCityReq.parseFrom(payload);
// Level up city
session
.getPlayer()
.getSotsManager()
.levelUpSotS(req.getAreaId(), req.getSceneId(), req.getItemNum());
}
}

View File

@ -3,7 +3,6 @@ package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.CityInfoOuterClass.CityInfo;
import emu.grasscutter.net.proto.GetSceneAreaRspOuterClass.GetSceneAreaRsp;
public class PacketGetSceneAreaRsp extends BasePacket {
@ -17,9 +16,9 @@ public class PacketGetSceneAreaRsp extends BasePacket {
GetSceneAreaRsp.newBuilder()
.setSceneId(sceneId)
.addAllAreaIdList(player.getUnlockedSceneAreas(sceneId))
.addCityInfoList(CityInfo.newBuilder().setCityId(1).setLevel(1).build())
.addCityInfoList(CityInfo.newBuilder().setCityId(2).setLevel(1).build())
.addCityInfoList(CityInfo.newBuilder().setCityId(3).setLevel(1).build())
.addCityInfoList(player.getSotsManager().getCityInfo(1).toProto())
.addCityInfoList(player.getSotsManager().getCityInfo(2).toProto())
.addCityInfoList(player.getSotsManager().getCityInfo(3).toProto())
.build();
this.setData(p);

View File

@ -0,0 +1,29 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.CityInfoOuterClass.CityInfo;
import emu.grasscutter.net.proto.LevelupCityRspOuterClass.LevelupCityRsp;
public class PacketLevelupCityRsp extends BasePacket {
public PacketLevelupCityRsp(
int sceneId, int level, int cityId, int crystalNum, int areaId, int retcode) {
super(PacketOpcodes.LevelupCityRsp);
LevelupCityRsp proto =
LevelupCityRsp.newBuilder()
.setSceneId(sceneId)
.setCityInfo(
CityInfo.newBuilder()
.setCityId(cityId)
.setLevel(level)
.setCrystalNum(crystalNum)
.build())
.setAreaId(areaId)
.setRetcode(retcode)
.build();
this.setData(proto);
}
}