|
|
@ -36,10 +36,11 @@ start_link(Port) ->
|
|
|
|
%% @spec cleanup(Pid) -> ok
|
|
|
|
%% @spec cleanup(Pid) -> ok
|
|
|
|
%% @doc Cleanup the data associated with the failing process.
|
|
|
|
%% @doc Cleanup the data associated with the failing process.
|
|
|
|
cleanup(Pid) ->
|
|
|
|
cleanup(Pid) ->
|
|
|
|
User = egs_db:users_select_by_pid(Pid),
|
|
|
|
{ok, User} = egs_user_model:read({pid, Pid}),
|
|
|
|
egs_db:users_delete(User#users.gid),
|
|
|
|
egs_user_model:delete(User#egs_user_model.id),
|
|
|
|
lists:foreach(fun(Other) -> Other#users.pid ! {psu_player_unspawn, User} end, egs_db:users_select_others_in_area(User)),
|
|
|
|
{ok, List} = egs_user_model:select({neighbors, User}),
|
|
|
|
io:format("game (~p): quit~n", [User#users.gid]).
|
|
|
|
lists:foreach(fun(Other) -> Other#egs_user_model.pid ! {psu_player_unspawn, User} end, List),
|
|
|
|
|
|
|
|
io:format("game (~p): quit~n", [User#egs_user_model.id]).
|
|
|
|
|
|
|
|
|
|
|
|
%% @doc Listen for connections.
|
|
|
|
%% @doc Listen for connections.
|
|
|
|
|
|
|
|
|
|
|
@ -88,23 +89,23 @@ process() ->
|
|
|
|
|
|
|
|
|
|
|
|
process_handle(16#020d, << GID:32/little-unsigned-integer, Auth:32/bits, _/bits >>) ->
|
|
|
|
process_handle(16#020d, << GID:32/little-unsigned-integer, Auth:32/bits, _/bits >>) ->
|
|
|
|
CSocket = get(socket),
|
|
|
|
CSocket = get(socket),
|
|
|
|
case egs_db:users_select(GID) of
|
|
|
|
case egs_user_model:read(GID) of
|
|
|
|
error ->
|
|
|
|
{error, badarg} ->
|
|
|
|
log("can't find user, closing"),
|
|
|
|
log("can't find user, closing"),
|
|
|
|
ssl:close(CSocket);
|
|
|
|
ssl:close(CSocket);
|
|
|
|
User ->
|
|
|
|
{ok, User} ->
|
|
|
|
case User#users.auth of
|
|
|
|
case User#egs_user_model.state of
|
|
|
|
Auth ->
|
|
|
|
{wait_for_authentication, Auth} ->
|
|
|
|
put(gid, GID),
|
|
|
|
put(gid, GID),
|
|
|
|
log("auth success"),
|
|
|
|
log("auth success"),
|
|
|
|
LID = 1 + egs_db:next(lobby) rem 1023,
|
|
|
|
LID = 1 + egs_db:next(lobby) rem 1023,
|
|
|
|
Time = calendar:datetime_to_gregorian_seconds(calendar:universal_time()),
|
|
|
|
Time = calendar:datetime_to_gregorian_seconds(calendar:universal_time()),
|
|
|
|
egs_db:users_insert(#users{gid=GID, pid=self(), socket=CSocket, auth=success, time=Time, folder=User#users.folder, lid=LID}),
|
|
|
|
egs_user_model:write(#egs_user_model{id=GID, pid=self(), socket=CSocket, state=authenticated, time=Time, folder=User#egs_user_model.folder, lid=LID}),
|
|
|
|
send_0d05(),
|
|
|
|
send_0d05(),
|
|
|
|
?MODULE:char_select();
|
|
|
|
?MODULE:char_select();
|
|
|
|
_ ->
|
|
|
|
_ ->
|
|
|
|
log("quit, auth failed"),
|
|
|
|
log("quit, auth failed"),
|
|
|
|
egs_db:users_delete(GID),
|
|
|
|
egs_user_model:delete(GID),
|
|
|
|
ssl:close(CSocket)
|
|
|
|
ssl:close(CSocket)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end;
|
|
|
|
end;
|
|
|
@ -134,7 +135,7 @@ char_select() ->
|
|
|
|
?MODULE:char_select();
|
|
|
|
?MODULE:char_select();
|
|
|
|
{error, closed} ->
|
|
|
|
{error, closed} ->
|
|
|
|
log("quit"),
|
|
|
|
log("quit"),
|
|
|
|
egs_db:users_delete(get(gid))
|
|
|
|
egs_user_model:delete(get(gid))
|
|
|
|
end.
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
%% @doc Character selection handler.
|
|
|
|
%% @doc Character selection handler.
|
|
|
@ -154,17 +155,17 @@ char_select_handle(16#0d02, << Number:32/little-unsigned-integer, Char/bits >>)
|
|
|
|
Appearance = psu_appearance:binary_to_tuple(Race, AppearanceBin),
|
|
|
|
Appearance = psu_appearance:binary_to_tuple(Race, AppearanceBin),
|
|
|
|
psu_appearance:validate_char_create(Race, Gender, Appearance),
|
|
|
|
psu_appearance:validate_char_create(Race, Gender, Appearance),
|
|
|
|
% end of check, continue doing it wrong past that point for now
|
|
|
|
% end of check, continue doing it wrong past that point for now
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
_ = file:make_dir(io_lib:format("save/~s", [User#users.folder])),
|
|
|
|
_ = file:make_dir(io_lib:format("save/~s", [User#egs_user_model.folder])),
|
|
|
|
file:write_file(io_lib:format("save/~s/~b-character", [User#users.folder, Number]), Char),
|
|
|
|
file:write_file(io_lib:format("save/~s/~b-character", [User#egs_user_model.folder, Number]), Char),
|
|
|
|
file:write_file(io_lib:format("save/~s/~b-character.options", [User#users.folder, Number]), << 0:128, 4, 0:56 >>), % default 0 to everything except brightness 4
|
|
|
|
file:write_file(io_lib:format("save/~s/~b-character.options", [User#egs_user_model.folder, Number]), << 0:128, 4, 0:56 >>), % default 0 to everything except brightness 4
|
|
|
|
char_select_load(Number);
|
|
|
|
char_select_load(Number);
|
|
|
|
|
|
|
|
|
|
|
|
%% @doc Character selection screen request.
|
|
|
|
%% @doc Character selection screen request.
|
|
|
|
|
|
|
|
|
|
|
|
char_select_handle(16#0d06, _) ->
|
|
|
|
char_select_handle(16#0d06, _) ->
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
send_0d03(data_load(User#users.folder, 0), data_load(User#users.folder, 1), data_load(User#users.folder, 2), data_load(User#users.folder, 3)),
|
|
|
|
send_0d03(data_load(User#egs_user_model.folder, 0), data_load(User#egs_user_model.folder, 1), data_load(User#egs_user_model.folder, 2), data_load(User#egs_user_model.folder, 3)),
|
|
|
|
?MODULE:char_select();
|
|
|
|
?MODULE:char_select();
|
|
|
|
|
|
|
|
|
|
|
|
%% @doc Silently ignore packet 0818. Gives CPU/GPU information.
|
|
|
|
%% @doc Silently ignore packet 0818. Gives CPU/GPU information.
|
|
|
@ -182,8 +183,8 @@ char_select_handle(Command, _) ->
|
|
|
|
%% The default entry point currently is 4th floor, Linear Line counter.
|
|
|
|
%% The default entry point currently is 4th floor, Linear Line counter.
|
|
|
|
|
|
|
|
|
|
|
|
char_select_load(Number) ->
|
|
|
|
char_select_load(Number) ->
|
|
|
|
OldUser = egs_db:users_select(get(gid)),
|
|
|
|
{ok, OldUser} = egs_user_model:read(get(gid)),
|
|
|
|
[{status, 1}, {char, CharBin}, {options, OptionsBin}] = data_load(OldUser#users.folder, Number),
|
|
|
|
[{status, 1}, {char, CharBin}, {options, OptionsBin}] = data_load(OldUser#egs_user_model.folder, Number),
|
|
|
|
<< Name:512/bits, RaceBin:8, GenderBin:8, ClassBin:8, AppearanceBin:776/bits, _/bits >> = CharBin,
|
|
|
|
<< Name:512/bits, RaceBin:8, GenderBin:8, ClassBin:8, AppearanceBin:776/bits, _/bits >> = CharBin,
|
|
|
|
psu_characters:validate_name(Name), % TODO: don't validate name when loading character, do it at creation
|
|
|
|
psu_characters:validate_name(Name), % TODO: don't validate name when loading character, do it at creation
|
|
|
|
Race = psu_characters:race_binary_to_atom(RaceBin),
|
|
|
|
Race = psu_characters:race_binary_to_atom(RaceBin),
|
|
|
@ -192,8 +193,8 @@ char_select_load(Number) ->
|
|
|
|
Appearance = psu_appearance:binary_to_tuple(Race, AppearanceBin),
|
|
|
|
Appearance = psu_appearance:binary_to_tuple(Race, AppearanceBin),
|
|
|
|
Options = psu_characters:options_binary_to_tuple(OptionsBin),
|
|
|
|
Options = psu_characters:options_binary_to_tuple(OptionsBin),
|
|
|
|
Character = #characters{slot=Number, name=Name, race=Race, gender=Gender, class=Class, appearance=Appearance, options=Options}, % TODO: temporary set the slot here, won't be needed later
|
|
|
|
Character = #characters{slot=Number, name=Name, race=Race, gender=Gender, class=Class, appearance=Appearance, options=Options}, % TODO: temporary set the slot here, won't be needed later
|
|
|
|
User = OldUser#users{character=Character, pos=#pos{x=0.0, y=0.0, z=0.0, dir=0.0}},
|
|
|
|
User = OldUser#egs_user_model{state=online, character=Character, area=#psu_area{questid=undefined, zoneid=undefined, mapid=undefined}, pos=#pos{x=0.0, y=0.0, z=0.0, dir=0.0}},
|
|
|
|
egs_db:users_insert(User),
|
|
|
|
egs_user_model:write(User),
|
|
|
|
char_load(User),
|
|
|
|
char_load(User),
|
|
|
|
send_021b(),
|
|
|
|
send_021b(),
|
|
|
|
area_load(1100000, 0, 4, 5),
|
|
|
|
area_load(1100000, 0, 4, 5),
|
|
|
@ -219,7 +220,7 @@ char_load(User) ->
|
|
|
|
% 0246
|
|
|
|
% 0246
|
|
|
|
send_0a0a(),
|
|
|
|
send_0a0a(),
|
|
|
|
send_1006(5),
|
|
|
|
send_1006(5),
|
|
|
|
send_1005((User#users.character)#characters.name),
|
|
|
|
send_1005((User#egs_user_model.character)#characters.name),
|
|
|
|
send_1006(12),
|
|
|
|
send_1006(12),
|
|
|
|
send_0210(),
|
|
|
|
send_0210(),
|
|
|
|
send_0222(),
|
|
|
|
send_0222(),
|
|
|
@ -232,15 +233,17 @@ char_load(User) ->
|
|
|
|
%% @doc Load the given map as a mission counter.
|
|
|
|
%% @doc Load the given map as a mission counter.
|
|
|
|
|
|
|
|
|
|
|
|
counter_load(QuestID, ZoneID, MapID, EntryID) ->
|
|
|
|
counter_load(QuestID, ZoneID, MapID, EntryID) ->
|
|
|
|
OldUser = egs_db:users_select(get(gid)),
|
|
|
|
{ok, OldUser} = egs_user_model:read(get(gid)),
|
|
|
|
User = OldUser#users{areatype=counter, questid=QuestID, zoneid=ZoneID, mapid=MapID, entryid=EntryID,
|
|
|
|
OldArea = OldUser#egs_user_model.area,
|
|
|
|
savedquestid=OldUser#users.questid, savedzoneid=OldUser#users.zoneid, savedmapid=ZoneID, savedentryid=MapID},
|
|
|
|
User = OldUser#egs_user_model{areatype=counter, area={psu_area, QuestID, ZoneID, MapID}, entryid=EntryID, prev_entryid=MapID,
|
|
|
|
egs_db:users_insert(User),
|
|
|
|
prev_area={psu_area, OldArea#psu_area.questid, OldArea#psu_area.zoneid, ZoneID}},
|
|
|
|
|
|
|
|
egs_user_model:write(User),
|
|
|
|
AreaName = "Mission counter",
|
|
|
|
AreaName = "Mission counter",
|
|
|
|
QuestFile = "data/lobby/counter.quest.nbl",
|
|
|
|
QuestFile = "data/lobby/counter.quest.nbl",
|
|
|
|
ZoneFile = "data/lobby/counter.zone.nbl",
|
|
|
|
ZoneFile = "data/lobby/counter.zone.nbl",
|
|
|
|
% broadcast unspawn to other people
|
|
|
|
% broadcast unspawn to other people
|
|
|
|
lists:foreach(fun(Other) -> Other#users.pid ! {psu_player_unspawn, User} end, egs_db:users_select_others_in_area(OldUser)),
|
|
|
|
{ok, UnspawnList} = egs_user_model:select({neighbors, OldUser}),
|
|
|
|
|
|
|
|
lists:foreach(fun(Other) -> Other#egs_user_model.pid ! {psu_player_unspawn, User} end, UnspawnList),
|
|
|
|
% load counter
|
|
|
|
% load counter
|
|
|
|
send_0c00(16#7fffffff),
|
|
|
|
send_0c00(16#7fffffff),
|
|
|
|
send_020e(QuestFile),
|
|
|
|
send_020e(QuestFile),
|
|
|
@ -306,14 +309,14 @@ area_get_season(QuestID) ->
|
|
|
|
%% @doc Load the given map as a standard lobby.
|
|
|
|
%% @doc Load the given map as a standard lobby.
|
|
|
|
|
|
|
|
|
|
|
|
area_load(QuestID, ZoneID, MapID, EntryID) ->
|
|
|
|
area_load(QuestID, ZoneID, MapID, EntryID) ->
|
|
|
|
OldUser = egs_db:users_select(get(gid)),
|
|
|
|
{ok, OldUser} = egs_user_model:read(get(gid)),
|
|
|
|
[{type, AreaType}, {file, QuestFile}|StartInfo] = proplists:get_value(QuestID, ?QUESTS, [{type, undefined}, {file, undefined}]),
|
|
|
|
[{type, AreaType}, {file, QuestFile}|StartInfo] = proplists:get_value(QuestID, ?QUESTS, [{type, undefined}, {file, undefined}]),
|
|
|
|
[IsStart, InstanceID, RealZoneID, RealMapID, RealEntryID] = case AreaType of
|
|
|
|
[IsStart, InstanceID, RealZoneID, RealMapID, RealEntryID] = case AreaType of
|
|
|
|
mission ->
|
|
|
|
mission ->
|
|
|
|
if ZoneID =:= 65535 ->
|
|
|
|
if ZoneID =:= 65535 ->
|
|
|
|
[{start, [TmpZoneID, TmpMapID, TmpEntryID]}] = StartInfo,
|
|
|
|
[{start, [TmpZoneID, TmpMapID, TmpEntryID]}] = StartInfo,
|
|
|
|
[true, OldUser#users.gid, TmpZoneID, TmpMapID, TmpEntryID];
|
|
|
|
[true, OldUser#egs_user_model.id, TmpZoneID, TmpMapID, TmpEntryID];
|
|
|
|
true -> [false, OldUser#users.gid, ZoneID, MapID, EntryID]
|
|
|
|
true -> [false, OldUser#egs_user_model.id, ZoneID, MapID, EntryID]
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
myroom ->
|
|
|
|
myroom ->
|
|
|
|
if ZoneID =:= 0 ->
|
|
|
|
if ZoneID =:= 0 ->
|
|
|
@ -329,8 +332,8 @@ area_load(QuestID, ZoneID, MapID, EntryID) ->
|
|
|
|
true ->
|
|
|
|
true ->
|
|
|
|
[{name, AreaName}] = proplists:get_value([QuestID, RealMapID], ?MAPS, [{name, "dammy"}])
|
|
|
|
[{name, AreaName}] = proplists:get_value([QuestID, RealMapID], ?MAPS, [{name, "dammy"}])
|
|
|
|
end,
|
|
|
|
end,
|
|
|
|
User = OldUser#users{instanceid=InstanceID, areatype=AreaType, questid=QuestID, zoneid=RealZoneID, mapid=RealMapID, entryid=RealEntryID},
|
|
|
|
User = OldUser#egs_user_model{instanceid=InstanceID, areatype=AreaType, area={psu_area, QuestID, RealZoneID, RealMapID}, entryid=RealEntryID},
|
|
|
|
egs_db:users_insert(User),
|
|
|
|
egs_user_model:write(User),
|
|
|
|
%% @todo Don't recalculate SetID when leaving the mission and back (this changes the spawns).
|
|
|
|
%% @todo Don't recalculate SetID when leaving the mission and back (this changes the spawns).
|
|
|
|
SetID = if IsStart =:= true -> crypto:rand_uniform(0, 4); true -> 0 end,
|
|
|
|
SetID = if IsStart =:= true -> crypto:rand_uniform(0, 4); true -> 0 end,
|
|
|
|
if IsStart =:= true -> % initialize the mission
|
|
|
|
if IsStart =:= true -> % initialize the mission
|
|
|
@ -340,28 +343,32 @@ area_load(QuestID, ZoneID, MapID, EntryID) ->
|
|
|
|
area_load(AreaType, IsStart, SetID, OldUser, User, QuestFile, ZoneFile, AreaName).
|
|
|
|
area_load(AreaType, IsStart, SetID, OldUser, User, QuestFile, ZoneFile, AreaName).
|
|
|
|
|
|
|
|
|
|
|
|
area_load(AreaType, IsStart, SetID, OldUser, User, QuestFile, ZoneFile, AreaName) ->
|
|
|
|
area_load(AreaType, IsStart, SetID, OldUser, User, QuestFile, ZoneFile, AreaName) ->
|
|
|
|
QuestChange = if OldUser#users.questid /= User#users.questid, QuestFile /= undefined -> true; true -> false end,
|
|
|
|
#psu_area{questid=OldQuestID, zoneid=OldZoneID} = OldUser#egs_user_model.area,
|
|
|
|
|
|
|
|
#psu_area{questid=QuestID, zoneid=ZoneID} = User#egs_user_model.area,
|
|
|
|
|
|
|
|
QuestChange = if OldQuestID /= QuestID, QuestFile /= undefined -> true; true -> false end,
|
|
|
|
if ZoneFile =:= undefined ->
|
|
|
|
if ZoneFile =:= undefined ->
|
|
|
|
ZoneChange = false;
|
|
|
|
ZoneChange = false;
|
|
|
|
true ->
|
|
|
|
true ->
|
|
|
|
ZoneChange = if OldUser#users.questid =:= User#users.questid, OldUser#users.zoneid =:= User#users.zoneid -> false; true -> true end
|
|
|
|
ZoneChange = if OldQuestID =:= QuestID, OldZoneID =:= ZoneID -> false; true -> true end
|
|
|
|
end,
|
|
|
|
end,
|
|
|
|
[{status, IsSeasonal}, {season, SeasonID}] = area_get_season(User#users.questid),
|
|
|
|
[{status, IsSeasonal}, {season, SeasonID}] = area_get_season(QuestID),
|
|
|
|
% broadcast spawn and unspawn to other people
|
|
|
|
% broadcast spawn and unspawn to other people
|
|
|
|
lists:foreach(fun(Other) -> Other#users.pid ! {psu_player_unspawn, User} end, egs_db:users_select_others_in_area(OldUser)),
|
|
|
|
{ok, UnspawnList} = egs_user_model:select({neighbors, OldUser}),
|
|
|
|
|
|
|
|
{ok, SpawnList} = egs_user_model:select({neighbors, User}),
|
|
|
|
|
|
|
|
lists:foreach(fun(Other) -> Other#egs_user_model.pid ! {psu_player_unspawn, User} end, UnspawnList),
|
|
|
|
if AreaType =:= lobby ->
|
|
|
|
if AreaType =:= lobby ->
|
|
|
|
lists:foreach(fun(Other) -> Other#users.pid ! {psu_player_spawn, User} end, egs_db:users_select_others_in_area(User));
|
|
|
|
lists:foreach(fun(Other) -> Other#egs_user_model.pid ! {psu_player_spawn, User} end, SpawnList);
|
|
|
|
true -> ignore
|
|
|
|
true -> ignore
|
|
|
|
end,
|
|
|
|
end,
|
|
|
|
% load area
|
|
|
|
% load area
|
|
|
|
if QuestChange =:= true ->
|
|
|
|
if QuestChange =:= true ->
|
|
|
|
% reload the character if entering or leaving the room quest
|
|
|
|
% reload the character if entering or leaving the room quest
|
|
|
|
if OldUser#users.questid =:= 1120000; User#users.questid =:= 1120000 ->
|
|
|
|
if OldQuestID =:= 1120000; QuestID =:= 1120000 ->
|
|
|
|
char_load(User);
|
|
|
|
char_load(User);
|
|
|
|
true -> ignore
|
|
|
|
true -> ignore
|
|
|
|
end,
|
|
|
|
end,
|
|
|
|
% load new quest
|
|
|
|
% load new quest
|
|
|
|
send_0c00(User#users.questid),
|
|
|
|
send_0c00(QuestID),
|
|
|
|
send_020e(QuestFile);
|
|
|
|
send_020e(QuestFile);
|
|
|
|
true -> ignore
|
|
|
|
true -> ignore
|
|
|
|
end,
|
|
|
|
end,
|
|
|
@ -381,8 +388,8 @@ area_load(AreaType, IsStart, SetID, OldUser, User, QuestFile, ZoneFile, AreaName
|
|
|
|
send_020f(ZoneFile, SetID, SeasonID);
|
|
|
|
send_020f(ZoneFile, SetID, SeasonID);
|
|
|
|
true -> ignore
|
|
|
|
true -> ignore
|
|
|
|
end,
|
|
|
|
end,
|
|
|
|
send_0205(User#users.zoneid, User#users.mapid, User#users.entryid, IsSeasonal),
|
|
|
|
send_0205(ZoneID, (User#egs_user_model.area)#psu_area.mapid, User#egs_user_model.entryid, IsSeasonal),
|
|
|
|
send_100e(User#users.questid, User#users.zoneid, User#users.mapid, AreaName, 16#ffffffff),
|
|
|
|
send_100e(QuestID, ZoneID, (User#egs_user_model.area)#psu_area.mapid, AreaName, 16#ffffffff),
|
|
|
|
if AreaType =:= mission ->
|
|
|
|
if AreaType =:= mission ->
|
|
|
|
send_0215(0),
|
|
|
|
send_0215(0),
|
|
|
|
if IsStart =:= true ->
|
|
|
|
if IsStart =:= true ->
|
|
|
@ -419,7 +426,7 @@ area_load(AreaType, IsStart, SetID, OldUser, User, QuestFile, ZoneFile, AreaName
|
|
|
|
send_0a06();
|
|
|
|
send_0a06();
|
|
|
|
true -> ignore
|
|
|
|
true -> ignore
|
|
|
|
end,
|
|
|
|
end,
|
|
|
|
send_0233(egs_db:users_select_others_in_area(User)),
|
|
|
|
send_0233(SpawnList),
|
|
|
|
send_0208(),
|
|
|
|
send_0208(),
|
|
|
|
send_0236().
|
|
|
|
send_0236().
|
|
|
|
|
|
|
|
|
|
|
@ -445,10 +452,12 @@ loop(SoFar) ->
|
|
|
|
?MODULE:loop(SoFar);
|
|
|
|
?MODULE:loop(SoFar);
|
|
|
|
{psu_player_spawn, _Spawn} ->
|
|
|
|
{psu_player_spawn, _Spawn} ->
|
|
|
|
% Should be something along the lines of 203 201 204 or something.
|
|
|
|
% Should be something along the lines of 203 201 204 or something.
|
|
|
|
send_0233(egs_db:users_select_others_in_area(egs_db:users_select(get(gid)))),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
|
|
|
|
{ok, SpawnList} = egs_user_model:select({neighbors, User}),
|
|
|
|
|
|
|
|
send_0233(SpawnList),
|
|
|
|
?MODULE:loop(SoFar);
|
|
|
|
?MODULE:loop(SoFar);
|
|
|
|
{psu_player_unspawn, Spawn} ->
|
|
|
|
{psu_player_unspawn, Spawn} ->
|
|
|
|
send_0204(Spawn#users.gid, Spawn#users.lid, 5),
|
|
|
|
send_0204(Spawn#egs_user_model.id, Spawn#egs_user_model.lid, 5),
|
|
|
|
?MODULE:loop(SoFar);
|
|
|
|
?MODULE:loop(SoFar);
|
|
|
|
{psu_warp, QuestID, ZoneID, MapID, EntryID} ->
|
|
|
|
{psu_warp, QuestID, ZoneID, MapID, EntryID} ->
|
|
|
|
area_load(QuestID, ZoneID, MapID, EntryID),
|
|
|
|
area_load(QuestID, ZoneID, MapID, EntryID),
|
|
|
@ -484,9 +493,9 @@ broadcast(16#0503, Orig) ->
|
|
|
|
<< _:424, Dir:24/little-unsigned-integer, _PrevCoords:96, X:32/little-float, Y:32/little-float, Z:32/little-float,
|
|
|
|
<< _:424, Dir:24/little-unsigned-integer, _PrevCoords:96, X:32/little-float, Y:32/little-float, Z:32/little-float,
|
|
|
|
QuestID:32/little-unsigned-integer, ZoneID:32/little-unsigned-integer, MapID:32/little-unsigned-integer, EntryID:32/little-unsigned-integer, _:32 >> = Orig,
|
|
|
|
QuestID:32/little-unsigned-integer, ZoneID:32/little-unsigned-integer, MapID:32/little-unsigned-integer, EntryID:32/little-unsigned-integer, _:32 >> = Orig,
|
|
|
|
FloatDir = Dir / 46603.375,
|
|
|
|
FloatDir = Dir / 46603.375,
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
NewUser = User#users{pos=#pos{x=X, y=Y, z=Z, dir=FloatDir}, questid=QuestID, zoneid=ZoneID, mapid=MapID, entryid=EntryID},
|
|
|
|
NewUser = User#egs_user_model{pos=#pos{x=X, y=Y, z=Z, dir=FloatDir}, area=#psu_area{questid=QuestID, zoneid=ZoneID, mapid=MapID}, entryid=EntryID},
|
|
|
|
egs_db:users_insert(NewUser),
|
|
|
|
egs_user_model:write(NewUser),
|
|
|
|
broadcast(default, Orig);
|
|
|
|
broadcast(default, Orig);
|
|
|
|
|
|
|
|
|
|
|
|
%% @doc Stand still broadcast handler. Save the position and then dispatch it.
|
|
|
|
%% @doc Stand still broadcast handler. Save the position and then dispatch it.
|
|
|
@ -496,9 +505,9 @@ broadcast(16#0514, Orig) ->
|
|
|
|
QuestID:32/little-unsigned-integer, ZoneID:32/little-unsigned-integer,
|
|
|
|
QuestID:32/little-unsigned-integer, ZoneID:32/little-unsigned-integer,
|
|
|
|
MapID:32/little-unsigned-integer, EntryID:32/little-unsigned-integer, _/bits >> = Orig,
|
|
|
|
MapID:32/little-unsigned-integer, EntryID:32/little-unsigned-integer, _/bits >> = Orig,
|
|
|
|
FloatDir = Dir / 46603.375,
|
|
|
|
FloatDir = Dir / 46603.375,
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
NewUser = User#users{pos=#pos{x=X, y=Y, z=Z, dir=FloatDir}, questid=QuestID, zoneid=ZoneID, mapid=MapID, entryid=EntryID},
|
|
|
|
NewUser = User#egs_user_model{pos=#pos{x=X, y=Y, z=Z, dir=FloatDir}, area=#psu_area{questid=QuestID, zoneid=ZoneID, mapid=MapID}, entryid=EntryID},
|
|
|
|
egs_db:users_insert(NewUser),
|
|
|
|
egs_user_model:write(NewUser),
|
|
|
|
broadcast(default, Orig);
|
|
|
|
broadcast(default, Orig);
|
|
|
|
|
|
|
|
|
|
|
|
%% @doc Default broadcast handler. Dispatch the command to everyone.
|
|
|
|
%% @doc Default broadcast handler. Dispatch the command to everyone.
|
|
|
@ -516,14 +525,15 @@ broadcast(Command, Orig)
|
|
|
|
Command =:= default ->
|
|
|
|
Command =:= default ->
|
|
|
|
<< _:32, A:64/bits, _:64, B:192/bits, _:64, C/bits >> = Orig,
|
|
|
|
<< _:32, A:64/bits, _:64, B:192/bits, _:64, C/bits >> = Orig,
|
|
|
|
GID = get(gid),
|
|
|
|
GID = get(gid),
|
|
|
|
case egs_db:users_select(GID) of
|
|
|
|
case egs_user_model:read(GID) of
|
|
|
|
error ->
|
|
|
|
{error, _Reason} ->
|
|
|
|
ignore;
|
|
|
|
ignore;
|
|
|
|
Self ->
|
|
|
|
{ok, Self} ->
|
|
|
|
LID = Self#users.lid,
|
|
|
|
LID = Self#egs_user_model.lid,
|
|
|
|
Packet = << A/binary, 16#00011300:32, GID:32/little-unsigned-integer, B/binary,
|
|
|
|
Packet = << A/binary, 16#00011300:32, GID:32/little-unsigned-integer, B/binary,
|
|
|
|
GID:32/little-unsigned-integer, LID:32/little-unsigned-integer, C/binary >>,
|
|
|
|
GID:32/little-unsigned-integer, LID:32/little-unsigned-integer, C/binary >>,
|
|
|
|
lists:foreach(fun(User) -> User#users.pid ! {psu_broadcast, Packet} end, egs_db:users_select_others_in_area(Self))
|
|
|
|
{ok, SpawnList} = egs_user_model:select({neighbors, Self}),
|
|
|
|
|
|
|
|
lists:foreach(fun(User) -> User#egs_user_model.pid ! {psu_broadcast, Packet} end, SpawnList)
|
|
|
|
end.
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
%% @doc Movement (non-broadcast) handler. Do nothing.
|
|
|
|
%% @doc Movement (non-broadcast) handler. Do nothing.
|
|
|
@ -647,10 +657,11 @@ handle(16#021f, << Uni:32/little-unsigned-integer, _/bits >>) ->
|
|
|
|
send_0230(),
|
|
|
|
send_0230(),
|
|
|
|
% 0220
|
|
|
|
% 0220
|
|
|
|
% force reloading the character and data files (hack)
|
|
|
|
% force reloading the character and data files (hack)
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
NewRow = User#users{questid=1120000, zoneid=undefined},
|
|
|
|
Area = User#egs_user_model.area,
|
|
|
|
egs_db:users_insert(NewRow),
|
|
|
|
NewRow = User#egs_user_model{area=Area#psu_area{questid=1120000, zoneid=undefined}},
|
|
|
|
area_load(User#users.questid, User#users.zoneid, User#users.mapid, User#users.entryid)
|
|
|
|
egs_user_model:write(NewRow),
|
|
|
|
|
|
|
|
area_load(Area#psu_area.questid, Area#psu_area.zoneid, Area#psu_area.mapid, User#egs_user_model.entryid)
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
%% @doc Shortcut changes handler. Do nothing.
|
|
|
|
%% @doc Shortcut changes handler. Do nothing.
|
|
|
@ -665,18 +676,19 @@ handle(16#0302, _) ->
|
|
|
|
%% @todo Only broadcast to people in the same map.
|
|
|
|
%% @todo Only broadcast to people in the same map.
|
|
|
|
|
|
|
|
|
|
|
|
handle(16#0304, Data) ->
|
|
|
|
handle(16#0304, Data) ->
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
case get(version) of
|
|
|
|
case get(version) of
|
|
|
|
0 -> % AOTI v2.000
|
|
|
|
0 -> % AOTI v2.000
|
|
|
|
<< _:64, Modifiers:128/bits, Message/bits >> = Data;
|
|
|
|
<< _:64, Modifiers:128/bits, Message/bits >> = Data;
|
|
|
|
_ -> % Above
|
|
|
|
_ -> % Above
|
|
|
|
<< _:64, Modifiers:128/bits, _:512, Message/bits >> = Data
|
|
|
|
<< _:64, Modifiers:128/bits, _:512, Message/bits >> = Data
|
|
|
|
end,
|
|
|
|
end,
|
|
|
|
[LogName|_] = re:split((User#users.character)#characters.name, "\\0\\0", [{return, binary}]),
|
|
|
|
[LogName|_] = re:split((User#egs_user_model.character)#characters.name, "\\0\\0", [{return, binary}]),
|
|
|
|
[TmpMessage|_] = re:split(Message, "\\0\\0", [{return, binary}]),
|
|
|
|
[TmpMessage|_] = re:split(Message, "\\0\\0", [{return, binary}]),
|
|
|
|
LogMessage = re:replace(TmpMessage, "\\n", " ", [global, {return, binary}]),
|
|
|
|
LogMessage = re:replace(TmpMessage, "\\n", " ", [global, {return, binary}]),
|
|
|
|
log("chat from ~s: ~s", [[re:replace(LogName, "\\0", "", [global, {return, binary}])], [re:replace(LogMessage, "\\0", "", [global, {return, binary}])]]),
|
|
|
|
log("chat from ~s: ~s", [[re:replace(LogName, "\\0", "", [global, {return, binary}])], [re:replace(LogMessage, "\\0", "", [global, {return, binary}])]]),
|
|
|
|
lists:foreach(fun(X) -> X#users.pid ! {psu_chat, get(gid), (User#users.character)#characters.name, Modifiers, Message} end, egs_db:users_select_all());
|
|
|
|
{ok, List} = egs_user_model:select(all),
|
|
|
|
|
|
|
|
lists:foreach(fun(X) -> X#egs_user_model.pid ! {psu_chat, get(gid), (User#egs_user_model.character)#characters.name, Modifiers, Message} end, List);
|
|
|
|
|
|
|
|
|
|
|
|
%% @todo Handle this packet properly.
|
|
|
|
%% @todo Handle this packet properly.
|
|
|
|
%% @todo Spawn cleared response event shouldn't be handled following this packet but when we see the spawn actually dead HP-wise.
|
|
|
|
%% @todo Spawn cleared response event shouldn't be handled following this packet but when we see the spawn actually dead HP-wise.
|
|
|
@ -686,8 +698,8 @@ handle(16#0402, Data) ->
|
|
|
|
case Type of
|
|
|
|
case Type of
|
|
|
|
7 -> % spawn cleared @todo 1201 sent back with same values apparently, but not always
|
|
|
|
7 -> % spawn cleared @todo 1201 sent back with same values apparently, but not always
|
|
|
|
log("cleared spawn ~b", [SpawnID]),
|
|
|
|
log("cleared spawn ~b", [SpawnID]),
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
[EventID, BlockID] = psu_missions:spawn_cleared(User#users.instanceid, SpawnID),
|
|
|
|
[EventID, BlockID] = psu_missions:spawn_cleared(User#egs_user_model.instanceid, SpawnID),
|
|
|
|
if EventID =:= false -> ignore;
|
|
|
|
if EventID =:= false -> ignore;
|
|
|
|
true -> send_1205(EventID, BlockID, 0)
|
|
|
|
true -> send_1205(EventID, BlockID, 0)
|
|
|
|
end;
|
|
|
|
end;
|
|
|
@ -724,8 +736,10 @@ handle(16#0811, Data) ->
|
|
|
|
%% @doc Leave mission counter handler. Lobby values depend on which counter was entered.
|
|
|
|
%% @doc Leave mission counter handler. Lobby values depend on which counter was entered.
|
|
|
|
|
|
|
|
|
|
|
|
handle(16#0812, _) ->
|
|
|
|
handle(16#0812, _) ->
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
area_load(User#users.savedquestid, User#users.savedzoneid, User#users.zoneid, User#users.mapid);
|
|
|
|
Area = User#egs_user_model.area,
|
|
|
|
|
|
|
|
PrevArea = User#egs_user_model.prev_area,
|
|
|
|
|
|
|
|
area_load(PrevArea#psu_area.questid, PrevArea#psu_area.zoneid, Area#psu_area.zoneid, Area#psu_area.mapid);
|
|
|
|
|
|
|
|
|
|
|
|
%% @doc Item description request.
|
|
|
|
%% @doc Item description request.
|
|
|
|
%% @todo Send something other than just "dammy".
|
|
|
|
%% @todo Send something other than just "dammy".
|
|
|
@ -747,8 +761,8 @@ handle(16#0c01, << QuestID:32/little-unsigned-integer >>) ->
|
|
|
|
%% @todo Handle correctly.
|
|
|
|
%% @todo Handle correctly.
|
|
|
|
|
|
|
|
|
|
|
|
handle(16#0c05, _) ->
|
|
|
|
handle(16#0c05, _) ->
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
[{quests, Filename}, {bg, _}, {options, _}] = proplists:get_value(User#users.entryid, ?COUNTERS),
|
|
|
|
[{quests, Filename}, {bg, _}, {options, _}] = proplists:get_value(User#egs_user_model.entryid, ?COUNTERS),
|
|
|
|
send_0c06(Filename);
|
|
|
|
send_0c06(Filename);
|
|
|
|
|
|
|
|
|
|
|
|
%% @doc Lobby transport handler? Just ignore the meseta price for now and send the player where he wanna be!
|
|
|
|
%% @doc Lobby transport handler? Just ignore the meseta price for now and send the player where he wanna be!
|
|
|
@ -762,18 +776,19 @@ handle(16#0c07, _) ->
|
|
|
|
|
|
|
|
|
|
|
|
handle(16#0c0e, _) ->
|
|
|
|
handle(16#0c0e, _) ->
|
|
|
|
send_1006(11),
|
|
|
|
send_1006(11),
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
%% delete the mission
|
|
|
|
%% delete the mission
|
|
|
|
psu_missions:stop(User#users.instanceid),
|
|
|
|
psu_missions:stop(User#egs_user_model.instanceid),
|
|
|
|
%% full hp
|
|
|
|
%% full hp
|
|
|
|
Character = User#users.character,
|
|
|
|
Character = User#egs_user_model.character,
|
|
|
|
MaxHP = Character#characters.maxhp,
|
|
|
|
MaxHP = Character#characters.maxhp,
|
|
|
|
NewCharacter = Character#characters{currenthp=MaxHP},
|
|
|
|
NewCharacter = Character#characters{currenthp=MaxHP},
|
|
|
|
NewUser = User#users{character=NewCharacter},
|
|
|
|
NewUser = User#egs_user_model{character=NewCharacter},
|
|
|
|
egs_db:users_insert(NewUser),
|
|
|
|
egs_user_model:write(NewUser),
|
|
|
|
%% map change (temporary)
|
|
|
|
%% map change (temporary)
|
|
|
|
if User#users.areatype =:= mission ->
|
|
|
|
if User#egs_user_model.areatype =:= mission ->
|
|
|
|
area_load(User#users.savedquestid, User#users.savedzoneid, User#users.savedmapid, User#users.savedentryid);
|
|
|
|
Area = User#egs_user_model.prev_area,
|
|
|
|
|
|
|
|
area_load(Area#psu_area.questid, Area#psu_area.zoneid, Area#psu_area.mapid, User#egs_user_model.prev_entryid);
|
|
|
|
true -> ignore
|
|
|
|
true -> ignore
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
@ -781,8 +796,8 @@ handle(16#0c0e, _) ->
|
|
|
|
%% @todo Temporarily allow rare mission and LL all difficulties to all players.
|
|
|
|
%% @todo Temporarily allow rare mission and LL all difficulties to all players.
|
|
|
|
|
|
|
|
|
|
|
|
handle(16#0c0f, _) ->
|
|
|
|
handle(16#0c0f, _) ->
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
[{quests, _}, {bg, _}, {options, Options}] = proplists:get_value(User#users.entryid, ?COUNTERS),
|
|
|
|
[{quests, _}, {bg, _}, {options, Options}] = proplists:get_value(User#egs_user_model.entryid, ?COUNTERS),
|
|
|
|
send_0c10(Options);
|
|
|
|
send_0c10(Options);
|
|
|
|
|
|
|
|
|
|
|
|
%% @doc Set flag handler. Associate a new flag with the character.
|
|
|
|
%% @doc Set flag handler. Associate a new flag with the character.
|
|
|
@ -803,8 +818,8 @@ handle(16#0d07, Data) ->
|
|
|
|
Options = psu_characters:options_binary_to_tuple(Data),
|
|
|
|
Options = psu_characters:options_binary_to_tuple(Data),
|
|
|
|
psu_characters:validate_options(Options),
|
|
|
|
psu_characters:validate_options(Options),
|
|
|
|
% End of validation
|
|
|
|
% End of validation
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
file:write_file(io_lib:format("save/~s/~b-character.options", [User#users.folder, (User#users.character)#characters.slot]), Data);
|
|
|
|
file:write_file(io_lib:format("save/~s/~b-character.options", [User#egs_user_model.folder, (User#egs_user_model.character)#characters.slot]), Data);
|
|
|
|
|
|
|
|
|
|
|
|
%% @doc Hit handler.
|
|
|
|
%% @doc Hit handler.
|
|
|
|
%% @todo Finish the work on it.
|
|
|
|
%% @todo Finish the work on it.
|
|
|
@ -824,11 +839,11 @@ handle(16#0f0a, Data) ->
|
|
|
|
log("object event handler: action ~b object ~b a ~b b ~b c ~b", [Action, ObjectID, A, B, C]),
|
|
|
|
log("object event handler: action ~b object ~b a ~b b ~b c ~b", [Action, ObjectID, A, B, C]),
|
|
|
|
case Action of
|
|
|
|
case Action of
|
|
|
|
0 -> % warp
|
|
|
|
0 -> % warp
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
{X, Y, Z, Dir} = psu_missions:warp_event(User#users.instanceid, BlockID, ListNb, ObjectNb),
|
|
|
|
{X, Y, Z, Dir} = psu_missions:warp_event(User#egs_user_model.instanceid, BlockID, ListNb, ObjectNb),
|
|
|
|
NewUser = User#users{pos=#pos{x=X, y=Y, z=Z, dir=Dir}},
|
|
|
|
NewUser = User#egs_user_model{pos=#pos{x=X, y=Y, z=Z, dir=Dir}},
|
|
|
|
egs_db:users_insert(NewUser),
|
|
|
|
egs_user_model:write(NewUser),
|
|
|
|
send_0503(User#users.pos),
|
|
|
|
send_0503(User#egs_user_model.pos),
|
|
|
|
send_1211(A, C, B, 0);
|
|
|
|
send_1211(A, C, B, 0);
|
|
|
|
3 -> % crystal activation
|
|
|
|
3 -> % crystal activation
|
|
|
|
send_1213(ObjectID, 1);
|
|
|
|
send_1213(ObjectID, 1);
|
|
|
@ -847,8 +862,8 @@ handle(16#0f0a, Data) ->
|
|
|
|
% 0117, 0111, 0117?
|
|
|
|
% 0117, 0111, 0117?
|
|
|
|
ignore;
|
|
|
|
ignore;
|
|
|
|
12 -> % pick/use key
|
|
|
|
12 -> % pick/use key
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
[[EventID|_], BlockID] = psu_missions:key_event(User#users.instanceid, ObjectID),
|
|
|
|
[[EventID|_], BlockID] = psu_missions:key_event(User#egs_user_model.instanceid, ObjectID),
|
|
|
|
send_1205(EventID, BlockID, 0),
|
|
|
|
send_1205(EventID, BlockID, 0),
|
|
|
|
send_1213(ObjectID, 1);
|
|
|
|
send_1213(ObjectID, 1);
|
|
|
|
13 -> % floor_button on (also sent when clearing a few of the rooms in black nest)
|
|
|
|
13 -> % floor_button on (also sent when clearing a few of the rooms in black nest)
|
|
|
@ -862,12 +877,12 @@ handle(16#0f0a, Data) ->
|
|
|
|
20 -> % enter counter/elevator/room/spaceport/pick key/use key
|
|
|
|
20 -> % enter counter/elevator/room/spaceport/pick key/use key
|
|
|
|
ignore;
|
|
|
|
ignore;
|
|
|
|
23 -> % initialize key slots (called when picking a key or checking the gate directly with no key)
|
|
|
|
23 -> % initialize key slots (called when picking a key or checking the gate directly with no key)
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
[[_, EventID, _], BlockID] = psu_missions:key_event(User#users.instanceid, ObjectID),
|
|
|
|
[[_, EventID, _], BlockID] = psu_missions:key_event(User#egs_user_model.instanceid, ObjectID),
|
|
|
|
send_1205(EventID, BlockID, 0); % in block 1, 202 = key [1] x1, 203 = key [-] x1
|
|
|
|
send_1205(EventID, BlockID, 0); % in block 1, 202 = key [1] x1, 203 = key [-] x1
|
|
|
|
24 -> % open gate (only when client has key)
|
|
|
|
24 -> % open gate (only when client has key)
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
[[_, _, EventID], BlockID] = psu_missions:key_event(User#users.instanceid, ObjectID),
|
|
|
|
[[_, _, EventID], BlockID] = psu_missions:key_event(User#egs_user_model.instanceid, ObjectID),
|
|
|
|
send_1205(EventID, BlockID, 0),
|
|
|
|
send_1205(EventID, BlockID, 0),
|
|
|
|
send_1213(ObjectID, 1);
|
|
|
|
send_1213(ObjectID, 1);
|
|
|
|
25 -> % sit on chair
|
|
|
|
25 -> % sit on chair
|
|
|
@ -894,8 +909,8 @@ handle(16#1112, Data) ->
|
|
|
|
%% @todo Handle when the party already exists! And stop doing it wrong.
|
|
|
|
%% @todo Handle when the party already exists! And stop doing it wrong.
|
|
|
|
|
|
|
|
|
|
|
|
handle(16#1705, _) ->
|
|
|
|
handle(16#1705, _) ->
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
send_1706((User#users.character)#characters.name);
|
|
|
|
send_1706((User#egs_user_model.character)#characters.name);
|
|
|
|
|
|
|
|
|
|
|
|
%% @doc Mission selected handler. Send the currently selected mission.
|
|
|
|
%% @doc Mission selected handler. Send the currently selected mission.
|
|
|
|
%% @todo Probably need to dispatch that info to other party members in the same counter.
|
|
|
|
%% @todo Probably need to dispatch that info to other party members in the same counter.
|
|
|
@ -918,8 +933,8 @@ handle(16#170b, _) ->
|
|
|
|
%% @todo Handle correctly.
|
|
|
|
%% @todo Handle correctly.
|
|
|
|
|
|
|
|
|
|
|
|
handle(16#1710, _) ->
|
|
|
|
handle(16#1710, _) ->
|
|
|
|
User = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
[{quests, _}, {bg, Background}, {options, _}] = proplists:get_value(User#users.entryid, ?COUNTERS),
|
|
|
|
[{quests, _}, {bg, Background}, {options, _}] = proplists:get_value(User#egs_user_model.entryid, ?COUNTERS),
|
|
|
|
send_1711(Background);
|
|
|
|
send_1711(Background);
|
|
|
|
|
|
|
|
|
|
|
|
%% @doc Dialog request handler. Do what we can.
|
|
|
|
%% @doc Dialog request handler. Do what we can.
|
|
|
@ -986,7 +1001,7 @@ handle_hits(Data) ->
|
|
|
|
<< _:96, SourceID:32/little-unsigned-integer, TargetID:32/little-unsigned-integer, _/bits >> = A,
|
|
|
|
<< _:96, SourceID:32/little-unsigned-integer, TargetID:32/little-unsigned-integer, _/bits >> = A,
|
|
|
|
% retrieve
|
|
|
|
% retrieve
|
|
|
|
GID = get(gid),
|
|
|
|
GID = get(gid),
|
|
|
|
User = egs_db:users_select(GID),
|
|
|
|
{ok, User} = egs_user_model:read(GID),
|
|
|
|
% hit!
|
|
|
|
% hit!
|
|
|
|
#hit_response{type=Type, user=NewUser, exp=HasEXP, damage=Damage, targethp=TargetHP, targetse=TargetSE, events=Events} = psu_missions:object_hit(User, SourceID, TargetID),
|
|
|
|
#hit_response{type=Type, user=NewUser, exp=HasEXP, damage=Damage, targethp=TargetHP, targetse=TargetSE, events=Events} = psu_missions:object_hit(User, SourceID, TargetID),
|
|
|
|
case Type of
|
|
|
|
case Type of
|
|
|
@ -994,7 +1009,7 @@ handle_hits(Data) ->
|
|
|
|
% TODO: also has a hit sent, we should send it too
|
|
|
|
% TODO: also has a hit sent, we should send it too
|
|
|
|
handle_events(Events);
|
|
|
|
handle_events(Events);
|
|
|
|
_ ->
|
|
|
|
_ ->
|
|
|
|
PlayerHP = (NewUser#users.character)#characters.currenthp,
|
|
|
|
PlayerHP = (NewUser#egs_user_model.character)#characters.currenthp,
|
|
|
|
case lists:member(death, TargetSE) of
|
|
|
|
case lists:member(death, TargetSE) of
|
|
|
|
true -> SE = 16#01000200;
|
|
|
|
true -> SE = 16#01000200;
|
|
|
|
false -> SE = 16#01000000
|
|
|
|
false -> SE = 16#01000000
|
|
|
@ -1007,13 +1022,13 @@ handle_hits(Data) ->
|
|
|
|
end,
|
|
|
|
end,
|
|
|
|
% exp
|
|
|
|
% exp
|
|
|
|
if HasEXP =:= true ->
|
|
|
|
if HasEXP =:= true ->
|
|
|
|
Character = NewUser#users.character,
|
|
|
|
Character = NewUser#egs_user_model.character,
|
|
|
|
Level = Character#characters.mainlevel,
|
|
|
|
Level = Character#characters.mainlevel,
|
|
|
|
send_0115(GID, TargetID, Level#level.number, Level#level.exp, Character#characters.money);
|
|
|
|
send_0115(GID, TargetID, Level#level.number, Level#level.exp, Character#characters.money);
|
|
|
|
true -> ignore
|
|
|
|
true -> ignore
|
|
|
|
end,
|
|
|
|
end,
|
|
|
|
% save
|
|
|
|
% save
|
|
|
|
egs_db:users_insert(NewUser),
|
|
|
|
egs_user_model:write(NewUser),
|
|
|
|
% next
|
|
|
|
% next
|
|
|
|
handle_hits(Rest).
|
|
|
|
handle_hits(Rest).
|
|
|
|
|
|
|
|
|
|
|
@ -1046,8 +1061,8 @@ send(Packet) ->
|
|
|
|
|
|
|
|
|
|
|
|
send_010d(User) ->
|
|
|
|
send_010d(User) ->
|
|
|
|
GID = get(gid),
|
|
|
|
GID = get(gid),
|
|
|
|
CharGID = User#users.gid,
|
|
|
|
CharGID = User#egs_user_model.id,
|
|
|
|
<< _:640, CharBin/bits >> = psu_characters:character_user_to_binary(User#users{lid=0}),
|
|
|
|
<< _:640, CharBin/bits >> = psu_characters:character_user_to_binary(User#egs_user_model{lid=0}),
|
|
|
|
send(<< 16#010d0300:32, 0:160, 16#00011300:32, GID:32/little-unsigned-integer, 0:64,
|
|
|
|
send(<< 16#010d0300:32, 0:160, 16#00011300:32, GID:32/little-unsigned-integer, 0:64,
|
|
|
|
1:32/little-unsigned-integer, 0:32, 16#00000300:32, 16#ffff0000:32, 0:32, CharGID:32/little-unsigned-integer,
|
|
|
|
1:32/little-unsigned-integer, 0:32, 16#00000300:32, 16#ffff0000:32, 0:32, CharGID:32/little-unsigned-integer,
|
|
|
|
0:192, CharGID:32/little-unsigned-integer, 0:32, 16#ffffffff:32, CharBin/binary >>).
|
|
|
|
0:192, CharGID:32/little-unsigned-integer, 0:32, 16#ffffffff:32, CharBin/binary >>).
|
|
|
@ -1100,8 +1115,8 @@ send_0200(ZoneType) ->
|
|
|
|
|
|
|
|
|
|
|
|
send_0201(User) ->
|
|
|
|
send_0201(User) ->
|
|
|
|
GID = get(gid),
|
|
|
|
GID = get(gid),
|
|
|
|
CharGID = User#users.gid,
|
|
|
|
CharGID = User#egs_user_model.id,
|
|
|
|
CharBin = psu_characters:character_user_to_binary(User#users{lid=0}),
|
|
|
|
CharBin = psu_characters:character_user_to_binary(User#egs_user_model{lid=0}),
|
|
|
|
IsGM = 0,
|
|
|
|
IsGM = 0,
|
|
|
|
OnlineStatus = 0,
|
|
|
|
OnlineStatus = 0,
|
|
|
|
GameVersion = 0,
|
|
|
|
GameVersion = 0,
|
|
|
@ -1175,7 +1190,8 @@ send_021b() ->
|
|
|
|
|
|
|
|
|
|
|
|
send_021e() ->
|
|
|
|
send_021e() ->
|
|
|
|
{ok, << File:1184/bits, _/bits >>} = file:read_file("p/unicube.bin"),
|
|
|
|
{ok, << File:1184/bits, _/bits >>} = file:read_file("p/unicube.bin"),
|
|
|
|
[StrCount] = io_lib:format("~b", [egs_db:users_count()]),
|
|
|
|
{ok, Count} = egs_user_model:count(),
|
|
|
|
|
|
|
|
[StrCount] = io_lib:format("~b", [Count]),
|
|
|
|
UCS2Count = << << X:8, 0:8 >> || X <- StrCount >>,
|
|
|
|
UCS2Count = << << X:8, 0:8 >> || X <- StrCount >>,
|
|
|
|
PaddingSize = (12 - byte_size(UCS2Count)) * 8,
|
|
|
|
PaddingSize = (12 - byte_size(UCS2Count)) * 8,
|
|
|
|
send(<< 16#021e0300:32, 0:288, File/binary, UCS2Count/binary, 0:PaddingSize >>).
|
|
|
|
send(<< 16#021e0300:32, 0:288, File/binary, UCS2Count/binary, 0:PaddingSize >>).
|
|
|
@ -1217,8 +1233,8 @@ build_0233_contents([]) ->
|
|
|
|
<< >>;
|
|
|
|
<< >>;
|
|
|
|
build_0233_contents(Users) ->
|
|
|
|
build_0233_contents(Users) ->
|
|
|
|
[User|Rest] = Users,
|
|
|
|
[User|Rest] = Users,
|
|
|
|
LID = 16#010000 + User#users.lid, % @todo The LID must be 16 bits and 0233 seems to (almost always) require that 01 right there...
|
|
|
|
LID = 16#010000 + User#egs_user_model.lid, % @todo The LID must be 16 bits and 0233 seems to (almost always) require that 01 right there...
|
|
|
|
CharBin = psu_characters:character_user_to_binary(User#users{lid=LID}),
|
|
|
|
CharBin = psu_characters:character_user_to_binary(User#egs_user_model{lid=LID}),
|
|
|
|
IsGM = 0,
|
|
|
|
IsGM = 0,
|
|
|
|
GameVersion = 0,
|
|
|
|
GameVersion = 0,
|
|
|
|
Chunk = << CharBin/binary, IsGM:8, 0:8, GameVersion:8, 0:8 >>,
|
|
|
|
Chunk = << CharBin/binary, IsGM:8, 0:8, GameVersion:8, 0:8 >>,
|
|
|
@ -1243,7 +1259,8 @@ send_0304(FromGID, FromName, Modifiers, Message) ->
|
|
|
|
%% @todo The value before IntDir seems to be the player's current animation. 01 stand up, 08 ?, 17 normal sit
|
|
|
|
%% @todo The value before IntDir seems to be the player's current animation. 01 stand up, 08 ?, 17 normal sit
|
|
|
|
|
|
|
|
|
|
|
|
send_0503(#pos{x=PrevX, y=PrevY, z=PrevZ, dir=_}) ->
|
|
|
|
send_0503(#pos{x=PrevX, y=PrevY, z=PrevZ, dir=_}) ->
|
|
|
|
#users{gid=GID, pos=#pos{x=X, y=Y, z=Z, dir=Dir}, questid=QuestID, zoneid=ZoneID, mapid=MapID, entryid=EntryID} = egs_db:users_select(get(gid)),
|
|
|
|
{ok, User} = egs_user_model:read(get(gid)),
|
|
|
|
|
|
|
|
#egs_user_model{id=GID, pos=#pos{x=X, y=Y, z=Z, dir=Dir}, area=#psu_area{questid=QuestID, zoneid=ZoneID, mapid=MapID}, entryid=EntryID} = User,
|
|
|
|
IntDir = trunc(Dir * 182.0416),
|
|
|
|
IntDir = trunc(Dir * 182.0416),
|
|
|
|
send(<< 16#05030300:32, 0:64, GID:32/little-unsigned-integer, 0:64, 16#00011300:32, GID:32/little-unsigned-integer, 0:64, GID:32/little-unsigned-integer, 0:32,
|
|
|
|
send(<< 16#05030300:32, 0:64, GID:32/little-unsigned-integer, 0:64, 16#00011300:32, GID:32/little-unsigned-integer, 0:64, GID:32/little-unsigned-integer, 0:32,
|
|
|
|
16#1000:16, IntDir:16/little-unsigned-integer, PrevX:32/little-float, PrevY:32/little-float, PrevZ:32/little-float, X:32/little-float, Y:32/little-float, Z:32/little-float,
|
|
|
|
16#1000:16, IntDir:16/little-unsigned-integer, PrevX:32/little-float, PrevY:32/little-float, PrevZ:32/little-float, X:32/little-float, Y:32/little-float, Z:32/little-float,
|
|
|
@ -1320,8 +1337,8 @@ send_0c10(Options) ->
|
|
|
|
%% @todo The values after the Char variable are the flags. Probably use bits to define what flag is and isn't set. Handle correctly.
|
|
|
|
%% @todo The values after the Char variable are the flags. Probably use bits to define what flag is and isn't set. Handle correctly.
|
|
|
|
|
|
|
|
|
|
|
|
send_0d01(User) ->
|
|
|
|
send_0d01(User) ->
|
|
|
|
CharBin = psu_characters:character_tuple_to_binary(User#users.character),
|
|
|
|
CharBin = psu_characters:character_tuple_to_binary(User#egs_user_model.character),
|
|
|
|
OptionsBin = psu_characters:options_tuple_to_binary((User#users.character)#characters.options),
|
|
|
|
OptionsBin = psu_characters:options_tuple_to_binary((User#egs_user_model.character)#characters.options),
|
|
|
|
send(<< (header(16#0d01))/binary, CharBin/binary,
|
|
|
|
send(<< (header(16#0d01))/binary, CharBin/binary,
|
|
|
|
16#ffbbef1c:32, 16#f8ff0700:32, 16#fc810916:32, 16#7802134c:32,
|
|
|
|
16#ffbbef1c:32, 16#f8ff0700:32, 16#fc810916:32, 16#7802134c:32,
|
|
|
|
16#b0c0040f:32, 16#7cf0e583:32, 16#b7bce0c6:32, 16#7ff8f963:32,
|
|
|
|
16#b0c0040f:32, 16#7cf0e583:32, 16#b7bce0c6:32, 16#7ff8f963:32,
|
|
|
@ -1465,7 +1482,7 @@ send_1215(A, B) ->
|
|
|
|
%% @todo Find out the remaining values.
|
|
|
|
%% @todo Find out the remaining values.
|
|
|
|
|
|
|
|
|
|
|
|
send_1500(User) ->
|
|
|
|
send_1500(User) ->
|
|
|
|
#characters{slot=Slot, name=Name, race=Race, gender=Gender, class=Class} = User#users.character,
|
|
|
|
#characters{slot=Slot, name=Name, race=Race, gender=Gender, class=Class} = User#egs_user_model.character,
|
|
|
|
RaceBin = psu_characters:race_atom_to_binary(Race),
|
|
|
|
RaceBin = psu_characters:race_atom_to_binary(Race),
|
|
|
|
GenderBin = psu_characters:gender_atom_to_binary(Gender),
|
|
|
|
GenderBin = psu_characters:gender_atom_to_binary(Gender),
|
|
|
|
ClassBin = psu_characters:class_atom_to_binary(Class),
|
|
|
|
ClassBin = psu_characters:class_atom_to_binary(Class),
|
|
|
|