Implement basic stamina system (Everyone disliked that)

This commit is contained in:
Melledy
2023-11-12 17:59:20 -08:00
parent c379b8dea3
commit 541ae7cf77
8 changed files with 502 additions and 15 deletions

View File

@@ -0,0 +1,421 @@
// Code generated by protocol buffer compiler. Do not edit!
package emu.lunarcore.proto;
import java.io.IOException;
import us.hebi.quickbuf.FieldName;
import us.hebi.quickbuf.InvalidProtocolBufferException;
import us.hebi.quickbuf.JsonSink;
import us.hebi.quickbuf.JsonSource;
import us.hebi.quickbuf.MessageFactory;
import us.hebi.quickbuf.ProtoMessage;
import us.hebi.quickbuf.ProtoSink;
import us.hebi.quickbuf.ProtoSource;
public final class StaminaInfoScNotifyOuterClass {
/**
* Protobuf type {@code StaminaInfoScNotify}
*/
public static final class StaminaInfoScNotify extends ProtoMessage<StaminaInfoScNotify> implements Cloneable {
private static final long serialVersionUID = 0L;
/**
* <code>optional int64 next_recover_time = 3;</code>
*/
private long nextRecoverTime;
/**
* <code>optional uint32 stamina = 5;</code>
*/
private int stamina;
/**
* <code>optional uint32 reserve_stamina = 11;</code>
*/
private int reserveStamina;
private StaminaInfoScNotify() {
}
/**
* @return a new empty instance of {@code StaminaInfoScNotify}
*/
public static StaminaInfoScNotify newInstance() {
return new StaminaInfoScNotify();
}
/**
* <code>optional int64 next_recover_time = 3;</code>
* @return whether the nextRecoverTime field is set
*/
public boolean hasNextRecoverTime() {
return (bitField0_ & 0x00000001) != 0;
}
/**
* <code>optional int64 next_recover_time = 3;</code>
* @return this
*/
public StaminaInfoScNotify clearNextRecoverTime() {
bitField0_ &= ~0x00000001;
nextRecoverTime = 0L;
return this;
}
/**
* <code>optional int64 next_recover_time = 3;</code>
* @return the nextRecoverTime
*/
public long getNextRecoverTime() {
return nextRecoverTime;
}
/**
* <code>optional int64 next_recover_time = 3;</code>
* @param value the nextRecoverTime to set
* @return this
*/
public StaminaInfoScNotify setNextRecoverTime(final long value) {
bitField0_ |= 0x00000001;
nextRecoverTime = value;
return this;
}
/**
* <code>optional uint32 stamina = 5;</code>
* @return whether the stamina field is set
*/
public boolean hasStamina() {
return (bitField0_ & 0x00000002) != 0;
}
/**
* <code>optional uint32 stamina = 5;</code>
* @return this
*/
public StaminaInfoScNotify clearStamina() {
bitField0_ &= ~0x00000002;
stamina = 0;
return this;
}
/**
* <code>optional uint32 stamina = 5;</code>
* @return the stamina
*/
public int getStamina() {
return stamina;
}
/**
* <code>optional uint32 stamina = 5;</code>
* @param value the stamina to set
* @return this
*/
public StaminaInfoScNotify setStamina(final int value) {
bitField0_ |= 0x00000002;
stamina = value;
return this;
}
/**
* <code>optional uint32 reserve_stamina = 11;</code>
* @return whether the reserveStamina field is set
*/
public boolean hasReserveStamina() {
return (bitField0_ & 0x00000004) != 0;
}
/**
* <code>optional uint32 reserve_stamina = 11;</code>
* @return this
*/
public StaminaInfoScNotify clearReserveStamina() {
bitField0_ &= ~0x00000004;
reserveStamina = 0;
return this;
}
/**
* <code>optional uint32 reserve_stamina = 11;</code>
* @return the reserveStamina
*/
public int getReserveStamina() {
return reserveStamina;
}
/**
* <code>optional uint32 reserve_stamina = 11;</code>
* @param value the reserveStamina to set
* @return this
*/
public StaminaInfoScNotify setReserveStamina(final int value) {
bitField0_ |= 0x00000004;
reserveStamina = value;
return this;
}
@Override
public StaminaInfoScNotify copyFrom(final StaminaInfoScNotify other) {
cachedSize = other.cachedSize;
if ((bitField0_ | other.bitField0_) != 0) {
bitField0_ = other.bitField0_;
nextRecoverTime = other.nextRecoverTime;
stamina = other.stamina;
reserveStamina = other.reserveStamina;
}
return this;
}
@Override
public StaminaInfoScNotify mergeFrom(final StaminaInfoScNotify other) {
if (other.isEmpty()) {
return this;
}
cachedSize = -1;
if (other.hasNextRecoverTime()) {
setNextRecoverTime(other.nextRecoverTime);
}
if (other.hasStamina()) {
setStamina(other.stamina);
}
if (other.hasReserveStamina()) {
setReserveStamina(other.reserveStamina);
}
return this;
}
@Override
public StaminaInfoScNotify clear() {
if (isEmpty()) {
return this;
}
cachedSize = -1;
bitField0_ = 0;
nextRecoverTime = 0L;
stamina = 0;
reserveStamina = 0;
return this;
}
@Override
public StaminaInfoScNotify clearQuick() {
if (isEmpty()) {
return this;
}
cachedSize = -1;
bitField0_ = 0;
return this;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof StaminaInfoScNotify)) {
return false;
}
StaminaInfoScNotify other = (StaminaInfoScNotify) o;
return bitField0_ == other.bitField0_
&& (!hasNextRecoverTime() || nextRecoverTime == other.nextRecoverTime)
&& (!hasStamina() || stamina == other.stamina)
&& (!hasReserveStamina() || reserveStamina == other.reserveStamina);
}
@Override
public void writeTo(final ProtoSink output) throws IOException {
if ((bitField0_ & 0x00000001) != 0) {
output.writeRawByte((byte) 24);
output.writeInt64NoTag(nextRecoverTime);
}
if ((bitField0_ & 0x00000002) != 0) {
output.writeRawByte((byte) 40);
output.writeUInt32NoTag(stamina);
}
if ((bitField0_ & 0x00000004) != 0) {
output.writeRawByte((byte) 88);
output.writeUInt32NoTag(reserveStamina);
}
}
@Override
protected int computeSerializedSize() {
int size = 0;
if ((bitField0_ & 0x00000001) != 0) {
size += 1 + ProtoSink.computeInt64SizeNoTag(nextRecoverTime);
}
if ((bitField0_ & 0x00000002) != 0) {
size += 1 + ProtoSink.computeUInt32SizeNoTag(stamina);
}
if ((bitField0_ & 0x00000004) != 0) {
size += 1 + ProtoSink.computeUInt32SizeNoTag(reserveStamina);
}
return size;
}
@Override
@SuppressWarnings("fallthrough")
public StaminaInfoScNotify mergeFrom(final ProtoSource input) throws IOException {
// Enabled Fall-Through Optimization (QuickBuffers)
int tag = input.readTag();
while (true) {
switch (tag) {
case 24: {
// nextRecoverTime
nextRecoverTime = input.readInt64();
bitField0_ |= 0x00000001;
tag = input.readTag();
if (tag != 40) {
break;
}
}
case 40: {
// stamina
stamina = input.readUInt32();
bitField0_ |= 0x00000002;
tag = input.readTag();
if (tag != 88) {
break;
}
}
case 88: {
// reserveStamina
reserveStamina = input.readUInt32();
bitField0_ |= 0x00000004;
tag = input.readTag();
if (tag != 0) {
break;
}
}
case 0: {
return this;
}
default: {
if (!input.skipField(tag)) {
return this;
}
tag = input.readTag();
break;
}
}
}
}
@Override
public void writeTo(final JsonSink output) throws IOException {
output.beginObject();
if ((bitField0_ & 0x00000001) != 0) {
output.writeInt64(FieldNames.nextRecoverTime, nextRecoverTime);
}
if ((bitField0_ & 0x00000002) != 0) {
output.writeUInt32(FieldNames.stamina, stamina);
}
if ((bitField0_ & 0x00000004) != 0) {
output.writeUInt32(FieldNames.reserveStamina, reserveStamina);
}
output.endObject();
}
@Override
public StaminaInfoScNotify mergeFrom(final JsonSource input) throws IOException {
if (!input.beginObject()) {
return this;
}
while (!input.isAtEnd()) {
switch (input.readFieldHash()) {
case -1964148386:
case 394600084: {
if (input.isAtField(FieldNames.nextRecoverTime)) {
if (!input.trySkipNullValue()) {
nextRecoverTime = input.readInt64();
bitField0_ |= 0x00000001;
}
} else {
input.skipUnknownField();
}
break;
}
case -1897344401: {
if (input.isAtField(FieldNames.stamina)) {
if (!input.trySkipNullValue()) {
stamina = input.readUInt32();
bitField0_ |= 0x00000002;
}
} else {
input.skipUnknownField();
}
break;
}
case -273362413:
case -799929876: {
if (input.isAtField(FieldNames.reserveStamina)) {
if (!input.trySkipNullValue()) {
reserveStamina = input.readUInt32();
bitField0_ |= 0x00000004;
}
} else {
input.skipUnknownField();
}
break;
}
default: {
input.skipUnknownField();
break;
}
}
}
input.endObject();
return this;
}
@Override
public StaminaInfoScNotify clone() {
return new StaminaInfoScNotify().copyFrom(this);
}
@Override
public boolean isEmpty() {
return ((bitField0_) == 0);
}
public static StaminaInfoScNotify parseFrom(final byte[] data) throws
InvalidProtocolBufferException {
return ProtoMessage.mergeFrom(new StaminaInfoScNotify(), data).checkInitialized();
}
public static StaminaInfoScNotify parseFrom(final ProtoSource input) throws IOException {
return ProtoMessage.mergeFrom(new StaminaInfoScNotify(), input).checkInitialized();
}
public static StaminaInfoScNotify parseFrom(final JsonSource input) throws IOException {
return ProtoMessage.mergeFrom(new StaminaInfoScNotify(), input).checkInitialized();
}
/**
* @return factory for creating StaminaInfoScNotify messages
*/
public static MessageFactory<StaminaInfoScNotify> getFactory() {
return StaminaInfoScNotifyFactory.INSTANCE;
}
private enum StaminaInfoScNotifyFactory implements MessageFactory<StaminaInfoScNotify> {
INSTANCE;
@Override
public StaminaInfoScNotify create() {
return StaminaInfoScNotify.newInstance();
}
}
/**
* Contains name constants used for serializing JSON
*/
static class FieldNames {
static final FieldName nextRecoverTime = FieldName.forField("nextRecoverTime", "next_recover_time");
static final FieldName stamina = FieldName.forField("stamina");
static final FieldName reserveStamina = FieldName.forField("reserveStamina", "reserve_stamina");
}
}
}

