Merge unstable into development (#2173)

* Remove more scene synchronized

* Fix worktop options not appearing

* Format code [skip actions]

* Fix delay with server tasks

* Format code [skip actions]

* Fully fix fairy clock (#2146)

* Fix scene transition

* fully fix fairy clock

* Re-add call to `Player#updatePlayerGameTime`

* Format code [skip actions]

* Initialize the script loader in `ResourceLoader#loadAll`

* Fix region removal checking

* Format code [skip actions]

* Use Lombok's `EqualsAndHashCode` for comparing scene regions

* Format code [skip actions]

* Move 'invalid gather object' to `trace`

* Add more information to the 'unknown condition handler' message

* Move invalid ability action to trace

* Make `KcpTunnel` public

* Validate the NPC being talked to

* Format code [skip actions]

* NPCs are not spawned server side; change logic to handle it

* Format code [skip actions]

* unload scene when there are no players (#2147)

* unload scene when there are no players

* Update src/main/java/emu/grasscutter/game/world/Scene.java

Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com>

---------

Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com>

* Check if a command should be copied or HTTP should be used

* Lint Code [skip actions]

* Fix character names rendering incorrectly

* Add basic troubleshooting command

* Implement handbook teleporting

also a few formatting changes and sort data by logical sense

* Fix listener `ConcurrentModificationException` issue

* Add color change to `Join the Community!`

* Lint Code [skip actions]

* Make clickable buttons appear clickable

* Remove 'Mechanicus' entities from the list of entities

* Format code [skip actions]

* Fix going back returning a blank screen

* Implement entity spawning

* Add setting level to entity card

* Add support for 'plain text' mode

* Make descriptions of objects scrollable

* Lint Code [skip actions]

* Format code [skip actions]

* Change the way existing hooks work

* Format code [skip actions]

* Upgrade Javalin to 5.5.0 & Fix project warnings

* Upgrade logging libraries

* Fix gacha mappings static file issue

* Add temporary backwards compatability for `ServerHelper`

* Format code [skip actions]

* Remove artifact signatures from VCS

* Fix forge queue data protocol definition

* Run `spotlessApply`

* Format code [skip actions]

* Download data required for building artifacts

* Add call for Facebook logins

* Add the wiki page as a submodule

* Format code [skip actions]

* Update translation (#2150)

* Update translation

* Update translation

* Separate the dispatch and game servers (pt. 1)

gacha is still broken, handbook still needs to be done

* Format code [skip actions]

* Separate the dispatch and game servers (pt. 2)

this commit fixes the gacha page

* Add description for '/troubleshoot'

* Set default avatar talent level to 10

* Separate the dispatch and game servers (pt. 3)

implement handbook across servers!

* Format code [skip actions]

* Update GitHub Actions to use 'download-file' over 'wget'

* Gm handbook lmao (#2149)

* Fix font issue

* Fix avatars

* Fix text overflow in commands

* Fix virtualized lists and items page 😭😭

* magix why 💀

* use hover style in all minicards

* button

* remove console.log

* lint

* Add icons

* magix asked

* Fix overflow padding issue

* Fix achievement text overflow

* remove icons from repo

* Change command icon

* Add the wiki page as a submodule

* total magix moment

* fix text overflow in commands

* Fix discord button

* Make text scale on Minicard

* import icons and font from another source

* Add hover effects to siebar buttons

* move font and readme to submodule repo

* Make data folder a submodule

* import icons and font from data submodule

* Update README.md

* total magix moment

* magix moment v2

* submodule change

* Import `.webp` files

* Resize `HomeButton`

* Fix 'Copy Command' reappearing after changing pages

---------

Co-authored-by: KingRainbow44 <kobedo11@gmail.com>

* Lint Code [skip actions]

* Download data for the build, not for the lint

* format imports

this is really just to see if build handbook works kek

* Implement proper handbook authentication (pt. 1)

* Implement proper handbook authentication (pt. 2)

* Format code [skip actions]

* Add quest data dumping for the handbook

* Change colors to fit _something suitable_

* Format code [skip actions]

* Fix force pushing to branches after linting

* Fix logic of `SetPlayerPropReq`

* Move more group loading to `trace`

* Add handbook IP authentication in hybrid mode

* Fix player level up not displaying on the client properly

* Format code [skip actions]

* Fix game time locking

* Format code [skip actions]

* Update player properties

* Format code [skip actions]

* Move `warn`s for groups to `debug`

* Fix player pausing

* Move more logs to `trace`

* Use `removeItemById` for deleting items via quests

* Clean up logger more

* Pause in-game time when the world is paused

* Format code [skip actions]

* More player property documentation

* Multi-threaded resource loading

* Format code [skip actions]

* Add quest widgets

* Add quests page (basic impl.)

* Add/fix colors

also fix tailwind

* Remove banned packets

client modifications already perform the job of blocking malicious packets from being executed, no point in having this if self-windy is wanted

* Re-add `BeginCameraSceneLookNotify`

* Fix being unable to attack (#2157)

* Add `PlayerOpenChestEvent`

* Add methods to get players from the server

* Add static methods to register an event handler

* Add `PlayerEnterDungeonEvent`

* Remove legacy documentation from `PlayerMoveEvent`

* Add `PlayerChatEvent`

* Add defaults to `Position`

* Clean up `.utils`

* Revert `Multi-threaded resource loading`

* Fix changing target UID when talking to the server

* Lint Code [skip actions]

* Format code [skip actions]

* fix NPC talk triggering main quest in 46101 (#2158)

Make it so that only talks where the param matches the talkId are checked.

* Format code [skip actions]

* Partially fix Chasing Shadows (#2159)

* Partially fix Chasing Shadows

* Go ahead and move it before the return before Magix tells me to.

* Format code [skip actions]

* Bring back period lol (#2160)

* Disable SNI for the HTTPS server

* Add `EntityCreationEvent`

* Add initial startup message

this is so the server appears like its preparing to start

* Format code [skip actions]

* Enable debug mode for plugin loggers if enabled for the primary logger

* Add documentation about `WorldAreaConfigData`

* Make more fields in excels accessible

* Remove deprecated fields from `GetShopRsp`

* Run `spotlessApply` on definitions

* Add `PlayerEnterAreaEvent`

* Optimize event calls

* Fix event invokes

* Format code [skip actions]

* Remove manual autofinish for main quests. (#2162)

* Add world areas to the textmap cache

* Format code [skip actions]

* Don't overdefine variables in extended classes (#2163)

* Add dumper for world areas

* Format code [skip actions]

* instantiate personalLineList (#2165)

* Fix protocol definitions

thank you Nazrin! (+ hiro for raw definitions)

* Fix the background color leaking from the character widget

* Change HTML spacing to 2 spaces

* Implement hiding widgets

* Change scrollbar to a vibrant color

* Add _some_ scaling to the home buttons and its text

* Build the handbook with Gradle

* Fix the 'finer details' with the handbook UI

* Lint Code [skip actions]

* Fix target destination for the Gradle-built handbook

* Implement fetching a player across servers & Add a chainable JsonObject

useful for plugins! might be used in grasscutter eventually

* Fix GitHub actions

* Fix event calling & canceling

* Run `spotlessApply`

* Rename fields (might be wrong)

* Add/update all/more protocol definitions

* Add/update all/more protocol definitions

* Remove outdated packet

* Fix protocol definitions

* Format code [skip actions]

* Implement some lua variables for less console spam (#2172)

* Implement some lua variables for less console spam

* Add GetHostQuestState

This fixes some chapter 3 stuff.

* Format code [skip actions]

* Fix merge import

* Format code [skip actions]

* Fully fix fairy clock for real this time (#2167)

* Fully fix fairy clock For real this time

* Make it so relogging keeps the time lock state.

* Refactor out questLockTime

* Per Hartie, the client packet needs to be changed too

* Update src/main/java/emu/grasscutter/game/world/World.java

Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com>

* Update src/main/java/emu/grasscutter/server/packet/recv/HandlerClientLockGameTimeNotify.java

* Remove all code not needed to get clock working

---------

Co-authored-by: Magix <27646710+KingRainbow44@users.noreply.github.com>

* Implement a proper ability system (#2166)

* Apply fix `21dec2fe`

* Apply fix `89d01d5f`

* Apply fix `d900f154`

this one was already implemented; updated to use call from previous commit

* Ability changing commit

TODO: change info to debug

* Remove use of deprecated methods/fields

* Temp commit v2
(Adding LoseHP and some fixes)

* Oopsie

* Probably fix monster battle

* Fix issue with reflecting into fields

* Fix some things

* Fix ability names for 3.6 resources

* Improve logging

---------

Co-authored-by: StartForKiller <jesussanz2003@gmail.com>

* Format code [skip actions]

* Add system for sending messages between servers

* Format some code

* Remove protocol definitions from Spotless

* Default debug to false; enable with `-debug`

* Implement completely useless global value copying

* HACK: Return the avatar which holds the weapon when the weapon is referred to by ID

* Add properties to `AbilityModifier`

* Change the way HTML is served after authentication

* Use thread executors to speed up the database loading process

* Format code [skip actions]

* Add system for setting handbook address and port

* Lint Code [skip actions]

* Format code [skip actions]

* Fix game-related data not saving

* Format code [skip actions]

* Fix handbook server details

* Lint Code [skip actions]

* Format code [skip actions]

* Use the headers provided by a context to get the IP address

should acknowledge #1975

* Format code [skip actions]

* Move more logs to `trace`

* Format code [skip actions]

* more trace

* Fix something and implement weapon entities

* Format code [skip actions]

* Fix `EntityWeapon`

* Remove deprecated API & Fix resource checking

* Fix unnecessary warning for first-time setup

* Implement handbook request limiting

* Format code [skip actions]

* Fix new avatar weapons being null

* Format code [skip actions]

* Fix issue with 35303 being un-completable & Try to fix fulfilled quest conditions being met

* Load activity config on server startup

* Require plugins to specify an API version and match with the server

* Add default open state ignore list

* Format code [skip actions]

* Quick fix for questing, needs more investigation
This would make the questing work again

* Remove existing hack for 35303

* Fix ignored open states from being set

* Format code [skip actions]

* fix the stupidest bug ive ever seen

* Optimize player kicking on server close

* Format code [skip actions]

* Re-add hack to fix 35303

* Update GitHub actions

* Format code [skip actions]

* Potentially fix issues with regions

* Download additional handbook data

* Revert "Potentially fix issues with regions"

This reverts commit 84e3823695.

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: scooterboo <lewasite@yahoo.com>
Co-authored-by: Tesutarin <105267106+Tesutarin@users.noreply.github.com>
Co-authored-by: Scald <104459145+Arikatsu@users.noreply.github.com>
Co-authored-by: StartForKiller <jesussanz2003@gmail.com>
This commit is contained in:
Magix
2023-05-31 20:48:16 -07:00
committed by GitHub
parent f46fd372d2
commit 9e5b57a043
3839 changed files with 1841548 additions and 37533 deletions

View File

@@ -1,17 +1,14 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.GameConstants;
import emu.grasscutter.*;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.AvatarData;
import emu.grasscutter.data.excels.AvatarSkillDepotData;
import emu.grasscutter.data.excels.avatar.*;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.inventory.EquipType;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.inventory.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.props.*;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.world.*;
import emu.grasscutter.net.proto.AbilityControlBlockOuterClass.AbilityControlBlock;
import emu.grasscutter.net.proto.AbilityEmbryoOuterClass.AbilityEmbryo;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
@@ -30,15 +27,11 @@ import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.server.event.player.PlayerMoveEvent;
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.Utils;
import emu.grasscutter.utils.helpers.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;
import lombok.val;
import lombok.*;
public class EntityAvatar extends GameEntity {
@Getter private final Avatar avatar;
@@ -52,39 +45,68 @@ public class EntityAvatar extends GameEntity {
public EntityAvatar(Scene scene, Avatar avatar) {
super(scene);
this.avatar = avatar;
this.avatar.setCurrentEnergy();
if (getScene() != null)
{
this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
GameItem weapon = getAvatar().getWeapon();
if (scene != null) {
var world = scene.getWorld();
this.id = world.getNextEntityId(EntityIdType.AVATAR);
var weapon = this.getAvatar().getWeapon();
if (weapon != null) {
weapon.setWeaponEntityId(getScene().getWorld().getNextEntityId(EntityIdType.WEAPON));
if (!(weapon.getWeaponEntity() != null && weapon.getWeaponEntity().getScene() == scene)) {
weapon.setWeaponEntity(
new EntityWeapon(this.getPlayer().getScene(), weapon.getItemData().getGadgetId()));
scene.getWeaponEntities().put(weapon.getWeaponEntity().getId(), weapon.getWeaponEntity());
}
}
} else {
Grasscutter.getLogger()
.error("Unable to create EntityAvatar instance; provided scene is null.");
}
this.initAbilities();
}
public Player getPlayer() {return this.avatar.getPlayer();}
@Override
public int getEntityTypeId() {
return this.getAvatar().getAvatarId();
}
public Player getPlayer() {
return this.avatar.getPlayer();
}
@Override
public Position getPosition() {return getPlayer().getPosition();}
public Position getPosition() {
return getPlayer().getPosition();
}
@Override
public Position getRotation() {return getPlayer().getRotation();}
public Position getRotation() {
return getPlayer().getRotation();
}
@Override
public boolean isAlive() {
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
}
@Override public Int2FloatMap getFightProperties() {return getAvatar().getFightProperties();}
@Override
public Int2FloatMap getFightProperties() {
return getAvatar().getFightProperties();
}
/**
* @return The entity ID of the avatar's equipped weapon.
*/
public int getWeaponEntityId() {
if (getAvatar().getWeapon() != null) {
return getAvatar().getWeapon().getWeaponEntityId();
}
return 0;
var avatar = this.getAvatar();
if (avatar.getWeapon() != null && avatar.getWeapon().getWeaponEntity() != null) {
return avatar.getWeapon().getWeaponEntity().getId();
} else return 0;
}
@Override
@@ -105,23 +127,44 @@ public class EntityAvatar extends GameEntity {
}
@Override
public float heal(float amount) {
public void initAbilities() {}
private void addConfigAbility(String abilityName) {
var data = GameData.getAbilityData(abilityName);
if (data != null)
this.getScene().getWorld().getHost().getAbilityManager().addAbilityToEntity(this, data);
}
@Override
public float heal(float amount, boolean mute) {
// Do not heal character if they are dead
if (!this.isAlive()) {
return 0f;
}
float healed = super.heal(amount);
float healed = super.heal(amount, mute);
if (healed > 0f) {
getScene().broadcastPacket(
new PacketEntityFightPropChangeReasonNotify(this, FightProperty.FIGHT_PROP_CUR_HP, healed, PropChangeReason.PROP_CHANGE_REASON_ABILITY, ChangeHpReason.CHANGE_HP_REASON_ADD_ABILITY)
);
getScene()
.broadcastPacket(
new PacketEntityFightPropChangeReasonNotify(
this,
FightProperty.FIGHT_PROP_CUR_HP,
healed,
mute
? PropChangeReason.PROP_CHANGE_REASON_NONE
: PropChangeReason.PROP_CHANGE_REASON_ABILITY,
ChangeHpReason.CHANGE_HP_REASON_ADD_ABILITY));
}
return healed;
}
@Override
public float heal(float amount) {
return this.heal(amount, false);
}
public void clearEnergy(ChangeEnergyReason reason) {
// Fight props.
val curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
@@ -134,13 +177,32 @@ public class EntityAvatar extends GameEntity {
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) {
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -curEnergy, reason));
this.getScene()
.broadcastPacket(
new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -curEnergy, reason));
}
}
/**
* Adds a fixed amount of energy to the current avatar.
*
* @param amount The amount of energy to add.
* @return True if the energy was added, false if the energy was not added.
*/
public boolean addEnergy(float amount) {
var curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
var curEnergy = this.getFightProperty(curEnergyProp);
if (curEnergy == amount) return false;
this.getAvatar().setCurrentEnergy(curEnergyProp, amount);
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
return true;
}
public void addEnergy(float amount, PropChangeReason reason) {
this.addEnergy(amount, reason, false);
}
public void addEnergy(float amount, PropChangeReason reason, boolean isFlat) {
// Get current and maximum energy for this avatar.
val elementType = this.getAvatar().getSkillDepot().getElementType();
@@ -162,29 +224,33 @@ public class EntityAvatar extends GameEntity {
if (newEnergy != curEnergy) {
this.avatar.setCurrentEnergy(curEnergyProp, newEnergy);
this.getScene().broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp));
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, reason));
this.getScene()
.broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp));
this.getScene()
.broadcastPacket(
new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, reason));
}
}
public SceneAvatarInfo getSceneAvatarInfo() {
val avatar = this.getAvatar();
val player = this.getPlayer();
SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder()
.setUid(player.getUid())
.setAvatarId(avatar.getAvatarId())
.setGuid(avatar.getGuid())
.setPeerId(player.getPeerId())
.addAllTalentIdList(avatar.getTalentIdList())
.setCoreProudSkillLevel(avatar.getCoreProudSkillLevel())
.putAllSkillLevelMap(avatar.getSkillLevelMap())
.setSkillDepotId(avatar.getSkillDepotId())
.addAllInherentProudSkillList(avatar.getProudSkillList())
.putAllProudSkillExtraLevelMap(avatar.getProudSkillBonusMap())
.addAllTeamResonanceList(player.getTeamManager().getTeamResonances())
.setWearingFlycloakId(avatar.getFlyCloak())
.setCostumeId(avatar.getCostume())
.setBornTime(avatar.getBornTime());
SceneAvatarInfo.Builder avatarInfo =
SceneAvatarInfo.newBuilder()
.setUid(player.getUid())
.setAvatarId(avatar.getAvatarId())
.setGuid(avatar.getGuid())
.setPeerId(player.getPeerId())
.addAllTalentIdList(avatar.getTalentIdList())
.setCoreProudSkillLevel(avatar.getCoreProudSkillLevel())
.putAllSkillLevelMap(avatar.getSkillLevelMap())
.setSkillDepotId(avatar.getSkillDepotId())
.addAllInherentProudSkillList(avatar.getProudSkillList())
.putAllProudSkillExtraLevelMap(avatar.getProudSkillBonusMap())
.addAllTeamResonanceList(player.getTeamManager().getTeamResonances())
.setWearingFlycloakId(avatar.getFlyCloak())
.setCostumeId(avatar.getCostume())
.setBornTime(avatar.getBornTime());
for (GameItem item : avatar.getEquips().values()) {
if (item.getItemData().getEquipType() == EquipType.EQUIP_WEAPON) {
@@ -200,22 +266,25 @@ public class EntityAvatar extends GameEntity {
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder())
.build();
EntityAuthorityInfo authority =
EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(
SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder())
.build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_AVATAR)
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLastMoveSceneTimeMs(this.getLastMoveSceneTimeMs())
.setLastMoveReliableSeq(this.getLastMoveReliableSeq())
.setLifeState(this.getLifeState().getValue());
SceneEntityInfo.Builder entityInfo =
SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_AVATAR)
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLastMoveSceneTimeMs(this.getLastMoveSceneTimeMs())
.setLastMoveReliableSeq(this.getLastMoveReliableSeq())
.setLifeState(this.getLifeState().getValue());
if (this.getScene() != null) {
entityInfo.setMotionInfo(this.getMotionInfo());
@@ -223,10 +292,12 @@ public class EntityAvatar extends GameEntity {
this.addAllFightPropsToEntityInfo(entityInfo);
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getAvatar().getLevel()))
.build();
PropPair pair =
PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(
ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getAvatar().getLevel()))
.build();
entityInfo.addPropList(pair);
entityInfo.setAvatar(this.getSceneAvatarInfo());
@@ -242,52 +313,58 @@ public class EntityAvatar extends GameEntity {
// Add avatar abilities
if (data.getAbilities() != null) {
for (int id : data.getAbilities()) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
AbilityEmbryo emb =
AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
abilityControlBlock.addAbilityEmbryoList(emb);
}
}
// Add default abilities
for (int id : GameConstants.DEFAULT_ABILITY_HASHES) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
AbilityEmbryo emb =
AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
abilityControlBlock.addAbilityEmbryoList(emb);
}
// Add team resonances
for (int id : this.getPlayer().getTeamManager().getTeamResonancesConfig()) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
AbilityEmbryo emb =
AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
abilityControlBlock.addAbilityEmbryoList(emb);
}
// Add skill depot abilities
AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(this.getAvatar().getSkillDepotId());
AvatarSkillDepotData skillDepot =
GameData.getAvatarSkillDepotDataMap().get(this.getAvatar().getSkillDepotId());
if (skillDepot != null && skillDepot.getAbilities() != null) {
for (int id : skillDepot.getAbilities()) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
AbilityEmbryo emb =
AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
abilityControlBlock.addAbilityEmbryoList(emb);
}
}
// Add equip abilities
if (this.getAvatar().getExtraAbilityEmbryos().size() > 0) {
for (String skill : this.getAvatar().getExtraAbilityEmbryos()) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(Utils.abilityHash(skill))
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
AbilityEmbryo emb =
AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(Utils.abilityHash(skill))
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
abilityControlBlock.addAbilityEmbryoList(emb);
}
}
@@ -297,19 +374,35 @@ public class EntityAvatar extends GameEntity {
}
/**
* Move this entity to a new position.
* Additionally invoke player move event.
* Move this entity to a new position. Additionally invoke player move event.
*
* @param newPosition The new position.
* @param rotation The new rotation.
*/
@Override public void move(Position newPosition, Position rotation) {
@Override
public void move(Position newPosition, Position rotation) {
// Invoke player move event.
PlayerMoveEvent event = new PlayerMoveEvent(
this.getPlayer(), PlayerMoveEvent.MoveType.PLAYER,
this.getPosition(), newPosition
); event.call();
PlayerMoveEvent event =
new PlayerMoveEvent(
this.getPlayer(), PlayerMoveEvent.MoveType.PLAYER, this.getPosition(), newPosition);
event.call();
// Set position and rotation.
super.move(event.getDestination(), rotation);
}
@Override
public void onAbilityValueUpdate() {
super.onAbilityValueUpdate();
// TODO: Replace with a proper implementation/call.
// Check if the condition for 35303 is met.
if (this.getGlobalAbilityValues().containsKey("_ABILITY_UziExplode_Count")) {
var count = this.getGlobalAbilityValues().get("_ABILITY_UziExplode_Count");
if (count == 2f) {
this.getGlobalAbilityValues().remove("_ABILITY_UziExplode_Count");
this.getPlayer().getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_SKILL, 10006);
}
}
}
}

View File

@@ -1,35 +1,79 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.data.binout.ConfigGadget;
import static emu.grasscutter.scripts.constants.EventType.EVENT_SPECIFIC_GADGET_HP_CHANGE;
import emu.grasscutter.data.binout.config.ConfigEntityGadget;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.utils.Position;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.event.entity.EntityDamageEvent;
import lombok.Getter;
public abstract class EntityBaseGadget extends GameEntity {
@Getter(onMethod_ = @Override)
protected final Position position;
@Getter(onMethod_ = @Override)
protected final Position rotation;
@Getter private final int campId;
@Getter private final int campType;
public EntityBaseGadget(Scene scene) {
this(scene, null, null);
}
public EntityBaseGadget(Scene scene, Position position, Position rotation) {
this(scene, position, rotation, 0, 0);
}
public EntityBaseGadget(
Scene scene, Position position, Position rotation, int campId, int campType) {
super(scene);
this.position = position != null ? position.clone() : new Position();
this.rotation = rotation != null ? rotation.clone() : new Position();
this.campId = campId;
this.campType = campType;
}
public abstract int getGadgetId();
@Override
public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method.
public int getEntityTypeId() {
return this.getGadgetId();
}
protected void fillFightProps(ConfigGadget configGadget) {
@Override
public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method.
getScene()
.getPlayers()
.forEach(
p ->
p.getQuestManager()
.queueEvent(QuestContent.QUEST_CONTENT_DESTROY_GADGET, this.getGadgetId()));
}
@Override
public void runLuaCallbacks(EntityDamageEvent event) {
super.runLuaCallbacks(event);
getScene()
.getScriptManager()
.callEvent(
new ScriptArgs(
this.getGroupId(),
EVENT_SPECIFIC_GADGET_HP_CHANGE,
getConfigId(),
getGadgetId())
.setSourceEntityId(getId())
.setParam3((int) this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP))
.setEventSource(Integer.toString(getConfigId())));
}
protected void fillFightProps(ConfigEntityGadget configGadget) {
if (configGadget == null || configGadget.getCombat() == null) {
return;
}

View File

@@ -1,7 +1,12 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.config.ConfigEntityGadget;
import emu.grasscutter.data.binout.config.fields.ConfigAbilityData;
import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
@@ -17,8 +22,7 @@ import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import emu.grasscutter.utils.helpers.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;
@@ -28,32 +32,56 @@ public class EntityClientGadget extends EntityBaseGadget {
@Getter(onMethod_ = @Override)
private int gadgetId;
@Getter private int campId;
@Getter private int campType;
@Getter private int ownerEntityId;
@Getter private int targetEntityId;
@Getter private boolean asyncLoad;
@Getter private int originalOwnerEntityId;
@Getter private final GadgetData gadgetData;
private ConfigEntityGadget configGadget;
public EntityClientGadget(Scene scene, Player player, EvtCreateGadgetNotify notify) {
super(scene, new Position(notify.getInitPos()), new Position(notify.getInitEulerAngles()));
super(
scene,
new Position(notify.getInitPos()),
new Position(notify.getInitEulerAngles()),
notify.getCampId(),
notify.getCampType());
this.owner = player;
this.id = notify.getEntityId();
this.gadgetId = notify.getConfigId();
this.campId = notify.getCampId();
this.campType = notify.getCampType();
this.ownerEntityId = notify.getPropOwnerEntityId();
this.targetEntityId = notify.getTargetEntityId();
this.asyncLoad = notify.getIsAsyncLoad();
this.gadgetData = GameData.getGadgetDataMap().get(gadgetId);
if (gadgetData != null && gadgetData.getJsonName() != null) {
this.configGadget = GameData.getGadgetConfigData().get(gadgetData.getJsonName());
}
GameEntity owner = scene.getEntityById(this.ownerEntityId);
if (owner instanceof EntityClientGadget ownerGadget) {
this.originalOwnerEntityId = ownerGadget.getOriginalOwnerEntityId();
}
else {
} else {
this.originalOwnerEntityId = this.ownerEntityId;
}
this.initAbilities();
}
@Override
public void initAbilities() {
if (this.configGadget != null && this.configGadget.getAbilities() != null) {
for (var ability : this.configGadget.getAbilities()) {
addConfigAbility(ability);
}
}
}
private void addConfigAbility(ConfigAbilityData abilityData) {
var data = GameData.getAbilityData(abilityData.getAbilityName());
if (data != null) owner.getAbilityManager().addAbilityToEntity(this, data);
}
@Override
@@ -61,47 +89,60 @@ public class EntityClientGadget extends EntityBaseGadget {
super.onDeath(killerId); // Invoke super class's onDeath() method.
}
@Override public Int2FloatMap getFightProperties() {return null;}
@Override
public Int2FloatMap getFightProperties() {
return null;
}
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder())
.build();
EntityAuthorityInfo authority =
EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.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)
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
SceneEntityInfo.Builder entityInfo =
SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo(
MotionInfo.newBuilder()
.setPos(getPosition().toProto())
.setRot(getRotation().toProto())
.setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
.build();
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())
.setOwnerEntityId(this.getOwnerEntityId())
.setTargetEntityId(this.getTargetEntityId())
.setAsyncLoad(this.isAsyncLoad())
.build();
ClientGadgetInfoOuterClass.ClientGadgetInfo clientGadget =
ClientGadgetInfoOuterClass.ClientGadgetInfo.newBuilder()
.setCampId(this.getCampId())
.setCampType(this.getCampType())
.setOwnerEntityId(this.getOwnerEntityId())
.setTargetEntityId(this.getTargetEntityId())
.setAsyncLoad(this.isAsyncLoad())
.build();
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
.setOwnerEntityId(this.getOwnerEntityId())
.setIsEnableInteract(true)
.setClientGadget(clientGadget)
.setPropOwnerEntityId(this.getOwnerEntityId())
.setAuthorityPeerId(this.getOwner().getPeerId());
SceneGadgetInfo.Builder gadgetInfo =
SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
.setOwnerEntityId(this.getOwnerEntityId())
.setIsEnableInteract(true)
.setClientGadget(clientGadget)
.setPropOwnerEntityId(this.getOwnerEntityId())
.setAuthorityPeerId(this.getOwner().getPeerId());
entityInfo.setGadget(gadgetInfo);

View File

@@ -1,13 +1,18 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.ConfigGadget;
import emu.grasscutter.data.binout.config.ConfigEntityGadget;
import emu.grasscutter.data.binout.config.fields.ConfigAbilityData;
import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.entity.gadget.*;
import emu.grasscutter.game.entity.gadget.platform.BaseRoute;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.SceneGroupInstance;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
@@ -15,42 +20,61 @@ import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
import emu.grasscutter.net.proto.PlatformInfoOuterClass;
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType;
import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.net.proto.VisionTypeOuterClass;
import emu.grasscutter.scripts.EntityControllerScriptManager;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketGadgetStateNotify;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import emu.grasscutter.server.packet.send.PacketPlatformStartRouteNotify;
import emu.grasscutter.server.packet.send.PacketPlatformStopRouteNotify;
import emu.grasscutter.server.packet.send.PacketSceneTimeNotify;
import emu.grasscutter.utils.helpers.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.Optional;
import javax.annotation.Nullable;
@ToString(callSuper = true)
public class EntityGadget extends EntityBaseGadget {
@Getter private final GadgetData gadgetData;
@Getter(onMethod_ = @Override) @Setter
@Getter(onMethod_ = @Override)
@Setter
private int gadgetId;
@Getter @Setter private int state;
@Getter private final Position bornPos;
@Getter private final Position bornRot;
@Getter @Setter private GameEntity owner = null;
@Getter @Setter private List<GameEntity> children = new ArrayList<>();
@Getter private int state;
@Getter @Setter private int pointType;
@Getter private GadgetContent content;
@Getter(onMethod_ = @Override, lazy = true)
private final Int2FloatMap fightProperties = new Int2FloatOpenHashMap();
@Getter @Setter private SceneGadget metaGadget;
@Nullable @Getter
private ConfigGadget configGadget;
@Nullable @Getter ConfigEntityGadget configGadget;
@Getter @Setter private BaseRoute routeConfig;
@Getter @Setter private int stopValue = 0; // Controller related, inited to zero
@Getter @Setter private int startValue = 0; // Controller related, inited to zero
@Getter @Setter private int ticksSinceChange;
@Getter private boolean interactEnabled = true;
public EntityGadget(Scene scene, int gadgetId, Position pos) {
this(scene, gadgetId, pos, null, null);
@@ -60,20 +84,91 @@ public class EntityGadget extends EntityBaseGadget {
this(scene, gadgetId, pos, rot, null);
}
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) {
super(scene, pos, rot);
public EntityGadget(
Scene scene, int gadgetId, Position pos, Position rot, int campId, int campType) {
this(scene, gadgetId, pos, rot, null, campId, campType);
}
public EntityGadget(
Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) {
this(scene, gadgetId, pos, rot, content, 0, 0);
}
public EntityGadget(
Scene scene,
int gadgetId,
Position pos,
Position rot,
GadgetContent content,
int campId,
int campType) {
super(scene, pos, rot, campId, campType);
this.gadgetData = GameData.getGadgetDataMap().get(gadgetId);
this.configGadget = Optional.ofNullable(this.gadgetData).map(GadgetData::getJsonName).map(GameData.getGadgetConfigData()::get).orElse(null);
if (gadgetData != null && gadgetData.getJsonName() != null) {
this.configGadget = GameData.getGadgetConfigData().get(gadgetData.getJsonName());
}
this.id = this.getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.gadgetId = gadgetId;
this.content = content;
fillFightProps(configGadget);
this.bornPos = this.getPosition().clone();
this.bornRot = this.getRotation().clone();
this.fillFightProps(configGadget);
if (GameData.getGadgetMappingMap().containsKey(gadgetId)) {
var controllerName = GameData.getGadgetMappingMap().get(gadgetId).getServerController();
this.setEntityController(EntityControllerScriptManager.getGadgetController(controllerName));
if (this.getEntityController() == null) {
Grasscutter.getLogger().warn("Gadget controller {} not found.", controllerName);
}
}
this.initAbilities(); // TODO: move this
}
private void addConfigAbility(ConfigAbilityData abilityData) {
var data = GameData.getAbilityData(abilityData.getAbilityName());
if (data != null)
this.getScene().getWorld().getHost().getAbilityManager().addAbilityToEntity(this, data);
}
@Override
public void initAbilities() {
// TODO: handle pre-dynamic, static and dynamic here
if (this.configGadget != null && this.configGadget.getAbilities() != null) {
for (var ability : this.configGadget.getAbilities()) {
this.addConfigAbility(ability);
}
}
}
public void setInteractEnabled(boolean enable) {
this.interactEnabled = enable;
this.getScene()
.broadcastPacket(new PacketGadgetStateNotify(this, this.getState())); // Update the interact
}
public void setState(int state) {
this.state = state;
// Cache the gadget state
if (metaGadget != null && metaGadget.group != null) {
var instance = getScene().getScriptManager().getCachedGroupInstanceById(metaGadget.group.id);
if (instance != null) instance.cacheGadgetState(metaGadget, state);
}
}
public void updateState(int state) {
if (state == this.getState()) return; // Don't triggers events
this.setState(state);
ticksSinceChange = getScene().getSceneTimeSeconds();
this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state));
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_STATE_CHANGE, new ScriptArgs(state, this.getConfigId()));
getScene()
.getScriptManager()
.callEvent(
new ScriptArgs(
this.getGroupId(), EventType.EVENT_GADGET_STATE_CHANGE, state, this.getConfigId()));
}
@Deprecated(forRemoval = true) // Dont use!
@@ -83,23 +178,28 @@ public class EntityGadget extends EntityBaseGadget {
// TODO refactor
public void buildContent() {
if (this.getContent() != null || this.getGadgetData() == null || this.getGadgetData().getType() == null) {
if (this.getContent() != null
|| this.getGadgetData() == null
|| this.getGadgetData().getType() == null) {
return;
}
this.content = switch (this.getGadgetData().getType()) {
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);
case Gadget -> new GadgetObject(this);
default -> null;
};
this.content =
switch (this.getGadgetData().getType()) {
case GatherPoint -> new GadgetGatherPoint(this);
case GatherObject -> new GadgetGatherObject(this);
case Worktop, SealGadget -> new GadgetWorktop(this);
case RewardStatue -> new GadgetRewardStatue(this);
case Chest -> new GadgetChest(this);
case Gadget -> new GadgetObject(this);
default -> null;
};
}
@Override
public void onInteract(Player player, GadgetInteractReq interactReq) {
if (!this.interactEnabled) return;
if (this.getContent() == null) {
return;
}
@@ -114,7 +214,19 @@ public class EntityGadget extends EntityBaseGadget {
@Override
public void onCreate() {
// Lua event
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(this.getConfigId()));
getScene()
.getScriptManager()
.callEvent(
new ScriptArgs(this.getGroupId(), EventType.EVENT_GADGET_CREATE, this.getConfigId()));
}
@Override
public void onRemoved() {
super.onRemoved();
if (!children.isEmpty()) {
getScene().removeEntities(children, VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE);
children.clear();
}
}
@Override
@@ -127,56 +239,119 @@ public class EntityGadget extends EntityBaseGadget {
if (getScene().getChallenge() != null) {
getScene().getChallenge().onGadgetDeath(this);
}
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_GADGET_DIE, new ScriptArgs(this.getConfigId()));
getScene()
.getScriptManager()
.callEvent(
new ScriptArgs(this.getGroupId(), EventType.EVENT_ANY_GADGET_DIE, this.getConfigId()));
SceneGroupInstance groupInstance =
getScene().getScriptManager().getCachedGroupInstanceById(this.getGroupId());
if (groupInstance != null && metaGadget != null)
groupInstance.getDeadEntities().add(metaGadget.config_id);
}
public boolean startPlatform() {
if (routeConfig == null) {
return false;
}
if (routeConfig.isStarted()) {
return true;
}
getScene().broadcastPacket(new PacketSceneTimeNotify(getScene()));
routeConfig.startRoute(getScene());
getScene().broadcastPacket(new PacketPlatformStartRouteNotify(this));
return true;
}
public boolean stopPlatform() {
if (routeConfig == null) {
return false;
}
if (!routeConfig.isStarted()) {
return true;
}
routeConfig.stopRoute(getScene());
getScene().broadcastPacket(new PacketPlatformStopRouteNotify(this));
return true;
}
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder())
.build();
EntityAuthorityInfo authority =
EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(
SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(bornPos.toProto()))
.setBornPos(bornPos.toProto())
.build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
SceneEntityInfo.Builder entityInfo =
SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo(
MotionInfo.newBuilder()
.setPos(getPosition().toProto())
.setRot(getRotation().toProto())
.setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
.build();
PropPair pair =
PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
.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
// We do not use the getter to null check because the getter will create a fight prop map if it
// is null
if (this.fightProperties != null) {
addAllFightPropsToEntityInfo(entityInfo);
}
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
.setGroupId(this.getGroupId())
.setConfigId(this.getConfigId())
.setGadgetState(this.getState())
.setIsEnableInteract(true)
.setAuthorityPeerId(this.getScene().getWorld().getHostPeerId());
var gadgetInfo =
SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
.setGroupId(this.getGroupId())
.setConfigId(this.getConfigId())
.setGadgetState(this.getState())
.setIsEnableInteract(this.interactEnabled)
.setAuthorityPeerId(this.getScene().getWorld().getHostPeerId());
if (this.metaGadget != null) {
gadgetInfo.setDraftId(this.metaGadget.draft_id);
}
if (owner != null) {
gadgetInfo.setOwnerEntityId(owner.getId());
}
if (this.getContent() != null) {
this.getContent().onBuildProto(gadgetInfo);
}
if (routeConfig != null) {
gadgetInfo.setPlatform(getPlatformInfo());
}
entityInfo.setGadget(gadgetInfo);
return entityInfo.build();
}
public PlatformInfoOuterClass.PlatformInfo.Builder getPlatformInfo() {
if (routeConfig != null) {
return routeConfig.toProto();
}
return PlatformInfoOuterClass.PlatformInfo.newBuilder();
}
}

View File

@@ -6,6 +6,7 @@ import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
@@ -23,8 +24,7 @@ import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import emu.grasscutter.utils.helpers.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;
@@ -37,13 +37,17 @@ public class EntityItem extends EntityBaseGadget {
this(scene, player, itemData, pos, count, 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
// 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) {
public EntityItem(
Scene scene, Player player, ItemData itemData, Position pos, int count, boolean share) {
super(scene, pos, null);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
this.guid =
player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
this.item = new GameItem(itemData, count);
this.share = share;
}
@@ -61,7 +65,10 @@ public class EntityItem extends EntityBaseGadget {
return this.getItemData().getGadgetId();
}
@Override public Int2FloatMap getFightProperties() {return null;}
@Override
public Int2FloatMap getFightProperties() {
return null;
}
@Override
public void onInteract(Player player, GadgetInteractReq interactReq) {
@@ -82,44 +89,61 @@ public class EntityItem extends EntityBaseGadget {
if (!this.isShare()) { // not shared drop
player.sendPacket(new PacketGadgetInteractRsp(this, InteractType.INTERACT_TYPE_PICK_ITEM));
} else {
this.getScene().broadcastPacket(new PacketGadgetInteractRsp(this, InteractType.INTERACT_TYPE_PICK_ITEM));
this.getScene()
.broadcastPacket(
new PacketGadgetInteractRsp(this, InteractType.INTERACT_TYPE_PICK_ITEM));
}
}
}
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder())
.build();
EntityAuthorityInfo authority =
EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.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)
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
SceneEntityInfo.Builder entityInfo =
SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo(
MotionInfo.newBuilder()
.setPos(getPosition().toProto())
.setRot(getRotation().toProto())
.setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
.build();
PropPair pair =
PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
.build();
entityInfo.addPropList(pair);
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
.setGadgetId(this.getItemData().getGadgetId())
.setTrifleItem(this.getItem().toProto())
.setBornType(GadgetBornType.GADGET_BORN_TYPE_IN_AIR)
.setAuthorityPeerId(this.getWorld().getHostPeerId())
.setIsEnableInteract(true);
SceneGadgetInfo.Builder gadgetInfo =
SceneGadgetInfo.newBuilder()
.setGadgetId(this.getItemData().getGadgetId())
.setTrifleItem(this.getItem().toProto())
.setBornType(GadgetBornType.GADGET_BORN_TYPE_IN_AIR)
.setAuthorityPeerId(this.getWorld().getHostPeerId())
.setIsEnableInteract(true);
entityInfo.setGadget(gadgetInfo);
return entityInfo.build();
}
@Override
public void initAbilities() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'initAbilities'");
}
}

