aboutsummaryrefslogtreecommitdiff
path: root/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src
diff options
context:
space:
mode:
Diffstat (limited to 'learn-you-some-erlang/processquest/apps/processquest-1.1.0/src')
-rw-r--r--learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_enemy.erl32
-rw-r--r--learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_events.erl53
-rw-r--r--learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_market.erl77
-rw-r--r--learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_player.erl216
-rw-r--r--learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_quest.erl15
-rw-r--r--learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_stats.erl19
-rw-r--r--learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_sup.erl31
-rw-r--r--learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_supersup.erl28
-rw-r--r--learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/processquest.erl39
9 files changed, 510 insertions, 0 deletions
diff --git a/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_enemy.erl b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_enemy.erl
new file mode 100644
index 0000000..e1c2332
--- /dev/null
+++ b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_enemy.erl
@@ -0,0 +1,32 @@
+%% Gives random enemies
+-module(pq_enemy).
+-export([fetch/0]).
+
+fetch() ->
+ L = enemies(),
+ lists:nth(random:uniform(length(L)), L).
+
+enemies() ->
+ [{<<"Spider">>, [{drop, {<<"Spider Egg">>, 1}}, {experience, 1}]},
+ {<<"Wildcat">>, [{drop, {<<"Pelt">>, 1}}, {experience, 1}]},
+ {<<"Pig">>, [{drop, {<<"Bacon">>, 1}}, {experience, 1}]},
+ {<<"Wild Pig">>, [{drop, {<<"Tasty Ribs">>, 2}}, {experience, 1}]},
+ {<<"Goblin">>, [{drop, {<<"Goblin hair">>, 1}}, {experience, 2}]},
+ {<<"Robot">>, [{drop, {<<"Chunks of Metal">>, 3}}, {experience, 2}]},
+ {<<"Factory Worker">>, [{drop, {<<"Wrench">>,2}}, {experience,1}]},
+ {<<"Carnie">>, [{drop, {<<"Cotton Candy">>,1}}, {experience,1}]},
+ {<<"Mad Beaver">>, [{drop, {<<"Wood chips">>, 2}}, {experience, 1}]},
+ {<<"Silent magpie">>, [{drop, {<<"Shiny things">>, 3}}, {experience, 1}]},
+ {<<"Great Lizard">>, [{drop, {<<"Lizard tail">>, 1}}, {experience, 1}]},
+ {<<"Cheetah">>, [{drop, {<<"Fur">>, 3}}, {experience, 4}]},
+ {<<"Radish Horse">>, [{drop, {<<"Horseradish">>,1}}, {experience, 2}]},
+ {<<"Sand Worm">>, [{drop, {<<"Spices">>,10}}, {experience, 25}]},
+ {<<"Mule">>, [{drop, {<<"Map">>, 2}}, {experience, 12}]},
+ {<<"Man Tree">>, [{drop, {<<"branch">>,1}}, {experience, 2}]},
+ {<<"Penguin Lord">>, [{drop, {<<"Penguin Egg">>,1}}, {experience, 3}]},
+ {<<"Cursed Priest">>, [{drop, {<<"Grail">>, 3}}, {experience, 5}]},
+ {<<"Bearded cow">>, [{drop, {<<"Hairy milk">>, 1}}, {experience, 6}]},
+ {<<"Hellish crow">>, [{drop, {<<"Black feather">>, 1}}, {experience, 1}]},
+ {<<"Wolverine">>, [{drop, {<<"Puddle of blood">>, 1}}, {experience, 2}]},
+ {<<"Gangsta Bear">>, [{drop, {<<"Bear Grylls">>, 3}}, {experience, 4}]}].
+
diff --git a/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_events.erl b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_events.erl
new file mode 100644
index 0000000..faea13e
--- /dev/null
+++ b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_events.erl
@@ -0,0 +1,53 @@
+%%% Wrapper module for the event manager of ProgressQuest players.
+%%% It adds a few functions to wrap the events and sleep on the right
+%%% scale on behalf of the pq_player process.
+-module(pq_events).
+-export([killed/3, location/3, lvl_up/5, buy/4, sell/3, quest/4]).
+-export([start_link/1, stop/1, add_handler/3, delete_handler/3, notify/2]).
+
+start_link(Name) ->
+ {ok, Pid} = gen_event:start_link(),
+ ok = regis:register({events, Name}, Pid),
+ {ok, Pid}.
+
+stop(Name) ->
+ ManagerPid = regis:whereis({events, Name}),
+ gen_event:stop(ManagerPid).
+
+add_handler(Name, Handler, Args) ->
+ ManagerPid = regis:whereis({events, Name}),
+ gen_event:add_handler(ManagerPid, Handler, Args).
+
+delete_handler(Name, Handler, Args) ->
+ ManagerPid = regis:whereis({events, Name}),
+ gen_event:delete_handler(ManagerPid, Handler, Args).
+
+notify(Name, Msg) ->
+ ManagerPid = regis:whereis({events, Name}),
+ gen_event:notify(ManagerPid, Msg).
+
+killed(Name, Enemy = {_EnemyName, _Props}, Time) ->
+ notify(Name, {Name, killed, Time*2, Enemy}),
+ timer:sleep(Time*2).
+
+location(Name, Place, Time) ->
+ notify(Name, {Name, heading, Time, Place}),
+ timer:sleep(Time).
+
+lvl_up(Name, NewStats, NewLvl, NewExp, _Time) ->
+ notify(Name, {Name, lvl_up, 0, NewStats, NewLvl, NewExp}),
+ ok.
+
+buy(Name, Slot, Item, Time) ->
+ T = round(Time/2),
+ notify(Name, {Name, buy, T, Slot, Item}),
+ timer:sleep(T).
+
+sell(Name, Item, Time) ->
+ T = round(Time/5),
+ notify(Name, {Name, sell, T, Item}),
+ timer:sleep(Time).
+
+quest(Name, {Old, _}, {New, _}, _Time) ->
+ notify(Name, {Name, quest, 0, Old, New}),
+ ok.
diff --git a/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_market.erl b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_market.erl
new file mode 100644
index 0000000..241e9cd
--- /dev/null
+++ b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_market.erl
@@ -0,0 +1,77 @@
+%%% Can be used to obtain weapons and pieces of equipment of various types
+%%% to be equipped by the hero. The standard format is:
+%%% {Name, LevelModifier, Level, Price}.
+-module(pq_market).
+-export([helmet/2, weapon/2, shield/2, armor/2]).
+
+weapon(CombinedLvl, Money) ->
+ L = [
+ {<<"plastic knife">>, -1, 1, 2},
+ {<<"plastic knife">>, 0, 1, 3},
+ {<<"plastic knife">>, 1, 1, 5},
+ {<<"metal spoon">>, -1, 4, 3},
+ {<<"metal spoon">>, 0, 4, 4},
+ {<<"butter knife">>, -1, 6, 5},
+ {<<"butter knife">>, 0, 6, 7},
+ {<<"butter knife">>, 1, 6, 9},
+ {<<"machete">>, -1, 9, 15},
+ {<<"machete">>, 0, 9, 20},
+ {<<"machete">>, 1, 9, 25},
+ {<<"broad sword">>, -1, 12, 23},
+ {<<"broad sword">>, 0, 12, 30},
+ {<<"broad sword">>, 1, 12, 38},
+ {<<"lance">>, -1, 15, 32},
+ {<<"lance">>, 0, 15, 44},
+ {<<"lance">>, 1, 15, 57},
+ {<<"pistol">>, -1, 25, 95},
+ {<<"pistol">>, 0, 25, 105},
+ {<<"pistol">>, 1, 25, 155},
+ {<<"submachine gun">>, -1, 40, 200},
+ {<<"submachine gun">>, 0, 40, 245},
+ {<<"submachine gun">>, 1, 40, 365}
+ ],
+ first_match(fun(W = {_, Modifier, Lvl, Price}) ->
+ if Modifier+Lvl > CombinedLvl, Price =< Money -> W;
+ true -> continue
+ end
+ end, L).
+
+helmet(CombinedLvl, Money) -> pick_material(CombinedLvl, Money).
+shield(CombinedLvl, Money) -> pick_material(CombinedLvl, Money).
+armor(CombinedLvl, Money) -> pick_material(CombinedLvl, Money).
+
+pick_material(CombinedLvl, Money) ->
+ L = materials(),
+ first_match(fun(W = {_, Modifier, Lvl, Price}) ->
+ if Modifier+Lvl > CombinedLvl, Price =< Money -> W;
+ true -> continue
+ end
+ end, L).
+
+
+first_match(_, []) -> undefined;
+first_match(F, [H|T]) ->
+ case F(H) of
+ continue -> first_match(F,T);
+ Val -> Val
+ end.
+
+materials() ->
+ [{<<"wool">>, 0, 1, 25},
+ {<<"pleather">>, 0, 2, 45},
+ {<<"pleather">>, 1, 2, 50},
+ {<<"pleather">>, 2, 2, 65},
+ {<<"leather">>, -2, 7, 30},
+ {<<"leather">>, -1, 7, 35},
+ {<<"leather">>, 0, 7, 45},
+ {<<"leather">>, 2, 7, 65},
+ {<<"chain mail">>, -2, 12, 70},
+ {<<"chain mail">>, 0, 12, 85},
+ {<<"chain mail">>, 1, 12, 95},
+ {<<"chain mail">>, 2, 12, 105},
+ {<<"plate mail">>, -2, 17, 90},
+ {<<"plate mail">>, -1, 17, 95},
+ {<<"plate mail">>, 0, 17, 105},
+ {<<"plate mail">>, 1, 17, 115},
+ {<<"plate mail">>, 2, 17, 135}].
+
diff --git a/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_player.erl b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_player.erl
new file mode 100644
index 0000000..9cca7e0
--- /dev/null
+++ b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_player.erl
@@ -0,0 +1,216 @@
+%%% The core of ProcessQuest -- the player FSM,
+%%% acting for each of the players in the game.
+%%%
+%%% The FSM actually depends on no external events and only sends messages
+%%% to itself to prompt state changes. This is somewhat unusual as far as
+%%% gen_fsm usages go, but it's pretty useful when needing to work with
+%%% a standalone process.
+-module(pq_player).
+-behaviour(gen_fsm).
+-export([start_link/2]).
+-export([init/1, market/2, killing/2, handle_event/3, handle_sync_event/4,
+ handle_info/3, terminate/3, code_change/4]).
+
+-record(state, {name, stats, exp=0, lvlexp=1000, lvl=1,
+ equip=[], money=0, loot=[], bought=[],
+ time=0, quest}).
+
+
+%%% Possible states & events
+%%
+% sell buy
+% / | | \
+% \ ^ ^ /
+% [market]<--,
+% | |
+% done buying |
+% | bag full
+% v /
+% [killing fields]
+% / V V |
+% \ / | |
+% kill lvl up
+
+start_link(Name, Opts) ->
+ gen_fsm:start_link(?MODULE, {Name, Opts}, []).
+
+init({Name, Opts}) ->
+ %% Properly seeding stuff. If not doing this, the random module will
+ %% seed it based on a slightly unique time value. However, when starting
+ %% many processes at about the same time, the seeds can be very close
+ %% and give barely random results. The crypto:rand_bytes/1 function
+ %% allows for much better seeding.
+ <<A:32, B:32, C:32>> = crypto:rand_bytes(12),
+ random:seed({A,B,C}),
+ %% The first event, to start the FSM
+ gen_fsm:send_event(self(), kill),
+ case regis:register(Name, self()) of
+ {error, _} ->
+ {stop, name_taken};
+ ok ->
+ %% Use proplists with default values to let the user configure
+ %% all parts of the FSM's state by using the Opts proplist.
+ S = #state{
+ name=Name,
+ stats=proplists:get_value(stats, Opts, pq_stats:initial_roll()),
+ exp=proplists:get_value(exp, Opts, 0),
+ lvlexp=proplists:get_value(lvlexp, Opts, 1000),
+ lvl=proplists:get_value(lvl, Opts, 1),
+ equip=proplists:get_value(equip, Opts, []),
+ money=proplists:get_value(money, Opts, 0),
+ loot=proplists:get_value(loot, Opts, []),
+ bought=proplists:get_value(bought, Opts, []),
+ time=proplists:get_value(time, Opts, 0),
+ quest=proplists:get_value(quest, Opts, pq_quest:fetch())
+ },
+ {ok, market, S}
+ end.
+
+%% Done selling. Switch to the event where we head to the killing fields
+market(sell, S = #state{loot=[]}) ->
+ gen_fsm:send_event(self(), buy),
+ {next_state, market, S};
+%% Selling an Item we have looted to the market, for whatever value it has
+market(sell, S = #state{loot=[{Drop,Val}|T], money=M, lvl=Lvl}) ->
+ pq_events:sell(S#state.name, {Drop, Val*Lvl}, S#state.time),
+ gen_fsm:send_event(self(), sell),
+ {next_state, market, S#state{loot=T, money=M+Val*Lvl}};
+%% When done selling, buy items with your money
+market(buy, S = #state{equip=Equip, money=Money, bought=Bought}) ->
+ %% we have slots of equipment. It's useless to buy the same
+ %% kind of item time after time, so we must select one we haven't observed yet
+ case next_slot(Equip, Bought) of
+ undefined ->
+ %% when no slot is found, go to the killing field
+ gen_fsm:send_event(self(), kill),
+ {next_state, market, S#state{bought=[]}};
+ OldItem = {Slot, {_Name, Modifier, Lvl, _Price}} ->
+ %% Replace the item by a slightly better one if possible.
+ case pq_market:Slot(Modifier+Lvl, Money) of
+ undefined ->
+ market(buy, S#state{bought=[Slot|Bought]});
+ NewItem = {_, _, _, Price} ->
+ pq_events:buy(S#state.name, Slot, NewItem, S#state.time),
+ gen_fsm:send_event(self(), buy),
+ NewEquip = [{Slot, NewItem} | Equip -- [OldItem]],
+ {next_state, market, S#state{equip=NewEquip,
+ money=Money-Price,
+ bought=[Slot|Bought]}}
+ end
+ end;
+%% Heading to the killing field. State only useful as a state transition.
+market(kill, S) ->
+ pq_events:location(S#state.name, killing, S#state.time),
+ gen_fsm:send_event(self(), kill),
+ {next_state, killing, S}.
+
+%% Killing an enemy on the killing field. Taking its drop and keeping it
+%% in our loot.
+killing(kill, S = #state{loot=Loot, stats=Stats, exp=Exp, lvlexp=LvlExp,
+ quest=Quest}) ->
+ MaxSize = proplists:get_value(strength, Stats)*2,
+ {EnemyName, Props} = pq_enemy:fetch(),
+ pq_events:killed(S#state.name, {EnemyName, Props}, S#state.time),
+ Drop = {_N, _V} = proplists:get_value(drop, Props),
+ KillExp = proplists:get_value(experience, Props),
+ NewLoot = [Drop|Loot],
+ {QuestExp, NewQuest} = case check_quest(Quest) of
+ UpdatedQuest = {0, _} -> UpdatedQuest;
+ QuestBeaten = {_, NewQuest0} ->
+ pq_events:quest(S#state.name, Quest, NewQuest0, S#state.time),
+ QuestBeaten
+ end,
+ if length(NewLoot) =:= MaxSize ->
+ gen_fsm:send_event(self(), market);
+ Exp+KillExp+QuestExp >= LvlExp ->
+ gen_fsm:send_event(self(), lvl_up);
+ true ->
+ gen_fsm:send_event(self(), kill)
+ end,
+ {next_state, killing, S#state{loot=NewLoot,
+ exp=Exp+KillExp+QuestExp,
+ quest=NewQuest}};
+%% If we just leveled up, the stats get updated before we keep killing.
+killing(lvl_up, S = #state{stats=Stats, lvl=Lvl, lvlexp=LvlExp}) ->
+ NewStats = [{charisma, proplists:get_value(charisma, Stats)+pq_stats:roll()},
+ {constitution, proplists:get_value(constitution, Stats)+pq_stats:roll()},
+ {dexterity, proplists:get_value(dexterity, Stats)+pq_stats:roll()},
+ {intelligence, proplists:get_value(intelligence, Stats)+pq_stats:roll()},
+ {strength, proplists:get_value(strength, Stats)+pq_stats:roll()},
+ {wisdom, proplists:get_value(wisdom, Stats)+pq_stats:roll()}],
+ gen_fsm:send_event(self(), kill),
+ pq_events:lvl_up(S#state.name, NewStats, Lvl+1, LvlExp*2, S#state.time),
+ {next_state, killing, S#state{stats=NewStats, lvl=Lvl+1, lvlexp=LvlExp*2}};
+%% Heading to the market state transition
+killing(market, S) ->
+ pq_events:location(S#state.name, market, S#state.time),
+ gen_fsm:send_event(self(), sell),
+ {next_state, market, S}.
+
+handle_event(_Event, StateName, State) ->
+ {next_state, StateName, State}.
+
+handle_sync_event(_Event, _From, StateName, State) ->
+ {next_state, StateName, State}.
+
+handle_info(_Event, StateName, State) ->
+ {next_state, StateName, State}.
+
+terminate(_Reason, _StateName, _State) ->
+ ok.
+
+code_change({down, _},
+ StateName,
+ #state{name=N, stats=S, exp=E, lvlexp=LE, lvl=L, equip=Eq,
+ money=M, loot=Lo, bought=B, time=T},
+ _Extra) ->
+ Old = {state, N, S, E, LE, L, Eq, M, Lo, B, T},
+ {ok, StateName, Old};
+code_change(_OldVsn,
+ StateName,
+ {state, Name, Stats, Exp, LvlExp, Lvl, Equip, Money, Loot,
+ Bought, Time},
+ _Extra) ->
+ State = #state{
+ name=Name, stats=Stats, exp=Exp, lvlexp=LvlExp, lvl=Lvl, equip=Equip,
+ money=Money, loot=Loot, bought=Bought, time=Time, quest=pq_quest:fetch()
+ },
+ {ok, StateName, State}.
+
+%%%%%%%%%%%%%%%
+%%% PRIVATE %%%
+%%%%%%%%%%%%%%%
+
+%% Picks a slot based on what has been seen so far, combined with the
+%% current weakest item.
+next_slot(Equip, Bought) ->
+ L = expand(Equip),
+ case lists:sort([{Mod+Lvl, Entry} || Entry = {Slot, {_, Mod, Lvl, _}} <- L,
+ not lists:member(Slot, Bought)]) of
+ [] -> undefined;
+ [{_, Entry}|_] -> Entry
+ end.
+
+expand(L) ->
+ [expand_field(armor, L),
+ expand_field(helmet, L),
+ expand_field(shield, L),
+ expand_field(weapon, L)].
+
+expand_field(F, L) ->
+ {F, proplists:get_value(F, L, {undefined,0,0,0})}.
+
+%% Checks quests, if they are ready for the next level or not
+check_quest({Name, Props}) ->
+ case proplists:get_value(kills, Props) of
+ 1 ->
+ case pq_quest:fetch() of
+ %% Same name, we want new stuff!
+ {Name, _} -> check_quest({Name, Props});
+ NewQuest ->
+ Exp = proplists:get_value(experience, Props),
+ {Exp, NewQuest}
+ end;
+ Q ->
+ {0, {Name, [{kills,Q-1} | Props--[{kills,Q}]]}}
+ end.
diff --git a/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_quest.erl b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_quest.erl
new file mode 100644
index 0000000..b8294fb
--- /dev/null
+++ b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_quest.erl
@@ -0,0 +1,15 @@
+-module(pq_quest).
+-export([fetch/0]).
+
+fetch() ->
+ L = quests(),
+ lists:nth(random:uniform(length(L)), L).
+
+quests() ->
+ [{<<"Fetch me a nut">>, [{experience, 150}, {kills, 20}]},
+ {<<"Cancel the festival">>, [{experience, 65}, {kills, 8}]},
+ {<<"Summon the dragon">>, [{experience, 1000}, {kills, 100}]},
+ {<<"Meet the invisible man">>, [{experience, 200}, {kills, 25}]},
+ {<<"Find quest ideas">>, [{experience, 340}, {kills, 32}]},
+ {<<"Invent maple syrup">>, [{experience, 1500}, {kills, 175}]},
+ {<<"Slay the Bieber">>, [{experience, 500}, {kills, 45}]}].
diff --git a/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_stats.erl b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_stats.erl
new file mode 100644
index 0000000..379f5e9
--- /dev/null
+++ b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_stats.erl
@@ -0,0 +1,19 @@
+%%% Rolls dice to generate statistics or level increases for a character
+-module(pq_stats).
+-export([initial_roll/0, roll/0]).
+
+%% First roll, when setting the stats up for the first time
+initial_roll() ->
+ [{charisma, roll(3)},
+ {constitution, roll(3)},
+ {dexterity, roll(3)},
+ {intelligence, roll(3)},
+ {strength, roll(3)},
+ {wisdom, roll(3)}].
+
+%% Rolls a single die. Used when leveling up
+roll() -> roll(1).
+
+%% Rolls Num 6-faced dice
+roll(Num) ->
+ lists:sum([random:uniform(6) || _ <- lists:seq(1,Num)]).
diff --git a/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_sup.erl b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_sup.erl
new file mode 100644
index 0000000..de89a9a
--- /dev/null
+++ b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_sup.erl
@@ -0,0 +1,31 @@
+%%% Supervisor for each player. Goes over a pair of a
+%%% gen_fsm (pq_player) and gen_event (pq_events).
+-module(pq_sup).
+-behaviour(supervisor).
+-export([start_link/2]).
+-export([init/1]).
+
+
+start_link(Name, Info) ->
+ supervisor:start_link(?MODULE, {Name,Info}).
+
+%% The name is passed to the events so that
+%% it can register itself as {events, Name} into the
+%% 'regis' regsitry app.
+%% Same for pq_player, which also gets the info.
+%%
+%% It is important that pq_events is started before
+%% pq_player, otherwise we might create race conditions
+%% when starting a player and then quickly generating events to
+%% an event manager that doesn't exist.
+init({Name, Info}) ->
+ {ok,
+ {{one_for_all, 2, 3600},
+ [{events,
+ {pq_events, start_link, [Name]},
+ permanent, 5000, worker, [dynamic]},
+ {player,
+ {pq_player, start_link, [Name, Info]},
+ permanent, 2000, worker, [pq_player]}]}}.
+
+
diff --git a/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_supersup.erl b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_supersup.erl
new file mode 100644
index 0000000..577bbed
--- /dev/null
+++ b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/pq_supersup.erl
@@ -0,0 +1,28 @@
+%%% pq_supersup is the ProcessQuest top-level supervisor.
+%%% It sits over many pq_sup instances, allowing to have
+%%% a truckload of different players running at once.
+-module(pq_supersup).
+-behaviour(supervisor).
+-export([start_link/0, start_player/2, stop_player/1]).
+-export([init/1]).
+
+%% We register it so that it's guaranteed to be unique
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+%% Using a SOFO strategy because we get to have many
+%% supervisees of the same type.
+init([]) ->
+ {ok,
+ {{simple_one_for_one, 1, 60000},
+ [{sup,
+ {pq_sup, start_link, []},
+ permanent, infinity, supervisor, [pq_sup]}]}}.
+
+%% Starts an individual player
+start_player(Name, Info) ->
+ supervisor:start_child(?MODULE, [Name, Info]).
+
+%% Stops a player.
+stop_player(Name) ->
+ supervisor:terminate_child(?MODULE, regis:whereis(Name)).
diff --git a/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/processquest.erl b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/processquest.erl
new file mode 100644
index 0000000..469dcb0
--- /dev/null
+++ b/learn-you-some-erlang/processquest/apps/processquest-1.1.0/src/processquest.erl
@@ -0,0 +1,39 @@
+%%% ProcessQuest's main wrapping module.
+%%% Start ProcessQuest by calling application:start(processquest).
+%%% Create a player by calling processquest:start_player(Name, Info).
+%%% - Name is any term to identify the player
+%%% - Info is additional information to configure the player. Consult
+%%% the pq_player module for more info.
+%%%
+%%% You can subscribe to the player events by calling
+%%% processquest:subscribe(Name, Handler, Args).
+%%% The handler is a regular event handler. See test/pq_events_handler.erl.
+-module(processquest).
+-behaviour(application).
+-export([start/2, stop/1]).
+-export([start_player/2, stop_player/1, subscribe/3]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% APPLICATION CALLBACKS %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+start(normal, []) ->
+ pq_supersup:start_link().
+
+stop(_) -> ok.
+
+%%%%%%%%%%%%%%%%%%%%%%
+%%% USER INTERFACE %%%
+%%%%%%%%%%%%%%%%%%%%%%
+
+%% Starts a player
+start_player(Name, Info) ->
+ pq_supersup:start_player(Name, Info).
+
+%% Stops a player
+stop_player(Name) ->
+ pq_supersup:stop_player(Name).
+
+%% Subscribe to user events
+subscribe(Name, Handler, Args) ->
+ pq_events:add_handler(Name, Handler, Args).