implement simple drop system

This commit is contained in:
Kengxxiao 2022-04-27 17:42:02 +08:00
parent 9fc8cc1c17
commit 8b614d8df8
8 changed files with 254 additions and 38 deletions

14
data/Drop.json Normal file
View File

@ -0,0 +1,14 @@
[
{
"monsterId": 20010201,
"dropDataList": [
{
"itemId": 223,
"minCount": 1,
"maxCount": 100,
"minWeight": 0,
"maxWeight": 10000
}
]
}
]

View File

@ -0,0 +1,57 @@
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;
}

View File

@ -0,0 +1,16 @@
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;
}

View File

@ -0,0 +1,99 @@
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);
}
}
}
}
}
}

View File

@ -6,7 +6,6 @@ import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.World;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
@ -30,14 +29,30 @@ public class EntityItem extends EntityGadget {
private final GameItem item;
private final long guid;
private final boolean share;
public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count) {
super(scene);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.pos = new Position(pos);
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.share = share;
}
@Override
@ -81,6 +96,10 @@ public class EntityItem extends EntityGadget {
return null;
}
public boolean isShare() {
return share;
}
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()

View File

@ -1,20 +1,16 @@
package emu.grasscutter.game.player;
import java.time.Instant;
import java.util.*;
import dev.morphia.annotations.*;
import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.PlayerLevelData;
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.CoopRequest;
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.GameEntity;
import emu.grasscutter.game.friends.FriendsList;
@ -34,7 +30,6 @@ import emu.grasscutter.net.proto.HeadImageOuterClass.HeadImage;
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
import emu.grasscutter.net.proto.MpSettingTypeOuterClass.MpSettingType;
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.PlayerLocationInfoOuterClass.PlayerLocationInfo;
import emu.grasscutter.net.proto.PlayerWorldLocationInfoOuterClass;
@ -42,11 +37,12 @@ import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.DateHelper;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.time.Instant;
import java.util.*;
@Entity(value = "players", useDiscriminator = false)
@ -714,19 +710,30 @@ public class Player {
return;
}
// Delete
entity.getScene().removeEntity(entity);
// Handle
if (entity instanceof EntityItem) {
// Pick item
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());
// Add to inventory
boolean success = getInventory().addItem(item, ActionReason.SubfieldDrop);
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;

View File

@ -1,28 +1,12 @@
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.data.GameData;
import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.data.def.SceneData;
import emu.grasscutter.data.def.WorldLevelData;
import emu.grasscutter.game.entity.EntityAvatar;
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.entity.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.player.TeamInfo;
import emu.grasscutter.game.props.ClimateType;
@ -37,10 +21,12 @@ import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
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.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.danilopianini.util.SpatialIndex;
import java.util.*;
public class Scene {
private final World world;
@ -228,6 +214,11 @@ public class Scene {
this.addEntityDirectly(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) {
for (GameEntity entity : entities) {
@ -310,6 +301,12 @@ public class Scene {
public void killEntity(GameEntity target, int attackerId) {
// Packet
this.broadcastPacket(new PacketLifeStateChangeNotify(attackerId, target, LifeState.LIFE_DEAD));
// Reward drop
if (target instanceof EntityMonster) {
Grasscutter.getGameServer().getDropManager().callDrop((EntityMonster) target);
}
this.removeEntity(target);
// Death event

View File

@ -1,15 +1,11 @@
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.Grasscutter;
import emu.grasscutter.command.CommandMap;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.drop.DropManager;
import emu.grasscutter.game.dungeons.DungeonManager;
import emu.grasscutter.game.gacha.GachaManager;
import emu.grasscutter.game.managers.ChatManager;
@ -27,6 +23,11 @@ import emu.grasscutter.server.event.internal.ServerStartEvent;
import emu.grasscutter.server.event.internal.ServerStopEvent;
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 {
private final InetSocketAddress address;
private final GameServerPacketHandler packetHandler;
@ -42,6 +43,7 @@ public final class GameServer extends KcpServer {
private final DungeonManager dungeonManager;
private final CommandMap commandMap;
private final TaskMap taskMap;
private final DropManager dropManager;
public GameServer(InetSocketAddress address) {
super(address);
@ -60,6 +62,7 @@ public final class GameServer extends KcpServer {
this.dungeonManager = new DungeonManager(this);
this.commandMap = new CommandMap(true);
this.taskMap = new TaskMap(true);
this.dropManager = new DropManager(this);
// Schedule game loop.
Timer gameLoop = new Timer();
@ -109,6 +112,10 @@ public final class GameServer extends KcpServer {
public MultiplayerManager getMultiplayerManager() {
return multiplayerManager;
}
public DropManager getDropManager() {
return dropManager;
}
public DungeonManager getDungeonManager() {
return dungeonManager;