1/*
    2 * >module> 
    3% NPC_Interface for calling actions
    4%
    5% "command"s are often text or a description of proposed actions
    6% "action" is a MUD understood command (GOAL)
    7%
    8% Douglas Miles
    9% Dec 13, 2035
   10%
   11*/
   12% :-swi_module(world_agent,[get_session_id/1]).
   13
   14:- include(prologmud(mud_header)).   15/*
   16:-swi_module(world_agent, [ agent_call_unparsed/1, agent_call_unparsed/2,  agent_call_command_now/2 ]).
   17
   18
   19tAgent - Players and Bot Bodies
   20
   21Sessions - places players control things
   22      - irc clients (no threads/consoles)
   23      - telnet users
   24
   25agent->nick
   26
   27
   28
   29*/
   30:- multifile baseKB:agent_action_queue/3.   31:- dynamic baseKB:agent_action_queue/3.   32
   33immediate_session(_P,_C,_O):-!.
   34
   35do_agent_action_queue(P):- agent_action_queue(P,C,O),must(with_session(O,agent_call_unparsed(P,C))),retract(agent_action_queue(P,C,O)).
   36do_agent_action_queue(_). % was empty already
   37
   38
   39:-export(enqueue_agent_action/1).   40enqueue_agent_action(C):-enqueue_agent_action(_,C).
   41
   42:-export(enqueue_agent_action/2).   43enqueue_agent_action(P,C):-foc_current_agent(P),get_agent_session(P,O),enqueue_agent_action(P,C,O).
   44
   45:-export(enqueue_agent_action/3).   46:-dynamic(enqueue_agent_action/3).   47enqueue_agent_action(P,C,O):- immediate_session(P,C,O),!, do_agent_action(P,C,O).
   48enqueue_agent_action(P,C,O):- assertz(agent_action_queue(P,C,O)),must(once(ain(agent_action_queue(P,C,O)))),!.
   49
   50:-export(do_agent_action/1).   51do_agent_action(C):-enqueue_agent_action(C).
   52:-export(do_agent_action/2).   53do_agent_action(P,C):-enqueue_agent_action(P,C).
   54:-export(do_agent_action/2).   55do_agent_action(P,C,O):- \+ immediate_session(P,C,O), !, enqueue_agent_action(P,C,O).
   56do_agent_action(P,C,_):- var(C),!,fmt('unknown_var_command(~q,~q).',[P,C]).
   57do_agent_action(_,EOF,_):- end_of_file == EOF, !, npc_tick_tock.
   58do_agent_action(_,'',_):-!, npc_tick_tock.
   59do_agent_action(P,C,O):- do_gc,with_session(O,agent_call_unparsed(P, C)),!.
   60do_agent_action(P,C,_):-wdmsg("skipping_unknown_player_action(~q,~q).~n",[P,C]),!.
   61
   62%check_word(SVERB):- var_non_attvar(SVERB),!, when(nonvar(SVERB),check_word(SVERB)),!.
   63check_word(SVERB):- atom(SVERB), atom_concat('[',_,SVERB),trace_or_throw(bad_parse_agent_text_command(SVERB)).
   64check_word(_).
   65
   66:-export(parse_agent_text_command_checked/5).   67parse_agent_text_command_checked(Agent,VERB,ARGS,NewAgent,CMD):- 
   68  % check_word(VERB), 
   69  catch(( parse_agent_text_command(Agent,VERB,ARGS,NewAgent,CMD),
   70         nonvar(CMD),must(nonvar(NewAgent))),'$aborted',true),
   71         ignore((CMD=actTick)),ignore((NewAgent=Agent)).
   72
   73parse_agent_text_command_checked(Agent,VERB,ARGS,NewAgent,CMD):- 
   74   debugging_logicmoo(logicmoo(parser)), parse_agent_text_command(Agent,VERB,ARGS,NewAgent,CMD).
   75
   76must_ac(G):- show_failure(must(G)).
   77
   78:-  message_queue_property(_Queue, alias(waq)) -> true;message_queue_create(_,[alias(waq)]).   79
   80thread_signal_blocked(ID,Goal):- thread_self(ID),!,Goal.
   81thread_signal_blocked(ID,Goal):- message_queue_property(Queue, alias(waq)),thread_self(Waiter), thread_signal(ID,(Goal,thread_send_message(Queue,done(Goal,Waiter),[]))),thread_get_message(Queue,done(Goal,Waiter)).
   82
   83
   84do_agent_action_queues:- repeat,sleep(0.25),once(on_x_log_cont(do_agent_action_queue(_))),fail.
   85
   86start_agent_action_thread:- 
   87  (thread_property(T,alias(agent_action_queue_thread)) ->
   88   (thread_property(T,status(running))->true;(thread_join(agent_action_queue_thread,_);
   89     thread_create(do_agent_action_queues,_,[alias(agent_action_queue_thread)])));
   90    thread_create(do_agent_action_queues,_,[alias(agent_action_queue_thread)])).
   91
   92
   93   
   94
   95% restarts if it it died
   96one_minute_timer_tick:- start_agent_action_thread.
   97
   98with_session(ID,CALL):-locally(t_l:session_id(ID),CALL).
   99
  100
  101% =====================================================================================================================
  102% agent_call_unparsed --> agent_call_words --> agent_call_command_now
  103% =====================================================================================================================
  104
  105agent_call_unparsed(C):-foc_current_agent(A),!,agent_call_unparsed(A,C).
  106
  107agent_call_unparsed(A,C):-  locally(tlbugger:old_no_repeats, must(agent_call_unparsed_0(A,C))).
  108
  109agent_call_unparsed_0(Agent,Var):-var(Var),trace_or_throw(var_agent_call_unparsed(Agent,Var)).
  110
  111agent_call_unparsed_0(_Gent,Atom):- (Atom='[]' ;Atom='''' ;Atom='""' ; (atomic(Atom);(atom(Atom),atom_length(Atom,0)))),!.
  112% execute a prolog command including prolog/0
  113agent_call_unparsed_0(_Gent,Atom):- atomic(Atom), catch((
  114   (once((on_x_fail(read_term_from_atom(Atom,OneCmd,[variables(VARS)])),
  115      predicate_property(OneCmd,_),
  116      fmt('doing command ~q~n',[OneCmd]))),!, doall((OneCmd,fmt('Yes: ~w',[VARS]))))),E,(dmsg(E),fail)).
  117
  118
  119% lists
  120agent_call_unparsed_0(A,Atom):-to_word_list(Atom,List),must(is_list(List)),!,agent_call_words(A,List).
  121
  122agent_call_words(_,Words):- Words==[],!.
  123agent_call_words(A,Words):- (\+ is_list(Words)),must(agent_call_unparsed(A,Words)),!.
  124agent_call_words(Agent,Text):- text_to_string_safe(Text,String)->Text\=@=String,!,agent_call_unparsed(Agent,String).
  125
  126
  127% remove period at end
  128agent_call_words(A,PeriodAtEnd):-append(New,[(.)],PeriodAtEnd),!,agent_call_words(A,New).
  129
  130% concat the '@'
  131agent_call_words(Ag,[A,B|REST]):- atom(A),atom(B),A=='@',atom_concat(A,B,C),!,agent_call_words(Ag,[C|REST]).
  132
  133agent_call_words(Agent,[VERB|ARGS]):-
  134
  135  check_word(VERB),
  136  %sanity(freeze(ARGS,must(is_list(ARGS)))),  
  137  %sanity(freeze(CMD,sanity(callable(CMD)))),
  138
  139      must(on_x_debug(parse_agent_text_command_checked(Agent,VERB,ARGS,NewAgent,CMD))),
  140      must_ac(agent_call_command_now(NewAgent,CMD)),!.
  141
  142agent_call_words(A,[CMD]):- !, must_ac(agent_call_command_now(A,CMD)),!.
  143agent_call_words(A,CMD):- must_ac(agent_call_command_now(A,CMD)),!.
  144
  145:-export(where_atloc/2).  146where_atloc(Agent,Where):-mudAtLoc(Agent,Where).
  147where_atloc(Agent,Where):-localityOfObject(Agent,Where).
  148where_atloc(Agent,Where):-mudAtLoc(Agent,Loc),!,locationToRegion(Loc,Where).
  149where_atloc(Agent,'OffStage'):-fail,nonvar(Agent).
  150
  151
  152% All Actions must be called from here!
  153agent_call_command_now(Agent,CMD  ):- var(CMD),!,trace_or_throw(var_agent_call_command_now(Agent,CMD)).
  154agent_call_command_now(Agent,Text ):- text_to_string_safe(Text,String)->show_call(loop_check(agent_call_unparsed(Agent,String))),!.
  155agent_call_command_now(Agent,CMD  ):- subst(CMD,isSelfAgent,Agent,NewCMD),CMD\=@=NewCMD,!,agent_call_command_now(Agent,NewCMD).
  156agent_call_command_now(Agent,Words):- is_list(Words),maplist(check_word,Words),loop_check(agent_call_words(Agent,Words)).
  157agent_call_command_now(Agent,CMD  ):- correctCommand(Agent,CMD,NewCMD),CMD\=@=NewCMD,!,agent_call_command_now(Agent,NewCMD).
  158agent_call_command_now(Agent,CMD  ):- \+ where_atloc(Agent,_),!, agent_call_command_now_2(Agent,CMD),!.
  159agent_call_command_now(Agent,CMD  ):- where_atloc(Agent,Where),
  160   % start event
  161   must(raise_location_event(Where,actNotice(reciever,begin(Agent,CMD)))),
  162   (call(on_x_debug(agent_call_command_now_2(Agent,CMD)) ->
  163   % event done
  164     send_command_completed_message(Agent,Where,done,CMD);
  165   % event fail
  166     send_command_completed_message(Agent,Where,failed,CMD))),!.
  167
  168agent_call_command_now_2(Agent,CMD):- loop_check((agent_call_command_now_3(Agent,CMD)),dmsg(looped(agent_call_command_now_2(Agent,CMD)))).
  169agent_call_command_now_3(Agent,CMD):-
  170   with_agent(Agent,
  171     locally(t_l:side_effect_ok,
  172     locally(t_l:agent_current_action(Agent,CMD),
  173  ((
  174  % call_no_cuts(agent_call_command(Agent,CMD))
  175    find_and_call(agent_call_command(Agent,CMD))
  176     *->true;agent_call_command_all_fallback(Agent,CMD)))))),
  177  padd(Agent,mudLastCommand(CMD)).
  178
  179agent_call_command_all_fallback(Agent,CMD):- if_defined(agent_call_command_fallback(Agent,CMD)),!.
  180agent_call_command_all_fallback(_Agent,CMD):- nop(xlisting(CMD)).
  181
  182:-export(send_command_completed_message/4).  183send_command_completed_message(Agent,Where,Done,CMD):-
  184     ignore((must_det_l((flush_output,renumbervars_prev(CMD,SCMD),Message =..[Done,Agent,SCMD],
  185                raise_location_event(Where,actNotice(reciever,Message)),flush_output)))),!.
  186
  187
  188
  189correctCommand(_,CMD,CMD):-!.
  190correctCommand(Who,CMD,OUT):-compound(CMD),show_failure(correctCommand_0(Who,CMD,OUT)),!.
  191correctCommand(_,CMD,CMD).
  192
  193correctEachTypeOrFail( Who, F, Q,ARGS,TYPES,NEWS):- is_list(TYPES),!,maplist(correctEachTypeOrFail(Who,F,Q),ARGS,TYPES,NEWS).
  194correctEachTypeOrFail(_Who,_F,_Q,Arg,Type,Inst):- not(is_ephemeral(Arg)),not(is_ephemeral(Type)),isa(Arg,Type),!,Inst = Arg.
  195correctEachTypeOrFail(_Who,_F,_Q,Arg,Type,Inst):- not(is_ephemeral(Arg)),not(is_ephemeral(Type)), must(coerce(Arg,Type,Inst)),not(is_ephemeral(Inst)),!.
  196correctEachTypeOrFail(_Who,_F,_Q,Arg,Type,Inst):- not(is_ephemeral(Arg)), show_failure(coerce(Arg,Type,Inst)),not(is_ephemeral(Inst)),!.
  197correctEachTypeOrFail(_Who,_F,_Q,Arg,Type,Inst):- coerce(Arg,Type,Inst),not(is_ephemeral(Inst)),!.
  198correctEachTypeOrFail(_Who,_F,_Q,Arg,Type,Inst):- !,acceptableArg(Arg,Type),!,Inst = Arg.
  199
  200correctCommand_0(Who,CMD,OUT):-
  201   compound(CMD),
  202   must(current_agent(Who)),
  203   CMD=..[F|ARGS],   
  204   functor(CMD,F,A),
  205   functor(MATCH,F,A),!,
  206   vtActionTemplate(MATCH),compound(MATCH),MATCH=..[F|TYPES],
  207   correctEachTypeOrFail(Who,F,query(t, must),ARGS,TYPES,NEWS),!,
  208   OUT=..[F|NEWS],!.
  209
  210acceptableArg(Arg,Type):-dmsg(acceptableArg(Arg,Type)).
  211
  212:-export(current_agent/1).  213current_agent(PIn):- get_session_id(O),get_agent_session(P,O),!,P=PIn.
  214% :-mpred_core:import(current_agent/1).
  215
  216:-export(current_agent_or_var/1).  217current_agent_or_var(P):- once(current_agent(PIn)),P=PIn,!.
  218current_agent_or_var(_).
  219
  220interesting_to_player(Type,Agent,C):- contains_var(C,Agent),dmsg(agent_database_hook(Type,C)),!.
  221interesting_to_player(Type,Agent,C):-is_asserted(localityOfObject(Agent,Region)),contains_var(C,Region),dmsg(region_database_hook(Type,C)),!.
  222interesting_to_player(Type,Agent,C):-is_asserted(localityOfObject(Agent,Region)),is_asserted(localityOfObject(Other,Region)),contains_var(C,Other),!,dmsg(other_database_hook(Type,C)),!.
  223
  224decl_database_hook(Type,C):- current_agent(Agent),interesting_to_player(Type,Agent,C).
  225
  226get_agent_input_stream(P,In):-no_repeats(P-In,(get_agent_session(P,O),lmcache:session_io(O,In,_,_))).
  227
  228get_agent_input_thread(P,Id):-no_repeats(P-Id,(get_agent_input_stream(P,In),lmcache:session_io(_,In,_,Id))).
  229
  230with_agent(P,CALL):-with_agent0(P,CALL).
  231
  232with_agent0(P,CALL):-
  233 get_session_id(TS),must(nonvar(TS)),
  234 thread_self(Self),
  235 get_agent_session(P,O),lmcache:session_io(O,In,_Out,Id),Id=Self,current_input(In),!,
  236 locally([t_l:put_server_no_max,lmcache:session_agent(TS,P),lmcache:agent_session(P,TS)],CALL).
  237
  238with_agent0(P,CALL):-
  239 get_session_id(TS),must(nonvar(TS)),
  240 thread_self(Self),
  241 ((get_agent_session(P,O),lmcache:session_io(O,_In,_Out,Id),Id\=Self)->Wrap=thread_signal_blocked(Id);Wrap=call),!,
  242  call(Wrap, 
  243    locally([t_l:put_server_no_max,lmcache:session_agent(TS,P),lmcache:agent_session(P,TS)],
  244      with_output_to_predicate(deliver_event(P),CALL))).
  245
  246has_tty(O):-no_repeats(O,lmcache:session_io(O,_,_,_)).
  247
  248:-export(get_agent_session/2).  249get_agent_session(P,O):-get_session_id(O),get_agent_sessions(P,O),!.
  250get_agent_session(P,O):-get_agent_sessions(P,O),has_tty(O).
  251:-export(get_agent_sessions/2).  252get_agent_sessions(P,O):- no_repeats(P-O,(lmcache:session_agent(O,P);lmcache:agent_session(P,O);(baseKB:irc_user_plays(P,O,C),ground(baseKB:irc_user_plays(P,O,C))))).
  253
  254:-export(foc_current_agent/1).  255foc_current_agent(P):- current_agent(P),nonvar(P),!.
  256foc_current_agent(P):- nonvar(P),tAgent(P),!,become_player(P),!.
  257foc_current_agent(P):- 
  258  must_det_l((    
  259             get_session_id(_),
  260             once((get_dettached_npc(NPC),NPC=P);generate_new_player(P)),!,
  261             become_player(P))),!.
  262               
  263
  264:-user:ensure_loaded(library(http/http_session)).  265
  266:-export(get_session_id/1).  267get_session_id(IDIn):-guess_session_ids(ID),nonvar(ID),!,ID=IDIn.
  268
  269% return any thread locally set session
  270guess_session_ids(ID):-t_l:session_id(ID).
  271% irc session
  272guess_session_ids(ID):-if_defined(chat_config:chat_isWith(_,ID),true).
  273% telnet session
  274guess_session_ids(ID):-thread_self(TID),lmcache:session_io(ID,_,_,TID).
  275% returns http sessions as well
  276guess_session_ids(ID):-if_defined(http_in_session:http_in_session(ID)).
  277% tcp session
  278guess_session_ids(ID):-thread_self(TID),thread_property(TID,alias(ID)).
  279% anonymous sessions
  280guess_session_ids(ID):-thread_self(ID), \+ thread_property(ID,alias(ID)).
  281% anonymous sessions
  282guess_session_ids(In):-thread_self(ID),call(call,thread_util:has_console(ID,In,_Out,_Err)).
  283
  284
  285:-export(my_random_member/2).  286my_random_member(ELE,[LOC]):-nonvar(LOC),!,ELE=LOC.
  287my_random_member(LOC,LOCS):- must_det((length(LOCS,Len),Len>0)),random_permutation(LOCS,LOCS2),!,member(LOC,LOCS2).
  288
  289:-multifile(system:random_instance/3).  290:-export(system:random_instance/3).  291:-meta_predicate(random_instance_no_throw(+,-,0)).  292
  293random_instance_no_throw(Type,Value,Test):- random_instance_no_throw0(Type,Value,Test).
  294
  295random_instance_no_throw0(Type,Value,Test):-var(Test),!,random_instance_no_throw(Type,Value,isa(Test,Type)).
  296
  297random_instance_no_throw0(Type,Value,Test):- fail, copy_term(ri(Type,Value,Test),ri(RType,RValue,RTest)),
  298  Responded = responded(_),
  299  (  ((call_u(hooked_random_instance(RType,RValue,RTest)) *-> nb_setarg(1,Responded,answered) ; fail)) ;
  300     (ground(Responded)->(!,fail);fail)),
  301   checkAnyType(query(_,_),RValue,Type,Value),
  302   once(Test).
  303
  304random_instance_no_throw0(Type,Value,Test):- atom(Type),atom_concat('random_',Type,Pred),
  305   Fact=..[Pred,Value],current_predicate(Pred/1),!,call(Fact),Test.
  306
  307random_instance_no_throw0(Type,Value,Test):- compound(Type),get_functor(Type,F),
  308  atom_concat('random_',F,Pred),current_predicate(F/1),
  309  Fact=..[Pred,Value],
  310  current_predicate(Pred/1),!,Fact,Test.
  311
  312random_instance_no_throw0(Type,Value,Test):- compound(Type),get_functor(Type,F,A),functor(Formatted,F,A),t(meta_argtypes,Formatted),
  313                         Formatted=..[F|ArgTypes],functor(Value,F,A),Value=..[F|ValueArgs],
  314                           must((maplist(random_instance_no_throw,ArgTypes,ValueArgs,_),Test)).
  315
  316random_instance_no_throw0(Type,Value,Test):-
  317   must(( findall(V,isa(V,Type),Possibles),Possibles\==[])),!,must((my_random_member(Value,Possibles),Test)).
  318
  319
  320system:random_instance(Type,Value,Test):- must(random_instance_no_throw(Type,Value,Test)).
  321
  322
  323get_dettached_npc(P):- random_instance_no_throw(tAgent,P, \+ isa(P,tHumanControlled)).
  324:- listing(get_dettached_npc/1).  325
  326
  327generate_new_player(P):- var(P),!,must_det_l((gensym(iExplorer,N), \+ ((isa_asserted(N,tAgent))),P=N,ensure_new_player(P))),!.
  328generate_new_player(P):- ensure_new_player(P),!.
  329
  330ensure_new_player(P):- must_det_l([nonvar(P),assert_isa(P,mobExplorer),assert_isa(P,tHumanControlled),assert_isa(P,tAgent)]),!.
  331
  332assumed_detached_player(P):- lmcache:agent_session(P,_),!,trace_or_throw(assumed_detached_player(P)).
  333assumed_detached_player(_).
  334
  335:-export(become_player/1).  336become_player(P):- once(current_agent(Was)),Was=P,!.
  337become_player(P):- get_session_id(O),retractall(lmcache:agent_session(_,O)),
  338  assert_isa(P,tHumanControlled),must(create_agent(P))->
  339  assumed_detached_player(P),asserta_new(lmcache:agent_session(P,O)),!,
  340  put_in_world(P).
  341
  342:-export(become_player/2).  343become_player(_Old,NewName):-become_player(NewName).
  344
  345% Lists all the agents in the run. Except for other monsters.
  346list_agents(Agents) :- agent_list(Agents), !.
  347list_agents(Agents) :- % build cache
  348	findall(NearAgent,req1(tAgent(NearAgent)),Agents),
  349	assert(agent_list(Agents)),!.
  350
  351:-export((agent_into_corpse/1, display_stats/1)).  352
  353tCol(tCorpse).
  354
  355% When an agent dies, it turns into a tCorpse.
  356% corpse is defined as an object in the *.objects.pl files
  357agent_into_corpse(Agent) :-
  358        must(mudAtLoc(Agent,LOC)),
  359        Newthing = iCorpseFn(Agent),
  360        assert_isa(Newthing,tCorpse),
  361	ain(mudAtLoc(Newthing,LOC)),
  362        del(mudAtLoc(Agent,LOC)),
  363	clr(mudStr(Agent,_)),
  364	clr(mudHeight(Agent,_)),
  365	clr(mudStm(Agent,_)),
  366	clr(mudSpd(Agent,_)).
  367
  368% Displays all the agents stats. Used at end of a run.
  369display_stats(Agents) :-
  370	forall(member(Agent,Agents),
  371	          (mudEnergy(Agent,Chg),
  372		  mudHealth(Agent,Dam),
  373		  mudScore(Agent,Scr),
  374		  findall(Obj,mudPossess(Agent,Obj),Inv),
  375		  write('Agent = '), write(Agent), nl,
  376		  write('Charge = '), write(Chg), write('  '),
  377		  write('Dam= ' ), write(Dam), write('  '),
  378		  write('Score = '), write(Scr), nl,
  379		  write('Inventory = '), write(Inv), nl)).
  380
  381:- all_source_file_predicates_are_transparent.