diff --git a/include/maps.hrl b/include/maps.hrl index 1e80ff8..7e5756d 100644 --- a/include/maps.hrl +++ b/include/maps.hrl @@ -383,34 +383,34 @@ {[1003102, 0], [{file, "data/missions/dark-god.1.a.zone-0.nbl"}]}, %~ {[1003102, 1], [{file, "data/missions/dark-god.1.a.zone-1.nbl"}]}, {[1003103, 0], [{file, "data/missions/dark-god.1.s.zone-0.nbl"}]}, - {[1003103, 1], [{file, "data/missions/dark-god.1.s.zone-1.nbl"}]}, + %~ {[1003103, 1], [{file, "data/missions/dark-god.1.s.zone-1.nbl"}]}, %% @todo {[1003104, 0], [{file, "data/missions/dark-god.1.s2.zone-0.nbl"}]}, - {[1003104, 1], [{file, "data/missions/dark-god.1.s2.zone-1.nbl"}]}, - {[1003104, 2], [{file, "data/missions/dark-god.1.s2.zone-2.nbl"}]}, + %~ {[1003104, 1], [{file, "data/missions/dark-god.1.s2.zone-1.nbl"}]}, %% @todo + %~ {[1003104, 2], [{file, "data/missions/dark-god.1.s2.zone-2.nbl"}]}, %% @todo {[1003110, 0], [{file, "data/missions/dark-god.2.c.zone-0.nbl"}]}, - {[1003110, 1], [{file, "data/missions/dark-god.2.c.zone-1.nbl"}]}, + %~ {[1003110, 1], [{file, "data/missions/dark-god.2.c.zone-1.nbl"}]}, %% @todo {[1003111, 0], [{file, "data/missions/dark-god.2.b.zone-0.nbl"}]}, - {[1003111, 1], [{file, "data/missions/dark-god.2.b.zone-1.nbl"}]}, + %~ {[1003111, 1], [{file, "data/missions/dark-god.2.b.zone-1.nbl"}]}, %% @todo {[1003112, 0], [{file, "data/missions/dark-god.2.a.zone-0.nbl"}]}, - {[1003112, 1], [{file, "data/missions/dark-god.2.a.zone-1.nbl"}]}, + %~ {[1003112, 1], [{file, "data/missions/dark-god.2.a.zone-1.nbl"}]}, %% @todo {[1003113, 0], [{file, "data/missions/dark-god.2.s.zone-0.nbl"}]}, %~ {[1003113, 1], [{file, "data/missions/dark-god.2.s.zone-1.nbl"}]}, {[1003114, 0], [{file, "data/missions/dark-god.2.s2.zone-0.nbl"}]}, - {[1003114, 1], [{file, "data/missions/dark-god.2.s2.zone-1.nbl"}]}, - {[1003114, 2], [{file, "data/missions/dark-god.2.s2.zone-2.nbl"}]}, + %~ {[1003114, 1], [{file, "data/missions/dark-god.2.s2.zone-1.nbl"}]}, %% @todo + %~ {[1003114, 2], [{file, "data/missions/dark-god.2.s2.zone-2.nbl"}]}, %% @todo {[1003120, 0], [{file, "data/missions/dark-god.3.c.zone-0.nbl"}]}, - {[1003120, 1], [{file, "data/missions/dark-god.3.c.zone-1.nbl"}]}, + %~ {[1003120, 1], [{file, "data/missions/dark-god.3.c.zone-1.nbl"}]}, %% @todo {[1003121, 0], [{file, "data/missions/dark-god.3.b.zone-0.nbl"}]}, - {[1003121, 1], [{file, "data/missions/dark-god.3.b.zone-1.nbl"}]}, + %~ {[1003121, 1], [{file, "data/missions/dark-god.3.b.zone-1.nbl"}]}, %% @todo {[1003122, 0], [{file, "data/missions/dark-god.3.a.zone-0.nbl"}]}, - {[1003122, 1], [{file, "data/missions/dark-god.3.a.zone-1.nbl"}]}, + %~ {[1003122, 1], [{file, "data/missions/dark-god.3.a.zone-1.nbl"}]}, %% @todo {[1003123, 0], [{file, "data/missions/dark-god.3.s.zone-0.nbl"}]}, - {[1003123, 1], [{file, "data/missions/dark-god.3.s.zone-1.nbl"}]}, + %~ {[1003123, 1], [{file, "data/missions/dark-god.3.s.zone-1.nbl"}]}, %% @todo {[1003124, 0], [{file, "data/missions/dark-god.3.s2.zone-0.nbl"}]}, - {[1003124, 1], [{file, "data/missions/dark-god.3.s2.zone-1.nbl"}]}, - {[1003124, 2], [{file, "data/missions/dark-god.3.s2.zone-2.nbl"}]}, + %~ {[1003124, 1], [{file, "data/missions/dark-god.3.s2.zone-1.nbl"}]}, %% @todo + %~ {[1003124, 2], [{file, "data/missions/dark-god.3.s2.zone-2.nbl"}]}, %% @todo % Phantom Ruins (Linear Line counter) diff --git a/src/psu/psu_game.erl b/src/psu/psu_game.erl index 10d9749..75af2c1 100644 --- a/src/psu/psu_game.erl +++ b/src/psu/psu_game.erl @@ -874,11 +874,16 @@ handle(16#0f0a, Data) -> send_1205(EventID, BlockID, 0), send_1213(ObjectID, 1); 13 -> % floor_button on (also sent when clearing a few of the rooms in black nest) - % 1205 1213 - ignore; + {ok, User} = egs_user_model:read(get(gid)), + {BlockID, EventID} = psu_instance:floor_button_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 - % 1205(same, with 1 as last value) 1213(same, with 0 as last value) - ignore; + %% @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:floor_button_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 diff --git a/src/psu/psu_instance.erl b/src/psu/psu_instance.erl index b18102f..e8746f6 100644 --- a/src/psu/psu_instance.erl +++ b/src/psu/psu_instance.erl @@ -20,7 +20,7 @@ -module(psu_instance). -behavior(gen_server). --export([start_link/1, stop/1, key_event/3, spawn_cleared_event/3, warp_event/5, hit/3]). %% API. +-export([start_link/1, stop/1, floor_button_event/3, key_event/3, spawn_cleared_event/3, warp_event/5, hit/3]). %% API. -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %% gen_server. -include_lib("stdlib/include/qlc.hrl"). @@ -43,6 +43,10 @@ start_link(Zones) -> stop(InstancePid) -> gen_server:call(InstancePid, stop). +%% @todo @spec +floor_button_event(InstancePid, ZoneID, ObjectID) -> + gen_server:call(InstancePid, {floor_button_event, ZoneID, ObjectID}). + %% @todo @spec event(ServerPid, ObjectID, Args) -> Response key_event(InstancePid, ZoneID, ObjectID) -> gen_server:call(InstancePid, {key_event, ZoneID, ObjectID}). @@ -88,6 +92,11 @@ object_init([{box, _Model, Breakable, TrigEventID}|Tail], ZoneID, BlockID, Objec end, object_init(Tail, ZoneID, BlockID, ObjectID + 1, TargetID + 1, ListIndex, ObjectIndex + 1); +%% @doc floor_button: {InstancePid, ZoneID, floor_button, ObjectID +object_init([{floor_button, TrigEventID}|Tail], ZoneID, BlockID, ObjectID, TargetID, ListIndex, ObjectIndex) -> + object_insert(#psu_object{id={self(), ZoneID, floor_button, ObjectID}, instancepid=self(), type=floor_button, args={BlockID, TrigEventID}}), + object_init(Tail, ZoneID, BlockID, ObjectID + 1, TargetID + 1, ListIndex, ObjectIndex + 1); + %% @doc key: {InstancePid, ZoneID, key, ObjectID} object_init([{key, _KeySet, TrigEventID, _ReqEventID}|Tail], ZoneID, BlockID, ObjectID, TargetID, ListIndex, ObjectIndex) -> object_insert(#psu_object{id={self(), ZoneID, key, ObjectID}, instancepid=self(), type=key, args={BlockID, [TrigEventID]}}), @@ -110,25 +119,22 @@ object_init([{warp, DestX, DestY, DestZ, DestDir}|Tail], ZoneID, BlockID, Object object_insert(#psu_object{id={self(), ZoneID, warp, BlockID, ListIndex, ObjectIndex}, instancepid=self(), type=warp, args=#pos{x=DestX, y=DestY, z=DestZ, dir=DestDir}}), object_init(Tail, ZoneID, BlockID, ObjectID, TargetID, ListIndex, ObjectIndex + 1); -%% @doc Ignore for now: crystal -%% @todo Probably have 2 TargetID because of its on/off state. -object_init([crystal|Tail], ZoneID, BlockID, ObjectID, TargetID, ListIndex, ObjectIndex) -> - object_init(Tail, ZoneID, BlockID, ObjectID + 1, TargetID + 2, ListIndex, ObjectIndex + 1); - -%% @doc Ignore for now: floor_button, shoot_button, google_target, trap (all kinds) +%% @doc Ignore for now: shoot_button, google_target, trap (all kinds) object_init([Object|Tail], ZoneID, BlockID, ObjectID, TargetID, ListIndex, ObjectIndex) - when Object =:= floor_button; - Object =:= shoot_button; - Object =:= google_target; + when Object =:= shoot_button; + Object =:= goggle_target; Object =:= trap -> object_init(Tail, ZoneID, BlockID, ObjectID + 1, TargetID + 1, ListIndex, ObjectIndex + 1); +%% @doc Ignore for now: 'exit' (seems to take a TargetID but not an ObjectID +object_init(['exit'|Tail], ZoneID, BlockID, ObjectID, TargetID, ListIndex, ObjectIndex) -> + object_init(Tail, ZoneID, BlockID, ObjectID, TargetID + 1, ListIndex, ObjectIndex + 1); + %% @doc Ignore for now: objects without any ObjectID or TargetID. object_init([Object|Tail], ZoneID, BlockID, ObjectID, TargetID, ListIndex, ObjectIndex) when Object =:= static_model; Object =:= invisible_block; Object =:= entrance; - Object =:= 'exit'; Object =:= label; Object =:= hidden_minimap_section; Object =:= fog; @@ -142,6 +148,10 @@ object_init([_Object|Tail], ZoneID, BlockID, ObjectID, TargetID, ListIndex, Obje %% Event handlers +handle_call({floor_button_event, ZoneID, ObjectID}, _From, State) -> + #psu_object{args=Args} = object_select({self(), ZoneID, floor_button, ObjectID}), + {reply, Args, State}; + handle_call({key_event, ZoneID, ObjectID}, _From, State) -> #psu_object{args=Args} = object_select({self(), ZoneID, key, ObjectID}), {reply, Args, State}; diff --git a/src/psu_parser.erl b/src/psu_parser.erl index bbbf6d9..a488fcc 100644 --- a/src/psu_parser.erl +++ b/src/psu_parser.erl @@ -24,7 +24,7 @@ -define(NBL, "./nbl"). run() -> - List = [parse_zone(QuestID, Filename) || {[QuestID, ZoneID], [{file, Filename}]} <- ?ZONES, ZoneID =:= 0, QuestID < 1100000], + List = [{QuestID, parse_quest(QuestID)} || {QuestID, _} <- ?QUESTS, QuestID < 1100000], Begin = "%% This file is automatically generated by EGS. %% Please do not edit it manually, as you would risk losing your changes. @@ -35,17 +35,20 @@ run() -> Missions = io_lib:format("~s~p~s", [Begin, List, End]), file:write_file("include/missions.hrl", Missions). -parse_zone(QuestID, NblFilename) -> +parse_quest(QuestID) -> + [{ZoneID, parse_zone(Filename)} || {[ZoneQuestID, ZoneID], [{file, Filename}]} <- ?ZONES, ZoneQuestID =:= QuestID]. + +parse_zone(NblFilename) -> Files = nbl_list_files(NblFilename), log("~p", [Files]), nbl_extract_files(NblFilename), - Filename = "set_r3.rel", + Filename = "set_r0.rel", BasePtr = calc_base_ptr(Filename, Files, 0), {ok, << $N, $X, $R, 0, EndRelPtr:32/little-unsigned-integer, AreaIDListRelPtr:32/little-unsigned-integer, 0:32, Data/bits >>} = file:read_file(io_lib:format("tmp/~s", [Filename])), log("header: end ptr(~b) areaid list ptr(~b)", [EndRelPtr, AreaIDListRelPtr]), {ok, _AreaCode, NbMaps, MapsListPtr} = parse_areaid_list(Data, AreaIDListRelPtr - 16), MapList = parse_mapnumbers_list(Data, NbMaps, MapsListPtr - BasePtr - 16), - ObjList = {QuestID, [{0, [{MapID, parse_object_list_headers(BasePtr, Data, NbHeaders, ObjListHeadersPtr - BasePtr - 16)} || {MapID, NbHeaders, ObjListHeadersPtr} <- MapList]}]}, + ObjList = [{MapID, parse_object_list_headers(BasePtr, Data, NbHeaders, ObjListHeadersPtr - BasePtr - 16)} || {MapID, NbHeaders, ObjListHeadersPtr} <- MapList], nbl_cleanup(), ObjList. @@ -128,12 +131,18 @@ parse_object_args(ObjType, Params, Data, Ptr) -> parse_object_args(4, _Params, _Data) -> static_model; -parse_object_args(5, _Params, _Data) -> - floor_button; +%% @todo Many unknowns. +parse_object_args(5, _Params, Data) -> + << _:352, TrigEvent:16/little-unsigned-integer, _/bits >> = Data, + log("floor_button: trigevent(~p)", [TrigEvent]), + {floor_button, TrigEvent}; parse_object_args(6, _Params, _Data) -> fog; +parse_object_args(9, _Params, _Data) -> + menu_prompt; + parse_object_args(10, _Params, _Data) -> invisible_block; @@ -161,6 +170,9 @@ parse_object_args(14, {params, {pos, PosX, PosY, PosZ}, _Rot}, Data) -> parse_object_args(17, _Params, _Data) -> fence; +parse_object_args(18, _Params, _Data) -> + npc; + parse_object_args(20, _Params, _Data) -> door; @@ -219,6 +231,9 @@ parse_object_args(43, _Params, _Data) -> parse_object_args(44, _Params, _Data) -> trap; +parse_object_args(45, _Params, _Data) -> + npc_talk; + parse_object_args(48, _Params, _Data) -> boss_gate; @@ -234,6 +249,10 @@ parse_object_args(51, _Params, _Data) -> parse_object_args(53, _Params, _Data) -> label; +%% @todo Find out! Only used in the cake sisters shop so far. +parse_object_args(59, _Params, _Data) -> + unknown_object; + parse_object_args(62, _Params, _Data) -> pp_cube;