mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-01-09 04:13:00 +08:00
Implement diving
This commit is contained in:
parent
f656143038
commit
cc91a2dcfc
@ -35,7 +35,6 @@ public final class GameConstants {
|
|||||||
"Avatar_FallAnthem_Achievement_Listener",
|
"Avatar_FallAnthem_Achievement_Listener",
|
||||||
"GrapplingHookSkill_Ability",
|
"GrapplingHookSkill_Ability",
|
||||||
"Avatar_PlayerBoy_DiveStamina_Reduction",
|
"Avatar_PlayerBoy_DiveStamina_Reduction",
|
||||||
"Ability_Avatar_Dive_Team",
|
|
||||||
"Ability_Avatar_Dive_SealEcho",
|
"Ability_Avatar_Dive_SealEcho",
|
||||||
"Absorb_SealEcho_Bullet_01",
|
"Absorb_SealEcho_Bullet_01",
|
||||||
"Absorb_SealEcho_Bullet_02",
|
"Absorb_SealEcho_Bullet_02",
|
||||||
@ -43,6 +42,9 @@ public final class GameConstants {
|
|||||||
"ActivityAbility_Absorb_Shoot",
|
"ActivityAbility_Absorb_Shoot",
|
||||||
"SceneAbility_DiveVolume"
|
"SceneAbility_DiveVolume"
|
||||||
};
|
};
|
||||||
|
public static final String[] DEFAULT_TEAM_ABILITY_STRINGS = {
|
||||||
|
"Ability_Avatar_Dive_Team"
|
||||||
|
};
|
||||||
public static final SparseSet ILLEGAL_WEAPONS = new SparseSet("""
|
public static final SparseSet ILLEGAL_WEAPONS = new SparseSet("""
|
||||||
10000-10008, 11411, 11506-11508, 12505, 12506, 12508, 12509,
|
10000-10008, 11411, 11506-11508, 12505, 12506, 12508, 12509,
|
||||||
13503, 13506, 14411, 14503, 14505, 14508, 15504-15506
|
13503, 13506, 14411, 14503, 14505, 14508, 15504-15506
|
||||||
|
@ -86,6 +86,12 @@ public final class SetPropCommand implements CommandHandler {
|
|||||||
this.props.put("fly", flyable);
|
this.props.put("fly", flyable);
|
||||||
this.props.put("glider", flyable);
|
this.props.put("glider", flyable);
|
||||||
this.props.put("canglide", flyable);
|
this.props.put("canglide", flyable);
|
||||||
|
|
||||||
|
Prop dive = new Prop("CanDive", PlayerProperty.PROP_PLAYER_CAN_DIVE, PseudoProp.CAN_DIVE);
|
||||||
|
this.props.put("dive", dive);
|
||||||
|
this.props.put("swim", dive);
|
||||||
|
this.props.put("water", dive);
|
||||||
|
this.props.put("candive", dive);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -129,6 +135,7 @@ public final class SetPropCommand implements CommandHandler {
|
|||||||
case SET_OPENSTATE -> this.setOpenState(targetPlayer, value, 1);
|
case SET_OPENSTATE -> this.setOpenState(targetPlayer, value, 1);
|
||||||
case UNSET_OPENSTATE -> this.setOpenState(targetPlayer, value, 0);
|
case UNSET_OPENSTATE -> this.setOpenState(targetPlayer, value, 0);
|
||||||
case UNLOCK_MAP -> unlockMap(targetPlayer, value);
|
case UNLOCK_MAP -> unlockMap(targetPlayer, value);
|
||||||
|
case CAN_DIVE -> canDive(targetPlayer, value);
|
||||||
default -> targetPlayer.setProperty(prop.prop, value);
|
default -> targetPlayer.setProperty(prop.prop, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -219,6 +226,20 @@ public final class SetPropCommand implements CommandHandler {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean canDive(Player targetPlayer, int value) {
|
||||||
|
// allow diving and set max stamina OR not
|
||||||
|
if (value == 0) {
|
||||||
|
targetPlayer.setProperty(PlayerProperty.PROP_PLAYER_CAN_DIVE, 0);
|
||||||
|
targetPlayer.setProperty(PlayerProperty.PROP_DIVE_MAX_STAMINA, 0);
|
||||||
|
targetPlayer.setProperty(PlayerProperty.PROP_DIVE_CUR_STAMINA, 0);
|
||||||
|
} else {
|
||||||
|
targetPlayer.setProperty(PlayerProperty.PROP_PLAYER_CAN_DIVE, 1);
|
||||||
|
targetPlayer.setProperty(PlayerProperty.PROP_DIVE_MAX_STAMINA, 10000);
|
||||||
|
targetPlayer.setProperty(PlayerProperty.PROP_DIVE_CUR_STAMINA, 10000);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean unlockMap(Player targetPlayer, int value) {
|
private boolean unlockMap(Player targetPlayer, int value) {
|
||||||
// Unlock.
|
// Unlock.
|
||||||
GameData.getScenePointsPerScene()
|
GameData.getScenePointsPerScene()
|
||||||
@ -270,7 +291,8 @@ public final class SetPropCommand implements CommandHandler {
|
|||||||
SET_OPENSTATE,
|
SET_OPENSTATE,
|
||||||
UNSET_OPENSTATE,
|
UNSET_OPENSTATE,
|
||||||
UNLOCK_MAP,
|
UNLOCK_MAP,
|
||||||
IS_FLYABLE
|
IS_FLYABLE,
|
||||||
|
CAN_DIVE
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Prop {
|
static class Prop {
|
||||||
|
@ -579,14 +579,20 @@ public class Player implements PlayerHook, FieldFetch {
|
|||||||
this.setOrFetch(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, 50);
|
this.setOrFetch(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, 50);
|
||||||
this.setOrFetch(PlayerProperty.PROP_IS_FLYABLE,
|
this.setOrFetch(PlayerProperty.PROP_IS_FLYABLE,
|
||||||
withQuesting ? 0 : 1);
|
withQuesting ? 0 : 1);
|
||||||
|
this.setOrFetch(PlayerProperty.PROP_PLAYER_CAN_DIVE,
|
||||||
|
withQuesting ? 0 : 1);
|
||||||
this.setOrFetch(PlayerProperty.PROP_IS_TRANSFERABLE, 1);
|
this.setOrFetch(PlayerProperty.PROP_IS_TRANSFERABLE, 1);
|
||||||
this.setOrFetch(PlayerProperty.PROP_MAX_STAMINA,
|
this.setOrFetch(PlayerProperty.PROP_MAX_STAMINA,
|
||||||
withQuesting ? 10000 : 24000);
|
withQuesting ? 10000 : 24000);
|
||||||
|
this.setOrFetch(PlayerProperty.PROP_DIVE_MAX_STAMINA,
|
||||||
|
withQuesting ? 10000 : 0);
|
||||||
this.setOrFetch(PlayerProperty.PROP_PLAYER_RESIN, 160);
|
this.setOrFetch(PlayerProperty.PROP_PLAYER_RESIN, 160);
|
||||||
|
|
||||||
// The player's current stamina is always their max stamina.
|
// The player's current stamina is always their max stamina.
|
||||||
this.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA,
|
this.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA,
|
||||||
this.getProperty(PlayerProperty.PROP_MAX_STAMINA));
|
this.getProperty(PlayerProperty.PROP_MAX_STAMINA));
|
||||||
|
this.setProperty(PlayerProperty.PROP_DIVE_CUR_STAMINA,
|
||||||
|
this.getProperty(PlayerProperty.PROP_DIVE_MAX_STAMINA));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1515,7 +1521,7 @@ public class Player implements PlayerHook, FieldFetch {
|
|||||||
int min = this.getPropertyMin(prop);
|
int min = this.getPropertyMin(prop);
|
||||||
int max = this.getPropertyMax(prop);
|
int max = this.getPropertyMax(prop);
|
||||||
if (min <= value && value <= max) {
|
if (min <= value && value <= max) {
|
||||||
int currentValue = this.properties.get(prop.getId());
|
int currentValue = this.properties.getOrDefault(prop.getId(), 0);
|
||||||
this.properties.put(prop.getId(), value);
|
this.properties.put(prop.getId(), value);
|
||||||
if (sendPacket) {
|
if (sendPacket) {
|
||||||
// Send property change reasons if needed.
|
// Send property change reasons if needed.
|
||||||
|
@ -1,53 +1,48 @@
|
|||||||
package emu.grasscutter.game.player;
|
package emu.grasscutter.game.player;
|
||||||
|
|
||||||
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
import dev.morphia.annotations.*;
|
||||||
|
import emu.grasscutter.*;
|
||||||
import dev.morphia.annotations.Entity;
|
|
||||||
import dev.morphia.annotations.Transient;
|
|
||||||
import emu.grasscutter.GameConstants;
|
|
||||||
import emu.grasscutter.Grasscutter;
|
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
|
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
|
||||||
import emu.grasscutter.game.avatar.Avatar;
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
import emu.grasscutter.game.entity.EntityAvatar;
|
import emu.grasscutter.game.entity.*;
|
||||||
import emu.grasscutter.game.entity.EntityBaseGadget;
|
import emu.grasscutter.game.props.*;
|
||||||
import emu.grasscutter.game.entity.EntityTeam;
|
import emu.grasscutter.game.world.*;
|
||||||
import emu.grasscutter.game.props.ElementType;
|
import emu.grasscutter.net.packet.*;
|
||||||
import emu.grasscutter.game.props.EnterReason;
|
import emu.grasscutter.net.proto.*;
|
||||||
import emu.grasscutter.game.props.FightProperty;
|
|
||||||
import emu.grasscutter.game.world.Position;
|
|
||||||
import emu.grasscutter.game.world.Scene;
|
|
||||||
import emu.grasscutter.game.world.World;
|
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
|
||||||
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
|
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
|
||||||
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
||||||
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
||||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||||
import emu.grasscutter.net.proto.TrialAvatarGrantRecordOuterClass.TrialAvatarGrantRecord.GrantReason;
|
import emu.grasscutter.net.proto.TrialAvatarGrantRecordOuterClass.TrialAvatarGrantRecord.GrantReason;
|
||||||
import emu.grasscutter.net.proto.VisionTypeOuterClass;
|
|
||||||
import emu.grasscutter.server.event.entity.EntityCreationEvent;
|
import emu.grasscutter.server.event.entity.EntityCreationEvent;
|
||||||
import emu.grasscutter.server.event.player.PlayerTeamDeathEvent;
|
import emu.grasscutter.server.event.player.PlayerTeamDeathEvent;
|
||||||
import emu.grasscutter.server.packet.send.*;
|
import emu.grasscutter.server.packet.send.*;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.*;
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
public final class TeamManager extends BasePlayerDataManager {
|
public final class TeamManager extends BasePlayerDataManager {
|
||||||
@Transient private final List<EntityAvatar> avatars;
|
@Transient private final List<EntityAvatar> avatars;
|
||||||
@Transient @Getter private final Set<EntityBaseGadget> gadgets;
|
@Transient @Getter private final Set<EntityBaseGadget> gadgets;
|
||||||
@Transient @Getter private final IntSet teamResonances;
|
@Transient @Getter private final IntSet teamResonances;
|
||||||
@Transient @Getter private final IntSet teamResonancesConfig;
|
@Transient
|
||||||
|
@Getter
|
||||||
|
private final IntSet teamResonancesConfig;
|
||||||
|
@Transient
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private Set<String> teamAbilityEmbryos;
|
||||||
// This needs to be a LinkedHashMap to guarantee insertion order.
|
// This needs to be a LinkedHashMap to guarantee insertion order.
|
||||||
@Getter private LinkedHashMap<Integer, TeamInfo> teams;
|
@Getter
|
||||||
|
private LinkedHashMap<Integer, TeamInfo> teams;
|
||||||
private int currentTeamIndex;
|
private int currentTeamIndex;
|
||||||
@Getter @Setter private int currentCharacterIndex;
|
@Getter @Setter private int currentCharacterIndex;
|
||||||
@Transient @Getter @Setter private TeamInfo mpTeam;
|
@Transient @Getter @Setter private TeamInfo mpTeam;
|
||||||
@ -69,6 +64,7 @@ public final class TeamManager extends BasePlayerDataManager {
|
|||||||
this.gadgets = new HashSet<>();
|
this.gadgets = new HashSet<>();
|
||||||
this.teamResonances = new IntOpenHashSet();
|
this.teamResonances = new IntOpenHashSet();
|
||||||
this.teamResonancesConfig = new IntOpenHashSet();
|
this.teamResonancesConfig = new IntOpenHashSet();
|
||||||
|
this.teamAbilityEmbryos = new HashSet<>();
|
||||||
this.trialAvatars = new HashMap<>();
|
this.trialAvatars = new HashMap<>();
|
||||||
this.trialAvatarTeam = new TeamInfo();
|
this.trialAvatarTeam = new TeamInfo();
|
||||||
}
|
}
|
||||||
@ -84,6 +80,42 @@ public final class TeamManager extends BasePlayerDataManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add team ability embryos, NOT to be confused with avatarAbilties.
|
||||||
|
// These should include the ones in LevelEntity (according to levelEntityConfig field in sceneId)
|
||||||
|
// rn only apply to big world defaults, but will fix scaramouch domain circles (BinOutput/LevelEntity/Level_Monster_Nada_setting)
|
||||||
|
public AbilityControlBlockOuterClass.AbilityControlBlock getAbilityControlBlock() {
|
||||||
|
AbilityControlBlockOuterClass.AbilityControlBlock.Builder abilityControlBlock = AbilityControlBlockOuterClass.AbilityControlBlock.newBuilder();
|
||||||
|
int embryoId = 0;
|
||||||
|
|
||||||
|
// add from default
|
||||||
|
if (Arrays.stream(GameConstants.DEFAULT_TEAM_ABILITY_STRINGS).count() > 0) {
|
||||||
|
List<String> teamAbilties = Arrays.stream(GameConstants.DEFAULT_TEAM_ABILITY_STRINGS).toList();
|
||||||
|
for (String skill : teamAbilties) {
|
||||||
|
AbilityEmbryoOuterClass.AbilityEmbryo emb = AbilityEmbryoOuterClass.AbilityEmbryo.newBuilder()
|
||||||
|
.setAbilityId(++embryoId)
|
||||||
|
.setAbilityNameHash(Utils.abilityHash(skill))
|
||||||
|
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||||
|
.build();
|
||||||
|
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// same as avatar ability hash (add frm levelEntityConfig data)
|
||||||
|
if (this.getTeamAbilityEmbryos().size() > 0) {
|
||||||
|
for (String skill : this.getTeamAbilityEmbryos()) {
|
||||||
|
AbilityEmbryoOuterClass.AbilityEmbryo emb = AbilityEmbryoOuterClass.AbilityEmbryo.newBuilder()
|
||||||
|
.setAbilityId(++embryoId)
|
||||||
|
.setAbilityNameHash(Utils.abilityHash(skill))
|
||||||
|
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||||
|
.build();
|
||||||
|
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return block to add
|
||||||
|
return abilityControlBlock.build();
|
||||||
|
}
|
||||||
|
|
||||||
public World getWorld() {
|
public World getWorld() {
|
||||||
return this.getPlayer().getWorld();
|
return this.getPlayer().getWorld();
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package emu.grasscutter.game.props;
|
package emu.grasscutter.game.props;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.*;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public enum PlayerProperty {
|
public enum PlayerProperty {
|
||||||
PROP_NONE(0),
|
PROP_NONE(0),
|
||||||
PROP_EXP(1001, 0),
|
PROP_EXP(1001, 0),
|
||||||
@ -60,7 +60,10 @@ public enum PlayerProperty {
|
|||||||
PROP_IS_AUTO_UNLOCK_SPECIFIC_EQUIP(10044), // New; unknown/un-used.
|
PROP_IS_AUTO_UNLOCK_SPECIFIC_EQUIP(10044), // New; unknown/un-used.
|
||||||
PROP_PLAYER_GCG_COIN(10045), // New; unknown/un-used.
|
PROP_PLAYER_GCG_COIN(10045), // New; unknown/un-used.
|
||||||
PROP_PLAYER_WAIT_SUB_GCG_COIN(10046), // New; unknown/un-used.
|
PROP_PLAYER_WAIT_SUB_GCG_COIN(10046), // New; unknown/un-used.
|
||||||
PROP_PLAYER_ONLINE_TIME(10047); // New; unknown/un-used.
|
PROP_PLAYER_ONLINE_TIME(10047), // New; unknown/un-used.
|
||||||
|
PROP_PLAYER_CAN_DIVE(10048, 0, 1), // Can the player dive? [0, 1]
|
||||||
|
PROP_DIVE_MAX_STAMINA(10049, 0, 10000), // The maximum stamina of the player when diving. [0, 10000]
|
||||||
|
PROP_DIVE_CUR_STAMINA(10050, 0, 10000); // The current stamina of the player when diving. [0, 10000]
|
||||||
|
|
||||||
private static final int inf = Integer.MAX_VALUE; // Maybe this should be something else?
|
private static final int inf = Integer.MAX_VALUE; // Maybe this should be something else?
|
||||||
private static final Int2ObjectMap<PlayerProperty> map = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<PlayerProperty> map = new Int2ObjectOpenHashMap<>();
|
||||||
|
@ -3,9 +3,7 @@ package emu.grasscutter.server.packet.send;
|
|||||||
import emu.grasscutter.game.entity.EntityAvatar;
|
import emu.grasscutter.game.entity.EntityAvatar;
|
||||||
import emu.grasscutter.game.inventory.GameItem;
|
import emu.grasscutter.game.inventory.GameItem;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.packet.BasePacket;
|
import emu.grasscutter.net.packet.*;
|
||||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
|
||||||
import emu.grasscutter.net.proto.AbilityControlBlockOuterClass;
|
|
||||||
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
||||||
import emu.grasscutter.net.proto.AvatarEnterSceneInfoOuterClass.AvatarEnterSceneInfo;
|
import emu.grasscutter.net.proto.AvatarEnterSceneInfoOuterClass.AvatarEnterSceneInfo;
|
||||||
import emu.grasscutter.net.proto.MPLevelEntityInfoOuterClass.MPLevelEntityInfo;
|
import emu.grasscutter.net.proto.MPLevelEntityInfoOuterClass.MPLevelEntityInfo;
|
||||||
@ -28,8 +26,7 @@ public class PacketPlayerEnterSceneInfoNotify extends BasePacket {
|
|||||||
TeamEnterSceneInfo.newBuilder()
|
TeamEnterSceneInfo.newBuilder()
|
||||||
.setTeamEntityId(player.getTeamManager().getEntity().getId()) // 150995833
|
.setTeamEntityId(player.getTeamManager().getEntity().getId()) // 150995833
|
||||||
.setTeamAbilityInfo(empty)
|
.setTeamAbilityInfo(empty)
|
||||||
.setAbilityControlBlock(
|
.setAbilityControlBlock(player.getTeamManager().getAbilityControlBlock()));
|
||||||
AbilityControlBlockOuterClass.AbilityControlBlock.newBuilder().build()));
|
|
||||||
proto.setMpLevelEntityInfo(
|
proto.setMpLevelEntityInfo(
|
||||||
MPLevelEntityInfo.newBuilder()
|
MPLevelEntityInfo.newBuilder()
|
||||||
.setEntityId(player.getWorld().getLevelEntityId()) // 184550274
|
.setEntityId(player.getWorld().getLevelEntityId()) // 184550274
|
||||||
|
Loading…
Reference in New Issue
Block a user