Commit d05b3207 authored by Magix's avatar Magix Committed by GitHub
Browse files

Add more events

Merge pull request #1516 from Grasscutters/more-events
parents 9a104f6f a3e0f7f5
......@@ -3,7 +3,7 @@ package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.utils.Position;
import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType;
import java.util.List;
......@@ -22,9 +22,8 @@ public final class TeleportAllCommand implements CommandHandler {
for (Player player : targetPlayer.getWorld().getPlayers()) {
if (player.equals(targetPlayer))
continue;
Position pos = targetPlayer.getPosition();
player.getWorld().transferPlayerToScene(player, targetPlayer.getSceneId(), pos);
player.getWorld().transferPlayerToScene(player, targetPlayer.getSceneId(), TeleportType.COMMAND, targetPlayer.getPosition());
}
CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.success"));
......
......@@ -3,6 +3,7 @@ package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType;
import emu.grasscutter.utils.Position;
import java.util.List;
......@@ -40,20 +41,21 @@ public final class TeleportCommand implements CommandHandler {
} // Fallthrough
case 3:
try {
x = parseRelative(args.get(0), x);
y = parseRelative(args.get(1), y);
z = parseRelative(args.get(2), z);
x = this.parseRelative(args.get(0), x);
y = this.parseRelative(args.get(1), y);
z = this.parseRelative(args.get(2), z);
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.invalid_position"));
}
break;
default:
sendUsageMessage(sender);
this.sendUsageMessage(sender);
return;
}
Position target_pos = new Position(x, y, z);
boolean result = targetPlayer.getWorld().transferPlayerToScene(targetPlayer, sceneId, target_pos);
boolean result = targetPlayer.getWorld().transferPlayerToScene(targetPlayer, sceneId, TeleportType.COMMAND, target_pos);
if (!result) {
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.exists_error"));
} else {
......
......@@ -8,11 +8,11 @@ public abstract class EntityBaseGadget extends GameEntity {
public EntityBaseGadget(Scene scene) {
super(scene);
}
public abstract int getGadgetId();
@Override
public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method.
}
}
......@@ -3,7 +3,6 @@ package emu.grasscutter.game.entity;
import emu.grasscutter.game.player.Player;
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.ClientGadgetInfoOuterClass;
......@@ -11,7 +10,6 @@ import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityIn
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
import emu.grasscutter.net.proto.EvtCreateGadgetNotifyOuterClass.EvtCreateGadgetNotify;
import emu.grasscutter.net.proto.GadgetClientParamOuterClass.GadgetClientParam;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType;
......@@ -25,10 +23,10 @@ import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
public class EntityClientGadget extends EntityBaseGadget {
private final Player owner;
private final Position pos;
private final Position rot;
private int configId;
private int campId;
private int campType;
......@@ -37,7 +35,7 @@ public class EntityClientGadget extends EntityBaseGadget {
private boolean asyncLoad;
private int originalOwnerEntityId;
public EntityClientGadget(Scene scene, Player player, EvtCreateGadgetNotify notify) {
super(scene);
this.owner = player;
......@@ -59,20 +57,20 @@ public class EntityClientGadget extends EntityBaseGadget {
this.originalOwnerEntityId = this.ownerEntityId;
}
}
@Override
public int getGadgetId() {
return configId;
}
public Player getOwner() {
return owner;
}
public int getCampId() {
return campId;
}
public int getCampType() {
return campType;
}
......@@ -80,7 +78,7 @@ public class EntityClientGadget extends EntityBaseGadget {
public int getOwnerEntityId() {
return ownerEntityId;
}
public int getTargetEntityId() {
return targetEntityId;
}
......@@ -95,7 +93,7 @@ public class EntityClientGadget extends EntityBaseGadget {
@Override
public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method.
}
@Override
......@@ -124,7 +122,7 @@ public class EntityClientGadget extends EntityBaseGadget {
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder())
.build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
......@@ -133,13 +131,13 @@ public class EntityClientGadget extends EntityBaseGadget {
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
.build();
entityInfo.addPropList(pair);
ClientGadgetInfoOuterClass.ClientGadgetInfo clientGadget = ClientGadgetInfoOuterClass.ClientGadgetInfo.newBuilder()
.setCampId(this.getCampId())
.setCampType(this.getCampType())
......@@ -147,7 +145,7 @@ public class EntityClientGadget extends EntityBaseGadget {
.setTargetEntityId(this.getTargetEntityId())
.setAsyncLoad(this.isAsyncLoad())
.build();
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
.setOwnerEntityId(this.getOwnerEntityId())
......@@ -157,7 +155,7 @@ public class EntityClientGadget extends EntityBaseGadget {
.setAuthorityPeerId(this.getOwner().getPeerId());
entityInfo.setGadget(gadgetInfo);
return entityInfo.build();
}
}
......@@ -175,6 +175,8 @@ public class EntityGadget extends EntityBaseGadget {
@Override
public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method.
if (this.getSpawnEntry() != null) {
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
}
......
......@@ -158,6 +158,8 @@ public class EntityMonster extends GameEntity {
@Override
public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method.
if (this.getSpawnEntry() != null) {
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
}
......
......@@ -15,6 +15,7 @@ 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.event.entity.EntityDeathEvent;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
......@@ -23,64 +24,64 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public abstract class GameEntity {
protected int id;
private final Scene scene;
private SpawnDataEntry spawnEntry;
private int blockId;
private int configId;
private int groupId;
private MotionState moveState;
private int lastMoveSceneTimeMs;
private int lastMoveReliableSeq;
// Abilities
private Map<String, Float> metaOverrideMap;
private Int2ObjectMap<String> metaModifiers;
public GameEntity(Scene scene) {
this.scene = scene;
this.moveState = MotionState.MOTION_STATE_NONE;
}
public int getId() {
return this.id;
}
public int getEntityType() {
return getId() >> 24;
}
public World getWorld() {
return this.getScene().getWorld();
}
public Scene getScene() {
return this.scene;
}
public boolean isAlive() {
return true;
}
public LifeState getLifeState() {
return isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD;
}
public Map<String, Float> getMetaOverrideMap() {
if (this.metaOverrideMap == null) {
this.metaOverrideMap = new HashMap<>();
}
return this.metaOverrideMap;
}
public Int2ObjectMap<String> getMetaModifiers() {
if (this.metaModifiers == null) {
this.metaModifiers = new Int2ObjectOpenHashMap<>();
}
return this.metaModifiers;
}
protected int id;
private final Scene scene;
private SpawnDataEntry spawnEntry;
private int blockId;
private int configId;
private int groupId;
private MotionState moveState;
private int lastMoveSceneTimeMs;
private int lastMoveReliableSeq;
// Abilities
private Map<String, Float> metaOverrideMap;
private Int2ObjectMap<String> metaModifiers;
public GameEntity(Scene scene) {
this.scene = scene;
this.moveState = MotionState.MOTION_STATE_NONE;
}
public int getId() {
return this.id;
}
public int getEntityType() {
return getId() >> 24;
}
public World getWorld() {
return this.getScene().getWorld();
}
public Scene getScene() {
return this.scene;
}
public boolean isAlive() {
return true;
}
public LifeState getLifeState() {
return isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD;
}
public Map<String, Float> getMetaOverrideMap() {
if (this.metaOverrideMap == null) {
this.metaOverrideMap = new HashMap<>();
}
return this.metaOverrideMap;
}
public Int2ObjectMap<String> getMetaModifiers() {
if (this.metaModifiers == null) {
this.metaModifiers = new Int2ObjectOpenHashMap<>();
}
return this.metaModifiers;
}
public abstract Int2FloatOpenHashMap getFightProperties();
......@@ -230,6 +231,17 @@ public abstract class GameEntity {
}
}
/**
* Move this entity to a new position.
* @param position The new position.
* @param rotation The new rotation.
*/
public void move(Position position, Position rotation) {
// Set the position and rotation.
this.getPosition().set(position);
this.getRotation().set(rotation);
}
/**
* Called when a player interacts with this entity
* @param player Player that is interacting with this entity
......@@ -251,7 +263,9 @@ public abstract class GameEntity {
* @param killerId Entity id of the entity that killed this entity
*/
public void onDeath(int killerId) {
// Invoke entity death event.
EntityDeathEvent event = new EntityDeathEvent(this, killerId);
event.call();
}
public abstract SceneEntityInfo toProto();
......
......@@ -28,7 +28,6 @@ import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
......@@ -42,15 +41,14 @@ import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
import static java.util.Map.entry;
import com.google.gson.reflect.TypeToken;
import com.google.protobuf.InvalidProtocolBufferException;
public class EnergyManager extends BasePlayerManager {
private final Map<EntityAvatar, Integer> avatarNormalProbabilities;
// energyUsage for each player
private Boolean energyUsage;
private boolean energyUsage; // Should energy usage be enabled for this player?
private final static Int2ObjectMap<List<EnergyDropInfo>> energyDropData = new Int2ObjectOpenHashMap<>();
private final static Int2ObjectMap<List<SkillParticleGenerationInfo>> skillParticleGenerationData = new Int2ObjectOpenHashMap<>();
......@@ -90,9 +88,9 @@ public class EnergyManager extends BasePlayerManager {
}
}
/**********
Particle creation for elemental skills.
**********/
/**
* Particle creation for elemental skills.
*/
private int getBallCountForAvatar(int avatarId) {
// We default to two particles.
int count = 2;
......@@ -120,12 +118,12 @@ public class EnergyManager extends BasePlayerManager {
}
private int getBallIdForElement(ElementType element) {
// If we have no element, we default to an elementless particle.
// If we have no element, we default to an element-less particle.
if (element == null) {
return 2024;
}
// Otherwise, we determin the particle's ID based on the element.
// Otherwise, we determine the particle's ID based on the element.
return switch (element) {
case Fire -> 2017;
case Water -> 2018;
......@@ -156,7 +154,7 @@ public class EnergyManager extends BasePlayerManager {
int amount = 2;
// Try to get the casting avatar from the player's party.
Optional<EntityAvatar> avatarEntity = getCastingAvatarEntityForEnergy(invoke.getEntityId());
Optional<EntityAvatar> avatarEntity = this.getCastingAvatarEntityForEnergy(invoke.getEntityId());
// Bug: invokes twice sometimes, Ayato, Keqing
// ToDo: deal with press, hold difference. deal with charge(Beidou, Yunjin)
......@@ -174,20 +172,21 @@ public class EnergyManager extends BasePlayerManager {
// particles we have to generate.
if (skillDepotData != null) {
ElementType element = skillDepotData.getElementType();
itemId = getBallIdForElement(element);
itemId = this.getBallIdForElement(element);
}
}
}
// Generate the particles.
for (int i = 0; i < amount; i++) {
generateElemBall(itemId, new Position(action.getPos()), 1);
this.generateElemBall(itemId, new Position(action.getPos()), 1);
}
}
/**********
Pickup of elemental particles and orbs.
**********/
/**
* Pickup of elemental particles and orbs.
* @param elemBall The elemental particle or orb.
*/
public void handlePickupElemBall(GameItem elemBall) {
// Check if the item is indeed an energy particle/orb.
if (elemBall.getItemId() < 2001 ||elemBall.getItemId() > 2024) {
......@@ -242,9 +241,10 @@ public class EnergyManager extends BasePlayerManager {
}
}
/**********
Energy generation for NAs/CAs.
**********/
/**
* Energy generation for NAs/CAs.
* @param avatar The avatar.
*/
private void generateEnergyForNormalAndCharged(EntityAvatar avatar) {
// This logic is based on the descriptions given in
// https://genshin-impact.fandom.com/wiki/Energy#Energy_Generated_by_Normal_Attacks
......@@ -290,11 +290,10 @@ public class EnergyManager extends BasePlayerManager {
// Make sure the target is an actual enemy.
GameEntity targetEntity = this.player.getScene().getEntityById(attackRes.getDefenseId());
if (!(targetEntity instanceof EntityMonster)) {
if (!(targetEntity instanceof EntityMonster targetMonster)) {
return;
}
EntityMonster targetMonster = (EntityMonster)targetEntity;
MonsterType targetType = targetMonster.getMonsterData().getType();
if (targetType != MonsterType.MONSTER_ORDINARY && targetType != MonsterType.MONSTER_BOSS) {
return;
......@@ -319,10 +318,10 @@ public class EnergyManager extends BasePlayerManager {
this.generateEnergyForNormalAndCharged(attackerEntity.get());
}
/*
* Energy logic related to using skills.
*/
/**********
Energy logic related to using skills.
**********/
private void handleBurstCast(Avatar avatar, int skillId) {
// Don't do anything if energy usage is disabled.
if (!GAME_OPTIONS.energyUsage || !this.energyUsage) {
......@@ -351,9 +350,10 @@ public class EnergyManager extends BasePlayerManager {
this.handleBurstCast(avatar, skillId);
}
/**********
Monster energy drops.
**********/
/*
* Monster energy drops.
*/
private void generateElemBallDrops(EntityMonster monster, int dropId) {
// Generate all drops specified for the given drop id.
if (!energyDropData.containsKey(dropId)) {
......@@ -365,6 +365,7 @@ public class EnergyManager extends BasePlayerManager {
this.generateElemBall(info.getBallId(), monster.getPosition(), info.getCount());
}
}
public void handleMonsterEnergyDrop(EntityMonster monster, float hpBeforeDamage, float hpAfterDamage) {
// Make sure this is actually a monster.
// Note that some wildlife also has that type, like boars or birds.
......@@ -373,7 +374,7 @@ public class EnergyManager extends BasePlayerManager {
return;
}
// Calculate the HP tresholds for before and after the damage was taken.
// Calculate the HP thresholds for before and after the damage was taken.
float maxHp = monster.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
float thresholdBefore = hpBeforeDamage / maxHp;
float thresholdAfter = hpAfterDamage / maxHp;
......@@ -386,19 +387,20 @@ public class EnergyManager extends BasePlayerManager {
float threshold = drop.getHpPercent() / 100.0f;
if (threshold < thresholdBefore && threshold >= thresholdAfter) {
generateElemBallDrops(monster, drop.getDropId());
this.generateElemBallDrops(monster, drop.getDropId());
}
}
// Handle kill drops.
if (hpAfterDamage <= 0 && monster.getMonsterData().getKillDropId() != 0) {
generateElemBallDrops(monster, monster.getMonsterData().getKillDropId());
this.generateElemBallDrops(monster, monster.getMonsterData().getKillDropId());
}
}
/**********
Utility.
**********/
/*
* Utilities.
*/
private void generateElemBall(int ballId, Position position, int count) {
// Generate a particle/orb with the specified parameters.
ItemData itemData = GameData.getItemDataMap().get(ballId);
......@@ -417,7 +419,7 @@ public class EnergyManager extends BasePlayerManager {
// that cast the skill.
// Try to get the invoking entity from the scene.
GameEntity entity = player.getScene().getEntityById(invokeEntityId);
GameEntity entity = this.player.getScene().getEntityById(invokeEntityId);
// Determine the ID of the entity that originally cast this skill. If the scene entity is null,
// or not an `EntityClientGadget`, we assume that we are directly looking at the casting avatar
......@@ -430,20 +432,20 @@ public class EnergyManager extends BasePlayerManager {
: ((EntityClientGadget)entity).getOriginalOwnerEntityId();
// Finally, find the avatar entity in the player's team.
return player.getTeamManager().getActiveTeam()
return this.player.getTeamManager().getActiveTeam()
.stream()
.filter(character -> character.getId() == avatarEntityId)
.findFirst();
}
public Boolean getEnergyUsage() {
return energyUsage;
public boolean getEnergyUsage() {
return this.energyUsage;
}
public void setEnergyUsage(Boolean energyUsage) {
public void setEnergyUsage(boolean energyUsage) {
this.energyUsage = energyUsage;
if (!energyUsage) { // Refill team energy if usage is disabled
for (EntityAvatar entityAvatar : player.getTeamManager().getActiveTeam()) {
for (EntityAvatar entityAvatar : this.player.getTeamManager().getActiveTeam()) {
entityAvatar.addEnergy(1000, PropChangeReason.PROP_CHANGE_REASON_GM,true);
}
}
......
......@@ -5,11 +5,11 @@ import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.MapMarkPointTypeOuterClass.MapMarkPointType;
import emu.grasscutter.net.proto.MarkMapReqOuterClass.MarkMapReq;
import emu.grasscutter.net.proto.MarkMapReqOuterClass.MarkMapReq.Operation;
import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType;
import emu.grasscutter.server.packet.send.PacketMarkMapRsp;
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
import emu.grasscutter.utils.Position;
import java.util.HashMap;
import java.util.Map;
public class MapMarksManager extends BasePlayerManager {
......@@ -20,7 +20,7 @@ public class MapMarksManager extends BasePlayerManager {
}
public Map<String, MapMark> getMapMarks() {
return getPlayer().getMapMarks();
return this.getPlayer().getMapMarks();
}
public void handleMapMarkReq(MarkMapReq req) {
......@@ -30,26 +30,26 @@ public class MapMarksManager extends BasePlayerManager {
MapMark createMark = new MapMark(req.getMark());
// keep teleporting functionality on fishhook mark.
if (createMark.getMapMarkPointType() == MapMarkPointType.MAP_MARK_POINT_TYPE_FISH_POOL) {
teleport(player, createMark);
this.teleport(player, createMark);
return;
}
addMapMark(createMark);
this.addMapMark(createMark);
}
case OPERATION_MOD -> {
MapMark oldMark = new MapMark(req.getOld());
removeMapMark(oldMark.getPosition());
this.removeMapMark(oldMark.getPosition());
MapMark newMark = new MapMark(req.getMark());
addMapMark(newMark);
this.addMapMark(newMark);
}
case OPERATION_DEL -> {
MapMark deleteMark = new MapMark(req.getMark());
removeMapMark(deleteMark.getPosition());
this.removeMapMark(deleteMark.getPosition());
}
}
if (op != Operation.OPERATION_GET) {
save();
this.save();
}
player.getSession().send(new PacketMarkMapRsp(getMapMarks()));
player.getSession().send(new PacketMarkMapRsp(this.getMapMarks()));
}
public String getMapMarkKey(Position position) {
......@@ -57,27 +57,25 @@ public class MapMarksManager extends BasePlayerManager {
}
public void removeMapMark(Position position) {
getMapMarks().remove(getMapMarkKey(position));
this.getMapMarks().remove(this.getMapMarkKey(position));
}
public void addMapMark(MapMark mapMark) {
if (getMapMarks().size() < mapMarkMaxCount) {
getMapMarks().put(getMapMarkKey(mapMark.getPosition()), mapMark);
if (this.getMapMarks().size() < mapMarkMaxCount) {
this.getMapMarks().put(this.getMapMarkKey(mapMark.getPosition()), mapMark);
}
}
private void teleport(Player player, MapMark mapMark) {
float y;
try {
y = (float)Integer.parseInt(mapMark.getName());
y = Float.parseFloat(mapMark.getName());
} catch (Exception e) {
y = 300;
}
Position pos = mapMark.getPosition();
player.getPosition().set(pos.getX(), y, pos.getZ());
if (mapMark.getSceneId() != player.getSceneId()) {
player.getWorld().transferPlayerToScene(player, mapMark.getSceneId(), player.getPosition());
}
player.getWorld().transferPlayerToScene(player, mapMark.getSceneId(), TeleportType.MAP, new Position(pos.getX(), y, pos.getZ()));
player.getScene().broadcastPacket(new PacketSceneEntityAppearNotify(player));
}
}
......@@ -30,6 +30,7 @@ import emu.grasscutter.game.shop.ShopChestBatchUseTable;
import emu.grasscutter.game.shop.ShopChestTable;
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
import emu.grasscutter.net.proto.MaterialInfoOuterClass.MaterialInfo;
import emu.grasscutter.server.event.player.PlayerUseFoodEvent;
import emu.grasscutter.server.game.BaseGameSystem;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.*;
......@@ -812,38 +813,49 @@ public class InventorySystem extends BaseGameSystem {
int used = 0;
boolean useSuccess = false;
ItemData itemData = useItem.getItemData();
// Use
switch (useItem.getItemData().getMaterialType()) {
switch (itemData.getMaterialType()) {
case MATERIAL_FOOD:
if (useItem.getItemData().getUseTarget() == ItemUseTarget.ITEM_USE_TARGET_SPECIFY_DEAD_AVATAR) {
if (itemData.getUseTarget() == ItemUseTarget.ITEM_USE_TARGET_SPECIFY_DEAD_AVATAR) {
if (target == null) {
break;
}
used = player.getTeamManager().reviveAvatar(target) ? 1 : 0;
// Invoke player use food event.
PlayerUseFoodEvent event = new PlayerUseFoodEvent(player, itemData, target.getAsEntity());
// Call the event.
event.call(); if(!event.isCanceled()) {
used = player.getTeamManager().reviveAvatar(target) ? 1 : 0;
}
} else {
used = 1;
}
break;
case MATERIAL_NOTICE_ADD_HP:
if (useItem.getItemData().getUseTarget() == ItemUseTarget.ITEM_USE_TARGET_SPECIFY_ALIVE_AVATAR) {
if (itemData.getUseTarget() == ItemUseTarget.ITEM_USE_TARGET_SPECIFY_ALIVE_AVATAR) {
if (target == null) {
break;
}
int[] SatiationParams = useItem.getItemData().getSatiationParams();
used = player.getTeamManager().healAvatar(target, SatiationParams[0], SatiationParams[1]) ? 1 : 0;
// Invoke player use food event.
PlayerUseFoodEvent event = new PlayerUseFoodEvent(player, itemData, target.getAsEntity());
// Call the event.
event.call(); if(!event.isCanceled()) {
int[] SatiationParams = itemData.getSatiationParams();
used = player.getTeamManager().healAvatar(target, SatiationParams[0], SatiationParams[1]) ? 1 : 0;
}
}
break;
case MATERIAL_CONSUME:
// Make sure we have usage data for this material.
if (useItem.getItemData().getItemUse() == null) {
if (itemData.getItemUse() == null) {
break;
}
ItemUseOp useOp = useItem.getItemData().getItemUse().get(0).getUseOp();
ItemUseOp useOp = itemData.getItemUse().get(0).getUseOp();
// Unlock item based on use operation
useSuccess = switch (useOp) {
case ITEM_USE_UNLOCK_FORGE -> player.getForgingManager().unlockForgingBlueprint(useItem);
......@@ -854,7 +866,7 @@ public class InventorySystem extends BaseGameSystem {
break;
case MATERIAL_FURNITURE_FORMULA:
case MATERIAL_FURNITURE_SUITE_FORMULA:
if (useItem.getItemData().getItemUse() == null) {
if (itemData.getItemUse() == null) {
break;
}
useSuccess = player.getFurnitureManager().unlockFurnitureOrSuite(useItem);
......@@ -862,7 +874,7 @@ public class InventorySystem extends BaseGameSystem {
break;
case MATERIAL_CONSUME_BATCH_USE:
// Make sure we have usage data for this material.
if (useItem.getItemData().getItemUse() == null) {
if (itemData.getItemUse() == null) {
break;
}
......@@ -889,11 +901,11 @@ public class InventorySystem extends BaseGameSystem {
}
for (ItemParamData itemParamData : shopChestTable.getContainsItem()) {
ItemData itemData = GameData.getItemDataMap().get(itemParamData.getId());
if (itemData == null) {
ItemData containedItem = GameData.getItemDataMap().get(itemParamData.getId());
if (containedItem == null) {
continue;
}
rewardItemList.add(new GameItem(itemData, itemParamData.getCount()));
rewardItemList.add(new GameItem(containedItem, itemParamData.getCount()));
}
if (!rewardItemList.isEmpty()) {
......@@ -919,12 +931,12 @@ public class InventorySystem extends BaseGameSystem {
}
int optionItemId = shopChestBatchUseTable.getOptionItem().get(optionId - 1);
ItemData itemData = GameData.getItemDataMap().get(optionItemId);
if (itemData == null) {
ItemData optionItem = GameData.getItemDataMap().get(optionItemId);
if (optionItem == null) {
break;
}
player.getInventory().addItem(new GameItem(itemData, count), ActionReason.Shop);
player.getInventory().addItem(new GameItem(optionItem, count), ActionReason.Shop);
used = count;
break;
......@@ -944,23 +956,23 @@ public class InventorySystem extends BaseGameSystem {
// we return the item to make UseItemRsp a success.
if (used > 0) {
// Handle use params, mainly server buffs
for (ItemUseData useData : useItem.getItemData().getItemUse()) {
for (ItemUseData useData : itemData.getItemUse()) {
switch (useData.getUseOp()) {
case ITEM_USE_ADD_SERVER_BUFF -> {
int buffId = Integer.parseInt(useData.getUseParam()[0]);
float time = Float.parseFloat(useData.getUseParam()[1]);
player.getBuffManager().addBuff(buffId, time);
}
default -> {}
}
}
// Remove item from inventory since we used it
player.getInventory().removeItem(useItem, used);
return useItem;
}
if (useSuccess) {
return useItem;
}
......
......@@ -17,6 +17,8 @@ import emu.grasscutter.data.excels.SceneData;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
import emu.grasscutter.scripts.data.SceneConfig;
import emu.grasscutter.server.event.player.PlayerTeleportEvent;
import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.PacketDelTeamEntityNotify;
import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify;
......@@ -53,7 +55,7 @@ public class World implements Iterable<Player> {
this.players = Collections.synchronizedList(new ArrayList<>());
this.scenes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
this.levelEntityId = getNextEntityId(EntityIdType.MPLEVEL);
this.levelEntityId = this.getNextEntityId(EntityIdType.MPLEVEL);
this.worldLevel = player.getWorldLevel();
this.isMultiplayer = isMultiplayer;
......@@ -101,7 +103,7 @@ public class World implements Iterable<Player> {
public Scene getSceneById(int sceneId) {
// Get scene normally
Scene scene = getScenes().get(sceneId);
Scene scene = this.getScenes().get(sceneId);
if (scene != null) {
return scene;
}
......@@ -118,7 +120,7 @@ public class World implements Iterable<Player> {
}
public int getPlayerCount() {
return getPlayers().size();
return this.getPlayers().size();
}
public boolean isMultiplayer() {
......@@ -131,7 +133,7 @@ public class World implements Iterable<Player> {
public synchronized void addPlayer(Player player) {
// Check if player already in
if (getPlayers().contains(player)) {
if (this.getPlayers().contains(player)) {
return;
}
......@@ -142,11 +144,11 @@ public class World implements Iterable<Player> {
// Register
player.setWorld(this);
getPlayers().add(player);
this.getPlayers().add(player);
// Set player variables
player.setPeerId(this.getNextPeerId());
player.getTeamManager().setEntityId(getNextEntityId(EntityIdType.TEAM));
player.getTeamManager().setEntityId(this.getNextEntityId(EntityIdType.TEAM));
// Copy main team to multiplayer team
if (this.isMultiplayer()) {
......@@ -169,12 +171,12 @@ public class World implements Iterable<Player> {
player.sendPacket(
new PacketDelTeamEntityNotify(
player.getSceneId(),
getPlayers().stream().map(p -> p.getTeamManager().getEntityId()).collect(Collectors.toList())
this.getPlayers().stream().map(p -> p.getTeamManager().getEntityId()).collect(Collectors.toList())
)
);
// Deregister
getPlayers().remove(player);
this.getPlayers().remove(player);
player.setWorld(null);
// Remove from scene
......@@ -187,7 +189,7 @@ public class World implements Iterable<Player> {
}
// Disband world if host leaves
if (getHost() == player) {
if (this.getHost() == player) {
List<Player> kicked = new ArrayList<>(this.getPlayers());
for (Player victim : kicked) {
World world = new World(victim);
......@@ -207,14 +209,28 @@ public class World implements Iterable<Player> {
}
public boolean transferPlayerToScene(Player player, int sceneId, Position pos) {
return transferPlayerToScene(player, sceneId, null, pos);
return this.transferPlayerToScene(player, sceneId, TeleportType.INTERNAL, null, pos);
}
public boolean transferPlayerToScene(Player player, int sceneId, TeleportType teleportType, Position pos) {
return this.transferPlayerToScene(player, sceneId, teleportType, null, pos);
}
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData data) {
return transferPlayerToScene(player, sceneId, data, null);
return this.transferPlayerToScene(player, sceneId, TeleportType.DUNGEON, data, null);
}
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData dungeonData, Position pos) {
public boolean transferPlayerToScene(Player player, int sceneId, TeleportType teleportType, DungeonData dungeonData, Position teleportTo) {
// Call player teleport event.
PlayerTeleportEvent event = new PlayerTeleportEvent(player, teleportType, player.getPosition(), teleportTo);
// Call event & check if it was canceled.
event.call(); if (event.isCanceled()) {
return false; // Teleport was canceled.
}
// Set the destination.
teleportTo = event.getDestination();
if (GameData.getSceneDataMap().get(sceneId) == null) {
return false;
}
......@@ -224,7 +240,7 @@ public class World implements Iterable<Player> {
if (player.getScene() != null) {
oldScene = player.getScene();
// Dont deregister scenes if the player is going to tp back into them
// Don't deregister scenes if the player is going to tp back into them
if (oldScene.getId() == sceneId) {
oldScene.setDontDestroyWhenEmpty(true);
}
......@@ -238,9 +254,9 @@ public class World implements Iterable<Player> {
// Dungeon
SceneConfig config = newScene.getScriptManager().getConfig();
if (pos == null && config != null) {
if (teleportTo == null && config != null) {
if (config.born_pos != null) {
pos = newScene.getScriptManager().getConfig().born_pos;
teleportTo = newScene.getScriptManager().getConfig().born_pos;
}
if (config.born_rot != null) {
player.getRotation().set(config.born_rot);
......@@ -248,11 +264,11 @@ public class World implements Iterable<Player> {
}
// Set player position
if (pos == null) {
pos = player.getPosition();
if (teleportTo == null) {
teleportTo = player.getPosition();
}
player.getPosition().set(pos);
player.getPosition().set(teleportTo);
if (oldScene != null) {
newScene.setPrevScene(oldScene.getId());
......@@ -276,12 +292,12 @@ public class World implements Iterable<Player> {
}
// Teleport packet
player.sendPacket(new PacketPlayerEnterSceneNotify(player, enterType, enterReason, sceneId, pos));
player.sendPacket(new PacketPlayerEnterSceneNotify(player, enterType, enterReason, sceneId, teleportTo));
return true;
}
private void updatePlayerInfos(Player paramPlayer) {
for (Player player : getPlayers()) {
for (Player player : this.getPlayers()) {
// Dont send packets if player is logging in and filter out joining player
if (!player.hasSentLoginPackets() || player == paramPlayer) {
continue;
......@@ -292,7 +308,7 @@ public class World implements Iterable<Player> {
player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getMpTeam(), player.getTeamManager().getMaxTeamSize());
player.getTeamManager().updateTeamEntities(null);
}
// Dont send packets if player is loading into the scene
if (player.getSceneLoadState().getValue() < SceneLoadState.INIT.getValue() ) {
// World player info packets
......@@ -326,6 +342,6 @@ public class World implements Iterable<Player> {
@Override
public Iterator<Player> iterator() {
return getPlayers().iterator();
return this.getPlayers().iterator();
}
}
package emu.grasscutter.server.event.entity;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.server.event.types.EntityEvent;
import emu.grasscutter.utils.Location;
import lombok.Getter;
import javax.annotation.Nullable;
public final class EntityDeathEvent extends EntityEvent {
@Getter private final Location deathLocation;
@Getter @Nullable private final GameEntity killer;
public EntityDeathEvent(GameEntity entity, int killerId) {
super(entity);
this.deathLocation = new Location(entity.getScene(), entity.getPosition());
this.killer = entity.getScene().getEntityById(killerId);
}
}
\ No newline at end of file
package emu.grasscutter.server.event.game;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.event.types.ServerEvent;
/**
* @deprecated Will be removed in 1.2.3-dev or 1.3.0.
*/
@Deprecated(since = "1.2.2-dev", forRemoval = true)
public final class CommandResponseEvent extends ServerEvent {
private String message;
private Player player;
public CommandResponseEvent(Type type, Player player,String message) {
super(type);
this.message = message;
this.player = player;
}
public String getMessage() {
return message;
}
public Player getPlayer() {
return player;
}
}
package emu.grasscutter.server.event.player;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.event.types.PlayerEvent;
import emu.grasscutter.utils.Position;
/**
* TODO: Allow plugins to change the position of the player.
*/
public final class PlayerMoveEvent extends PlayerEvent {
private final MoveType type;
private final Position from;
private final Position to;
public PlayerMoveEvent(Player player, MoveType type, Position from, Position to) {
super(player);
this.type = type;
this.from = from;
this.to = to;
}
public MoveType getMoveType() {
return this.type;
}
public Position getSource() {
return this.from;
}
public Position getDestination() {
return this.to;
}
public enum MoveType {
/**
* The player has sent a combat invocation to move.
*/
PLAYER,
/**
* The server has requested that the player moves.
*/
SERVER
}
}
\ No newline at end of file
package emu.grasscutter.server.event.player;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.event.types.PlayerEvent;
import lombok.Getter;
/**
* This event is invoked when the ENTIRE TEAM dies.
* To listen for one player death, use {@link emu.grasscutter.server.event.entity.EntityDeathEvent}.
*/
public final class PlayerTeamDeathEvent extends PlayerEvent {
@Getter private final EntityAvatar selectedAvatar;
public PlayerTeamDeathEvent(Player player, EntityAvatar selectedAvatar) {
super(player);
this.selectedAvatar = selectedAvatar;
}
}
\ No newline at end of file
package emu.grasscutter.server.event.player;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.event.Cancellable;
import emu.grasscutter.server.event.types.PlayerEvent;
import emu.grasscutter.utils.Position;
public final class PlayerTeleportEvent extends PlayerEvent implements Cancellable {
private final TeleportType type;
private final Position from;
private Position to;
public PlayerTeleportEvent(Player player, TeleportType type, Position from, Position to) {
super(player);
this.type = type;
this.from = from;
this.to = to;
}
public TeleportType getTeleportType() {
return this.type;
}
public Position getSource() {
return this.from;
}
public Position getDestination() {
return this.to;
}
public void setDestination(Position to) {
this.to = to;
}
public enum TeleportType {
/**
* There is no specified reason to teleport.
*/
INTERNAL,
/**
* The player has asked to teleport to a waypoint.
*/
WAYPOINT,
/**
* The player has asked to teleport using the map.
*/
MAP,
/**
* The player has asked to teleport into a dungeon.
*/
DUNGEON,
/**
* The player has asked to teleport using the command.
*/
COMMAND
}
}
\ No newline at end of file
package emu.grasscutter.server.event.player;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.event.Cancellable;
import emu.grasscutter.server.event.types.PlayerEvent;
import lombok.Getter;
import lombok.Setter;
/**
* This event is invoked when the player uses food on an avatar.
*/
public final class PlayerUseFoodEvent extends PlayerEvent implements Cancellable {
@Getter @Setter private ItemData foodUsed;
@Getter private final EntityAvatar selectedAvatar;
public PlayerUseFoodEvent(Player player, ItemData foodUsed, EntityAvatar selectedAvatar) {
super(player);
this.foodUsed = foodUsed;
this.selectedAvatar = selectedAvatar;
}
}
\ No newline at end of file
......@@ -60,8 +60,7 @@ public class HandlerCombatInvocationsNotify extends PacketHandler {
new Position(motionInfo.getRot()), motionState);
event.call();
entity.getPosition().set(motionInfo.getPos());
entity.getRotation().set(motionInfo.getRot());
entity.move(event.getPosition(), event.getRotation());
entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime());
entity.setLastMoveReliableSeq(moveInfo.getReliableSeq());
entity.setMotionState(motionState);
......
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