mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-23 16:16:46 +00:00
Merge branch 'development' into patch-3
This commit is contained in:
commit
85821eb16b
@ -70,7 +70,7 @@ dependencies {
|
||||
|
||||
implementation group: 'io.netty', name: 'netty-all', version: '4.1.71.Final'
|
||||
|
||||
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.8'
|
||||
implementation group: 'com.google.code.gson', name: 'gson', version: '2.9.0'
|
||||
implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.18.2'
|
||||
|
||||
implementation group: 'org.reflections', name: 'reflections', version: '0.10.2'
|
||||
|
5
data/TowerSchedule.json
Normal file
5
data/TowerSchedule.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"scheduleId" : 1,
|
||||
"scheduleStartTime" : "2022-05-01T00:00:00+08:00",
|
||||
"nextScheduleChangeTime" : "2022-05-30T00:00:00+08:00"
|
||||
}
|
11
proto/AbilityActionGenerateElemBall.proto
Normal file
11
proto/AbilityActionGenerateElemBall.proto
Normal file
@ -0,0 +1,11 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "Vector.proto";
|
||||
|
||||
message AbilityActionGenerateElemBall {
|
||||
Vector pos = 1;
|
||||
Vector rot = 2;
|
||||
uint32 room_id = 3;
|
||||
}
|
21
proto/AbilityMetaModifierChange.proto
Normal file
21
proto/AbilityMetaModifierChange.proto
Normal file
@ -0,0 +1,21 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "ModifierAction.proto";
|
||||
import "AbilityString.proto";
|
||||
import "AbilityAttachedModifier.proto";
|
||||
import "ModifierProperty.proto";
|
||||
|
||||
message AbilityMetaModifierChange {
|
||||
ModifierAction action = 1;
|
||||
AbilityString parent_ability_name = 2;
|
||||
AbilityString parent_ability_override = 3;
|
||||
AbilityAttachedModifier attached_instanced_modifier = 4;
|
||||
repeated ModifierProperty properties = 5;
|
||||
int32 modifier_local_id = 6;
|
||||
bool is_mute_remote = 7;
|
||||
uint32 apply_entity_id = 8;
|
||||
bool is_attached_parent_ability = 9;
|
||||
uint32 server_buff_uid = 10;
|
||||
}
|
9
proto/AbilityMetaReInitOverrideMap.proto
Normal file
9
proto/AbilityMetaReInitOverrideMap.proto
Normal file
@ -0,0 +1,9 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "AbilityScalarValueEntry.proto";
|
||||
|
||||
message AbilityMetaReInitOverrideMap {
|
||||
repeated AbilityScalarValueEntry override_map = 1;
|
||||
}
|
8
proto/ModifierAction.proto
Normal file
8
proto/ModifierAction.proto
Normal file
@ -0,0 +1,8 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
enum ModifierAction {
|
||||
ADDED = 0;
|
||||
REMOVED = 1;
|
||||
}
|
10
proto/ModifierProperty.proto
Normal file
10
proto/ModifierProperty.proto
Normal file
@ -0,0 +1,10 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "AbilityString.proto";
|
||||
|
||||
message ModifierProperty {
|
||||
AbilityString key = 1;
|
||||
float value = 2;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.tower.TowerLevelRecord;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "unlocktower", usage = "unlocktower", aliases = {"ut"},
|
||||
description = "Unlock all levels of tower", permission = "player.tower")
|
||||
public class UnlockTowerCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
unlockFloor(sender, sender.getServer().getTowerScheduleManager()
|
||||
.getCurrentTowerScheduleData().getEntranceFloorId());
|
||||
|
||||
unlockFloor(sender, sender.getServer().getTowerScheduleManager()
|
||||
.getScheduleFloors());
|
||||
|
||||
CommandHandler.sendMessage(sender, translate("commands.tower.unlock_done"));
|
||||
}
|
||||
|
||||
public void unlockFloor(Player player, List<Integer> floors){
|
||||
floors.stream()
|
||||
.filter(id -> !player.getTowerManager().getRecordMap().containsKey(id))
|
||||
.forEach(id -> player.getTowerManager().getRecordMap().put(id, new TowerLevelRecord(id)));
|
||||
}
|
||||
}
|
@ -9,6 +9,8 @@ import java.util.Map;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import emu.grasscutter.data.custom.AbilityEmbryoEntry;
|
||||
import emu.grasscutter.data.custom.AbilityModifier;
|
||||
import emu.grasscutter.data.custom.AbilityModifierEntry;
|
||||
import emu.grasscutter.data.custom.OpenConfigEntry;
|
||||
import emu.grasscutter.data.custom.ScenePointEntry;
|
||||
import emu.grasscutter.data.def.*;
|
||||
@ -22,6 +24,7 @@ public class GameData {
|
||||
// BinOutputs
|
||||
private static final Int2ObjectMap<String> abilityHashes = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<String, AbilityEmbryoEntry> abilityEmbryos = new HashMap<>();
|
||||
private static final Map<String, AbilityModifierEntry> abilityModifiers = new HashMap<>();
|
||||
private static final Map<String, OpenConfigEntry> openConfigEntries = new HashMap<>();
|
||||
private static final Map<String, ScenePointEntry> scenePointEntries = new HashMap<>();
|
||||
|
||||
@ -70,6 +73,7 @@ public class GameData {
|
||||
private static final Int2ObjectMap<RewardPreviewData> rewardPreviewDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<TowerFloorData> towerFloorDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<TowerLevelData> towerLevelDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<TowerScheduleData> towerScheduleDataMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
// Cache
|
||||
private static Map<Integer, List<Integer>> fetters = new HashMap<>();
|
||||
@ -101,6 +105,10 @@ public class GameData {
|
||||
return abilityEmbryos;
|
||||
}
|
||||
|
||||
public static Map<String, AbilityModifierEntry> getAbilityModifiers() {
|
||||
return abilityModifiers;
|
||||
}
|
||||
|
||||
public static Map<String, OpenConfigEntry> getOpenConfigEntries() {
|
||||
return openConfigEntries;
|
||||
}
|
||||
@ -320,4 +328,7 @@ public class GameData {
|
||||
public static Int2ObjectMap<TowerLevelData> getTowerLevelDataMap(){
|
||||
return towerLevelDataMap;
|
||||
}
|
||||
public static Int2ObjectMap<TowerScheduleData> getTowerScheduleDataMap(){
|
||||
return towerScheduleDataMap;
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,11 @@ import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.common.PointData;
|
||||
import emu.grasscutter.data.common.ScenePointConfig;
|
||||
import emu.grasscutter.data.custom.AbilityEmbryoEntry;
|
||||
import emu.grasscutter.data.custom.AbilityModifier;
|
||||
import emu.grasscutter.data.custom.AbilityModifier.AbilityConfigData;
|
||||
import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierAction;
|
||||
import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierActionType;
|
||||
import emu.grasscutter.data.custom.AbilityModifierEntry;
|
||||
import emu.grasscutter.data.custom.OpenConfigEntry;
|
||||
import emu.grasscutter.data.custom.ScenePointEntry;
|
||||
import emu.grasscutter.game.world.SpawnDataEntry;
|
||||
@ -47,6 +52,7 @@ public class ResourceLoader {
|
||||
// Load ability lists
|
||||
loadAbilityEmbryos();
|
||||
loadOpenConfig();
|
||||
loadAbilityModifiers();
|
||||
// Load resources
|
||||
loadResources();
|
||||
// Process into depots
|
||||
@ -244,6 +250,69 @@ public class ResourceLoader {
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadAbilityModifiers() {
|
||||
// Load from BinOutput
|
||||
File folder = new File(Utils.toFilePath(Grasscutter.getConfig().RESOURCE_FOLDER + "BinOutput/Ability/Temp/AvatarAbilities/"));
|
||||
File[] files = folder.listFiles();
|
||||
if (files == null) {
|
||||
Grasscutter.getLogger().error("Error loading ability modifiers: no files found in " + folder.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
|
||||
for (File file : files) {
|
||||
List<AbilityConfigData> abilityConfigList = null;
|
||||
|
||||
try (FileReader fileReader = new FileReader(file)) {
|
||||
abilityConfigList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, AbilityConfigData.class).getType());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
|
||||
for (AbilityConfigData data : abilityConfigList) {
|
||||
if (data.Default.modifiers == null || data.Default.modifiers.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AbilityModifierEntry modifierEntry = new AbilityModifierEntry(data.Default.abilityName);
|
||||
|
||||
for (Entry<String, AbilityModifier> entry : data.Default.modifiers.entrySet()) {
|
||||
AbilityModifier modifier = entry.getValue();
|
||||
|
||||
// Stare.
|
||||
if (modifier.onAdded != null) {
|
||||
for (AbilityModifierAction action : modifier.onAdded) {
|
||||
if (action.$type.contains("HealHP")) {
|
||||
action.type = AbilityModifierActionType.HealHP;
|
||||
modifierEntry.getOnAdded().add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (modifier.onThinkInterval != null) {
|
||||
for (AbilityModifierAction action : modifier.onThinkInterval) {
|
||||
if (action.$type.contains("HealHP")) {
|
||||
action.type = AbilityModifierActionType.HealHP;
|
||||
modifierEntry.getOnThinkInterval().add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (modifier.onRemoved != null) {
|
||||
for (AbilityModifierAction action : modifier.onRemoved) {
|
||||
if (action.$type.contains("HealHP")) {
|
||||
action.type = AbilityModifierActionType.HealHP;
|
||||
modifierEntry.getOnRemoved().add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GameData.getAbilityModifiers().put(modifierEntry.getName(), modifierEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadSpawnData() {
|
||||
// Read from cached file if exists
|
||||
File spawnDataEntries = new File(Grasscutter.getConfig().DATA_FOLDER + "Spawns.json");
|
||||
|
@ -0,0 +1,36 @@
|
||||
package emu.grasscutter.data.custom;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class AbilityModifier {
|
||||
public AbilityModifierAction[] onAdded;
|
||||
public AbilityModifierAction[] onThinkInterval;
|
||||
public AbilityModifierAction[] onRemoved;
|
||||
|
||||
public static class AbilityConfigData {
|
||||
public AbilityData Default;
|
||||
}
|
||||
|
||||
public static class AbilityData {
|
||||
public String abilityName;
|
||||
public Map<String, AbilityModifier> modifiers;
|
||||
}
|
||||
|
||||
public static class AbilityModifierAction {
|
||||
public String $type;
|
||||
public AbilityModifierActionType type;
|
||||
public String target;
|
||||
public AbilityModifierValue amount;
|
||||
public AbilityModifierValue amountByTargetCurrentHPRatio;
|
||||
}
|
||||
|
||||
public static class AbilityModifierValue {
|
||||
public boolean isFormula;
|
||||
public boolean isDynamic;
|
||||
public String dynamicKey;
|
||||
}
|
||||
|
||||
public enum AbilityModifierActionType {
|
||||
HealHP, ApplyModifier, LoseHP;
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package emu.grasscutter.data.custom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierAction;
|
||||
|
||||
public class AbilityModifierEntry {
|
||||
private String name; // Custom value
|
||||
public List<AbilityModifierAction> onModifierAdded;
|
||||
public List<AbilityModifierAction> onThinkInterval;
|
||||
public List<AbilityModifierAction> onRemoved;
|
||||
|
||||
public AbilityModifierEntry(String name) {
|
||||
this.name = name;
|
||||
this.onModifierAdded = new ArrayList<>();
|
||||
this.onThinkInterval = new ArrayList<>();
|
||||
this.onRemoved = new ArrayList<>();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<AbilityModifierAction> getOnAdded() {
|
||||
return onModifierAdded;
|
||||
}
|
||||
|
||||
public List<AbilityModifierAction> getOnThinkInterval() {
|
||||
return onThinkInterval;
|
||||
}
|
||||
|
||||
public List<AbilityModifierAction> getOnRemoved() {
|
||||
return onRemoved;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ResourceType(name = "TowerScheduleExcelConfigData.json")
|
||||
public class TowerScheduleData extends GameResource {
|
||||
private int ScheduleId;
|
||||
private List<Integer> EntranceFloorId;
|
||||
private List<ScheduleDetail> Schedules;
|
||||
private int MonthlyLevelConfigId;
|
||||
@Override
|
||||
public int getId() {
|
||||
return ScheduleId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
super.onLoad();
|
||||
this.Schedules = this.Schedules.stream()
|
||||
.filter(item -> item.getFloorList().size() > 0)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public int getScheduleId() {
|
||||
return ScheduleId;
|
||||
}
|
||||
|
||||
public void setScheduleId(int scheduleId) {
|
||||
ScheduleId = scheduleId;
|
||||
}
|
||||
|
||||
public List<Integer> getEntranceFloorId() {
|
||||
return EntranceFloorId;
|
||||
}
|
||||
|
||||
public void setEntranceFloorId(List<Integer> entranceFloorId) {
|
||||
EntranceFloorId = entranceFloorId;
|
||||
}
|
||||
|
||||
public List<ScheduleDetail> getSchedules() {
|
||||
return Schedules;
|
||||
}
|
||||
|
||||
public void setSchedules(List<ScheduleDetail> schedules) {
|
||||
Schedules = schedules;
|
||||
}
|
||||
|
||||
public int getMonthlyLevelConfigId() {
|
||||
return MonthlyLevelConfigId;
|
||||
}
|
||||
|
||||
public void setMonthlyLevelConfigId(int monthlyLevelConfigId) {
|
||||
MonthlyLevelConfigId = monthlyLevelConfigId;
|
||||
}
|
||||
|
||||
public static class ScheduleDetail{
|
||||
private List<Integer> FloorList;
|
||||
|
||||
public List<Integer> getFloorList() {
|
||||
return FloorList;
|
||||
}
|
||||
|
||||
public void setFloorList(List<Integer> floorList) {
|
||||
FloorList = floorList;
|
||||
}
|
||||
}
|
||||
}
|
202
src/main/java/emu/grasscutter/game/ability/AbilityManager.java
Normal file
202
src/main/java/emu/grasscutter/game/ability/AbilityManager.java
Normal file
@ -0,0 +1,202 @@
|
||||
package emu.grasscutter.game.ability;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.custom.AbilityModifier;
|
||||
import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierAction;
|
||||
import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierActionType;
|
||||
import emu.grasscutter.data.def.ItemData;
|
||||
import emu.grasscutter.data.custom.AbilityModifierEntry;
|
||||
import emu.grasscutter.game.entity.EntityItem;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall;
|
||||
import emu.grasscutter.net.proto.AbilityInvokeArgumentOuterClass.AbilityInvokeArgument;
|
||||
import emu.grasscutter.net.proto.AbilityInvokeEntryHeadOuterClass.AbilityInvokeEntryHead;
|
||||
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
||||
import emu.grasscutter.net.proto.AbilityMetaModifierChangeOuterClass.AbilityMetaModifierChange;
|
||||
import emu.grasscutter.net.proto.AbilityMetaReInitOverrideMapOuterClass.AbilityMetaReInitOverrideMap;
|
||||
import emu.grasscutter.net.proto.AbilityScalarTypeOuterClass.AbilityScalarType;
|
||||
import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalarValueEntry;
|
||||
import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
|
||||
public class AbilityManager {
|
||||
private Player player;
|
||||
|
||||
public AbilityManager(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception {
|
||||
//System.out.println(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray()));
|
||||
switch (invoke.getArgumentType()) {
|
||||
case ABILITY_META_OVERRIDE_PARAM:
|
||||
handleOverrideParam(invoke);
|
||||
break;
|
||||
case ABILITY_META_REINIT_OVERRIDEMAP:
|
||||
handleReinitOverrideMap(invoke);
|
||||
break;
|
||||
case ABILITY_META_MODIFIER_CHANGE:
|
||||
handleModifierChange(invoke);
|
||||
break;
|
||||
case ABILITY_MIXIN_COST_STAMINA:
|
||||
handleMixinCostStamina(invoke);
|
||||
break;
|
||||
case ABILITY_ACTION_GENERATE_ELEM_BALL:
|
||||
handleGenerateElemBall(invoke);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleOverrideParam(AbilityInvokeEntry invoke) throws Exception {
|
||||
GameEntity entity = player.getScene().getEntityById(invoke.getEntityId());
|
||||
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AbilityScalarValueEntry entry = AbilityScalarValueEntry.parseFrom(invoke.getAbilityData());
|
||||
|
||||
entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue());
|
||||
}
|
||||
|
||||
private void handleReinitOverrideMap(AbilityInvokeEntry invoke) throws Exception {
|
||||
GameEntity entity = player.getScene().getEntityById(invoke.getEntityId());
|
||||
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AbilityMetaReInitOverrideMap map = AbilityMetaReInitOverrideMap.parseFrom(invoke.getAbilityData());
|
||||
|
||||
for (AbilityScalarValueEntry entry : map.getOverrideMapList()) {
|
||||
entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleModifierChange(AbilityInvokeEntry invoke) throws Exception {
|
||||
GameEntity target = player.getScene().getEntityById(invoke.getEntityId());
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AbilityInvokeEntryHead head = invoke.getHead();
|
||||
if (head == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AbilityMetaModifierChange data = AbilityMetaModifierChange.parseFrom(invoke.getAbilityData());
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameEntity sourceEntity = player.getScene().getEntityById(data.getApplyEntityId());
|
||||
if (sourceEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This is not how it works but we will keep it for now since healing abilities dont work properly anyways
|
||||
if (data.getAction() == ModifierAction.ADDED && data.getParentAbilityName() != null) {
|
||||
// Handle add modifier here
|
||||
String modifierString = data.getParentAbilityName().getStr();
|
||||
AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString);
|
||||
|
||||
if (modifier != null && modifier.getOnAdded().size() > 0) {
|
||||
for (AbilityModifierAction action : modifier.getOnAdded()) {
|
||||
invokeAction(action, target, sourceEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// Add to meta modifier list
|
||||
target.getMetaModifiers().put(head.getInstancedModifierId(), modifierString);
|
||||
} else if (data.getAction() == ModifierAction.REMOVED) {
|
||||
String modifierString = target.getMetaModifiers().get(head.getInstancedModifierId());
|
||||
|
||||
if (modifierString != null) {
|
||||
// Get modifier and call on remove event
|
||||
AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString);
|
||||
|
||||
if (modifier != null && modifier.getOnRemoved().size() > 0) {
|
||||
for (AbilityModifierAction action : modifier.getOnRemoved()) {
|
||||
invokeAction(action, target, sourceEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from meta modifiers
|
||||
target.getMetaModifiers().remove(head.getInstancedModifierId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMixinCostStamina(AbilityInvokeEntry invoke) {
|
||||
// Not the right way of doing this
|
||||
if (Grasscutter.getConfig().OpenStamina) {
|
||||
// getPlayer().getStaminaManager().updateStamina(getPlayer().getSession(), -450);
|
||||
// TODO
|
||||
// set flag in stamina/movement manager that specifies the player is currently using an alternate sprint
|
||||
}
|
||||
}
|
||||
|
||||
private void handleGenerateElemBall(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException {
|
||||
AbilityActionGenerateElemBall action = AbilityActionGenerateElemBall.parseFrom(invoke.getAbilityData());
|
||||
if (action == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemData itemData = GameData.getItemDataMap().get(2024);
|
||||
if (itemData == null) {
|
||||
return; // Should never happen
|
||||
}
|
||||
|
||||
EntityItem energyBall = new EntityItem(getPlayer().getScene(), getPlayer(), itemData, new Position(action.getPos()), 1);
|
||||
energyBall.getRotation().set(action.getRot());
|
||||
|
||||
getPlayer().getScene().addEntity(energyBall);
|
||||
}
|
||||
|
||||
private void invokeAction(AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) {
|
||||
switch (action.type) {
|
||||
case HealHP -> {
|
||||
if (action.amount == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float healAmount = 0;
|
||||
|
||||
if (action.amount.isDynamic && action.amount.dynamicKey != null) {
|
||||
healAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f);
|
||||
}
|
||||
|
||||
if (healAmount > 0) {
|
||||
target.heal(healAmount);
|
||||
}
|
||||
}
|
||||
case LoseHP -> {
|
||||
if (action.amountByTargetCurrentHPRatio == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float damageAmount = 0;
|
||||
|
||||
if (action.amount.isDynamic && action.amount.dynamicKey != null) {
|
||||
damageAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f);
|
||||
}
|
||||
|
||||
if (damageAmount > 0) {
|
||||
target.damage(damageAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ public class Avatar {
|
||||
|
||||
@Transient private Player owner;
|
||||
@Transient private AvatarData data;
|
||||
@Transient private AvatarSkillDepotData skillDepot;
|
||||
@Transient private long guid; // Player unique id
|
||||
private int avatarId; // Id of avatar
|
||||
|
||||
@ -103,8 +104,8 @@ public class Avatar {
|
||||
private int nameCardRewardId;
|
||||
private int nameCardId;
|
||||
|
||||
@Deprecated // Do not use. Morhpia only!
|
||||
public Avatar() {
|
||||
// Morhpia only!
|
||||
this.equips = new Int2ObjectOpenHashMap<>();
|
||||
this.fightProp = new Int2FloatOpenHashMap();
|
||||
this.extraAbilityEmbryos = new HashSet<>();
|
||||
@ -140,7 +141,7 @@ public class Avatar {
|
||||
}
|
||||
|
||||
// Skill depot
|
||||
this.setSkillDepot(getAvatarData().getSkillDepot());
|
||||
this.setSkillDepotData(getAvatarData().getSkillDepot());
|
||||
|
||||
// Set stats
|
||||
this.recalcStats();
|
||||
@ -164,7 +165,8 @@ public class Avatar {
|
||||
}
|
||||
|
||||
protected void setAvatarData(AvatarData data) {
|
||||
this.data = data;
|
||||
if (this.data != null) return;
|
||||
this.data = data; // Used while loading this from the database
|
||||
}
|
||||
|
||||
public int getOwnerId() {
|
||||
@ -257,9 +259,19 @@ public class Avatar {
|
||||
return skillDepotId;
|
||||
}
|
||||
|
||||
public void setSkillDepot(AvatarSkillDepotData skillDepot) {
|
||||
// Set id
|
||||
public AvatarSkillDepotData getSkillDepot() {
|
||||
return skillDepot;
|
||||
}
|
||||
|
||||
protected void setSkillDepot(AvatarSkillDepotData skillDepot) {
|
||||
if (this.skillDepot != null) return;
|
||||
this.skillDepot = skillDepot; // Used while loading this from the database
|
||||
}
|
||||
|
||||
public void setSkillDepotData(AvatarSkillDepotData skillDepot) {
|
||||
// Set id and depot
|
||||
this.skillDepotId = skillDepot.getId();
|
||||
this.skillDepot = skillDepot;
|
||||
// Clear, then add skills
|
||||
getSkillLevelMap().clear();
|
||||
if (skillDepot.getEnergySkill() > 0) {
|
||||
@ -501,8 +513,8 @@ public class Avatar {
|
||||
// Set energy usage
|
||||
if (data.getSkillDepot() != null && data.getSkillDepot().getEnergySkillData() != null) {
|
||||
ElementType element = data.getSkillDepot().getElementType();
|
||||
this.setFightProperty(element.getEnergyProperty(), data.getSkillDepot().getEnergySkillData().getCostElemVal());
|
||||
this.setFightProperty((element.getEnergyProperty().getId() % 70) + 1000, data.getSkillDepot().getEnergySkillData().getCostElemVal());
|
||||
this.setFightProperty(element.getMaxEnergyProp(), data.getSkillDepot().getEnergySkillData().getCostElemVal());
|
||||
this.setFightProperty((element.getMaxEnergyProp().getId() % 70) + 1000, data.getSkillDepot().getEnergySkillData().getCostElemVal());
|
||||
}
|
||||
|
||||
// Artifacts
|
||||
|
@ -5,6 +5,7 @@ import java.util.List;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.def.AvatarData;
|
||||
import emu.grasscutter.data.def.AvatarSkillDepotData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
@ -139,12 +140,14 @@ public class AvatarStorage implements Iterable<Avatar> {
|
||||
}
|
||||
|
||||
AvatarData avatarData = GameData.getAvatarDataMap().get(avatar.getAvatarId());
|
||||
if (avatarData == null) {
|
||||
AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId());
|
||||
if (avatarData == null || skillDepot == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set ownerships
|
||||
avatar.setAvatarData(avatarData);
|
||||
avatar.setSkillDepot(skillDepot);
|
||||
avatar.setOwner(getPlayer());
|
||||
|
||||
// Force recalc of const boosted skills
|
||||
|
@ -12,12 +12,18 @@ public class TowerDungeonSettleListener implements DungeonSettleListener {
|
||||
scene.setAutoCloseTime(Utils.getCurrentSeconds() + 1000);
|
||||
var towerManager = scene.getPlayers().get(0).getTowerManager();
|
||||
|
||||
towerManager.notifyCurLevelRecordChangeWhenDone();
|
||||
scene.broadcastPacket(new PacketTowerFloorRecordChangeNotify(towerManager.getCurrentFloorId()));
|
||||
scene.broadcastPacket(new PacketDungeonSettleNotify(scene.getChallenge(),
|
||||
true,
|
||||
towerManager.notifyCurLevelRecordChangeWhenDone(3);
|
||||
scene.broadcastPacket(new PacketTowerFloorRecordChangeNotify(
|
||||
towerManager.getCurrentFloorId(),
|
||||
3,
|
||||
towerManager.canEnterScheduleFloor()
|
||||
));
|
||||
|
||||
scene.broadcastPacket(new PacketDungeonSettleNotify(
|
||||
scene.getChallenge(),
|
||||
towerManager.hasNextFloor(),
|
||||
towerManager.hasNextLevel(),
|
||||
towerManager.getNextFloorId()
|
||||
towerManager.hasNextLevel() ? 0 : towerManager.getNextFloorId()
|
||||
));
|
||||
|
||||
}
|
||||
|
@ -17,17 +17,22 @@ import emu.grasscutter.net.proto.AbilityControlBlockOuterClass.AbilityControlBlo
|
||||
import emu.grasscutter.net.proto.AbilityEmbryoOuterClass.AbilityEmbryo;
|
||||
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
||||
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
|
||||
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
|
||||
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
|
||||
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
|
||||
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
|
||||
import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair;
|
||||
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
||||
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
|
||||
import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType;
|
||||
import emu.grasscutter.net.proto.SceneAvatarInfoOuterClass.SceneAvatarInfo;
|
||||
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.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.utils.Utils;
|
||||
@ -110,6 +115,35 @@ public class EntityAvatar extends GameEntity {
|
||||
this.killedBy = killerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float heal(float amount) {
|
||||
float healed = super.heal(amount);
|
||||
|
||||
if (healed > 0f) {
|
||||
getScene().broadcastPacket(
|
||||
new PacketEntityFightPropChangeReasonNotify(this, FightProperty.FIGHT_PROP_CUR_HP, healed, PropChangeReason.PROP_CHANGE_ABILITY, ChangeHpReason.ChangeHpAddAbility)
|
||||
);
|
||||
}
|
||||
|
||||
return healed;
|
||||
}
|
||||
|
||||
public void addEnergy(float amount) {
|
||||
FightProperty curEnergyProp = getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
|
||||
FightProperty maxEnergyProp = getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
|
||||
|
||||
float curEnergy = this.getFightProperty(curEnergyProp);
|
||||
float maxEnergy = this.getFightProperty(maxEnergyProp);
|
||||
float newEnergy = Math.min(curEnergy + amount, maxEnergy);
|
||||
|
||||
if (newEnergy != curEnergy) {
|
||||
setFightProperty(curEnergyProp, newEnergy);
|
||||
|
||||
getScene().broadcastPacket(new PacketAvatarFightPropUpdateNotify(getAvatar(), curEnergyProp));
|
||||
getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, PropChangeReason.PROP_CHANGE_ENERGY_BALL));
|
||||
}
|
||||
}
|
||||
|
||||
public SceneAvatarInfo getSceneAvatarInfo() {
|
||||
SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder()
|
||||
.setUid(this.getPlayer().getUid())
|
||||
@ -241,5 +275,5 @@ public class EntityAvatar extends GameEntity {
|
||||
|
||||
//
|
||||
return abilityControlBlock.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
package emu.grasscutter.game.entity;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.game.props.LifeState;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
@ -9,8 +12,11 @@ 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.packet.send.PacketEntityFightPropUpdateNotify;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public abstract class GameEntity {
|
||||
protected int id;
|
||||
@ -25,6 +31,10 @@ public abstract class GameEntity {
|
||||
private int lastMoveSceneTimeMs;
|
||||
private int lastMoveReliableSeq;
|
||||
|
||||
// Abilities
|
||||
private Map<String, Float> metaOverrideMap;
|
||||
private Int2ObjectMap<String> metaModifiers;
|
||||
|
||||
public GameEntity(Scene scene) {
|
||||
this.scene = scene;
|
||||
this.moveState = MotionState.MOTION_NONE;
|
||||
@ -54,6 +64,20 @@ public abstract class GameEntity {
|
||||
return isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD;
|
||||
}
|
||||
|
||||
public Map<String, Float> getMetaOverrideMap() {
|
||||
if (this.metaOverrideMap == null) {
|
||||
this.metaOverrideMap = new HashMap<>();
|
||||
}
|
||||
return this.metaOverrideMap;
|
||||
}
|
||||
|
||||
public Int2ObjectMap<String> getMetaModifiers() {
|
||||
if (this.metaModifiers == null) {
|
||||
this.metaModifiers = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
return this.metaModifiers;
|
||||
}
|
||||
|
||||
public abstract Int2FloatOpenHashMap getFightProperties();
|
||||
|
||||
public abstract Position getPosition();
|
||||
@ -146,4 +170,53 @@ public abstract class GameEntity {
|
||||
public void setSpawnEntry(SpawnDataEntry spawnEntry) {
|
||||
this.spawnEntry = spawnEntry;
|
||||
}
|
||||
|
||||
public float heal(float amount) {
|
||||
if (this.getFightProperties() == null) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
float curHp = getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
||||
float maxHp = getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||
|
||||
if (curHp >= maxHp) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
float healed = Math.min(maxHp - curHp, amount);
|
||||
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, healed);
|
||||
|
||||
getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
|
||||
|
||||
return healed;
|
||||
}
|
||||
|
||||
public void damage(float amount) {
|
||||
damage(amount, 0);
|
||||
}
|
||||
|
||||
public void damage(float amount, int killerId) {
|
||||
// Sanity check
|
||||
if (getFightProperties() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Lose hp
|
||||
addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -amount);
|
||||
|
||||
// Check if dead
|
||||
boolean isDead = false;
|
||||
if (getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) {
|
||||
setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
|
||||
isDead = true;
|
||||
}
|
||||
|
||||
// Packets
|
||||
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
|
||||
|
||||
// Check if dead
|
||||
if (isDead) {
|
||||
getScene().killEntity(this, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,6 +172,9 @@ public class Inventory implements Iterable<GameItem> {
|
||||
// Handle
|
||||
this.addVirtualItem(item.getItemId(), item.getCount());
|
||||
return item;
|
||||
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_ADSORBATE) {
|
||||
player.getTeamManager().addEnergyToTeam(item);
|
||||
return null;
|
||||
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_AVATAR) {
|
||||
// Get avatar id
|
||||
int avatarId = (item.getItemId() % 1000) + 10000000;
|
||||
|
@ -30,7 +30,7 @@ public class InvokeHandler<T> {
|
||||
}
|
||||
|
||||
public synchronized void update(Player player) {
|
||||
if (player.getWorld() == null) {
|
||||
if (player.getWorld() == null || player.getScene() == null) {
|
||||
this.entryListForwardAll.clear();
|
||||
this.entryListForwardAllExceptCur.clear();
|
||||
this.entryListForwardHost.clear();
|
||||
|
@ -8,6 +8,7 @@ import emu.grasscutter.data.def.PlayerLevelData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.CoopRequest;
|
||||
import emu.grasscutter.game.ability.AbilityManager;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.avatar.AvatarProfileData;
|
||||
import emu.grasscutter.game.avatar.AvatarStorage;
|
||||
@ -89,7 +90,8 @@ public class Player {
|
||||
@Transient private FriendsList friendsList;
|
||||
@Transient private MailHandler mailHandler;
|
||||
@Transient private MessageHandler messageHandler;
|
||||
|
||||
@Transient private AbilityManager abilityManager;
|
||||
|
||||
@Transient private SotSManager sotsManager;
|
||||
|
||||
private TeamManager teamManager;
|
||||
@ -142,6 +144,7 @@ public class Player {
|
||||
this.friendsList = new FriendsList(this);
|
||||
this.mailHandler = new MailHandler(this);
|
||||
this.towerManager = new TowerManager(this);
|
||||
this.abilityManager = new AbilityManager(this);
|
||||
this.pos = new Position();
|
||||
this.rotation = new Position();
|
||||
this.properties = new HashMap<>();
|
||||
@ -1025,6 +1028,10 @@ public class Player {
|
||||
|
||||
public SotSManager getSotSManager() { return sotsManager; }
|
||||
|
||||
public AbilityManager getAbilityManager() {
|
||||
return abilityManager;
|
||||
}
|
||||
|
||||
public synchronized void onTick() {
|
||||
// Check ping
|
||||
if (this.getLastPingTime() > System.currentTimeMillis() + 60000) {
|
||||
|
@ -10,6 +10,7 @@ import emu.grasscutter.data.def.AvatarSkillDepotData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.entity.EntityBaseGadget;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.props.ElementType;
|
||||
import emu.grasscutter.game.props.EnterReason;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
@ -579,6 +580,24 @@ public class TeamManager {
|
||||
// Packets
|
||||
getPlayer().sendPacket(new BasePacket(PacketOpcodes.WorldPlayerReviveRsp));
|
||||
}
|
||||
|
||||
public synchronized void addEnergyToTeam(GameItem energyBall) {
|
||||
// TODO
|
||||
float baseEnergy = 2;
|
||||
|
||||
for (int i = 0; i < getActiveTeam().size(); i++) {
|
||||
EntityAvatar entity = getActiveTeam().get(i);
|
||||
|
||||
float energyGain = baseEnergy;
|
||||
|
||||
// Active character gets full hp
|
||||
if (getCurrentCharacterIndex() != i) {
|
||||
energyGain *= Math.max(1.0 - (getActiveTeam().size() * .1f), .6f);
|
||||
}
|
||||
|
||||
entity.addEnergy(energyGain);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveAvatars() {
|
||||
// Save all avatars from active team
|
||||
|
@ -9,21 +9,22 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public enum ElementType {
|
||||
None (0, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY),
|
||||
Fire (1, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10101, "TeamResonance_Fire_Lv2"),
|
||||
Water (2, FightProperty.FIGHT_PROP_MAX_WATER_ENERGY, 10201, "TeamResonance_Water_Lv2"),
|
||||
Grass (3, FightProperty.FIGHT_PROP_MAX_GRASS_ENERGY),
|
||||
Electric (4, FightProperty.FIGHT_PROP_MAX_ELEC_ENERGY, 10401, "TeamResonance_Electric_Lv2"),
|
||||
Ice (5, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY, 10601, "TeamResonance_Ice_Lv2"),
|
||||
Frozen (6, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY),
|
||||
Wind (7, FightProperty.FIGHT_PROP_MAX_WIND_ENERGY, 10301, "TeamResonance_Wind_Lv2"),
|
||||
Rock (8, FightProperty.FIGHT_PROP_MAX_ROCK_ENERGY, 10701, "TeamResonance_Rock_Lv2"),
|
||||
AntiFire (9, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY),
|
||||
Default (255, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10801, "TeamResonance_AllDifferent");
|
||||
None (0, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY),
|
||||
Fire (1, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10101, "TeamResonance_Fire_Lv2"),
|
||||
Water (2, FightProperty.FIGHT_PROP_CUR_WATER_ENERGY, FightProperty.FIGHT_PROP_MAX_WATER_ENERGY, 10201, "TeamResonance_Water_Lv2"),
|
||||
Grass (3, FightProperty.FIGHT_PROP_CUR_GRASS_ENERGY, FightProperty.FIGHT_PROP_MAX_GRASS_ENERGY),
|
||||
Electric (4, FightProperty.FIGHT_PROP_CUR_ELEC_ENERGY, FightProperty.FIGHT_PROP_MAX_ELEC_ENERGY, 10401, "TeamResonance_Electric_Lv2"),
|
||||
Ice (5, FightProperty.FIGHT_PROP_CUR_ICE_ENERGY, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY, 10601, "TeamResonance_Ice_Lv2"),
|
||||
Frozen (6, FightProperty.FIGHT_PROP_CUR_ICE_ENERGY, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY),
|
||||
Wind (7, FightProperty.FIGHT_PROP_CUR_WIND_ENERGY, FightProperty.FIGHT_PROP_MAX_WIND_ENERGY, 10301, "TeamResonance_Wind_Lv2"),
|
||||
Rock (8, FightProperty.FIGHT_PROP_CUR_ROCK_ENERGY, FightProperty.FIGHT_PROP_MAX_ROCK_ENERGY, 10701, "TeamResonance_Rock_Lv2"),
|
||||
AntiFire (9, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY),
|
||||
Default (255, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10801, "TeamResonance_AllDifferent");
|
||||
|
||||
private final int value;
|
||||
private final int teamResonanceId;
|
||||
private final FightProperty energyProperty;
|
||||
private final FightProperty curEnergyProp;
|
||||
private final FightProperty maxEnergyProp;
|
||||
private final int configHash;
|
||||
private static final Int2ObjectMap<ElementType> map = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<String, ElementType> stringMap = new HashMap<>();
|
||||
@ -35,13 +36,14 @@ public enum ElementType {
|
||||
});
|
||||
}
|
||||
|
||||
private ElementType(int value, FightProperty energyProperty) {
|
||||
this(value, energyProperty, 0, null);
|
||||
private ElementType(int value, FightProperty curEnergyProp, FightProperty maxEnergyProp) {
|
||||
this(value, curEnergyProp, maxEnergyProp, 0, null);
|
||||
}
|
||||
|
||||
private ElementType(int value, FightProperty energyProperty, int teamResonanceId, String configName) {
|
||||
private ElementType(int value, FightProperty curEnergyProp, FightProperty maxEnergyProp, int teamResonanceId, String configName) {
|
||||
this.value = value;
|
||||
this.energyProperty = energyProperty;
|
||||
this.curEnergyProp = curEnergyProp;
|
||||
this.maxEnergyProp = maxEnergyProp;
|
||||
this.teamResonanceId = teamResonanceId;
|
||||
if (configName != null) {
|
||||
this.configHash = Utils.abilityHash(configName);
|
||||
@ -54,8 +56,12 @@ public enum ElementType {
|
||||
return value;
|
||||
}
|
||||
|
||||
public FightProperty getEnergyProperty() {
|
||||
return energyProperty;
|
||||
public FightProperty getCurEnergyProp() {
|
||||
return curEnergyProp;
|
||||
}
|
||||
|
||||
public FightProperty getMaxEnergyProp() {
|
||||
return maxEnergyProp;
|
||||
}
|
||||
|
||||
public int getTeamResonanceId() {
|
||||
|
@ -0,0 +1,64 @@
|
||||
package emu.grasscutter.game.tower;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Entity
|
||||
public class TowerLevelRecord {
|
||||
/**
|
||||
* floorId in config
|
||||
*/
|
||||
private int floorId;
|
||||
/**
|
||||
* LevelId - Stars
|
||||
*/
|
||||
private Map<Integer, Integer> passedLevelMap;
|
||||
|
||||
private int floorStarRewardProgress;
|
||||
|
||||
public TowerLevelRecord setLevelStars(int levelId, int stars){
|
||||
passedLevelMap.put(levelId, stars);
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getStarCount() {
|
||||
return passedLevelMap.values().stream().mapToInt(Integer::intValue).sum();
|
||||
}
|
||||
|
||||
public TowerLevelRecord(){
|
||||
|
||||
}
|
||||
|
||||
public TowerLevelRecord(int floorId){
|
||||
this.floorId = floorId;
|
||||
this.passedLevelMap = new HashMap<>();
|
||||
this.floorStarRewardProgress = 0;
|
||||
}
|
||||
|
||||
public int getFloorId() {
|
||||
return floorId;
|
||||
}
|
||||
|
||||
public void setFloorId(int floorId) {
|
||||
this.floorId = floorId;
|
||||
}
|
||||
|
||||
public Map<Integer, Integer> getPassedLevelMap() {
|
||||
return passedLevelMap;
|
||||
}
|
||||
|
||||
public void setPassedLevelMap(Map<Integer, Integer> passedLevelMap) {
|
||||
this.passedLevelMap = passedLevelMap;
|
||||
}
|
||||
|
||||
public int getFloorStarRewardProgress() {
|
||||
return floorStarRewardProgress;
|
||||
}
|
||||
|
||||
public void setFloorStarRewardProgress(int floorStarRewardProgress) {
|
||||
this.floorStarRewardProgress = floorStarRewardProgress;
|
||||
}
|
||||
|
||||
}
|
@ -9,10 +9,12 @@ import emu.grasscutter.game.dungeons.TowerDungeonSettleListener;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.packet.send.PacketCanUseSkillNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketTowerCurLevelRecordChangeNotify;
|
||||
|
||||
import emu.grasscutter.server.packet.send.PacketTowerEnterLevelRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketTowerLevelStarCondNotify;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Entity
|
||||
public class TowerManager {
|
||||
@ -26,11 +28,19 @@ public class TowerManager {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
/**
|
||||
* the floor players chose
|
||||
*/
|
||||
private int currentFloorId;
|
||||
private int currentLevel;
|
||||
@Transient
|
||||
private int currentLevelId;
|
||||
|
||||
/**
|
||||
* floorId - Record
|
||||
*/
|
||||
private Map<Integer, TowerLevelRecord> recordMap;
|
||||
|
||||
@Transient
|
||||
private int entryScene;
|
||||
|
||||
@ -38,7 +48,26 @@ public class TowerManager {
|
||||
return currentFloorId;
|
||||
}
|
||||
|
||||
public int getCurrentLevelId(){
|
||||
return this.currentLevelId + currentLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* form 1-3
|
||||
*/
|
||||
public int getCurrentLevel(){
|
||||
return currentLevel + 1;
|
||||
}
|
||||
private static final List<DungeonSettleListener> towerDungeonSettleListener = List.of(new TowerDungeonSettleListener());
|
||||
|
||||
public Map<Integer, TowerLevelRecord> getRecordMap() {
|
||||
if(recordMap == null){
|
||||
recordMap = new HashMap<>();
|
||||
recordMap.put(1001, new TowerLevelRecord(1001));
|
||||
}
|
||||
return recordMap;
|
||||
}
|
||||
|
||||
public void teamSelect(int floor, List<List<Long>> towerTeams) {
|
||||
var floorData = GameData.getTowerFloorDataMap().get(floor);
|
||||
|
||||
@ -54,51 +83,73 @@ public class TowerManager {
|
||||
entryScene = player.getSceneId();
|
||||
}
|
||||
|
||||
|
||||
player.getTeamManager().setupTemporaryTeam(towerTeams);
|
||||
}
|
||||
|
||||
|
||||
public void enterLevel(int enterPointId) {
|
||||
var levelData = GameData.getTowerLevelDataMap().get(currentLevelId + currentLevel);
|
||||
var levelData = GameData.getTowerLevelDataMap().get(getCurrentLevelId());
|
||||
|
||||
this.currentLevel++;
|
||||
var id = levelData.getDungeonId();
|
||||
var dungeonId = levelData.getDungeonId();
|
||||
|
||||
notifyCurLevelRecordChange();
|
||||
// use team user choose
|
||||
player.getTeamManager().useTemporaryTeam(0);
|
||||
player.getServer().getDungeonManager().handoffDungeon(player, id,
|
||||
player.getServer().getDungeonManager().handoffDungeon(player, dungeonId,
|
||||
towerDungeonSettleListener);
|
||||
|
||||
// make sure user can exit dungeon correctly
|
||||
player.getScene().setPrevScene(entryScene);
|
||||
player.getScene().setPrevScenePoint(enterPointId);
|
||||
|
||||
player.getSession().send(new PacketTowerEnterLevelRsp(currentFloorId, currentLevel));
|
||||
player.getSession().send(new PacketTowerEnterLevelRsp(currentFloorId, getCurrentLevel()));
|
||||
// stop using skill
|
||||
player.getSession().send(new PacketCanUseSkillNotify(false));
|
||||
// notify the cond of stars
|
||||
player.getSession().send(new PacketTowerLevelStarCondNotify(currentFloorId, getCurrentLevel()));
|
||||
}
|
||||
|
||||
public void notifyCurLevelRecordChange(){
|
||||
player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, currentLevel));
|
||||
player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, getCurrentLevel()));
|
||||
}
|
||||
public void notifyCurLevelRecordChangeWhenDone(){
|
||||
player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, currentLevel + 1));
|
||||
public void notifyCurLevelRecordChangeWhenDone(int stars){
|
||||
if(!recordMap.containsKey(currentFloorId)){
|
||||
recordMap.put(currentFloorId,
|
||||
new TowerLevelRecord(currentFloorId).setLevelStars(getCurrentLevelId(),stars));
|
||||
}else{
|
||||
recordMap.put(currentFloorId,
|
||||
recordMap.get(currentFloorId).setLevelStars(getCurrentLevelId(),stars));
|
||||
}
|
||||
|
||||
this.currentLevel++;
|
||||
|
||||
if(!hasNextLevel()){
|
||||
// set up the next floor
|
||||
recordMap.put(getNextFloorId(), new TowerLevelRecord(getNextFloorId()));
|
||||
player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(getNextFloorId(), 1));
|
||||
}else{
|
||||
player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, getCurrentLevel()));
|
||||
}
|
||||
}
|
||||
public boolean hasNextLevel(){
|
||||
return this.currentLevel < 3;
|
||||
}
|
||||
|
||||
public int getNextFloorId() {
|
||||
if(hasNextLevel()){
|
||||
return 0;
|
||||
}
|
||||
this.currentFloorId++;
|
||||
return this.currentFloorId;
|
||||
return player.getServer().getTowerScheduleManager().getNextFloorId(this.currentFloorId);
|
||||
}
|
||||
public boolean hasNextFloor(){
|
||||
return player.getServer().getTowerScheduleManager().getNextFloorId(this.currentFloorId) > 0;
|
||||
}
|
||||
|
||||
public void clearEntry() {
|
||||
this.entryScene = 0;
|
||||
}
|
||||
|
||||
public boolean canEnterScheduleFloor(){
|
||||
if(!recordMap.containsKey(player.getServer().getTowerScheduleManager().getLastEntranceFloor())){
|
||||
return false;
|
||||
}
|
||||
return recordMap.get(player.getServer().getTowerScheduleManager().getLastEntranceFloor())
|
||||
.getStarCount() >= 6;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,35 @@
|
||||
package emu.grasscutter.game.tower;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class TowerScheduleConfig {
|
||||
private int scheduleId;
|
||||
|
||||
private Date scheduleStartTime;
|
||||
private Date nextScheduleChangeTime;
|
||||
|
||||
|
||||
public int getScheduleId() {
|
||||
return scheduleId;
|
||||
}
|
||||
|
||||
public void setScheduleId(int scheduleId) {
|
||||
this.scheduleId = scheduleId;
|
||||
}
|
||||
|
||||
public Date getScheduleStartTime() {
|
||||
return scheduleStartTime;
|
||||
}
|
||||
|
||||
public void setScheduleStartTime(Date scheduleStartTime) {
|
||||
this.scheduleStartTime = scheduleStartTime;
|
||||
}
|
||||
|
||||
public Date getNextScheduleChangeTime() {
|
||||
return nextScheduleChangeTime;
|
||||
}
|
||||
|
||||
public void setNextScheduleChangeTime(Date nextScheduleChangeTime) {
|
||||
this.nextScheduleChangeTime = nextScheduleChangeTime;
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package emu.grasscutter.game.tower;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.def.TowerScheduleData;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
|
||||
import java.io.FileReader;
|
||||
import java.util.List;
|
||||
|
||||
public class TowerScheduleManager {
|
||||
private final GameServer gameServer;
|
||||
|
||||
public GameServer getGameServer() {
|
||||
return gameServer;
|
||||
}
|
||||
|
||||
public TowerScheduleManager(GameServer gameServer) {
|
||||
this.gameServer = gameServer;
|
||||
this.load();
|
||||
}
|
||||
|
||||
private TowerScheduleConfig towerScheduleConfig;
|
||||
|
||||
public synchronized void load(){
|
||||
try (FileReader fileReader = new FileReader(Grasscutter.getConfig().DATA_FOLDER + "TowerSchedule.json")) {
|
||||
towerScheduleConfig = Grasscutter.getGsonFactory().fromJson(fileReader, TowerScheduleConfig.class);
|
||||
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Unable to load tower schedule config.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public TowerScheduleConfig getTowerScheduleConfig() {
|
||||
return towerScheduleConfig;
|
||||
}
|
||||
|
||||
public TowerScheduleData getCurrentTowerScheduleData(){
|
||||
var data = GameData.getTowerScheduleDataMap().get(towerScheduleConfig.getScheduleId());
|
||||
if(data == null){
|
||||
Grasscutter.getLogger().error("Could not get current tower schedule data by config:{}", towerScheduleConfig);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public List<Integer> getScheduleFloors() {
|
||||
return getCurrentTowerScheduleData().getSchedules().get(0).getFloorList();
|
||||
}
|
||||
|
||||
public int getNextFloorId(int floorId){
|
||||
var entranceFloors = getCurrentTowerScheduleData().getEntranceFloorId();
|
||||
var nextId = 0;
|
||||
// find in entrance floors first
|
||||
for(int i=0;i<entranceFloors.size()-1;i++){
|
||||
if(floorId == entranceFloors.get(i)){
|
||||
nextId = entranceFloors.get(i+1);
|
||||
}
|
||||
}
|
||||
if(nextId != 0){
|
||||
return nextId;
|
||||
}
|
||||
var scheduleFloors = getScheduleFloors();
|
||||
// find in schedule floors
|
||||
for(int i=0;i<scheduleFloors.size()-1;i++){
|
||||
if(floorId == scheduleFloors.get(i)){
|
||||
nextId = scheduleFloors.get(i+1);
|
||||
}
|
||||
}
|
||||
return nextId;
|
||||
}
|
||||
|
||||
public Integer getLastEntranceFloor() {
|
||||
return getCurrentTowerScheduleData().getEntranceFloorId().get(getCurrentTowerScheduleData().getEntranceFloorId().size()-1);
|
||||
}
|
||||
}
|
@ -385,27 +385,7 @@ public class Scene {
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (target.getFightProperties() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Lose hp
|
||||
target.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -result.getDamage());
|
||||
|
||||
// Check if dead
|
||||
boolean isDead = false;
|
||||
if (target.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) {
|
||||
target.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
|
||||
isDead = true;
|
||||
}
|
||||
|
||||
// Packets
|
||||
this.broadcastPacket(new PacketEntityFightPropUpdateNotify(target, FightProperty.FIGHT_PROP_CUR_HP));
|
||||
|
||||
// Check if dead
|
||||
if (isDead) {
|
||||
this.killEntity(target, result.getAttackerId());
|
||||
}
|
||||
target.damage(result.getDamage(), result.getAttackerId());
|
||||
}
|
||||
|
||||
public void killEntity(GameEntity target, int attackerId) {
|
||||
|
@ -90,6 +90,10 @@ public class SceneScriptManager {
|
||||
return config;
|
||||
}
|
||||
|
||||
public SceneGroup getCurrentGroup() {
|
||||
return currentGroup;
|
||||
}
|
||||
|
||||
public List<SceneBlock> getBlocks() {
|
||||
return blocks;
|
||||
}
|
||||
@ -237,16 +241,16 @@ public class SceneScriptManager {
|
||||
|
||||
for (SceneSuite suite : group.suites) {
|
||||
suite.sceneMonsters = new ArrayList<>(suite.monsters.size());
|
||||
for (int id : suite.monsters) {
|
||||
try {
|
||||
SceneMonster monster = (SceneMonster) map.get(id);
|
||||
if (monster != null) {
|
||||
suite.sceneMonsters.add(monster);
|
||||
suite.monsters.forEach(id -> {
|
||||
Object objEntry = map.get(id.intValue());
|
||||
if (objEntry instanceof Map.Entry<?,?> monsterEntry) {
|
||||
Object monster = monsterEntry.getValue();
|
||||
if(monster instanceof SceneMonster sceneMonster){
|
||||
suite.sceneMonsters.add(sceneMonster);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
suite.sceneGadgets = new ArrayList<>(suite.gadgets.size());
|
||||
for (int id : suite.gadgets) {
|
||||
try {
|
||||
@ -320,13 +324,15 @@ public class SceneScriptManager {
|
||||
}
|
||||
|
||||
public void spawnMonstersInGroup(SceneGroup group, int suiteIndex) {
|
||||
this.currentGroup = group;
|
||||
this.monsterSceneLimit = 0;
|
||||
var suite = group.getSuiteByIndex(suiteIndex);
|
||||
if(suite == null){
|
||||
return;
|
||||
}
|
||||
suite.sceneMonsters.forEach(mob -> spawnMonstersInGroup(group, mob));
|
||||
if(suite.sceneMonsters.size() > 0){
|
||||
this.currentGroup = group;
|
||||
this.monsterSceneLimit = 0;
|
||||
suite.sceneMonsters.forEach(mob -> spawnMonstersInGroup(group, mob));
|
||||
}
|
||||
}
|
||||
|
||||
public void spawnMonstersInGroup(SceneGroup group) {
|
||||
@ -401,6 +407,7 @@ public class SceneScriptManager {
|
||||
spawnMonstersInGroup(this.currentGroup, this.currentGroup.monsters.get(this.monsterOrders.poll()));
|
||||
}else if(this.monsterAlive.get() == 0){
|
||||
// spawn the last turn of monsters
|
||||
//callEvent(EventType.EVENT_MONSTER_TIDE_DIE, new ScriptArgs());
|
||||
while(!this.monsterOrders.isEmpty()){
|
||||
spawnMonstersInGroup(this.currentGroup, this.currentGroup.monsters.get(this.monsterOrders.poll()));
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ public class ScriptLib {
|
||||
challengeIndex,groupId,ordersConfigId,tideCount,sceneLimit,param6);
|
||||
|
||||
SceneGroup group = getSceneScriptManager().getGroupById(groupId);
|
||||
|
||||
|
||||
if (group == null || group.monsters == null) {
|
||||
return 1;
|
||||
}
|
||||
@ -136,8 +136,7 @@ public class ScriptLib {
|
||||
if (group == null || group.monsters == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TODO just spawn all from group for now
|
||||
|
||||
this.getSceneScriptManager().spawnMonstersInGroup(group, suite);
|
||||
|
||||
return 0;
|
||||
@ -159,7 +158,13 @@ public class ScriptLib {
|
||||
if (group == null || group.monsters == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
if(getSceneScriptManager().getScene().getChallenge() != null &&
|
||||
getSceneScriptManager().getScene().getChallenge().inProgress())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DungeonChallenge challenge = new DungeonChallenge(getSceneScriptManager().getScene(), group);
|
||||
challenge.setChallengeId(challengeId);
|
||||
challenge.setChallengeIndex(challengeIndex);
|
||||
@ -249,7 +254,7 @@ public class ScriptLib {
|
||||
var1);
|
||||
|
||||
return (int) getSceneScriptManager().getScene().getEntities().values().stream()
|
||||
.filter(e -> e instanceof EntityMonster)
|
||||
.filter(e -> e instanceof EntityMonster && e.getGroupId() == getSceneScriptManager().getCurrentGroup().id)
|
||||
.count();
|
||||
}
|
||||
public int SetMonsterBattleByGroup(int var1, int var2, int var3){
|
||||
@ -266,13 +271,11 @@ public class ScriptLib {
|
||||
return 0;
|
||||
}
|
||||
// 8-1
|
||||
public int GetGroupVariableValueByGroup(int var1, String var2, int var3){
|
||||
logger.debug("[LUA] Call GetGroupVariableValueByGroup with {},{},{}",
|
||||
var1,var2,var3);
|
||||
public int GetGroupVariableValueByGroup(String name, int groupId){
|
||||
logger.debug("[LUA] Call GetGroupVariableValueByGroup with {},{}",
|
||||
name,groupId);
|
||||
|
||||
//TODO
|
||||
|
||||
return getSceneScriptManager().getVariables().getOrDefault(var2, 0);
|
||||
return getSceneScriptManager().getVariables().getOrDefault(name, 0);
|
||||
}
|
||||
|
||||
public int SetIsAllowUseSkill(int canUse, int var2){
|
||||
@ -299,4 +302,11 @@ public class ScriptLib {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int SetGroupVariableValueByGroup(String key, int value, int groupId){
|
||||
logger.debug("[LUA] Call SetGroupVariableValueByGroup with {},{},{}",
|
||||
key,value,groupId);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import emu.grasscutter.game.managers.InventoryManager;
|
||||
import emu.grasscutter.game.managers.MultiplayerManager;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.shop.ShopManager;
|
||||
import emu.grasscutter.game.tower.TowerScheduleManager;
|
||||
import emu.grasscutter.game.world.World;
|
||||
import emu.grasscutter.net.packet.PacketHandler;
|
||||
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
|
||||
@ -54,6 +55,7 @@ public final class GameServer extends KcpServer {
|
||||
private final DropManager dropManager;
|
||||
|
||||
private final CombineManger combineManger;
|
||||
private final TowerScheduleManager towerScheduleManager;
|
||||
|
||||
public GameServer() {
|
||||
this(new InetSocketAddress(
|
||||
@ -82,7 +84,7 @@ public final class GameServer extends KcpServer {
|
||||
this.dropManager = new DropManager(this);
|
||||
this.expeditionManager = new ExpeditionManager(this);
|
||||
this.combineManger = new CombineManger(this);
|
||||
|
||||
this.towerScheduleManager = new TowerScheduleManager(this);
|
||||
// Hook into shutdown event.
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
|
||||
}
|
||||
@ -139,6 +141,10 @@ public final class GameServer extends KcpServer {
|
||||
return this.combineManger;
|
||||
}
|
||||
|
||||
public TowerScheduleManager getTowerScheduleManager() {
|
||||
return towerScheduleManager;
|
||||
}
|
||||
|
||||
public TaskMap getTaskMap() {
|
||||
return this.taskMap;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import emu.grasscutter.net.proto.AbilityInvocationsNotifyOuterClass.AbilityInvoc
|
||||
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
||||
import emu.grasscutter.net.packet.PacketHandler;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
|
||||
@Opcodes(PacketOpcodes.AbilityInvocationsNotify)
|
||||
public class HandlerAbilityInvocationsNotify extends PacketHandler {
|
||||
@ -15,13 +16,9 @@ public class HandlerAbilityInvocationsNotify extends PacketHandler {
|
||||
AbilityInvocationsNotify notif = AbilityInvocationsNotify.parseFrom(payload);
|
||||
|
||||
for (AbilityInvokeEntry entry : notif.getInvokesList()) {
|
||||
//System.out.println(entry.getArgumentType() + ": " + Utils.bytesToHex(entry.getAbilityData().toByteArray()));
|
||||
session.getPlayer().getAbilityManager().onAbilityInvoke(entry);
|
||||
session.getPlayer().getAbilityInvokeHandler().addEntry(entry.getForwardType(), entry);
|
||||
}
|
||||
|
||||
if (notif.getInvokesList().size() > 0) {
|
||||
session.getPlayer().getAbilityInvokeHandler().update(session.getPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry
|
||||
import emu.grasscutter.net.proto.ClientAbilityInitFinishNotifyOuterClass.ClientAbilityInitFinishNotify;
|
||||
import emu.grasscutter.net.packet.PacketHandler;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
|
||||
@Opcodes(PacketOpcodes.ClientAbilityInitFinishNotify)
|
||||
public class HandlerClientAbilityInitFinishNotify extends PacketHandler {
|
||||
@ -15,6 +16,7 @@ public class HandlerClientAbilityInitFinishNotify extends PacketHandler {
|
||||
ClientAbilityInitFinishNotify notif = ClientAbilityInitFinishNotify.parseFrom(payload);
|
||||
|
||||
for (AbilityInvokeEntry entry : notif.getInvokesList()) {
|
||||
session.getPlayer().getAbilityManager().onAbilityInvoke(entry);
|
||||
session.getPlayer().getClientAbilityInitFinishHandler().addEntry(entry.getForwardType(), entry);
|
||||
}
|
||||
|
||||
|
@ -74,14 +74,6 @@ public class HandlerCombatInvocationsNotify extends PacketHandler {
|
||||
|
||||
session.getPlayer().getCombatInvokeHandler().addEntry(entry.getForwardType(), entry);
|
||||
}
|
||||
|
||||
if (notif.getInvokeListList().size() > 0) {
|
||||
session.getPlayer().getCombatInvokeHandler().update(session.getPlayer());
|
||||
}
|
||||
// Handle attack results last
|
||||
while (!session.getPlayer().getAttackResults().isEmpty()) {
|
||||
session.getPlayer().getScene().handleAttack(session.getPlayer().getAttackResults().poll());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFallOnGround(GameSession session, GameEntity entity, MotionState motionState) {
|
||||
|
@ -14,11 +14,6 @@ public class HandlerEvtCreateGadgetNotify extends PacketHandler {
|
||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||
EvtCreateGadgetNotify notify = EvtCreateGadgetNotify.parseFrom(payload);
|
||||
|
||||
// Dont handle in singleplayer
|
||||
if (!session.getPlayer().getWorld().isMultiplayer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanity check - dont add duplicate entities
|
||||
if (session.getPlayer().getScene().getEntityById(notify.getEntityId()) != null) {
|
||||
return;
|
||||
|
@ -12,11 +12,6 @@ public class HandlerEvtDestroyGadgetNotify extends PacketHandler {
|
||||
@Override
|
||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||
EvtDestroyGadgetNotify notify = EvtDestroyGadgetNotify.parseFrom(payload);
|
||||
|
||||
// Dont handle in singleplayer
|
||||
if (!session.getPlayer().getWorld().isMultiplayer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
session.getPlayer().getScene().onPlayerDestroyGadget(notify.getEntityId());
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ public class HandlerSetEntityClientDataNotify extends PacketHandler {
|
||||
BasePacket packet = new BasePacket(PacketOpcodes.SetEntityClientDataNotify, true);
|
||||
packet.setData(notif);
|
||||
|
||||
session.getPlayer().getScene().broadcastPacketToOthers(session.getPlayer(), packet);
|
||||
session.getPlayer().getScene().broadcastPacket(packet);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ public class HandlerSetPlayerBornDataReq extends PacketHandler {
|
||||
// Create avatar
|
||||
if (player.getAvatars().getAvatarCount() == 0) {
|
||||
Avatar mainCharacter = new Avatar(avatarId);
|
||||
mainCharacter.setSkillDepot(GameData.getAvatarSkillDepotDataMap().get(startingSkillDepot));
|
||||
mainCharacter.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(startingSkillDepot));
|
||||
player.addAvatar(mainCharacter);
|
||||
player.setMainCharacterId(avatarId);
|
||||
player.setHeadImage(avatarId);
|
||||
|
@ -11,7 +11,10 @@ public class HandlerTowerAllDataReq extends PacketHandler {
|
||||
|
||||
@Override
|
||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||
session.send(new PacketTowerAllDataRsp());
|
||||
session.send(new PacketTowerAllDataRsp(
|
||||
session.getServer().getTowerScheduleManager(),
|
||||
session.getPlayer().getTowerManager()
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,5 +15,14 @@ public class HandlerUnionCmdNotify extends PacketHandler {
|
||||
for (UnionCmd cmd : req.getCmdListList()) {
|
||||
session.getServer().getPacketHandler().handle(session, cmd.getMessageId(), EMPTY_BYTE_ARRAY, cmd.getBody().toByteArray());
|
||||
}
|
||||
|
||||
// Update
|
||||
session.getPlayer().getCombatInvokeHandler().update(session.getPlayer());
|
||||
session.getPlayer().getAbilityInvokeHandler().update(session.getPlayer());
|
||||
|
||||
// Handle attack results last
|
||||
while (!session.getPlayer().getAttackResults().isEmpty()) {
|
||||
session.getPlayer().getScene().handleAttack(session.getPlayer().getAttackResults().poll());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ public class PacketDungeonSettleNotify extends BasePacket {
|
||||
.setCount(1000)
|
||||
.build())
|
||||
;
|
||||
if(nextFloorId > 0){
|
||||
if(nextFloorId > 0 && canJump){
|
||||
towerLevelEndNotify.setNextFloorId(nextFloorId);
|
||||
}
|
||||
|
||||
|
@ -11,21 +11,27 @@ import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
||||
import java.util.List;
|
||||
|
||||
public class PacketEntityFightPropChangeReasonNotify extends BasePacket {
|
||||
|
||||
public PacketEntityFightPropChangeReasonNotify(GameEntity entity, FightProperty prop, Float value, List<Integer> param, PropChangeReason reason, ChangeHpReason changeHpReason) {
|
||||
super(PacketOpcodes.EntityFightPropChangeReasonNotify);
|
||||
|
||||
EntityFightPropChangeReasonNotify.Builder proto = EntityFightPropChangeReasonNotify.newBuilder()
|
||||
.setEntityId(entity.getId())
|
||||
.setPropType(prop.getId())
|
||||
.setPropDelta(value)
|
||||
.setReason(reason)
|
||||
.setChangeHpReason(changeHpReason);
|
||||
for(int p: param){
|
||||
|
||||
for(int p : param){
|
||||
proto.addParamList(p);
|
||||
}
|
||||
|
||||
this.setData(proto);
|
||||
}
|
||||
|
||||
public PacketEntityFightPropChangeReasonNotify(GameEntity entity, FightProperty prop, Float value, PropChangeReason reason, ChangeHpReason changeHpReason) {
|
||||
super(PacketOpcodes.EntityFightPropChangeReasonNotify);
|
||||
|
||||
EntityFightPropChangeReasonNotify proto = EntityFightPropChangeReasonNotify.newBuilder()
|
||||
.setEntityId(entity.getId())
|
||||
.setPropType(prop.getId())
|
||||
@ -33,6 +39,20 @@ public class PacketEntityFightPropChangeReasonNotify extends BasePacket {
|
||||
.setReason(reason)
|
||||
.setChangeHpReason(changeHpReason)
|
||||
.build();
|
||||
|
||||
this.setData(proto);
|
||||
}
|
||||
|
||||
public PacketEntityFightPropChangeReasonNotify(GameEntity entity, FightProperty prop, Float value, PropChangeReason reason) {
|
||||
super(PacketOpcodes.EntityFightPropChangeReasonNotify);
|
||||
|
||||
EntityFightPropChangeReasonNotify proto = EntityFightPropChangeReasonNotify.newBuilder()
|
||||
.setEntityId(entity.getId())
|
||||
.setPropType(prop.getId())
|
||||
.setPropDelta(value)
|
||||
.setReason(reason)
|
||||
.build();
|
||||
|
||||
this.setData(proto);
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +1,64 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.def.TowerFloorData;
|
||||
import emu.grasscutter.game.tower.TowerManager;
|
||||
import emu.grasscutter.game.tower.TowerScheduleManager;
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.proto.TowerAllDataRspOuterClass.TowerAllDataRsp;
|
||||
import emu.grasscutter.net.proto.TowerCurLevelRecordOuterClass.TowerCurLevelRecord;
|
||||
import emu.grasscutter.net.proto.TowerFloorRecordOuterClass.TowerFloorRecord;
|
||||
import emu.grasscutter.net.proto.TowerLevelRecordOuterClass;
|
||||
import emu.grasscutter.utils.DateHelper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class PacketTowerAllDataRsp extends BasePacket {
|
||||
|
||||
public PacketTowerAllDataRsp() {
|
||||
public PacketTowerAllDataRsp(TowerScheduleManager towerScheduleManager, TowerManager towerManager) {
|
||||
super(PacketOpcodes.TowerAllDataRsp);
|
||||
|
||||
var list = GameData.getTowerFloorDataMap().values().stream()
|
||||
.map(TowerFloorData::getFloorId)
|
||||
.map(id -> TowerFloorRecord.newBuilder().setFloorId(id).build())
|
||||
.collect(Collectors.toList());
|
||||
var recordList = towerManager.getRecordMap().values().stream()
|
||||
.map(rec -> TowerFloorRecord.newBuilder()
|
||||
.setFloorId(rec.getFloorId())
|
||||
.setFloorStarRewardProgress(rec.getFloorStarRewardProgress())
|
||||
.putAllPassedLevelMap(rec.getPassedLevelMap())
|
||||
.addAllPassedLevelRecordList(buildFromPassedLevelMap(rec.getPassedLevelMap()))
|
||||
.build()
|
||||
)
|
||||
.toList();
|
||||
|
||||
var openTimeMap = towerScheduleManager.getScheduleFloors().stream()
|
||||
.collect(Collectors.toMap(x -> x,
|
||||
y -> DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig()
|
||||
.getScheduleStartTime()))
|
||||
);
|
||||
|
||||
TowerAllDataRsp proto = TowerAllDataRsp.newBuilder()
|
||||
.setTowerScheduleId(29)
|
||||
.addAllTowerFloorRecordList(list)
|
||||
.setTowerScheduleId(towerScheduleManager.getCurrentTowerScheduleData().getScheduleId())
|
||||
.addAllTowerFloorRecordList(recordList)
|
||||
.setCurLevelRecord(TowerCurLevelRecord.newBuilder().setIsEmpty(true))
|
||||
.setNextScheduleChangeTime(Integer.MAX_VALUE)
|
||||
.putFloorOpenTimeMap(1024, 1630486800)
|
||||
.putFloorOpenTimeMap(1025, 1630486800)
|
||||
.putFloorOpenTimeMap(1026, 1630486800)
|
||||
.putFloorOpenTimeMap(1027, 1630486800)
|
||||
.setScheduleStartTime(1630486800)
|
||||
.setScheduleStartTime(DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig()
|
||||
.getScheduleStartTime()))
|
||||
.setNextScheduleChangeTime(DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig()
|
||||
.getNextScheduleChangeTime()))
|
||||
.putAllFloorOpenTimeMap(openTimeMap)
|
||||
.setIsFinishedEntranceFloor(towerManager.canEnterScheduleFloor())
|
||||
.build();
|
||||
|
||||
this.setData(proto);
|
||||
}
|
||||
|
||||
private List<TowerLevelRecordOuterClass.TowerLevelRecord> buildFromPassedLevelMap(Map<Integer, Integer> map){
|
||||
return map.entrySet().stream()
|
||||
.map(item -> TowerLevelRecordOuterClass.TowerLevelRecord.newBuilder()
|
||||
.setLevelId(item.getKey())
|
||||
.addAllSatisfiedCondList(IntStream.range(1, item.getValue() + 1).boxed().toList())
|
||||
.build())
|
||||
.toList();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,13 +8,13 @@ import emu.grasscutter.net.proto.TowerLevelRecordOuterClass.TowerLevelRecord;
|
||||
|
||||
public class PacketTowerFloorRecordChangeNotify extends BasePacket {
|
||||
|
||||
public PacketTowerFloorRecordChangeNotify(int floorId) {
|
||||
public PacketTowerFloorRecordChangeNotify(int floorId, int stars, boolean canEnterScheduleFloor) {
|
||||
super(PacketOpcodes.TowerFloorRecordChangeNotify);
|
||||
|
||||
TowerFloorRecordChangeNotify proto = TowerFloorRecordChangeNotify.newBuilder()
|
||||
.addTowerFloorRecordList(TowerFloorRecord.newBuilder()
|
||||
.setFloorId(floorId)
|
||||
.setFloorStarRewardProgress(3)
|
||||
.setFloorStarRewardProgress(stars)
|
||||
.addPassedLevelRecordList(TowerLevelRecord.newBuilder()
|
||||
.setLevelId(1)
|
||||
.addSatisfiedCondList(1)
|
||||
@ -22,7 +22,7 @@ public class PacketTowerFloorRecordChangeNotify extends BasePacket {
|
||||
.addSatisfiedCondList(3)
|
||||
.build())
|
||||
.build())
|
||||
.setIsFinishedEntranceFloor(true)
|
||||
.setIsFinishedEntranceFloor(canEnterScheduleFloor)
|
||||
.build();
|
||||
|
||||
this.setData(proto);
|
||||
|
@ -0,0 +1,32 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.proto.TowerLevelStarCondDataOuterClass.TowerLevelStarCondData;
|
||||
import emu.grasscutter.net.proto.TowerLevelStarCondNotifyOuterClass.TowerLevelStarCondNotify;
|
||||
|
||||
public class PacketTowerLevelStarCondNotify extends BasePacket {
|
||||
|
||||
public PacketTowerLevelStarCondNotify(int floorId, int levelIndex) {
|
||||
super(PacketOpcodes.TowerLevelStarCondNotify);
|
||||
|
||||
TowerLevelStarCondNotify proto = TowerLevelStarCondNotify.newBuilder()
|
||||
.setFloorId(floorId)
|
||||
.setLevelIndex(levelIndex)
|
||||
.addCondDataList(TowerLevelStarCondData.newBuilder()
|
||||
.setCondValue(1)
|
||||
.build()
|
||||
)
|
||||
.addCondDataList(TowerLevelStarCondData.newBuilder()
|
||||
.setCondValue(2)
|
||||
.build()
|
||||
)
|
||||
.addCondDataList(TowerLevelStarCondData.newBuilder()
|
||||
.setCondValue(3)
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
this.setData(proto);
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package emu.grasscutter.task;
|
||||
|
||||
import org.quartz.JobDataMap;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
|
@ -67,6 +67,40 @@ public final class TaskMap {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean pauseTask(String taskName) {
|
||||
try {
|
||||
Scheduler scheduler = schedulerFactory.getScheduler();
|
||||
scheduler.pauseJob(new JobKey(taskName));
|
||||
} catch (SchedulerException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean resumeTask(String taskName) {
|
||||
try {
|
||||
Scheduler scheduler = schedulerFactory.getScheduler();
|
||||
scheduler.resumeJob(new JobKey(taskName));
|
||||
} catch (SchedulerException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean cancelTask(String taskName) {
|
||||
Task task = this.annotations.get(taskName);
|
||||
if (task == null) return false;
|
||||
try {
|
||||
this.unregisterTask(this.tasks.get(taskName));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public TaskMap registerTask(String taskName, TaskHandler task) {
|
||||
Task annotation = task.getClass().getAnnotation(Task.class);
|
||||
this.annotations.put(taskName, annotation);
|
||||
@ -116,7 +150,7 @@ public final class TaskMap {
|
||||
classes.forEach(annotated -> {
|
||||
try {
|
||||
Task taskData = annotated.getAnnotation(Task.class);
|
||||
Object object = annotated.newInstance();
|
||||
Object object = annotated.getDeclaredConstructor().newInstance();
|
||||
if (object instanceof TaskHandler) {
|
||||
this.registerTask(taskData.taskName(), (TaskHandler) object);
|
||||
if (taskData.executeImmediatelyAfterReset()) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package emu.grasscutter.utils;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
public final class DateHelper {
|
||||
public static Date onlyYearMonthDay(Date now) {
|
||||
@ -13,4 +13,8 @@ public final class DateHelper {
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
public static int getUnixTime(Date localDateTime){
|
||||
return (int)(localDateTime.getTime() / 1000L);
|
||||
}
|
||||
}
|
||||
|
@ -312,6 +312,9 @@
|
||||
"success": "Teleported %s to %s, %s, %s in scene %s",
|
||||
"description": "Change the player's position."
|
||||
},
|
||||
"tower": {
|
||||
"unlock_done": "Abyss Corridor's Floors are all unlocked now."
|
||||
},
|
||||
"weather": {
|
||||
"usage": "Usage: weather <weatherId> [climateId]",
|
||||
"success": "Changed weather to %s with climate %s",
|
||||
|
@ -95,17 +95,20 @@
|
||||
"create": "已建立账号,UID 为 %s 。",
|
||||
"delete": "账号已刪除。",
|
||||
"no_account": "账号不存在。",
|
||||
"command_usage": "用法:account <create|delete> <username> [uid]"
|
||||
"command_usage": "用法:account <create|delete> <username> [uid]",
|
||||
"description": "创建或删除账号。"
|
||||
},
|
||||
"broadcast": {
|
||||
"command_usage": "用法:broadcast <消息>",
|
||||
"message_sent": "公告已发送。"
|
||||
"message_sent": "公告已发送。",
|
||||
"description": "向所有玩家发送公告。"
|
||||
},
|
||||
"changescene": {
|
||||
"usage": "用法:changescene <scene id>",
|
||||
"already_in_scene": "你已经在这个秘境中了。",
|
||||
"success": "已切换至秘境 %s.",
|
||||
"exists_error": "此秘境不存在。"
|
||||
"exists_error": "此秘境不存在。",
|
||||
"description": "切换指定秘境。"
|
||||
},
|
||||
"clear": {
|
||||
"command_usage": "用法: clear <all|wp|art|mat>",
|
||||
@ -115,35 +118,41 @@
|
||||
"furniture": "已将 %s 的尘歌壶家具清空。",
|
||||
"displays": "已清除 %s 的显示。",
|
||||
"virtuals": "已将 %s 的所有货币和经验值清空。",
|
||||
"everything": "已将 %s 的所有物品清空。"
|
||||
"everything": "已将 %s 的所有物品清空。",
|
||||
"description": "从您的背包中删除所有未装备且已解锁的物品,包括稀有物品。"
|
||||
},
|
||||
"coop": {
|
||||
"usage": "用法:coop <playerId> <target playerId>",
|
||||
"success": "已强制召唤 %s 到 %s的世界"
|
||||
"success": "已强制召唤 %s 到 %s的世界",
|
||||
"description": "强制召唤指定用户到他人的世界。"
|
||||
},
|
||||
"enter_dungeon": {
|
||||
"usage": "用法:enterdungeon <dungeon id>",
|
||||
"changed": "已进入秘境 %s",
|
||||
"not_found_error": "此秘境不存在。",
|
||||
"in_dungeon_error": "你已经在秘境中了。"
|
||||
"in_dungeon_error": "你已经在秘境中了。",
|
||||
"description": "进入指定秘境。"
|
||||
},
|
||||
"giveAll": {
|
||||
"usage": "用法:giveall [player] [amount]",
|
||||
"started": "正在给予全部物品...",
|
||||
"success": "已给予全部物品。",
|
||||
"invalid_amount_or_playerId": "无效的数量/玩家ID。"
|
||||
"invalid_amount_or_playerId": "无效的数量/玩家ID。",
|
||||
"description": "给予所有物品。"
|
||||
},
|
||||
"giveArtifact": {
|
||||
"usage": "用法:giveart|gart [player] <artifactId> <mainPropId> [<appendPropId>[,<times>]]... [level]",
|
||||
"id_error": "无效的圣遗物ID。",
|
||||
"success": "已将 %s 给予 %s。"
|
||||
"success": "已将 %s 给予 %s。",
|
||||
"description": "给予指定圣遗物。"
|
||||
},
|
||||
"giveChar": {
|
||||
"usage": "用法:givechar <player> <itemId|itemName> [amount]",
|
||||
"given": "给予角色 %s 等级 %s 向UID %s.",
|
||||
"invalid_avatar_id": "无效的角色ID。",
|
||||
"invalid_avatar_level": "无效的角色等級。.",
|
||||
"invalid_avatar_or_player_id": "无效的角色ID/玩家ID。"
|
||||
"invalid_avatar_or_player_id": "无效的角色ID/玩家ID。",
|
||||
"description": "给予指定角色。"
|
||||
},
|
||||
"give": {
|
||||
"usage": "用法:give <player> <itemId|itemName> [amount] [level] [refinement]",
|
||||
@ -151,29 +160,36 @@
|
||||
"refinement_must_between_1_and_5": "精炼等阶必须在 1 到 5 之间。",
|
||||
"given": "已将 %s 个 %s 给予 %s。",
|
||||
"given_with_level_and_refinement": "已将 %s [等級%s, 精炼%s] %s个给予 %s",
|
||||
"given_level": "已将 %s 等级 %s %s 个给予UID %s"
|
||||
"given_level": "已将 %s 等级 %s %s 个给予UID %s",
|
||||
"description": "给予指定物品。"
|
||||
},
|
||||
"godmode": {
|
||||
"success": "上帝模式已被设置为 %s 。 [用户:%s]"
|
||||
"success": "上帝模式已被设置为 %s 。 [用户:%s]",
|
||||
"description": "防止你受到伤害。"
|
||||
},
|
||||
"heal": {
|
||||
"success": "所有角色已被治疗。"
|
||||
"success": "所有角色已被治疗。",
|
||||
"description": "治疗所选队伍的角色。"
|
||||
},
|
||||
"kick": {
|
||||
"player_kick_player": "玩家 [%s:%s] 已将 [%s:%s] 踢出",
|
||||
"server_kick_player": "正在踢出玩家 [%s:%s]"
|
||||
"server_kick_player": "正在踢出玩家 [%s:%s]",
|
||||
"description": "从服务器内踢出指定玩家。"
|
||||
},
|
||||
"kill": {
|
||||
"usage": "用法:killall [playerUid] [sceneId]",
|
||||
"scene_not_found_in_player_world": "未在玩家世界中找到此场景",
|
||||
"kill_monsters_in_scene": "已杀死 %s 个怪物。 [场景ID: %s]"
|
||||
"kill_monsters_in_scene": "已杀死 %s 个怪物。 [场景ID: %s]",
|
||||
"description": "杀死所有怪物"
|
||||
},
|
||||
"killCharacter": {
|
||||
"usage": "用法:/killcharacter [playerId]",
|
||||
"success": "已杀死 %s 目前使用的角色。"
|
||||
"success": "已杀死 %s 目前使用的角色。",
|
||||
"description": "杀死目前使用的角色"
|
||||
},
|
||||
"list": {
|
||||
"success": "目前在线人数:%s"
|
||||
"success": "目前在线人数:%s",
|
||||
"description": "查看所有玩家"
|
||||
},
|
||||
"permission": {
|
||||
"usage": "用法:permission <add|remove> <username> <permission>",
|
||||
@ -181,21 +197,26 @@
|
||||
"has_error": "此玩家已拥有此权限!",
|
||||
"remove": "权限已移除。",
|
||||
"not_have_error": "此玩家未拥有权限!",
|
||||
"account_error": "账号不存在!"
|
||||
"account_error": "账号不存在!",
|
||||
"description": "给予或移除指定玩家的权限。"
|
||||
},
|
||||
"position": {
|
||||
"success": "坐标:%.3f, %.3f, %.3f\n场景ID:%d"
|
||||
"success": "坐标:%.3f, %.3f, %.3f\n场景ID:%d",
|
||||
"description": "获取所在位置。"
|
||||
},
|
||||
"reload": {
|
||||
"reload_start": "正在重载配置文件和数据。",
|
||||
"reload_done": "重载完毕。"
|
||||
"reload_done": "重载完毕。",
|
||||
"description": "重载配置文件和数据。"
|
||||
},
|
||||
"resetConst": {
|
||||
"reset_all": "重置所有角色的命座。",
|
||||
"success": "已重置 %s 的命座,重新登录后将会生效。"
|
||||
"success": "已重置 %s 的命座,重新登录后将会生效。",
|
||||
"description": "重置当前角色的命之座,执行命令后需重新登录以生效。"
|
||||
},
|
||||
"resetShopLimit": {
|
||||
"usage": "用法:/resetshop <player id>"
|
||||
"usage": "用法:/resetshop <player id>",
|
||||
"description": "重置所选玩家的商店刷新时间。"
|
||||
},
|
||||
"sendMail": {
|
||||
"usage": "用法:give [player] <itemId|itemName> [amount]",
|
||||
@ -217,17 +238,20 @@
|
||||
"message": "<正文>",
|
||||
"sender": "<发件人>",
|
||||
"arguments": "<itemId|itemName|finish> [数量] [等级]",
|
||||
"error": "错误:无效的编写阶段 %s。需要 StackTrace 请查看服务器控制台。"
|
||||
"error": "错误:无效的编写阶段 %s。需要 StackTrace 请查看服务器控制台。",
|
||||
"description": "向指定用户发送邮件。 此命令的用法可根据附加的参数而变化。"
|
||||
},
|
||||
"sendMessage": {
|
||||
"usage": "用法:sendmessage <player> <message>",
|
||||
"success": "消息已发送。"
|
||||
"success": "消息已发送。",
|
||||
"description": "向指定玩家发送消息"
|
||||
},
|
||||
"setFetterLevel": {
|
||||
"usage": "用法:setfetterlevel <level>",
|
||||
"range_error": "好感度等级必须在 0 到 10 之间。",
|
||||
"fetter_set_level": "好感度已设置为 %s 级",
|
||||
"level_error": "无效的好感度等级。"
|
||||
"level_error": "无效的好感度等级。",
|
||||
"description": "设置当前角色的好感度等级。"
|
||||
},
|
||||
"setStats": {
|
||||
"usage_console": "用法:setstats|stats @<UID> <stat> <value>",
|
||||
@ -238,20 +262,24 @@
|
||||
"player_error": "玩家不存在或已离线。",
|
||||
"set_self": "%s 已经设置为 %s。",
|
||||
"set_for_uid": "%s 的使用者 %s 更改为 %s。",
|
||||
"set_max_hp": "最大生命值更改为 %s。"
|
||||
"set_max_hp": "最大生命值更改为 %s。",
|
||||
"description": "设置当前角色的属性。"
|
||||
},
|
||||
"setWorldLevel": {
|
||||
"usage": "用法:setworldlevel <level>",
|
||||
"value_error": "世界等级必须设置在0-8之间。",
|
||||
"success": "已将世界等级设为%s。",
|
||||
"invalid_world_level": "无效的世界等级。"
|
||||
"invalid_world_level": "无效的世界等级。",
|
||||
"description": "设置世界等级,执行命令后需重新登录以生效。"
|
||||
},
|
||||
"spawn": {
|
||||
"usage": "用法:spawn <entityId> [amount] [level(仅限怪物]",
|
||||
"success": "已生成 %s 个 %s。"
|
||||
"success": "已生成 %s 个 %s。",
|
||||
"description": "在你附近生成一个生物。"
|
||||
},
|
||||
"stop": {
|
||||
"success": "正在关闭服务器..."
|
||||
"success": "正在关闭服务器...",
|
||||
"description": "停止服务器"
|
||||
},
|
||||
"talent": {
|
||||
"usage_1": "设置天赋等级:/talent set <talentID> <value>",
|
||||
@ -267,32 +295,41 @@
|
||||
"invalid_level": "无效的天赋等级。",
|
||||
"normal_attack_id": "普通攻击的 ID 为 %s。",
|
||||
"e_skill_id": "元素战技ID %s。",
|
||||
"q_skill_id": "元素爆发ID %s。"
|
||||
"q_skill_id": "元素爆发ID %s。",
|
||||
"description": "设置当前角色的天赋等级。"
|
||||
},
|
||||
"teleportAll": {
|
||||
"success": "已将全部玩家传送到你的位置",
|
||||
"error": "命令仅限处于多人游戏状态下使用。"
|
||||
"error": "命令仅限处于多人游戏状态下使用。",
|
||||
"description": "将你世界中的所有玩家传送到你所在的位置。"
|
||||
},
|
||||
"teleport": {
|
||||
"usage_server": "用法:/tp @<player id> <x> <y> <z> [scene id]",
|
||||
"usage": "用法:/tp [@<player id>] <x> <y> <z> [scene id]",
|
||||
"specify_player_id": "你必须指定一个玩家ID。",
|
||||
"invalid_position": "无效的位置。",
|
||||
"success": "传送 %s 到坐标 %s,%s,%s,场景为 %s"
|
||||
"success": "传送 %s 到坐标 %s,%s,%s,场景为 %s",
|
||||
"description": "改变指定玩家的位置。"
|
||||
},
|
||||
"weather": {
|
||||
"usage": "用法:weather <weatherId> [climateId]",
|
||||
"success": "已将当前天气设定为 %s,气候为 %s。",
|
||||
"invalid_id": "无效的天气ID。"
|
||||
"invalid_id": "无效的天气ID。",
|
||||
"description": "改变天气"
|
||||
},
|
||||
"drop": {
|
||||
"command_usage": "用法:drop <itemId|itemName> [amount]",
|
||||
"success": "已将 %s x %s 丟在附近。"
|
||||
"success": "已将 %s x %s 丟在附近。",
|
||||
"description": "在你附近丢一个物品。"
|
||||
},
|
||||
"help": {
|
||||
"usage": "用法:",
|
||||
"aliases": "別名:",
|
||||
"available_commands": "可用指令:"
|
||||
"available_commands": "可用指令:",
|
||||
"description": "发送帮助信息或显示指定命令的信息。"
|
||||
},
|
||||
"restart": {
|
||||
"description": "重新启动服务器。"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user