Compare commits

...

40 Commits

Author SHA1 Message Date
诗音澄鸢 镜苑芳依
af6c27a002
Fix daily dungeon didn't display in note(笔记) (#1296)
* Fix Daily dungeon didn't display in note(笔记) -- Buggy

* 123
2022-06-17 23:50:55 -07:00
Melledy
45438cd2a7 2.7 Merge 2022-06-17 23:36:16 -07:00
Melledy
4501db1135 Merge branch 'dev-world-scripts' of https://github.com/Grasscutters/Grasscutter into development 2022-06-17 23:35:45 -07:00
Melledy
9daf89b953 Move ban check to GetPlayerTokenReq 2022-06-17 19:42:42 -07:00
Yazawazi
50f0a38e21 no more bin 2022-06-17 19:42:42 -07:00
Yazawazi
51df48ee14 2nd null check 2022-06-17 19:42:42 -07:00
Yazawazi
1ebd8f6810 feat(ban): Implementing ban.
Rough code, not based on permission.
2022-06-17 19:42:42 -07:00
Akka
1b9e39dcab a little fix 2022-06-17 18:15:49 -07:00
Akka
89ba8f5d01 a little fix 2022-06-17 18:15:49 -07:00
Akka
148395de67 fix the rot of furniture 2022-06-17 18:15:49 -07:00
Akka
d35ff068cf implement furniture make system 2022-06-17 18:15:49 -07:00
Akka
a695d0c33e npc/animals arrangement & support enter room scene 2022-06-17 18:15:49 -07:00
Akka
1c0d869ee5 Implement the Home System (Serenitea Pot) 2022-06-17 18:15:49 -07:00
Akka
5d0610b6f2 Fixed excessive memory usage of Spatial Index 2022-05-25 00:42:52 -07:00
Akka
e1770b5a68 Support spawn NPC 2022-05-24 21:43:51 -07:00
Akka
862bfa0611 remove unused imports 2022-05-23 15:46:17 -07:00
Akka
57260415b0 refactor the challenge 2022-05-23 15:46:17 -07:00
Akka
233b46b2f9 Support Boss Chest 2022-05-19 23:36:13 -07:00
Melledy
db8cbd4f83 Add error message in case data files in resources could not be found 2022-05-19 03:05:57 -07:00
Melledy
f876470369 Fix dataloader not getting path correctly 2022-05-19 03:00:59 -07:00
Melledy
29941aa8d0 Add area_id to SceneObject 2022-05-19 02:28:46 -07:00
Melledy
5f8cc47e87 Fix bad casting exceptions with scene garbages objects 2022-05-19 02:28:25 -07:00
Melledy
3feb98f08b Fix issue with groups that dont have any suites 2022-05-19 02:19:18 -07:00
Melledy
4ef3080c62 Move gadget/monster creation events to after they are spawned in scene 2022-05-19 00:36:38 -07:00
Melledy
89454726ac Fix dataloader with eclipse 2022-05-19 00:10:02 -07:00
Akka
8faf8decec optimized the Lua func binding so that the script will not eval again 2022-05-18 20:51:39 -07:00
Melledy
1fef837a97 Move chest rewards to new data format 2022-05-18 15:54:55 -07:00
Melledy
3902a5d744 Merge branch 'development' into dev-world-scripts 2022-05-18 15:54:35 -07:00
Melledy
2e24d77bc2 Cleanup and remove business_type filter 2022-05-18 05:33:00 -07:00
Melledy
9902ba381a Only load groups that have a business type of 0 2022-05-18 05:10:45 -07:00
Melledy
f53dda0335 Fix errors caused by merge + Refactor chests into GadgetChest 2022-05-18 02:36:50 -07:00
Melledy
73a88b2da2 Merge branch 'dev-world-scripts' of https://github.com/Grasscutters/Grasscutter into dev-world-scripts 2022-05-18 02:24:00 -07:00
Melledy
17a273387e Implement local specialty spawning 2022-05-18 02:21:34 -07:00
Akka
7c02e6c912 Support Open Chest 2022-05-18 01:07:06 -07:00
Melledy
e43946e44c Fixed issue with scene groups after merge 2022-05-16 22:09:10 -07:00
Melledy
3f27874765 Merge branch 'development' into dev-world-scripts 2022-05-16 22:08:28 -07:00
Akka
c103841a03 optimized the lua serializer 2022-05-16 21:30:04 -07:00
TangHuLuTaiTian
a63ed21213 Some clients Code 4206 Error 2022-05-16 17:49:46 -07:00
Akka
035b0ff8bd fix the dynamic group loading 2022-05-15 12:40:36 -07:00
Akka
8a7077dc04
Enable script in big world (#884)
* add docs for tower

* fix: LEAK: ByteBuf.release() was not called

* enableScriptInBigWorld

* not print log when loaded scripts from cache

* revert the change of server tick

* revert the change of server tick

* fix

* optimize the performance: lazy load & cache

* fix the refresh group

* fix NPE

Co-authored-by: Melledy <52122272+Melledy@users.noreply.github.com>
2022-05-15 04:19:24 -07:00
190 changed files with 5437 additions and 884 deletions

View File

@ -76,7 +76,7 @@ dependencies {
implementation group: 'org.reflections', name: 'reflections', version: '0.10.2' implementation group: 'org.reflections', name: 'reflections', version: '0.10.2'
implementation group: 'dev.morphia.morphia', name: 'morphia-core', version: '2.2.6' implementation group: 'dev.morphia.morphia', name: 'morphia-core', version: '2.2.7'
implementation group: 'org.greenrobot', name: 'eventbus-java', version: '3.3.1' implementation group: 'org.greenrobot', name: 'eventbus-java', version: '3.3.1'
implementation group: 'org.danilopianini', name: 'java-quadtree', version: '0.1.9' implementation group: 'org.danilopianini', name: 'java-quadtree', version: '0.1.9'
@ -86,6 +86,9 @@ dependencies {
implementation group: 'org.luaj', name: 'luaj-jse', version: '3.0.1' implementation group: 'org.luaj', name: 'luaj-jse', version: '3.0.1'
implementation group: 'com.esotericsoftware', name : 'reflectasm', version: '1.11.9'
implementation group: 'com.github.davidmoten', name : 'rtree-multi', version: '0.1'
protobuf files('proto/') protobuf files('proto/')
compileOnly 'org.projectlombok:lombok:1.18.24' compileOnly 'org.projectlombok:lombok:1.18.24'

View File

@ -0,0 +1,12 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "DungeonEntryInfo.proto";
message DailyDungeonEntryInfo {
uint32 dungeon_entry_id = 1;
uint32 dungeon_entry_config_id = 2;
uint32 recommend_dungeon_id = 3;
DungeonEntryInfo recommend_dungeon_entry_info = 4;
}

View File

@ -0,0 +1,12 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Uint32Pair.proto";
// CmdId: 4681
// EnetChannelId: 0
// EnetIsReliable: true
message FurnitureCurModuleArrangeCountNotify {
repeated Uint32Pair furniture_arrange_count_list = 9;
}

View File

@ -0,0 +1,13 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "ProfilePicture.proto";
message FurnitureMakeBeHelpedData {
string player_name = 1;
uint32 time = 2;
uint32 uid = 3;
uint32 icon = 4;
ProfilePicture profile_picture = 5;
}

View File

@ -0,0 +1,12 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message FurnitureMakeData {
uint32 index = 1;
uint32 make_id = 2;
uint32 begin_time = 3;
uint32 dur_time = 4;
uint32 accelerate_time = 5;
uint32 avatar_id = 6;
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message FurnitureMakeHelpData {
uint32 uid = 1;
uint32 times = 2;
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message FurnitureMakeMakeInfo {
uint32 furniture_id = 1;
uint32 make_count = 2;
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4551
// EnetChannelId: 0
// EnetIsReliable: true
// IsAllowClient: true
message FurnitureMakeReq {
}

View File

@ -0,0 +1,19 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "FurnitureMakeBeHelpedData.proto";
import "FurnitureMakeHelpData.proto";
import "FurnitureMakeMakeInfo.proto";
import "FurnitureMakeSlot.proto";
// CmdId: 4530
// EnetChannelId: 0
// EnetIsReliable: true
message FurnitureMakeRsp {
int32 retcode = 6;
FurnitureMakeSlot furniture_make_slot = 10;
repeated FurnitureMakeHelpData help_data_list = 13;
repeated FurnitureMakeBeHelpedData helped_data_list = 12;
repeated FurnitureMakeMakeInfo make_info_list = 11;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "FurnitureMakeData.proto";
message FurnitureMakeSlot {
repeated FurnitureMakeData furniture_make_data_list = 1;
}

View File

@ -0,0 +1,12 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4582
// EnetChannelId: 0
// EnetIsReliable: true
// IsAllowClient: true
message FurnitureMakeStartReq {
uint32 make_id = 1;
uint32 avatar_id = 14;
}

View File

@ -0,0 +1,13 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "FurnitureMakeSlot.proto";
// CmdId: 4463
// EnetChannelId: 0
// EnetIsReliable: true
message FurnitureMakeStartRsp {
int32 retcode = 8;
FurnitureMakeSlot furniture_make_slot = 10;
}

View File

@ -0,0 +1,11 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 929
// EnetChannelId: 0
// EnetIsReliable: true
// IsAllowClient: true
message GetDailyDungeonEntryInfoReq {
uint32 scene_id = 11;
}

View File

@ -0,0 +1,13 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "DailyDungeonEntryInfo.proto";
// CmdId: 925
// EnetChannelId: 0
// EnetIsReliable: true
message GetDailyDungeonEntryInfoRsp {
int32 retcode = 9;
repeated DailyDungeonEntryInfo daily_dungeon_info_list = 4;
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4603
// EnetChannelId: 0
// EnetIsReliable: true
// IsAllowClient: true
message GetFurnitureCurModuleArrangeCountReq {
}

View File

@ -0,0 +1,16 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message GetInvestigationMonsterReq {
enum CmdId {
option allow_alias = true;
ENET_CHANNEL_ID = 0;
NONE = 0;
ENET_IS_RELIABLE = 1;
IS_ALLOW_CLIENT = 1;
CMD_ID = 1916;
}
repeated uint32 city_id_list = 1;
}

View File

@ -0,0 +1,19 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "InvestigationMonster.proto";
message GetInvestigationMonsterRsp {
enum CmdId {
option allow_alias = true;
ENET_CHANNEL_ID = 0;
NONE = 0;
ENET_IS_RELIABLE = 1;
IS_ALLOW_CLIENT = 1;
CMD_ID = 1928;
}
int32 retcode = 1;
repeated InvestigationMonster monster_list = 2;
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4845
// EnetChannelId: 0
// EnetIsReliable: true
// IsAllowClient: true
message GetPlayerHomeCompInfoReq {
}

View File

@ -0,0 +1,15 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message GroupSuiteNotify {
enum CmdId {
option allow_alias = true;
NONE = 0;
ENET_CHANNEL_ID = 0;
ENET_IS_RELIABLE = 1;
CMD_ID = 3098;
}
map<uint32, uint32> group_map = 1;
}

View File

@ -0,0 +1,11 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Vector.proto";
message HomeAnimalData {
uint32 furniture_id = 1;
Vector spawn_pos = 2;
Vector spawn_rot = 3;
}

16
proto/HomeBasicInfo.proto Normal file
View File

@ -0,0 +1,16 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "HomeLimitedShopInfo.proto";
message HomeBasicInfo {
uint32 cur_module_id = 1;
uint32 cur_room_scene_id = 2;
bool is_in_edit_mode = 3;
uint64 exp = 4;
uint32 level = 5;
uint32 home_owner_uid = 6;
HomeLimitedShopInfo limited_shop_info = 7;
string owner_nick_name = 8;
}

View File

@ -0,0 +1,12 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "HomeBasicInfo.proto";
// CmdId: 4872
// EnetChannelId: 0
// EnetIsReliable: true
message HomeBasicInfoNotify {
HomeBasicInfo basic_info = 9;
}

View File

@ -0,0 +1,29 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
//import "BIEMCDLIFOD.proto";
//import "GOHMLAFNBGF.proto";
import "HomeAnimalData.proto";
import "HomeBlockDotPattern.proto";
import "HomeBlockFieldData.proto";
import "HomeFurnitureData.proto";
import "HomeFurnitureSuiteData.proto";
import "HomeNpcData.proto";
//import "WeekendDjinnInfo.proto";
message HomeBlockArrangementInfo {
uint32 block_id = 1;
repeated HomeFurnitureData persistent_furniture_list = 2;
repeated HomeFurnitureData deploy_furniure_list = 3;
repeated HomeNpcData deploy_npc_list = 4;
repeated HomeFurnitureSuiteData furniture_suite_list = 5;
repeated HomeAnimalData deploy_animal_list = 6;
bool is_unlocked = 7;
uint32 comfort_value = 8;
//repeated WeekendDjinnInfo weekend_djinn_info_list = 9;
repeated HomeBlockDotPattern dot_pattern_list = 10;
repeated HomeBlockFieldData field_list = 11;
// repeated GOHMLAFNBGF BOCBLHLEKNJ = 12;
// repeated BIEMCDLIFOD CONIAKDJHAN = 13;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message HomeBlockDotPattern {
uint32 height = 1;
uint32 width = 2;
bytes data = 3;
}

View File

@ -0,0 +1,14 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "HomeBlockSubFieldData.proto";
import "Vector.proto";
message HomeBlockFieldData {
uint32 guid = 1;
uint32 furniture_id = 2;
Vector pos = 3;
Vector rot = 4;
repeated HomeBlockSubFieldData sub_field_list = 5;
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4542
// EnetChannelId: 0
// EnetIsReliable: true
message HomeBlockNotify {
uint32 end_time = 7;
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Vector.proto";
message HomeBlockSubFieldData {
Vector pos = 1;
Vector rot = 2;
}

View File

@ -0,0 +1,11 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4625
// EnetChannelId: 0
// EnetIsReliable: true
// IsAllowClient: true
message HomeChangeEditModeReq {
bool is_enter_edit_mode = 5;
}

View File

@ -0,0 +1,11 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4885
// EnetChannelId: 0
// EnetIsReliable: true
message HomeChangeEditModeRsp {
int32 retcode = 11;
bool is_enter_edit_mode = 5;
}

View File

@ -0,0 +1,14 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Vector.proto";
message HomeFurnitureData {
uint32 furniture_id = 1;
Vector spawn_pos = 3;
Vector spawn_rot = 4;
int32 parent_furniture_index = 7;
uint32 guid = 8;
uint32 version = 9;
}

View File

@ -0,0 +1,13 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Vector.proto";
message HomeFurnitureSuiteData {
uint32 suite_id = 1;
Vector spawn_pos = 2;
repeated int32 included_furniture_index_list = 3;
uint32 guid = 5;
bool is_allow_summon = 6;
}

View File

@ -0,0 +1,11 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4848
// EnetChannelId: 0
// EnetIsReliable: true
// IsAllowClient: true
message HomeGetArrangementInfoReq {
repeated uint32 scene_id_list = 6;
}

View File

@ -0,0 +1,13 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "HomeSceneArrangementInfo.proto";
// CmdId: 4456
// EnetChannelId: 0
// EnetIsReliable: true
message HomeGetArrangementInfoRsp {
int32 retcode = 1;
repeated HomeSceneArrangementInfo scene_arrangement_info_list = 12;
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4535
// EnetChannelId: 0
// EnetIsReliable: true
// IsAllowClient: true
message HomeGetBasicInfoReq {
}

View File

@ -0,0 +1,14 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Vector.proto";
message HomeLimitedShopInfo {
uint32 uid = 1;
uint32 next_open_time = 3;
uint32 next_guest_open_time = 4;
uint32 next_close_time = 5;
Vector djinn_pos = 6;
Vector djinn_rot = 7;
}

View File

@ -0,0 +1,18 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "HomeMarkPointNPCData.proto";
import "HomeMarkPointSuiteData.proto";
import "Vector.proto";
message HomeMarkPointFurnitureData {
uint32 guid = 1;
uint32 furniture_id = 2;
uint32 furniture_type = 3;
Vector pos = 4;
oneof extra {
HomeMarkPointNPCData npc_data = 6;
HomeMarkPointSuiteData suite_data = 7;
}
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message HomeMarkPointNPCData {
uint32 avatar_id = 1;
uint32 costume_id = 2;
}

View File

@ -0,0 +1,12 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "HomeMarkPointSceneData.proto";
// CmdId: 4746
// EnetChannelId: 0
// EnetIsReliable: true
message HomeMarkPointNotify {
repeated HomeMarkPointSceneData mark_point_data_list = 13;
}

View File

@ -0,0 +1,13 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "HomeMarkPointFurnitureData.proto";
import "Vector.proto";
message HomeMarkPointSceneData {
uint32 module_id = 1;
uint32 scene_id = 2;
repeated HomeMarkPointFurnitureData furniture_list = 3;
Vector teapot_spirit_pos = 4;
}

View File

@ -0,0 +1,7 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message HomeMarkPointSuiteData {
uint32 suite_id = 1;
}

12
proto/HomeNpcData.proto Normal file
View File

@ -0,0 +1,12 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Vector.proto";
message HomeNpcData {
uint32 avatar_id = 1;
Vector spawn_pos = 2;
Vector spawn_rot = 3;
uint32 costume_id = 4;
}

View File

@ -0,0 +1,22 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "HomeBlockArrangementInfo.proto";
import "HomeFurnitureData.proto";
import "Vector.proto";
message HomeSceneArrangementInfo {
uint32 scene_id = 1;
repeated HomeBlockArrangementInfo block_arrangement_info_list = 2;
bool is_set_born_pos = 3;
Vector born_pos = 4;
Vector born_rot = 5;
repeated HomeFurnitureData door_list = 7;
repeated HomeFurnitureData stair_list = 8;
HomeFurnitureData main_house = 9;
uint32 comfort_value = 10;
Vector djinn_pos = 11;
uint32 tmp_version = 12;
uint32 CNLMNOEGKME = 13;
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4552
// EnetChannelId: 0
// EnetIsReliable: true
// IsAllowClient: true
message HomeSceneInitFinishReq {
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4592
// EnetChannelId: 0
// EnetIsReliable: true
message HomeSceneInitFinishRsp {
int32 retcode = 7;
}

View File

@ -0,0 +1,11 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4659
// EnetChannelId: 0
// EnetIsReliable: true
// IsAllowClient: true
message HomeSceneJumpReq {
bool is_enter_room_scene = 12;
}

View File

@ -0,0 +1,11 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4570
// EnetChannelId: 0
// EnetIsReliable: true
message HomeSceneJumpRsp {
int32 retcode = 10;
bool is_enter_room_scene = 8;
}

View File

@ -0,0 +1,7 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message HomeUnknown1Notify {
bool is_enter_edit_mode = 12;
}

View File

@ -0,0 +1,13 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "HomeSceneArrangementInfo.proto";
// CmdId: 4472
// EnetChannelId: 0
// EnetIsReliable: true
// IsAllowClient: true
message HomeUpdateArrangementInfoReq {
HomeSceneArrangementInfo scene_arrangement_info = 12;
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4822
// EnetChannelId: 0
// EnetIsReliable: true
message HomeUpdateArrangementInfoRsp {
int32 retcode = 1;
}

View File

@ -0,0 +1,31 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Vector.proto";
import "WeeklyBossResinDiscountInfo.proto";
message InvestigationMonster {
enum LockState {
LOCK_NONE = 0;
LOCK_QUEST = 1;
}
uint32 id = 1;
uint32 city_id = 2;
uint32 level = 3;
bool is_alive = 4;
uint32 next_refresh_time = 5;
uint32 refresh_interval = 6;
Vector pos = 7;
LockState lock_state = 8;
uint32 max_boss_chest_num = 9;
uint32 boss_chest_num = 10;
uint32 resin = 11;
bool is_area_locked = 12;
uint32 next_boss_chest_refresh_time = 13;
WeeklyBossResinDiscountInfo weekly_boss_resin_discount_info = 14;
uint32 scene_id = 15;
uint32 group_id = 16;
uint32 monster_id = 17;
}

View File

@ -0,0 +1,13 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4768
// EnetChannelId: 0
// EnetIsReliable: true
// IsAllowClient: true
message TakeFurnitureMakeReq {
uint32 index = 9;
uint32 make_id = 4;
bool is_fast_finish = 2;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "FurnitureMakeSlot.proto";
import "ItemParam.proto";
// CmdId: 4599
// EnetChannelId: 0
// EnetIsReliable: true
message TakeFurnitureMakeRsp {
int32 retcode = 9;
uint32 make_id = 2;
FurnitureMakeSlot furniture_make_slot = 15;
repeated ItemParam output_item_list = 10;
repeated ItemParam return_item_list = 5;
}

View File

@ -0,0 +1,11 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4680
// EnetChannelId: 0
// EnetIsReliable: true
message UnlockedFurnitureFormulaDataNotify {
bool is_all = 14;
repeated uint32 furniture_id_list = 7;
}

View File

@ -0,0 +1,11 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
// CmdId: 4717
// EnetChannelId: 0
// EnetIsReliable: true
message UnlockedFurnitureSuiteDataNotify {
bool is_all = 10;
repeated uint32 furniture_suite_id_list = 15;
}

View File

@ -0,0 +1,90 @@
package emu.grasscutter.command.commands;
import java.util.List;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.player.Player;
import static emu.grasscutter.utils.Language.translate;
@Command(
label = "ban",
usage = "ban <player> [time] [reason]",
description = "commands.ban.description",
targetRequirement = Command.TargetRequirement.NONE
)
public final class BanCommand implements CommandHandler {
private boolean banAccount(int uid, int time, String reason) {
Player player = Grasscutter.getGameServer().getPlayerByUid(uid, true);
if (player == null) {
return false;
}
Account account = player.getAccount();
if (account == null) {
account = DatabaseHelper.getAccountByPlayerId(uid);
if (account == null) {
return false;
}
}
account.setBanReason(reason);
account.setBanEndTime(time);
account.setBanStartTime((int) System.currentTimeMillis() / 1000);
account.setBanned(true);
account.save();
Player banUser = Grasscutter.getGameServer().getPlayerByUid(uid);
if (banUser != null) {
banUser.getSession().close();
}
return true;
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 1) {
CommandHandler.sendMessage(sender, translate(sender, "commands.ban.command_usage"));
return;
}
int uid = 0;
int time = 2051190000;
String reason = "Reason not specified.";
if (args.size() >= 1) {
try {
uid = Integer.parseInt(args.get(0));
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate(sender, "commands.ban.invalid_player_id"));
return;
}
}
if (args.size() >= 2) {
try {
time = Integer.parseInt(args.get(1));
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate(sender, "commands.ban.invalid_time"));
return;
}
}
if (args.size() >= 3) {
reason = args.get(2);
}
if (banAccount(uid, time, reason)) {
CommandHandler.sendMessage(sender, translate(sender, "commands.ban.success"));
} else {
CommandHandler.sendMessage(sender, translate(sender, "commands.ban.failure"));
}
}
}

View File

@ -0,0 +1,69 @@
package emu.grasscutter.command.commands;
import java.util.List;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.player.Player;
import static emu.grasscutter.utils.Language.translate;
@Command(
label = "unban",
usage = "unban <player>",
description = "commands.unban.description",
targetRequirement = Command.TargetRequirement.NONE
)
public final class UnBanCommand implements CommandHandler {
private boolean unBanAccount(int uid) {
Player player = Grasscutter.getGameServer().getPlayerByUid(uid, true);
if (player == null) {
return false;
}
Account account = player.getAccount();
if (account == null) {
account = DatabaseHelper.getAccountByPlayerId(uid);
if (account == null) {
return false;
}
}
account.setBanReason(null);
account.setBanEndTime(0);
account.setBanStartTime(0);
account.setBanned(false);
account.save();
return true;
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 1) {
CommandHandler.sendMessage(sender, translate(sender, "commands.unban.command_usage"));
return;
}
int uid = 0;
try {
uid = Integer.parseInt(args.get(0));
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate(sender, "commands.unban.invalid_player_id"));
return;
}
if (unBanAccount(uid)) {
CommandHandler.sendMessage(sender, translate(sender, "commands.unban.success"));
} else {
CommandHandler.sendMessage(sender, translate(sender, "commands.unban.failure"));
}
}
}

View File

@ -7,12 +7,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.binout.*;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import emu.grasscutter.data.binout.AbilityEmbryoEntry;
import emu.grasscutter.data.binout.AbilityModifierEntry;
import emu.grasscutter.data.binout.MainQuestData;
import emu.grasscutter.data.binout.OpenConfigEntry;
import emu.grasscutter.data.binout.ScenePointEntry;
import emu.grasscutter.data.excels.*; import emu.grasscutter.data.excels.*;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@ -28,7 +24,9 @@ public class GameData {
private static final Map<String, OpenConfigEntry> openConfigEntries = new HashMap<>(); private static final Map<String, OpenConfigEntry> openConfigEntries = new HashMap<>();
private static final Map<String, ScenePointEntry> scenePointEntries = new HashMap<>(); private static final Map<String, ScenePointEntry> scenePointEntries = new HashMap<>();
private static final Int2ObjectMap<MainQuestData> mainQuestData = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<MainQuestData> mainQuestData = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<HomeworldDefaultSaveData> homeworldDefaultSaveData = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<SceneNpcBornData> npcBornData = new Int2ObjectOpenHashMap<>();
// ExcelConfigs // ExcelConfigs
private static final Int2ObjectMap<PlayerLevelData> playerLevelDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<PlayerLevelData> playerLevelDataMap = new Int2ObjectOpenHashMap<>();
@ -80,15 +78,21 @@ public class GameData {
private static final Int2ObjectMap<WorldAreaData> worldAreaDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<WorldAreaData> worldAreaDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<WorldLevelData> worldLevelDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<WorldLevelData> worldLevelDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<DailyDungeonData> dailyDungeonDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<DailyDungeonData> dailyDungeonDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<DungeonEntryData> dungeonEntryDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<DungeonData> dungeonDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<DungeonData> dungeonDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<QuestData> questDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<QuestData> questDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<ShopGoodsData> shopGoodsDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<ShopGoodsData> shopGoodsDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<CombineData> combineDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<CombineData> combineDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<RewardPreviewData> rewardPreviewDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<RewardPreviewData> rewardPreviewDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<GatherData> gatherDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<TowerFloorData> towerFloorDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<TowerFloorData> towerFloorDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<TowerLevelData> towerLevelDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<TowerLevelData> towerLevelDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<TowerScheduleData> towerScheduleDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<TowerScheduleData> towerScheduleDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<ForgeData> forgeDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<ForgeData> forgeDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<HomeWorldLevelData> homeWorldLevelDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<FurnitureMakeConfigData> furnitureMakeConfigDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<InvestigationMonsterData> investigationMonsterDataMap = new Int2ObjectOpenHashMap<>();
// Cache // Cache
private static Map<Integer, List<Integer>> fetters = new HashMap<>(); private static Map<Integer, List<Integer>> fetters = new HashMap<>();
@ -141,6 +145,14 @@ public class GameData {
return mainQuestData; return mainQuestData;
} }
public static Int2ObjectMap<HomeworldDefaultSaveData> getHomeworldDefaultSaveData() {
return homeworldDefaultSaveData;
}
public static Int2ObjectMap<SceneNpcBornData> getSceneNpcBornData() {
return npcBornData;
}
public static Int2ObjectMap<AvatarData> getAvatarDataMap() { public static Int2ObjectMap<AvatarData> getAvatarDataMap() {
return avatarDataMap; return avatarDataMap;
} }
@ -336,6 +348,10 @@ public class GameData {
return dailyDungeonDataMap; return dailyDungeonDataMap;
} }
public static Int2ObjectMap<DungeonEntryData> getDungeonEntryDatatMap(){
return dungeonEntryDataMap;
}
public static Map<Integer, List<ShopGoodsData>> getShopGoodsDataEntries() { public static Map<Integer, List<ShopGoodsData>> getShopGoodsDataEntries() {
if (shopGoods.isEmpty()) { if (shopGoods.isEmpty()) {
shopGoodsDataMap.forEach((k, v) -> { shopGoodsDataMap.forEach((k, v) -> {
@ -363,9 +379,11 @@ public class GameData {
public static Int2ObjectMap<TowerFloorData> getTowerFloorDataMap(){ public static Int2ObjectMap<TowerFloorData> getTowerFloorDataMap(){
return towerFloorDataMap; return towerFloorDataMap;
} }
public static Int2ObjectMap<TowerLevelData> getTowerLevelDataMap(){ public static Int2ObjectMap<TowerLevelData> getTowerLevelDataMap(){
return towerLevelDataMap; return towerLevelDataMap;
} }
public static Int2ObjectMap<TowerScheduleData> getTowerScheduleDataMap(){ public static Int2ObjectMap<TowerScheduleData> getTowerScheduleDataMap(){
return towerScheduleDataMap; return towerScheduleDataMap;
} }
@ -377,4 +395,20 @@ public class GameData {
public static Int2ObjectMap<ForgeData> getForgeDataMap() { public static Int2ObjectMap<ForgeData> getForgeDataMap() {
return forgeDataMap; return forgeDataMap;
} }
public static Int2ObjectMap<HomeWorldLevelData> getHomeWorldLevelDataMap() {
return homeWorldLevelDataMap;
}
public static Int2ObjectMap<FurnitureMakeConfigData> getFurnitureMakeConfigDataMap() {
return furnitureMakeConfigDataMap;
}
public static Int2ObjectMap<GatherData> getGatherDataMap() {
return gatherDataMap;
}
public static Int2ObjectMap<InvestigationMonsterData> getInvestigationMonsterDataMap() {
return investigationMonsterDataMap;
}
} }

View File

@ -1,13 +1,18 @@
package emu.grasscutter.data; package emu.grasscutter.data;
import java.io.*; import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import com.google.gson.Gson; import com.google.gson.Gson;
import emu.grasscutter.data.binout.*;
import emu.grasscutter.scripts.SceneIndexManager;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import lombok.SneakyThrows;
import org.reflections.Reflections; import org.reflections.Reflections;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
@ -15,12 +20,6 @@ import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.binout.AbilityEmbryoEntry;
import emu.grasscutter.data.binout.AbilityModifier;
import emu.grasscutter.data.binout.AbilityModifierEntry;
import emu.grasscutter.data.binout.MainQuestData;
import emu.grasscutter.data.binout.OpenConfigEntry;
import emu.grasscutter.data.binout.ScenePointEntry;
import emu.grasscutter.data.binout.AbilityModifier.AbilityConfigData; import emu.grasscutter.data.binout.AbilityModifier.AbilityConfigData;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction; import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierActionType; import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierActionType;
@ -66,6 +65,9 @@ public class ResourceLoader {
loadQuests(); loadQuests();
// Load scene points - must be done AFTER resources are loaded // Load scene points - must be done AFTER resources are loaded
loadScenePoints(); loadScenePoints();
// Load default home layout
loadHomeworldDefaultSaveData();
loadNpcBornData();
} }
public static void loadResources() { public static void loadResources() {
@ -394,6 +396,47 @@ public class ResourceLoader {
Grasscutter.getLogger().info("Loaded " + GameData.getMainQuestDataMap().size() + " MainQuestDatas."); Grasscutter.getLogger().info("Loaded " + GameData.getMainQuestDataMap().size() + " MainQuestDatas.");
} }
@SneakyThrows
private static void loadHomeworldDefaultSaveData(){
var folder = Files.list(Path.of(RESOURCE("BinOutput/HomeworldDefaultSave"))).toList();
var pattern = Pattern.compile("scene(.*)_home_config.json");
for(var file : folder){
var matcher = pattern.matcher(file.getFileName().toString());
if(!matcher.find()){
continue;
}
var sceneId = matcher.group(1);
var data = Grasscutter.getGsonFactory().fromJson(Files.readString(file), HomeworldDefaultSaveData.class);
GameData.getHomeworldDefaultSaveData().put(Integer.parseInt(sceneId), data);
}
Grasscutter.getLogger().info("Loaded " + GameData.getHomeworldDefaultSaveData().size() + " HomeworldDefaultSaveDatas.");
}
@SneakyThrows
private static void loadNpcBornData(){
var folder = Files.list(Path.of(RESOURCE("BinOutput/Scene/SceneNpcBorn"))).toList();
for(var file : folder){
if(file.toFile().isDirectory()){
continue;
}
var data = Grasscutter.getGsonFactory().fromJson(Files.readString(file), SceneNpcBornData.class);
if(data.getBornPosList() == null || data.getBornPosList().size() == 0){
continue;
}
data.setIndex(SceneIndexManager.buildIndex(3, data.getBornPosList(), item -> item.getPos().toPoint()));
GameData.getSceneNpcBornData().put(data.getSceneId(), data);
}
Grasscutter.getLogger().info("Loaded " + GameData.getSceneNpcBornData().size() + " SceneNpcBornDatas.");
}
// BinOutput configs // BinOutput configs
public static class AvatarConfig { public static class AvatarConfig {

View File

@ -0,0 +1,52 @@
package emu.grasscutter.data.binout;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.utils.Position;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import java.util.List;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class HomeworldDefaultSaveData {
@SerializedName(value = "KFHBFNPDJBE", alternate = "PKACPHDGGEI")
List<HomeBlock> homeBlockLists;
@SerializedName(value = "IJNPADKGNKE", alternate = "MINCKHBNING")
Position bornPos;
@SerializedName("IPIIGEMFLHK")
Position bornRot;
@SerializedName("HHOLBNPIHEM")
Position djinPos;
@SerializedName("KNHCJKHCOAN")
HomeFurniture mainhouse;
@SerializedName("NIHOJFEKFPG")
List<HomeFurniture> doorLists;
@SerializedName("EPGELGEFJFK")
List<HomeFurniture> stairLists;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public static class HomeBlock{
@SerializedName(value = "FGIJCELCGFI", alternate = "PGDPDIDJEEL")
int blockId;
@SerializedName(value = "BEAPOFELABD", alternate = "MLIODLGDFHJ")
List<HomeFurniture> furnitures;
}
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public static class HomeFurniture{
@SerializedName(value = "ENHNGKJBJAB", alternate = "KMAAJJHPNBA")
int id;
@SerializedName(value = "NGIEEIOLPPO", alternate = "JFKAHNCPDME")
Position pos;
//@SerializedName(value = "HEOCEHKEBFM", alternate = "LKCKOOGFDBM")
Position rot;
}
}

View File

@ -0,0 +1,29 @@
package emu.grasscutter.data.binout;
import com.github.davidmoten.rtreemulti.RTree;
import com.github.davidmoten.rtreemulti.geometry.Geometry;
import emu.grasscutter.scripts.data.SceneGroup;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class SceneNpcBornData {
int sceneId;
List<SceneNpcBornEntry> bornPosList;
/**
* Spatial Index For NPC
*/
transient RTree<SceneNpcBornEntry, Geometry> index;
/**
* npc groups
*/
transient Map<Integer, SceneGroup> groups = new ConcurrentHashMap<>();
}

View File

@ -0,0 +1,32 @@
package emu.grasscutter.data.binout;
import emu.grasscutter.utils.Position;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import java.util.List;
import com.google.gson.annotations.SerializedName;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class SceneNpcBornEntry {
@SerializedName(value="id", alternate={"_id", "ID"})
int id;
@SerializedName(value="configId", alternate={"_configId"})
int configId;
@SerializedName(value="pos", alternate={"_pos"})
Position pos;
@SerializedName(value="rot", alternate={"_rot"})
Position rot;
@SerializedName(value="groupId", alternate={"_groupId"})
int groupId;
@SerializedName(value="suiteIdList", alternate={"_suiteIdList"})
List<Integer> suiteIdList;
}

View File

@ -0,0 +1,25 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Getter;
import lombok.Setter;
@ResourceType(name = "DungeonEntryExcelConfigData.json")
@Getter
@Setter
public class DungeonEntryData extends GameResource {
private int dungeonEntryId;
private int sceneId;
private int id;
@Override
public int getId() {
return this.id;
}
@Override
public void onLoad() {
}
}

View File

@ -0,0 +1,37 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemParamData;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
import java.util.List;
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
@ResourceType(name = {"FurnitureMakeExcelConfigData.json"})
public class FurnitureMakeConfigData extends GameResource {
int configID;
int furnitureItemID;
int count;
int exp;
List<ItemParamData> materialItems;
int makeTime;
int maxAccelerateTime;
int quickFetchMaterialNum;
@Override
public int getId() {
return configID;
}
@Override
public void onLoad() {
this.materialItems = materialItems.stream()
.filter(x -> x.getId() > 0)
.toList();
}
}

View File

@ -0,0 +1,49 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "GatherExcelConfigData.json")
public class GatherData extends GameResource {
private int pointType;
private int id;
private int gadgetId;
private int itemId;
private int cd; // Probably hours
private boolean isForbidGuest;
private boolean initDisableInteract;
@Override
public int getId() {
return this.pointType;
}
public int getGatherId() {
return id;
}
public int getGadgetId() {
return gadgetId;
}
public int getItemId() {
return itemId;
}
public int getCd() {
return cd;
}
public boolean isForbidGuest() {
return isForbidGuest;
}
public boolean initDisableInteract() {
return initDisableInteract;
}
@Override
public void onLoad() {
}
}

View File

@ -0,0 +1,37 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
import java.util.List;
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
@ResourceType(name = {"HomeworldLevelExcelConfigData.json"})
public class HomeWorldLevelData extends GameResource {
int level;
int exp;
int homeCoinStoreLimit;
int homeFetterExpStoreLimit;
int rewardId;
int furnitureMakeSlotCount;
int outdoorUnlockBlockCount;
int freeUnlockModuleCount;
int deployNpcCount;
int limitShopGoodsCount;
List<String> levelFuncs;
@Override
public int getId() {
return level;
}
@Override
public void onLoad() {
super.onLoad();
}
}

View File

@ -0,0 +1,29 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Data;
import java.util.List;
@ResourceType(name = "InvestigationMonsterConfigData.json")
@Data
public class InvestigationMonsterData extends GameResource {
private int id;
private int cityId;
private List<Integer> monsterIdList;
private List<Integer> groupIdList;
private int rewardPreviewId;
private String mapMarkCreateType;
private String monsterCategory;
@Override
public int getId() {
return this.id;
}
@Override
public void onLoad() {
super.onLoad();
}
}

View File

@ -2,14 +2,20 @@ package emu.grasscutter.data.excels;
import java.util.List; import java.util.List;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemUseData; import emu.grasscutter.data.common.ItemUseData;
import emu.grasscutter.game.inventory.*;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.ints.IntSet;
@ResourceType(name = {"MaterialExcelConfigData.json", "WeaponExcelConfigData.json", "ReliquaryExcelConfigData.json"}) @ResourceType(name = {"MaterialExcelConfigData.json",
"WeaponExcelConfigData.json",
"ReliquaryExcelConfigData.json",
"HomeWorldFurnitureExcelConfigData.json"
})
public class ItemData extends GameResource { public class ItemData extends GameResource {
private int id; private int id;
@ -63,12 +69,19 @@ public class ItemData extends GameResource {
private long nameTextMapHash; private long nameTextMapHash;
// Post load // Post load
private transient emu.grasscutter.game.inventory.MaterialType materialEnumType; private transient MaterialType materialEnumType;
private transient emu.grasscutter.game.inventory.ItemType itemEnumType; private transient ItemType itemEnumType;
private transient emu.grasscutter.game.inventory.EquipType equipEnumType; private transient EquipType equipEnumType;
private IntSet addPropLevelSet; private IntSet addPropLevelSet;
// Furniture
private int comfort;
private List<Integer> furnType;
private List<Integer> furnitureGadgetID;
@SerializedName("JFDLJGDFIGL")
private int roomSceneId;
@Override @Override
public int getId(){ public int getId(){
return this.id; return this.id;
@ -193,41 +206,57 @@ public class ItemData extends GameResource {
public int getMaxLevel() { public int getMaxLevel() {
return maxLevel; return maxLevel;
} }
public emu.grasscutter.game.inventory.ItemType getItemType() { public int getComfort() {
return comfort;
}
public List<Integer> getFurnType() {
return furnType;
}
public List<Integer> getFurnitureGadgetID() {
return furnitureGadgetID;
}
public int getRoomSceneId() {
return roomSceneId;
}
public ItemType getItemType() {
return this.itemEnumType; return this.itemEnumType;
} }
public emu.grasscutter.game.inventory.MaterialType getMaterialType() { public MaterialType getMaterialType() {
return this.materialEnumType; return this.materialEnumType;
} }
public emu.grasscutter.game.inventory.EquipType getEquipType() { public EquipType getEquipType() {
return this.equipEnumType; return this.equipEnumType;
} }
public boolean canAddRelicProp(int level) { public boolean canAddRelicProp(int level) {
return this.addPropLevelSet != null & this.addPropLevelSet.contains(level); return this.addPropLevelSet != null && this.addPropLevelSet.contains(level);
} }
public boolean isEquip() { public boolean isEquip() {
return this.itemEnumType == emu.grasscutter.game.inventory.ItemType.ITEM_RELIQUARY || this.itemEnumType == emu.grasscutter.game.inventory.ItemType.ITEM_WEAPON; return this.itemEnumType == ItemType.ITEM_RELIQUARY || this.itemEnumType == ItemType.ITEM_WEAPON;
} }
@Override @Override
public void onLoad() { public void onLoad() {
this.itemEnumType = emu.grasscutter.game.inventory.ItemType.getTypeByName(getItemTypeString()); this.itemEnumType = ItemType.getTypeByName(getItemTypeString());
this.materialEnumType = emu.grasscutter.game.inventory.MaterialType.getTypeByName(getMaterialTypeString()); this.materialEnumType = MaterialType.getTypeByName(getMaterialTypeString());
if (this.itemEnumType == emu.grasscutter.game.inventory.ItemType.ITEM_RELIQUARY) { if (this.itemEnumType == ItemType.ITEM_RELIQUARY) {
this.equipEnumType = emu.grasscutter.game.inventory.EquipType.getTypeByName(this.equipType); this.equipEnumType = EquipType.getTypeByName(this.equipType);
if (this.addPropLevels != null && this.addPropLevels.length > 0) { if (this.addPropLevels != null && this.addPropLevels.length > 0) {
this.addPropLevelSet = new IntOpenHashSet(this.addPropLevels); this.addPropLevelSet = new IntOpenHashSet(this.addPropLevels);
} }
} else if (this.itemEnumType == emu.grasscutter.game.inventory.ItemType.ITEM_WEAPON) { } else if (this.itemEnumType == ItemType.ITEM_WEAPON) {
this.equipEnumType = emu.grasscutter.game.inventory.EquipType.EQUIP_WEAPON; this.equipEnumType = EquipType.EQUIP_WEAPON;
} else { } else {
this.equipEnumType = emu.grasscutter.game.inventory.EquipType.EQUIP_NONE; this.equipEnumType = EquipType.EQUIP_NONE;
} }
if (this.getWeaponProperties() != null) { if (this.getWeaponProperties() != null) {
@ -235,6 +264,13 @@ public class ItemData extends GameResource {
weaponProperty.onLoad(); weaponProperty.onLoad();
} }
} }
if(this.getFurnType() != null){
this.furnType = this.furnType.stream().filter(x -> x > 0).toList();
}
if(this.getFurnitureGadgetID() != null){
this.furnitureGadgetID = this.furnitureGadgetID.stream().filter(x -> x > 0).toList();
}
} }
public static class WeaponProperty { public static class WeaponProperty {

View File

@ -13,6 +13,7 @@ import emu.grasscutter.game.Account;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.friends.Friendship; import emu.grasscutter.game.friends.Friendship;
import emu.grasscutter.game.gacha.GachaRecord; import emu.grasscutter.game.gacha.GachaRecord;
import emu.grasscutter.game.home.GameHome;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
@ -295,4 +296,10 @@ public final class DatabaseHelper {
public static boolean deleteQuest(GameMainQuest quest) { public static boolean deleteQuest(GameMainQuest quest) {
return DatabaseManager.getGameDatastore().delete(quest).wasAcknowledged(); return DatabaseManager.getGameDatastore().delete(quest).wasAcknowledged();
} }
public static GameHome getHomeByUid(int id) {
return DatabaseManager.getGameDatastore().find(GameHome.class).filter(Filters.eq("ownerUid", id)).first();
}
public static void saveHome(GameHome gameHome) {
DatabaseManager.getGameDatastore().save(gameHome);
}
} }

View File

@ -16,6 +16,7 @@ import emu.grasscutter.game.Account;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.friends.Friendship; import emu.grasscutter.game.friends.Friendship;
import emu.grasscutter.game.gacha.GachaRecord; import emu.grasscutter.game.gacha.GachaRecord;
import emu.grasscutter.game.home.GameHome;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
@ -30,7 +31,7 @@ public final class DatabaseManager {
private static final Class<?>[] mappedClasses = new Class<?>[] { private static final Class<?>[] mappedClasses = new Class<?>[] {
DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class, DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class,
GachaRecord.class, Mail.class, GameMainQuest.class GachaRecord.class, Mail.class, GameMainQuest.class, GameHome.class
}; };
public static Datastore getGameDatastore() { public static Datastore getGameDatastore() {

View File

@ -28,6 +28,11 @@ public class Account {
private String sessionKey; // Session token for dispatch server private String sessionKey; // Session token for dispatch server
private List<String> permissions; private List<String> permissions;
private Locale locale; private Locale locale;
private String banReason;
private int banEndTime;
private int banStartTime;
private boolean isBanned;
@Deprecated @Deprecated
public Account() { public Account() {
@ -105,6 +110,46 @@ public class Account {
this.locale = locale; this.locale = locale;
} }
public String getBanReason() {
return banReason;
}
public void setBanReason(String banReason) {
this.banReason = banReason;
}
public int getBanEndTime() {
return banEndTime;
}
public void setBanEndTime(int banEndTime) {
this.banEndTime = banEndTime;
}
public int getBanStartTime() {
return banStartTime;
}
public void setBanStartTime(int banStartTime) {
this.banStartTime = banStartTime;
}
public boolean isBanned() {
if (banEndTime > 0 && banEndTime < System.currentTimeMillis() / 1000) {
this.isBanned = false;
this.banEndTime = 0;
this.banStartTime = 0;
this.banReason = null;
save();
}
return isBanned;
}
public void setBanned(boolean isBanned) {
this.isBanned = isBanned;
}
/** /**
* The collection of a player's permissions. * The collection of a player's permissions.
*/ */

View File

@ -1,223 +0,0 @@
package emu.grasscutter.game.dungeons;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.DungeonData;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.ArrayList;
import java.util.List;
public class DungeonChallenge {
private final Scene scene;
private final SceneGroup group;
private int challengeIndex;
private int challengeId;
private boolean success;
private boolean progress;
/**
* has more challenge
*/
private boolean stage;
private int score;
private int objective = 0;
private IntSet rewardedPlayers;
public DungeonChallenge(Scene scene, SceneGroup group, int challengeId, int challengeIndex, int objective) {
this.scene = scene;
this.group = group;
this.challengeId = challengeId;
this.challengeIndex = challengeIndex;
this.objective = objective;
this.setRewardedPlayers(new IntOpenHashSet());
}
public Scene getScene() {
return scene;
}
public SceneGroup getGroup() {
return group;
}
public int getChallengeIndex() {
return challengeIndex;
}
public void setChallengeIndex(int challengeIndex) {
this.challengeIndex = challengeIndex;
}
public int getChallengeId() {
return challengeId;
}
public void setChallengeId(int challengeId) {
this.challengeId = challengeId;
}
public int getObjective() {
return objective;
}
public void setObjective(int objective) {
this.objective = objective;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean isSuccess) {
this.success = isSuccess;
}
public boolean inProgress() {
return progress;
}
public int getScore() {
return score;
}
public boolean isStage() {
return stage;
}
public void setStage(boolean stage) {
this.stage = stage;
}
public int getTimeLimit() {
return 600;
}
public IntSet getRewardedPlayers() {
return rewardedPlayers;
}
public void setRewardedPlayers(IntSet rewardedPlayers) {
this.rewardedPlayers = rewardedPlayers;
}
public void start() {
this.progress = true;
getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this));
}
public void finish() {
this.progress = false;
getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this));
if (this.isSuccess()) {
// Call success script event
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_SUCCESS, null);
// Settle
settle();
} else {
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_FAIL, null);
}
}
private void settle() {
getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
if(!stage){
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE, new ScriptArgs(this.isSuccess() ? 1 : 0));
}
}
public void onMonsterDie(EntityMonster entity) {
score = getScore() + 1;
getScene().broadcastPacket(new PacketChallengeDataNotify(this, 1, getScore()));
if (getScore() >= getObjective() && this.progress) {
this.setSuccess(true);
finish();
}
}
private List<GameItem> rollRewards() {
List<GameItem> rewards = new ArrayList<>();
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
}
return rewards;
}
public void getStatueDrops(Player player, GadgetInteractReq request) {
DungeonData dungeonData = getScene().getDungeonData();
int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20;
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
return;
}
// Already rewarded
if (getRewardedPlayers().contains(player.getUid())) {
return;
}
// Get rewards.
List<GameItem> rewards = new ArrayList<>();
if (request.getIsUseCondenseResin()) {
// Check if condensed resin is usable here.
// For this, we use the following logic for now:
// The normal resin cost of the dungeon has to be 20.
if (resinCost != 20) {
return;
}
// Make sure the player has condensed resin.
GameItem condensedResin = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(220007);
if (condensedResin == null || condensedResin.getCount() <= 0) {
return;
}
// Deduct.
player.getInventory().removeItem(condensedResin, 1);
// Roll rewards, twice (because condensed).
rewards.addAll(this.rollRewards());
rewards.addAll(this.rollRewards());
}
else {
// If the player used regular resin, try to deduct.
// Stop if insufficient resin.
boolean success = player.getResinManager().useResin(resinCost);
if (!success) {
return;
}
// Roll rewards.
rewards.addAll(this.rollRewards());
}
// Add rewards to player and send notification.
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
getRewardedPlayers().add(player.getUid());
}
}

View File

@ -0,0 +1,91 @@
package emu.grasscutter.game.dungeons.challenge;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.DungeonData;
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.ArrayList;
import java.util.List;
public class DungeonChallenge extends WorldChallenge {
/**
* has more challenge
*/
private boolean stage;
private IntSet rewardedPlayers;
public DungeonChallenge(Scene scene, SceneGroup group,
int challengeId, int challengeIndex,
List<Integer> paramList,
int timeLimit, int goal,
List<ChallengeTrigger> challengeTriggers) {
super(scene, group, challengeId, challengeIndex, paramList, timeLimit, goal, challengeTriggers);
this.setRewardedPlayers(new IntOpenHashSet());
}
public boolean isStage() {
return stage;
}
public void setStage(boolean stage) {
this.stage = stage;
}
public IntSet getRewardedPlayers() {
return rewardedPlayers;
}
public void setRewardedPlayers(IntSet rewardedPlayers) {
this.rewardedPlayers = rewardedPlayers;
}
@Override
public void done() {
super.done();
if (this.isSuccess()) {
// Settle
settle();
}
}
private void settle() {
if(!stage){
getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE,
new ScriptArgs(this.isSuccess() ? 1 : 0));
}
}
public void getStatueDrops(Player player) {
DungeonData dungeonData = getScene().getDungeonData();
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
return;
}
// Already rewarded
if (getRewardedPlayers().contains(player.getUid())) {
return;
}
List<GameItem> rewards = new ArrayList<>();
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
}
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
getRewardedPlayers().add(player.getUid());
}
}

View File

@ -0,0 +1,132 @@
package emu.grasscutter.game.dungeons.challenge;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Getter
@Setter
public class WorldChallenge {
private final Scene scene;
private final SceneGroup group;
private final int challengeId;
private final int challengeIndex;
private final List<Integer> paramList;
private final int timeLimit;
private final List<ChallengeTrigger> challengeTriggers;
private boolean progress;
private boolean success;
private long startedAt;
private int finishedTime;
private final int goal;
private final AtomicInteger score;
public WorldChallenge(Scene scene, SceneGroup group,
int challengeId, int challengeIndex, List<Integer> paramList,
int timeLimit, int goal,
List<ChallengeTrigger> challengeTriggers){
this.scene = scene;
this.group = group;
this.challengeId = challengeId;
this.challengeIndex = challengeIndex;
this.paramList = paramList;
this.timeLimit = timeLimit;
this.challengeTriggers = challengeTriggers;
this.goal = goal;
this.score = new AtomicInteger(0);
}
public boolean inProgress(){
return this.progress;
}
public void onCheckTimeOut(){
if(!inProgress()){
return;
}
if(timeLimit <= 0){
return;
}
challengeTriggers.forEach(t -> t.onCheckTimeout(this));
}
public void start(){
if(inProgress()){
Grasscutter.getLogger().info("Could not start a in progress challenge.");
return;
}
this.progress = true;
this.startedAt = System.currentTimeMillis();
getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this));
challengeTriggers.forEach(t -> t.onBegin(this));
}
public void done(){
if(!inProgress()){
return;
}
finish(true);
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_SUCCESS,
// TODO record the time in PARAM2 and used in action
new ScriptArgs().setParam2(finishedTime));
challengeTriggers.forEach(t -> t.onFinish(this));
}
public void fail(){
if(!inProgress()){
return;
}
finish(false);
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_FAIL, null);
challengeTriggers.forEach(t -> t.onFinish(this));
}
private void finish(boolean success){
this.progress = false;
this.success = success;
this.finishedTime = (int)((System.currentTimeMillis() - this.startedAt) / 1000L);
getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this));
}
public int increaseScore(){
return score.incrementAndGet();
}
public void onMonsterDeath(EntityMonster monster){
if(!inProgress()){
return;
}
if(monster.getGroupId() != getGroup().id){
return;
}
this.challengeTriggers.forEach(t -> t.onMonsterDeath(this, monster));
}
public void onGadgetDeath(EntityGadget gadget){
if(!inProgress()){
return;
}
if(gadget.getGroupId() != getGroup().id){
return;
}
this.challengeTriggers.forEach(t -> t.onGadgetDeath(this, gadget));
}
public void onGadgetDamage(EntityGadget gadget){
if(!inProgress()){
return;
}
if(gadget.getGroupId() != getGroup().id){
return;
}
this.challengeTriggers.forEach(t -> t.onGadgetDamage(this, gadget));
}
}

View File

@ -0,0 +1,30 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup;
import java.util.ArrayList;
import java.util.List;
public class ChallengeFactory {
private static final List<ChallengeFactoryHandler> challengeFactoryHandlers = new ArrayList<>();
static {
challengeFactoryHandlers.add(new DungeonChallengeFactoryHandler());
challengeFactoryHandlers.add(new DungeonGuardChallengeFactoryHandler());
challengeFactoryHandlers.add(new KillGadgetChallengeFactoryHandler());
challengeFactoryHandlers.add(new KillMonsterChallengeFactoryHandler());
}
public static WorldChallenge getChallenge(int param1, int param2, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group){
for(var handler : challengeFactoryHandlers){
if(!handler.isThisType(param1, param2, param3, param4, param5, param6, scene, group)){
continue;
}
return handler.build(param1, param2, param3, param4, param5, param6, scene, group);
}
return null;
}
}

View File

@ -0,0 +1,10 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup;
public interface ChallengeFactoryHandler {
boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group);
WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group);
}

View File

@ -0,0 +1,33 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
public class DungeonChallengeFactoryHandler implements ChallengeFactoryHandler{
@Override
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
// ActiveChallenge with 1,1000,300,233101003,15,0
return scene.getSceneType() == SceneType.SCENE_DUNGEON
&& param4 == group.id;
}
@Override
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
var realGroup = scene.getScriptManager().getGroupById(param4);
return new DungeonChallenge(
scene, realGroup,
challengeId, // Id
challengeIndex, // Index
List.of(param5, param3),
param3, // Limit
param5, // Goal
List.of(new InTimeTrigger(), new KillMonsterTrigger()));
}
}

View File

@ -0,0 +1,34 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.trigger.GuardTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
public class DungeonGuardChallengeFactoryHandler implements ChallengeFactoryHandler{
@Override
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
// ActiveChallenge with 1,188,234101003,12,3030,0
return scene.getSceneType() == SceneType.SCENE_DUNGEON
&& param3 == group.id;
}
@Override
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
var realGroup = scene.getScriptManager().getGroupById(param3);
return new DungeonChallenge(
scene, realGroup,
challengeId, // Id
challengeIndex, // Index
List.of(param4, 0),
0, // Limit
param5, // Goal
List.of(new GuardTrigger()));
}
}

View File

@ -0,0 +1,34 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.factory.ChallengeFactoryHandler;
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.KillGadgetTrigger;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
public class KillGadgetChallengeFactoryHandler implements ChallengeFactoryHandler {
@Override
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
// kill gadgets(explosive barrel) in time
// ActiveChallenge with 56,201,20,2,201,4
// open chest in time
// ActiveChallenge with 666,202,30,7,202,1
return challengeId == 201 || challengeId == 202;
}
@Override
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
return new WorldChallenge(
scene, group,
challengeId, // Id
challengeIndex, // Index
List.of(param3, param6, 0),
param3, // Limit
param6, // Goal
List.of(new InTimeTrigger(), new KillGadgetTrigger())
);
}
}

View File

@ -0,0 +1,31 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
public class KillMonsterChallengeFactoryHandler implements ChallengeFactoryHandler{
@Override
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
// ActiveChallenge with 180,180,45,133108061,1,0
return challengeId == 180;
}
@Override
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
var realGroup = scene.getScriptManager().getGroupById(param4);
return new WorldChallenge(
scene, realGroup,
challengeId, // Id
challengeIndex, // Index
List.of(param5, param3),
param3, // Limit
param5, // Goal
List.of(new KillMonsterTrigger(), new InTimeTrigger())
);
}
}

View File

@ -0,0 +1,14 @@
package emu.grasscutter.game.dungeons.challenge.trigger;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster;
public abstract class ChallengeTrigger {
public void onBegin(WorldChallenge challenge){}
public void onFinish(WorldChallenge challenge){}
public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster){}
public void onGadgetDeath(WorldChallenge challenge, EntityGadget gadget){}
public void onCheckTimeout(WorldChallenge challenge){}
public void onGadgetDamage(WorldChallenge challenge, EntityGadget gadget){}
}

View File

@ -0,0 +1,27 @@
package emu.grasscutter.game.dungeons.challenge.trigger;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
public class GuardTrigger extends KillMonsterTrigger{
@Override
public void onBegin(WorldChallenge challenge) {
super.onBegin(challenge);
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, 100));
}
@Override
public void onGadgetDamage(WorldChallenge challenge, EntityGadget gadget) {
var curHp = gadget.getFightProperties().get(FightProperty.FIGHT_PROP_CUR_HP.getId());
var maxHp = gadget.getFightProperties().get(FightProperty.FIGHT_PROP_BASE_HP.getId());
int percent = (int) (curHp / maxHp);
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, percent));
if(percent <= 0){
challenge.fail();
}
}
}

