mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-27 08:36:38 +00:00
Merge branch 'development' into dev-quests
This commit is contained in:
commit
0df4a076d7
8
proto/AbilityMixinWidgetMpSupport.proto
Normal file
8
proto/AbilityMixinWidgetMpSupport.proto
Normal file
@ -0,0 +1,8 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message AbilityMixinWidgetMpSupport {
|
||||
uint32 target_entity_id = 1;
|
||||
}
|
29
proto/AllWidgetDataNotify.proto
Normal file
29
proto/AllWidgetDataNotify.proto
Normal file
@ -0,0 +1,29 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "LunchBoxData.proto";
|
||||
import "AnchorPointData.proto";
|
||||
import "OneoffGatherPointDetectorData.proto";
|
||||
import "ClientCollectorData.proto";
|
||||
import "WidgetCoolDownData.proto";
|
||||
import "WidgetSlotData.proto";
|
||||
|
||||
message AllWidgetDataNotify {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4260;
|
||||
}
|
||||
|
||||
repeated AnchorPointData anchor_point_list = 2;
|
||||
uint32 next_anchor_point_usable_time = 3;
|
||||
LunchBoxData lunch_box_data = 5;
|
||||
repeated OneoffGatherPointDetectorData oneoff_gather_point_detector_data_list = 6;
|
||||
repeated ClientCollectorData client_collector_data_list = 7;
|
||||
repeated WidgetCoolDownData cool_down_group_data_list = 8;
|
||||
repeated WidgetCoolDownData normal_cool_down_data_list = 9;
|
||||
repeated WidgetSlotData slot_list = 11;
|
||||
}
|
12
proto/AnchorPointData.proto
Normal file
12
proto/AnchorPointData.proto
Normal file
@ -0,0 +1,12 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "Vector.proto";
|
||||
|
||||
message AnchorPointData {
|
||||
uint32 anchor_point_id = 1;
|
||||
Vector pos = 2;
|
||||
Vector rot = 3;
|
||||
uint32 end_time = 4;
|
||||
}
|
18
proto/AnchorPointDataNotify.proto
Normal file
18
proto/AnchorPointDataNotify.proto
Normal file
@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "AnchorPointData.proto";
|
||||
|
||||
message AnchorPointDataNotify {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4253;
|
||||
}
|
||||
|
||||
repeated AnchorPointData anchor_point_list = 1;
|
||||
uint32 next_usable_time = 2;
|
||||
}
|
24
proto/AnchorPointOpReq.proto
Normal file
24
proto/AnchorPointOpReq.proto
Normal file
@ -0,0 +1,24 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message AnchorPointOpReq {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
NONE = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
IS_ALLOW_CLIENT = 1;
|
||||
CMD_ID = 4255;
|
||||
}
|
||||
|
||||
enum AnchorPointOpType {
|
||||
ANCHOR_POINT_OP_NONE = 0;
|
||||
ANCHOR_POINT_OP_TELEPORT = 1;
|
||||
ANCHOR_POINT_OP_REMOVE = 2;
|
||||
}
|
||||
|
||||
uint32 anchor_point_op_type = 1;
|
||||
uint32 anchor_point_id = 2;
|
||||
}
|
18
proto/AnchorPointOpRsp.proto
Normal file
18
proto/AnchorPointOpRsp.proto
Normal file
@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message AnchorPointOpRsp {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4274;
|
||||
}
|
||||
|
||||
int32 retcode = 1;
|
||||
uint32 anchor_point_op_type = 2;
|
||||
uint32 anchor_point_id = 3;
|
||||
}
|
10
proto/ClientCollectorData.proto
Normal file
10
proto/ClientCollectorData.proto
Normal file
@ -0,0 +1,10 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message ClientCollectorData {
|
||||
uint32 material_id = 1;
|
||||
uint32 max_points = 2;
|
||||
uint32 curr_points = 3;
|
||||
}
|
17
proto/ClientCollectorDataNotify.proto
Normal file
17
proto/ClientCollectorDataNotify.proto
Normal file
@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "ClientCollectorData.proto";
|
||||
|
||||
message ClientCollectorDataNotify {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4280;
|
||||
}
|
||||
|
||||
repeated ClientCollectorData client_collector_data_list = 1;
|
||||
}
|
16
proto/GetWidgetSlotReq.proto
Normal file
16
proto/GetWidgetSlotReq.proto
Normal file
@ -0,0 +1,16 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message GetWidgetSlotReq {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
NONE = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
IS_ALLOW_CLIENT = 1;
|
||||
CMD_ID = 4285;
|
||||
}
|
||||
|
||||
}
|
18
proto/GetWidgetSlotRsp.proto
Normal file
18
proto/GetWidgetSlotRsp.proto
Normal file
@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "WidgetSlotData.proto";
|
||||
|
||||
message GetWidgetSlotRsp {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4291;
|
||||
}
|
||||
|
||||
int32 retcode = 1;
|
||||
repeated WidgetSlotData slot_list = 2;
|
||||
}
|
8
proto/LunchBoxData.proto
Normal file
8
proto/LunchBoxData.proto
Normal file
@ -0,0 +1,8 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message LunchBoxData {
|
||||
map<uint32, uint32> slot_material_map = 1;
|
||||
}
|
9
proto/LunchBoxSlotType.proto
Normal file
9
proto/LunchBoxSlotType.proto
Normal file
@ -0,0 +1,9 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
enum LunchBoxSlotType {
|
||||
LUNCH_BOX_SLOT_NONE = 0;
|
||||
LUNCH_BOX_SLOT_REVIVE = 1;
|
||||
LUNCH_BOX_SLOT_HEAL = 2;
|
||||
}
|
15
proto/OneoffGatherPointDetectorData.proto
Normal file
15
proto/OneoffGatherPointDetectorData.proto
Normal file
@ -0,0 +1,15 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "Vector.proto";
|
||||
|
||||
message OneoffGatherPointDetectorData {
|
||||
uint32 material_id = 1;
|
||||
bool is_all_collected = 2;
|
||||
bool is_hint_valid = 3;
|
||||
Vector hint_center_pos = 4;
|
||||
uint32 hint_radius = 5;
|
||||
uint32 group_id = 6;
|
||||
uint32 config_id = 7;
|
||||
}
|
17
proto/OneoffGatherPointDetectorDataNotify.proto
Normal file
17
proto/OneoffGatherPointDetectorDataNotify.proto
Normal file
@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "OneoffGatherPointDetectorData.proto";
|
||||
|
||||
message OneoffGatherPointDetectorDataNotify {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4288;
|
||||
}
|
||||
|
||||
repeated OneoffGatherPointDetectorData oneoff_gather_point_detector_data_list = 1;
|
||||
}
|
26
proto/QuickUseWidgetReq.proto
Normal file
26
proto/QuickUseWidgetReq.proto
Normal file
@ -0,0 +1,26 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "WidgetCreateLocationInfo.proto";
|
||||
import "WidgetCameraInfo.proto";
|
||||
import "WidgetCreatorInfo.proto";
|
||||
import "WidgetThunderBirdFeatherInfo.proto";
|
||||
|
||||
message QuickUseWidgetReq {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
NONE = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
IS_ALLOW_CLIENT = 1;
|
||||
CMD_ID = 4300;
|
||||
}
|
||||
|
||||
oneof Param {
|
||||
WidgetCreateLocationInfo location_info = 20;
|
||||
WidgetCameraInfo camera_info = 21;
|
||||
WidgetCreatorInfo creator_info = 22;
|
||||
WidgetThunderBirdFeatherInfo thunder_bird_feather_info = 23;
|
||||
}
|
||||
}
|
21
proto/QuickUseWidgetRsp.proto
Normal file
21
proto/QuickUseWidgetRsp.proto
Normal file
@ -0,0 +1,21 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "OneoffGatherPointDetectorData.proto";
|
||||
import "ClientCollectorData.proto";
|
||||
|
||||
message QuickUseWidgetRsp {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4258;
|
||||
}
|
||||
|
||||
int32 retcode = 1;
|
||||
uint32 material_id = 2;
|
||||
OneoffGatherPointDetectorData detector_data = 3;
|
||||
ClientCollectorData client_collector_data = 4;
|
||||
}
|
18
proto/SetUpLunchBoxWidgetReq.proto
Normal file
18
proto/SetUpLunchBoxWidgetReq.proto
Normal file
@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "LunchBoxData.proto";
|
||||
|
||||
message SetUpLunchBoxWidgetReq {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
NONE = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
IS_ALLOW_CLIENT = 1;
|
||||
CMD_ID = 4265;
|
||||
}
|
||||
|
||||
LunchBoxData lunch_box_data = 1;
|
||||
}
|
18
proto/SetUpLunchBoxWidgetRsp.proto
Normal file
18
proto/SetUpLunchBoxWidgetRsp.proto
Normal file
@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "LunchBoxData.proto";
|
||||
|
||||
message SetUpLunchBoxWidgetRsp {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4297;
|
||||
}
|
||||
|
||||
int32 retcode = 1;
|
||||
LunchBoxData lunch_box_data = 2;
|
||||
}
|
21
proto/SetWidgetSlotReq.proto
Normal file
21
proto/SetWidgetSlotReq.proto
Normal file
@ -0,0 +1,21 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "WidgetSlotOp.proto";
|
||||
import "WidgetSlotTag.proto";
|
||||
|
||||
message SetWidgetSlotReq {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
NONE = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
IS_ALLOW_CLIENT = 1;
|
||||
CMD_ID = 4269;
|
||||
}
|
||||
|
||||
WidgetSlotOp op = 2;
|
||||
repeated WidgetSlotTag tag_list = 3;
|
||||
uint32 material_id = 4;
|
||||
}
|
21
proto/SetWidgetSlotRsp.proto
Normal file
21
proto/SetWidgetSlotRsp.proto
Normal file
@ -0,0 +1,21 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "WidgetSlotOp.proto";
|
||||
import "WidgetSlotTag.proto";
|
||||
|
||||
message SetWidgetSlotRsp {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4279;
|
||||
}
|
||||
|
||||
int32 retcode = 1;
|
||||
WidgetSlotOp op = 2;
|
||||
repeated WidgetSlotTag tag_list = 3;
|
||||
uint32 material_id = 4;
|
||||
}
|
20
proto/UseWidgetCreateGadgetReq.proto
Normal file
20
proto/UseWidgetCreateGadgetReq.proto
Normal file
@ -0,0 +1,20 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "Vector.proto";
|
||||
|
||||
message UseWidgetCreateGadgetReq {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
NONE = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
IS_ALLOW_CLIENT = 1;
|
||||
CMD_ID = 4276;
|
||||
}
|
||||
|
||||
uint32 material_id = 1;
|
||||
Vector pos = 2;
|
||||
Vector rot = 3;
|
||||
}
|
17
proto/UseWidgetCreateGadgetRsp.proto
Normal file
17
proto/UseWidgetCreateGadgetRsp.proto
Normal file
@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message UseWidgetCreateGadgetRsp {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4270;
|
||||
}
|
||||
|
||||
int32 retcode = 1;
|
||||
uint32 material_id = 2;
|
||||
}
|
17
proto/UseWidgetRetractGadgetReq.proto
Normal file
17
proto/UseWidgetRetractGadgetReq.proto
Normal file
@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message UseWidgetRetractGadgetReq {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
NONE = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
IS_ALLOW_CLIENT = 1;
|
||||
CMD_ID = 4273;
|
||||
}
|
||||
|
||||
uint32 entity_id = 1;
|
||||
}
|
18
proto/UseWidgetRetractGadgetRsp.proto
Normal file
18
proto/UseWidgetRetractGadgetRsp.proto
Normal file
@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message UseWidgetRetractGadgetRsp {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
NONE = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
IS_ALLOW_CLIENT = 1;
|
||||
CMD_ID = 4266;
|
||||
}
|
||||
|
||||
int32 retcode = 1;
|
||||
uint32 entity_id = 2;
|
||||
}
|
17
proto/WidgetActiveChangeNotify.proto
Normal file
17
proto/WidgetActiveChangeNotify.proto
Normal file
@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "WidgetSlotData.proto";
|
||||
|
||||
message WidgetActiveChangeNotify {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4264;
|
||||
}
|
||||
|
||||
repeated WidgetSlotData widget_data_list = 1;
|
||||
}
|
8
proto/WidgetCameraInfo.proto
Normal file
8
proto/WidgetCameraInfo.proto
Normal file
@ -0,0 +1,8 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message WidgetCameraInfo {
|
||||
uint32 target_entity_id = 1;
|
||||
}
|
10
proto/WidgetCoolDownData.proto
Normal file
10
proto/WidgetCoolDownData.proto
Normal file
@ -0,0 +1,10 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message WidgetCoolDownData {
|
||||
uint32 id = 1;
|
||||
uint64 cool_down_time = 2;
|
||||
bool is_success = 3;
|
||||
}
|
18
proto/WidgetCoolDownNotify.proto
Normal file
18
proto/WidgetCoolDownNotify.proto
Normal file
@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "WidgetCoolDownData.proto";
|
||||
|
||||
message WidgetCoolDownNotify {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4298;
|
||||
}
|
||||
|
||||
repeated WidgetCoolDownData group_cool_down_data_list = 1;
|
||||
repeated WidgetCoolDownData normal_cool_down_data_list = 2;
|
||||
}
|
10
proto/WidgetCreateLocationInfo.proto
Normal file
10
proto/WidgetCreateLocationInfo.proto
Normal file
@ -0,0 +1,10 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "Vector.proto";
|
||||
|
||||
message WidgetCreateLocationInfo {
|
||||
Vector pos = 1;
|
||||
Vector rot = 2;
|
||||
}
|
12
proto/WidgetCreatorInfo.proto
Normal file
12
proto/WidgetCreatorInfo.proto
Normal file
@ -0,0 +1,12 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "WidgetCreatorOpType.proto";
|
||||
import "WidgetCreateLocationInfo.proto";
|
||||
|
||||
message WidgetCreatorInfo {
|
||||
WidgetCreatorOpType op_type = 1;
|
||||
uint32 entity_id = 2;
|
||||
WidgetCreateLocationInfo location_info = 3;
|
||||
}
|
9
proto/WidgetCreatorOpType.proto
Normal file
9
proto/WidgetCreatorOpType.proto
Normal file
@ -0,0 +1,9 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
enum WidgetCreatorOpType {
|
||||
WIDGET_CREATOR_TYPE_NONE = 0;
|
||||
WIDGET_CREATOR_TYPE_RETRACT = 1;
|
||||
WIDGET_CREATOR_TYPE_RETRACT_AND_CREATE = 2;
|
||||
}
|
23
proto/WidgetDoBagReq.proto
Normal file
23
proto/WidgetDoBagReq.proto
Normal file
@ -0,0 +1,23 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "WidgetCreateLocationInfo.proto";
|
||||
import "WidgetCreatorInfo.proto";
|
||||
|
||||
message WidgetDoBagReq {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
NONE = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
IS_ALLOW_CLIENT = 1;
|
||||
CMD_ID = 4290;
|
||||
}
|
||||
|
||||
oneof OpInfo {
|
||||
WidgetCreateLocationInfo location_info = 20;
|
||||
WidgetCreatorInfo widget_creator_info = 21;
|
||||
}
|
||||
uint32 material_id = 1;
|
||||
}
|
17
proto/WidgetDoBagRsp.proto
Normal file
17
proto/WidgetDoBagRsp.proto
Normal file
@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message WidgetDoBagRsp {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4271;
|
||||
}
|
||||
|
||||
int32 retcode = 1;
|
||||
uint32 material_id = 2;
|
||||
}
|
17
proto/WidgetGadgetAllDataNotify.proto
Normal file
17
proto/WidgetGadgetAllDataNotify.proto
Normal file
@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "WidgetGadgetData.proto";
|
||||
|
||||
message WidgetGadgetAllDataNotify {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4262;
|
||||
}
|
||||
|
||||
repeated WidgetGadgetData widget_gadget_data = 1;
|
||||
}
|
9
proto/WidgetGadgetData.proto
Normal file
9
proto/WidgetGadgetData.proto
Normal file
@ -0,0 +1,9 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message WidgetGadgetData {
|
||||
uint32 gadget_id = 1;
|
||||
repeated uint32 gadget_entity_id_list = 3;
|
||||
}
|
17
proto/WidgetGadgetDataNotify.proto
Normal file
17
proto/WidgetGadgetDataNotify.proto
Normal file
@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "WidgetGadgetData.proto";
|
||||
|
||||
message WidgetGadgetDataNotify {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4256;
|
||||
}
|
||||
|
||||
WidgetGadgetData widget_gadget_data = 1;
|
||||
}
|
16
proto/WidgetGadgetDestroyNotify.proto
Normal file
16
proto/WidgetGadgetDestroyNotify.proto
Normal file
@ -0,0 +1,16 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message WidgetGadgetDestroyNotify {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4268;
|
||||
}
|
||||
|
||||
uint32 entity_id = 1;
|
||||
}
|
19
proto/WidgetReportReq.proto
Normal file
19
proto/WidgetReportReq.proto
Normal file
@ -0,0 +1,19 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message WidgetReportReq {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
NONE = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
IS_ALLOW_CLIENT = 1;
|
||||
CMD_ID = 4294;
|
||||
}
|
||||
|
||||
uint32 material_id = 1;
|
||||
bool is_clear_hint = 2;
|
||||
bool is_client_collect = 3;
|
||||
}
|
17
proto/WidgetReportRsp.proto
Normal file
17
proto/WidgetReportRsp.proto
Normal file
@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message WidgetReportRsp {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4259;
|
||||
}
|
||||
|
||||
int32 retcode = 1;
|
||||
uint32 material_id = 2;
|
||||
}
|
19
proto/WidgetSlotChangeNotify.proto
Normal file
19
proto/WidgetSlotChangeNotify.proto
Normal file
@ -0,0 +1,19 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "WidgetSlotOp.proto";
|
||||
import "WidgetSlotData.proto";
|
||||
|
||||
message WidgetSlotChangeNotify {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4289;
|
||||
}
|
||||
|
||||
WidgetSlotOp op = 1;
|
||||
WidgetSlotData slot = 2;
|
||||
}
|
12
proto/WidgetSlotData.proto
Normal file
12
proto/WidgetSlotData.proto
Normal file
@ -0,0 +1,12 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
import "WidgetSlotTag.proto";
|
||||
|
||||
message WidgetSlotData {
|
||||
WidgetSlotTag tag = 1;
|
||||
uint32 material_id = 2;
|
||||
uint32 cd_over_time = 3;
|
||||
bool is_active = 4;
|
||||
}
|
8
proto/WidgetSlotOp.proto
Normal file
8
proto/WidgetSlotOp.proto
Normal file
@ -0,0 +1,8 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
enum WidgetSlotOp {
|
||||
ATTACH = 0;
|
||||
DETACH = 1;
|
||||
}
|
8
proto/WidgetSlotTag.proto
Normal file
8
proto/WidgetSlotTag.proto
Normal file
@ -0,0 +1,8 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
enum WidgetSlotTag {
|
||||
WIDGET_SLOT_QUICK_USE = 0;
|
||||
WIDGET_SLOT_ATTACH_AVATAR = 1;
|
||||
}
|
7
proto/WidgetSlotTagComparer.proto
Normal file
7
proto/WidgetSlotTagComparer.proto
Normal file
@ -0,0 +1,7 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message WidgetSlotTagComparer {
|
||||
}
|
8
proto/WidgetThunderBirdFeatherInfo.proto
Normal file
8
proto/WidgetThunderBirdFeatherInfo.proto
Normal file
@ -0,0 +1,8 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message WidgetThunderBirdFeatherInfo {
|
||||
repeated uint32 entity_id_list = 1;
|
||||
}
|
17
proto/WidgetUseAttachAbilityGroupChangeNotify.proto
Normal file
17
proto/WidgetUseAttachAbilityGroupChangeNotify.proto
Normal file
@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "emu.grasscutter.net.proto";
|
||||
|
||||
|
||||
message WidgetUseAttachAbilityGroupChangeNotify {
|
||||
enum CmdId {
|
||||
option allow_alias = true;
|
||||
NONE = 0;
|
||||
ENET_CHANNEL_ID = 0;
|
||||
ENET_IS_RELIABLE = 1;
|
||||
CMD_ID = 4292;
|
||||
}
|
||||
|
||||
uint32 material_id = 1;
|
||||
bool is_attach = 2;
|
||||
}
|
@ -4,6 +4,7 @@ import java.io.*;
|
||||
import java.util.Calendar;
|
||||
|
||||
import emu.grasscutter.command.CommandMap;
|
||||
import emu.grasscutter.game.managers.StaminaManager.StaminaManager;
|
||||
import emu.grasscutter.plugin.PluginManager;
|
||||
import emu.grasscutter.plugin.api.ServerHook;
|
||||
import emu.grasscutter.scripts.ScriptLoader;
|
||||
@ -111,6 +112,9 @@ public final class Grasscutter {
|
||||
// Create plugin manager instance.
|
||||
pluginManager = new PluginManager();
|
||||
|
||||
// TODO: find a better place?
|
||||
StaminaManager.initialize();
|
||||
|
||||
// Start servers.
|
||||
var runMode = SERVER.runMode;
|
||||
if (runMode == ServerRunMode.HYBRID) {
|
||||
|
@ -13,6 +13,7 @@ import com.google.gson.reflect.TypeToken;
|
||||
import com.sun.nio.file.SensitivityWatchEventModifier;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.def.ItemData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
@ -127,15 +128,10 @@ public class GachaManager {
|
||||
}
|
||||
|
||||
// Spend currency
|
||||
if (banner.getCostItem() > 0) {
|
||||
GameItem costItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(banner.getCostItem());
|
||||
if (costItem == null || costItem.getCount() < times) {
|
||||
if (banner.getCostItem() > 0 && !player.getInventory().payItem(banner.getCostItem(), times)) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.getInventory().removeItem(costItem, times);
|
||||
}
|
||||
|
||||
// Roll
|
||||
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(banner);
|
||||
IntList wonItems = new IntArrayList(times);
|
||||
|
@ -7,6 +7,7 @@ import java.util.List;
|
||||
|
||||
import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.def.AvatarCostumeData;
|
||||
import emu.grasscutter.data.def.AvatarData;
|
||||
import emu.grasscutter.data.def.AvatarFlycloakData;
|
||||
@ -257,6 +258,64 @@ public class Inventory implements Iterable<GameItem> {
|
||||
}
|
||||
}
|
||||
|
||||
private int getVirtualItemCount(int itemId) {
|
||||
switch (itemId) {
|
||||
case 201: // Primogem
|
||||
return player.getPrimogems();
|
||||
case 202: // Mora
|
||||
return player.getMora();
|
||||
case 203: // Genesis Crystals
|
||||
return player.getCrystals();
|
||||
default:
|
||||
GameItem item = getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId); // What if we ever want to operate on weapons/relics/furniture? :S
|
||||
return (item == null) ? 0 : item.getCount();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean payItem(int id, int count) {
|
||||
return payItem(new ItemParamData(id, count));
|
||||
}
|
||||
|
||||
public boolean payItem(ItemParamData costItem) {
|
||||
return payItems(new ItemParamData[] {costItem}, 1, null);
|
||||
}
|
||||
|
||||
public boolean payItems(ItemParamData[] costItems) {
|
||||
return payItems(costItems, 1, null);
|
||||
}
|
||||
|
||||
public boolean payItems(ItemParamData[] costItems, int quantity) {
|
||||
return payItems(costItems, quantity, null);
|
||||
}
|
||||
|
||||
public synchronized boolean payItems(ItemParamData[] costItems, int quantity, ActionReason reason) {
|
||||
// Make sure player has requisite items
|
||||
for (ItemParamData cost : costItems) {
|
||||
if (getVirtualItemCount(cost.getId()) < (cost.getCount() * quantity)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// All costs are satisfied, now remove them all
|
||||
for (ItemParamData cost : costItems) {
|
||||
switch (cost.getId()) {
|
||||
case 201 -> // Primogem
|
||||
player.setPrimogems(player.getPrimogems() - (cost.getCount() * quantity));
|
||||
case 202 -> // Mora
|
||||
player.setMora(player.getMora() - (cost.getCount() * quantity));
|
||||
case 203 -> // Genesis Crystals
|
||||
player.setCrystals(player.getCrystals() - (cost.getCount() * quantity));
|
||||
default ->
|
||||
removeItem(getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()), cost.getCount() * quantity);
|
||||
}
|
||||
}
|
||||
|
||||
if (reason != null) { // Do we need these?
|
||||
// getPlayer().sendPacket(new PacketItemAddHintNotify(changedItems, reason));
|
||||
}
|
||||
// getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void removeItems(List<GameItem> items) {
|
||||
// TODO Bulk delete
|
||||
for (GameItem item : items) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package emu.grasscutter.game.managers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
@ -38,6 +39,8 @@ public class InventoryManager {
|
||||
|
||||
private final static int RELIC_MATERIAL_1 = 105002; // Sanctifying Unction
|
||||
private final static int RELIC_MATERIAL_2 = 105003; // Sanctifying Essence
|
||||
private final static int RELIC_MATERIAL_EXP_1 = 2500; // Sanctifying Unction
|
||||
private final static int RELIC_MATERIAL_EXP_2 = 10000; // Sanctifying Essence
|
||||
|
||||
private final static int WEAPON_ORE_1 = 104011; // Enhancement Ore
|
||||
private final static int WEAPON_ORE_2 = 104012; // Fine Enhancement Ore
|
||||
@ -85,6 +88,7 @@ public class InventoryManager {
|
||||
int moraCost = 0;
|
||||
int expGain = 0;
|
||||
|
||||
List<GameItem> foodRelics = new ArrayList<GameItem>();
|
||||
for (long guid : foodRelicList) {
|
||||
// Add to delete queue
|
||||
GameItem food = player.getInventory().getItemByGuid(guid);
|
||||
@ -96,23 +100,21 @@ public class InventoryManager {
|
||||
expGain += food.getItemData().getBaseConvExp();
|
||||
// Feeding artifact with exp already
|
||||
if (food.getTotalExp() > 0) {
|
||||
expGain += (int) Math.floor(food.getTotalExp() * .8f);
|
||||
expGain += (food.getTotalExp() * 4) / 5;
|
||||
}
|
||||
foodRelics.add(food);
|
||||
}
|
||||
List<ItemParamData> payList = new ArrayList<ItemParamData>();
|
||||
for (ItemParam itemParam : list) {
|
||||
GameItem food = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemParam.getItemId());
|
||||
if (food == null || food.getItemData().getMaterialType() != MaterialType.MATERIAL_RELIQUARY_MATERIAL) {
|
||||
continue;
|
||||
}
|
||||
int amount = Math.min(food.getCount(), itemParam.getCount());
|
||||
int gain = 0;
|
||||
if (food.getItemId() == RELIC_MATERIAL_2) {
|
||||
gain = 10000 * amount;
|
||||
} else if (food.getItemId() == RELIC_MATERIAL_1) {
|
||||
gain = 2500 * amount;
|
||||
}
|
||||
int amount = itemParam.getCount(); // Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order
|
||||
int gain = amount * switch(itemParam.getItemId()) {
|
||||
case RELIC_MATERIAL_1 -> RELIC_MATERIAL_EXP_1;
|
||||
case RELIC_MATERIAL_2 -> RELIC_MATERIAL_EXP_2;
|
||||
default -> 0;
|
||||
};
|
||||
expGain += gain;
|
||||
moraCost += gain;
|
||||
payList.add(new ItemParamData(itemParam.getItemId(), itemParam.getCount()));
|
||||
}
|
||||
|
||||
// Make sure exp gain is valid
|
||||
@ -120,28 +122,14 @@ public class InventoryManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check mora
|
||||
if (player.getMora() < moraCost) {
|
||||
// Confirm payment of materials and mora (assume food relics are payable afterwards)
|
||||
payList.add(new ItemParamData(202, moraCost));
|
||||
if (!player.getInventory().payItems(payList.toArray(new ItemParamData[0]))) {
|
||||
return;
|
||||
}
|
||||
player.setMora(player.getMora() - moraCost);
|
||||
|
||||
// Consume food items
|
||||
for (long guid : foodRelicList) {
|
||||
GameItem food = player.getInventory().getItemByGuid(guid);
|
||||
if (food == null || !food.isDestroyable()) {
|
||||
continue;
|
||||
}
|
||||
player.getInventory().removeItem(food);
|
||||
}
|
||||
for (ItemParam itemParam : list) {
|
||||
GameItem food = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemParam.getItemId());
|
||||
if (food == null || food.getItemData().getMaterialType() != MaterialType.MATERIAL_RELIQUARY_MATERIAL) {
|
||||
continue;
|
||||
}
|
||||
int amount = Math.min(food.getCount(), itemParam.getCount());
|
||||
player.getInventory().removeItem(food, amount);
|
||||
}
|
||||
// Consume food relics
|
||||
player.getInventory().removeItems(foodRelics);
|
||||
|
||||
// Implement random rate boost
|
||||
int rate = 1;
|
||||
@ -231,22 +219,16 @@ public class InventoryManager {
|
||||
}
|
||||
expGain += food.getItemData().getWeaponBaseExp();
|
||||
if (food.getTotalExp() > 0) {
|
||||
expGain += (int) Math.floor(food.getTotalExp() * .8f);
|
||||
expGain += (food.getTotalExp() * 4) / 5;
|
||||
}
|
||||
}
|
||||
for (ItemParam param : itemParamList) {
|
||||
GameItem food = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(param.getItemId());
|
||||
if (food == null || food.getItemData().getMaterialType() != MaterialType.MATERIAL_WEAPON_EXP_STONE) {
|
||||
continue;
|
||||
}
|
||||
int amount = Math.min(param.getCount(), food.getCount());
|
||||
if (food.getItemId() == WEAPON_ORE_3) {
|
||||
expGain += 10000 * amount;
|
||||
} else if (food.getItemId() == WEAPON_ORE_2) {
|
||||
expGain += 2000 * amount;
|
||||
} else if (food.getItemId() == WEAPON_ORE_1) {
|
||||
expGain += 400 * amount;
|
||||
}
|
||||
expGain += param.getCount() * switch(param.getItemId()) {
|
||||
case WEAPON_ORE_1 -> WEAPON_ORE_EXP_1;
|
||||
case WEAPON_ORE_2 -> WEAPON_ORE_EXP_2;
|
||||
case WEAPON_ORE_3 -> WEAPON_ORE_EXP_3;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
// Try
|
||||
@ -288,65 +270,45 @@ public class InventoryManager {
|
||||
}
|
||||
|
||||
// Get exp gain
|
||||
int expGain = 0, moraCost = 0;
|
||||
|
||||
int expGain = 0, expGainFree = 0;
|
||||
List<GameItem> foodWeapons = new ArrayList<GameItem>();
|
||||
for (long guid : foodWeaponGuidList) {
|
||||
GameItem food = player.getInventory().getItemByGuid(guid);
|
||||
if (food == null || !food.isDestroyable()) {
|
||||
continue;
|
||||
}
|
||||
expGain += food.getItemData().getWeaponBaseExp();
|
||||
moraCost += (int) Math.floor(food.getItemData().getWeaponBaseExp() * .1f);
|
||||
if (food.getTotalExp() > 0) {
|
||||
expGain += (int) Math.floor(food.getTotalExp() * .8f);
|
||||
expGainFree += (food.getTotalExp() * 4) / 5; // No tax :D
|
||||
}
|
||||
foodWeapons.add(food);
|
||||
}
|
||||
List<ItemParamData> payList = new ArrayList<ItemParamData>();
|
||||
for (ItemParam param : itemParamList) {
|
||||
GameItem food = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(param.getItemId());
|
||||
if (food == null || food.getItemData().getMaterialType() != MaterialType.MATERIAL_WEAPON_EXP_STONE) {
|
||||
continue;
|
||||
}
|
||||
int amount = Math.min(param.getCount(), food.getCount());
|
||||
int gain = 0;
|
||||
if (food.getItemId() == WEAPON_ORE_3) {
|
||||
gain = 10000 * amount;
|
||||
} else if (food.getItemId() == WEAPON_ORE_2) {
|
||||
gain = 2000 * amount;
|
||||
} else if (food.getItemId() == WEAPON_ORE_1) {
|
||||
gain = 400 * amount;
|
||||
}
|
||||
int amount = param.getCount(); // Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order
|
||||
int gain = amount * switch(param.getItemId()) {
|
||||
case WEAPON_ORE_1 -> WEAPON_ORE_EXP_1;
|
||||
case WEAPON_ORE_2 -> WEAPON_ORE_EXP_2;
|
||||
case WEAPON_ORE_3 -> WEAPON_ORE_EXP_3;
|
||||
default -> 0;
|
||||
};
|
||||
expGain += gain;
|
||||
moraCost += (int) Math.floor(gain * .1f);
|
||||
payList.add(new ItemParamData(param.getItemId(), amount));
|
||||
}
|
||||
|
||||
// Make sure exp gain is valid
|
||||
int moraCost = expGain / 10;
|
||||
expGain += expGainFree;
|
||||
if (expGain <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mora check
|
||||
if (player.getMora() >= moraCost) {
|
||||
player.setMora(player.getMora() - moraCost);
|
||||
} else {
|
||||
// Confirm payment of materials and mora (assume food weapons are payable afterwards)
|
||||
payList.add(new ItemParamData(202, moraCost));
|
||||
if (!player.getInventory().payItems(payList.toArray(new ItemParamData[0]))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Consume weapon/items used to feed
|
||||
for (long guid : foodWeaponGuidList) {
|
||||
GameItem food = player.getInventory().getItemByGuid(guid);
|
||||
if (food == null || !food.isDestroyable()) {
|
||||
continue;
|
||||
}
|
||||
player.getInventory().removeItem(food);
|
||||
}
|
||||
for (ItemParam param : itemParamList) {
|
||||
GameItem food = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(param.getItemId());
|
||||
if (food == null || food.getItemData().getMaterialType() != MaterialType.MATERIAL_WEAPON_EXP_STONE) {
|
||||
continue;
|
||||
}
|
||||
int amount = Math.min(param.getCount(), food.getCount());
|
||||
player.getInventory().removeItem(food, amount);
|
||||
}
|
||||
player.getInventory().removeItems(foodWeapons);
|
||||
|
||||
// Level up
|
||||
int maxLevel = promoteData.getUnlockMaxLevel();
|
||||
@ -393,7 +355,7 @@ public class InventoryManager {
|
||||
player.sendPacket(new PacketWeaponUpgradeRsp(weapon, oldLevel, leftovers));
|
||||
}
|
||||
|
||||
private List<ItemParam> getLeftoverOres(float leftover) {
|
||||
private List<ItemParam> getLeftoverOres(int leftover) {
|
||||
List<ItemParam> leftoverOreList = new ArrayList<>(3);
|
||||
|
||||
if (leftover < WEAPON_ORE_EXP_1) {
|
||||
@ -401,11 +363,11 @@ public class InventoryManager {
|
||||
}
|
||||
|
||||
// Get leftovers
|
||||
int ore3 = (int) Math.floor(leftover / WEAPON_ORE_EXP_3);
|
||||
int ore3 = leftover / WEAPON_ORE_EXP_3;
|
||||
leftover = leftover % WEAPON_ORE_EXP_3;
|
||||
int ore2 = (int) Math.floor(leftover / WEAPON_ORE_EXP_2);
|
||||
int ore2 = leftover / WEAPON_ORE_EXP_2;
|
||||
leftover = leftover % WEAPON_ORE_EXP_2;
|
||||
int ore1 = (int) Math.floor(leftover / WEAPON_ORE_EXP_1);
|
||||
int ore1 = leftover / WEAPON_ORE_EXP_1;
|
||||
|
||||
if (ore3 > 0) {
|
||||
leftoverOreList.add(ItemParam.newBuilder().setItemId(WEAPON_ORE_3).setCount(ore3).build());
|
||||
@ -496,26 +458,15 @@ public class InventoryManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure player has promote items
|
||||
for (ItemParamData cost : nextPromoteData.getCostItems()) {
|
||||
GameItem feedItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId());
|
||||
if (feedItem == null || feedItem.getCount() < cost.getCount()) {
|
||||
// Pay materials and mora if possible
|
||||
ItemParamData[] costs = nextPromoteData.getCostItems(); // Can this be null?
|
||||
if (nextPromoteData.getCoinCost() > 0) {
|
||||
costs = Arrays.copyOf(costs, costs.length + 1);
|
||||
costs[costs.length-1] = new ItemParamData(202, nextPromoteData.getCoinCost());
|
||||
}
|
||||
if (!player.getInventory().payItems(costs)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Mora check
|
||||
if (player.getMora() >= nextPromoteData.getCoinCost()) {
|
||||
player.setMora(player.getMora() - nextPromoteData.getCoinCost());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Consume promote filler items
|
||||
for (ItemParamData cost : nextPromoteData.getCostItems()) {
|
||||
GameItem feedItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId());
|
||||
player.getInventory().removeItem(feedItem, cost.getCount());
|
||||
}
|
||||
|
||||
int oldPromoteLevel = weapon.getPromoteLevel();
|
||||
weapon.setPromoteLevel(nextPromoteLevel);
|
||||
@ -552,26 +503,15 @@ public class InventoryManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure player has cost items
|
||||
for (ItemParamData cost : nextPromoteData.getCostItems()) {
|
||||
GameItem feedItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId());
|
||||
if (feedItem == null || feedItem.getCount() < cost.getCount()) {
|
||||
// Pay materials and mora if possible
|
||||
ItemParamData[] costs = nextPromoteData.getCostItems(); // Can this be null?
|
||||
if (nextPromoteData.getCoinCost() > 0) {
|
||||
costs = Arrays.copyOf(costs, costs.length + 1);
|
||||
costs[costs.length-1] = new ItemParamData(202, nextPromoteData.getCoinCost());
|
||||
}
|
||||
if (!player.getInventory().payItems(costs)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Mora check
|
||||
if (player.getMora() >= nextPromoteData.getCoinCost()) {
|
||||
player.setMora(player.getMora() - nextPromoteData.getCoinCost());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Consume promote filler items
|
||||
for (ItemParamData cost : nextPromoteData.getCostItems()) {
|
||||
GameItem feedItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId());
|
||||
player.getInventory().removeItem(feedItem, cost.getCount());
|
||||
}
|
||||
|
||||
// Update promote level
|
||||
avatar.setPromoteLevel(nextPromoteLevel);
|
||||
@ -616,34 +556,25 @@ public class InventoryManager {
|
||||
return;
|
||||
}
|
||||
|
||||
GameItem feedItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId);
|
||||
|
||||
if (feedItem == null || feedItem.getItemData().getMaterialType() != MaterialType.MATERIAL_EXP_FRUIT || feedItem.getCount() < count) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calc exp
|
||||
int expGain = 0, moraCost = 0;
|
||||
int expGain = switch(itemId) {
|
||||
case AVATAR_BOOK_1 -> AVATAR_BOOK_EXP_1 * count;
|
||||
case AVATAR_BOOK_2 -> AVATAR_BOOK_EXP_2 * count;
|
||||
case AVATAR_BOOK_3 -> AVATAR_BOOK_EXP_3 * count;
|
||||
default -> 0;
|
||||
};
|
||||
|
||||
// TODO clean up
|
||||
if (itemId == AVATAR_BOOK_3) {
|
||||
expGain = AVATAR_BOOK_EXP_3 * count;
|
||||
} else if (itemId == AVATAR_BOOK_2) {
|
||||
expGain = AVATAR_BOOK_EXP_2 * count;
|
||||
} else if (itemId == AVATAR_BOOK_1) {
|
||||
expGain = AVATAR_BOOK_EXP_1 * count;
|
||||
}
|
||||
moraCost = (int) Math.floor(expGain * .2f);
|
||||
|
||||
// Mora check
|
||||
if (player.getMora() >= moraCost) {
|
||||
player.setMora(player.getMora() - moraCost);
|
||||
} else {
|
||||
// Sanity check
|
||||
if (expGain <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Consume items
|
||||
player.getInventory().removeItem(feedItem, count);
|
||||
// Payment check
|
||||
int moraCost = expGain / 5;
|
||||
ItemParamData[] costItems = new ItemParamData[] {new ItemParamData(itemId, count), new ItemParamData(202, moraCost)};
|
||||
if (!player.getInventory().payItems(costItems)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Level up
|
||||
upgradeAvatar(player, avatar, promoteData, expGain);
|
||||
@ -764,32 +695,14 @@ public class InventoryManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure player has cost items
|
||||
for (ItemParamData cost : proudSkill.getCostItems()) {
|
||||
if (cost.getId() == 0) {
|
||||
continue;
|
||||
// Pay materials and mora if possible
|
||||
List<ItemParamData> costs = new ArrayList<ItemParamData>(proudSkill.getCostItems()); // Can this be null?
|
||||
if (proudSkill.getCoinCost() > 0) {
|
||||
costs.add(new ItemParamData(202, proudSkill.getCoinCost()));
|
||||
}
|
||||
GameItem feedItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId());
|
||||
if (feedItem == null || feedItem.getCount() < cost.getCount()) {
|
||||
if (!player.getInventory().payItems(costs.toArray(new ItemParamData[0]))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Mora check
|
||||
if (player.getMora() >= proudSkill.getCoinCost()) {
|
||||
player.setMora(player.getMora() - proudSkill.getCoinCost());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Consume promote filler items
|
||||
for (ItemParamData cost : proudSkill.getCostItems()) {
|
||||
if (cost.getId() == 0) {
|
||||
continue;
|
||||
}
|
||||
GameItem feedItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId());
|
||||
player.getInventory().removeItem(feedItem, cost.getCount());
|
||||
}
|
||||
|
||||
// Upgrade skill
|
||||
avatar.getSkillLevelMap().put(skillId, nextLevel);
|
||||
@ -822,14 +735,11 @@ public class InventoryManager {
|
||||
return;
|
||||
}
|
||||
|
||||
GameItem costItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(talentData.getMainCostItemId());
|
||||
if (costItem == null || costItem.getCount() < talentData.getMainCostItemCount()) {
|
||||
// Pay constellation item if possible
|
||||
if (!player.getInventory().payItem(talentData.getMainCostItemId(), 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Consume item
|
||||
player.getInventory().removeItem(costItem, talentData.getMainCostItemCount());
|
||||
|
||||
// Apply + recalc
|
||||
avatar.getTalentIdList().add(talentData.getId());
|
||||
avatar.setCoreProudSkillLevel(currentTalentLevel + 1);
|
||||
|
@ -8,5 +8,5 @@ public interface AfterUpdateStaminaListener {
|
||||
* @param reason Why updating stamina.
|
||||
* @param newStamina New Stamina value.
|
||||
*/
|
||||
void onAfterUpdateStamina(String reason, int newStamina);
|
||||
void onAfterUpdateStamina(String reason, int newStamina, boolean isCharacterStamina);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ public interface BeforeUpdateStaminaListener {
|
||||
* @param newStamina New ABSOLUTE stamina value.
|
||||
* @return true if you want to cancel this update, otherwise false.
|
||||
*/
|
||||
int onBeforeUpdateStamina(String reason, int newStamina);
|
||||
int onBeforeUpdateStamina(String reason, int newStamina, boolean isCharacterStamina);
|
||||
/**
|
||||
* onBeforeUpdateStamina() will be called before StaminaManager attempt to update the player's current stamina.
|
||||
* This gives listeners a chance to intercept this update.
|
||||
@ -16,5 +16,5 @@ public interface BeforeUpdateStaminaListener {
|
||||
* @param consumption ConsumptionType and RELATIVE stamina change amount.
|
||||
* @return true if you want to cancel this update, otherwise false.
|
||||
*/
|
||||
Consumption onBeforeUpdateStamina(String reason, Consumption consumption);
|
||||
Consumption onBeforeUpdateStamina(String reason, Consumption consumption, boolean isCharacterStamina);
|
||||
}
|
@ -13,18 +13,19 @@ public enum ConsumptionType {
|
||||
// Slow swimming is handled per movement, not per second.
|
||||
// Arm movement frequency depends on gender/age/height.
|
||||
// TODO: Instead of cost -80 per tick, find a proper way to calculate cost.
|
||||
SKIFF(-300), // TODO: Get real value
|
||||
SKIFF_DASH(-204),
|
||||
SPRINT(-1800),
|
||||
SWIM_DASH_START(-20),
|
||||
SWIM_DASH_START(-2000),
|
||||
SWIM_DASH(-204), // -10.2 per second, 5Hz = -204 each tick
|
||||
SWIMMING(-80),
|
||||
TALENT_DASH(-300), // -1500 per second, 5Hz = -300 each tick
|
||||
TALENT_DASH_START(-1000),
|
||||
|
||||
// restore
|
||||
POWERED_FLY(500), // TODO: Get real value
|
||||
POWERED_SKIFF(2000), // TODO: Get real value
|
||||
POWERED_FLY(500),
|
||||
POWERED_SKIFF(500),
|
||||
RUN(500),
|
||||
SKIFF(500),
|
||||
STANDBY(500),
|
||||
WALK(500);
|
||||
|
||||
|
@ -2,6 +2,7 @@ package emu.grasscutter.game.managers.StaminaManager;
|
||||
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
@ -13,21 +14,21 @@ import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
|
||||
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
||||
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
||||
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
||||
import emu.grasscutter.net.proto.VehicleInteractTypeOuterClass.VehicleInteractType;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.Math;
|
||||
import java.util.*;
|
||||
|
||||
import static emu.grasscutter.Configuration.*;
|
||||
import static emu.grasscutter.Configuration.GAME_OPTIONS;
|
||||
|
||||
public class StaminaManager {
|
||||
|
||||
// TODO: Skiff state detection?
|
||||
private final Player player;
|
||||
private final HashMap<String, HashSet<MotionState>> MotionStatesCategorized = new HashMap<>() {{
|
||||
private static final HashMap<String, HashSet<MotionState>> MotionStatesCategorized = new HashMap<>() {{
|
||||
put("CLIMB", new HashSet<>(List.of(
|
||||
MotionState.MOTION_CLIMB, // sustained, when not moving no cost no recover
|
||||
MotionState.MOTION_STANDBY_TO_CLIMB // NOT OBSERVED, see MOTION_JUMP_UP_WALL_FOR_STANDBY
|
||||
@ -48,7 +49,7 @@ public class StaminaManager {
|
||||
)));
|
||||
put("SKIFF", new HashSet<>(List.of(
|
||||
MotionState.MOTION_SKIFF_BOARDING, // NOT OBSERVED even when boarding
|
||||
MotionState.MOTION_SKIFF_DASH, // NOT OBSERVED even when dashing
|
||||
MotionState.MOTION_SKIFF_DASH, // sustained, observed with waverider entity ID.
|
||||
MotionState.MOTION_SKIFF_NORMAL, // sustained, OBSERVED when both normal and dashing
|
||||
MotionState.MOTION_SKIFF_POWERED_DASH // sustained, recover
|
||||
)));
|
||||
@ -108,7 +109,8 @@ public class StaminaManager {
|
||||
}};
|
||||
|
||||
private final Logger logger = Grasscutter.getLogger();
|
||||
public final static int GlobalMaximumStamina = 24000;
|
||||
public final static int GlobalCharacterMaximumStamina = 24000;
|
||||
public final static int GlobalVehicleMaxStamina = 24000;
|
||||
private Position currentCoordinates = new Position(0, 0, 0);
|
||||
private Position previousCoordinates = new Position(0, 0, 0);
|
||||
private MotionState currentState = MotionState.MOTION_STANDBY;
|
||||
@ -122,74 +124,58 @@ public class StaminaManager {
|
||||
private int lastSkillId = 0;
|
||||
private int lastSkillCasterId = 0;
|
||||
private boolean lastSkillFirstTick = true;
|
||||
public static final HashSet<Integer> TalentMovements = new HashSet<>(List.of(
|
||||
10013, // Kamisato Ayaka
|
||||
10413 // Mona
|
||||
private int vehicleId = -1;
|
||||
private int vehicleStamina = GlobalVehicleMaxStamina;
|
||||
private static final HashSet<Integer> TalentMovements = new HashSet<>(List.of(
|
||||
10013, 10413
|
||||
));
|
||||
private static final HashMap<Integer, Float> ClimbFoodReductionMap = new HashMap<>() {{
|
||||
// TODO: get real food id
|
||||
put(0, 0.8f); // Sample food
|
||||
}};
|
||||
private static final HashMap<Integer, Float> DashFoodReductionMap = new HashMap<>() {{
|
||||
// TODO: get real food id
|
||||
put(0, 0.8f); // Sample food
|
||||
}};
|
||||
private static final HashMap<Integer, Float> FlyFoodReductionMap = new HashMap<>() {{
|
||||
// TODO: get real food id
|
||||
put(0, 0.8f); // Sample food
|
||||
}};
|
||||
private static final HashMap<Integer, Float> SwimFoodReductionMap = new HashMap<>() {{
|
||||
// TODO: get real food id
|
||||
put(0, 0.8f); // Sample food
|
||||
}};
|
||||
private static final HashMap<Integer, Float> ClimbTalentReductionMap = new HashMap<>() {{
|
||||
put(262301, 0.8f);
|
||||
}};
|
||||
private static final HashMap<Integer, Float> FlyTalentReductionMap = new HashMap<>() {{
|
||||
put(212301, 0.8f);
|
||||
put(222301, 0.8f);
|
||||
}};
|
||||
private static final HashMap<Integer, Float> SwimTalentReductionMap = new HashMap<>() {{
|
||||
put(242301, 0.8f);
|
||||
put(542301, 0.8f);
|
||||
}};
|
||||
|
||||
// TODO: Get from somewhere else, instead of hard-coded here?
|
||||
public static final HashSet<Integer> ClaymoreSkills = new HashSet<>(List.of(
|
||||
10160, // Diluc, /=2
|
||||
10201, // Razor
|
||||
10241, // Beidou
|
||||
10341, // Noelle
|
||||
10401, // Chongyun
|
||||
10441, // Xinyan
|
||||
10511, // Eula
|
||||
10531, // Sayu
|
||||
10571 // Arataki Itto, = 0
|
||||
));
|
||||
public static final HashSet<Integer> CatalystSkills = new HashSet<>(List.of(
|
||||
10060, // Lisa
|
||||
10070, // Barbara
|
||||
10271, // Ningguang
|
||||
10291, // Klee
|
||||
10411, // Mona
|
||||
10431, // Sucrose
|
||||
10481, // Yanfei
|
||||
10541, // Sangonomoiya Kokomi
|
||||
10581 // Yae Miko
|
||||
));
|
||||
public static final HashSet<Integer> PolearmSkills = new HashSet<>(List.of(
|
||||
10231, // Xiangling
|
||||
10261, // Xiao
|
||||
10301, // Zhongli
|
||||
10451, // Rosaria
|
||||
10461, // Hu Tao
|
||||
10501, // Thoma
|
||||
10521, // Raiden Shogun
|
||||
10631, // Shenhe
|
||||
10641 // Yunjin
|
||||
));
|
||||
public static final HashSet<Integer> SwordSkills = new HashSet<>(List.of(
|
||||
10024, // Kamisato Ayaka
|
||||
10031, // Jean
|
||||
10073, // Kaeya
|
||||
10321, // Bennett
|
||||
10337, // Tartaglia, melee stance (10332 switch to melee, 10336 switch to ranged stance)
|
||||
10351, // Qiqi
|
||||
10381, // Xingqiu
|
||||
10386, // Albedo
|
||||
10421, // Keqing, =-2500
|
||||
10471, // Kaedehara Kazuha
|
||||
10661, // Kamisato Ayato
|
||||
100553, // Lumine
|
||||
100540 // Aether
|
||||
));
|
||||
public static final HashSet<Integer> BowSkills = new HashSet<>(List.of(
|
||||
10041, 10043, // Amber
|
||||
10221, 10223,// Venti
|
||||
10311, 10315, // Fischl
|
||||
10331, 10335, // Tartaglia, ranged stance
|
||||
10371, // Ganyu
|
||||
10391, 10394, // Diona
|
||||
10491, // Yoimiya
|
||||
10551, 10554, // Gorou
|
||||
10561, 10564, // Kojou Sara
|
||||
10621, // Aloy
|
||||
99998, 99999 // Yelan // TODO: get real values
|
||||
));
|
||||
public static final HashSet<Integer> BowAvatars = new HashSet<>();
|
||||
public static final HashSet<Integer> CatalystAvatars = new HashSet<>();
|
||||
public static final HashSet<Integer> ClaymoreAvatars = new HashSet<>();
|
||||
public static final HashSet<Integer> PolearmAvatars = new HashSet<>();
|
||||
public static final HashSet<Integer> SwordAvatars = new HashSet<>();
|
||||
|
||||
public static void initialize() {
|
||||
// Initialize skill categories
|
||||
GameData.getAvatarDataMap().forEach((avatarId, avatarData) -> {
|
||||
switch (avatarData.getWeaponType()) {
|
||||
case "WEAPON_BOW" -> BowAvatars.add(avatarId);
|
||||
case "WEAPON_CLAYMORE" -> ClaymoreAvatars.add(avatarId);
|
||||
case "WEAPON_CATALYST" -> CatalystAvatars.add(avatarId);
|
||||
case "WEAPON_POLE" -> PolearmAvatars.add(avatarId);
|
||||
case "WEAPON_SWORD_ONE_HAND" -> SwordAvatars.add(avatarId);
|
||||
}
|
||||
});
|
||||
// TODO: Initialize foods etc.
|
||||
}
|
||||
|
||||
public StaminaManager(Player player) {
|
||||
this.player = player;
|
||||
@ -203,6 +189,22 @@ public class StaminaManager {
|
||||
lastSkillCasterId = skillCasterId;
|
||||
}
|
||||
|
||||
public int getMaxCharacterStamina() {
|
||||
return player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
||||
}
|
||||
|
||||
public int getCurrentCharacterStamina() {
|
||||
return player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||
}
|
||||
|
||||
public int getMaxVehicleStamina() {
|
||||
return GlobalVehicleMaxStamina;
|
||||
}
|
||||
|
||||
public int getCurrentVehicleStamina() {
|
||||
return vehicleStamina;
|
||||
}
|
||||
|
||||
public boolean registerBeforeUpdateStaminaListener(String listenerName, BeforeUpdateStaminaListener listener) {
|
||||
if (beforeUpdateStaminaListeners.containsKey(listenerName)) {
|
||||
return false;
|
||||
@ -244,67 +246,71 @@ public class StaminaManager {
|
||||
return Math.abs(diffX) > 0.3 || Math.abs(diffY) > 0.2 || Math.abs(diffZ) > 0.3;
|
||||
}
|
||||
|
||||
public int updateStaminaRelative(GameSession session, Consumption consumption) {
|
||||
int currentStamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||
public int updateStaminaRelative(GameSession session, Consumption consumption, boolean isCharacterStamina) {
|
||||
int currentStamina = isCharacterStamina ? getCurrentCharacterStamina() : getCurrentVehicleStamina();
|
||||
if (consumption.amount == 0) {
|
||||
return currentStamina;
|
||||
}
|
||||
// notify will update
|
||||
for (Map.Entry<String, BeforeUpdateStaminaListener> listener : beforeUpdateStaminaListeners.entrySet()) {
|
||||
Consumption overriddenConsumption = listener.getValue().onBeforeUpdateStamina(consumption.type.toString(), consumption);
|
||||
Consumption overriddenConsumption = listener.getValue().onBeforeUpdateStamina(consumption.type.toString(), consumption, isCharacterStamina);
|
||||
if ((overriddenConsumption.type != consumption.type) && (overriddenConsumption.amount != consumption.amount)) {
|
||||
logger.debug("[StaminaManager] Stamina update relative(" +
|
||||
logger.debug("Stamina update relative(" +
|
||||
consumption.type.toString() + ", " + consumption.amount + ") overridden to relative(" +
|
||||
consumption.type.toString() + ", " + consumption.amount + ") by: " + listener.getKey());
|
||||
return currentStamina;
|
||||
}
|
||||
}
|
||||
int playerMaxStamina = player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
||||
logger.trace(currentStamina + "/" + playerMaxStamina + "\t" + currentState + "\t" +
|
||||
int maxStamina = isCharacterStamina ? getMaxCharacterStamina() : getMaxVehicleStamina();
|
||||
logger.trace((isCharacterStamina ? "C " : "V ") + currentStamina + "/" + maxStamina + "\t" + currentState + "\t" +
|
||||
(isPlayerMoving() ? "moving" : " ") + "\t(" + consumption.type + "," +
|
||||
consumption.amount + ")");
|
||||
int newStamina = currentStamina + consumption.amount;
|
||||
if (newStamina < 0) {
|
||||
newStamina = 0;
|
||||
} else if (newStamina > playerMaxStamina) {
|
||||
newStamina = playerMaxStamina;
|
||||
} else if (newStamina > maxStamina) {
|
||||
newStamina = maxStamina;
|
||||
}
|
||||
return setStamina(session, consumption.type.toString(), newStamina);
|
||||
return setStamina(session, consumption.type.toString(), newStamina, isCharacterStamina);
|
||||
}
|
||||
|
||||
public int updateStaminaAbsolute(GameSession session, String reason, int newStamina) {
|
||||
int currentStamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||
public int updateStaminaAbsolute(GameSession session, String reason, int newStamina, boolean isCharacterStamina) {
|
||||
int currentStamina = isCharacterStamina ? getCurrentCharacterStamina() : getCurrentVehicleStamina();
|
||||
// notify will update
|
||||
for (Map.Entry<String, BeforeUpdateStaminaListener> listener : beforeUpdateStaminaListeners.entrySet()) {
|
||||
int overriddenNewStamina = listener.getValue().onBeforeUpdateStamina(reason, newStamina);
|
||||
int overriddenNewStamina = listener.getValue().onBeforeUpdateStamina(reason, newStamina, isCharacterStamina);
|
||||
if (overriddenNewStamina != newStamina) {
|
||||
logger.debug("[StaminaManager] Stamina update absolute(" +
|
||||
logger.debug("Stamina update absolute(" +
|
||||
reason + ", " + newStamina + ") overridden to absolute(" +
|
||||
reason + ", " + newStamina + ") by: " + listener.getKey());
|
||||
return currentStamina;
|
||||
}
|
||||
}
|
||||
int playerMaxStamina = player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
||||
int maxStamina = isCharacterStamina ? getMaxCharacterStamina() : getMaxVehicleStamina();
|
||||
if (newStamina < 0) {
|
||||
newStamina = 0;
|
||||
} else if (newStamina > playerMaxStamina) {
|
||||
newStamina = playerMaxStamina;
|
||||
} else if (newStamina > maxStamina) {
|
||||
newStamina = maxStamina;
|
||||
}
|
||||
return setStamina(session, reason, newStamina);
|
||||
return setStamina(session, reason, newStamina, isCharacterStamina);
|
||||
}
|
||||
|
||||
// Returns new stamina and sends PlayerPropNotify
|
||||
public int setStamina(GameSession session, String reason, int newStamina) {
|
||||
// Returns new stamina and sends PlayerPropNotify or VehicleStaminaNotify
|
||||
public int setStamina(GameSession session, String reason, int newStamina, boolean isCharacterStamina) {
|
||||
if (!GAME_OPTIONS.staminaUsage) {
|
||||
newStamina = player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
||||
newStamina = getMaxCharacterStamina();
|
||||
}
|
||||
|
||||
// set stamina
|
||||
// set stamina if is character stamina
|
||||
if (isCharacterStamina) {
|
||||
player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
|
||||
session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA));
|
||||
} else {
|
||||
vehicleStamina = newStamina;
|
||||
session.send(new PacketVehicleStaminaNotify(vehicleId, ((float) newStamina) / 100));
|
||||
}
|
||||
// notify updated
|
||||
for (Map.Entry<String, AfterUpdateStaminaListener> listener : afterUpdateStaminaListeners.entrySet()) {
|
||||
listener.getValue().onAfterUpdateStamina(reason, newStamina);
|
||||
listener.getValue().onAfterUpdateStamina(reason, newStamina, isCharacterStamina);
|
||||
}
|
||||
return newStamina;
|
||||
}
|
||||
@ -343,22 +349,23 @@ public class StaminaManager {
|
||||
// External trigger handler
|
||||
|
||||
public void handleEvtDoSkillSuccNotify(GameSession session, int skillId, int casterId) {
|
||||
// Ignore if skill not cast by not current active
|
||||
// Ignore if skill not cast by not current active avatar
|
||||
if (casterId != player.getTeamManager().getCurrentAvatarEntity().getId()) {
|
||||
return;
|
||||
}
|
||||
setSkillCast(skillId, casterId);
|
||||
// Handle immediate stamina cost
|
||||
if (ClaymoreSkills.contains(skillId)) {
|
||||
int currentAvatarId = player.getTeamManager().getCurrentAvatarEntity().getAvatar().getAvatarId();
|
||||
if (ClaymoreAvatars.contains(currentAvatarId)) {
|
||||
// Exclude claymore as their stamina cost starts when MixinStaminaCost gets in
|
||||
return;
|
||||
}
|
||||
// TODO: Differentiate normal attacks from charged attacks and exclude
|
||||
// TODO: Temporary: Exclude non-claymore attacks for now
|
||||
if (BowSkills.contains(skillId)
|
||||
|| SwordSkills.contains(skillId)
|
||||
|| PolearmSkills.contains(skillId)
|
||||
|| CatalystSkills.contains(skillId)
|
||||
if (BowAvatars.contains(currentAvatarId)
|
||||
|| SwordAvatars.contains(currentAvatarId)
|
||||
|| PolearmAvatars.contains(currentAvatarId)
|
||||
|| CatalystAvatars.contains(currentAvatarId)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@ -367,7 +374,7 @@ public class StaminaManager {
|
||||
|
||||
public void handleMixinCostStamina(boolean isSwim) {
|
||||
// Talent moving and claymore avatar charged attack duration
|
||||
// logger.trace("abilityMixinCostStamina: isSwim: " + isSwim);
|
||||
// logger.trace("abilityMixinCostStamina: isSwim: " + isSwim + "\tlastSkill: " + lastSkillId);
|
||||
if (lastSkillCasterId == player.getTeamManager().getCurrentAvatarEntity().getId()) {
|
||||
handleImmediateStamina(cachedSession, lastSkillId);
|
||||
}
|
||||
@ -381,11 +388,11 @@ public class StaminaManager {
|
||||
MotionState motionState = motionInfo.getState();
|
||||
int notifyEntityId = entity.getId();
|
||||
int currentAvatarEntityId = session.getPlayer().getTeamManager().getCurrentAvatarEntity().getId();
|
||||
if (notifyEntityId != currentAvatarEntityId) {
|
||||
if (notifyEntityId != currentAvatarEntityId && notifyEntityId != vehicleId) {
|
||||
return;
|
||||
}
|
||||
currentState = motionState;
|
||||
// logger.trace("" + currentState);
|
||||
// logger.trace(currentState + "\t" + (notifyEntityId == currentAvatarEntityId ? "character" : "vehicle"));
|
||||
Vector posVector = motionInfo.getPos();
|
||||
Position newPos = new Position(posVector.getX(), posVector.getY(), posVector.getZ());
|
||||
if (newPos.getX() != 0 && newPos.getY() != 0 && newPos.getZ() != 0) {
|
||||
@ -395,28 +402,40 @@ public class StaminaManager {
|
||||
handleImmediateStamina(session, motionState);
|
||||
}
|
||||
|
||||
public void handleVehicleInteractReq(GameSession session, int vehicleId, VehicleInteractType vehicleInteractType) {
|
||||
if (vehicleInteractType == VehicleInteractType.VEHICLE_INTERACT_IN) {
|
||||
this.vehicleId = vehicleId;
|
||||
// Reset character stamina here to prevent falling into water immediately on ejection if char stamina is
|
||||
// close to empty when boarding.
|
||||
updateStaminaAbsolute(session, "board vehicle", getMaxCharacterStamina(), true);
|
||||
updateStaminaAbsolute(session, "board vehicle", getMaxVehicleStamina(), false);
|
||||
} else {
|
||||
this.vehicleId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Internal handler
|
||||
|
||||
private void handleImmediateStamina(GameSession session, @NotNull MotionState motionState) {
|
||||
switch (motionState) {
|
||||
case MOTION_CLIMB:
|
||||
if (currentState != MotionState.MOTION_CLIMB) {
|
||||
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_START));
|
||||
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_START), true);
|
||||
}
|
||||
break;
|
||||
case MOTION_DASH_BEFORE_SHAKE:
|
||||
if (previousState != MotionState.MOTION_DASH_BEFORE_SHAKE) {
|
||||
updateStaminaRelative(session, new Consumption(ConsumptionType.SPRINT));
|
||||
updateStaminaRelative(session, new Consumption(ConsumptionType.SPRINT), true);
|
||||
}
|
||||
break;
|
||||
case MOTION_CLIMB_JUMP:
|
||||
if (previousState != MotionState.MOTION_CLIMB_JUMP) {
|
||||
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_JUMP));
|
||||
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_JUMP), true);
|
||||
}
|
||||
break;
|
||||
case MOTION_SWIM_DASH:
|
||||
if (previousState != MotionState.MOTION_SWIM_DASH) {
|
||||
updateStaminaRelative(session, new Consumption(ConsumptionType.SWIM_DASH_START));
|
||||
updateStaminaRelative(session, new Consumption(ConsumptionType.SWIM_DASH_START), true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -424,18 +443,20 @@ public class StaminaManager {
|
||||
|
||||
private void handleImmediateStamina(GameSession session, int skillId) {
|
||||
Consumption consumption = getFightConsumption(skillId);
|
||||
updateStaminaRelative(session, consumption);
|
||||
updateStaminaRelative(session, consumption, true);
|
||||
}
|
||||
|
||||
private class SustainedStaminaHandler extends TimerTask {
|
||||
public void run() {
|
||||
boolean moving = isPlayerMoving();
|
||||
int currentStamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||
int maxStamina = player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
||||
if (moving || (currentStamina < maxStamina)) {
|
||||
int currentCharacterStamina = getCurrentCharacterStamina();
|
||||
int maxCharacterStamina = getMaxCharacterStamina();
|
||||
int currentVehicleStamina = getCurrentVehicleStamina();
|
||||
int maxVehicleStamina = getMaxVehicleStamina();
|
||||
if (moving || (currentCharacterStamina < maxCharacterStamina) || (currentVehicleStamina < maxVehicleStamina)) {
|
||||
logger.trace("Player moving: " + moving + ", stamina full: " +
|
||||
(currentStamina >= maxStamina) + ", recalculate stamina");
|
||||
|
||||
(currentCharacterStamina >= maxCharacterStamina) + ", recalculate stamina");
|
||||
boolean isCharacterStamina = true;
|
||||
Consumption consumption;
|
||||
if (MotionStatesCategorized.get("CLIMB").contains(currentState)) {
|
||||
consumption = getClimbConsumption();
|
||||
@ -447,43 +468,44 @@ public class StaminaManager {
|
||||
consumption = new Consumption(ConsumptionType.RUN);
|
||||
} else if (MotionStatesCategorized.get("SKIFF").contains(currentState)) {
|
||||
consumption = getSkiffConsumption();
|
||||
isCharacterStamina = false;
|
||||
} else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) {
|
||||
consumption = new Consumption(ConsumptionType.STANDBY);
|
||||
} else if (MotionStatesCategorized.get("SWIM").contains((currentState))) {
|
||||
} else if (MotionStatesCategorized.get("SWIM").contains(currentState)) {
|
||||
consumption = getSwimConsumptions();
|
||||
} else if (MotionStatesCategorized.get("WALK").contains((currentState))) {
|
||||
} else if (MotionStatesCategorized.get("WALK").contains(currentState)) {
|
||||
consumption = new Consumption(ConsumptionType.WALK);
|
||||
} else if (MotionStatesCategorized.get("OTHER").contains((currentState))) {
|
||||
} else if (MotionStatesCategorized.get("NOCOST_NORECOVER").contains(currentState)) {
|
||||
consumption = new Consumption();
|
||||
} else if (MotionStatesCategorized.get("OTHER").contains(currentState)) {
|
||||
consumption = getOtherConsumptions();
|
||||
} else {
|
||||
// ignore
|
||||
} else { // ignore
|
||||
return;
|
||||
}
|
||||
if (consumption.amount < 0) {
|
||||
/* Do not apply reduction factor when recovering stamina
|
||||
TODO: Reductions that apply to all motion types:
|
||||
Elemental Resonance
|
||||
Wind: -15%
|
||||
Skills
|
||||
Diona E: -10% while shield lasts - applies to SP+MP
|
||||
Barbara E: -12% while lasts - applies to SP+MP
|
||||
*/
|
||||
|
||||
if (consumption.amount < 0 && isCharacterStamina) {
|
||||
// Do not apply reduction factor when recovering stamina
|
||||
if (player.getTeamManager().getTeamResonances().contains(10301)) {
|
||||
consumption.amount *= 0.85f;
|
||||
}
|
||||
// Delay 2 seconds before starts recovering stamina
|
||||
if (cachedSession != null) {
|
||||
}
|
||||
// Delay 1 seconds before starts recovering stamina
|
||||
if (consumption.amount != 0 && cachedSession != null) {
|
||||
if (consumption.amount < 0) {
|
||||
staminaRecoverDelay = 0;
|
||||
}
|
||||
if (consumption.amount > 0 && consumption.type != ConsumptionType.POWERED_FLY) {
|
||||
// For POWERED_FLY recover immediately - things like Amber's gliding exam may require this.
|
||||
if (staminaRecoverDelay < 10) {
|
||||
// For others recover after 2 seconds (10 ticks) - as official server does.
|
||||
if (consumption.amount > 0
|
||||
&& consumption.type != ConsumptionType.POWERED_FLY
|
||||
&& consumption.type != ConsumptionType.POWERED_SKIFF) {
|
||||
// For POWERED_* recover immediately - things like Amber's gliding exam and skiff challenges may require this.
|
||||
if (staminaRecoverDelay < 5) {
|
||||
// For others recover after 1 seconds (5 ticks) - as official server does.
|
||||
staminaRecoverDelay++;
|
||||
consumption.amount = 0;
|
||||
logger.trace("[StaminaManager] Delaying recovery: " + staminaRecoverDelay);
|
||||
logger.trace("Delaying recovery: " + staminaRecoverDelay);
|
||||
}
|
||||
}
|
||||
updateStaminaRelative(cachedSession, consumption);
|
||||
updateStaminaRelative(cachedSession, consumption, isCharacterStamina);
|
||||
}
|
||||
}
|
||||
previousState = currentState;
|
||||
@ -496,10 +518,11 @@ public class StaminaManager {
|
||||
}
|
||||
|
||||
private void handleDrowning() {
|
||||
int stamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||
// TODO: fix drowning waverider entity
|
||||
int stamina = getCurrentCharacterStamina();
|
||||
if (stamina < 10) {
|
||||
logger.trace(player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + "/" +
|
||||
player.getProperty(PlayerProperty.PROP_MAX_STAMINA) + "\t" + currentState);
|
||||
logger.trace(getCurrentCharacterStamina() + "/" +
|
||||
getMaxCharacterStamina() + "\t" + currentState);
|
||||
if (currentState != MotionState.MOTION_SWIM_IDLE) {
|
||||
killAvatar(cachedSession, cachedEntity, PlayerDieType.PLAYER_DIE_DRAWN);
|
||||
}
|
||||
@ -517,24 +540,25 @@ public class StaminaManager {
|
||||
return getTalentMovingSustainedCost(skillCasting);
|
||||
}
|
||||
// Bow avatar charged attack
|
||||
if (BowSkills.contains(skillCasting)) {
|
||||
int currentAvatarId = player.getTeamManager().getCurrentAvatarEntity().getAvatar().getAvatarId();
|
||||
if (BowAvatars.contains(currentAvatarId)) {
|
||||
return getBowSustainedCost(skillCasting);
|
||||
}
|
||||
// Claymore avatar charged attack
|
||||
if (ClaymoreSkills.contains(skillCasting)) {
|
||||
if (ClaymoreAvatars.contains(currentAvatarId)) {
|
||||
return getClaymoreSustainedCost(skillCasting);
|
||||
}
|
||||
// Catalyst avatar charged attack
|
||||
if (CatalystSkills.contains(skillCasting)) {
|
||||
return getCatalystSustainedCost(skillCasting);
|
||||
if (CatalystAvatars.contains(currentAvatarId)) {
|
||||
return getCatalystCost(skillCasting);
|
||||
}
|
||||
// Polearm avatar charged attack
|
||||
if (PolearmSkills.contains(skillCasting)) {
|
||||
return getPolearmSustainedCost(skillCasting);
|
||||
if (PolearmAvatars.contains(currentAvatarId)) {
|
||||
return getPolearmCost(skillCasting);
|
||||
}
|
||||
// Sword avatar charged attack
|
||||
if (SwordSkills.contains(skillCasting)) {
|
||||
return getSwordSustainedCost(skillCasting);
|
||||
if (SwordAvatars.contains(skillCasting)) {
|
||||
return getSwordCost(skillCasting);
|
||||
}
|
||||
return new Consumption();
|
||||
}
|
||||
@ -546,18 +570,8 @@ public class StaminaManager {
|
||||
consumption.amount = ConsumptionType.CLIMBING.amount;
|
||||
}
|
||||
// Climbing specific reductions
|
||||
// TODO: create a food cost reduction map
|
||||
HashMap<Integer, Float> foodReductionMap = new HashMap<>() {{
|
||||
// TODO: get real talent id
|
||||
put(0, 0.8f); // Sample food
|
||||
}};
|
||||
consumption.amount *= getFoodCostReductionFactor(foodReductionMap);
|
||||
|
||||
HashMap<Integer, Float> talentReductionMap = new HashMap<>() {{
|
||||
// TODO: get real talent id
|
||||
put(0, 0.8f); // Xiao
|
||||
}};
|
||||
consumption.amount *= getTalentCostReductionFactor(talentReductionMap);
|
||||
consumption.amount *= getFoodCostReductionFactor(ClimbFoodReductionMap);
|
||||
consumption.amount *= getTalentCostReductionFactor(ClimbTalentReductionMap);
|
||||
return consumption;
|
||||
}
|
||||
|
||||
@ -572,13 +586,9 @@ public class StaminaManager {
|
||||
consumption.type = ConsumptionType.SWIM_DASH;
|
||||
consumption.amount = ConsumptionType.SWIM_DASH.amount;
|
||||
}
|
||||
// Reductions
|
||||
HashMap<Integer, Float> talentReductionMap = new HashMap<>() {{
|
||||
// TODO: get real talent id
|
||||
put(0, 0.8f); // Beidou
|
||||
put(1, 0.8f); // Sangonomiya Kokomi
|
||||
}};
|
||||
consumption.amount *= getTalentCostReductionFactor(talentReductionMap);
|
||||
// Swimming specific reductions
|
||||
consumption.amount *= getFoodCostReductionFactor(SwimFoodReductionMap);
|
||||
consumption.amount *= getTalentCostReductionFactor(SwimTalentReductionMap);
|
||||
return consumption;
|
||||
}
|
||||
|
||||
@ -587,8 +597,8 @@ public class StaminaManager {
|
||||
if (currentState == MotionState.MOTION_DASH) {
|
||||
consumption.type = ConsumptionType.DASH;
|
||||
consumption.amount = ConsumptionType.DASH.amount;
|
||||
// TODO: Dashing specific reductions
|
||||
// Foods:
|
||||
// Dashing specific reductions
|
||||
consumption.amount *= getFoodCostReductionFactor(DashFoodReductionMap);
|
||||
}
|
||||
return consumption;
|
||||
}
|
||||
@ -599,32 +609,34 @@ public class StaminaManager {
|
||||
return new Consumption(ConsumptionType.POWERED_FLY);
|
||||
}
|
||||
Consumption consumption = new Consumption(ConsumptionType.FLY);
|
||||
// Passive Talents
|
||||
HashMap<Integer, Float> talentReductionMap = new HashMap<>() {{
|
||||
put(212301, 0.8f); // Amber
|
||||
put(222301, 0.8f); // Venti
|
||||
}};
|
||||
consumption.amount *= getTalentCostReductionFactor(talentReductionMap);
|
||||
// TODO: Foods
|
||||
// Flying specific reductions
|
||||
consumption.amount *= getFoodCostReductionFactor(FlyFoodReductionMap);
|
||||
consumption.amount *= getTalentCostReductionFactor(FlyTalentReductionMap);
|
||||
return consumption;
|
||||
}
|
||||
|
||||
private Consumption getSkiffConsumption() {
|
||||
// POWERED_SKIFF, e.g. wind tunnel
|
||||
if (currentState == MotionState.MOTION_SKIFF_POWERED_DASH) {
|
||||
return new Consumption(ConsumptionType.POWERED_SKIFF);
|
||||
}
|
||||
// No known reduction for skiffing.
|
||||
return new Consumption(ConsumptionType.SKIFF);
|
||||
return switch (currentState) {
|
||||
case MOTION_SKIFF_DASH -> new Consumption(ConsumptionType.SKIFF_DASH);
|
||||
case MOTION_SKIFF_POWERED_DASH -> new Consumption(ConsumptionType.POWERED_SKIFF);
|
||||
case MOTION_SKIFF_NORMAL -> new Consumption(ConsumptionType.SKIFF);
|
||||
default -> new Consumption();
|
||||
};
|
||||
}
|
||||
|
||||
private Consumption getOtherConsumptions() {
|
||||
if (currentState == MotionState.MOTION_NOTIFY) {
|
||||
if (BowSkills.contains(lastSkillId)) {
|
||||
switch (currentState) {
|
||||
case MOTION_NOTIFY:
|
||||
// if (BowSkills.contains(lastSkillId)) {
|
||||
// return new Consumption(ConsumptionType.FIGHT, 500);
|
||||
// }
|
||||
break;
|
||||
case MOTION_FIGHT:
|
||||
// TODO: what if charged attack
|
||||
return new Consumption(ConsumptionType.FIGHT, 500);
|
||||
}
|
||||
}
|
||||
// TODO: Add other logic
|
||||
|
||||
return new Consumption();
|
||||
}
|
||||
|
||||
@ -671,11 +683,11 @@ public class StaminaManager {
|
||||
return new Consumption(ConsumptionType.FIGHT, +500);
|
||||
}
|
||||
|
||||
private Consumption getCatalystSustainedCost(int skillId) {
|
||||
private Consumption getCatalystCost(int skillId) {
|
||||
Consumption consumption = new Consumption(ConsumptionType.FIGHT, -5000);
|
||||
// Character specific handling
|
||||
switch (skillId) {
|
||||
// TODO: Yanfei
|
||||
// TODO:
|
||||
}
|
||||
return consumption;
|
||||
}
|
||||
@ -684,18 +696,20 @@ public class StaminaManager {
|
||||
Consumption consumption = new Consumption(ConsumptionType.FIGHT, -1333); // 4000 / 3 = 1333
|
||||
// Character specific handling
|
||||
switch (skillId) {
|
||||
case 10571: // Arataki Itto, does not consume stamina at all.
|
||||
case 10571:
|
||||
case 10532:
|
||||
consumption.amount = 0;
|
||||
break;
|
||||
case 10160: // Diluc, with talent "Relentless" stamina cost is decreased by 50%
|
||||
// TODO: How to get talent status?
|
||||
case 10160:
|
||||
if (player.getTeamManager().getCurrentAvatarEntity().getAvatar().getProudSkillList().contains(162101)) {
|
||||
consumption.amount /= 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return consumption;
|
||||
}
|
||||
|
||||
private Consumption getPolearmSustainedCost(int skillId) {
|
||||
private Consumption getPolearmCost(int skillId) {
|
||||
Consumption consumption = new Consumption(ConsumptionType.FIGHT, -2500);
|
||||
// Character specific handling
|
||||
switch (skillId) {
|
||||
@ -704,11 +718,11 @@ public class StaminaManager {
|
||||
return consumption;
|
||||
}
|
||||
|
||||
private Consumption getSwordSustainedCost(int skillId) {
|
||||
private Consumption getSwordCost(int skillId) {
|
||||
Consumption consumption = new Consumption(ConsumptionType.FIGHT, -2000);
|
||||
// Character specific handling
|
||||
switch (skillId) {
|
||||
case 10421: // Keqing, -2500
|
||||
case 10421:
|
||||
consumption.amount = -2500;
|
||||
break;
|
||||
}
|
||||
|
@ -85,6 +85,8 @@ public class Player {
|
||||
private Set<Integer> flyCloakList;
|
||||
private Set<Integer> costumeList;
|
||||
|
||||
private Integer widgetId;
|
||||
|
||||
@Transient private long nextGuid = 0;
|
||||
@Transient private int peerId;
|
||||
@Transient private World world;
|
||||
@ -302,6 +304,14 @@ public class Player {
|
||||
this.updateProfile();
|
||||
}
|
||||
|
||||
public Integer getWidgetId() {
|
||||
return widgetId;
|
||||
}
|
||||
|
||||
public void setWidgetId(Integer widgetId) {
|
||||
this.widgetId = widgetId;
|
||||
}
|
||||
|
||||
public Position getPos() {
|
||||
return pos;
|
||||
}
|
||||
@ -1170,6 +1180,8 @@ public class Player {
|
||||
session.send(new PacketFinishedParentQuestNotify(this));
|
||||
session.send(new PacketQuestListNotify(this));
|
||||
session.send(new PacketServerCondMeetQuestListUpdateNotify(this));
|
||||
session.send(new PacketAllWidgetDataNotify(this));
|
||||
session.send(new PacketWidgetGadgetAllDataNotify());
|
||||
|
||||
getTodayMoonCard(); // The timer works at 0:0, some users log in after that, use this method to check if they have received a reward today or not. If not, send the reward.
|
||||
|
||||
@ -1267,7 +1279,7 @@ public class Player {
|
||||
} else if (prop == PlayerProperty.PROP_IS_TRANSFERABLE) { // 10009
|
||||
if (!(0 <= value && value <= 1)) { return false; }
|
||||
} else if (prop == PlayerProperty.PROP_MAX_STAMINA) { // 10010
|
||||
if (!(value >= 0 && value <= StaminaManager.GlobalMaximumStamina)) { return false; }
|
||||
if (!(value >= 0 && value <= StaminaManager.GlobalCharacterMaximumStamina)) { return false; }
|
||||
} else if (prop == PlayerProperty.PROP_CUR_PERSIST_STAMINA) { // 10011
|
||||
int playerMaximumStamina = getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
||||
if (!(value >= 0 && value <= playerMaximumStamina)) { return false; }
|
||||
|
@ -18,7 +18,7 @@ import emu.grasscutter.server.packet.send.PacketBuyGoodsRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketStoreItemChangeNotify;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@ -56,36 +56,13 @@ public class HandlerBuyGoodsReq extends PacketHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sg.getScoin() > 0 && session.getPlayer().getMora() < buyGoodsReq.getBoughtNum() * sg.getScoin()) {
|
||||
List<ItemParamData> costs = new ArrayList<ItemParamData>(sg.getCostItemList()); // Can this even be null?
|
||||
costs.add(new ItemParamData(202, sg.getScoin()));
|
||||
costs.add(new ItemParamData(201, sg.getHcoin()));
|
||||
costs.add(new ItemParamData(203, sg.getMcoin()));
|
||||
if (!session.getPlayer().getInventory().payItems(costs.toArray(new ItemParamData[0]), buyGoodsReq.getBoughtNum())) {
|
||||
return;
|
||||
}
|
||||
if (sg.getHcoin() > 0 && session.getPlayer().getPrimogems() < buyGoodsReq.getBoughtNum() * sg.getHcoin()) {
|
||||
return;
|
||||
}
|
||||
if (sg.getMcoin() > 0 && session.getPlayer().getCrystals() < buyGoodsReq.getBoughtNum() * sg.getMcoin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
HashMap<GameItem, Integer> itemsCache = new HashMap<>();
|
||||
if (sg.getCostItemList() != null) {
|
||||
for (ItemParamData p : sg.getCostItemList()) {
|
||||
Optional<GameItem> invItem = session.getPlayer().getInventory().getItems().values().stream().filter(x -> x.getItemId() == p.getId()).findFirst();
|
||||
if (invItem.isEmpty() || invItem.get().getCount() < p.getCount())
|
||||
return;
|
||||
itemsCache.put(invItem.get(), p.getCount() * buyGoodsReq.getBoughtNum());
|
||||
}
|
||||
}
|
||||
|
||||
session.getPlayer().setMora(session.getPlayer().getMora() - buyGoodsReq.getBoughtNum() * sg.getScoin());
|
||||
session.getPlayer().setPrimogems(session.getPlayer().getPrimogems() - buyGoodsReq.getBoughtNum() * sg.getHcoin());
|
||||
session.getPlayer().setCrystals(session.getPlayer().getCrystals() - buyGoodsReq.getBoughtNum() * sg.getMcoin());
|
||||
|
||||
if (!itemsCache.isEmpty()) {
|
||||
for (GameItem gi : itemsCache.keySet()) {
|
||||
session.getPlayer().getInventory().removeItem(gi, itemsCache.get(gi));
|
||||
}
|
||||
itemsCache.clear();
|
||||
}
|
||||
|
||||
session.getPlayer().addShopLimit(sg.getGoodsId(), buyGoodsReq.getBoughtNum(), ShopManager.getShopNextRefreshTime(sg));
|
||||
GameItem item = new GameItem(GameData.getItemDataMap().get(sg.getGoodsItem().getId()));
|
||||
|
@ -1,16 +1,20 @@
|
||||
package emu.grasscutter.server.packet.recv;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.net.packet.Opcodes;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.packet.PacketHandler;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import emu.grasscutter.server.packet.send.PacketGetShopRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketGetWidgetSlotRsp;
|
||||
|
||||
@Opcodes(PacketOpcodes.GetWidgetSlotReq)
|
||||
public class HandlerGetWidgetSlotReq extends PacketHandler {
|
||||
|
||||
@Override
|
||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||
// Unhandled
|
||||
Player player = session.getPlayer();
|
||||
session.send(new PacketGetWidgetSlotRsp(player));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
package emu.grasscutter.server.packet.recv;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.net.packet.Opcodes;
|
||||
import emu.grasscutter.net.packet.PacketHandler;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.proto.SetWidgetSlotReqOuterClass;
|
||||
import emu.grasscutter.net.proto.WidgetSlotOpOuterClass;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import emu.grasscutter.server.packet.send.PacketSetWidgetSlotRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketWidgetSlotChangeNotify;
|
||||
|
||||
@Opcodes(PacketOpcodes.SetWidgetSlotReq)
|
||||
public class HandlerSetWidgetSlotReq extends PacketHandler {
|
||||
|
||||
@Override
|
||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||
SetWidgetSlotReqOuterClass.SetWidgetSlotReq req = SetWidgetSlotReqOuterClass.SetWidgetSlotReq.parseFrom(payload);
|
||||
|
||||
Player player = session.getPlayer();
|
||||
player.setWidgetId(req.getMaterialId());
|
||||
|
||||
// WidgetSlotChangeNotify op & slot key
|
||||
session.send(new PacketWidgetSlotChangeNotify(WidgetSlotOpOuterClass.WidgetSlotOp.DETACH));
|
||||
// WidgetSlotChangeNotify slot
|
||||
session.send(new PacketWidgetSlotChangeNotify(req.getMaterialId()));
|
||||
|
||||
// SetWidgetSlotRsp
|
||||
session.send(new PacketSetWidgetSlotRsp(req.getMaterialId()));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ public class HandlerVehicleInteractReq extends PacketHandler {
|
||||
@Override
|
||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||
VehicleInteractReqOuterClass.VehicleInteractReq req = VehicleInteractReqOuterClass.VehicleInteractReq.parseFrom(payload);
|
||||
session.getPlayer().getStaminaManager().handleVehicleInteractReq(session, req.getEntityId(), req.getInteractType());
|
||||
session.send(new PacketVehicleInteractRsp(session.getPlayer(), req.getEntityId(), req.getInteractType()));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.proto.AllWidgetDataNotifyOuterClass.AllWidgetDataNotify;
|
||||
import emu.grasscutter.net.proto.LunchBoxDataOuterClass;
|
||||
import emu.grasscutter.net.proto.WidgetSlotDataOuterClass;
|
||||
import emu.grasscutter.net.proto.WidgetSlotTagOuterClass;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PacketAllWidgetDataNotify extends BasePacket {
|
||||
|
||||
public PacketAllWidgetDataNotify(Player player) {
|
||||
super(PacketOpcodes.AllWidgetDataNotify);
|
||||
|
||||
// TODO: Implement this
|
||||
|
||||
AllWidgetDataNotify.Builder proto = AllWidgetDataNotify.newBuilder()
|
||||
// If you want to implement this, feel free to do so. :)
|
||||
.setLunchBoxData(
|
||||
LunchBoxDataOuterClass.LunchBoxData.newBuilder().build()
|
||||
)
|
||||
// Maybe it's a little difficult, or it makes you upset :(
|
||||
.addAllOneoffGatherPointDetectorDataList(List.of())
|
||||
// So, goodbye, and hopefully sometime in the future o(* ̄▽ ̄*)ブ
|
||||
.addAllCoolDownGroupDataList(List.of())
|
||||
// I'll see your PR with a title that says (・∀・(・∀・(・∀・*)
|
||||
.addAllAnchorPointList(List.of())
|
||||
// "Complete implementation of widget functionality" b( ̄▽ ̄)d
|
||||
.addAllClientCollectorDataList(List.of())
|
||||
// Good luck, my boy.
|
||||
.addAllNormalCoolDownDataList(List.of());
|
||||
|
||||
if (player.getWidgetId() == null) {
|
||||
proto.addAllSlotList(List.of());
|
||||
} else {
|
||||
proto.addSlotList(
|
||||
WidgetSlotDataOuterClass.WidgetSlotData.newBuilder()
|
||||
.setIsActive(true)
|
||||
.setMaterialId(player.getWidgetId())
|
||||
.build()
|
||||
);
|
||||
|
||||
proto.addSlotList(
|
||||
WidgetSlotDataOuterClass.WidgetSlotData.newBuilder()
|
||||
.setTag(WidgetSlotTagOuterClass.WidgetSlotTag.WIDGET_SLOT_ATTACH_AVATAR)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
AllWidgetDataNotify protoData = proto.build();
|
||||
|
||||
this.setData(protoData);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.proto.GetWidgetSlotRspOuterClass;
|
||||
import emu.grasscutter.net.proto.WidgetSlotDataOuterClass;
|
||||
import emu.grasscutter.net.proto.WidgetSlotTagOuterClass;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PacketGetWidgetSlotRsp extends BasePacket {
|
||||
|
||||
public PacketGetWidgetSlotRsp(Player player) {
|
||||
super(PacketOpcodes.GetWidgetSlotRsp);
|
||||
|
||||
GetWidgetSlotRspOuterClass.GetWidgetSlotRsp.Builder proto =
|
||||
GetWidgetSlotRspOuterClass.GetWidgetSlotRsp.newBuilder();
|
||||
|
||||
if (player.getWidgetId() == null) {
|
||||
proto.addAllSlotList(List.of());
|
||||
} else {
|
||||
proto.addSlotList(
|
||||
WidgetSlotDataOuterClass.WidgetSlotData.newBuilder()
|
||||
.setIsActive(true)
|
||||
.setMaterialId(player.getWidgetId())
|
||||
.build()
|
||||
);
|
||||
|
||||
proto.addSlotList(
|
||||
WidgetSlotDataOuterClass.WidgetSlotData.newBuilder()
|
||||
.setTag(WidgetSlotTagOuterClass.WidgetSlotTag.WIDGET_SLOT_ATTACH_AVATAR)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
GetWidgetSlotRspOuterClass.GetWidgetSlotRsp protoData = proto.build();
|
||||
|
||||
this.setData(protoData);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.proto.SetWidgetSlotRspOuterClass;
|
||||
|
||||
public class PacketSetWidgetSlotRsp extends BasePacket {
|
||||
|
||||
public PacketSetWidgetSlotRsp(int materialId) {
|
||||
super(PacketOpcodes.SetWidgetSlotRsp);
|
||||
|
||||
SetWidgetSlotRspOuterClass.SetWidgetSlotRsp proto = SetWidgetSlotRspOuterClass.SetWidgetSlotRsp.newBuilder()
|
||||
.setMaterialId(materialId)
|
||||
.build();
|
||||
|
||||
this.setData(proto);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.proto.VehicleStaminaNotifyOuterClass.VehicleStaminaNotify;
|
||||
|
||||
public class PacketVehicleStaminaNotify extends BasePacket {
|
||||
|
||||
public PacketVehicleStaminaNotify(int vehicleId, float newStamina) {
|
||||
super(PacketOpcodes.VehicleStaminaNotify);
|
||||
VehicleStaminaNotify.Builder proto = VehicleStaminaNotify.newBuilder();
|
||||
|
||||
proto.setEntityId(vehicleId);
|
||||
proto.setCurStamina(newStamina);
|
||||
|
||||
this.setData(proto.build());
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.proto.WidgetGadgetAllDataNotifyOuterClass.WidgetGadgetAllDataNotify;
|
||||
|
||||
public class PacketWidgetGadgetAllDataNotify extends BasePacket {
|
||||
|
||||
public PacketWidgetGadgetAllDataNotify() {
|
||||
super(PacketOpcodes.AllWidgetDataNotify);
|
||||
|
||||
WidgetGadgetAllDataNotify proto = WidgetGadgetAllDataNotify.newBuilder().build();
|
||||
|
||||
this.setData(proto);
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.net.proto.WidgetSlotChangeNotifyOuterClass;
|
||||
import emu.grasscutter.net.proto.WidgetSlotDataOuterClass;
|
||||
import emu.grasscutter.net.proto.WidgetSlotOpOuterClass;
|
||||
|
||||
public class PacketWidgetSlotChangeNotify extends BasePacket {
|
||||
|
||||
public PacketWidgetSlotChangeNotify(WidgetSlotChangeNotifyOuterClass.WidgetSlotChangeNotify proto) {
|
||||
super(PacketOpcodes.WidgetSlotChangeNotify);
|
||||
|
||||
this.setData(proto);
|
||||
}
|
||||
|
||||
public PacketWidgetSlotChangeNotify(WidgetSlotOpOuterClass.WidgetSlotOp op) {
|
||||
super(PacketOpcodes.WidgetSlotChangeNotify);
|
||||
|
||||
WidgetSlotChangeNotifyOuterClass.WidgetSlotChangeNotify proto = WidgetSlotChangeNotifyOuterClass.WidgetSlotChangeNotify.newBuilder()
|
||||
.setOp(op)
|
||||
.setSlot(
|
||||
WidgetSlotDataOuterClass.WidgetSlotData.newBuilder()
|
||||
.setIsActive(true)
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
this.setData(proto);
|
||||
}
|
||||
|
||||
public PacketWidgetSlotChangeNotify(int materialId) {
|
||||
super(PacketOpcodes.WidgetSlotChangeNotify);
|
||||
|
||||
WidgetSlotChangeNotifyOuterClass.WidgetSlotChangeNotify proto = WidgetSlotChangeNotifyOuterClass.WidgetSlotChangeNotify.newBuilder()
|
||||
.setSlot(
|
||||
WidgetSlotDataOuterClass.WidgetSlotData.newBuilder()
|
||||
.setIsActive(true)
|
||||
.setMaterialId(materialId)
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
this.setData(proto);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user