diff --git a/src/psu/psu_game.erl b/src/psu/psu_game.erl index adc5578..324464b 100644 --- a/src/psu/psu_game.erl +++ b/src/psu/psu_game.erl @@ -772,6 +772,87 @@ event({npc_shop_request, ShopID}) -> _ -> send_1a02(0, 0, 1, 0, 0) end; +%% @todo Not sure what are those hardcoded values. +event({object_boss_gate_activate, ObjectID}) -> + send_1213(ObjectID, 0), + send_1215(2, 16#7008), + %% @todo Following sent after the warp? + send_1213(37, 0), + %% @todo Why resend this? + send_1213(ObjectID, 0); + +event({object_boss_gate_enter, ObjectID}) -> + send_1213(ObjectID, 1); + +%% @todo Do we need to send something back here? +event({object_boss_gate_leave, _ObjectID}) -> + ignore; + +%% @todo Second send_1211 argument should be User#egs_user_model.lid. Fix when it's correctly handled. +event({object_chair_sit, ObjectTargetID}) -> + %~ {ok, User} = egs_user_model:read(get(gid)), + send_1211(ObjectTargetID, 0, 8, 0); + +%% @todo Second send_1211 argument should be User#egs_user_model.lid. Fix when it's correctly handled. +event({object_chair_stand, ObjectTargetID}) -> + %~ {ok, User} = egs_user_model:read(get(gid)), + send_1211(ObjectTargetID, 0, 8, 2); + +event({object_crystal_activate, ObjectID}) -> + send_1213(ObjectID, 1); + +event({object_key_console_enable, ObjectID}) -> + {ok, User} = egs_user_model:read(get(gid)), + {BlockID, [EventID|_]} = psu_instance:std_event(User#egs_user_model.instancepid, (User#egs_user_model.area)#psu_area.zoneid, ObjectID), + send_1205(EventID, BlockID, 0), + send_1213(ObjectID, 1); + +event({object_key_console_init, ObjectID}) -> + {ok, User} = egs_user_model:read(get(gid)), + {BlockID, [_, EventID, _]} = psu_instance:std_event(User#egs_user_model.instancepid, (User#egs_user_model.area)#psu_area.zoneid, ObjectID), + send_1205(EventID, BlockID, 0); + +event({object_key_console_open_gate, ObjectID}) -> + {ok, User} = egs_user_model:read(get(gid)), + {BlockID, [_, _, EventID]} = psu_instance:std_event(User#egs_user_model.instancepid, (User#egs_user_model.area)#psu_area.zoneid, ObjectID), + send_1205(EventID, BlockID, 0), + send_1213(ObjectID, 1); + +%% @todo Now that it's separate from object_key_console_enable, handle it better than that, don't need a list of events. +event({object_key_enable, ObjectID}) -> + {ok, User} = egs_user_model:read(get(gid)), + {BlockID, [EventID|_]} = psu_instance:std_event(User#egs_user_model.instancepid, (User#egs_user_model.area)#psu_area.zoneid, ObjectID), + send_1205(EventID, BlockID, 0), + send_1213(ObjectID, 1); + +%% @todo Some switch objects apparently work differently, like the light switch in Mines in MAG'. +event({object_switch_off, ObjectID}) -> + {ok, User} = egs_user_model:read(get(gid)), + {BlockID, EventID} = psu_instance:std_event(User#egs_user_model.instancepid, (User#egs_user_model.area)#psu_area.zoneid, ObjectID), + send_1205(EventID, BlockID, 1), + send_1213(ObjectID, 0); + +event({object_switch_on, ObjectID}) -> + {ok, User} = egs_user_model:read(get(gid)), + {BlockID, EventID} = psu_instance:std_event(User#egs_user_model.instancepid, (User#egs_user_model.area)#psu_area.zoneid, ObjectID), + send_1205(EventID, BlockID, 0), + send_1213(ObjectID, 1); + +event({object_vehicle_boost_enable, ObjectID}) -> + send_1213(ObjectID, 1); + +event({object_vehicle_boost_respawn, ObjectID}) -> + send_1213(ObjectID, 0); + +%% @todo Second send_1211 argument should be User#egs_user_model.lid. Fix when it's correctly handled. +event({object_warp_take, BlockID, ListNb, ObjectNb}) -> + {ok, User} = egs_user_model:read(get(gid)), + Pos = psu_instance:warp_event(User#egs_user_model.instancepid, (User#egs_user_model.area)#psu_area.zoneid, BlockID, ListNb, ObjectNb), + NewUser = User#egs_user_model{pos=Pos}, + egs_user_model:write(NewUser), + send_0503(User#egs_user_model.pos), + send_1211(16#ffffffff, 0, 14, 0); + event(player_type_availability_request) -> send_1a07(); @@ -920,83 +1001,6 @@ handle(16#0f07, Data) -> log("after enter vehicle: ~b ~b", [A, B]), send(<< (header(16#120f))/binary, A:32/little-unsigned-integer, B:32/little-unsigned-integer >>); -%% @doc Object event handler. -%% @todo Handle all events appropriately. -%% @todo B should be the ObjType. -handle(16#0f0a, Data) -> - << BlockID:16/little-unsigned-integer, ListNb:16/little-unsigned-integer, ObjectNb:16/little-unsigned-integer, _MapID:16/little-unsigned-integer, ObjectID:16/little-unsigned-integer, - _:16, A:32/little-unsigned-integer, B:32/little-unsigned-integer, _:32, C:32/little-unsigned-integer, _:272, Action:8, _/bits >> = Data, - log("object event handler: action ~b object ~b a ~b b ~b c ~b", [Action, ObjectID, A, B, C]), - case Action of - 0 -> % warp - {ok, User} = egs_user_model:read(get(gid)), - Pos = psu_instance:warp_event(User#egs_user_model.instancepid, (User#egs_user_model.area)#psu_area.zoneid, BlockID, ListNb, ObjectNb), - NewUser = User#egs_user_model{pos=Pos}, - egs_user_model:write(NewUser), - send_0503(User#egs_user_model.pos), - send_1211(A, C, B, 0); - 3 -> % crystal activation - send_1213(ObjectID, 1); - 4 -> % enter boss gate - send_1213(ObjectID, 1); - 5 -> % leave boss gate - % probably 1213, unknown last value - ignore; - 6 -> % activate boss gate - send_1213(ObjectID, 0), - send_1215(2, 16#7008), - %% @todo Sent after warp but not necessarily, also what's 37 (should be a B1 object) and why resend the 1213(id)? - send_1213(37, 0), - send_1213(ObjectID, 0); - 9 -> % healing pad - % 0117, 0111, 0117? - ignore; - 12 -> % pick/use key, pick vehicle_boost - {ok, User} = egs_user_model:read(get(gid)), - Args = psu_instance:std_event(User#egs_user_model.instancepid, (User#egs_user_model.area)#psu_area.zoneid, ObjectID), - case Args of - undefined -> %% vehicle boost doesn't send an event - ignore; - {BlockID, [EventID|_]} -> - send_1205(EventID, BlockID, 0) - end, - send_1213(ObjectID, 1); - 13 -> % floor_button on (also sent when clearing a few of the rooms in black nest) - {ok, User} = egs_user_model:read(get(gid)), - {BlockID, EventID} = psu_instance:std_event(User#egs_user_model.instancepid, (User#egs_user_model.area)#psu_area.zoneid, ObjectID), - send_1205(EventID, BlockID, 0), - send_1213(ObjectID, 1); - 14 -> % floor_button off - %% @todo Apparently when it's not a floor_button but a light switch, this here should be handled differently. - {ok, User} = egs_user_model:read(get(gid)), - {BlockID, EventID} = psu_instance:std_event(User#egs_user_model.instancepid, (User#egs_user_model.area)#psu_area.zoneid, ObjectID), - send_1205(EventID, BlockID, 1), - send_1213(ObjectID, 0); - %~ 19 -> % activate trap - %~ ignore; - 20 -> % enter counter/elevator/room/spaceport/pick key/use key - ignore; - 23 -> % initialize key slots (called when picking a key or checking the gate directly with no key) - {ok, User} = egs_user_model:read(get(gid)), - {BlockID, [_, EventID, _]} = psu_instance:std_event(User#egs_user_model.instancepid, (User#egs_user_model.area)#psu_area.zoneid, ObjectID), - send_1205(EventID, BlockID, 0); % in block 1, 202 = key [1] x1, 203 = key [-] x1 - 24 -> % open gate (only when client has key) - {ok, User} = egs_user_model:read(get(gid)), - {BlockID, [_, _, EventID]} = psu_instance:std_event(User#egs_user_model.instancepid, (User#egs_user_model.area)#psu_area.zoneid, ObjectID), - send_1205(EventID, BlockID, 0), - send_1213(ObjectID, 1); - 25 -> % sit on chair - send_1211(A, C, 8, 0); - 26 -> % sit out of chair - send_1211(A, C, 8, 2); - 28 -> % respawn object picked (like vehicle_boost) - send_1213(ObjectID, 0); - %~ 30 -> % @todo (phantom ruins block 4, dark god 2 block 1 (fake key block)) - %~ ignore; - _ -> - log("object event ~b", [Action]) - end; - %% @todo Not sure yet. handle(16#1019, _) -> ignore; @@ -1518,6 +1522,7 @@ send_1207() -> send(<< (header(16#1207))/binary, Chunk/binary, Chunk/binary, Chunk/binary, Chunk/binary, Chunk/binary, Chunk/binary >>). %% @todo Object interaction? Figure out. C probably the interaction type. +%% @todo Apparently A would be TargetID/ffffffff, B would be the player LID, C would be the object type? D still completely unknown. send_1211(A, B, C, D) -> send(<< (header(16#1211))/binary, A:32/little-unsigned-integer, B:32/little-unsigned-integer, C:32/little-unsigned-integer, D:32/little-unsigned-integer >>). diff --git a/src/psu/psu_proto.erl b/src/psu/psu_proto.erl index 004385f..dd02e9e 100644 --- a/src/psu/psu_proto.erl +++ b/src/psu/psu_proto.erl @@ -40,11 +40,11 @@ log(Msg, FmtVars) -> %% @spec assert() -> ok %% @doc Log a detailed message when the function is called. --define(ASSERT(), log("assert error in module ~p on line ~p~n", [?MODULE, ?LINE])). +-define(ASSERT(), log("assert error in module ~p on line ~p", [?MODULE, ?LINE])). %% @spec assert(A, B) -> ok %% @doc Log a detailed message when the assertion A =:= B fails. --define(ASSERT_EQ(A, B), if A =:= B -> ok; true -> log("assert error in module ~p on line ~p~n", [?MODULE, ?LINE]) end). +-define(ASSERT_EQ(A, B), if A =:= B -> ok; true -> log("assert error in module ~p on line ~p", [?MODULE, ?LINE]) end). %% @spec parse(Packet) -> Result %% @doc Parse the packet and return a result accordingly. @@ -382,6 +382,189 @@ parse(Size, 16#0c0f, Channel, Data) -> ?ASSERT_EQ(VarI, 0), {counter_quest_options_request, CounterID}; +parse(Size, 16#0f0a, Channel, Data) -> + << _LID:16/little, VarA:16/little, VarB:32/little, VarC:32/little, VarD:32/little, VarE:32/little, + VarF:32/little, VarG:32/little, VarH:32/little, VarI:32/little, BlockID:16/little, ListNb:16/little, + ObjectNb:16/little, _MapID:16/little, ObjectID:16/little, VarJ:16/little, ObjectTargetID:32/little, + ObjectType:16/little, VarK:16/little, ObjectBaseTargetID:16/little, VarL:16/little, _PartyPosOrLID:32/little, + VarN:32/little, VarO:32/little, VarP:32/little, VarQ:32/little, VarR:32/little, VarS:32/little, + VarT:32/little, VarU:32/little, ObjectType2:16/little, EventID:8, VarV:8, VarW:32/little >> = Data, + ?ASSERT_EQ(Size, 112), + ?ASSERT_EQ(Channel, 2), + ?ASSERT_EQ(VarA, 0), + ?ASSERT_EQ(VarB, 0), + ?ASSERT_EQ(VarC, 0), + ?ASSERT_EQ(VarD, 0), + ?ASSERT_EQ(VarE, 0), + ?ASSERT_EQ(VarF, 0), + ?ASSERT_EQ(VarG, 0), + ?ASSERT_EQ(VarH, 0), + ?ASSERT_EQ(VarI, 0), + ?ASSERT_EQ(VarK, 0), + ?ASSERT_EQ(VarP, 16#ffffffff), + ?ASSERT_EQ(VarQ, 16#ffffffff), + ?ASSERT_EQ(VarR, 16#ffffffff), + ?ASSERT_EQ(VarS, 0), + ?ASSERT_EQ(VarT, 0), + ?ASSERT_EQ(VarU, 0), + ?ASSERT_EQ(ObjectType, ObjectType2), + case [ObjectType, EventID] of + [ 5, 13] -> + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + {object_switch_on, ObjectID}; + [ 5, 14] -> + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + {object_switch_off, ObjectID}; + [ 9, 20] -> + %% @todo We probably need to handle it for Airboard Rally. + ignore; %% object_sensor_trigger + [14, 0] -> + ?ASSERT_EQ(ObjectID, 16#ffff), + ?ASSERT_EQ(ObjectTargetID, 16#ffffffff), + ?ASSERT_EQ(ObjectBaseTargetID, 16#ffff), + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + {object_warp_take, BlockID, ListNb, ObjectNb}; + [22, 12] -> + ?ASSERT_EQ(VarJ, 134), + ?ASSERT_EQ(ObjectTargetID, 16#ffffffff), + ?ASSERT_EQ(ObjectBaseTargetID, 16#ffff), + ?ASSERT_EQ(VarL, 116), + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + {object_key_console_enable, ObjectID}; + [22, 23] -> + ?ASSERT_EQ(VarJ, 134), + ?ASSERT_EQ(ObjectTargetID, 16#ffffffff), + ?ASSERT_EQ(ObjectBaseTargetID, 16#ffff), + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + {object_key_console_init, ObjectID}; + [22, 24] -> + ?ASSERT_EQ(VarJ, 134), + ?ASSERT_EQ(ObjectTargetID, 16#ffffffff), + ?ASSERT_EQ(ObjectBaseTargetID, 16#ffff), + ?ASSERT_EQ(VarL, 116), + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + {object_key_console_open_gate, ObjectID}; + [31, 12] -> + ?ASSERT_EQ(VarJ, 134), + ?ASSERT_EQ(ObjectTargetID, 16#ffffffff), + ?ASSERT_EQ(ObjectBaseTargetID, 16#ffff), + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + {object_key_enable, ObjectID}; + [48, 4] -> + ?ASSERT_EQ(VarJ, 134), + ?ASSERT_EQ(VarL, 116), + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + {object_boss_gate_enter, ObjectID}; + [48, 5] -> + ?ASSERT_EQ(VarJ, 134), + ?ASSERT_EQ(VarL, 116), + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + {object_boss_gate_leave, ObjectID}; + [48, 6] -> + ?ASSERT_EQ(VarJ, 134), + ?ASSERT_EQ(VarL, 116), + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + {object_boss_gate_activate, ObjectID}; + [48, 7] -> + ?ASSERT_EQ(VarJ, 134), + ?ASSERT_EQ(VarL, 116), + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + ?ASSERT(), + ignore; %% @todo object_boss_gate_??? + [49, 3] -> + ?ASSERT_EQ(VarJ, 134), + ?ASSERT_EQ(ObjectTargetID, 16#ffffffff), + ?ASSERT_EQ(ObjectBaseTargetID, 16#ffff), + ?ASSERT_EQ(VarL, 116), + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + {object_crystal_activate, ObjectID}; + [51, 1] -> + ?ASSERT_EQ(VarL, 116), + ?ASSERT_EQ(ObjectTargetID, VarN), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + ?ASSERT(), + ignore; %% @todo object_goggle_target_??? + [56, 25] -> + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + {object_chair_sit, ObjectTargetID}; + [56, 26] -> + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + {object_chair_stand, ObjectTargetID}; + [57, 12] -> + ?ASSERT_EQ(VarJ, 134), + ?ASSERT_EQ(ObjectTargetID, 16#ffffffff), + ?ASSERT_EQ(ObjectBaseTargetID, 16#ffff), + ?ASSERT_EQ(VarL, 116), + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + {object_vehicle_boost_enable, ObjectID}; + [57, 28] -> + ?ASSERT_EQ(VarJ, 134), + ?ASSERT_EQ(ObjectTargetID, 16#ffffffff), + ?ASSERT_EQ(ObjectBaseTargetID, 16#ffff), + ?ASSERT_EQ(VarL, 116), + ?ASSERT_EQ(VarN, 16#ffffffff), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + {object_vehicle_boost_respawn, ObjectID}; + [71, 27] -> + ?ASSERT_EQ(VarJ, 134), + ?ASSERT_EQ(ObjectTargetID, VarN), + ?ASSERT_EQ(VarO, 16#ffffffff), + ?ASSERT_EQ(VarV, 1), + ?ASSERT_EQ(VarW, 0), + ?ASSERT(), + ignore; %% @todo object_trap(3rd)_??? + _ -> %% Unhandled actions. + log("unknown 0f0a ObjectType ~p EventID ~p", [ObjectType, EventID]), + ignore + end; + parse(Size, 16#1705, Channel, Data) -> << _LID:16/little, VarA:16/little, VarB:32/little, VarC:32/little, VarD:32/little, VarE:32/little, VarF:32/little, VarG:32/little, VarH:32/little, VarI:32/little >> = Data, ?ASSERT_EQ(Size, 44),