View File

@@ -1,20 +1,19 @@
package emu.grasscutter.game.entity;
import java.util.Optional;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.AbilityData;
import emu.grasscutter.data.binout.config.ConfigEntityMonster;
import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.data.excels.EnvAnimalGatherConfigData;
import emu.grasscutter.data.excels.MonsterCurveData;
import emu.grasscutter.data.excels.MonsterData;
import emu.grasscutter.data.excels.monster.MonsterCurveData;
import emu.grasscutter.data.excels.monster.MonsterData;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.props.*;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.data.excels.MonsterSpecialNameData;
import emu.grasscutter.game.world.SceneGroupInstance;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
@@ -29,15 +28,22 @@ import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.SceneMonsterInfoOuterClass.SceneMonsterInfo;
import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneMonster;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import emu.grasscutter.server.event.entity.EntityDamageEvent;
import emu.grasscutter.utils.helpers.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import lombok.Getter;
import lombok.Setter;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static emu.grasscutter.scripts.constants.EventType.EVENT_SPECIFIC_MONSTER_HP_CHANGE;
public class EntityMonster extends GameEntity {
@Getter private final MonsterData monsterData;
@Getter(onMethod_ = @Override)
private final Int2FloatOpenHashMap fightProperties;
@@ -45,12 +51,17 @@ public class EntityMonster extends GameEntity {
private final Position position;
@Getter(onMethod_ = @Override)
private final Position rotation;
@Getter private final MonsterData monsterData;
@Getter private final ConfigEntityMonster configEntityMonster;
@Getter private final Position bornPos;
@Getter private final int level;
private int weaponEntityId;
@Getter private EntityWeapon weaponEntity;
@Getter @Setter private int poseId;
@Getter @Setter private int aiId = -1;
@Getter private List<Player> playerOnBattle;
@Nullable @Getter @Setter private SceneMonster metaMonster;
public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) {
super(scene);
this.id = getWorld().getNextEntityId(EntityIdType.MONSTER);
@@ -60,13 +71,107 @@ public class EntityMonster extends GameEntity {
this.rotation = new Position();
this.bornPos = getPosition().clone();
this.level = level;
this.playerOnBattle = new ArrayList<>();
if(GameData.getMonsterMappingMap().containsKey(getMonsterId())) {
this.configEntityMonster = GameData.getMonsterConfigData().get(GameData.getMonsterMappingMap().get(getMonsterId()).getMonsterJson());
} else {
this.configEntityMonster = null;
}
// Monster weapon
if (getMonsterWeaponId() > 0) {
this.weaponEntityId = getWorld().getNextEntityId(EntityIdType.WEAPON);
this.weaponEntity = new EntityWeapon(scene, getMonsterWeaponId());
scene.getWeaponEntities().put(this.weaponEntity.getId(), this.weaponEntity);
//this.weaponEntityId = getWorld().getNextEntityId(EntityIdType.WEAPON);
}
this.recalcStats();
initAbilities();
}
private void addConfigAbility(String name){
AbilityData data = GameData.getAbilityData(name);
if(data != null)
getScene().getWorld().getHost().getAbilityManager().addAbilityToEntity(
this, data);
}
@Override
public void initAbilities() {
if(configEntityMonster != null) {
// Affix abilities
var optionalGroup = getScene().getLoadedGroups().stream()
.filter(g -> g.id == this.getGroupId())
.findAny();
List<Integer> affixes = null;
if (optionalGroup.isPresent()) {
var group = optionalGroup.get();
SceneMonster monster = group.monsters.get(getConfigId());
if(monster != null) affixes = monster.affix;
}
if (affixes != null) {
for(var affixId : affixes) {
var affix = GameData.getMonsterAffixDataMap().get(affixId.intValue());
if (!affix.isPreAdd()) continue;
//Add the ability
for(var name : affix.getAbilityName()) {
this.addConfigAbility(name);
}
}
}
//TODO: Research if any monster is non humanoid
for(var ability : GameData.getConfigGlobalCombat().getDefaultAbilities().getNonHumanoidMoveAbilities()) {
this.addConfigAbility(ability);
}
if (configEntityMonster.getAbilities() != null)
for (var configAbilityData : configEntityMonster.getAbilities()) {
this.addConfigAbility(configAbilityData.abilityName);
}
if (optionalGroup.isPresent()) {
var group = optionalGroup.get();
SceneMonster monster = group.monsters.get(getConfigId());
if(monster != null && monster.isElite) {
addConfigAbility(GameData.getConfigGlobalCombat().getDefaultAbilities().getMonterEliteAbilityName());
}
}
if (affixes != null) {
for (var affixId : affixes) {
var affix = GameData.getMonsterAffixDataMap().get(affixId.intValue());
if(affix.isPreAdd()) continue;
//Add the ability
for(var name : affix.getAbilityName()) {
this.addConfigAbility(name);
}
}
}
var levelEntityConfig = getScene().getSceneData().getLevelEntityConfig();
var config = GameData.getConfigLevelEntityDataMap().get(levelEntityConfig);
if (config == null){
return;
}
if (config.getMonsterAbilities() != null) {
for (var monsterAbility : config.getMonsterAbilities()) {
addConfigAbility(monsterAbility.abilityName);
}
}
}
}
@Override
public int getEntityTypeId() {
return getMonsterId();
}
public int getMonsterWeaponId() {
@@ -98,16 +203,16 @@ public class EntityMonster extends GameEntity {
@Override
public void onCreate() {
// Lua event
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(this.getConfigId()));
getScene().getScriptManager().callEvent(new ScriptArgs(this.getGroupId(), EventType.EVENT_ANY_MONSTER_LIVE, this.getConfigId()));
}
@Override
public void damage(float amount, int killerId) {
public void damage(float amount, int killerId, ElementType attackType) {
// Get HP before damage.
float hpBeforeDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
// Apply damage.
super.damage(amount, killerId);
super.damage(amount, killerId, attackType);
// Get HP after damage.
float hpAfterDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
@@ -118,6 +223,15 @@ public class EntityMonster extends GameEntity {
}
}
@Override
public void runLuaCallbacks(EntityDamageEvent event) {
super.runLuaCallbacks(event);
getScene().getScriptManager().callEvent(new ScriptArgs(this.getGroupId(), EVENT_SPECIFIC_MONSTER_HP_CHANGE, getConfigId(), monsterData.getId())
.setSourceEntityId(getId())
.setParam3((int) this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP))
.setEventSource(Integer.toString(getConfigId())));
}
@Override
public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method.
@@ -134,11 +248,26 @@ public class EntityMonster extends GameEntity {
Optional.ofNullable(scriptManager.getScriptMonsterSpawnService()).ifPresent(s -> s.onMonsterDead(this));
// prevent spawn monster after success
if (challenge.map(c -> c.inProgress()).orElse(true))
scriptManager.callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
/*if (challenge.map(c -> c.inProgress()).orElse(true)) {
scriptManager.callEvent(new ScriptArgs(EventType.EVENT_ANY_MONSTER_DIE, this.getConfigId()).setGroupId(this.getGroupId()));
} else if (getScene().getChallenge() == null) {
}*/
scriptManager.callEvent(new ScriptArgs(this.getGroupId(), EventType.EVENT_ANY_MONSTER_DIE, this.getConfigId()));
}
// Battle Pass trigger
scene.getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_MONSTER_DIE, this.getMonsterId(), 1));
scene.getPlayers().forEach(p -> p.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_MONSTER_DIE, this.getMonsterId()));
scene.getPlayers().forEach(p -> p.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_KILL_MONSTER, this.getMonsterId()));
scene.getPlayers().forEach(p -> p.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_CLEAR_GROUP_MONSTER, this.getGroupId()));
SceneGroupInstance groupInstance = scene.getScriptManager().getGroupInstanceById(this.getGroupId());
if(groupInstance != null && metaMonster != null)
groupInstance.getDeadEntities().add(metaMonster.config_id);
scene.triggerDungeonEvent(DungeonPassConditionType.DUNGEON_COND_KILL_GROUP_MONSTER, this.getGroupId());
scene.triggerDungeonEvent(DungeonPassConditionType.DUNGEON_COND_KILL_TYPE_MONSTER, this.getMonsterData().getType().getValue());
scene.triggerDungeonEvent(DungeonPassConditionType.DUNGEON_COND_KILL_MONSTER, this.getMonsterId());
}
public void recalcStats() {
@@ -173,54 +302,63 @@ public class EntityMonster extends GameEntity {
@Override
public SceneEntityInfo toProto() {
var data = this.getMonsterData();
var authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(this.getBornPos().toProto()))
.setBornPos(this.getBornPos().toProto())
.build();
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder()
.setIsAiOpen(true)
.setBornPos(this.getBornPos().toProto()))
.setBornPos(this.getBornPos().toProto())
.build();
var entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_MONSTER)
.setMotionInfo(this.getMotionInfo())
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(this.getLifeState().getValue());
.setEntityId(this.getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_MONSTER)
.setMotionInfo(this.getMotionInfo())
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(this.getLifeState().getValue());
this.addAllFightPropsToEntityInfo(entityInfo);
entityInfo.addPropList(PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getLevel()))
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, this.getLevel()))
.build());
var monsterInfo = SceneMonsterInfo.newBuilder()
.setMonsterId(getMonsterId())
.setGroupId(this.getGroupId())
.setConfigId(this.getConfigId())
.addAllAffixList(getMonsterData().getAffix())
.setAuthorityPeerId(getWorld().getHostPeerId())
.setPoseId(this.getPoseId())
.setBlockId(getScene().getId())
.setBornType(MonsterBornType.MONSTER_BORN_TYPE_DEFAULT);
.setMonsterId(getMonsterId())
.setGroupId(this.getGroupId())
.setConfigId(this.getConfigId())
.addAllAffixList(data.getAffix())
.setAuthorityPeerId(this.getWorld().getHostPeerId())
.setPoseId(this.getPoseId())
.setBlockId(this.getScene().getId())
.setBornType(MonsterBornType.MONSTER_BORN_TYPE_DEFAULT);
if (getMonsterData().getDescribeData() != null) {
monsterInfo.setTitleId(getMonsterData().getDescribeData().getTitleId())
.setSpecialNameId(getMonsterData().getSpecialNameId());
if (this.metaMonster != null) {
if (this.metaMonster.special_name_id != 0) {
monsterInfo.setTitleId(this.metaMonster.title_id)
.setSpecialNameId(this.metaMonster.special_name_id);
} else if (data.getDescribeData() != null) {
monsterInfo.setTitleId(data.getDescribeData().getTitleId())
.setSpecialNameId(data.getSpecialNameId());
}
}
if (this.getMonsterWeaponId() > 0) {
SceneWeaponInfo weaponInfo = SceneWeaponInfo.newBuilder()
.setEntityId(this.weaponEntityId)
.setGadgetId(this.getMonsterWeaponId())
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.build();
.setEntityId(this.getWeaponEntity() != null ? this.getWeaponEntity().getId() : 0)
.setGadgetId(this.getMonsterWeaponId())
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.build();
monsterInfo.addWeaponList(weaponInfo);
}
if (this.aiId!=-1) {
if (this.aiId != -1) {
monsterInfo.setAiConfigId(aiId);
}
@@ -228,4 +366,4 @@ public class EntityMonster extends GameEntity {
return entityInfo.build();
}
}
}