View File

@ -0,0 +1,13 @@
package emu.grasscutter.game.dungeons.challenge.trigger;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
public class InTimeTrigger extends ChallengeTrigger{
@Override
public void onCheckTimeout(WorldChallenge challenge) {
var current = System.currentTimeMillis();
if(current - challenge.getStartedAt() > challenge.getTimeLimit() * 1000L){
challenge.fail();
}
}
}

View File

@ -0,0 +1,23 @@
package emu.grasscutter.game.dungeons.challenge.trigger;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
public class KillGadgetTrigger extends ChallengeTrigger{
@Override
public void onBegin(WorldChallenge challenge) {
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, challenge.getScore().get()));
}
@Override
public void onGadgetDeath(WorldChallenge challenge, EntityGadget gadget) {
var newScore = challenge.increaseScore();
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, newScore));
if(newScore >= challenge.getGoal()){
challenge.done();
}
}
}

View File

@ -0,0 +1,23 @@
package emu.grasscutter.game.dungeons.challenge.trigger;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
public class KillMonsterTrigger extends ChallengeTrigger{
@Override
public void onBegin(WorldChallenge challenge) {
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 1, challenge.getScore().get()));
}
@Override
public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster) {
var newScore = challenge.increaseScore();
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 1, newScore));
if(newScore >= challenge.getGoal()){
challenge.done();
}
}
}