View File

@@ -18,6 +18,7 @@ public class GameConstants {
public static final int MATERIAL_HCOIN_ID = 1; // Material id for jades. DO NOT CHANGE
public static final int MATERIAL_COIN_ID = 2; // Material id for credits. DO NOT CHANGE
public static final int MAX_STAMINA = 240;
public static final int MAX_STAMINA_RESERVE = 2400;
public static final int MAX_AVATARS_IN_TEAM = 4;
public static final int DEFAULT_TEAMS = 6;
public static final int MAX_MP = 5; // Client doesnt like more than 5

View File

@@ -33,6 +33,7 @@ public class Battle {
private final List<GameItem> drops;
private final long timestamp;
@Setter private int staminaCost;
@Setter private int levelOverride;
@Setter private int roundsLimit;

View File

@@ -160,7 +160,11 @@ public class BattleService extends BaseGameService {
// Get waves
wave = Math.min(Math.max(1, wave), cocoonExcel.getMaxWave());
// TODO sanity check stamina
// Sanity check stamina
int cost = cocoonExcel.getStaminaCost() * wave;
if (player.getStamina() < cost) {
return;
}
// Get stages from cocoon
List<StageExcel> stages = new ArrayList<>();
@@ -181,6 +185,8 @@ public class BattleService extends BaseGameService {
// Build battle from cocoon data
Battle battle = new Battle(player, player.getLineupManager().getCurrentLineup(), stages);
battle.setStaminaCost(cost);
player.setBattle(battle);
// Send packet
@@ -212,6 +218,10 @@ public class BattleService extends BaseGameService {
}
// Drops
getServer().getDropService().calculateDrops(battle);
// Spend stamina
if (battle.getStaminaCost() > 0) {
player.spendStamina(battle.getStaminaCost());
}
}
case BATTLE_END_LOSE -> {
// Set avatar hp to 20% if the player's party is downed

View File

@@ -44,10 +44,7 @@ import emu.lunarcore.server.game.GameServer;
import emu.lunarcore.server.game.GameSession;
import emu.lunarcore.server.packet.BasePacket;
import emu.lunarcore.server.packet.SessionState;
import emu.lunarcore.server.packet.send.PacketEnterSceneByServerScNotify;
import emu.lunarcore.server.packet.send.PacketPlayerSyncScNotify;
import emu.lunarcore.server.packet.send.PacketSceneEntityMoveScNotify;
import emu.lunarcore.server.packet.send.PacketSyncRogueVirtualItemInfoScNotify;
import emu.lunarcore.server.packet.send.*;
import emu.lunarcore.util.Position;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
@@ -69,11 +66,13 @@ public class Player {
private int level;
private int exp;
private int worldLevel;
private int stamina;
private int scoin; // Credits
private int hcoin; // Jade
private int mcoin; // Crystals
private int talentPoints;
private int talentPoints; // Rogue talent points
private int stamina;
private long nextStaminaRecover;
private transient Battle battle;
private transient Scene scene;
@@ -307,11 +306,6 @@ public class Player {
this.sendPacket(new PacketSyncRogueVirtualItemInfoScNotify(this));
}
public void addStamina(int amount) {
this.stamina = Math.min(this.stamina + amount, GameConstants.MAX_STAMINA);
this.sendPacket(new PacketPlayerSyncScNotify(this));
}
public void addExp(int amount) {
// Required exp
int reqExp = GameData.getPlayerExpRequired(level + 1);
@@ -363,6 +357,42 @@ public class Player {
this.battle = battle;
}
public void addStamina(int amount) {
this.stamina = Math.min(this.stamina + amount, GameConstants.MAX_STAMINA);
this.sendPacket(new PacketStaminaInfoScNotify(this));
}
public void spendStamina(int amount) {
this.stamina = Math.max(this.stamina - amount, 0);
this.sendPacket(new PacketStaminaInfoScNotify(this));
}
private void updateStamina() {
// Get current timestamp
long time = System.currentTimeMillis();
boolean hasChanged = false;
// Check if we can add stamina
while (time >= this.nextStaminaRecover) {
// Add stamina
if (this.stamina < GameConstants.MAX_STAMINA) {
this.stamina += 1;
hasChanged = true;
}
// Calculate next stamina recover time
if (this.stamina >= GameConstants.MAX_STAMINA) {
this.nextStaminaRecover = time;
}
this.nextStaminaRecover += 5 * 60 * 1000;
}
// Send packet
if (hasChanged && this.hasLoggedIn()) {
this.getSession().send(new PacketStaminaInfoScNotify(this));
}
}
public EntityProp interactWithProp(int propEntityId) {
// Sanity
if (this.getScene() == null) return null;
@@ -532,7 +562,7 @@ public class Player {
}
public void onTick() {
this.updateStamina();
}
// Database
@@ -561,6 +591,9 @@ public class Player {
// Post database load
this.getAvatars().setupHeroPaths();
// Update stamina
this.updateStamina();
// Check instances
if (this.getChallengeInstance() != null && !this.getChallengeInstance().validate(this)) {
// Delete instance if it failed to validate (example: missing an excel)

View File

@@ -6,6 +6,7 @@ import emu.lunarcore.server.packet.Opcodes;
import emu.lunarcore.server.packet.PacketHandler;
import emu.lunarcore.server.packet.SessionState;
import emu.lunarcore.server.packet.send.PacketPlayerLoginScRsp;
import emu.lunarcore.server.packet.send.PacketStaminaInfoScNotify;
@Opcodes(CmdId.PlayerLoginCsReq)
public class HandlerPlayerLoginCsReq extends PacketHandler {
@@ -15,8 +16,9 @@ public class HandlerPlayerLoginCsReq extends PacketHandler {
// Set session flag
session.setState(SessionState.ACTIVE);
// Send
// Send packets
session.send(new PacketPlayerLoginScRsp(session));
session.send(new PacketStaminaInfoScNotify(session.getPlayer()));
}
}

View File

@@ -13,7 +13,7 @@ public class PacketGetBasicInfoScRsp extends BasePacket {
var data = GetBasicInfoScRsp.newInstance()
.setCurDay(1)
.setNextRecoverTime(0)
.setNextRecoverTime(session.getPlayer().getNextStaminaRecover() / 1000)
.setGameplayBirthday(session.getPlayer().getBirthday())
.setPlayerSettingInfo(PlayerSettingInfo.newInstance());

View File

@@ -0,0 +1,19 @@
package emu.lunarcore.server.packet.send;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.proto.StaminaInfoScNotifyOuterClass.StaminaInfoScNotify;
import emu.lunarcore.server.packet.BasePacket;
import emu.lunarcore.server.packet.CmdId;
public class PacketStaminaInfoScNotify extends BasePacket {
public PacketStaminaInfoScNotify(Player player) {
super(CmdId.StaminaInfoScNotify);
var data = StaminaInfoScNotify.newInstance()
.setNextRecoverTime(player.getNextStaminaRecover() / 1000)
.setStamina(player.getStamina());
this.setData(data);
}
}