View File

@@ -1,18 +1,20 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.*;
import emu.grasscutter.scripts.data.SceneNPC;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;
public class EntityNPC extends GameEntity{
public class EntityNPC extends GameEntity {
@Getter(onMethod_ = @Override)
private final Position position;
@Getter(onMethod_ = @Override)
private final Position rotation;
private final SceneNPC metaNpc;
@Getter private final int suiteId;
@@ -26,41 +28,61 @@ public class EntityNPC extends GameEntity{
this.position = metaNPC.pos.clone();
this.rotation = metaNPC.rot.clone();
this.metaNpc = metaNPC;
}
@Override public Int2FloatMap getFightProperties() {return null;}
@Override
public int getEntityTypeId() {
return this.metaNpc.npc_id;
}
@Override
public Int2FloatMap getFightProperties() {
return null;
}
@Override
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
EntityAuthorityInfoOuterClass.EntityAuthorityInfo authority = EntityAuthorityInfoOuterClass.EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfoOuterClass.SceneEntityAiInfo.newBuilder()
.setIsAiOpen(true)
.setBornPos(getPosition().toProto()))
.setBornPos(getPosition().toProto())
.build();
EntityAuthorityInfoOuterClass.EntityAuthorityInfo authority =
EntityAuthorityInfoOuterClass.EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(
EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo.newBuilder())
.setAiInfo(
SceneEntityAiInfoOuterClass.SceneEntityAiInfo.newBuilder()
.setIsAiOpen(true)
.setBornPos(getPosition().toProto()))
.setBornPos(getPosition().toProto())
.build();
SceneEntityInfoOuterClass.SceneEntityInfo.Builder entityInfo = SceneEntityInfoOuterClass.SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityTypeOuterClass.ProtEntityType.PROT_ENTITY_TYPE_NPC)
.setMotionInfo(MotionInfoOuterClass.MotionInfo.newBuilder()
.setPos(getPosition().toProto())
.setRot(getRotation().toProto())
.setSpeed(VectorOuterClass.Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientDataOuterClass.EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
SceneEntityInfoOuterClass.SceneEntityInfo.Builder entityInfo =
SceneEntityInfoOuterClass.SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityTypeOuterClass.ProtEntityType.PROT_ENTITY_TYPE_NPC)
.setMotionInfo(
MotionInfoOuterClass.MotionInfo.newBuilder()
.setPos(getPosition().toProto())
.setRot(getRotation().toProto())
.setSpeed(VectorOuterClass.Vector.newBuilder()))
.addAnimatorParaList(
AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair
.newBuilder())
.setEntityClientData(EntityClientDataOuterClass.EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
entityInfo.setNpc(SceneNpcInfoOuterClass.SceneNpcInfo.newBuilder()
entityInfo.setNpc(
SceneNpcInfoOuterClass.SceneNpcInfo.newBuilder()
.setNpcId(metaNpc.npc_id)
.setBlockId(getBlockId())
.build());
.build());
return entityInfo.build();
}
@Override
public void initAbilities() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'initAbilities'");
}
}