View File

@ -1,16 +1,13 @@
package emu.grasscutter.game.entity; package emu.grasscutter.game.entity;
import java.util.Arrays;
import java.util.List;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.GadgetData; import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.entity.gadget.*;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.EntityType; import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.World;
import emu.grasscutter.net.proto.ClientGadgetInfoOuterClass;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair; import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo; import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
@ -23,15 +20,17 @@ import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.net.proto.VectorOuterClass.Vector; import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo; import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketGadgetStateNotify;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList; import lombok.ToString;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
@ToString(callSuper = true)
public class EntityGadget extends EntityBaseGadget { public class EntityGadget extends EntityBaseGadget {
private final GadgetData data; private final GadgetData data;
private final Position pos; private final Position pos;
@ -39,7 +38,9 @@ public class EntityGadget extends EntityBaseGadget {
private int gadgetId; private int gadgetId;
private int state; private int state;
private IntSet worktopOptions; private int pointType;
private GadgetContent content;
private SceneGadget metaGadget;
public EntityGadget(Scene scene, int gadgetId, Position pos) { public EntityGadget(Scene scene, int gadgetId, Position pos) {
super(scene); super(scene);
@ -50,19 +51,22 @@ public class EntityGadget extends EntityBaseGadget {
this.rot = new Position(); this.rot = new Position();
} }
public EntityGadget(Scene scene, int gadgetId, Position pos, GadgetContent content) {
this(scene, gadgetId, pos);
this.content = content;
}
public GadgetData getGadgetData() { public GadgetData getGadgetData() {
return data; return data;
} }
@Override @Override
public Position getPosition() { public Position getPosition() {
// TODO Auto-generated method stub
return this.pos; return this.pos;
} }
@Override @Override
public Position getRotation() { public Position getRotation() {
// TODO Auto-generated method stub
return this.rot; return this.rot;
} }
@ -81,34 +85,73 @@ public class EntityGadget extends EntityBaseGadget {
public void setState(int state) { public void setState(int state) {
this.state = state; this.state = state;
} }
public void updateState(int state){
this.setState(state);
this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state));
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_STATE_CHANGE, new ScriptArgs(state, this.getConfigId()));
}
public IntSet getWorktopOptions() { public int getPointType() {
return worktopOptions; return pointType;
} }
public void addWorktopOptions(int[] options) { public void setPointType(int pointType) {
if (this.worktopOptions == null) { this.pointType = pointType;
this.worktopOptions = new IntOpenHashSet();
}
Arrays.stream(options).forEach(this.worktopOptions::add);
} }
public void removeWorktopOption(int option) { public GadgetContent getContent() {
if (this.worktopOptions == null) { return content;
}
@Deprecated // Dont use!
public void setContent(GadgetContent content) {
this.content = this.content == null ? content : this.content;
}
public SceneGadget getMetaGadget() {
return metaGadget;
}
public void setMetaGadget(SceneGadget metaGadget) {
this.metaGadget = metaGadget;
}
// TODO refactor
public void buildContent() {
if (getContent() != null || getGadgetData() == null || getGadgetData().getType() == null) {
return; return;
} }
this.worktopOptions.remove(option);
EntityType type = getGadgetData().getType();
GadgetContent content = switch (type) {
case GatherPoint -> new GadgetGatherPoint(this);
case Worktop -> new GadgetWorktop(this);
case RewardStatue -> new GadgetRewardStatue(this);
case Chest -> new GadgetChest(this);
default -> null;
};
this.content = content;
} }
@Override @Override
public Int2FloatOpenHashMap getFightProperties() { public Int2FloatOpenHashMap getFightProperties() {
// TODO Auto-generated method stub
return null; return null;
} }
@Override
public void onCreate() {
// Lua event
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(this.getConfigId()));
}
@Override @Override
public void onDeath(int killerId) { public void onDeath(int killerId) {
if(getScene().getChallenge() != null){
getScene().getChallenge().onGadgetDeath(this);
}
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_GADGET_DIE, new ScriptArgs(this.getConfigId()));
} }
@Override @Override
@ -143,15 +186,16 @@ public class EntityGadget extends EntityBaseGadget {
.setIsEnableInteract(true) .setIsEnableInteract(true)
.setAuthorityPeerId(this.getScene().getWorld().getHostPeerId()); .setAuthorityPeerId(this.getScene().getWorld().getHostPeerId());
if (this.getGadgetData().getType() == EntityType.Worktop && this.getWorktopOptions() != null) { if (this.getContent() != null) {
WorktopInfo worktop = WorktopInfo.newBuilder() this.getContent().onBuildProto(gadgetInfo);
.addAllOptionList(this.getWorktopOptions())
.build();
gadgetInfo.setWorktop(worktop);
} }
entityInfo.setGadget(gadgetInfo); entityInfo.setGadget(gadgetInfo);
return entityInfo.build(); return entityInfo.build();
} }
public void die() {
getScene().broadcastPacket(new PacketLifeStateChangeNotify(this, LifeState.LIFE_DEAD));
this.onDeath(0);
}
} }

