Implement diving

This commit is contained in:
KingRainbow44 2023-08-26 20:59:58 -04:00
parent f656143038
commit cc91a2dcfc
No known key found for this signature in database
GPG Key ID: FC2CB64B00D257BE
6 changed files with 100 additions and 38 deletions

View File

@ -35,7 +35,6 @@ public final class GameConstants {
"Avatar_FallAnthem_Achievement_Listener",
"GrapplingHookSkill_Ability",
"Avatar_PlayerBoy_DiveStamina_Reduction",
"Ability_Avatar_Dive_Team",
"Ability_Avatar_Dive_SealEcho",
"Absorb_SealEcho_Bullet_01",
"Absorb_SealEcho_Bullet_02",
@ -43,6 +42,9 @@ public final class GameConstants {
"ActivityAbility_Absorb_Shoot",
"SceneAbility_DiveVolume"
};
public static final String[] DEFAULT_TEAM_ABILITY_STRINGS = {
"Ability_Avatar_Dive_Team"
};
public static final SparseSet ILLEGAL_WEAPONS = new SparseSet("""
10000-10008, 11411, 11506-11508, 12505, 12506, 12508, 12509,
13503, 13506, 14411, 14503, 14505, 14508, 15504-15506

View File

@ -86,6 +86,12 @@ public final class SetPropCommand implements CommandHandler {
this.props.put("fly", flyable);
this.props.put("glider", 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
@ -129,6 +135,7 @@ public final class SetPropCommand implements CommandHandler {
case SET_OPENSTATE -> this.setOpenState(targetPlayer, value, 1);
case UNSET_OPENSTATE -> this.setOpenState(targetPlayer, value, 0);
case UNLOCK_MAP -> unlockMap(targetPlayer, value);
case CAN_DIVE -> canDive(targetPlayer, value);
default -> targetPlayer.setProperty(prop.prop, value);
};
@ -219,6 +226,20 @@ public final class SetPropCommand implements CommandHandler {
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) {
// Unlock.
GameData.getScenePointsPerScene()
@ -270,7 +291,8 @@ public final class SetPropCommand implements CommandHandler {
SET_OPENSTATE,
UNSET_OPENSTATE,
UNLOCK_MAP,
IS_FLYABLE
IS_FLYABLE,
CAN_DIVE
}
static class Prop {

View File

@ -579,14 +579,20 @@ public class Player implements PlayerHook, FieldFetch {
this.setOrFetch(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, 50);
this.setOrFetch(PlayerProperty.PROP_IS_FLYABLE,
withQuesting ? 0 : 1);
this.setOrFetch(PlayerProperty.PROP_PLAYER_CAN_DIVE,
withQuesting ? 0 : 1);
this.setOrFetch(PlayerProperty.PROP_IS_TRANSFERABLE, 1);
this.setOrFetch(PlayerProperty.PROP_MAX_STAMINA,
withQuesting ? 10000 : 24000);
this.setOrFetch(PlayerProperty.PROP_DIVE_MAX_STAMINA,
withQuesting ? 10000 : 0);
this.setOrFetch(PlayerProperty.PROP_PLAYER_RESIN, 160);
// The player's current stamina is always their max stamina.
this.setProperty(PlayerProperty.PROP_CUR_PERSIST_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 max = this.getPropertyMax(prop);
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);
if (sendPacket) {
// Send property change reasons if needed.

View File

@ -1,53 +1,48 @@
package emu.grasscutter.game.player;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Transient;
import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter;
import dev.morphia.annotations.*;
import emu.grasscutter.*;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.EntityBaseGadget;
import emu.grasscutter.game.entity.EntityTeam;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.game.props.EnterReason;
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.game.entity.*;
import emu.grasscutter.game.props.*;
import emu.grasscutter.game.world.*;
import emu.grasscutter.net.packet.*;
import emu.grasscutter.net.proto.*;
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
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.player.PlayerTeamDeathEvent;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import lombok.*;
import java.util.*;
import java.util.stream.Stream;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
@Entity
public final class TeamManager extends BasePlayerDataManager {
@Transient private final List<EntityAvatar> avatars;
@Transient @Getter private final Set<EntityBaseGadget> gadgets;
@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.
@Getter private LinkedHashMap<Integer, TeamInfo> teams;
@Getter
private LinkedHashMap<Integer, TeamInfo> teams;
private int currentTeamIndex;
@Getter @Setter private int currentCharacterIndex;
@Transient @Getter @Setter private TeamInfo mpTeam;
@ -69,6 +64,7 @@ public final class TeamManager extends BasePlayerDataManager {
this.gadgets = new HashSet<>();
this.teamResonances = new IntOpenHashSet();
this.teamResonancesConfig = new IntOpenHashSet();
this.teamAbilityEmbryos = new HashSet<>();
this.trialAvatars = new HashMap<>();
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() {
return this.getPlayer().getWorld();
}

View File

@ -1,10 +1,10 @@
package emu.grasscutter.game.props;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.stream.Stream;
import it.unimi.dsi.fastutil.ints.*;
import lombok.Getter;
import java.util.stream.Stream;
public enum PlayerProperty {
PROP_NONE(0),
PROP_EXP(1001, 0),
@ -60,7 +60,10 @@ public enum PlayerProperty {
PROP_IS_AUTO_UNLOCK_SPECIFIC_EQUIP(10044), // 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_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 Int2ObjectMap<PlayerProperty> map = new Int2ObjectOpenHashMap<>();

View File

@ -3,9 +3,7 @@ package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.AbilityControlBlockOuterClass;
import emu.grasscutter.net.packet.*;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AvatarEnterSceneInfoOuterClass.AvatarEnterSceneInfo;
import emu.grasscutter.net.proto.MPLevelEntityInfoOuterClass.MPLevelEntityInfo;
@ -28,8 +26,7 @@ public class PacketPlayerEnterSceneInfoNotify extends BasePacket {
TeamEnterSceneInfo.newBuilder()
.setTeamEntityId(player.getTeamManager().getEntity().getId()) // 150995833
.setTeamAbilityInfo(empty)
.setAbilityControlBlock(
AbilityControlBlockOuterClass.AbilityControlBlock.newBuilder().build()));
.setAbilityControlBlock(player.getTeamManager().getAbilityControlBlock()));
proto.setMpLevelEntityInfo(
MPLevelEntityInfo.newBuilder()
.setEntityId(player.getWorld().getLevelEntityId()) // 184550274