View File

@@ -1,49 +1,52 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass;
import emu.grasscutter.scripts.data.SceneRegion;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import lombok.Getter;
@Getter
public class EntityRegion extends GameEntity{
public class EntityRegion extends GameEntity {
private final Position position;
private boolean hasNewEntities;
private boolean entityLeave;
private final Set<Integer> entities; // Ids of entities inside this region
private final SceneRegion metaRegion;
private boolean entityEnter;
private boolean entityLeave;
public EntityRegion(Scene scene, SceneRegion region) {
super(scene);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.REGION);
setGroupId(region.group.id);
setBlockId(region.group.block_id);
setConfigId(region.config_id);
this.setGroupId(region.group.id);
this.setBlockId(region.group.block_id);
this.setConfigId(region.config_id);
this.position = region.pos.clone();
this.entities = ConcurrentHashMap.newKeySet();
this.metaRegion = region;
}
@Override
public int getEntityTypeId() {
return this.metaRegion.config_id;
}
public void resetNewEntities() {
this.entityEnter = false;
this.entityLeave = false;
}
public void addEntity(GameEntity entity) {
if (this.getEntities().contains(entity.getId())) {
return;
}
this.getEntities().add(entity.getId());
this.hasNewEntities = true;
}
public boolean hasNewEntities() {
return hasNewEntities;
}
public void resetNewEntities() {
hasNewEntities = false;
this.entityEnter = true;
}
public void removeEntity(int entityId) {
@@ -52,26 +55,45 @@ public class EntityRegion extends GameEntity{
}
public void removeEntity(GameEntity entity) {
this.getEntities().remove(entity.getId());
this.entityLeave = true;
this.removeEntity(entity.getId());
}
public boolean entityLeave() {return this.entityLeave;}
public void resetEntityLeave() {this.entityLeave = false;}
@Override public Int2FloatMap getFightProperties() {return null;}
@Override public Position getPosition() {return position;}
public boolean entityHasEntered() {
return this.entityEnter;
}
@Override public Position getRotation() {return null;}
public boolean entityHasLeft() {
return this.entityLeave;
}
@Override
public Int2FloatMap getFightProperties() {
return null;
}
@Override
public Position getPosition() {
return position;
}
@Override
public Position getRotation() {
return null;
}
@Override
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
/**
* The Region Entity would not be sent to client.
*/
/** The Region Entity would not be sent to client. */
return null;
}
public int getFirstEntityId() {
return entities.stream().findFirst().orElse(0);
}
@Override
public void initAbilities() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'initAbilities'");
}
}