View File

@ -4,13 +4,11 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.PropGrowCurve; import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.data.excels.MonsterCurveData; import emu.grasscutter.data.excels.MonsterCurveData;
import emu.grasscutter.data.excels.MonsterData; import emu.grasscutter.data.excels.MonsterData;
import emu.grasscutter.game.dungeons.DungeonChallenge;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.World;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair; import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo; import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
@ -25,6 +23,7 @@ import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
import emu.grasscutter.net.proto.SceneMonsterInfoOuterClass.SceneMonsterInfo; import emu.grasscutter.net.proto.SceneMonsterInfoOuterClass.SceneMonsterInfo;
import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo; import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo;
import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap;
@ -111,6 +110,12 @@ public class EntityMonster extends GameEntity {
public void setPoseId(int poseId) { public void setPoseId(int poseId) {
this.poseId = poseId; this.poseId = poseId;
} }
@Override
public void onCreate() {
// Lua event
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(this.getConfigId()));
}
@Override @Override
public void damage(float amount, int killerId) { public void damage(float amount, int killerId) {
@ -135,8 +140,8 @@ public class EntityMonster extends GameEntity {
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry()); this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
} }
// first set the challenge data // first set the challenge data
if (getScene().getChallenge() != null && getScene().getChallenge().getGroup().id == this.getGroupId()) { if (getScene().getChallenge() != null) {
getScene().getChallenge().onMonsterDie(this); getScene().getChallenge().onMonsterDeath(this);
} }
if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) { if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) {
if(getScene().getScriptManager().getScriptMonsterSpawnService() != null){ if(getScene().getScriptManager().getScriptMonsterSpawnService() != null){
@ -144,7 +149,9 @@ public class EntityMonster extends GameEntity {
} }
// prevent spawn monster after success // prevent spawn monster after success
if(getScene().getChallenge() != null && getScene().getChallenge().inProgress()){ if(getScene().getChallenge() != null && getScene().getChallenge().inProgress()){
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, null); getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
}else if(getScene().getChallenge() == null){
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
} }
} }
} }

