Commit ef2e159b authored by Melledy's avatar Melledy Committed by GitHub
Browse files

Merge pull request #297 from Kengxxiao/dev-feature-drop

Implement simple monster drop
parents e1b6cbc8 669e58d9
[
{
"monsterId": 21010101,
"dropDataList": [
{
"itemId": 112005,
"minCount": 1,
"maxCount": 3,
"minWeight": 0,
"maxWeight": 10000
}
]
}
]
...@@ -16,6 +16,7 @@ public final class ReloadCommand implements CommandHandler { ...@@ -16,6 +16,7 @@ public final class ReloadCommand implements CommandHandler {
CommandHandler.sendMessage(sender, "Reloading config."); CommandHandler.sendMessage(sender, "Reloading config.");
Grasscutter.loadConfig(); Grasscutter.loadConfig();
Grasscutter.getGameServer().getGachaManager().load(); Grasscutter.getGameServer().getGachaManager().load();
Grasscutter.getGameServer().getDropManager().load();
Grasscutter.getGameServer().getShopManager().load(); Grasscutter.getGameServer().getShopManager().load();
Grasscutter.getDispatchServer().loadQueries(); Grasscutter.getDispatchServer().loadQueries();
CommandHandler.sendMessage(sender, "Reload complete."); CommandHandler.sendMessage(sender, "Reload complete.");
......
package emu.grasscutter.game.drop;
public class DropData {
private int minWeight;
private int maxWeight;
private int itemId;
private int minCount;
private int maxCount;
private boolean share = false;
public int getItemId() {
return itemId;
}
public void setItemId(int itemId) {
this.itemId = itemId;
}
public int getMinCount() {
return minCount;
}
public int getMaxCount() {
return maxCount;
}
public int getMinWeight() {
return minWeight;
}
public int getMaxWeight() {
return maxWeight;
}
public boolean isShare() {
return share;
}
public void setIsShare(boolean share) {
this.share = share;
}
public boolean isGive() {
return give;
}
private boolean give = false;
public boolean isExp() {
return exp;
}
private boolean exp = false;
}
package emu.grasscutter.game.drop;
import java.util.List;
public class DropInfo {
public int getMonsterId() {
return monsterId;
}
public List<DropData> getDropDataList() {
return dropDataList;
}
private int monsterId;
private List<DropData> dropDataList;
}
package emu.grasscutter.game.drop;
import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.ItemData;
import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.FileReader;
import java.util.Collection;
import java.util.List;
public class DropManager {
public GameServer getGameServer() {
return gameServer;
}
private final GameServer gameServer;
public Int2ObjectMap<List<DropData>> getDropData() {
return dropData;
}
private final Int2ObjectMap<List<DropData>> dropData;
public DropManager(GameServer gameServer) {
this.gameServer = gameServer;
this.dropData = new Int2ObjectOpenHashMap<>();
this.load();
}
public synchronized void load() {
try (FileReader fileReader = new FileReader(Grasscutter.getConfig().DATA_FOLDER + "Drop.json")) {
getDropData().clear();
List<DropInfo> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, DropInfo.class).getType());
if(banners.size() > 0) {
for (DropInfo di : banners) {
getDropData().put(di.getMonsterId(), di.getDropDataList());
}
Grasscutter.getLogger().info("Drop data successfully loaded.");
} else {
Grasscutter.getLogger().error("Unable to load drop data. Drop data size is 0.");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void processDrop(DropData dd, EntityMonster em, Player gp) {
int target = Utils.randomRange(1, 10000);
if (target >= dd.getMinWeight() && target < dd.getMaxWeight()) {
ItemData itemData = GameData.getItemDataMap().get(dd.getItemId());
int num = Utils.randomRange(dd.getMinCount(), dd.getMaxCount());
if (!dd.isGive()) {
if (itemData.isEquip()) {
for (int i = 0; i < num; i++) {
float range = (5f + (.1f * num));
Position pos = em.getPosition().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2));
EntityItem entity = new EntityItem(em.getScene(), gp, itemData, pos, num, dd.isShare());
if (!dd.isShare())
em.getScene().addEntityToSingleClient(gp, entity);
else
em.getScene().addEntity(entity);
}
} else {
Position pos = em.getPosition().clone().addY(3f);
EntityItem entity = new EntityItem(em.getScene(), gp, itemData, pos, num, dd.isShare());
if (!dd.isShare())
em.getScene().addEntityToSingleClient(gp, entity);
else
em.getScene().addEntity(entity);
}
}
}
}
public void callDrop(EntityMonster em) {
int id = em.getMonsterData().getId();
if (getDropData().containsKey(id)) {
for (DropData dd : getDropData().get(id)) {
if (dd.isShare())
processDrop(dd, em, null);
else {
for (Player gp : em.getScene().getPlayers()) {
processDrop(dd, em, gp);
}
}
}
}
}
}
...@@ -6,7 +6,6 @@ import emu.grasscutter.game.player.Player; ...@@ -6,7 +6,6 @@ import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.World;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair; import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo; import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
...@@ -30,14 +29,30 @@ public class EntityItem extends EntityGadget { ...@@ -30,14 +29,30 @@ public class EntityItem extends EntityGadget {
private final GameItem item; private final GameItem item;
private final long guid; private final long guid;
private final boolean share;
public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count) { public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count) {
super(scene); super(scene);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET); this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.pos = new Position(pos); this.pos = new Position(pos);
this.rot = new Position(); this.rot = new Position();
this.guid = player.getNextGameGuid(); this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
this.item = new GameItem(itemData, count);
this.share = true;
}
// In official game, some drop items are shared to all players, and some other items are independent to all players
// For example, if you killed a monster in MP mode, all players could get drops but rarity and number of them are different
// but if you broke regional mine, when someone picked up the drop then it disappeared
public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count, boolean share) {
super(scene);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.pos = new Position(pos);
this.rot = new Position();
this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
this.item = new GameItem(itemData, count); this.item = new GameItem(itemData, count);
this.share = share;
} }
@Override @Override
...@@ -81,6 +96,10 @@ public class EntityItem extends EntityGadget { ...@@ -81,6 +96,10 @@ public class EntityItem extends EntityGadget {
return null; return null;
} }
public boolean isShare() {
return share;
}
@Override @Override
public SceneEntityInfo toProto() { public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder() EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
......
package emu.grasscutter.game.player; package emu.grasscutter.game.player;
import java.time.Instant;
import java.util.*;
import dev.morphia.annotations.*; import dev.morphia.annotations.*;
import emu.grasscutter.GameConstants; import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.PlayerLevelData; import emu.grasscutter.data.def.PlayerLevelData;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.avatar.AvatarProfileData;
import emu.grasscutter.game.avatar.AvatarStorage;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.game.CoopRequest; import emu.grasscutter.game.CoopRequest;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.avatar.AvatarProfileData;
import emu.grasscutter.game.avatar.AvatarStorage;
import emu.grasscutter.game.entity.EntityItem; import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.friends.FriendsList; import emu.grasscutter.game.friends.FriendsList;
...@@ -35,7 +31,6 @@ import emu.grasscutter.net.proto.HeadImageOuterClass.HeadImage; ...@@ -35,7 +31,6 @@ import emu.grasscutter.net.proto.HeadImageOuterClass.HeadImage;
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;
import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo; import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo;
import emu.grasscutter.net.proto.PlayerApplyEnterMpReasonOuterClass.PlayerApplyEnterMpReason;
import emu.grasscutter.net.proto.PlayerApplyEnterMpResultNotifyOuterClass; import emu.grasscutter.net.proto.PlayerApplyEnterMpResultNotifyOuterClass;
import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo; import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo;
import emu.grasscutter.net.proto.PlayerWorldLocationInfoOuterClass; import emu.grasscutter.net.proto.PlayerWorldLocationInfoOuterClass;
...@@ -43,11 +38,12 @@ import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; ...@@ -43,11 +38,12 @@ import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.*; import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.DateHelper; import emu.grasscutter.utils.DateHelper;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.time.Instant;
import java.util.*; import java.util.*;
@Entity(value = "players", useDiscriminator = false) @Entity(value = "players", useDiscriminator = false)
...@@ -747,19 +743,30 @@ public class Player { ...@@ -747,19 +743,30 @@ public class Player {
return; return;
} }
// Delete
entity.getScene().removeEntity(entity);
// Handle // Handle
if (entity instanceof EntityItem) { if (entity instanceof EntityItem) {
// Pick item // Pick item
EntityItem drop = (EntityItem) entity; EntityItem drop = (EntityItem) entity;
if (!drop.isShare()) // check drop owner to avoid someone picked up item in others' world
{
int dropOwner = (int)(drop.getGuid() >> 32);
if (dropOwner != getUid())
return;
}
entity.getScene().removeEntity(entity);
GameItem item = new GameItem(drop.getItemData(), drop.getCount()); GameItem item = new GameItem(drop.getItemData(), drop.getCount());
// Add to inventory // Add to inventory
boolean success = getInventory().addItem(item, ActionReason.SubfieldDrop); boolean success = getInventory().addItem(item, ActionReason.SubfieldDrop);
if (success) { if (success) {
this.sendPacket(new PacketGadgetInteractRsp(drop, InteractType.INTERACT_PICK_ITEM));
if (!drop.isShare()) // not shared drop
this.sendPacket(new PacketGadgetInteractRsp(drop, InteractType.INTERACT_PICK_ITEM));
else
this.getScene().broadcastPacket(new PacketGadgetInteractRsp(drop, InteractType.INTERACT_PICK_ITEM));
} }
} else {
// Delete directly
entity.getScene().removeEntity(entity);
} }
return; return;
......
package emu.grasscutter.game.world; package emu.grasscutter.game.world;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.danilopianini.util.SpatialIndex;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameDepot; import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.def.MonsterData; import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.data.def.SceneData; import emu.grasscutter.data.def.SceneData;
import emu.grasscutter.data.def.WorldLevelData; import emu.grasscutter.data.def.WorldLevelData;
import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.entity.EntityClientGadget;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.player.TeamInfo; import emu.grasscutter.game.player.TeamInfo;
import emu.grasscutter.game.props.ClimateType; import emu.grasscutter.game.props.ClimateType;
...@@ -37,10 +21,12 @@ import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; ...@@ -37,10 +21,12 @@ import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify; import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.danilopianini.util.SpatialIndex;
import java.util.*;
public class Scene { public class Scene {
private final World world; private final World world;
...@@ -228,6 +214,11 @@ public class Scene { ...@@ -228,6 +214,11 @@ public class Scene {
this.addEntityDirectly(entity); this.addEntityDirectly(entity);
this.broadcastPacket(new PacketSceneEntityAppearNotify(entity)); this.broadcastPacket(new PacketSceneEntityAppearNotify(entity));
} }
public synchronized void addEntityToSingleClient(Player player, GameEntity entity) {
this.addEntityDirectly(entity);
player.sendPacket(new PacketSceneEntityAppearNotify(entity));
}
public synchronized void addEntities(Collection<GameEntity> entities) { public synchronized void addEntities(Collection<GameEntity> entities) {
for (GameEntity entity : entities) { for (GameEntity entity : entities) {
...@@ -310,6 +301,12 @@ public class Scene { ...@@ -310,6 +301,12 @@ public class Scene {
public void killEntity(GameEntity target, int attackerId) { public void killEntity(GameEntity target, int attackerId) {
// Packet // Packet
this.broadcastPacket(new PacketLifeStateChangeNotify(attackerId, target, LifeState.LIFE_DEAD)); this.broadcastPacket(new PacketLifeStateChangeNotify(attackerId, target, LifeState.LIFE_DEAD));
// Reward drop
if (target instanceof EntityMonster) {
Grasscutter.getGameServer().getDropManager().callDrop((EntityMonster) target);
}
this.removeEntity(target); this.removeEntity(target);
// Death event // Death event
......
package emu.grasscutter.server.game; package emu.grasscutter.server.game;
import java.net.InetSocketAddress;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import emu.grasscutter.GameConstants; import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.CommandMap; import emu.grasscutter.command.CommandMap;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.game.drop.DropManager;
import emu.grasscutter.game.dungeons.DungeonManager; import emu.grasscutter.game.dungeons.DungeonManager;
import emu.grasscutter.game.gacha.GachaManager; import emu.grasscutter.game.gacha.GachaManager;
import emu.grasscutter.game.managers.ChatManager; import emu.grasscutter.game.managers.ChatManager;
...@@ -27,6 +23,11 @@ import emu.grasscutter.server.event.internal.ServerStartEvent; ...@@ -27,6 +23,11 @@ import emu.grasscutter.server.event.internal.ServerStartEvent;
import emu.grasscutter.server.event.internal.ServerStopEvent; import emu.grasscutter.server.event.internal.ServerStopEvent;
import emu.grasscutter.task.TaskMap; import emu.grasscutter.task.TaskMap;
import java.net.InetSocketAddress;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public final class GameServer extends KcpServer { public final class GameServer extends KcpServer {
private final InetSocketAddress address; private final InetSocketAddress address;
private final GameServerPacketHandler packetHandler; private final GameServerPacketHandler packetHandler;
...@@ -42,6 +43,7 @@ public final class GameServer extends KcpServer { ...@@ -42,6 +43,7 @@ public final class GameServer extends KcpServer {
private final DungeonManager dungeonManager; private final DungeonManager dungeonManager;
private final CommandMap commandMap; private final CommandMap commandMap;
private final TaskMap taskMap; private final TaskMap taskMap;
private final DropManager dropManager;
public GameServer(InetSocketAddress address) { public GameServer(InetSocketAddress address) {
super(address); super(address);
...@@ -60,6 +62,7 @@ public final class GameServer extends KcpServer { ...@@ -60,6 +62,7 @@ public final class GameServer extends KcpServer {
this.dungeonManager = new DungeonManager(this); this.dungeonManager = new DungeonManager(this);
this.commandMap = new CommandMap(true); this.commandMap = new CommandMap(true);
this.taskMap = new TaskMap(true); this.taskMap = new TaskMap(true);
this.dropManager = new DropManager(this);
// Schedule game loop. // Schedule game loop.
Timer gameLoop = new Timer(); Timer gameLoop = new Timer();
...@@ -109,6 +112,10 @@ public final class GameServer extends KcpServer { ...@@ -109,6 +112,10 @@ public final class GameServer extends KcpServer {
public MultiplayerManager getMultiplayerManager() { public MultiplayerManager getMultiplayerManager() {
return multiplayerManager; return multiplayerManager;
} }
public DropManager getDropManager() {
return dropManager;
}
public DungeonManager getDungeonManager() { public DungeonManager getDungeonManager() {
return dungeonManager; return dungeonManager;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment