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,  baseKB: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 --> baseKB: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(baseKB:agent_call_command_now(NewAgent,CMD)),!.
  141
  142agent_call_words(A,[CMD]):- !, must_ac(baseKB:agent_call_command_now(A,CMD)),!.
  143agent_call_words(A,CMD):- must_ac(baseKB: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!
  153baseKB:agent_call_command_now(Agent,CMD  ):- var(CMD),!,trace_or_throw(var_baseKB:agent_call_command_now(Agent,CMD)).
  154baseKB:agent_call_command_now(Agent,Text ):- text_to_string_safe(Text,String)->show_call(loop_check(agent_call_unparsed(Agent,String))),!.
  155baseKB:agent_call_command_now(Agent,CMD  ):- subst(CMD,isSelfAgent,Agent,NewCMD),CMD\=@=NewCMD,!,baseKB:agent_call_command_now(Agent,NewCMD).
  156baseKB:agent_call_command_now(Agent,Words):- is_list(Words),maplist(check_word,Words),loop_check(agent_call_words(Agent,Words)).
  157baseKB:agent_call_command_now(Agent,CMD  ):- correctCommand(Agent,CMD,NewCMD),CMD\=@=NewCMD,!,baseKB:agent_call_command_now(Agent,NewCMD).
  158baseKB:agent_call_command_now(Agent,CMD  ):- \+ where_atloc(Agent,_),!, baseKB:agent_call_command_now_2(Agent,CMD),!.
  159baseKB:agent_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*/(baseKB: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
  168baseKB:agent_call_command_now_2(Agent,CMD):- 
  169  loop_check((baseKB:agent_call_command_now_3(Agent,CMD)),
  170   dmsg(looped(baseKB:agent_call_command_now_2(Agent,CMD)))).
  171
  172baseKB:agent_call_command_now_3(Agent,CMD):- 
  173  once(baseKB:agent_call_command_now_4(Agent,CMD)).
  174
  175baseKB:agent_call_command_now_4(Agent,CMD):-
  176   with_agent(Agent,
  177     locally(t_l:side_effect_ok,
  178     locally(t_l:agent_current_action(Agent,CMD),
  179  ((
  180  % call_no_cuts(baseKB:agent_call_command(Agent,CMD))
  181    find_and_call(baseKB:agent_call_command(Agent,CMD))
  182     *->true;baseKB:agent_call_command_all_fallback(Agent,CMD)))))),
  183  padd(Agent,mudLastCommand(CMD)).
  184
  185baseKB:agent_call_command_all_fallback(Agent,CMD):- if_defined(baseKB:agent_call_command_fallback(Agent,CMD)),!.
  186baseKB:agent_call_command_all_fallback(_Agent,CMD):- fail, nop(xlisting(CMD)).
  187
  188:-export(send_command_completed_message/4).  189send_command_completed_message(Agent,Where,Done,CMD):-
  190     ignore((must_det_l((flush_output,renumbervars_prev(CMD,SCMD),Message =..[Done,Agent,SCMD],
  191                raise_location_event(Where,actNotice(reciever,Message)),flush_output)))),!.
  192
  193
  194
  195correctCommand(_,CMD,CMD):-!.
  196correctCommand(Who,CMD,OUT):-compound(CMD),show_failure(correctCommand_0(Who,CMD,OUT)),!.
  197correctCommand(_,CMD,CMD).
  198
  199correctEachTypeOrFail( Who, F, Q,ARGS,TYPES,NEWS):- is_list(TYPES),!,maplist(correctEachTypeOrFail(Who,F,Q),ARGS,TYPES,NEWS).
  200correctEachTypeOrFail(_Who,_F,_Q,Arg,Type,Inst):- not(is_ephemeral(Arg)),not(is_ephemeral(Type)),isa(Arg,Type),!,Inst = Arg.
  201correctEachTypeOrFail(_Who,_F,_Q,Arg,Type,Inst):- not(is_ephemeral(Arg)),not(is_ephemeral(Type)), must(coerce(Arg,Type,Inst)),not(is_ephemeral(Inst)),!.
  202correctEachTypeOrFail(_Who,_F,_Q,Arg,Type,Inst):- not(is_ephemeral(Arg)), show_failure(coerce(Arg,Type,Inst)),not(is_ephemeral(Inst)),!.
  203correctEachTypeOrFail(_Who,_F,_Q,Arg,Type,Inst):- coerce(Arg,Type,Inst),not(is_ephemeral(Inst)),!.
  204correctEachTypeOrFail(_Who,_F,_Q,Arg,Type,Inst):- !,acceptableArg(Arg,Type),!,Inst = Arg.
  205
  206correctCommand_0(Who,CMD,OUT):-
  207   compound(CMD),
  208   must(current_agent(Who)),
  209   CMD=..[F|ARGS],   
  210   functor(CMD,F,A),
  211   functor(MATCH,F,A),!,
  212   vtActionTemplate(MATCH),compound(MATCH),MATCH=..[F|TYPES],
  213   correctEachTypeOrFail(Who,F,query(t, must),ARGS,TYPES,NEWS),!,
  214   OUT=..[F|NEWS],!.
  215
  216acceptableArg(Arg,Type):-dmsg(acceptableArg(Arg,Type)).
  217
  218:-export(current_agent/1).  219current_agent(PIn):- get_session_id(O),get_agent_session(P,O),!,P=PIn.
  220% :-mpred_core:import(current_agent/1).
  221
  222:-export(current_agent_or_var/1).  223current_agent_or_var(P):- once(current_agent(PIn)),P=PIn,!.
  224current_agent_or_var(_).
  225
  226interesting_to_player(Type,Agent,C):- contains_var(C,Agent),dmsg(agent_database_hook(Type,C)),!.
  227interesting_to_player(Type,Agent,C):-is_asserted(localityOfObject(Agent,Region)),contains_var(C,Region),dmsg(region_database_hook(Type,C)),!.
  228interesting_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)),!.
  229
  230decl_database_hook(Type,C):- current_agent(Agent),interesting_to_player(Type,Agent,C).
  231
  232get_agent_input_stream(P,In):-no_repeats(P-In,(get_agent_session(P,O),lmcache:session_io(O,In,_,_))).
  233
  234get_agent_input_thread(P,Id):-no_repeats(P-Id,(get_agent_input_stream(P,In),lmcache:session_io(_,In,_,Id))).
  235
  236with_agent(P,CALL):-with_agent0(P,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,current_input(In),!,
  242 locally([t_l:put_server_no_max,lmcache:session_agent(TS,P),lmcache:agent_session(P,TS)],CALL).
  243
  244with_agent0(P,CALL):-
  245 get_session_id(TS),must(nonvar(TS)),
  246 thread_self(Self),
  247 ((get_agent_session(P,O),lmcache:session_io(O,_In,_Out,Id),Id\=Self)->Wrap=thread_signal_blocked(Id);Wrap=call),!,
  248  call(Wrap, 
  249    locally([t_l:put_server_no_max,lmcache:session_agent(TS,P),lmcache:agent_session(P,TS)],
  250      with_output_to_predicate(deliver_event(P),CALL))).
  251
  252has_tty(O):-no_repeats(O,lmcache:session_io(O,_,_,_)).
  253
  254:-export(get_agent_session/2).  255get_agent_session(P,O):-get_session_id(O),get_agent_sessions(P,O),!.
  256get_agent_session(P,O):-get_agent_sessions(P,O),has_tty(O).
  257:-export(get_agent_sessions/2).  258get_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))))).
  259
  260:-export(foc_current_agent/1).  261foc_current_agent(P):- current_agent(P),nonvar(P),!.
  262foc_current_agent(P):- nonvar(P),tAgent(P),!,become_player(P),!.
  263foc_current_agent(P):- 
  264  must_det_l((    
  265             get_session_id(_),
  266             once((get_dettached_npc(NPC),NPC=P);generate_new_player(P)),!,
  267             become_player(P))),!.
  268               
  269
  270:-user:ensure_loaded(library(http/http_session)).  271
  272get_session_id(IDIn):-guess_session_ids(ID),nonvar(ID),!,ID=IDIn.
  273:-export(get_session_id/1).  274:-system:import(get_session_id/1).  275
  276% return any thread locally set session
  277guess_session_ids(ID):-t_l:session_id(ID).
  278% irc session
  279guess_session_ids(ID):-if_defined(chat_config:chat_isWith(_,ID),true).
  280% telnet session
  281guess_session_ids(ID):-thread_self(TID),lmcache:session_io(ID,_,_,TID).
  282% returns http sessions as well
  283guess_session_ids(ID):-if_defined(http_in_session:http_in_session(ID)).
  284% tcp session
  285guess_session_ids(ID):-thread_self(TID),thread_property(TID,alias(ID)).
  286% anonymous sessions
  287guess_session_ids(ID):-thread_self(ID), \+ thread_property(ID,alias(ID)).
  288% anonymous sessions
  289guess_session_ids(In):-thread_self(ID),call(call,thread_util:has_console(ID,In,_Out,_Err)).
  290
  291
  292:-export(my_random_member/2).  293my_random_member(ELE,[LOC]):-nonvar(LOC),!,ELE=LOC.
  294my_random_member(LOC,LOCS):- must_det((length(LOCS,Len),Len>0)),random_permutation(LOCS,LOCS2),!,member(LOC,LOCS2).
  295:-system:import(my_random_member/2).  296
  297:-meta_predicate(random_instance_no_throw(+,-,0)).  298random_instance_no_throw(Type,Value,Test):- random_instance_no_throw0(Type,Value,Test).
  299
  300random_instance_no_throw0(Type,Value,Test):-var(Test),!,random_instance_no_throw(Type,Value,isa(Test,Type)).
  301
  302random_instance_no_throw0(Type,Value,Test):- fail, copy_term(ri(Type,Value,Test),ri(RType,RValue,RTest)),
  303  Responded = responded(_),
  304  (  ((call_u(hooked_random_instance(RType,RValue,RTest)) *-> nb_setarg(1,Responded,answered) ; fail)) ;
  305     (ground(Responded)->(!,fail);fail)),
  306   checkAnyType(query(_,_),RValue,Type,Value),
  307   once(Test).
  308
  309random_instance_no_throw0(Type,Value,Test):- atom(Type),atom_concat('random_',Type,Pred),
  310   Fact=..[Pred,Value],current_predicate(Pred/1),!,call(Fact),Test.
  311
  312random_instance_no_throw0(Type,Value,Test):- compound(Type),get_functor(Type,F),
  313  atom_concat('random_',F,Pred),current_predicate(F/1),
  314  Fact=..[Pred,Value],
  315  current_predicate(Pred/1),!,Fact,Test.
  316
  317random_instance_no_throw0(Type,Value,Test):- compound(Type),get_functor(Type,F,A),functor(Formatted,F,A),t(meta_argtypes,Formatted),
  318                         Formatted=..[F|ArgTypes],functor(Value,F,A),Value=..[F|ValueArgs],
  319                           must((maplist(random_instance_no_throw,ArgTypes,ValueArgs,_),Test)).
  320
  321random_instance_no_throw0(Type,Value,Test):-
  322   must(( findall(V,isa(V,Type),Possibles),Possibles\==[])),!,((my_random_member(Value,Possibles),Test)).
  323
  324
  325get_dettached_npc(P):- random_instance_no_throw(tAgent,P, \+ isa(P,tHumanControlled)).
  326
  327
  328:-multifile(system:random_instance/3).  329:-dynamic(system:random_instance/3).  330:-export(system:random_instance/3).  331%:- rtrace.
  332system:random_instance(Type,Value,Test):- cwc, must(random_instance_no_throw(Type,Value,Test)).
  333%:- nortrace.
  334%:- break.
  335
  336
  337generate_new_player(P):- var(P),!,must_det_l((gensym(iExplorer,N), \+ ((isa_asserted(N,tAgent))),P=N,ensure_new_player(P))),!.
  338generate_new_player(P):- ensure_new_player(P),!.
  339
  340ensure_new_player(P):- must_det_l([nonvar(P),assert_isa(P,mobExplorer),assert_isa(P,tHumanControlled),assert_isa(P,tAgent)]),!.
  341
  342assumed_detached_player(P):- lmcache:agent_session(P,_),!,trace_or_throw(assumed_detached_player(P)).
  343assumed_detached_player(_).
  344
  345:-export(become_player/1).  346become_player(P):- once(current_agent(Was)),Was=P,!.
  347become_player(P):- get_session_id(O),retractall(lmcache:agent_session(_,O)),
  348  assert_isa(P,tHumanControlled),must(create_agent(P))->
  349  assumed_detached_player(P),asserta_new(lmcache:agent_session(P,O)),!,
  350  put_in_world(P).
  351
  352:-export(become_player/2).  353become_player(_Old,NewName):-become_player(NewName).
  354
  355% Lists all the agents in the run. Except for other monsters.
  356list_agents(Agents) :- agent_list(Agents), !.
  357list_agents(Agents) :- % build cache
  358	findall(NearAgent,req1(tAgent(NearAgent)),Agents),
  359	assert(agent_list(Agents)),!.
  360
  361:-export((agent_into_corpse/1, display_stats/1)).  362
  363tCol(tCorpse).
  364
  365% When an agent dies, it turns into a tCorpse.
  366% corpse is defined as an object in the *.objects.pl files
  367agent_into_corpse(Agent) :-
  368        must(mudAtLoc(Agent,LOC)),
  369        Newthing = iCorpseFn(Agent),
  370        assert_isa(Newthing,tCorpse),
  371	ain(mudAtLoc(Newthing,LOC)),
  372        del(mudAtLoc(Agent,LOC)),
  373	clr(mudStr(Agent,_)),
  374	clr(mudHeight(Agent,_)),
  375	clr(mudStm(Agent,_)),
  376	clr(mudSpd(Agent,_)).
  377
  378% Displays all the agents stats. Used at end of a run.
  379display_stats(Agents) :-
  380	forall(member(Agent,Agents),
  381	          (mudEnergy(Agent,Chg),
  382		  mudHealth(Agent,Dam),
  383		  mudScore(Agent,Scr),
  384		  findall(Obj,mudPossess(Agent,Obj),Inv),
  385		  write('Agent = '), write(Agent), nl,
  386		  write('Charge = '), write(Chg), write('  '),
  387		  write('Dam= ' ), write(Dam), write('  '),
  388		  write('Score = '), write(Scr), nl,
  389		  write('Inventory = '), write(Inv), nl)).
  390
  391:- fixup_exports.  392:- all_source_file_predicates_are_transparent.