View File

@ -0,0 +1,81 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.*;
import emu.grasscutter.scripts.data.SceneNPC;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
public class EntityNPC extends GameEntity{
private final Position position;
private final Position rotation;
private final SceneNPC metaNpc;
private final int suiteId;
public EntityNPC(Scene scene, SceneNPC metaNPC, int blockId, int suiteId) {
super(scene);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.NPC);
setConfigId(metaNPC.config_id);
setGroupId(metaNPC.group.id);
setBlockId(blockId);
this.suiteId = suiteId;
this.position = metaNPC.pos.clone();
this.rotation = metaNPC.rot.clone();
this.metaNpc = metaNPC;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return null;
}
@Override
public Position getPosition() {
return position;
}
@Override
public Position getRotation() {
return rotation;
}
public int getSuiteId() {
return suiteId;
}
@Override
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
EntityAuthorityInfoOuterClass.EntityAuthorityInfo authority = EntityAuthorityInfoOuterClass.EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfoOuterClass.SceneEntityAiInfo.newBuilder()
.setIsAiOpen(true)
.setBornPos(getPosition().toProto()))
.setBornPos(getPosition().toProto())
.build();
SceneEntityInfoOuterClass.SceneEntityInfo.Builder entityInfo = SceneEntityInfoOuterClass.SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityTypeOuterClass.ProtEntityType.PROT_ENTITY_TYPE_NPC)
.setMotionInfo(MotionInfoOuterClass.MotionInfo.newBuilder()
.setPos(getPosition().toProto())
.setRot(getRotation().toProto())
.setSpeed(VectorOuterClass.Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientDataOuterClass.EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
entityInfo.setNpc(SceneNpcInfoOuterClass.SceneNpcInfo.newBuilder()
.setNpcId(metaNpc.npc_id)
.setBlockId(getBlockId())
.build());
return entityInfo.build();
}
}

View File

@ -107,10 +107,6 @@ public abstract class GameEntity {
public void setLastMoveReliableSeq(int lastMoveReliableSeq) { public void setLastMoveReliableSeq(int lastMoveReliableSeq) {
this.lastMoveReliableSeq = lastMoveReliableSeq; this.lastMoveReliableSeq = lastMoveReliableSeq;
} }
public abstract SceneEntityInfo toProto();
public abstract void onDeath(int killerId);
public void setFightProperty(FightProperty prop, float value) { public void setFightProperty(FightProperty prop, float value) {
this.getFightProperties().put(prop.getId(), value); this.getFightProperties().put(prop.getId(), value);
@ -219,4 +215,21 @@ public abstract class GameEntity {
getScene().killEntity(this, killerId); getScene().killEntity(this, killerId);
} }
} }
/**
* Called when this entity is added to the world
*/
public void onCreate() {
}
/**
* Called when this entity dies
* @param killerId Entity id of the entity that killed this entity
*/
public void onDeath(int killerId) {
}
public abstract SceneEntityInfo toProto();
} }

View File

@ -0,0 +1,64 @@
package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.BossChestInfoOuterClass.BossChestInfo;
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
import emu.grasscutter.net.proto.InterOpTypeOuterClass.InterOpType;
import emu.grasscutter.net.proto.InteractTypeOuterClass;
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.scripts.constants.ScriptGadgetState;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
public class GadgetChest extends GadgetContent {
public GadgetChest(EntityGadget gadget) {
super(gadget);
}
public boolean onInteract(Player player, InterOpType opType) {
var chestInteractHandlerMap = getGadget().getScene().getWorld().getServer().getWorldDataManager().getChestInteractHandlerMap();
var handler = chestInteractHandlerMap.get(getGadget().getGadgetData().getJsonName());
if(handler == null){
Grasscutter.getLogger().warn("Could not found the handler of this type of Chests {}", getGadget().getGadgetData().getJsonName());
return false;
}
if(opType == InterOpType.INTER_OP_TYPE_START && handler.isTwoStep()){
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_START));
return false;
}else{
var success = handler.onInteract(this, player);
if (!success){
return false;
}
getGadget().updateState(ScriptGadgetState.ChestOpened);
player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractTypeOuterClass.InteractType.INTERACT_TYPE_OPEN_CHEST));
// let the chest disappear
getGadget().die();
return true;
}
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
if(getGadget().getMetaGadget() == null){
return;
}
var bossChest = getGadget().getMetaGadget().boss_chest;
if(bossChest != null){
var players = getGadget().getScene().getPlayers().stream().map(Player::getUid).toList();
gadgetInfo.setBossChest(BossChestInfo.newBuilder()
.setMonsterConfigId(bossChest.monster_config_id)
.setResin(bossChest.resin)
.addAllQualifyUidList(players)
.addAllRemainUidList(players)
.build());
}
}
}

