home *** CD-ROM | disk | FTP | other *** search
/ Amiga MA Magazine 1998 #7 / amigamamagazinepolishissue1998.iso / rozrywka / rpg / amigamud / src / scenario / news.m < prev    next >
Text File  |  1997-05-30  |  24KB  |  958 lines

  1. /*
  2.  * Amiga MUD
  3.  *
  4.  * Copyright (c) 1997 by Chris Gray
  5.  */
  6.  
  7. /*
  8.  * news.m - allow the player to interact with usenet news.
  9.  */
  10.  
  11. /* NOTE: this current setup is intended for use with Matt Dillon's UUCP
  12.    and news distribution, version 1.16 or later. To use it with other
  13.    forms of news, you will perhaps need to change various things.
  14.    Dependencies: UULIB:NewsGroups, a hierarchical set of news directories
  15.    in UUNEWS:, postnews, T: .
  16.  
  17.    NOTE: as of UUCP-V1.17b4, the s:UUConfig file allows the UUCP user to
  18.    avoid having as many UUCP-related assigns. This AmigaMUD code does
  19.    not attempt to read that file - it assumes the assigns it needs are
  20.    present: UUNEWS: and UULIB: */
  21.  
  22. use t_streets
  23.  
  24. /* The following property is actually added to SysAdmin. It is used as a
  25.    global variable to check whether this MUD allows players to set their
  26.    own realnames. Since mail/news can go all over the world, many sysadmins
  27.    may prefer that everything that originates with their site at least have
  28.    a valid and traceable name attached to it. Set the value to 'true' if
  29.    you want people to be able to change their own realnames. Think seriously
  30.    about it before you do it. */
  31.  
  32. define tp_news NewsThing CreateThing(nil)$
  33. define tp_news p_pCanChangeName CreateBoolProp()$
  34. NewsThing@p_pCanChangeName := false$
  35.  
  36. define tp_news g_news CreateGrammar()$
  37.  
  38. define tp_news p_pNRealName CreateStringProp()$
  39. define tp_news p_pNSaveHandler CreateActionProp()$
  40. define tp_news p_pNSavePrompt CreateStringProp()$
  41. define tp_news p_pNGroupList CreateThingListProp()$
  42. define tp_news p_pNCurrentGroup CreateIntProp()$
  43. define tp_news p_pNUnreadIndex CreateIntProp()$
  44. define tp_news p_pNCurrentArticle CreateIntProp()$
  45. define tp_news p_pNFd CreateIntProp()$
  46. define tp_news p_pNString CreateStringProp()$
  47. define tp_news p_pNString2 CreateStringProp()$
  48. define tp_news p_pNSubject CreateStringProp()$
  49.  
  50. define tp_news p_NGroupName CreateStringProp()$
  51. define tp_news p_NHighestRead CreateIntProp()$
  52. define tp_news p_NLowest CreateIntProp()$
  53. define tp_news p_NHighest CreateIntProp()$
  54. define tp_news p_NUnread CreateIntListProp()$
  55.  
  56. CharacterThing(SysAdmin)@p_pNRealName := "MUD administrator"$
  57.  
  58. define tp_news proc nv_help()bool:
  59.     Print(
  60. "Commands in the newsroom are:\n\n"
  61. "  info - some additional comments on MUD news\n"
  62. "  groups/newsgroups - print the available groups\n"
  63. "  subscribe/sub - print the groups you are subscribed to\n"
  64. "  subscribe/sub \"group\" - subscribe to the given group\n"
  65. "  unsubscribe/unsub \"group\" - unsubscribe from the given group\n"
  66. "  catchup \"group\" - mark all current articles as read\n"
  67. "  read - start reading news\n"
  68. "  name/realname <name> - set your real name to <name>\n"
  69. "  post \"group\" [subject] - post an article to the given group\n"
  70.     );
  71.     true
  72. corp;
  73.  
  74. Verb0(g_news, "help", 0, nv_help)$
  75. Synonym(g_news, "help", "?")$
  76.  
  77. define tp_news proc nv_info()bool:
  78.  
  79.     Print(
  80. "Newsgroup names almost always contain periods, so you must enclose them in "
  81. "quotes to prevent MUD's normal parsing from treating e.g.\n"
  82. "    subscribe rec.games.mud.misc\n"
  83. "as 'subscribe rec' followed by 'games' followed by 'mud' followed by 'misc'. "
  84. "The optional subject on the 'post' command can be quoted or not, depending "
  85. "on whether you need to put punctuation in it. Similarly, your real name on "
  86. "the 'name' command can be quoted or not. The MUD newsreader is quite "
  87. "primitive - it doesn't understand message threads, followups, replies, etc. "
  88. "If you need to use usenet news a lot, you should try to get a normal usenet "
  89. "connection.\n"
  90.     );
  91.     true
  92. corp;
  93.  
  94. Verb0(g_news, "info", 0, nv_info)$
  95.  
  96. define tp_news proc nv_groups()bool:
  97.     int fd, blank;
  98.     string line;
  99.     bool first;
  100.  
  101.     fd := FileOpenForRead("UULIB:NewsGroups");
  102.     if fd = 0 then
  103.     Print("This MUD is not set up to offer news reading.\n");
  104.     else
  105.     Print("Newsgroups available on this machine:\n");
  106.     first := true;
  107.     while
  108.         line := FileRead(fd);
  109.         line ~= ""
  110.     do
  111.         blank := Index(line, " ");
  112.         if blank < 0 then
  113.         blank := Index(line, "\t");
  114.         fi;
  115.         if blank > 0 then
  116.         line := SubString(line, 0, blank);
  117.         fi;
  118.         if not line == "Junk" then
  119.         if first then
  120.             first := false;
  121.         else
  122.             Print(", ");
  123.         fi;
  124.         Print(line);
  125.         fi;
  126.     od;
  127.     Print("\n");
  128.     FileClose(fd);
  129.     fi;
  130.     true
  131. corp;
  132.  
  133. Verb0(g_news, "groups", 0, nv_groups)$
  134. Synonym(g_news, "groups", "group")$
  135. Synonym(g_news, "groups", "newsgroups")$
  136.  
  137. define tp_news proc nv_subscribe(string group)bool:
  138.     thing me, th;
  139.     int count, n, fd;
  140.     list thing lt;
  141.     string line;
  142.  
  143.     me := Me();
  144.     lt := me@p_pNGroupList;
  145.     if group = "" then
  146.     if lt = nil or Count(lt) = 0 then
  147.         Print("You are not currently subscribed to any groups.\n");
  148.     else
  149.         Print("You are currently subscribed to the following groups:\n");
  150.         count := Count(lt);
  151.         n := 0;
  152.         while n ~= count do
  153.         Print("  ");
  154.         Print(lt[n]@p_NGroupName);
  155.         Print("\n");
  156.         n := n + 1;
  157.         od;
  158.     fi;
  159.     else
  160.     fd := FileOpenForRead("UULIB:NewsGroups");
  161.     if fd = 0 then
  162.         Print("This MUD is not set up to offer news reading.\n");
  163.     else
  164.         if lt = nil then
  165.         lt := CreateThingList();
  166.         me@p_pNGroupList := lt;
  167.         fi;
  168.         while
  169.         line := FileRead(fd);
  170.         if line = "" then
  171.             false
  172.         else
  173.             n := Index(line, " ");
  174.             if n < 0 then
  175.             n := Index(line, "\t");
  176.             fi;
  177.             if n > 0 then
  178.             line := SubString(line, 0, n);
  179.             fi;
  180.             not line == group
  181.         fi
  182.         do
  183.         od;
  184.         FileClose(fd);
  185.         if line = "" then
  186.         Print(
  187.             "This MUD does not have group \"" + group + "\".\n"
  188.             "You probably need to enclose the group name in quotes.\n"
  189.         );
  190.         elif FindName(me@p_pNGroupList, p_NGroupName, group) ~= fail then
  191.         Print("You already subscribe to group \"" + group + "\".\n");
  192.         else
  193.         th := CreateThing(nil);
  194.         th@p_NGroupName := group;
  195.         th@p_NHighestRead := 0;
  196.         th@p_NUnread := CreateIntList();
  197.         th@p_NLowest := 0;
  198.         th@p_NHighest := 0;
  199.         AddTail(lt, th);
  200.         Print("Subscribed to group \"" + group + "\".\n");
  201.         fi;
  202.     fi;
  203.     fi;
  204.     true
  205. corp;
  206.  
  207. Verb1(g_news, "subscribe", 0, nv_subscribe)$
  208. Synonym(g_news, "subscribe", "sub")$
  209.  
  210. define tp_news proc nv_unsubscribe(string name)bool:
  211.     thing me, group;
  212.     list thing lt;
  213.  
  214.     if name = "" then
  215.     Print("You must specify a group to unsubscribe from.\n");
  216.     false
  217.     else
  218.     me := Me();
  219.     lt := me@p_pNGroupList;
  220.     if lt = nil or FindName(me@p_pNGroupList, p_NGroupName, name) = fail
  221.     then
  222.         Print("You are not subscribed to group \"" + name + "\".\n");
  223.     else
  224.         group := FindResult();
  225.         ClearThing(group);
  226.         DelElement(lt, group);
  227.         Print("Unsubscribed from group \"" + name + "\".\n");
  228.     fi;
  229.     true
  230.     fi
  231. corp;
  232.  
  233. Verb1(g_news, "unsubscribe", 0, nv_unsubscribe)$
  234. Synonym(g_news, "unsubscribe", "unsub")$
  235.  
  236. define tp_news proc newsParse(string input)void: corp;
  237. define tp_news proc newsGroupParse(string line)void: corp;
  238. define tp_news proc newsHeaderParse(string line)void: corp;
  239.  
  240. define tp_news proc readDotNextFile(string name)int:
  241.     int i;
  242.     string path, line;
  243.  
  244.     path := name;
  245.     while
  246.     i := Index(path, ".");
  247.     i ~= -1
  248.     do
  249.     path := StringReplace(path, i, "/");
  250.     od;
  251.     i := FileOpenForRead("UUNEWS:" + path + "/.next");
  252.     if i = 0 then
  253.     Print(name + ": no such newsgroup???\n");
  254.     else
  255.     /* Grrr. This was not needed in older UUCP stuff! Note that it WILL
  256.        work for multiple simultaneous readers, since this whole set of
  257.        stuff is done atomically for a given client. */
  258.     FileClose(i);
  259.     i := FileOpenForWrite("t:crfile");
  260.     FileWrite(i, "\n");
  261.     FileClose(i);
  262.     Execute("join UUNEWS:" + path + "/.next t:crfile as t:.next");
  263.     i := FileOpenForRead("t:.next");
  264.     if i ~= 0 then
  265.         line := FileRead(i);
  266.         FileClose(i);
  267.         i := StringToInt(line);
  268.         if i <= 0 then
  269.         Print(name + ": something wrong in .next file!\n");
  270.         i := 0;
  271.         fi;
  272.     fi;
  273.     Execute("delete t:crfile t:.next");
  274.     fi;
  275.     i
  276. corp;
  277.  
  278. define tp_news proc openArticleFile(string name; int article)int:
  279.     int i;
  280.     string path;
  281.  
  282.     path := name;
  283.     while
  284.     i := Index(path, ".");
  285.     i ~= -1
  286.     do
  287.     path := StringReplace(path, i, "/");
  288.     od;
  289.     FileOpenForRead("UUNEWS:" + path + "/" + IntToString(article))
  290. corp;
  291.  
  292. define tp_news proc nv_catchUp(string name)bool:
  293.     thing me, group;
  294.     list thing lt;
  295.     int nextArticle;
  296.  
  297.     if name = "" then
  298.     Print("You must specify a group to catchup in.\n");
  299.     false
  300.     else
  301.     me := Me();
  302.     lt := me@p_pNGroupList;
  303.     if lt = nil or FindName(me@p_pNGroupList, p_NGroupName, name) = fail
  304.     then
  305.         Print("You are not subscribed to group \"" + name + "\".\n");
  306.     else
  307.         group := FindResult();
  308.         nextArticle := readDotNextFile(name);
  309.         if nextArticle >= 0 then
  310.         if nextArticle = 1 then
  311.             Print("No articles to catch up yet!\n");
  312.         else
  313.             group@p_NHighestRead := nextArticle - 1;
  314.             Print("Caught up in group \"" + name + "\".\n");
  315.         fi;
  316.         fi;
  317.     fi;
  318.     true
  319.     fi
  320. corp;
  321.  
  322. Verb1(g_news, "catchup", 0, nv_catchUp)$
  323.  
  324. define tp_news proc newsFindNextGroup()bool:
  325.     list thing lt;
  326.     thing me, group;
  327.     int which, groupCount, fd, nextArticle, cnt, lo, hi, mid;
  328.     string name, prompt;
  329.     list int li;
  330.     bool gotOne;
  331.  
  332.     me := Me();
  333.     lt := me@p_pNGroupList;
  334.     groupCount := Count(lt);
  335.     gotOne := false;
  336.     which := me@p_pNCurrentGroup;
  337.     while
  338.     if which >= groupCount then
  339.         false
  340.     else
  341.         group := lt[which];
  342.         name := group@p_NGroupName;
  343.         nextArticle := readDotNextFile(name);
  344.         if nextArticle <= 1 then
  345.         true
  346.         else
  347.         lo := group@p_NHighestRead;
  348.         hi := nextArticle - 1;
  349.         while lo < hi do
  350.             mid := (lo + hi) / 2;
  351.             fd := openArticleFile(name, mid);
  352.             if fd = 0 then
  353.             lo := mid + 1;
  354.             else
  355.             FileClose(fd);
  356.             hi := mid - 1;
  357.             fi;
  358.         od;
  359.         /* sometimes miss by one */
  360.         fd := openArticleFile(name, lo);
  361.         if fd = 0 then
  362.             lo := lo + 1;
  363.         else
  364.             FileClose(fd);
  365.         fi;
  366.         group@p_NLowest := lo;
  367.         lo := lo - 1;
  368.         if group@p_NHighestRead < lo then
  369.             group@p_NHighestRead := lo;
  370.         fi;
  371.         hi := nextArticle - 1;
  372.         group@p_NHighest := hi;
  373.         lo := group@p_NHighestRead;
  374.         prompt := "Group " + name + " has ";
  375.         if lo < hi then
  376.             gotOne := true;
  377.             prompt := prompt + IntToString(hi - lo) + " new";
  378.         fi;
  379.         li := group@p_NUnread;
  380.         cnt := Count(li);
  381.         mid := 0;
  382.         lo := 0;
  383.         hi := 0;
  384.         while hi < cnt do
  385.             mid := li[hi];
  386.             fd := openArticleFile(name, mid);
  387.             if fd ~= 0 then
  388.             FileClose(fd);
  389.             lo := lo + 1;
  390.             fi;
  391.             hi := hi + 1;
  392.         od;
  393.         if lo ~= 0 then
  394.             if not gotOne then
  395.             gotOne := true;
  396.             else
  397.             prompt := prompt + " and ";
  398.             fi;
  399.             prompt := prompt + IntToString(lo) + " old";
  400.         fi;
  401.         if gotOne then
  402.             Print(prompt + " unread articles.\n");
  403.             false
  404.         else
  405.             true
  406.         fi
  407.         fi
  408.     fi
  409.     do
  410.     which := which + 1;
  411.     od;
  412.     me@p_pNCurrentGroup := which;
  413.     gotOne
  414. corp;
  415.  
  416. define tp_news proc newsAllDone()void:
  417.  
  418.     ignore SetPrompt("newsroom> ");
  419.     ignore SetCharacterInputAction(newsParse);
  420. corp;
  421.  
  422. define tp_news proc newsReadGroup()void:
  423.  
  424.     Me()@p_pNUnreadIndex := 0;
  425.     ignore SetPrompt("Rnq? ");
  426.     ignore SetCharacterInputAction(newsGroupParse);
  427. corp;
  428.  
  429. define tp_news proc newsNextGroup()void:
  430.  
  431.     Me()@p_pNCurrentGroup := Me()@p_pNCurrentGroup + 1;
  432.     if newsFindNextGroup() then
  433.     newsReadGroup();
  434.     else
  435.     newsAllDone();
  436.     fi;
  437. corp;
  438.  
  439. define tp_news proc newsFindNextArticle()bool:
  440.     thing me, group;
  441.     int article;
  442.     list int unread;
  443.     string line;
  444.  
  445.     me := Me();
  446.     group := me@p_pNGroupList[me@p_pNCurrentGroup];
  447.     article := me@p_pNUnreadIndex;
  448.     unread := group@p_NUnread;
  449.     if article >= Count(unread) then
  450.     article := group@p_NHighestRead + 1;
  451.     if article > group@p_NHighest then
  452.         false
  453.     else
  454.         me@p_pNCurrentArticle := article;
  455.         true
  456.     fi
  457.     else
  458.     me@p_pNCurrentArticle := unread[article];
  459.     true
  460.     fi
  461. corp;
  462.  
  463. define tp_news proc newsShowHeader()bool:
  464.     thing me;
  465.     int article, fd, colon;
  466.     string line, header;
  467.  
  468.     me := Me();
  469.     fd := openArticleFile(
  470.         me@p_pNGroupList[me@p_pNCurrentGroup]@p_NGroupName,
  471.         me@p_pNCurrentArticle);
  472.     if fd ~= 0 then
  473.     while
  474.         line := FileRead(fd);
  475.         if line = "" then
  476.         false
  477.         else
  478.         colon := Index(line, ":");
  479.         if colon <= 0 then
  480.             false
  481.         else
  482.             header := SubString(line, 0, colon);
  483.             Index(header, " ") < 0
  484.         fi
  485.         fi
  486.     do
  487.         Print(line);
  488.         Print("\n");
  489.     od;
  490.     me@p_pNFd := fd;
  491.     if line ~= "" then
  492.         me@p_pNString := line;
  493.     fi;
  494.     true
  495.     else
  496.     false
  497.     fi
  498. corp;
  499.  
  500. define tp_news proc newsNextArticle()void:
  501.     thing me, group;
  502.     int index;
  503.  
  504.     me := Me();
  505.     group := me@p_pNGroupList[me@p_pNCurrentGroup];
  506.     if newsFindNextArticle() then
  507.     if newsShowHeader() then
  508.         ignore SetPrompt("Rnsq? ");
  509.         ignore SetCharacterInputAction(newsHeaderParse);
  510.     else
  511.         Print("Hmm. Article has disappeared!\n");
  512.     fi;
  513.     else
  514.     Print("End of group " + group@p_NGroupName + ".\n");
  515.     newsNextGroup();
  516.     fi;
  517. corp;
  518.  
  519. define tp_news proc newsDoneArticle()void:
  520.     thing me, group;
  521.     int index;
  522.  
  523.     me := Me();
  524.     index := me@p_pNUnreadIndex;
  525.     group := me@p_pNGroupList[me@p_pNCurrentGroup];
  526.     if index < Count(group@p_NUnread) then
  527.     RemHead(group@p_NUnread);
  528.     else
  529.     group@p_NHighestRead := group@p_NHighestRead + 1;
  530.     fi;
  531.     newsNextArticle();
  532. corp;
  533.  
  534. define tp_news proc newsFileClose()void:
  535.     int fd;
  536.  
  537.     fd := Me()@p_pNFd;
  538.     if fd ~= 0 then
  539.     FileClose(fd);
  540.     Me()@p_pNFd := 0;
  541.     fi;
  542. corp;
  543.  
  544. define tp_news proc newsUnreadArticle()void:
  545.     thing me, group;
  546.  
  547.     me := Me();
  548.     group := me@p_pNGroupList[me@p_pNCurrentGroup];
  549.     if me@p_pNUnreadIndex >= Count(group@p_NUnread) then
  550.     AddTail(group@p_NUnread, me@p_pNCurrentArticle);
  551.     me@p_pNUnreadIndex := me@p_pNUnreadIndex + 1;
  552.     group@p_NHighestRead := group@p_NHighestRead + 1;
  553.     fi;
  554.     Print("Marking article as unread.\n");
  555.     newsNextArticle();
  556. corp;
  557.  
  558. define tp_news proc newsEndParse(string line)void:
  559.  
  560.     if line = "" or line == "n" then
  561.     newsDoneArticle();
  562.     elif line == "s" then
  563.     newsUnreadArticle();
  564.     elif line == "q" then
  565.     newsNextGroup();
  566.     else
  567.     Print("Options are:\n"
  568.         "  n - go on to next article\n"
  569.         "  s - mark article as unread\n"
  570.         "  q - exit from this group\n"
  571.         "Empty line is equivalent to 'n'\n"
  572.     );
  573.     fi;
  574. corp;
  575.  
  576. define tp_news proc newsShowPage()bool:
  577.     thing me;
  578.     int fd, length, n, width, len;
  579.     string line;
  580.  
  581.     me := Me();
  582.     fd := me@p_pNFd;
  583.     length := TextHeight(0);
  584.     width := TextWidth(0);
  585.     line := me@p_pNString;
  586.     me -- p_pNString;
  587.     n := 1;
  588.     while
  589.     if n >= length - 1 then
  590.         false
  591.     else
  592.         if line = "" then
  593.         line := FileRead(fd);
  594.         fi;
  595.         if line = "" then
  596.         FileClose(fd);
  597.         me@p_pNFd := 0;
  598.         ignore SetPrompt("Nsq? ");
  599.         ignore SetCharacterInputAction(newsEndParse);
  600.         false
  601.         else
  602.         true
  603.         fi
  604.     fi
  605.     do
  606.     Print(line);
  607.     Print("\n");
  608.     len := Length(line);
  609.     if len <= width then
  610.         n := n + 1;
  611.     else
  612.         n := n + (len + width - 4) / (width - 9);
  613.     fi;
  614.     line := "";
  615.     od;
  616.     line ~= ""
  617. corp;
  618.  
  619. define tp_news proc newsBodyParse(string line)void:
  620.  
  621.     if line = "" or line == "c" then
  622.     ignore newsShowPage();
  623.     elif line == "n" then
  624.     newsFileClose();
  625.     newsDoneArticle();
  626.     elif line == "s" then
  627.     newsFileClose();
  628.     newsUnreadArticle();
  629.     elif line == "q" then
  630.     newsFileClose();
  631.     newsNextGroup();
  632.     else
  633.     Print("Options are:\n"
  634.         "  c - continue with article\n"
  635.         "  n - go on to next article\n"
  636.         "  s - mark article as unread\n"
  637.         "  q - exit from this group\n"
  638.         "Empty line is equivalent to 'c'\n"
  639.     );
  640.     fi;
  641. corp;
  642.  
  643. replace newsHeaderParse(string line)void:
  644.  
  645.     if line = "" or line == "r" or line == "y" then
  646.     if newsShowPage() then
  647.         ignore SetCharacterInputAction(newsBodyParse);
  648.         ignore SetPrompt("[M O R E] Cnsq? ");
  649.     fi;
  650.     elif line == "n" then
  651.     newsFileClose();
  652.     newsDoneArticle();
  653.     elif line == "s" then
  654.     newsFileClose();
  655.     newsUnreadArticle();
  656.     elif line == "q" then
  657.     newsFileClose();
  658.     newsNextGroup();
  659.     else
  660.     Print("Options are:\n"
  661.         "  r - read the body of this article\n"
  662.         "  n - go on to next article\n"
  663.         "  s - mark article as unread\n"
  664.         "  q - exit from this group\n"
  665.         "Empty line is equivalent to 'r'\n"
  666.     );
  667.     fi;
  668. corp;
  669.  
  670. replace newsGroupParse(string line)void:
  671.  
  672.     if line = "" or line == "r" then
  673.     newsNextArticle();
  674.     elif line == "n" then
  675.     newsNextGroup();
  676.     elif line == "q" then
  677.     newsAllDone();
  678.     else
  679.     Print("Options are:\n"
  680.         "  r - read this group\n"
  681.         "  n - go to next group with unread articles\n"
  682.         "  q - exit to top level\n"
  683.         "Empty line is equivalent to 'r'\n"
  684.     );
  685.     fi;
  686. corp;
  687.  
  688. define tp_news proc nv_read()bool:
  689.     thing me;
  690.  
  691.     me := Me();
  692.     if me@p_pNGroupList ~= nil then
  693.     me@p_pNCurrentGroup := 0;
  694.     if newsFindNextGroup() then
  695.         newsReadGroup();
  696.     else
  697.         Print("No unread news in subscribed-to groups.\n");
  698.     fi;
  699.     else
  700.     Print("You are not subscribed to any groups.\n");
  701.     fi;
  702.     true
  703. corp;
  704.  
  705. Verb0(g_news, "read", 0, nv_read)$
  706.  
  707. replace newsParse(string input)void:
  708.     string word;
  709.  
  710.     SetTail(input);
  711.     word := GetWord();
  712.     if word ~= "" then
  713.     if FindAnyWord(g_news, word) ~= 0 then
  714.         ignore Parse(g_news, input);
  715.     else
  716.         ignore Parse(G, input);
  717.     fi;
  718.     fi;
  719. corp;
  720.  
  721. define tp_news proc newsPostArticle(string s)void:
  722.     int fd;
  723.     thing me;
  724.  
  725.     me := Me();
  726.     if s = "" then
  727.     Print("Empty article - not posted.\n");
  728.     else
  729.     /* note: server runs atomically, so no conflict over the temp file */
  730.     fd := FileOpenForWrite("T:MUD.article");
  731.     if fd = 0 then
  732.         Print("Sorry - can't open article file.\n");
  733.     else
  734.         /* You might be tempted to put a 'Run' in front of the postnews
  735.            command, so that the player is not hung waiting for an
  736.            automatic 'batchnews' run. Beware, however, since doing so
  737.            might allow 'batchnews' to be run twice at the same time, with
  738.            the resultant news duplication. */
  739.         Print("Posting article (could take a while) ...\n");
  740.         OPrint("");     /* force a flush */
  741.         Log("'" + me@p_pName + "' posting article to " + me@p_pNString2 +
  742.         "\n");
  743.         FileWrite(fd, "Newsgroups: " + me@p_pNString2 + "\n");
  744.         me -- p_pNString2;
  745.         if me@p_pNSubject ~= "" then
  746.         FileWrite(fd, "Subject: " + me@p_pNSubject + "\n");
  747.         me -- p_pNSubject;
  748.         fi;
  749.         FileWrite(fd, "X-NewsSoftware: AmigaMUD newsroom\n");
  750.         FileWrite(fd, "\n");
  751.         FileWrite(fd, s);
  752.         FileClose(fd);
  753.         Execute("postnews < T:MUD.article -f \"" + me@p_pName +
  754.             "\" -r \"" + me@p_pNRealName + "\""
  755.         );
  756.         Execute("delete T:MUD.article");
  757.         Print("... done!\n");
  758.     fi;
  759.     fi;
  760. corp;
  761.  
  762. define tp_news proc nv_post()bool:
  763.     int fd, blank;
  764.     string groupName, line;
  765.  
  766.     groupName := GetWord();
  767.     if groupName = "" then
  768.     Print("You must specify the name of the newsgroup to post to.\n");
  769.     false
  770.     else
  771.     fd := FileOpenForRead("UULIB:NewsGroups");
  772.     if fd = 0 then
  773.         Print("This MUD is not set up to offer news reading.\n");
  774.         false
  775.     elif Me()@p_pNRealName = "" then
  776.         Print("You cannot post until you have a realname set up.\n");
  777.         false
  778.     else
  779.         while
  780.         line := FileRead(fd);
  781.         if line = "" then
  782.             false
  783.         else
  784.             blank := Index(line, " ");
  785.             if blank < 0 then
  786.             blank := Index(line, "\t");
  787.             fi;
  788.             if blank > 0 then
  789.             line := SubString(line, 0, blank);
  790.             fi;
  791.             not line == groupName
  792.         fi
  793.         do
  794.         od;
  795.         FileClose(fd);
  796.         if line = "" then
  797.         Print(
  798.             "This MUD does not have group \"" + groupName + "\".\n"
  799.             "You probably need to enclose the group name in quotes.\n"
  800.         );
  801.         false
  802.         else
  803.         Me()@p_pNString2 := groupName;
  804.         groupName := GetTail();
  805.         if groupName ~= "" then
  806.             if SubString(groupName, 0, 1) = "\"" then
  807.             groupName :=
  808.                 SubString(groupName, 1, Length(groupName) - 2);
  809.             fi;
  810.             Me()@p_pNSubject := groupName;
  811.         fi;
  812.         GetDocument("post> ", "Enter news article", "",
  813.                 newsPostArticle, true)
  814.         fi
  815.     fi
  816.     fi
  817. corp;
  818.  
  819. VerbTail(g_news, "post", nv_post)$
  820.  
  821. define tp_news proc nv_name()bool:
  822.     thing me;
  823.     string newName;
  824.  
  825.     if NewsThing@p_pCanChangeName then
  826.     me := Me();
  827.     newName := GetTail();
  828.     if newName = "" then
  829.         Print(
  830.         "To set up a real name for use with mail and news, use, e.g.\n"
  831.         "   name \"John H. Smith\"\n");
  832.     else
  833.         if Index(newName, "\"") = 0 then
  834.         newName := SubString(newName, 1, Length(newName) - 2);
  835.         fi;
  836.         me@p_pNRealName := newName;
  837.         Print(
  838.         "Your 'real name' is now '" + newName + "'. "
  839.         "This will be used on news articles you post and on letters "
  840.         "that you mail.\n");
  841.     fi;
  842.     true
  843.     else
  844.     Print(
  845.         "The sysadmin of this MUD has not allowed you to set your own "
  846.         "realname. Send a MUD mail message ('write to SysAdmin') to "
  847.         "him/her with your full name so that it can be set up. You will "
  848.         "need a pen and a pad (available from the store in the minimall) "
  849.         "to be able to do this.\n");
  850.     false
  851.     fi
  852. corp;
  853.  
  854. VerbTail(g_news, "name", nv_name)$
  855. Synonym(g_news, "name", "realname")$
  856.  
  857. define tp_news proc newsResetHandler()void:
  858.     thing me;
  859.  
  860.     newsAllDone();
  861.     me := Me();
  862.     me -- p_pNString;
  863.     me -- p_pNString2;
  864.     me -- p_pNSubject;
  865.     me@p_pNFd := 0;
  866. corp;
  867.  
  868. define tp_news proc newsIdleHandler()void:
  869.  
  870.     newsFileClose();
  871.     newsResetHandler();
  872. corp;
  873.  
  874. define tp_news proc newsEnter()status:
  875.     thing me;
  876.  
  877.     me := Me();
  878.     if Character(me@p_pName) = nil then
  879.     /* cannot do this stuff with machines! */
  880.     OPrint(Capitalize(CharacterNameS(me)) + " will not enter.\n");
  881.     fail
  882.     else
  883.     AddHead(me@p_pExitActions, newsIdleHandler);
  884.     AddHead(me@p_pEnterActions, newsResetHandler);
  885.     me@p_pNSaveHandler := SetCharacterInputAction(newsParse);
  886.     me@p_pNSavePrompt := SetPrompt("newsroom> ");
  887.     continue
  888.     fi
  889. corp;
  890.  
  891. define tp_news proc newsExit()status:
  892.     thing me;
  893.  
  894.     me := Me();
  895.     DelElement(me@p_pEnterActions, newsResetHandler);
  896.     DelElement(me@p_pExitActions, newsIdleHandler);
  897.     ignore SetCharacterInputAction(me@p_pNSaveHandler);
  898.     ignore SetPrompt(me@p_pNSavePrompt);
  899.     me -- p_pNSaveHandler;
  900.     me -- p_pNSavePrompt;
  901.     continue
  902. corp;
  903.  
  904. define tp_news r_newsRoom CreateThing(r_indoors)$
  905. SetupRoom(r_newsRoom, "in the news room",
  906.     "In this room, there are additional commands which are used to read and "
  907.     "post news articles. Use 'help' to find out how to use the service "
  908.     "provided here.")$
  909. r_newsRoom@p_rNoMachines := true$
  910. Connect(r_ne2, r_newsRoom, D_EAST)$
  911. Connect(r_ne2, r_newsRoom, D_ENTER)$
  912. UniConnect(r_ne2, r_newsRoom, D_UP)$
  913. ExtendDesc(r_ne2,
  914.     "The building here is old, but in good repair. Its exterior finish is "
  915.     "that of smooth sandstone blocks, common in important buildings of the "
  916.     "era. A wide staircase leads up to double wooden doors with diamond-paned "
  917.     "windows. The doors are bordered by a pair of sandstone pillars, which "
  918.     "support a similar cap, carved with a pair of lions rampant. At the very "
  919.     "top of the building, you can see granite gargoyles on each corner. A "
  920.     "plaque by the door reads \"Daily News\".")$
  921. AddEastChecker(r_ne2, newsEnter, false)$
  922. AddEnterChecker(r_ne2, newsEnter, false)$
  923. AddUpChecker(r_ne2, newsEnter, false)$
  924. AddWestChecker(r_newsRoom, newsExit, false)$
  925. AddExitChecker(r_newsRoom, newsExit, false)$
  926. Sign(r_ne2, "plaque.door;plaque,by,the", "", "\"Daily News\"")$
  927. Scenery(r_ne2,
  928.     "gargoyle;granite."
  929.     "building;old."
  930.     "block;smooth,sandstone."
  931.     "staircase;wide."
  932.     "door;double,wooden."
  933.     "window;diamond-paned,diamond,paned."
  934.     "pillar;sandstone."
  935.     "cap;sandstone."
  936.     "lion;rampant."
  937.     "rampant;lions,lion")$
  938.  
  939. define tp_news NEWSROOM_ID NextEffectId()$
  940. define tp_news proc drawNewsRoom()void:
  941.  
  942.     if not KnowsEffect(nil, NEWSROOM_ID) then
  943.     DefineEffect(nil, NEWSROOM_ID);
  944.     GSetImage(nil, "Town/newsroom");
  945.     IfFound(nil);
  946.         ShowCurrentImage();
  947.     Else(nil);
  948.         TextBox("Read and post", "usenet news", "here");
  949.     Fi(nil);
  950.     EndEffect();
  951.     fi;
  952.     CallEffect(nil, NEWSROOM_ID);
  953. corp;
  954.  
  955. RoomGraphics(r_newsRoom, "NewsRoom", "", NextMapGroup(), 0.0,0.0,drawNewsRoom)$
  956.  
  957. unuse t_streets
  958.