View File

@@ -0,0 +1,55 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.AbilityData;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import it.unimi.dsi.fastutil.ints.Int2FloatArrayMap;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
public class EntityScene extends GameEntity {
public EntityScene(Scene scene) {
super(scene);
initAbilities();
}
@Override
public void initAbilities() {
// Load abilities from levelElementAbilities
for (var ability :
GameData.getConfigGlobalCombat().getDefaultAbilities().getLevelElementAbilities()) {
AbilityData data = GameData.getAbilityData(ability);
if (data != null)
getScene().getWorld().getHost().getAbilityManager().addAbilityToEntity(this, data);
}
}
@Override
public int getEntityTypeId() {
return 0x13;
}
@Override
public Int2FloatMap getFightProperties() {
// TODO
return new Int2FloatArrayMap();
}
@Override
public Position getPosition() {
// TODO Auto-generated method stub
return new Position(0, 0, 0);
}
@Override
public Position getRotation() {
return new Position(0, 0, 0);
}
@Override
public SceneEntityInfo toProto() {
return null;
}
}

View File

@@ -1,6 +1,5 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.game.entity.platform.EntityPlatform;
import emu.grasscutter.game.entity.platform.EntitySolarIsotomaElevatorPlatform;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.Scene;
@@ -10,22 +9,25 @@ import lombok.Getter;
public class EntitySolarIsotomaClientGadget extends EntityClientGadget {
public static final int GADGET_ID = 41038001;
public static final int ELEVATOR_GADGET_ID = 41038002;
@Getter private EntityPlatform platformGadget;
@Getter private EntityGadget platformGadget;
public EntitySolarIsotomaClientGadget(Scene scene, Player player, EvtCreateGadgetNotifyOuterClass.EvtCreateGadgetNotify notify) {
public EntitySolarIsotomaClientGadget(
Scene scene, Player player, EvtCreateGadgetNotifyOuterClass.EvtCreateGadgetNotify notify) {
super(scene, player, notify);
}
@Override
public void onCreate() {
//Create solar isotoma elevator and send to all.
this.platformGadget = new EntitySolarIsotomaElevatorPlatform(this, getScene(), getOwner(), ELEVATOR_GADGET_ID, getPosition(), getRotation());
// Create solar isotoma elevator and send to all.
this.platformGadget =
new EntitySolarIsotomaElevatorPlatform(
this, getScene(), ELEVATOR_GADGET_ID, getPosition(), getRotation());
getScene().addEntity(this.platformGadget);
}
@Override
public void onRemoved() {
//Remove solar isotoma elevator entity.
// Remove solar isotoma elevator entity.
getScene().removeEntity(this.platformGadget);
}
}

View File

@@ -0,0 +1,66 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.AbilityData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.game.world.World;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import it.unimi.dsi.fastutil.ints.Int2FloatArrayMap;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
public class EntityTeam extends GameEntity {
private Player player;
public EntityTeam(Player player) {
super(player.getScene());
initAbilities();
this.id = player.getWorld().getNextEntityId(EntityIdType.TEAM);
}
@Override
public void initAbilities() {
// Load abilities from levelElementAbilities
var defaultAbilities = GameData.getConfigGlobalCombat().getDefaultAbilities();
if (defaultAbilities.getDefaultTeamAbilities() != null)
for (var ability : defaultAbilities.getDefaultTeamAbilities()) {
AbilityData data = GameData.getAbilityData(ability);
if (data != null)
player.getWorld().getHost().getAbilityManager().addAbilityToEntity(this, data);
}
}
@Override
public World getWorld() {
return player.getWorld();
}
@Override
public int getEntityTypeId() {
return EntityIdType.TEAM.getId();
}
@Override
public Int2FloatMap getFightProperties() {
// TODO
return new Int2FloatArrayMap();
}
@Override
public Position getPosition() {
// TODO Auto-generated method stub
return new Position(0, 0, 0);
}
@Override
public Position getRotation() {
return new Position(0, 0, 0);
}
@Override
public SceneEntityInfo toProto() {
return null;
}
}

View File

@@ -1,12 +1,13 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.ConfigGadget;
import emu.grasscutter.data.binout.config.ConfigEntityGadget;
import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
@@ -21,20 +22,19 @@ import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.net.proto.VehicleInfoOuterClass.VehicleInfo;
import emu.grasscutter.net.proto.VehicleMemberOuterClass.VehicleMember;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import emu.grasscutter.utils.helpers.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import lombok.Getter;
import lombok.Setter;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import lombok.Getter;
import lombok.Setter;
public class EntityVehicle extends EntityBaseGadget {
@Getter private final Player owner;
@Getter(onMethod_ = @Override)
private final Int2FloatMap fightProperties;
@@ -43,9 +43,10 @@ public class EntityVehicle extends EntityBaseGadget {
@Getter @Setter private float curStamina;
@Getter private List<VehicleMember> vehicleMembers;
@Nullable @Getter private ConfigGadget configGadget;
@Nullable @Getter private ConfigEntityGadget configGadget;
public EntityVehicle(Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) {
public EntityVehicle(
Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) {
super(scene, pos, rot);
this.owner = player;
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
@@ -63,7 +64,7 @@ public class EntityVehicle extends EntityBaseGadget {
}
@Override
protected void fillFightProps(ConfigGadget configGadget) {
protected void fillFightProps(ConfigEntityGadget configGadget) {
super.fillFightProps(configGadget);
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_SPEED, 0);
this.addFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, 0);
@@ -72,41 +73,59 @@ public class EntityVehicle extends EntityBaseGadget {
@Override
public SceneEntityInfo toProto() {
VehicleInfo vehicle = VehicleInfo.newBuilder()
.setOwnerUid(this.owner.getUid())
.setCurStamina(getCurStamina())
.build();
VehicleInfo vehicle =
VehicleInfo.newBuilder()
.setOwnerUid(this.owner.getUid())
.setCurStamina(getCurStamina())
.build();
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(getPosition().toProto()))
.setBornPos(getPosition().toProto())
.build();
EntityAuthorityInfo authority =
EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(
SceneEntityAiInfo.newBuilder()
.setIsAiOpen(true)
.setBornPos(getPosition().toProto()))
.setBornPos(getPosition().toProto())
.build();
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
.setAuthorityPeerId(this.getOwner().getPeerId())
.setIsEnableInteract(true)
.setVehicleInfo(vehicle);
SceneGadgetInfo.Builder gadgetInfo =
SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
.setAuthorityPeerId(this.getOwner().getPeerId())
.setIsEnableInteract(true)
.setVehicleInfo(vehicle);
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setGadget(gadgetInfo)
.setEntityAuthorityInfo(authority)
.setLifeState(1);
SceneEntityInfo.Builder entityInfo =
SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo(
MotionInfo.newBuilder()
.setPos(getPosition().toProto())
.setRot(getRotation().toProto())
.setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setGadget(gadgetInfo)
.setEntityAuthorityInfo(authority)
.setLifeState(1);
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 47))
.build();
PropPair pair =
PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 47))
.build();
this.addAllFightPropsToEntityInfo(entityInfo);
entityInfo.addPropList(pair);
return entityInfo.build();
}
@Override
public void initAbilities() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'initAbilities'");
}
}

View File

@@ -0,0 +1,79 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.config.ConfigEntityGadget;
import emu.grasscutter.data.binout.config.fields.ConfigAbilityData;
import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.world.*;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.scripts.EntityControllerScriptManager;
import it.unimi.dsi.fastutil.ints.*;
import javax.annotation.Nullable;
import lombok.*;
@ToString(callSuper = true)
public class EntityWeapon extends EntityBaseGadget {
@Getter private final GadgetData gadgetData;
@Getter(onMethod_ = @Override)
@Setter
private int gadgetId;
@Nullable @Getter private ConfigEntityGadget configGadget;
@Getter(onMethod_ = @Override, lazy = true)
private final Int2FloatMap fightProperties = new Int2FloatOpenHashMap();
@Getter private final Position bornPos;
@Getter private final Position bornRot;
public EntityWeapon(Scene scene, int gadgetId) {
super(scene);
this.gadgetData = GameData.getGadgetDataMap().get(gadgetId);
if (gadgetData != null && gadgetData.getJsonName() != null) {
this.configGadget = GameData.getGadgetConfigData().get(gadgetData.getJsonName());
}
this.gadgetId = gadgetId;
this.bornPos = this.getPosition().clone();
this.bornRot = this.getRotation().clone();
this.fillFightProps(configGadget);
if (GameData.getGadgetMappingMap().containsKey(gadgetId)) {
var controllerName = GameData.getGadgetMappingMap().get(gadgetId).getServerController();
this.setEntityController(EntityControllerScriptManager.getGadgetController(controllerName));
if (getEntityController() == null) {
Grasscutter.getLogger().warn("Gadget controller {} not found.", controllerName);
}
}
this.id = scene.getWorld().getNextEntityId(EntityIdType.WEAPON);
Grasscutter.getLogger()
.trace("New weapon entity {} in scene {}.", this.id, this.getScene().getId());
this.initAbilities();
}
private void addConfigAbility(ConfigAbilityData abilityData) {
var data = GameData.getAbilityData(abilityData.getAbilityName());
if (data != null) this.getWorld().getHost().getAbilityManager().addAbilityToEntity(this, data);
}
@Override
public void initAbilities() {
// TODO: handle pre-dynamic, static and dynamic here
if (this.configGadget != null && this.configGadget.getAbilities() != null) {
for (var ability : this.configGadget.getAbilities()) {
this.addConfigAbility(ability);
}
}
}
@Override
public SceneEntityInfo toProto() {
return null;
}
}

View File

@@ -0,0 +1,66 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.data.GameData;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.World;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import it.unimi.dsi.fastutil.ints.Int2FloatArrayMap;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;
public class EntityWorld extends GameEntity {
@Getter private World world;
public EntityWorld(World world) {
super(null);
this.world = world;
this.id = world.getNextEntityId(EntityIdType.MPLEVEL);
this.initAbilities();
}
@Override
public Scene getScene() {
return this.world.getHost().getScene();
}
@Override
public void initAbilities() {
// Load abilities from levelElementAbilities
for (var ability :
GameData.getConfigGlobalCombat().getDefaultAbilities().getDefaultMPLevelAbilities()) {
var data = GameData.getAbilityData(ability);
if (data != null) world.getHost().getAbilityManager().addAbilityToEntity(this, data);
}
}
@Override
public int getEntityTypeId() {
return EntityIdType.TEAM.getId();
}
@Override
public Int2FloatMap getFightProperties() {
// TODO
return new Int2FloatArrayMap();
}
@Override
public Position getPosition() {
// TODO Auto-generated method stub
return new Position(0, 0, 0);
}
@Override
public Position getRotation() {
return new Position(0, 0, 0);
}
@Override
public SceneEntityInfo toProto() {
return null;
}
}