View File

@ -0,0 +1,22 @@
package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
public abstract class GadgetContent {
private final EntityGadget gadget;
public GadgetContent(EntityGadget gadget) {
this.gadget = gadget;
}
public EntityGadget getGadget() {
return gadget;
}
public abstract boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType);
public abstract void onBuildProto(SceneGadgetInfo.Builder gadgetInfo);
}

View File

@ -0,0 +1,45 @@
package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.GatherData;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
public class GadgetGatherPoint extends GadgetContent {
private GatherData gatherData;
public GadgetGatherPoint(EntityGadget gadget) {
super(gadget);
this.gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
}
public GatherData getGatherData() {
return gatherData;
}
public int getItemId() {
return getGatherData().getItemId();
}
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
GameItem item = new GameItem(gatherData.getItemId(), 1);
player.getInventory().addItem(item, ActionReason.Gather);
return true;
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
GatherGadgetInfo gatherGadgetInfo = GatherGadgetInfo.newBuilder()
.setItemId(this.getItemId())
.setIsForbidGuest(this.getGatherData().isForbidGuest())
.build();
gadgetInfo.setGatherGadget(gatherGadgetInfo);
}
}

View File

@ -0,0 +1,30 @@
package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
public class GadgetRewardStatue extends GadgetContent {
public GadgetRewardStatue(EntityGadget gadget) {
super(gadget);
}
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
if (player.getScene().getChallenge() != null && player.getScene().getChallenge() instanceof DungeonChallenge dungeonChallenge) {
dungeonChallenge.getStatueDrops(player);
}
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_STATUE));
return false;
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
}
}

