mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-06-26 18:14:50 +08:00
Compare commits
2 Commits
2006a21008
...
c2a260858c
Author | SHA1 | Date | |
---|---|---|---|
|
c2a260858c | ||
|
ea1d56a82a |
@ -8,6 +8,7 @@ import emu.grasscutter.auth.DefaultAuthentication;
|
|||||||
import emu.grasscutter.command.CommandMap;
|
import emu.grasscutter.command.CommandMap;
|
||||||
import emu.grasscutter.command.DefaultPermissionHandler;
|
import emu.grasscutter.command.DefaultPermissionHandler;
|
||||||
import emu.grasscutter.command.PermissionHandler;
|
import emu.grasscutter.command.PermissionHandler;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
|
||||||
import emu.grasscutter.data.ResourceLoader;
|
import emu.grasscutter.data.ResourceLoader;
|
||||||
import emu.grasscutter.database.DatabaseManager;
|
import emu.grasscutter.database.DatabaseManager;
|
||||||
import emu.grasscutter.game.managers.energy.EnergyManager;
|
import emu.grasscutter.game.managers.energy.EnergyManager;
|
||||||
@ -117,6 +118,7 @@ public final class Grasscutter {
|
|||||||
ResourceLoader.loadAll();
|
ResourceLoader.loadAll();
|
||||||
ScriptLoader.init();
|
ScriptLoader.init();
|
||||||
EnergyManager.initialize();
|
EnergyManager.initialize();
|
||||||
|
DungeonChallenge.initialize();
|
||||||
|
|
||||||
// Initialize database.
|
// Initialize database.
|
||||||
DatabaseManager.initialize();
|
DatabaseManager.initialize();
|
||||||
|
22
src/main/java/emu/grasscutter/game/dungeons/DungeonDrop.java
Normal file
22
src/main/java/emu/grasscutter/game/dungeons/DungeonDrop.java
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package emu.grasscutter.game.dungeons;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DungeonDrop {
|
||||||
|
private int dungeonId;
|
||||||
|
private List<DungeonDropEntry> drops;
|
||||||
|
|
||||||
|
public int getDungeonId() {
|
||||||
|
return dungeonId;
|
||||||
|
}
|
||||||
|
public void setDungeonId(int dungeonId) {
|
||||||
|
this.dungeonId = dungeonId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DungeonDropEntry> getDrops() {
|
||||||
|
return drops;
|
||||||
|
}
|
||||||
|
public void setDrops(List<DungeonDropEntry> drops) {
|
||||||
|
this.drops = drops;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package emu.grasscutter.game.dungeons;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DungeonDropEntry {
|
||||||
|
private List<Integer> counts;
|
||||||
|
private List<Integer> items;
|
||||||
|
private List<Integer> probabilities;
|
||||||
|
private List<Integer> itemProbabilities;
|
||||||
|
private boolean mpDouble;
|
||||||
|
|
||||||
|
public List<Integer> getCounts() {
|
||||||
|
return counts;
|
||||||
|
}
|
||||||
|
public void setCounts(List<Integer> counts) {
|
||||||
|
this.counts = counts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getItems() {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
public void setItems(List<Integer> items) {
|
||||||
|
this.items = items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getProbabilities() {
|
||||||
|
return probabilities;
|
||||||
|
}
|
||||||
|
public void setProbabilities(List<Integer> probabilities) {
|
||||||
|
this.probabilities = probabilities;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getItemProbabilities() {
|
||||||
|
return itemProbabilities;
|
||||||
|
}
|
||||||
|
public void setItemProbabilities(List<Integer> itemProbabilities) {
|
||||||
|
this.itemProbabilities = itemProbabilities;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMpDouble() {
|
||||||
|
return mpDouble;
|
||||||
|
}
|
||||||
|
public void setMpDouble(boolean mpDouble) {
|
||||||
|
this.mpDouble = mpDouble;
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +1,37 @@
|
|||||||
package emu.grasscutter.game.dungeons.challenge;
|
package emu.grasscutter.game.dungeons.challenge;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.data.DataLoader;
|
||||||
import emu.grasscutter.data.common.ItemParamData;
|
import emu.grasscutter.data.common.ItemParamData;
|
||||||
import emu.grasscutter.data.excels.DungeonData;
|
import emu.grasscutter.data.excels.DungeonData;
|
||||||
|
import emu.grasscutter.game.dungeons.DungeonDrop;
|
||||||
|
import emu.grasscutter.game.dungeons.DungeonDropEntry;
|
||||||
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
|
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
|
||||||
import emu.grasscutter.game.inventory.GameItem;
|
import emu.grasscutter.game.inventory.GameItem;
|
||||||
|
import emu.grasscutter.game.inventory.ItemType;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.ActionReason;
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
import emu.grasscutter.game.world.Scene;
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
import emu.grasscutter.scripts.constants.EventType;
|
import emu.grasscutter.scripts.constants.EventType;
|
||||||
import emu.grasscutter.scripts.data.SceneGroup;
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
import emu.grasscutter.scripts.data.ScriptArgs;
|
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||||
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
|
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
|
||||||
|
import emu.grasscutter.utils.Utils;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
|
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.Reader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
public class DungeonChallenge extends WorldChallenge {
|
public class DungeonChallenge extends WorldChallenge {
|
||||||
|
|
||||||
@ -25,14 +41,32 @@ public class DungeonChallenge extends WorldChallenge {
|
|||||||
private boolean stage;
|
private boolean stage;
|
||||||
private IntSet rewardedPlayers;
|
private IntSet rewardedPlayers;
|
||||||
|
|
||||||
public DungeonChallenge(Scene scene, SceneGroup group,
|
private final static Int2ObjectMap<List<DungeonDropEntry>> dungeonDropData = new Int2ObjectOpenHashMap<>();
|
||||||
int challengeId, int challengeIndex,
|
|
||||||
List<Integer> paramList,
|
public static void initialize() {
|
||||||
int timeLimit, int goal,
|
// Read the data we need for dungeon rewards drops.
|
||||||
List<ChallengeTrigger> challengeTriggers) {
|
try (Reader fileReader = new InputStreamReader(DataLoader.load("DungeonDrop.json"))) {
|
||||||
super(scene, group, challengeId, challengeIndex, paramList, timeLimit, goal, challengeTriggers);
|
List<DungeonDrop> dungeonDropList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, DungeonDrop.class).getType());
|
||||||
this.setRewardedPlayers(new IntOpenHashSet());
|
|
||||||
}
|
for (DungeonDrop entry : dungeonDropList) {
|
||||||
|
dungeonDropData.put(entry.getDungeonId(), entry.getDrops());
|
||||||
|
}
|
||||||
|
|
||||||
|
Grasscutter.getLogger().info("Loaded {} dungeon drop data entries.", dungeonDropData.size());
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
Grasscutter.getLogger().error("Unable to load dungeon drop data.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DungeonChallenge(Scene scene, SceneGroup group,
|
||||||
|
int challengeId, int challengeIndex,
|
||||||
|
List<Integer> paramList,
|
||||||
|
int timeLimit, int goal,
|
||||||
|
List<ChallengeTrigger> challengeTriggers) {
|
||||||
|
super(scene, group, challengeId, challengeIndex, paramList, timeLimit, goal, challengeTriggers);
|
||||||
|
this.setRewardedPlayers(new IntOpenHashSet());
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isStage() {
|
public boolean isStage() {
|
||||||
return this.stage;
|
return this.stage;
|
||||||
@ -50,42 +84,129 @@ public class DungeonChallenge extends WorldChallenge {
|
|||||||
this.rewardedPlayers = rewardedPlayers;
|
this.rewardedPlayers = rewardedPlayers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void done() {
|
public void done() {
|
||||||
super.done();
|
super.done();
|
||||||
if (this.isSuccess()) {
|
if (this.isSuccess()) {
|
||||||
// Settle
|
// Settle
|
||||||
this.settle();
|
settle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void settle() {
|
private void settle() {
|
||||||
if (!this.stage) {
|
if(!stage){
|
||||||
this.getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(this.getScene()));
|
getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
|
||||||
this.getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE,
|
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE,
|
||||||
new ScriptArgs(this.isSuccess() ? 1 : 0));
|
new ScriptArgs(this.isSuccess() ? 1 : 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getStatueDrops(Player player) {
|
private List<GameItem> rollRewards(boolean useCondensed) {
|
||||||
DungeonData dungeonData = this.getScene().getDungeonData();
|
List<GameItem> rewards = new ArrayList<>();
|
||||||
if (!this.isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
|
int dungeonId = this.getScene().getDungeonData().getId();
|
||||||
return;
|
// If we have specific drop data for this dungeon, we use it.
|
||||||
}
|
if (dungeonDropData.containsKey(dungeonId)) {
|
||||||
|
List<DungeonDropEntry> dropEntries = dungeonDropData.get(dungeonId);
|
||||||
|
|
||||||
// Already rewarded
|
// Roll for each drop group.
|
||||||
if (this.getRewardedPlayers().contains(player.getUid())) {
|
for (var entry : dropEntries) {
|
||||||
return;
|
// Determine the number of drops we get for this entry.
|
||||||
}
|
int start = entry.getCounts().get(0);
|
||||||
|
int end = entry.getCounts().get(entry.getCounts().size() - 1);
|
||||||
|
var candidateAmounts = IntStream.range(start, end + 1).boxed().collect(Collectors.toList());
|
||||||
|
|
||||||
List<GameItem> rewards = new ArrayList<>();
|
int amount = Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
|
||||||
for (ItemParamData param : this.getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
|
|
||||||
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
|
if (useCondensed) {
|
||||||
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
amount += Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
|
||||||
|
}
|
||||||
|
|
||||||
this.getRewardedPlayers().add(player.getUid());
|
// Double rewards in multiplay mode, if specified.
|
||||||
}
|
if (entry.isMpDouble() && this.getScene().getPlayerCount() > 1) {
|
||||||
|
amount *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roll items for this group.
|
||||||
|
// Here, we have to handle stacking, or the client will not display results correctly.
|
||||||
|
// For now, we use the following logic: If the possible drop item are a list of multiple items,
|
||||||
|
// we roll them separately. If not, we stack them. This should work out in practice, at least
|
||||||
|
// for the currently existing set of dungeons.
|
||||||
|
if (entry.getItems().size() == 1) {
|
||||||
|
rewards.add(new GameItem(entry.getItems().get(0), amount));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = 0; i < amount; i++) {
|
||||||
|
// int itemIndex = ThreadLocalRandom.current().nextInt(0, entry.getItems().size());
|
||||||
|
// int itemId = entry.getItems().get(itemIndex);
|
||||||
|
int itemId = Utils.drawRandomListElement(entry.getItems(), entry.getItemProbabilities());
|
||||||
|
rewards.add(new GameItem(itemId, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise, we fall back to the preview data.
|
||||||
|
else {
|
||||||
|
Grasscutter.getLogger().info("No drop data found or dungeon {}, falling back to preview data ...", dungeonId);
|
||||||
|
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
|
||||||
|
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rewards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getStatueDrops(Player player, GadgetInteractReq request) {
|
||||||
|
DungeonData dungeonData = getScene().getDungeonData();
|
||||||
|
int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20;
|
||||||
|
|
||||||
|
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already rewarded
|
||||||
|
if (getRewardedPlayers().contains(player.getUid())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get rewards.
|
||||||
|
List<GameItem> rewards = new ArrayList<>();
|
||||||
|
|
||||||
|
if (request.getIsUseCondenseResin()) {
|
||||||
|
// Check if condensed resin is usable here.
|
||||||
|
// For this, we use the following logic for now:
|
||||||
|
// The normal resin cost of the dungeon has to be 20.
|
||||||
|
if (resinCost != 20) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the player has condensed resin.
|
||||||
|
GameItem condensedResin = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(220007);
|
||||||
|
if (condensedResin == null || condensedResin.getCount() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deduct.
|
||||||
|
player.getInventory().removeItem(condensedResin, 1);
|
||||||
|
|
||||||
|
// Roll rewards.
|
||||||
|
rewards.addAll(this.rollRewards(true));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If the player used regular resin, try to deduct.
|
||||||
|
// Stop if insufficient resin.
|
||||||
|
boolean success = player.getResinManager().useResin(resinCost);
|
||||||
|
if (!success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roll rewards.
|
||||||
|
rewards.addAll(this.rollRewards(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add rewards to player and send notification.
|
||||||
|
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
|
||||||
|
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
||||||
|
|
||||||
|
getRewardedPlayers().add(player.getUid());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import emu.grasscutter.Grasscutter;
|
|||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.proto.BossChestInfoOuterClass.BossChestInfo;
|
import emu.grasscutter.net.proto.BossChestInfoOuterClass.BossChestInfo;
|
||||||
|
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
import emu.grasscutter.net.proto.InterOpTypeOuterClass.InterOpType;
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass.InterOpType;
|
||||||
import emu.grasscutter.net.proto.InteractTypeOuterClass;
|
import emu.grasscutter.net.proto.InteractTypeOuterClass;
|
||||||
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
||||||
@ -12,25 +13,24 @@ import emu.grasscutter.scripts.constants.ScriptGadgetState;
|
|||||||
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
|
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
|
||||||
|
|
||||||
public class GadgetChest extends GadgetContent {
|
public class GadgetChest extends GadgetContent {
|
||||||
|
|
||||||
public GadgetChest(EntityGadget gadget) {
|
public GadgetChest(EntityGadget gadget) {
|
||||||
super(gadget);
|
super(gadget);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onInteract(Player player, InterOpType opType) {
|
public boolean onInteract(Player player, GadgetInteractReq req) {
|
||||||
var chestInteractHandlerMap = this.getGadget().getScene().getWorld().getServer().getWorldDataManager().getChestInteractHandlerMap();
|
var chestInteractHandlerMap = getGadget().getScene().getWorld().getServer().getWorldDataManager().getChestInteractHandlerMap();
|
||||||
var handler = chestInteractHandlerMap.get(this.getGadget().getGadgetData().getJsonName());
|
var handler = chestInteractHandlerMap.get(getGadget().getGadgetData().getJsonName());
|
||||||
if (handler == null) {
|
if(handler == null){
|
||||||
Grasscutter.getLogger().warn("Could not found the handler of this type of Chests {}", this.getGadget().getGadgetData().getJsonName());
|
Grasscutter.getLogger().warn("Could not found the handler of this type of Chests {}", getGadget().getGadgetData().getJsonName());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opType == InterOpType.INTER_OP_TYPE_START && handler.isTwoStep()) {
|
if(req.getOpType() == InterOpType.INTER_OP_TYPE_START && handler.isTwoStep()){
|
||||||
player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_START));
|
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_START));
|
||||||
return false;
|
return false;
|
||||||
} else {
|
}else{
|
||||||
var success = handler.onInteract(this, player);
|
var success = handler.onInteract(this, player);
|
||||||
if (!success) {
|
if (!success){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +58,5 @@ public class GadgetChest extends GadgetContent {
|
|||||||
.addAllRemainUidList(players)
|
.addAllRemainUidList(players)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package emu.grasscutter.game.entity.gadget;
|
|||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
|
|
||||||
public abstract class GadgetContent {
|
public abstract class GadgetContent {
|
||||||
@ -13,10 +14,10 @@ public abstract class GadgetContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public EntityGadget getGadget() {
|
public EntityGadget getGadget() {
|
||||||
return this.gadget;
|
return gadget;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType);
|
public abstract boolean onInteract(Player player, GadgetInteractReq req);
|
||||||
|
|
||||||
public abstract void onBuildProto(SceneGadgetInfo.Builder gadgetInfo);
|
public abstract void onBuildProto(SceneGadgetInfo.Builder gadgetInfo);
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,13 @@ import emu.grasscutter.game.player.Player;
|
|||||||
import emu.grasscutter.game.props.ActionReason;
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
|
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
|
||||||
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
|
|
||||||
public class GadgetGatherPoint extends GadgetContent {
|
public class GadgetGatherPoint extends GadgetContent {
|
||||||
private final GatherData gatherData;
|
private final GatherData gatherData;
|
||||||
|
|
||||||
|
|
||||||
public GadgetGatherPoint(EntityGadget gadget) {
|
public GadgetGatherPoint(EntityGadget gadget) {
|
||||||
super(gadget);
|
super(gadget);
|
||||||
this.gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
|
this.gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
|
||||||
@ -26,8 +28,8 @@ public class GadgetGatherPoint extends GadgetContent {
|
|||||||
return this.getGatherData().getItemId();
|
return this.getGatherData().getItemId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
|
public boolean onInteract(Player player, GadgetInteractReq req) {
|
||||||
GameItem item = new GameItem(this.gatherData.getItemId(), 1);
|
GameItem item = new GameItem(gatherData.getItemId(), 1);
|
||||||
|
|
||||||
player.getInventory().addItem(item, ActionReason.Gather);
|
player.getInventory().addItem(item, ActionReason.Gather);
|
||||||
|
|
||||||
|
@ -4,22 +4,22 @@ import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
|
|||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
||||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
|
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
|
||||||
|
|
||||||
public class GadgetRewardStatue extends GadgetContent {
|
public class GadgetRewardStatue extends GadgetContent {
|
||||||
|
|
||||||
public GadgetRewardStatue(EntityGadget gadget) {
|
public GadgetRewardStatue(EntityGadget gadget) {
|
||||||
super(gadget);
|
super(gadget);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
|
public boolean onInteract(Player player, GadgetInteractReq req) {
|
||||||
if (player.getScene().getChallenge() != null && player.getScene().getChallenge() instanceof DungeonChallenge dungeonChallenge) {
|
if (player.getScene().getChallenge() != null && player.getScene().getChallenge() instanceof DungeonChallenge dungeonChallenge) {
|
||||||
dungeonChallenge.getStatueDrops(player);
|
dungeonChallenge.getStatueDrops(player, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractType.INTERACT_TYPE_OPEN_STATUE));
|
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_STATUE));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package emu.grasscutter.game.entity.gadget;
|
|||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo;
|
import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo;
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
@ -13,6 +14,9 @@ import java.util.Arrays;
|
|||||||
public class GadgetWorktop extends GadgetContent {
|
public class GadgetWorktop extends GadgetContent {
|
||||||
private IntSet worktopOptions;
|
private IntSet worktopOptions;
|
||||||
|
|
||||||
|
public boolean onInteract(Player player, GadgetInteractReq req) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
public GadgetWorktop(EntityGadget gadget) {
|
public GadgetWorktop(EntityGadget gadget) {
|
||||||
super(gadget);
|
super(gadget);
|
||||||
}
|
}
|
||||||
@ -35,10 +39,6 @@ public class GadgetWorktop extends GadgetContent {
|
|||||||
this.worktopOptions.remove(option);
|
this.worktopOptions.remove(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||||
if (this.worktopOptions == null) {
|
if (this.worktopOptions == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -45,6 +45,7 @@ import emu.grasscutter.net.packet.BasePacket;
|
|||||||
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
||||||
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
||||||
import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
|
import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
|
||||||
|
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
import emu.grasscutter.net.proto.*;
|
import emu.grasscutter.net.proto.*;
|
||||||
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
||||||
import emu.grasscutter.net.proto.MpSettingTypeOuterClass.MpSettingType;
|
import emu.grasscutter.net.proto.MpSettingTypeOuterClass.MpSettingType;
|
||||||
@ -1026,7 +1027,7 @@ public class Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void interactWith(int gadgetEntityId, InterOpTypeOuterClass.InterOpType opType) {
|
public void interactWith(int gadgetEntityId, GadgetInteractReq req) {
|
||||||
GameEntity entity = this.getScene().getEntityById(gadgetEntityId);
|
GameEntity entity = this.getScene().getEntityById(gadgetEntityId);
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
return;
|
return;
|
||||||
@ -1059,7 +1060,7 @@ public class Player {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean shouldDelete = gadget.getContent().onInteract(this, opType);
|
boolean shouldDelete = gadget.getContent().onInteract(this, req);
|
||||||
|
|
||||||
if (shouldDelete) {
|
if (shouldDelete) {
|
||||||
entity.getScene().removeEntity(entity);
|
entity.getScene().removeEntity(entity);
|
||||||
|
@ -366,6 +366,17 @@ public class SceneScriptManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public EntityGadget createGadget(int groupId, int blockId, SceneGadget g) {
|
public EntityGadget createGadget(int groupId, int blockId, SceneGadget g) {
|
||||||
|
if(g.isOneoff){
|
||||||
|
var hasEntity = getScene().getEntities().values().stream()
|
||||||
|
.filter(e -> e instanceof EntityGadget)
|
||||||
|
.filter(e -> e.getGroupId() == g.group.id)
|
||||||
|
.filter(e -> e.getConfigId() == g.config_id)
|
||||||
|
.findFirst();
|
||||||
|
if(hasEntity.isPresent()){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EntityGadget entity = new EntityGadget(this.getScene(), g.gadget_id, g.pos);
|
EntityGadget entity = new EntityGadget(this.getScene(), g.gadget_id, g.pos);
|
||||||
|
|
||||||
if (entity.getGadgetData() == null) {
|
if (entity.getGadgetData() == null) {
|
||||||
|
@ -11,4 +11,9 @@ public class SceneGadget extends SceneObject {
|
|||||||
public int point_type;
|
public int point_type;
|
||||||
public SceneBossChest boss_chest;
|
public SceneBossChest boss_chest;
|
||||||
public int interact_id;
|
public int interact_id;
|
||||||
|
public boolean isOneoff;
|
||||||
|
|
||||||
|
public void setIsOneoff(boolean isOneoff){
|
||||||
|
this.isOneoff = isOneoff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,8 @@ public class LuaSerializer implements Serializer {
|
|||||||
object = (T) (Float) keyValue.tofloat(); // terrible...
|
object = (T) (Float) keyValue.tofloat(); // terrible...
|
||||||
} else if (keyValue.isstring()) {
|
} else if (keyValue.isstring()) {
|
||||||
object = (T) keyValue.tojstring();
|
object = (T) keyValue.tojstring();
|
||||||
|
} else if (keyValue.isboolean()) {
|
||||||
|
object = (T) (Boolean) keyValue.toboolean();
|
||||||
} else {
|
} else {
|
||||||
object = (T) keyValue;
|
object = (T) keyValue;
|
||||||
}
|
}
|
||||||
@ -115,6 +117,8 @@ public class LuaSerializer implements Serializer {
|
|||||||
methodAccess.invoke(object, fieldMeta.index, keyValue.toint());
|
methodAccess.invoke(object, fieldMeta.index, keyValue.toint());
|
||||||
} else if (fieldMeta.getType().equals(String.class)) {
|
} else if (fieldMeta.getType().equals(String.class)) {
|
||||||
methodAccess.invoke(object, fieldMeta.index, keyValue.tojstring());
|
methodAccess.invoke(object, fieldMeta.index, keyValue.tojstring());
|
||||||
|
} else if (fieldMeta.getType().equals(boolean.class)) {
|
||||||
|
methodAccess.invoke(object, fieldMeta.index, keyValue.toboolean());
|
||||||
} else {
|
} else {
|
||||||
methodAccess.invoke(object, fieldMeta.index, keyValue.tojstring());
|
methodAccess.invoke(object, fieldMeta.index, keyValue.tojstring());
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,10 @@ import emu.grasscutter.server.game.GameSession;
|
|||||||
|
|
||||||
@Opcodes(PacketOpcodes.GadgetInteractReq)
|
@Opcodes(PacketOpcodes.GadgetInteractReq)
|
||||||
public class HandlerGadgetInteractReq extends PacketHandler {
|
public class HandlerGadgetInteractReq extends PacketHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
GadgetInteractReq req = GadgetInteractReq.parseFrom(payload);
|
GadgetInteractReq req = GadgetInteractReq.parseFrom(payload);
|
||||||
|
|
||||||
session.getPlayer().interactWith(req.getGadgetEntityId(), req.getOpType());
|
session.getPlayer().interactWith(req.getGadgetEntityId(), req);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import emu.grasscutter.data.DataLoader;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufUtil;
|
import io.netty.buffer.ByteBufUtil;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.util.internal.ThreadLocalRandom;
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -20,6 +21,7 @@ import java.time.ZoneOffset;
|
|||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.temporal.TemporalAdjusters;
|
import java.time.temporal.TemporalAdjusters;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
@ -402,4 +404,35 @@ public final class Utils {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Draws a random element from the given list, following the given probability distribution, if given.
|
||||||
|
* @param list The list from which to draw the element.
|
||||||
|
* @param probabilities The probability distribution. This is given as a list of probabilities of the same length it `list`.
|
||||||
|
* @return A randomly drawn element from the given list.
|
||||||
|
*/
|
||||||
|
public static <T> T drawRandomListElement(List<T> list, List<Integer> probabilities) {
|
||||||
|
// If we don't have a probability distribution, or the size of the distribution does not match
|
||||||
|
// the size of the list, we assume uniform distribution.
|
||||||
|
if (probabilities == null || probabilities.size() <= 1 || probabilities.size() != list.size()) {
|
||||||
|
int index = ThreadLocalRandom.current().nextInt(0, list.size());
|
||||||
|
return list.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we roll with the given distribution.
|
||||||
|
int totalProbabilityMass = probabilities.stream().reduce(Integer::sum).get();
|
||||||
|
int roll = ThreadLocalRandom.current().nextInt(1, totalProbabilityMass + 1);
|
||||||
|
|
||||||
|
int currentTotalChance = 0;
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
currentTotalChance += probabilities.get(i);
|
||||||
|
|
||||||
|
if (roll <= currentTotalChance) {
|
||||||
|
return list.get(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should never happen.
|
||||||
|
return list.get(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
4142
src/main/resources/defaults/data/DungeonDrop.json
Normal file
4142
src/main/resources/defaults/data/DungeonDrop.json
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user