View File

@@ -1,32 +1,25 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.game.ability.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
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.game.props.*;
import emu.grasscutter.game.world.*;
import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
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.EntityDamageEvent;
import emu.grasscutter.server.event.entity.EntityDeathEvent;
import emu.grasscutter.scripts.data.controller.EntityController;
import emu.grasscutter.server.event.entity.*;
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.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2FloatMap;
import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap;
import lombok.Getter;
import lombok.Setter;
import it.unimi.dsi.fastutil.ints.*;
import java.util.*;
import lombok.*;
public abstract class GameEntity {
@Getter protected int id;
@Getter private final Scene scene;
@Getter protected int id;
@Getter @Setter private SpawnDataEntry spawnEntry;
@Getter @Setter private int blockId;
@@ -39,19 +32,31 @@ public abstract class GameEntity {
@Getter @Setter private boolean lockHP;
// Abilities
private Object2FloatMap<String> metaOverrideMap;
private Int2ObjectMap<String> metaModifiers;
// Lua controller for specific actions
@Getter @Setter private EntityController entityController;
@Getter private ElementType lastAttackType = ElementType.None;
@Getter private List<Ability> instancedAbilities = new ArrayList<>();
@Getter
private Int2ObjectMap<AbilityModifierController> instancedModifiers =
new Int2ObjectOpenHashMap<>();
@Getter private Map<String, Float> globalAbilityValues = new HashMap<>();
public GameEntity(Scene scene) {
this.scene = scene;
this.motionState = MotionState.MOTION_STATE_NONE;
}
public abstract void initAbilities();
public int getEntityType() {
return this.getId() >> 24;
}
public abstract int getEntityTypeId();
public World getWorld() {
return this.getScene().getWorld();
}
@@ -64,20 +69,6 @@ public abstract class GameEntity {
return this.isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD;
}
public Object2FloatMap<String> getMetaOverrideMap() {
if (this.metaOverrideMap == null) {
this.metaOverrideMap = new Object2FloatOpenHashMap<>();
}
return this.metaOverrideMap;
}
public Int2ObjectMap<String> getMetaModifiers() {
if (this.metaModifiers == null) {
this.metaModifiers = new Int2ObjectOpenHashMap<>();
}
return this.metaModifiers;
}
public abstract Int2FloatMap getFightProperties();
public abstract Position getPosition();
@@ -105,24 +96,29 @@ public abstract class GameEntity {
}
public void addAllFightPropsToEntityInfo(SceneEntityInfo.Builder entityInfo) {
this.getFightProperties().forEach((key, value) -> {
if (key == 0) return;
entityInfo.addFightPropList(FightPropPair.newBuilder().setPropType(key).setPropValue(value).build());
});
this.getFightProperties()
.forEach(
(key, value) -> {
if (key == 0) return;
entityInfo.addFightPropList(
FightPropPair.newBuilder().setPropType(key).setPropValue(value).build());
});
}
protected MotionInfo getMotionInfo() {
MotionInfo proto = MotionInfo.newBuilder()
return MotionInfo.newBuilder()
.setPos(this.getPosition().toProto())
.setRot(this.getRotation().toProto())
.setSpeed(Vector.newBuilder())
.setState(this.getMotionState())
.build();
return proto;
}
public float heal(float amount) {
return heal(amount, false);
}
public float heal(float amount, boolean mute) {
if (this.getFightProperties() == null) {
return 0f;
}
@@ -137,23 +133,30 @@ public abstract class GameEntity {
float healed = Math.min(maxHp - curHp, amount);
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, healed);
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
this.getScene()
.broadcastPacket(
new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
return healed;
}
public void damage(float amount) {
this.damage(amount, 0);
this.damage(amount, 0, ElementType.None);
}
public void damage(float amount, int killerId) {
public void damage(float amount, ElementType attackType) {
this.damage(amount, 0, attackType);
}
public void damage(float amount, int killerId, ElementType attackType) {
// Check if the entity has properties.
if (this.getFightProperties() == null || !hasFightProperty(FightProperty.FIGHT_PROP_CUR_HP)) {
return;
}
// Invoke entity damage event.
EntityDamageEvent event = new EntityDamageEvent(this, amount, this.getScene().getEntityById(killerId));
EntityDamageEvent event =
new EntityDamageEvent(this, amount, attackType, this.getScene().getEntityById(killerId));
event.call();
if (event.isCanceled()) {
return; // If the event is canceled, do not damage the entity.
@@ -165,6 +168,8 @@ public abstract class GameEntity {
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -(event.getDamage()));
}
this.lastAttackType = attackType;
// Check if dead
boolean isDead = false;
if (this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) {
@@ -172,8 +177,12 @@ public abstract class GameEntity {
isDead = true;
}
this.runLuaCallbacks(event);
// Packets
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
this.getScene()
.broadcastPacket(
new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
// Check if dead.
if (isDead) {
@@ -181,8 +190,20 @@ public abstract class GameEntity {
}
}
/**
* Runs the Lua callbacks for {@link EntityDamageEvent}.
*
* @param event The damage event.
*/
public void runLuaCallbacks(EntityDamageEvent event) {
if (entityController != null) {
entityController.onBeHurt(this, event.getAttackElementType(), true); // todo is host handling
}
}
/**
* Move this entity to a new position.
*
* @param position The new position.
* @param rotation The new rotation.
*/
@@ -194,32 +215,49 @@ public abstract class GameEntity {
/**
* Called when a player interacts with this entity
*
* @param player Player that is interacting with this entity
* @param interactReq Interact request protobuf data
*/
public void onInteract(Player player, GadgetInteractReq interactReq) {
public void onInteract(Player player, GadgetInteractReq interactReq) {}
/** Called when this entity is added to the world */
public void onCreate() {}
public void onRemoved() {}
public void onTick(int sceneTime) {
if (entityController != null) {
entityController.onTimer(this, sceneTime);
}
}
/**
* Called when this entity is added to the world
*/
public void onCreate() {
}
public void onRemoved() {
public int onClientExecuteRequest(int param1, int param2, int param3) {
if (entityController != null) {
return entityController.onClientExecuteRequest(this, param1, param2, param3);
}
return 0;
}
/**
* Called when this entity dies
*
* @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();
// Run Lua callbacks.
if (entityController != null) {
entityController.onDie(this, getLastAttackType());
}
}
/** Invoked when a global ability value is updated. */
public void onAbilityValueUpdate() {
// Does nothing.
}
public abstract SceneEntityInfo toProto();

View File

@@ -0,0 +1,38 @@
package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.game.entity.EntityBaseGadget;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.AbilityGadgetInfoOuterClass;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import lombok.val;
public class GadgetAbility extends GadgetContent {
private EntityBaseGadget parent;
public GadgetAbility(EntityGadget gadget, EntityBaseGadget parent) {
super(gadget);
this.parent = parent;
}
public boolean onInteract(Player player, GadgetInteractReq req) {
return false;
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
if (this.parent == null) {
return;
}
val abilityGadgetInfo =
AbilityGadgetInfoOuterClass.AbilityGadgetInfo.newBuilder()
.setCampId(parent.getCampId())
.setCampTargetType(parent.getCampType())
.setTargetEntityId(parent.getId())
.build();
gadgetInfo.setAbilityGadget(abilityGadgetInfo);
}
}

View File

@@ -1,10 +1,11 @@
package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.drop.DropSystem;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.gadget.chest.BossChestInteractHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.BossChestInfoOuterClass.BossChestInfo;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.net.proto.InterOpTypeOuterClass.InterOpType;
@@ -13,8 +14,9 @@ import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
import emu.grasscutter.net.proto.ResinCostTypeOuterClass;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.scripts.constants.ScriptGadgetState;
import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketWorldChestOpenNotify;
public class GadgetChest extends GadgetContent {
@@ -22,23 +24,110 @@ public class GadgetChest extends GadgetContent {
super(gadget);
}
/**
* @return Whether we should remove the gadget.
*/
public boolean onInteract(Player player, GadgetInteractReq req) {
var chestInteractHandlerMap = getGadget().getScene().getWorld().getServer().getWorldDataSystem().getChestInteractHandlerMap();
// If bigWorldScript enabled,use new drop system.
if (Grasscutter.getConfig().server.game.enableScriptInBigWorld) {
SceneGadget chest = getGadget().getMetaGadget();
DropSystem dropSystem = player.getServer().getDropSystem();
if (chest.boss_chest != null && chest.drop_tag != null) {
// Boss chest drop
// TODO:check for blossom chests
if (req.getOpType() == InterOpType.INTER_OP_TYPE_START) {
// Two steps
player.sendPacket(
new PacketGadgetInteractRsp(
getGadget(),
InteractType.INTERACT_TYPE_OPEN_CHEST,
InterOpType.INTER_OP_TYPE_START));
return false;
}
// TODO:check for take_num.(some boss rewards can only be claimed once a week.). Handle boss
// respawn.
// TODO:should return Retcode.RET_RESIN_NOT_ENOUGH ?
if (player.getResinManager().useResin(chest.boss_chest.resin)
&& dropSystem.handleBossChestDrop(chest.drop_tag, player)) {
// Is it correct?
player
.getBattlePassManager()
.triggerMission(
WatcherTriggerType.TRIGGER_WORLD_BOSS_REWARD,
chest.boss_chest.monster_config_id,
1);
getGadget().updateState(ScriptGadgetState.ChestOpened);
player.sendPacket(
new PacketGadgetInteractRsp(
this.getGadget(),
InteractTypeOuterClass.InteractType.INTERACT_TYPE_OPEN_CHEST,
InterOpType.INTER_OP_TYPE_FINISH));
return true;
}
// if failed,fallback to legacy drop system.
} else {
// Normal chest drop
// only the owner of the world can open chests.
if (player != player.getWorld().getHost()) return false;
boolean status = false;
if (chest.drop_tag != null) {
status = dropSystem.handleChestDrop(chest.drop_tag, chest.level, getGadget());
} else if (chest.chest_drop_id != 0) {
status = dropSystem.handleChestDrop(chest.chest_drop_id, chest.drop_count, getGadget());
}
if (status) {
getGadget().updateState(ScriptGadgetState.ChestOpened);
player.sendPacket(
new PacketGadgetInteractRsp(
getGadget(),
InteractType.INTERACT_TYPE_OPEN_CHEST,
InterOpType.INTER_OP_TYPE_FINISH));
player.sendPacket(
new PacketWorldChestOpenNotify(
getGadget().getGroupId(), player.getSceneId(), chest.config_id));
return true;
}
// if failed,fallback to legacy drop system.
}
Grasscutter.getLogger()
.warn(
"Can not solve chest drop: chest_drop_id = {} , drop_tag = {}.Fallback to legacy drop system.",
chest.chest_drop_id,
chest.drop_tag);
}
// Legacy chest drop system
var chestInteractHandlerMap =
getGadget()
.getScene()
.getWorld()
.getServer()
.getWorldDataSystem()
.getChestInteractHandlerMap();
var handler = chestInteractHandlerMap.get(getGadget().getGadgetData().getJsonName());
if (handler == null) {
Grasscutter.getLogger().warn("Could not found the handler of this type of Chests {}", getGadget().getGadgetData().getJsonName());
Grasscutter.getLogger()
.warn(
"Could not found the handler of this type of Chests {}",
getGadget().getGadgetData().getJsonName());
return false;
}
if (req.getOpType() == InterOpType.INTER_OP_TYPE_START && handler.isTwoStep()) {
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_START));
player.sendPacket(
new PacketGadgetInteractRsp(
getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_START));
return false;
}else {
} else {
boolean success;
if (handler instanceof BossChestInteractHandler bossChestInteractHandler) {
success = bossChestInteractHandler.onInteract(this, player,
req.getResinCostType()== ResinCostTypeOuterClass.ResinCostType.RESIN_COST_TYPE_CONDENSE);
}else {
success =
bossChestInteractHandler.onInteract(
this,
player,
req.getResinCostType()
== ResinCostTypeOuterClass.ResinCostType.RESIN_COST_TYPE_CONDENSE);
} else {
success = handler.onInteract(this, player);
}
if (!success) {
@@ -46,7 +135,11 @@ public class GadgetChest extends GadgetContent {
}
getGadget().updateState(ScriptGadgetState.ChestOpened);
player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractTypeOuterClass.InteractType.INTERACT_TYPE_OPEN_CHEST));
player.sendPacket(
new PacketGadgetInteractRsp(
this.getGadget(),
InteractTypeOuterClass.InteractType.INTERACT_TYPE_OPEN_CHEST,
InterOpType.INTER_OP_TYPE_FINISH));
return true;
}
@@ -61,13 +154,13 @@ public class GadgetChest extends GadgetContent {
if (bossChest != null) {
var players = getGadget().getScene().getPlayers().stream().map(Player::getUid).toList();
gadgetInfo.setBossChest(BossChestInfo.newBuilder()
.setMonsterConfigId(bossChest.monster_config_id)
.setResin(bossChest.resin)
.addAllQualifyUidList(players)
.addAllRemainUidList(players)
.build());
gadgetInfo.setBossChest(
BossChestInfo.newBuilder()
.setMonsterConfigId(bossChest.monster_config_id)
.setResin(bossChest.resin)
.addAllQualifyUidList(players)
.addAllRemainUidList(players)
.build());
}
}
}

View File

@@ -6,17 +6,17 @@ import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
public abstract class GadgetContent {
private final EntityGadget gadget;
private final EntityGadget gadget;
public GadgetContent(EntityGadget gadget) {
this.gadget = gadget;
}
public GadgetContent(EntityGadget gadget) {
this.gadget = gadget;
}
public EntityGadget getGadget() {
return gadget;
}
public abstract boolean onInteract(Player player, GadgetInteractReq req);
public abstract void onBuildProto(SceneGadgetInfo.Builder gadgetInfo);
public EntityGadget getGadget() {
return gadget;
}
public abstract boolean onInteract(Player player, GadgetInteractReq req);
public abstract void onBuildProto(SceneGadgetInfo.Builder gadgetInfo);
}

View File

@@ -1,6 +1,8 @@
package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.GatherData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityItem;
@@ -8,22 +10,32 @@ 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.GadgetInteractReqOuterClass.GadgetInteractReq;
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.Utils;
public class GadgetGatherObject extends GadgetContent {
public final class GadgetGatherObject extends GadgetContent {
private int itemId;
private boolean isForbidGuest;
public GadgetGatherObject(EntityGadget gadget) {
super(gadget);
// overwrites the default spawn handling
if (gadget.getSpawnEntry() != null) {
this.itemId = gadget.getSpawnEntry().getGatherItemId();
return;
}
GatherData gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
if (gatherData != null) {
this.itemId = gatherData.getItemId();
this.isForbidGuest = gatherData.isForbidGuest();
} else {
Grasscutter.getLogger().trace("invalid gather object: {}", gadget.getConfigId());
}
}
@@ -45,32 +57,37 @@ public class GadgetGatherObject extends GadgetContent {
GameItem item = new GameItem(itemData, 1);
player.getInventory().addItem(item, ActionReason.Gather);
getGadget().getScene().broadcastPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_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();
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);
int times = Utils.randomRange(1, 2);
for (int i = 0 ; i < times ; i++) {
EntityItem item = new EntityItem(
scene,
player,
GameData.getItemDataMap().get(itemId),
getGadget().getPosition().nearby2d(1f).addY(2f),
1,
true);
for (int i = 0; i < times; i++) {
EntityItem item =
new EntityItem(
scene,
player,
GameData.getItemDataMap().get(itemId),
getGadget().getPosition().nearby2d(1f).addY(2f),
1,
true);
scene.addEntity(item);
}

View File

@@ -3,38 +3,45 @@ 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.GatherGadgetInfoOuterClass.GatherGadgetInfo;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.utils.Utils;
public class GadgetGatherPoint extends GadgetContent {
private int itemId;
private boolean isForbidGuest;
/** Spawner for the gather objects */
public final class GadgetGatherPoint extends GadgetContent {
private final GatherData gatherData;
private final EntityGadget gatherObjectChild;
public GadgetGatherPoint(EntityGadget gadget) {
super(gadget);
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();
}
this.gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
var scene = gadget.getScene();
gatherObjectChild = new EntityGadget(scene, gatherData.getGadgetId(), gadget.getBornPos());
gatherObjectChild.setBlockId(gadget.getBlockId());
gatherObjectChild.setConfigId(gadget.getConfigId());
gatherObjectChild.setGroupId(gadget.getGroupId());
gatherObjectChild.getRotation().set(gadget.getRotation());
gatherObjectChild.setState(gadget.getState());
gatherObjectChild.setPointType(gadget.getPointType());
gatherObjectChild.setMetaGadget(gadget.getMetaGadget());
gatherObjectChild.buildContent();
gadget.getChildren().add(gatherObjectChild);
scene.addEntity(gatherObjectChild);
}
public int getItemId() {
return this.itemId;
return this.gatherData.getItemId();
}
public boolean isForbidGuest() {
return isForbidGuest;
return this.gatherData.isForbidGuest();
}
public boolean onInteract(Player player, GadgetInteractReq req) {
@@ -46,34 +53,13 @@ public class GadgetGatherPoint extends GadgetContent {
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
GatherGadgetInfo gatherGadgetInfo = GatherGadgetInfo.newBuilder()
.setItemId(this.getItemId())
.setIsForbidGuest(this.isForbidGuest())
.build();
// todo does official use this for the spawners?
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),
getGadget().getPosition().clone()
.addY(2f)
.addX(Utils.randomFloatRange(-1f, 1f))
.addZ(Utils.randomFloatRange(-1f, 1f)),
1,
true);
scene.addEntity(item);
}
scene.killEntity(this.getGadget(), player.getTeamManager().getCurrentAvatarEntity().getId());
// Todo: add record
}
}

View File

@@ -5,7 +5,7 @@ import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass;
public class GadgetObject extends GadgetContent{
public class GadgetObject extends GadgetContent {
public GadgetObject(EntityGadget gadget) {
super(gadget);
}
@@ -16,7 +16,5 @@ public class GadgetObject extends GadgetContent{
}
@Override
public void onBuildProto(SceneGadgetInfoOuterClass.SceneGadgetInfo.Builder gadgetInfo) {
}
public void onBuildProto(SceneGadgetInfoOuterClass.SceneGadgetInfo.Builder gadgetInfo) {}
}

View File

@@ -5,26 +5,30 @@ import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
import emu.grasscutter.net.proto.ResinCostTypeOuterClass;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
public class GadgetRewardStatue extends GadgetContent {
public GadgetRewardStatue(EntityGadget gadget) {
super(gadget);
}
public final class GadgetRewardStatue extends GadgetContent {
public boolean onInteract(Player player, GadgetInteractReq req) {
if (player.getScene().getChallenge() != null && player.getScene().getChallenge() instanceof DungeonChallenge dungeonChallenge) {
dungeonChallenge.getStatueDrops(player, req);
}
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_STATUE));
return false;
}
public GadgetRewardStatue(EntityGadget gadget) {
super(gadget);
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
}
public boolean onInteract(Player player, GadgetInteractReq req) {
var dungeonManager = player.getScene().getDungeonManager();
if (player.getScene().getChallenge() instanceof DungeonChallenge) {
var useCondensed =
req.getResinCostType() == ResinCostTypeOuterClass.ResinCostType.RESIN_COST_TYPE_CONDENSE;
dungeonManager.getStatueDrops(player, useCondensed, getGadget().getGroupId());
}
player.sendPacket(
new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_STATUE));
return false;
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {}
}

View File

@@ -1,7 +1,6 @@
package emu.grasscutter.game.entity.gadget;
import java.util.Arrays;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.gadget.worktop.WorktopWorktopOptionHandler;
import emu.grasscutter.game.player.Player;
@@ -10,17 +9,24 @@ import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass.SelectWorktopOptionReq;
import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class GadgetWorktop extends GadgetContent {
private IntSet worktopOptions;
public final class GadgetWorktop extends GadgetContent {
private Set<Integer> worktopOptions;
private WorktopWorktopOptionHandler handler;
public GadgetWorktop(EntityGadget gadget) {
super(gadget);
}
public IntSet getWorktopOptions() {
public Set<Integer> getWorktopOptions() {
if (this.worktopOptions == null) {
this.worktopOptions = new HashSet<>();
}
return worktopOptions;
}
@@ -43,25 +49,25 @@ public class GadgetWorktop extends GadgetContent {
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
if (this.worktopOptions == null) {
return;
var options = this.getWorktopOptions();
if (options == null) return;
try {
var worktop = WorktopInfo.newBuilder().addAllOptionList(options).build();
gadgetInfo.setWorktop(worktop);
} catch (NullPointerException ignored) {
// "this.wrapped" is null.
gadgetInfo.setWorktop(
WorktopInfo.newBuilder().addAllOptionList(Collections.emptyList()).build());
Grasscutter.getLogger().warn("GadgetWorktop.onBuildProto: this.wrapped is null");
}
WorktopInfo worktop = WorktopInfo.newBuilder()
.addAllOptionList(this.getWorktopOptions())
.build();
gadgetInfo.setWorktop(worktop);
}
public void setOnSelectWorktopOptionEvent(WorktopWorktopOptionHandler handler) {
this.handler = handler;
}
public boolean onSelectWorktopOption(SelectWorktopOptionReq req) {
if (this.handler != null) {
this.handler.onSelectWorktopOption(this, req.getOptionId());
}
return false;
}
public boolean onSelectWorktopOption(SelectWorktopOptionReq req) {
return this.handler != null && this.handler.onSelectWorktopOption(this, req.getOptionId());
}
}

View File

@@ -7,11 +7,10 @@ import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
import java.util.ArrayList;
import java.util.List;
public class BossChestInteractHandler implements ChestInteractHandler{
public class BossChestInteractHandler implements ChestInteractHandler {
@Override
public boolean isTwoStep() {
return true;
@@ -19,23 +18,40 @@ public class BossChestInteractHandler implements ChestInteractHandler{
@Override
public boolean onInteract(GadgetChest chest, Player player) {
return this.onInteract(chest,player,false);
return this.onInteract(chest, player, false);
}
public boolean onInteract(GadgetChest chest, Player player,boolean useCondensedResin) {
var blossomRewards = player.getScene().getBlossomManager().onReward(player,chest.getGadget(),useCondensedResin);
if (blossomRewards!=null) {
public boolean onInteract(GadgetChest chest, Player player, boolean useCondensedResin) {
var blossomRewards =
player
.getScene()
.getBlossomManager()
.onReward(player, chest.getGadget(), useCondensedResin);
if (blossomRewards != null) {
player.getInventory().addItems(blossomRewards, ActionReason.OpenWorldBossChest);
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(blossomRewards));
return true;
}
var worldDataManager = chest.getGadget().getScene().getWorld().getServer().getWorldDataSystem();
var monster = chest.getGadget().getMetaGadget().group.monsters.get(chest.getGadget().getMetaGadget().boss_chest.monster_config_id);
var monster =
chest
.getGadget()
.getMetaGadget()
.group
.monsters
.get(chest.getGadget().getMetaGadget().boss_chest.monster_config_id);
var reward = worldDataManager.getRewardByBossId(monster.monster_id);
if (reward == null) {
Grasscutter.getLogger().warn("Could not found the reward of boss monster {}", monster.monster_id);
var dungeonManager = player.getScene().getDungeonManager();
if (dungeonManager != null) {
return dungeonManager.getStatueDrops(
player, useCondensedResin, chest.getGadget().getGroupId());
}
Grasscutter.getLogger()
.warn("Could not found the reward of boss monster {}", monster.monster_id);
return false;
}
List<GameItem> rewards = new ArrayList<>();

View File

@@ -3,13 +3,13 @@ package emu.grasscutter.game.entity.gadget.chest;
import emu.grasscutter.game.entity.gadget.GadgetChest;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.ChestReward;
import emu.grasscutter.server.event.player.PlayerOpenChestEvent;
import java.util.Random;
public class NormalChestInteractHandler implements ChestInteractHandler {
private final ChestReward chestReward;
public NormalChestInteractHandler(ChestReward rewardData){
public NormalChestInteractHandler(ChestReward rewardData) {
this.chestReward = rewardData;
}
@@ -20,21 +20,35 @@ public class NormalChestInteractHandler implements ChestInteractHandler {
@Override
public boolean onInteract(GadgetChest chest, Player player) {
// Invoke open chest event.
var event = new PlayerOpenChestEvent(player, chest, this.chestReward);
event.call();
if (event.isCanceled()) return true;
player.earnExp(chestReward.getAdvExp());
player.getInventory().addItem(201, chestReward.getResin());
var mora = chestReward.getMora() * (1 + (player.getWorldLevel() - 1) * 0.5);
player.getInventory().addItem(202, (int)mora);
player.getInventory().addItem(202, (int) mora);
for(int i=0;i<chestReward.getContent().size();i++){
chest.getGadget().getScene().addItemEntity(chestReward.getContent().get(i).getItemId(), chestReward.getContent().get(i).getCount(), chest.getGadget());
for (int i = 0; i < chestReward.getContent().size(); i++) {
chest
.getGadget()
.getScene()
.addItemEntity(
chestReward.getContent().get(i).getItemId(),
chestReward.getContent().get(i).getCount(),
chest.getGadget());
}
var random = new Random(System.currentTimeMillis());
for(int i=0;i<chestReward.getRandomCount();i++){
for (int i = 0; i < chestReward.getRandomCount(); i++) {
var index = random.nextInt(chestReward.getRandomContent().size());
var item = chestReward.getRandomContent().get(index);
chest.getGadget().getScene().addItemEntity(item.getItemId(), item.getCount(), chest.getGadget());
chest
.getGadget()
.getScene()
.addItemEntity(item.getItemId(), item.getCount(), chest.getGadget());
}
return true;

View File

@@ -0,0 +1,28 @@
package emu.grasscutter.game.entity.gadget.platform;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.net.proto.MathQuaternionOuterClass.MathQuaternion;
import emu.grasscutter.net.proto.MovingPlatformTypeOuterClass;
import emu.grasscutter.net.proto.PlatformInfoOuterClass;
/** TODO mostly hardcoded for EntitySolarIsotomaElevatorPlatform, should be more generic */
public class AbilityRoute extends BaseRoute {
private final Position basePosition;
public AbilityRoute(
Position startRot, boolean startRoute, boolean isActive, Position basePosition) {
super(startRot, startRoute, isActive);
this.basePosition = basePosition;
}
@Override
public PlatformInfoOuterClass.PlatformInfo.Builder toProto() {
return super.toProto()
.setStartRot(MathQuaternion.newBuilder().setW(1.0F))
.setPosOffset(basePosition.toProto())
.setRotOffset(MathQuaternion.newBuilder().setW(1.0F))
.setMovingPlatformType(
MovingPlatformTypeOuterClass.MovingPlatformType.MOVING_PLATFORM_TYPE_ABILITY);
}
}

View File

@@ -0,0 +1,83 @@
package emu.grasscutter.game.entity.gadget.platform;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.MathQuaternionOuterClass.MathQuaternion;
import emu.grasscutter.net.proto.PlatformInfoOuterClass.PlatformInfo;
import emu.grasscutter.scripts.data.SceneGadget;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
public abstract class BaseRoute {
@Getter @Setter private boolean isStarted;
@Getter @Setter private boolean isActive;
@Getter @Setter private Position startRot;
@Getter @Setter private int startSceneTime;
@Getter @Setter private int stopSceneTime;
BaseRoute(Position startRot, boolean isStarted, boolean isActive) {
this.startRot = startRot;
this.isStarted = isStarted;
this.isActive = isActive;
}
BaseRoute(SceneGadget gadget) {
this.startRot = gadget.rot;
this.isStarted = gadget.start_route;
this.isActive = gadget.start_route;
}
public static BaseRoute fromSceneGadget(SceneGadget sceneGadget) {
if (sceneGadget.route_id != 0) {
return new ConfigRoute(sceneGadget);
} else if (sceneGadget.is_use_point_array) {
return new PointArrayRoute(sceneGadget);
}
return null;
}
public boolean startRoute(Scene scene) {
if (this.isStarted) {
return false;
}
this.isStarted = true;
this.isActive = true;
this.startSceneTime = scene.getSceneTime() + 300;
return true;
}
public boolean stopRoute(Scene scene) {
if (!this.isStarted) {
return false;
}
this.isStarted = false;
this.isActive = false;
this.startSceneTime = scene.getSceneTime();
this.stopSceneTime = scene.getSceneTime();
return true;
}
private MathQuaternion.Builder rotAsMathQuaternion() {
val result = MathQuaternion.newBuilder();
if (startRot != null) {
result.setX(startRot.getX()).setY(startRot.getY()).setZ(startRot.getZ());
}
return result;
}
public PlatformInfo.Builder toProto() {
val result =
PlatformInfo.newBuilder()
.setIsStarted(isStarted)
.setIsActive(isActive)
.setStartRot(rotAsMathQuaternion())
.setStartSceneTime(startSceneTime);
if (!isStarted) {
result.setStopSceneTime(stopSceneTime);
}
return result;
}
}

View File

@@ -0,0 +1,31 @@
package emu.grasscutter.game.entity.gadget.platform;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.net.proto.MovingPlatformTypeOuterClass;
import emu.grasscutter.net.proto.PlatformInfoOuterClass;
import emu.grasscutter.scripts.data.SceneGadget;
import lombok.Getter;
import lombok.Setter;
public class ConfigRoute extends BaseRoute {
@Getter @Setter private int routeId;
public ConfigRoute(SceneGadget gadget) {
super(gadget);
this.routeId = gadget.route_id;
}
public ConfigRoute(Position startRot, boolean startRoute, boolean isActive, int routeId) {
super(startRot, startRoute, isActive);
this.routeId = routeId;
}
@Override
public PlatformInfoOuterClass.PlatformInfo.Builder toProto() {
return super.toProto()
.setRouteId(routeId)
.setMovingPlatformType(
MovingPlatformTypeOuterClass.MovingPlatformType.MOVING_PLATFORM_TYPE_USE_CONFIG);
}
}

View File

@@ -0,0 +1,32 @@
package emu.grasscutter.game.entity.gadget.platform;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.net.proto.MovingPlatformTypeOuterClass;
import emu.grasscutter.net.proto.PlatformInfoOuterClass;
import emu.grasscutter.scripts.data.SceneGadget;
import lombok.Getter;
import lombok.Setter;
/** TODO implement point array routes, read from missing resources */
public class PointArrayRoute extends BaseRoute {
@Getter @Setter int currentPoint;
@Getter @Setter int pointArrayId;
public PointArrayRoute(SceneGadget gadget) {
super(gadget);
}
public PointArrayRoute(
Position startRot, boolean startRoute, boolean isActive, int pointArrayId) {
super(startRot, startRoute, isActive);
this.pointArrayId = pointArrayId;
}
@Override
public PlatformInfoOuterClass.PlatformInfo.Builder toProto() {
return super.toProto()
.setMovingPlatformType(
MovingPlatformTypeOuterClass.MovingPlatformType.MOVING_PLATFORM_TYPE_ROUTE);
}
}

View File

@@ -1,6 +1,7 @@
package emu.grasscutter.game.entity.gadget.worktop;
import emu.grasscutter.game.entity.gadget.GadgetWorktop;
public interface WorktopWorktopOptionHandler {
boolean onSelectWorktopOption(GadgetWorktop gadgetWorktop,int option);
boolean onSelectWorktopOption(GadgetWorktop gadgetWorktop, int option);
}

View File

@@ -1,97 +0,0 @@
package emu.grasscutter.game.entity.platform;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.ConfigGadget;
import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.entity.EntityBaseGadget;
import emu.grasscutter.game.entity.EntityClientGadget;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.*;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import lombok.Getter;
import lombok.Setter;
import javax.annotation.Nullable;
public class EntityPlatform extends EntityBaseGadget {
@Getter
private final Player owner;
@Getter(onMethod_ = @Override)
private final int gadgetId;
@Getter
private final EntityClientGadget gadget;
@Getter(onMethod_ = @Override)
private final Int2FloatMap fightProperties;
@Nullable
@Getter
private ConfigGadget configGadget;
@Getter
private final MovingPlatformTypeOuterClass.MovingPlatformType movingPlatformType;
@Getter
@Setter
private boolean isStarted;
@Getter
@Setter
private boolean isActive;
public EntityPlatform(EntityClientGadget gadget, Scene scene, Player player, int gadgetId, Position pos, Position rot, MovingPlatformTypeOuterClass.MovingPlatformType movingPlatformType) {
super(scene, pos, rot);
this.gadget = gadget;
this.owner = player;
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.fightProperties = new Int2FloatOpenHashMap();
this.movingPlatformType = movingPlatformType;
this.gadgetId = gadgetId;
GadgetData data = GameData.getGadgetDataMap().get(gadgetId);
if (data != null && data.getJsonName() != null) {
this.configGadget = GameData.getGadgetConfigData().get(data.getJsonName());
}
fillFightProps(configGadget);
}
@Override
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
var platform = PlatformInfoOuterClass.PlatformInfo.newBuilder()
.setMovingPlatformType(movingPlatformType)
.build();
var gadgetInfo = SceneGadgetInfoOuterClass.SceneGadgetInfo.newBuilder()
.setGadgetId(getGadgetId())
.setAuthorityPeerId(getOwner().getPeerId())
.setPlatform(platform);
var entityInfo = SceneEntityInfoOuterClass.SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityTypeOuterClass.ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setGadget(gadgetInfo)
.setLifeState(1);
this.addAllFightPropsToEntityInfo(entityInfo);
return entityInfo.build();
}
public PlatformInfoOuterClass.PlatformInfo onStartRoute() {
return PlatformInfoOuterClass.PlatformInfo.newBuilder()
.setStartSceneTime(getScene().getSceneTime())
.setIsStarted(true)
.setPosOffset(getPosition().toProto())
.setMovingPlatformType(getMovingPlatformType())
.setIsActive(true)
.build();
}
public PlatformInfoOuterClass.PlatformInfo onStopRoute() {
var sceneTime = getScene().getSceneTime();
return PlatformInfoOuterClass.PlatformInfo.newBuilder()
.setStartSceneTime(sceneTime)
.setStopSceneTime(sceneTime)
.setPosOffset(getPosition().toProto())
.setMovingPlatformType(getMovingPlatformType())
.build();
}
}

View File

@@ -1,25 +1,29 @@
package emu.grasscutter.game.entity.platform;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.binout.ConfigGadget;
import emu.grasscutter.game.entity.EntitySolarIsotomaClientGadget;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.data.binout.config.ConfigEntityGadget;
import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.entity.gadget.GadgetAbility;
import emu.grasscutter.game.entity.gadget.platform.AbilityRoute;
import emu.grasscutter.game.world.Position;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.*;
import emu.grasscutter.server.packet.send.PacketSceneTimeNotify;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
public class EntitySolarIsotomaElevatorPlatform extends EntityPlatform {
public EntitySolarIsotomaElevatorPlatform(EntitySolarIsotomaClientGadget isotoma, Scene scene, Player player, int gadgetId, Position pos, Position rot) {
super(isotoma, scene, player, gadgetId, pos, rot, MovingPlatformTypeOuterClass.MovingPlatformType.MOVING_PLATFORM_TYPE_ABILITY);
public class EntitySolarIsotomaElevatorPlatform extends EntityGadget {
@SuppressWarnings("removal")
public EntitySolarIsotomaElevatorPlatform(
EntitySolarIsotomaClientGadget isotoma,
Scene scene,
int gadgetId,
Position pos,
Position rot) {
super(scene, gadgetId, pos, rot);
setOwner(isotoma);
this.setRouteConfig(new AbilityRoute(rot, false, false, pos));
this.setContent(new GadgetAbility(this, isotoma));
}
@Override
protected void fillFightProps(ConfigGadget configGadget) {
protected void fillFightProps(ConfigEntityGadget configGadget) {
if (configGadget == null || configGadget.getCombat() == null) {
return;
}
@@ -27,10 +31,10 @@ public class EntitySolarIsotomaElevatorPlatform extends EntityPlatform {
var combatProperties = combatData.getProperty();
if (combatProperties.isUseCreatorProperty()) {
//If useCreatorProperty == true, use owner's property;
GameEntity ownerAvatar = getScene().getEntityById(getGadget().getOwnerEntityId());
if (ownerAvatar != null) {
getFightProperties().putAll(ownerAvatar.getFightProperties());
// If useCreatorProperty == true, use owner's property;
GameEntity ownerEntity = getOwner();
if (ownerEntity != null) {
getFightProperties().putAll(ownerEntity.getFightProperties());
return;
} else {
Grasscutter.getLogger().warn("Why gadget owner is null?");
@@ -39,98 +43,4 @@ public class EntitySolarIsotomaElevatorPlatform extends EntityPlatform {
super.fillFightProps(configGadget);
}
@Override
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
var gadget = SceneGadgetInfoOuterClass.SceneGadgetInfo.newBuilder()
.setGadgetId(getGadgetId())
.setOwnerEntityId(getGadget().getId())
.setAuthorityPeerId(getOwner().getPeerId())
.setIsEnableInteract(true)
.setAbilityGadget(AbilityGadgetInfoOuterClass.AbilityGadgetInfo.newBuilder()
.setCampId(getGadget().getCampId())
.setCampTargetType(getGadget().getCampType())
.setTargetEntityId(getGadget().getId())
.build())
.setPlatform(PlatformInfoOuterClass.PlatformInfo.newBuilder()
.setStartRot(MathQuaternionOuterClass.MathQuaternion.newBuilder()
.setW(1.0F)
.build())
.setPosOffset(getGadget().getPosition().toProto())
.setRotOffset(MathQuaternionOuterClass.MathQuaternion.newBuilder()
.setW(1.0F)
.build())
.setMovingPlatformType(MovingPlatformTypeOuterClass.MovingPlatformType.MOVING_PLATFORM_TYPE_ABILITY)
.build())
.build();
var authority = EntityAuthorityInfoOuterClass.EntityAuthorityInfo.newBuilder()
.setAiInfo(SceneEntityAiInfoOuterClass.SceneEntityAiInfo.newBuilder()
.setIsAiOpen(true)
.setBornPos(getGadget().getPosition().toProto()))
.setBornPos(getGadget().getPosition().toProto())
.build();
var info = SceneEntityInfoOuterClass.SceneEntityInfo.newBuilder()
.setEntityType(ProtEntityTypeOuterClass.ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setEntityId(getId())
.setMotionInfo(MotionInfoOuterClass.MotionInfo.newBuilder()
.setPos(getGadget().getPosition().toProto())
.setRot(getGadget().getRotation().toProto())
.build());
GameEntity entity = getScene().getEntityById(getGadget().getOwnerEntityId());
if (entity instanceof EntityAvatar avatar) {
info.addPropList(PropPairOuterClass.PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, avatar.getAvatar().getLevel()))
.build());
} else {
Grasscutter.getLogger().warn("Why gadget owner doesn't exist?");
}
this.addAllFightPropsToEntityInfo(info);
info.setLifeState(1)
.setGadget(gadget)
.setEntityAuthorityInfo(authority);
return info.build();
}
@Override
public PlatformInfoOuterClass.PlatformInfo onStartRoute() {
setStarted(true);
setActive(true);
var sceneTime = getScene().getSceneTime();
getOwner().sendPacket(new PacketSceneTimeNotify(getOwner()));
return PlatformInfoOuterClass.PlatformInfo.newBuilder()
.setStartSceneTime(sceneTime + 300)
.setIsStarted(true)
.setPosOffset(getPosition().toProto())
.setRotOffset(MathQuaternionOuterClass.MathQuaternion.newBuilder()
.setW(1.0F)
.build())
.setMovingPlatformType(getMovingPlatformType())
.setIsActive(true)
.build();
}
@Override
public PlatformInfoOuterClass.PlatformInfo onStopRoute() {
setStarted(false);
setActive(false);
return PlatformInfoOuterClass.PlatformInfo.newBuilder()
.setStartSceneTime(getScene().getSceneTime())
.setStopSceneTime(getScene().getSceneTime())
.setPosOffset(getPosition().toProto())
.setRotOffset(MathQuaternionOuterClass.MathQuaternion.newBuilder()
.setW(1.0F)
.build())
.setMovingPlatformType(getMovingPlatformType())
.build();
}
}