Commit 2462da2e authored by zhaodice's avatar zhaodice Committed by GitHub
Browse files

Big World Resources Collection Implement (#1368)



* init

* init

* init

* revoke

* fix error

* mining support

* mining support

* Roks endurance support

* Roks endurance support

* Timed refresh

* upgrade resource data

* Timed refresh support

* remove null gadget

* Coordination

* full synchronized

* oh no, my math teacher will hit me!

* synchronized onInteract

* remove break;

* supply re-spawn time , thanks to @wl23333

* Clean up and integrate collection spawns into SpawnDataEntries
Co-authored-by: default avatarMelledy <52122272+Melledy@users.noreply.github.com>
parent 17fb19eb
package emu.grasscutter.data;
import java.io.*;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
......@@ -27,6 +28,7 @@ import emu.grasscutter.data.common.PointData;
import emu.grasscutter.data.common.ScenePointConfig;
import emu.grasscutter.game.world.SpawnDataEntry.*;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import static emu.grasscutter.Configuration.*;
import static emu.grasscutter.utils.Language.translate;
......@@ -306,19 +308,32 @@ public class ResourceLoader {
}
private static void loadSpawnData() {
List<SpawnGroupEntry> spawnEntryList = null;
// Read from cached file if exists
try(InputStream spawnDataEntries = DataLoader.load("Spawns.json")) {
spawnEntryList = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(spawnDataEntries), TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType());
} catch (Exception ignored) {}
if (spawnEntryList == null || spawnEntryList.isEmpty()) {
String[] spawnDataNames = {"Spawns.json", "GadgetSpawns.json"};
Int2ObjectMap<SpawnGroupEntry> spawnEntryMap = new Int2ObjectOpenHashMap<>();
for (String name : spawnDataNames) {
// Load spawn entries from file
try (InputStream spawnDataEntries = DataLoader.load(name)) {
Type type = TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType();
List<SpawnGroupEntry> list = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(spawnDataEntries), type);
// Add spawns to group if it already exists in our spawn group map
for (SpawnGroupEntry group : list) {
if (spawnEntryMap.containsKey(group.getGroupId())) {
spawnEntryMap.get(group.getGroupId()).getSpawns().addAll(group.getSpawns());
} else {
spawnEntryMap.put(group.getGroupId(), group);
}
}
} catch (Exception ignored) {}
}
if (spawnEntryMap.isEmpty()) {
Grasscutter.getLogger().error("No spawn data loaded!");
return;
}
for (SpawnGroupEntry entry : spawnEntryList) {
for (SpawnGroupEntry entry : spawnEntryMap.values()) {
entry.getSpawns().forEach(s -> s.setGroup(entry));
GameDepot.getSpawnListById(entry.getSceneId()).insert(entry, entry.getPos().getX(), entry.getPos().getZ());
}
......
......@@ -16,8 +16,11 @@ import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.EntityClientGadget;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.entity.gadget.GadgetGatherObject;
import emu.grasscutter.game.entity.gadget.GadgetGatherPoint;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall;
......@@ -98,18 +101,28 @@ public class AbilityManager {
}
private void handleModifierChange(AbilityInvokeEntry invoke) throws Exception {
// Sanity checks
GameEntity target = player.getScene().getEntityById(invoke.getEntityId());
if (target == null) {
return;
}
AbilityInvokeEntryHead head = invoke.getHead();
if (head == null) {
AbilityMetaModifierChange data = AbilityMetaModifierChange.parseFrom(invoke.getAbilityData());
if (data == null) {
return;
}
AbilityMetaModifierChange data = AbilityMetaModifierChange.parseFrom(invoke.getAbilityData());
if (data == null) {
// Destroying rocks
if (target instanceof EntityGadget targetGadget && targetGadget.getContent() instanceof GadgetGatherObject gatherObject) {
if (data.getAction() == ModifierAction.REMOVED) {
gatherObject.dropItems(this.getPlayer());
return;
}
}
// Sanity checks
AbilityInvokeEntryHead head = invoke.getHead();
if (head == null) {
return;
}
......@@ -117,7 +130,7 @@ public class AbilityManager {
if (sourceEntity == null) {
return;
}
// This is not how it works but we will keep it for now since healing abilities dont work properly anyways
if (data.getAction() == ModifierAction.ADDED && data.getParentAbilityName() != null) {
// Handle add modifier here
......@@ -133,6 +146,7 @@ public class AbilityManager {
// Add to meta modifier list
target.getMetaModifiers().put(head.getInstancedModifierId(), modifierString);
} else if (data.getAction() == ModifierAction.REMOVED) {
// Handle remove modifier
String modifierString = target.getMetaModifiers().get(head.getInstancedModifierId());
if (modifierString != null) {
......
......@@ -8,11 +8,13 @@ import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.SpawnDataEntry;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType;
......@@ -27,6 +29,7 @@ import emu.grasscutter.server.packet.send.PacketGadgetStateNotify;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import lombok.ToString;
......@@ -40,19 +43,24 @@ public class EntityGadget extends EntityBaseGadget {
private int state;
private int pointType;
private GadgetContent content;
private Int2FloatOpenHashMap fightProp;
private SceneGadget metaGadget;
public EntityGadget(Scene scene, int gadgetId, Position pos) {
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot) {
super(scene);
this.data = GameData.getGadgetDataMap().get(gadgetId);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.gadgetId = gadgetId;
this.pos = pos.clone();
this.rot = new Position();
this.rot = rot != null ? rot.clone() : new Position();
}
public EntityGadget(Scene scene, int gadgetId, Position pos, GadgetContent content) {
this(scene, gadgetId, pos);
public EntityGadget(Scene scene, int gadgetId, Position pos) {
this(scene, gadgetId, pos, new Position());
}
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) {
this(scene, gadgetId, pos, rot);
this.content = content;
}
......@@ -126,6 +134,7 @@ public class EntityGadget extends EntityBaseGadget {
EntityType type = getGadgetData().getType();
GadgetContent content = switch (type) {
case GatherPoint -> new GadgetGatherPoint(this);
case GatherObject -> new GadgetGatherObject(this);
case Worktop -> new GadgetWorktop(this);
case RewardStatue -> new GadgetRewardStatue(this);
case Chest -> new GadgetChest(this);
......@@ -137,7 +146,8 @@ public class EntityGadget extends EntityBaseGadget {
@Override
public Int2FloatOpenHashMap getFightProperties() {
return null;
if (this.fightProp == null) this.fightProp = new Int2FloatOpenHashMap();
return this.fightProp;
}
@Override
......@@ -148,7 +158,10 @@ public class EntityGadget extends EntityBaseGadget {
@Override
public void onDeath(int killerId) {
if(getScene().getChallenge() != null){
if (this.getSpawnEntry() != null) {
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
}
if (getScene().getChallenge() != null) {
getScene().getChallenge().onGadgetDeath(this);
}
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_GADGET_DIE, new ScriptArgs(this.getConfigId()));
......@@ -178,6 +191,11 @@ public class EntityGadget extends EntityBaseGadget {
.build();
entityInfo.addPropList(pair);
// We do not use the getter to null check because the getter will create a fight prop map if it is null
if (this.fightProp != null) {
this.addAllFightPropsToEntityInfo(entityInfo);
}
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
.setGroupId(this.getGroupId())
......
......@@ -8,12 +8,14 @@ import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.SpawnDataEntry;
import emu.grasscutter.game.world.World;
import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
......@@ -124,6 +126,16 @@ public abstract class GameEntity {
return getFightProperties().getOrDefault(prop.getId(), 0f);
}
public void addAllFightPropsToEntityInfo(SceneEntityInfo.Builder entityInfo) {
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
if (entry.getIntKey() == 0) {
continue;
}
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
entityInfo.addFightPropList(fightProp);
}
}
public int getBlockId() {
return blockId;
}
......
......@@ -2,13 +2,12 @@ package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
public abstract class GadgetContent {
private final EntityGadget gadget;
public GadgetContent(EntityGadget gadget) {
this.gadget = gadget;
}
......
package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils;
public class GadgetGatherObject extends GadgetContent {
private int itemId;
private boolean isForbidGuest;
public GadgetGatherObject(EntityGadget gadget) {
super(gadget);
if (gadget.getSpawnEntry() != null) {
this.itemId = gadget.getSpawnEntry().getGatherItemId();
}
}
public int getItemId() {
return this.itemId;
}
public boolean isForbidGuest() {
return isForbidGuest;
}
public boolean onInteract(Player player, GadgetInteractReq req) {
// Sanity check
ItemData itemData = GameData.getItemDataMap().get(getItemId());
if (itemData == null) {
return false;
}
GameItem item = new GameItem(itemData, 1);
player.getInventory().addItem(item, ActionReason.Gather);
getGadget().getScene().broadcastPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_GATHER));
return true;
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
GatherGadgetInfo gatherGadgetInfo = GatherGadgetInfo.newBuilder()
.setItemId(this.getItemId())
.setIsForbidGuest(this.isForbidGuest())
.build();
gadgetInfo.setGatherGadget(gatherGadgetInfo);
}
public void dropItems(Player player) {
Scene scene = getGadget().getScene();
int times = Utils.randomRange(1,2);
for (int i = 0 ; i < times ; i++) {
EntityItem item = new EntityItem(
scene,
player,
GameData.getItemDataMap().get(itemId),
new Position(
getGadget().getPosition().getX() + (float)Utils.randomRange(1,5) / 5,
getGadget().getPosition().getY() + 2f,
getGadget().getPosition().getZ() + (float)Utils.randomRange(1,5) / 5
),
1,
true);
scene.addEntity(item);
}
scene.killEntity(this.getGadget(), player.getTeamManager().getCurrentAvatarEntity().getId());
// Todo: add record
}
}
......@@ -3,31 +3,43 @@ package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.GatherData;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils;
public class GadgetGatherPoint extends GadgetContent {
private GatherData gatherData;
private int itemId;
private boolean isForbidGuest;
public GadgetGatherPoint(EntityGadget gadget) {
super(gadget);
this.gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
}
public GatherData getGatherData() {
return gatherData;
if (gadget.getSpawnEntry() != null) {
this.itemId = gadget.getSpawnEntry().getGatherItemId();
} else {
GatherData gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
this.itemId = gatherData.getItemId();
this.isForbidGuest = gatherData.isForbidGuest();
}
}
public int getItemId() {
return getGatherData().getItemId();
return this.itemId;
}
public boolean isForbidGuest() {
return isForbidGuest;
}
public boolean onInteract(Player player, GadgetInteractReq req) {
GameItem item = new GameItem(gatherData.getItemId(), 1);
GameItem item = new GameItem(getItemId(), 1);
player.getInventory().addItem(item, ActionReason.Gather);
......@@ -37,9 +49,33 @@ public class GadgetGatherPoint extends GadgetContent {
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
GatherGadgetInfo gatherGadgetInfo = GatherGadgetInfo.newBuilder()
.setItemId(this.getItemId())
.setIsForbidGuest(this.getGatherData().isForbidGuest())
.setIsForbidGuest(this.isForbidGuest())
.build();
gadgetInfo.setGatherGadget(gatherGadgetInfo);
}
public void dropItems(Player player) {
Scene scene = getGadget().getScene();
int times = Utils.randomRange(1,2);
for (int i = 0 ; i < times ; i++) {
EntityItem item = new EntityItem(
scene,
player,
GameData.getItemDataMap().get(itemId),
new Position(
getGadget().getPosition().getX() + (float)Utils.randomRange(1,5) / 5,
getGadget().getPosition().getY() + 2f,
getGadget().getPosition().getZ() + (float)Utils.randomRange(1,5) / 5
),
1,
true);
scene.addEntity(item);
}
scene.killEntity(this.getGadget(), player.getTeamManager().getCurrentAvatarEntity().getId());
// Todo: add record
}
}
package emu.grasscutter.game.managers.collection;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.utils.Position;
public class CollectionData {
Gadget gadget;
MotionInfo motionInfo;
Prop[] fightPropList;
static class GatherGadget{
int itemId;
}
static class Gadget{
int gadgetId;
int authorityPeerId;
int configId;
int groupId;
boolean isEnableInteract;
GatherGadget gatherGadget;
}
static class MotionInfo{
Position pos;
Position rot;
}
static class Prop{
int propType;
float propValue;
}
}
package emu.grasscutter.game.managers.collection;
import java.util.HashMap;
import java.util.List;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player;
public class CollectionManager {
private static final long SECOND = 1000; //1 Second
private static final long MINUTE = SECOND*60; //1 Minute
private static final long HOUR = MINUTE*60; //1 Hour
private static final long DAY = HOUR*24; //1 Day
private static final HashMap<Integer,Long> DEFINE_REFRESH_TIME = new HashMap<>();// <GadgetId,Waiting Millisecond>
private static final long DEFAULT_REFRESH_TIME = HOUR*6; // default 6 Hours
static {
DEFINE_REFRESH_TIME.put(70590027,3*DAY);//星银矿石 3 Days
DEFINE_REFRESH_TIME.put(70590036,3*DAY);//紫晶块 3 Days
DEFINE_REFRESH_TIME.put(70520003,3*DAY);//水晶 3 Days
DEFINE_REFRESH_TIME.put(70590013,2*DAY);//嘟嘟莲 2 Days
DEFINE_REFRESH_TIME.put(70540029,2*DAY);//清心 2 Days
DEFINE_REFRESH_TIME.put(70540028,2*DAY);//星螺 2 Days
DEFINE_REFRESH_TIME.put(70540027,2*DAY);//马尾 2 Days
DEFINE_REFRESH_TIME.put(70540026,2*DAY);//琉璃袋 2 Days
DEFINE_REFRESH_TIME.put(70540022,2*DAY);//落落莓 2 Days
DEFINE_REFRESH_TIME.put(70540020,2*DAY);//慕风蘑菇 2 Days
DEFINE_REFRESH_TIME.put(70540019,2*DAY);//风车菊 2 Days
DEFINE_REFRESH_TIME.put(70540018,2*DAY);//塞西莉亚花 2 Days
DEFINE_REFRESH_TIME.put(70540015,2*DAY);//霓裳花 2 Days
DEFINE_REFRESH_TIME.put(70540014,2*DAY);//莲蓬 2 Days
DEFINE_REFRESH_TIME.put(70540013,2*DAY);//钩钩果 2 Days
DEFINE_REFRESH_TIME.put(70540012,2*DAY);//琉璃百合 2 Days
DEFINE_REFRESH_TIME.put(70540008,2*DAY);//绝云椒椒 2 Days
DEFINE_REFRESH_TIME.put(70520018,2*DAY);//夜泊石 2 Days
DEFINE_REFRESH_TIME.put(70520002,2*DAY);//白铁矿 2 Days
DEFINE_REFRESH_TIME.put(70510012,2*DAY);//石珀 2 Days
DEFINE_REFRESH_TIME.put(70510009,2*DAY);//蒲公英 2 Days
DEFINE_REFRESH_TIME.put(70510007,2*DAY);//冰雾花 2 Days
DEFINE_REFRESH_TIME.put(70510006,2*DAY);//烈焰花 2 Days
DEFINE_REFRESH_TIME.put(70510005,2*DAY);//电气水晶 2 Days
DEFINE_REFRESH_TIME.put(70510004,2*DAY);//小灯草 2 Days
DEFINE_REFRESH_TIME.put(70540021,DAY);//日落果 1 Day
DEFINE_REFRESH_TIME.put(70540005,DAY);//松果 1 Day
DEFINE_REFRESH_TIME.put(70540003,DAY);//苹果 1 Day
DEFINE_REFRESH_TIME.put(70540001,DAY);//树莓 1 Day
DEFINE_REFRESH_TIME.put(70520019,DAY);//魔晶块 1 Days
DEFINE_REFRESH_TIME.put(70520008,DAY);//金鱼草 1 Days
DEFINE_REFRESH_TIME.put(70520007,DAY);//白萝卜 1 Days
DEFINE_REFRESH_TIME.put(70520006,DAY);//胡萝卜 1 Days
DEFINE_REFRESH_TIME.put(70520004,DAY);//蘑菇 1 Day
DEFINE_REFRESH_TIME.put(70520001,DAY);//铁矿 1 Day
DEFINE_REFRESH_TIME.put(70520009,12*HOUR);//薄荷 12 Hours
DEFINE_REFRESH_TIME.put(70520005,12*HOUR);//甜甜花 12 Hours
}
private final static HashMap<Integer, List<CollectionData>> CollectionResourcesData = new HashMap<>();
private final HashMap<CollectionData,EntityGadget> spawnedEntities = new HashMap<>();
private CollectionRecordStore collectionRecordStore;
Player player;
private static long getGadgetRefreshTime(int gadgetId){
return DEFINE_REFRESH_TIME.getOrDefault(gadgetId,DEFAULT_REFRESH_TIME);
}
public synchronized void setPlayer(Player player) {
this.player = player;
this.collectionRecordStore = player.getCollectionRecordStore();
}
}
package emu.grasscutter.game.managers.collection;
import java.util.HashMap;
import java.util.Map;
import dev.morphia.annotations.Entity;
@Entity
public class CollectionRecordStore {
private Map<Integer, CollectionRecord> records;
private Map<Integer, CollectionRecord> getRecords() {
if (records == null) {
records = new HashMap<>();
}
return records;
}
public void addRecord(int configId, long expiredMillisecond){
Map<Integer, CollectionRecord> records;
synchronized (records = getRecords()) {
records.put(configId, new CollectionRecord(configId, expiredMillisecond + System.currentTimeMillis()));
}
}
public boolean findRecord(int configId) {
Map<Integer, CollectionRecord> records;
synchronized (records = getRecords()) {
CollectionRecord record = records.get(configId);
if (record == null) {
return false;
}
boolean expired = record.getExpiredTime() < System.currentTimeMillis();
if (expired) {
records.remove(configId);
return false;
}
return true;
}
}
@Entity
public static class CollectionRecord {
private int configId;
private long expiredTime;
@Deprecated // Morphia
public CollectionRecord() {}
public CollectionRecord(int configId, long expiredTime) {
this.configId = configId;
this.expiredTime = expiredTime;
}
public int getConfigId() {
return configId;
}
public long getExpiredTime() {
return expiredTime;
}
}
}
......@@ -32,6 +32,8 @@ import emu.grasscutter.game.mail.MailHandler;
import emu.grasscutter.game.managers.FurnitureManager;
import emu.grasscutter.game.managers.InsectCaptureManager;
import emu.grasscutter.game.managers.ResinManager;
import emu.grasscutter.game.managers.collection.CollectionManager;
import emu.grasscutter.game.managers.collection.CollectionRecordStore;
import emu.grasscutter.game.managers.deforestation.DeforestationManager;
import emu.grasscutter.game.managers.energy.EnergyManager;
import emu.grasscutter.game.managers.forging.ActiveForgeData;
......@@ -42,7 +44,6 @@ import emu.grasscutter.game.managers.SotSManager;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.ClimateType;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.quest.QuestManager;
import emu.grasscutter.game.shop.ShopLimit;
......@@ -62,7 +63,7 @@ import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo;
import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo;
import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture;
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
import emu.grasscutter.server.event.player.PlayerJoinEvent;
import emu.grasscutter.server.event.player.PlayerQuitEvent;
import emu.grasscutter.server.game.GameServer;
......@@ -183,6 +184,9 @@ public class Player {
@Transient private FurnitureManager furnitureManager;
@Transient private BattlePassManager battlePassManager;
@Transient private CollectionManager collectionManager;
private CollectionRecordStore collectionRecordStore;
private long springLastUsed;
private HashMap<String, MapMark> mapMarks;
private int nextResinRefresh;
......@@ -216,6 +220,7 @@ public class Player {
this.flyCloakList = new HashSet<>();
this.costumeList = new HashSet<>();
this.towerData = new TowerData();
this.collectionRecordStore = new CollectionRecordStore();
this.unlockedForgingBlueprints = new HashSet<>();
this.unlockedCombines = new HashSet<>();
this.unlockedFurniture = new HashSet<>();
......@@ -1098,7 +1103,6 @@ public class Player {
}
}
} else if (entity instanceof EntityGadget gadget) {
if (gadget.getContent() == null) {
return;
}
......@@ -1106,7 +1110,7 @@ public class Player {
boolean shouldDelete = gadget.getContent().onInteract(this, opType);
if (shouldDelete) {
entity.getScene().removeEntity(entity);
entity.getScene().removeEntity(entity, VisionType.VISION_TYPE_REMOVE);
}
} else if (entity instanceof EntityMonster monster) {
insectCaptureManager.arrestSmallCreature(monster);
......@@ -1306,6 +1310,20 @@ public class Player {
return deforestationManager;
}
public CollectionManager getCollectionManager() {
if(this.collectionManager==null){
this.collectionManager = new CollectionManager();
}
return this.collectionManager;
}
public CollectionRecordStore getCollectionRecordStore() {
if(this.collectionRecordStore==null){
this.collectionRecordStore = new CollectionRecordStore();
}
return collectionRecordStore;
}
public HashMap<String, MapMark> getMapMarks() { return mapMarks; }
public void setMapMarks(HashMap<String, MapMark> newMarks) { mapMarks = newMarks; }
......@@ -1432,6 +1450,7 @@ public class Player {
}
//Make sure towerManager's player is online player
this.getTowerManager().setPlayer(this);
this.getCollectionManager().setPlayer(this);
// Load from db
this.getAvatars().loadFromDatabase();
......@@ -1485,7 +1504,6 @@ public class Player {
session.send(new PacketCombineDataNotify(this.unlockedCombines));
this.forgingManager.sendForgeDataNotify();
this.resinManager.onPlayerLogin();
getTodayMoonCard(); // The timer works at 0:0, some users log in after that, use this method to check if they have received a reward today or not. If not, send the reward.
// Battle Pass trigger
......
......@@ -217,7 +217,7 @@ public class Scene {
getPlayers().add(player);
player.setSceneId(this.getId());
player.setScene(this);
this.setupPlayerAvatars(player);
}
......@@ -428,13 +428,22 @@ public class Scene {
}
}
public int getEntityLevel(int baseLevel, int worldLevelOverride) {
int level = worldLevelOverride > 0 ? worldLevelOverride + baseLevel - 22 : baseLevel;
level = level >= 100 ? 100 : level;
level = level <= 0 ? 1 : level;
return level;
}
// TODO - Test
public void checkSpawns() {
public synchronized void checkSpawns() {
SpatialIndex<SpawnGroupEntry> list = GameDepot.getSpawnListById(this.getId());
Set<SpawnDataEntry> visible = new HashSet<>();
for (Player player : this.getPlayers()) {
int RANGE = 100;
Collection<SpawnGroupEntry> entries = list.query(
new double[] {player.getPos().getX() - RANGE, player.getPos().getZ() - RANGE},
new double[] {player.getPos().getX() + RANGE, player.getPos().getZ() + RANGE}
......@@ -460,29 +469,45 @@ public class Scene {
List<GameEntity> toRemove = new LinkedList<>();
for (SpawnDataEntry entry : visible) {
// If spawn entry is in our view and hasnt been spawned/killed yet, we should spawn it
if (!this.getSpawnedEntities().contains(entry) && !this.getDeadSpawnedEntities().contains(entry)) {
// Spawn entity
MonsterData data = GameData.getMonsterDataMap().get(entry.getMonsterId());
// Entity object holder
GameEntity entity = null;
if (data == null) {
continue;
// Check if spawn entry is monster or gadget
if (entry.getMonsterId() > 0) {
MonsterData data = GameData.getMonsterDataMap().get(entry.getMonsterId());
if (data == null) continue;
int level = this.getEntityLevel(entry.getLevel(), worldLevelOverride);
EntityMonster monster = new EntityMonster(this, data, entry.getPos(), level);
monster.getRotation().set(entry.getRot());
monster.setGroupId(entry.getGroup().getGroupId());
monster.setPoseId(entry.getPoseId());
monster.setConfigId(entry.getConfigId());
monster.setSpawnEntry(entry);
entity = monster;
} else if (entry.getGadgetId() > 0) {
EntityGadget gadget = new EntityGadget(this, entry.getGadgetId(), entry.getPos(), entry.getRot());
gadget.setGroupId(entry.getGroup().getGroupId());
gadget.setConfigId(entry.getConfigId());
gadget.setSpawnEntry(entry);
gadget.buildContent();
gadget.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, 99999);
gadget.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 99999);
gadget.setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, 99999);
entity = gadget;
}
int level = worldLevelOverride > 0 ? worldLevelOverride + entry.getLevel() - 22 : entry.getLevel();
level = level >= 100 ? 100 : level;
level = level <= 0 ? 1 : level;
EntityMonster entity = new EntityMonster(this, data, entry.getPos(), level);
entity.getRotation().set(entry.getRot());
entity.setGroupId(entry.getGroup().getGroupId());
entity.setPoseId(entry.getPoseId());
entity.setConfigId(entry.getConfigId());
entity.setSpawnEntry(entry);
if (entity == null) continue;
// Add to scene and spawned list
toAdd.add(entity);
// Add to spawned list
this.getSpawnedEntities().add(entry);
getSpawnedEntities().add(entry);
}
}
......
package emu.grasscutter.game.world;
import java.util.ArrayList;
import java.util.List;
import emu.grasscutter.utils.Position;
......@@ -8,9 +7,11 @@ import emu.grasscutter.utils.Position;
public class SpawnDataEntry {
private transient SpawnGroupEntry group;
private int monsterId;
private int gadgetId;
private int configId;
private int level;
private int poseId;
private int gatherItemId;
private Position pos;
private Position rot;
......@@ -26,6 +27,10 @@ public class SpawnDataEntry {
return monsterId;
}
public int getGadgetId() {
return gadgetId;
}
public int getConfigId() {
return configId;
}
......@@ -38,6 +43,10 @@ public class SpawnDataEntry {
return poseId;
}
public int getGatherItemId() {
return gatherItemId;
}
public Position getPos() {
return pos;
}
......
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.entity.EntityBaseGadget;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.AbilityInvocationsNotifyOuterClass.AbilityInvocationsNotify;
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass;
import emu.grasscutter.net.proto.VisionTypeOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
@Opcodes(PacketOpcodes.AbilityInvocationsNotify)
public class HandlerAbilityInvocationsNotify extends PacketHandler {
......@@ -14,10 +23,11 @@ public class HandlerAbilityInvocationsNotify extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
AbilityInvocationsNotify notif = AbilityInvocationsNotify.parseFrom(payload);
Player player = session.getPlayer();
for (AbilityInvokeEntry entry : notif.getInvokesList()) {
session.getPlayer().getAbilityManager().onAbilityInvoke(entry);
session.getPlayer().getAbilityInvokeHandler().addEntry(entry.getForwardType(), entry);
player.getAbilityManager().onAbilityInvoke(entry);
player.getAbilityInvokeHandler().addEntry(entry.getForwardType(), entry);
}
}
......
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
......@@ -14,10 +15,11 @@ public class HandlerClientAbilityInitFinishNotify extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
ClientAbilityInitFinishNotify notif = ClientAbilityInitFinishNotify.parseFrom(payload);
Player player = session.getPlayer();
for (AbilityInvokeEntry entry : notif.getInvokesList()) {
session.getPlayer().getAbilityManager().onAbilityInvoke(entry);
session.getPlayer().getClientAbilityInitFinishHandler().addEntry(entry.getForwardType(), entry);
player.getAbilityManager().onAbilityInvoke(entry);
player.getClientAbilityInitFinishHandler().addEntry(entry.getForwardType(), entry);
}
if (notif.getInvokesList().size() > 0) {
......
......@@ -2,9 +2,12 @@ package emu.grasscutter.server.packet.recv;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.AttackResultOuterClass;
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify;
import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo;
......@@ -32,10 +35,13 @@ public class HandlerCombatInvocationsNotify extends PacketHandler {
for (CombatInvokeEntry entry : notif.getInvokeListList()) {
switch (entry.getArgumentType()) {
case COMBAT_TYPE_ARGUMENT_EVT_BEING_HIT:
// Handle damage
EvtBeingHitInfo hitInfo = EvtBeingHitInfo.parseFrom(entry.getCombatData());
session.getPlayer().getAttackResults().add(hitInfo.getAttackResult());
session.getPlayer().getEnergyManager().handleAttackHit(hitInfo);
AttackResult attackResult = hitInfo.getAttackResult();
Player player = session.getPlayer();
// Handle damage
player.getAttackResults().add(attackResult);
player.getEnergyManager().handleAttackHit(hitInfo);
break;
case COMBAT_TYPE_ARGUMENT_ENTITY_MOVE:
// Handle movement
......
......@@ -5,6 +5,7 @@ import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.GadgetInteractRspOuterClass.GadgetInteractRsp;
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
import emu.grasscutter.net.proto.InterOpTypeOuterClass.InterOpType;
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
import emu.grasscutter.net.proto.RetcodeOuterClass;
......@@ -12,7 +13,8 @@ public class PacketGadgetInteractRsp extends BasePacket {
public PacketGadgetInteractRsp(EntityBaseGadget gadget, InteractType interact) {
this(gadget, interact, null);
}
public PacketGadgetInteractRsp(EntityBaseGadget gadget, InteractType interact, InterOpTypeOuterClass.InterOpType opType) {
public PacketGadgetInteractRsp(EntityBaseGadget gadget, InteractType interact, InterOpType opType) {
super(PacketOpcodes.GadgetInteractRsp);
var proto = GadgetInteractRsp.newBuilder()
......@@ -20,7 +22,7 @@ public class PacketGadgetInteractRsp extends BasePacket {
.setInteractType(interact)
.setGadgetId(gadget.getGadgetId());
if(opType != null){
if (opType != null) {
proto.setOpType(opType);
}
......
......@@ -135,9 +135,20 @@ public class Position implements Serializable {
}
public boolean equal2d(Position other) {
return getX() == other.getX() && getY() == other.getY();
// Y is height
return getX() == other.getX() && getZ() == other.getZ();
}
public boolean equal3d(Position other) {
return getX() == other.getX() && getY() == other.getY() && getZ() == other.getZ();
}
public double computeDistance(Position b){
double detX = getX()-b.getX();
double detY = getY()-b.getY();
double detZ = getZ()-b.getZ();
return Math.sqrt(detX*detX+detY*detY+detZ*detZ);
}
public Position translateWithDegrees(float dist, float angle) {
angle = (float) Math.toRadians(angle);
this.x += dist * Math.sin(angle);
......
This source diff could not be displayed because it is too large. You can view the blob instead.
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