View File

@ -0,0 +1,53 @@
package emu.grasscutter.game.entity.gadget;
import java.util.Arrays;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
public class GadgetWorktop extends GadgetContent {
private IntSet worktopOptions;
public GadgetWorktop(EntityGadget gadget) {
super(gadget);
}
public IntSet getWorktopOptions() {
return worktopOptions;
}
public void addWorktopOptions(int[] options) {
if (this.worktopOptions == null) {
this.worktopOptions = new IntOpenHashSet();
}
Arrays.stream(options).forEach(this.worktopOptions::add);
}
public void removeWorktopOption(int option) {
if (this.worktopOptions == null) {
return;
}
this.worktopOptions.remove(option);
}
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
return false;
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
if (this.worktopOptions == null) {
return;
}
WorktopInfo worktop = WorktopInfo.newBuilder()
.addAllOptionList(this.getWorktopOptions())
.build();
gadgetInfo.setWorktop(worktop);
}
}

View File

@ -0,0 +1,40 @@
package emu.grasscutter.game.entity.gadget.chest;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.game.entity.gadget.GadgetChest;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
import java.util.ArrayList;
import java.util.List;
public class BossChestInteractHandler implements ChestInteractHandler{
@Override
public boolean isTwoStep() {
return true;
}
@Override
public boolean onInteract(GadgetChest chest, Player player) {
var worldDataManager = chest.getGadget().getScene().getWorld().getServer().getWorldDataManager();
var monster = chest.getGadget().getMetaGadget().group.monsters.get(chest.getGadget().getMetaGadget().boss_chest.monster_config_id);
var reward = worldDataManager.getRewardByBossId(monster.monster_id);
if(reward == null){
Grasscutter.getLogger().warn("Could not found the reward of boss monster {}", monster.monster_id);
return false;
}
List<GameItem> rewards = new ArrayList<>();
for (ItemParamData param : reward.getPreviewItems()) {
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
}
player.getInventory().addItems(rewards, ActionReason.OpenWorldBossChest);
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
return true;
}
}

View File

@ -0,0 +1,11 @@
package emu.grasscutter.game.entity.gadget.chest;
import emu.grasscutter.game.entity.gadget.GadgetChest;
import emu.grasscutter.game.player.Player;
public interface ChestInteractHandler {
boolean isTwoStep();
boolean onInteract(GadgetChest chest, Player player);
}

View File

@ -0,0 +1,42 @@
package emu.grasscutter.game.entity.gadget.chest;
import emu.grasscutter.game.entity.gadget.GadgetChest;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.ChestReward;
import java.util.Random;
public class NormalChestInteractHandler implements ChestInteractHandler {
private final ChestReward chestReward;
public NormalChestInteractHandler(ChestReward rewardData){
this.chestReward = rewardData;
}
@Override
public boolean isTwoStep() {
return false;
}
@Override
public boolean onInteract(GadgetChest chest, Player player) {
player.earnExp(chestReward.getAdvExp());
player.getInventory().addItem(201, chestReward.getResin());
var mora = chestReward.getMora() * (1 + (player.getWorldLevel() - 1) * 0.5);
player.getInventory().addItem(202, (int)mora);
for(int i=0;i<chestReward.getContent().size();i++){
chest.getGadget().getScene().addItemEntity(chestReward.getContent().get(i).getItemId(), chestReward.getContent().get(i).getCount(), chest.getGadget());
}
var random = new Random(System.currentTimeMillis());
for(int i=0;i<chestReward.getRandomCount();i++){
var index = random.nextInt(chestReward.getRandomContent().size());
var item = chestReward.getRandomContent().get(index);
chest.getGadget().getScene().addItemEntity(item.getItemId(), item.getCount(), chest.getGadget());
}
return true;
}
}

View File

@ -0,0 +1,33 @@
package emu.grasscutter.game.home;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import emu.grasscutter.net.proto.FurnitureMakeDataOuterClass;
import emu.grasscutter.net.proto.FurnitureMakeSlotOuterClass;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Entity
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of")
public class FurnitureMakeSlotItem {
@Id
int index;
int makeId;
int avatarId;
int beginTime;
int durTime;
public FurnitureMakeDataOuterClass.FurnitureMakeData toProto() {
return FurnitureMakeDataOuterClass.FurnitureMakeData.newBuilder()
.setIndex(index)
.setAvatarId(avatarId)
.setMakeId(makeId)
.setBeginTime(beginTime)
.setDurTime(durTime)
.build();
}
}

View File

@ -0,0 +1,80 @@
package emu.grasscutter.game.home;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.IndexOptions;
import dev.morphia.annotations.Indexed;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.HomeWorldLevelData;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.packet.send.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@Entity(value = "homes", useDiscriminator = false)
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of")
public class GameHome {
@Id
String id;
@Indexed(options = @IndexOptions(unique = true))
long ownerUid;
int level;
int exp;
List<FurnitureMakeSlotItem> furnitureMakeSlotItemList;
ConcurrentHashMap<Integer, HomeSceneItem> sceneMap;
public void save(){
DatabaseHelper.saveHome(this);
}
public static GameHome getByUid(Integer uid){
var home = DatabaseHelper.getHomeByUid(uid);
if (home == null) {
home = GameHome.create(uid);
}
return home;
}
public static GameHome create(Integer uid){
return GameHome.of()
.ownerUid(uid)
.level(1)
.sceneMap(new ConcurrentHashMap<>())
.build();
}
public HomeSceneItem getHomeSceneItem(int sceneId) {
return sceneMap.computeIfAbsent(sceneId, e -> {
var defaultItem = GameData.getHomeworldDefaultSaveData().get(sceneId);
if (defaultItem != null){
Grasscutter.getLogger().info("Set player {} home {} to initial setting", ownerUid, sceneId);
return HomeSceneItem.parseFrom(defaultItem, sceneId);
}
return null;
});
}
public void onOwnerLogin(Player player) {
player.getSession().send(new PacketHomeBasicInfoNotify(player, false));
player.getSession().send(new PacketPlayerHomeCompInfoNotify(player));
player.getSession().send(new PacketHomeComfortInfoNotify(player));
player.getSession().send(new PacketFurnitureCurModuleArrangeCountNotify());
player.getSession().send(new PacketHomeMarkPointNotify(player));
}
public HomeWorldLevelData getLevelData(){
return GameData.getHomeWorldLevelDataMap().get(level);
}
}

View File

@ -0,0 +1,37 @@
package emu.grasscutter.game.home;
import dev.morphia.annotations.Entity;
import emu.grasscutter.net.proto.HomeAnimalDataOuterClass;
import emu.grasscutter.net.proto.HomeFurnitureDataOuterClass;
import emu.grasscutter.utils.Position;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Entity
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of")
public class HomeAnimalItem {
int furnitureId;
Position spawnPos;
Position spawnRot;
public HomeAnimalDataOuterClass.HomeAnimalData toProto(){
return HomeAnimalDataOuterClass.HomeAnimalData.newBuilder()
.setFurnitureId(furnitureId)
.setSpawnPos(spawnPos.toProto())
.setSpawnRot(spawnRot.toProto())
.build();
}
public static HomeAnimalItem parseFrom(HomeAnimalDataOuterClass.HomeAnimalData homeAnimalData) {
return HomeAnimalItem.of()
.furnitureId(homeAnimalData.getFurnitureId())
.spawnPos(new Position(homeAnimalData.getSpawnPos()))
.spawnRot(new Position(homeAnimalData.getSpawnRot()))
.build();
}
}

View File

@ -0,0 +1,83 @@
package emu.grasscutter.game.home;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import emu.grasscutter.data.binout.HomeworldDefaultSaveData;
import emu.grasscutter.net.proto.HomeBlockArrangementInfoOuterClass.HomeBlockArrangementInfo;
import emu.grasscutter.utils.Position;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import java.util.List;
@Entity
@Data
@Builder(builderMethodName = "of")
@FieldDefaults(level = AccessLevel.PRIVATE)
public class HomeBlockItem {
@Id
int blockId;
boolean unlocked;
List<HomeFurnitureItem> deployFurnitureList;
List<HomeFurnitureItem> persistentFurnitureList;
List<HomeAnimalItem> deployAnimalList;
List<HomeNPCItem> deployNPCList;
public void update(HomeBlockArrangementInfo homeBlockArrangementInfo) {
this.blockId = homeBlockArrangementInfo.getBlockId();
this.deployFurnitureList = homeBlockArrangementInfo.getDeployFurniureListList().stream()
.map(HomeFurnitureItem::parseFrom)
.toList();
this.persistentFurnitureList = homeBlockArrangementInfo.getPersistentFurnitureListList().stream()
.map(HomeFurnitureItem::parseFrom)
.toList();
this.deployAnimalList = homeBlockArrangementInfo.getDeployAnimalListList().stream()
.map(HomeAnimalItem::parseFrom)
.toList();
this.deployNPCList = homeBlockArrangementInfo.getDeployNpcListList().stream()
.map(HomeNPCItem::parseFrom)
.toList();
}
public int calComfort(){
return this.deployFurnitureList.stream()
.mapToInt(HomeFurnitureItem::getComfort)
.sum();
}
public HomeBlockArrangementInfo toProto() {
var proto = HomeBlockArrangementInfo.newBuilder()
.setBlockId(blockId)
.setIsUnlocked(unlocked)
.setComfortValue(calComfort());
this.deployFurnitureList.forEach(f -> proto.addDeployFurniureList(f.toProto()));
this.persistentFurnitureList.forEach(f -> proto.addPersistentFurnitureList(f.toProto()));
this.deployAnimalList.forEach(f -> proto.addDeployAnimalList(f.toProto()));
this.deployNPCList.forEach(f -> proto.addDeployNpcList(f.toProto()));
return proto.build();
}
public static HomeBlockItem parseFrom(HomeworldDefaultSaveData.HomeBlock homeBlock) {
// create from default setting
return HomeBlockItem.of()
.blockId(homeBlock.getBlockId())
.unlocked(homeBlock.getFurnitures() != null)
.deployFurnitureList(
homeBlock.getFurnitures() == null ? List.of() :
homeBlock.getFurnitures().stream()
.map(HomeFurnitureItem::parseFrom)
.toList())
.deployAnimalList(List.of())
.deployNPCList(List.of())
.persistentFurnitureList(List.of())
.build();
}
}

Some files were not shown because too many files have changed in this diff Show More