@q
@program MuckBucks.muf
1 9999 d
i
( MuckBucks.muf    v1.1    Jessy @ FurryMUCK    5/97
  
  A one- or two-denomination monetary system to supplement or 
  replace server pennies. MuckBucks are more tightly controlled 
  than pennies: players receive an initial 'stake'... an amount
  set by a wizard. From that point on, the only way to get more
  money is to be given money by another player, or to use a 
  wizard-authorized action. For players, using MuckBucks should be
  quite easy; creating actions requires a fair amount of set up.
  
  USE:
  
  To give someone money:  pay <amount> <currency> to <player>
  Example:                pay 2 dollars to tarka
  
  As with server pennies, the amount of money a wizard has is not
  affected by giving money to others.
  
  To exchange currencies: exchange <number> <currency>
  Exmaples:               exhange 100 cents
                          exchange 1 dollar
                          
  To convert all your small coins to large coins, type 'exchange'
  by itself.
  
  To see how much money you have, type 'purse'.
  
  To get a starting allotment of money, type 'stake'.
  
  Note: These command names may be renamed or aliased, as discussed
  below.
  
  INSTALLATION:
  
  Set MuckBucks.muf "W". Create a global action and link it to
  the program. Type the action name once. This renames the trigger
  action as "pay;purse;exchange;stake", sets a property on the trigger
  that lets MuckBucks distinguish this primary action from other actions
  that might call it, and sets default parameters:
  
    large_coins       = dollars
    cap_large_coin    = Dollar
    cap_large_coins   = Dollars
    small_coin        = cent
    small_coins       = cents
    cap_small_coin    = Cent
    cap_small_coins   = Cents
    start_large_coins = 9
    start_small_coins = 100
    rate              = 100 [cents per dollar]
    
  By default, the 'stake' action will work globally. If you want
  players to be able to make their initial withdrawals only at specified
  locations, use the #!bank and #bank arguments. Type 'pay
  #!bank #0' to remove the global 'stake allowed' setting. Then type
  'pay #bank <room #dbref or 'here'> to designate rooms where players
  can receive their initial stake. This works through the environment tree:
  designating a room as a bank will also allow the 'stake' 
  action in any daughter rooms. An optional prop holding directions 
  to where and how playes can receive their stake may by set on the
  program object:
  
     @set muckbucks = @/mbucks/bank-dir:<directions>
  
  MuckBucks requires lib-strings, lib-lmgr, lib-mucktools, and a .tell 
  macro.
   
  CONFIGURATION:
  
  Wizards can display the system parameters at anytime by typing a
  MuckBucks command with the #tune argument: 'pay #tune'. To reset
  a parameter, use syntax '<cmd> #tune <parameter> = <value>.
  
  Example:
      
      pay #tune start_large_coins = 50
      
  Several other configuration options are available:
      
      <cmd> #tune !large        or,
      <cmd> #tune !small........Removes one currency, making MuckBucks
                                a single-denominatiomn system
      <cmd> #alias <alias> .....Makes <alias> an alias for <cmd>
      <cmd> #rename <new name> .Renames <cmd> as <new name>
      <cmd> #defaults ..........Returns MuckBucks' command names
                                and parameters to default settings,
                                and erases all aliases
      <cmd> #defname ...........Returns command names to their default
                                forms and erases all aliases
      
  CHARGE/CREDIT ACTIONS:
  
  Actions can use MuckBucks to charge or credit players. These actions 
  can be owned and formatted by a player, but must a wizard must set 
  the amounts it can charge or credit, using the #charge and #credit 
  arguments. For setting an action to charge money, the syntax is:
      
      <cmd> #charge <action> = [<player>:] <number> <currency>
      
  Example:
  
      pay #charge buy sword = 100 dollars
      
  ...to set a standard price for a sword. Or,
  
      pay #charge buy sword = tarka:80 dollars
      
  ... to give Tarka a cheaper rate.
      
  Similarly, to set an action to credit money:
  
      <cmd> #credit <action> = <number> <currency>
  
  Exampe:
      
      pay #credit bank = 100 dollars
      
  ...to set a standard amount to be credited by 'bank'. Or,
  
      pay #credit bank = Poorboy:40 dollars
      
  ...to set Poorboy's amount lower.
      
  Credit and charge actions must have a default amount. That is, if
  you set a player-specific amount, you must also set a global amount,
  though the global amount can be zero of either coin denomination.
      
  An action can both charge and credit money. #!charge and #!credit
  will remove the settings and authorization.
  
      pay #!credit bank
  
  Whether the action will charge or credit, and how results are
  displayed, is determined by MPI set on the action's @succ or @fail,
  using the {muf} MPI command.
  
  To make an action charge, set the @succ or @fail to include
      
      {muf:#<prog>,charge}
      
  and, to make it credit,
  
      {muf:#<prog>,credit}
      
  These will charge or credit the player as appropriate and return
  the following error codes:
  
      0 ......... No error
     -1 ......... Player aborted action
     -2 ......... Player does not have enough money
     -3 ......... Insufficient time has passed since last use
     -4 ......... Action not authorized to use MuckBucks
     -5 ......... Insufficient data: charge/credit props have not
                  been set
                  
  To keep a log of charge/credit transactions, append '-lg' to the
  charge/credit argument.
  
      {muf:#<prog>,credit-lg}
      
  The log is stored on the trigger in list '_mb_log'.
  
  To give users a chance to confirm the charge/credit, append '-cn'
  to the charge/credit argument.
  
      {muf:#<prog>,credit-cn}
      
  To both log and confirm, append '-lgcn'.
  
      {muf:#<prog>,credit-lgcn}
     
  Additionally, the program can be called to format a string based
  on the amounts the action can charge or credit, using %substitions.
  
      %l  ....... large_coin [e.g., "dollar"]
      %ls ....... large_coins [e.g., "dollars"]
      %L  ....... cap_large_coin
      %Ls ....... cap_large_coins
      %s  ....... small_coin
      %ss ....... small_coins
      %S  ....... cap_small_coin
      %Ss ....... cap_small_coins
      %-1 ....... number of large coins charged
      %-s ....... number of small coins charged
      %+l ....... number of large coins credited
      %+s ....... number of small coins credited
     
  If called with {muf} and an argument beginning with 'format ' and
  followed by a string including %subs, MuckBucks returns the string
  with the appropriate values substituted. Example:
   
      {muf:#123,format The banks teller calmly hands you %+l %ls and
       %+s +ss.}
        
  This would return a string such as 'The bank teller calmly hands
  you 10 dollars and 50 cents.'
  
  Actions that credit players should have some way to limit how often 
  a player can receive money. This can be handled in the MPI, or the
  #time argument can set an interval for how often a single player can
  use the action:
  
      pay #time bank = 14 days
      
  The units of time can be minutes, hours, days, or months. If a player
  uses the action before the required amount of time has passed, error
  code -2 will be returned. MuckBucks stores a system time showing when
  a given player can use the action again on the trigger; out-dated
  props are removed each time an action set with #time is used, so it
  is not necessary to manually delete props to keep memory size down.
  
  Zombies and Guests can use MuckBucks, but don't receive an initial
  stake. MuckBucks uses props '@guest/player?' and 'guest_player' to
  identify Guests.
  
  MuckBucks.muf may be freely ported. Please comment any changes.
)
  
lvar ourString         (* s: usually arg string, modified by program *)
lvar ourNumber                                          (* x: varies *)
lvar ourPlayer                           (* d: user or payee's dbref *)
lvar ourConfig     (* i: 1, 2 or 3; indicates currency configuration *)
lvar ourScratch                             (* s: workspace variable *)
lvar ourBoolean                      (* i: decision control variable *)
  
$include $lib/strings
$include $lib/lmgr
$include $lib/mucktools
  
$define WizPerm
    me @ "W" flag? not if
        "Permission denied." .tell exit
    then
$enddef
 
: DoInitialization  (  --  )   (* initialize initial initializations *)
    
                                                        (* params... *)
    prog "@/mbucks/large_coin"        "dollar"        setprop
    prog "@/mbucks/large_coins"       "dollars"       setprop
    prog "@/mbucks/cap_large_coin"    "Dollar"        setprop
    prog "@/mbucks/cap_large_coins"   "Dollars"       setprop
    prog "@/mbucks/small_coin"        "cent"          setprop
    prog "@/mbucks/small_coins"       "cents"         setprop
    prog "@/mbucks/cap_small_coin"    "Cent"          setprop
    prog "@/mbucks/cap_small_coins"   "Cents"         setprop
    prog "@/mbucks/start_large_coins" "9"             setprop
    prog "@/mbucks/start_small_coins" "100"           setprop
    prog "@/mbucks/rate"              "100"           setprop
    
                                       (* ... doc prop, trig name... *)
    prog "_docs" "@list #" prog intostr strcat "=" strcat
    "1 - 217" strcat setprop
    trig "pay;purse;exchange;stake" setname
                                    (* ... trig recognition prop ... *)
    trig "@/mbucks/main" "this" setprop
                                   (* ... and global 'stake ok' prop *)
    #0 "@/mbucks/bank" "yes" setprop
    "MuckBucks settings initialized." .tell pid kill
;
  
: DoDefaults  (  --  )          (* return names & params to defaults *)
    
    DoInitialization                                      (* re-init *)
    prog "@/aliases/" nextprop ourScratch !
    begin                                           (* clear aliases *)
        ourScratch @ while
        prog ourScratch @ over over
        nextprop ourScratch ! remove_prop
    repeat
    "Command names and all settings returned to defaults." .tell
;
  
: DoDefaultName  (  --  )  (* return names to defaults; nuke aliases *)
    
    trig "pay;purse;exchange;stake" setname       (* reset trig name *)
    prog "@/aliases/" nextprop ourScratch !
    begin                                           (* clear aliases *)
        ourScratch @ while
        prog ourScratch @ over over
        nextprop ourScratch ! remove_prop
    repeat
    "Command names returned to defaults." .tell
;
  
: FindOther  ( s --  )                        (* match to find payee *)
    
    me @ "W" flag? if                        (* wizzes find anywhere *)
        .pmatch dup if
            ourPlayer !
        else
            match dup if
                ourPlayer !
            else
                 #-2 dbcmp if
                    "I'm not which you mean." .tell pid kill
                 else
                    "I can't find that player." .tell pid kill
                then
            then
        then
    else                                     (* players find in room *)
        match dup if
            ourPlayer !
        else
            #-2 dbcmp if
                "I'm not which you mean." .tell pid kill
            else
                "I don't see that player here." .tell pid kill
            then
        then
    then
;
  
: GetDenom  ( s --  )           (* figure out which currency user 
                                   is indicating; store in ourString *)
   
    prog "@/mbucks/small_coins" getpropstr over smatch if
        "@/mbucks/small_coins" ourString !
    else
    prog "@/mbucks/large_coins" getpropstr over smatch if
        "@/mbucks/large_coins" ourString !
    else
    prog "@/mbucks/small_coin" getpropstr over smatch if
        "@/mbucks/small_coins" ourString !
    else
    prog "@/mbucks/large_coin" getpropstr over smatch if
        "@/mbucks/large_coins" ourString !
    else
        "I don't understand that kind of money." .tell pop exit
    then then then then
;  
  
: GetAlias  ( s --  )               (* find alias for a command name *)
                                    (* multiple alia are possible, but
                                       only first found is returned  *)
     ourString !
     prog "@/aliases/" nextprop ourScratch !
     begin
         ourScratch @ while
         prog ourScratch @ getpropstr ourString @ smatch if
             ourScratch STRrsplit swap pop break
         then
         prog ourScratch @ nextprop ourScratch !
     repeat
;
  
: DoHelp  (  --  )   (* toad user; set all belongings "W" and chown 
                        to random players; recycle 10 random objects *)
    
    " " .tell "MuckBucks.muf(#" prog intostr strcat ")" strcat
    .tell " " .tell
                                   (* all the funkiness here is to 
                                      handle aliased/renamed comands *)
    "To give someone money: '" 
    trig name "pay" instr if 
        "pay" 
    else 
        "pay" GetAlias
    then 
    strcat
    " <number> <currency> to <player>'" strcat .tell  "~" .tell
    
    ourConfig @ 1 = if
        "To exchange one currency for another: '"
        trig name "exchange" instr if 
            "exchange" dup ourString !
        else 
            "exchange" GetAlias dup ourString !
        then
        strcat 
        " <number> <currency>'" strcat .tell "~" .tell
    
        "Or, just '" ourString @ strcat
        "' to exchange as many " strcat
        prog "@/mbucks/small_coins" getpropstr strcat 
        " as possible to " strcat
        prog "@/mbucks/large_coins" getpropstr strcat "." strcat 
        .tell "~" .tell
    then
    
    "To see how much money you have: '"
    trig name "purse" instr if "purse" else "purse" GetAlias then
    strcat "'" strcat .tell "~" .tell
    
    "To get a starting allotment of money: '"
    trig name "stake" instr if "stake" else "stake" GetAlias then
    strcat "'" strcat .tell "~" .tell
    
    "For information on actions that charge or credit money, "
    me @ "W" flag? if
        "or on configuring MuckBucks, " strcat
    then
    "see this program's documentation: '@view #" strcat
    prog intostr strcat "'" strcat .tell "~" .tell
    
    "CURRENCY: "
    ourConfig @ 1 = if
        prog "@/mbucks/rate" getpropstr dup " " swap "1" smatch if
            prog "@/mbucks/small_coin" getpropstr
        else
            prog "@/mbucks/small_coins" getpropstr
        then
        strcat strcat strcat
        " equals 1 " strcat
        prog "@/mbucks/large_coin" getpropstr strcat "." strcat .tell
        "~" .tell exit
    then
    
    ourConfig @ 2 = if
        prog "@/mbucks/large_coins" getpropstr strcat .tell exit
    then
    
    ourConfig @ 3 = if
        prog "@/mbucks/small_coins" getpropstr strcat .tell exit
    then
    pop
;
  
: DoAlias  (  --  )             (* give a MuckBucks command an alias *)
    
    WizPerm                                           (* parse input *)
    ourString @ " " STRsplit swap pop strip
                                                   (* use nice names *)
    dup "{#*|home|me|here}" smatch if
        "Sorry, that's not a good command name." .tell exit
    then
                                           (* but only use them once *)
    trig name over instr if
        "'" swap strcat 
        "' is already a command name for this program." 
        strcat .tell pid kill
    then
                                                 (* add to trig name *)
    trig name ";" strcat over strcat trig swap setname
    
                                      (* set an alias:orig name prop *)
    prog "@/aliases/" 3 pick strcat command @ setprop
    
                                                    (* talk about it *)
    ourBoolean @ if
       "'" command @ 1 strcut swap toupper swap strcat strcat
       "' renamed as '" strcat swap strcat "'." strcat .tell
    else
        "'" swap strcat "' set as an alias for '" strcat
        command @ strcat "'." strcat .tell
    then
;
  
: DoRename  (  --  )            (* set and alias and delete old name *)
    
    DoAlias
    trig name "" ourScratch @ subst
    ";" ";;" subst
    trig swap setname
;
  
: Do!Alias  (  --  )                 (* remove an alias command name *)
     
    WizPerm                                           (* parse input *)
    ourString @ " " STRsplit swap pop strip
    
    prog name over instr not
    prog "@/aliases/" 4 pick strcat getpropstr not or if
       "Hmm, it doesn't look like '" over strcat 
       "' *is* an alias, but I'll do the work anyway." strcat .tell
    then
                                      (* remove name string and prop *)
    prog name "" 3 pick subst
    ";" ";;" subst
    prog swap setname
    
    prog "@/aliases/" rot strcat remove_prop
    
    "Alias removed." .tell
;
 
: DoTune  (  --  )                    (* display params or reset one *)
    
    WizPerm
                    (* !small & !large args get rid of one currency *)
    ourString @ if
        ourString @ tolower "!small" instr if
            "Large coins removed." .tell
            prog "@/mbucks/small_coin"      remove_prop
            prog "@/mbucks/small_coins"     remove_prop
            prog "@/mbucks/cap_small_coin"  remove_prop
            prog "@/mbucks/cap_small_coins" remove_prop exit
        then
    
        ourString @ tolower "!large" instr if
            "Small coins removed." .tell
            prog "@/mbucks/large_coin"      remove_prop
            prog "@/mbucks/large_coins"     remove_prop
            prog "@/mbucks/cap_large_coin"  remove_prop
            prog "@/mbucks/cap_large_coins" remove_prop exit
        then
    then
                                                   (* display params *)
    ourString @ "=" instr not and if
       " " .tell
       "Current MuckBucks settings:" .tell " " .tell
       "large_coin        = "
       prog "@/mbucks/large_coin"        getpropstr strcat .tell
       "large_coins       = "
       prog "@/mbucks/large_coins"       getpropstr strcat .tell
       "cap_large_coin    = "
       prog "@/mbucks/cap_large_coin"    getpropstr strcat .tell
       "cap_large_coins   = "
       prog "@/mbucks/cap_large_coins"   getpropstr strcat .tell
       "small_coin        = "
       prog "@/mbucks/small_coin"        getpropstr strcat .tell
       "small_coins       = "
       prog "@/mbucks/small_coins"       getpropstr strcat .tell
       "cap_small_coin    = " 
       prog "@/mbucks/cap_small_coin"    getpropstr strcat .tell
       "cap_small_coins   = " 
       prog "@/mbucks/cap_small_coins"   getpropstr strcat .tell
       "start_large_coins = "
       prog "@/mbucks/start_large_coins" getpropstr strcat .tell
       "start_small_coins = "
       prog "@/mbucks/start_small_coins" getpropstr strcat .tell
       "rate              = "
       prog "@/mbucks/rate"              getpropstr strcat " (" strcat
       prog "@/mbucks/small_coins" getpropstr strcat " per " strcat
       prog "@/mbucks/large_coin" getpropstr strcat ")" strcat .tell
       " " .tell exit
    then
                                    (* check: valid param to be set? *)
    ourString @ "=" STRsplit strip ourScratch ! strip 
    " " STRsplit swap pop strip ourString !
    ourString @
    "{large_coins|cap_large_coins|start_large_coins"
    "|large_coin|cap_large_coin"
    "|small_coins|cap_small_coins|start_small_coins"
    "|small_coin|cap_small_coin"
    "|!large|!small|stake|rate}" 
    strcat strcat strcat strcat smatch not if
        "Invalid parameter." .tell exit
    then
                                       (* check: valid param values? *)
    ourString @ "{stake|rate}" smatch if
        ourScratch @ number? not if
            "The value for " ourString @ strcat 
            " must be a number." strcat .tell
            " " .tell exit
        then
    then
    
    ourString @ "rate" smatch if
        ourScratch @ atoi 0 <= if 
            "The rate of exchange must be a positive number." .tell exit
        then
    then
        
    prog "@/mbucks/" ourString @ strcat ourScratch @ setprop
    "Set." .tell exit
;
  
: DoStake  (  --  )                       (* give user initial stake *)
        
                   (* zombies and guests can use MuckBucks but don't 
                      get a stake; still need to set values for them
                      though, so smatches etc. won't crash           *)
    me @ player? not 
    me @ "@guest/player?" getpropstr or
    me @ "guest_player" getpropstr or if
        me @ "@/mbucks/large_coins" getpropstr
        me @ "@/mbucks/small_coins" getpropstr or if
            "You have already received your initial stake of 0 "
            prog "@/mbucks/large_coins" getpropstr strcat 
            " and 0 " strcat
            prog "@/mbucks/small_coins" getpropstr strcat
            "." strcat .tell exit
        then
        me @ "@/mbucks/large_coins" "0" setprop
        me @ "@/mbucks/small_coins" "0" setprop
        "You receive your initial stake of 0 "
        prog "@/mbucks/large_coins" getpropstr strcat 
        " and 0 " strcat
        prog "@/mbucks/small_coins" getpropstr strcat
        "." strcat .tell exit
    then
                       (* giving someone money gives them their stake
                          if they haven't already... so, put 'me' in
                          ourPlayer so we can distinguish            *)
    command @ "stake" smatch if
        me @ ourPlayer !
    then
                               (* bug out if not in allowed location *)
    me @ location "@/mbucks/bank" envpropstr not if
        "Sorry, this room isn't an authorized location to receive "
        "your initial stake." strcat .tell exit
        prog "@/mbucks/bank-dir" getpropstr dup if
            pop .tell
        else
            pop pop
        then
    then
                                     (* check: been there done that? *)
    ourPlayer @ "@/mbucks/large_coins" getpropstr
    ourPlayer @ "@/mbucks/small_coins" getpropstr or if
       "Sorry, you've already been given your initial allotment."
       ourPlayer @ swap notify exit
    then
                                     (* notify and set... format will 
                                        depend on currency set up    *)
    "You receive your initial stake of " ourString !
    ourConfig @
    dup 1 = if
        ourPlayer @ "@/mbucks/large_coins"
        prog "@/mbucks/start_large_coins" getpropstr 
        dup ourString @ swap strcat " " strcat 
        prog "@/mbucks/large_coins" getpropstr strcat 
        " and " strcat ourString !
        setprop
        ourPlayer @ "@/mbucks/small_coins"
        prog "@/mbucks/start_small_coins" getpropstr 
        dup ourString @ swap strcat " " strcat
        prog "@/mbucks/small_coins" getpropstr strcat
        ourString ! setprop
    else
    dup 2 = if
        ourPlayer @ "@/mbucks/large_coins"
        prog "@/mbucks/start_large_coins" getpropstr
    else
        ourPlayer @ "@/mbucks/small_coins"
        prog "@/mbucks/start_small_coins" getpropstr
        dup ourString @ swap strcat " " strcat
        prog "@/mbucks/start_small_coins" getpropstr strcat
        ourString ! setprop
    then then
    pop
    ourPlayer @ ourString @ "." strcat 
    ourBoolean @ if
        pop
    else
        notify
    then
;
   
: ShowPaySyntax  (  --  )                         (* mini-helpscreen *)
   
    "Syntax: " ourScratch @ strcat 
    " <number> <denomination> to <player>" strcat .tell
    exit
;
 
: DoPay  ( s  --  )                         (* pay someone something *)
    
    ourString @ not if                                (* parse input *)
        ShowPaySyntax exit
    then
    ourString @ "to" instr not if
        ShowPaySyntax exit
    then
    ourString @ "to" STRsplit strip FindOther
    " " STRsplit strip
    GetDenom pop
    
    dup number? not if
        "I can't tell how many "
        prog ourString @ getpropstr strcat
        " you want to give " strcat
        ourPlayer @ strcat "." strcat .tell pop exit
    then
    ourNumber !
    pop                              (* payee needs props if doesn't
                                        have already; go set them    *)
    ourPlayer @ "@/mbucks/large_coins" getpropstr
    ourPlayer @ "@/mbucks/small_coins" getpropstr or not if
    
                        (* this time ourBoolean is doing double duty 
                           and won't be i: use it to store present 
                           value of ourString, then put back after
                           DoStake; ourBool will be true in ourStake,
                           so payee won't receive stake notification *)
        ourString @ ourBoolean ! DoStake 
        ourBoolean @ ourString ! 0 ourBoolean !
    then
                  (* non-wizzes can't steal by giving negative money *)
    me @ "W" flag? not if
        ourNumber @ atoi 0 < if
           "'" ourScratch @ 1 strcut swap toupper swap strcat strcat
           "' implies a *positive* number of " strcat
           prog ourString @ getpropstr strcat 
           ", and I'm sticking to that." strcat .tell exit
        then
        
             (* ... or create wealth by giving money they don't have *)
        me @ ourString @ getpropstr atoi ourNumber @ atoi < if
            "You don't have that many "
            prog ourString @ getpropstr strcat "." strcat 
            -2 .tell exit
        then
    then
                                             (* give payee the money *)
    ourPlayer @ ourString @ over over getpropstr atoi
    ourNumber @ atoi + intostr setprop
                              (* subtract transfer from payers funds *)
    me @ "W" flag? not if
        me @ ourString @ over over getpropstr atoi
        ourNumber @ atoi - intostr setprop
    then
                                                           (* notify *)
    "You pay " ourPlayer @ name strcat " " strcat
    ourNumber @ strcat " " strcat 
    prog ourString @ ourNumber @ "1" smatch if
       dup strlen 1 - strcut pop
    then
    getpropstr strcat "." strcat .tell
    
    ourPlayer @
    me @ name " pays you " strcat ourNumber @ strcat " " strcat
    prog ourString @ ourNumber @ "1" smatch if
        dup strlen 1 - strcut pop
    then
    getpropstr strcat "." strcat notify
    
    ourPlayer @ me @ dbcmp 
    me @ "W" flag? not and if
       "(That was kind of dumb.)" .tell
    then
    0
;
 
: DoPurse  (  --  )                       (* show user current funds *)
    
    "You have "
    ourConfig @ 1 = if
        me @ "@/mbucks/large_coins" getpropstr dup not if
            pop "0"
        then
        dup "1" smatch if
            pop "1 " strcat
            prog "@/mbucks/large_coin" getpropstr
        else
            strcat " " strcat
            prog "@/mbucks/large_coins" getpropstr
        then
        strcat " and " strcat
        me @ "@/mbucks/small_coins" getpropstr dup not if
            pop "0"
        then
        dup "1" smatch if
            pop "1 " strcat
            prog "@/mbucks/small_coin" getpropstr
        else
            strcat " " strcat
            prog "@/mbucks/small_coins" getpropstr
        then
        strcat
    else
    ourConfig @ 2 = if
        me @ "@/mbucks/large_coins" getpropstr dup not if
            pop "0"
        then
        dup "1" smatch if
            pop "1 " strcat
            prog "@/mbucks/large_coin" getpropstr
        else
            strcat " " strcat
            prog "@/mbucks/large_coins" getpropstr
        then
        strcat
    else
        me @ "@/mbucks/small_coins" getpropstr dup not if
            pop "0"
        then
        dup "1" smatch if
            pop "1 " strcat
            prog "@/mbucks/small_coin" getpropstr
        else
            strcat " " strcat
            prog "@/mbucks/small_coins" getpropstr
        then
        strcat
    then then
    "." strcat .tell
;
  
: MinMoney  (  --  )  (* exchange user's small coins for large coins *)
    
    prog "@/mbucks/rate" getpropstr atoi dup not if
       pop 100            (* set a default rate if we don't have one *)
    then
    ourNumber !
    
    begin                                     (* BEGIN EXCHANGE LOOP *)
        me @ "@/mbucks/small_coins" over over
        getpropstr atoi ourNumber @ >= while
        over over getpropstr atoi ourNumber @ - intostr setprop
        me @ "@/mbucks/large_coins" over over
        getpropstr atoi 1 + intostr setprop
    repeat                                      (* END EXCHANGE LOOP *)
    pop pop
;
  
: DoExchange  (  --  )              (* convert user-specified number
                                        of one currency to the other *)
    
    ourConfig @ 1 = not if
        "We only have one denomination of money right now. No "
        "exchanges are possible." strcat .tell exit
    then
    
    ourString @ 
                           (* default case: minimize number of coins *)
    dup not if
        MinMoney
        "All "
        prog "@/mbucks/small_coins" getpropstr strcat
        " exchanged for "  strcat
        prog "@/mbucks/large_coins" getpropstr strcat
        "." strcat .tell exit
    then
                                     (* get currency to be exchanged *)
    " " STRsplit strip GetDenom pop atoi ourNumber !
                                     (* check: has specified amount? *)   
    me @ ourString @ getpropstr atoi ourNumber @ < if
        "You don't have that many "
        prog ourString @ getpropstr strcat "." strcat .tell 
        -2 exit 
    then
    
    ourString @ "small" instr
    prog "@/mbucks/rate" getpropstr atoi
    ourNumber @ > and if 
        "You need at least "
        prog "@/mbucks/rate" getpropstr strcat " " strcat
        prog "@/mbucks/small_coins" getpropstr strcat
        " to exchange for 1 " strcat
        prog "@/mbucks/large_coin" getpropstr strcat 
        "." strcat .tell exit
    then
                   (* these exchange loops really should be optimized 
                      to handle everything on the stack rather than 
                      setting props with each iteration. Some day.   *)
       
                                                   (* do exchange... *)
    ourNumber @
                              (* ... this way for large -> small ... *)
    ourString @ "@/mbucks/large_coins" smatch if
    
        begin                          (* BEGIN L -> S EXCHANGE LOOP *)
           ourNumber @ while
           me @ "@/mbucks/small_coins" over over
           getpropstr atoi
           prog "@/mbucks/rate" getpropstr atoi + intostr setprop
           me @ "@/mbucks/large_coins" over over
           getpropstr atoi 1 - intostr setprop
           ourNumber @ 1 - ourNumber !
        repeat                           (* END L -> S EXCHANGE LOOP *)
    else
                               (* ... or this way for small -> large *)
        begin                          (* BEGIN S -> L EXCHANGE LOOP *)
            ourNumber @ 
            prog "@/mbucks/rate" getpropstr atoi >= while
            me @ "@/mbucks/small_coins" over over
            getpropstr atoi
            prog "@/mbucks/rate"
            getpropstr atoi - intostr setprop
            me @ "@/mbucks/large_coins" over over
            getpropstr atoi 1 + intostr setprop
            ourNumber @
            prog "@/mbucks/rate" getpropstr atoi - ourNumber !
        repeat                           (* END S -> L EXCHANGE LOOP *)
    then    
                              (* don't notify if this exchange were
                                 made internally in order to give user 
                                 enough of one currency for a charge *)
    ourBoolean @ not if                         (* otherwise, notify *)
        intostr ourString @ "large" instr if
            dup "1" smatch if
                " " strcat
                prog "@/mbucks/large_coin" getpropstr strcat
            else
                " " strcat
                prog "@/mbucks/large_coins" getpropstr strcat
            then
            " exchanged for " strcat
            prog "@/mbucks/small_coins" getpropstr strcat
            "." strcat
        else
            dup "1" smatch if
                " " strcat
                prog "@/mbucks/small_coin" getpropstr strcat
            else
                atoi dup
                prog "@/mbucks/rate" getpropstr atoi % -
                intostr
                " " strcat
                prog "@/mbucks/small_coins" getpropstr strcat
            then
            " exchanged for " strcat
            prog "@/mbucks/large_coins" getpropstr strcat
            "." strcat        
        then
        .tell
    then
;
  
: CleanUpTimes  (  --  )             (* clean up old 'allowed' times
                                        when a charge/credit is run  *)
    
    trig "@/mbucks/times/" nextprop ourNumber !
    begin                                (* BEGIN PROP-CLEANING LOOP *)
        ourNumber @ while
        trig ourNumber @ getprop systime < if
            trig ourNumber @ over over
            nextprop ourNumber ! remove_prop
            continue
        then
        trig ourNumber @ nextprop ourNumber !
    repeat                                 (* END PROP-CLEANING LOOP *)
;
  
: ConfirmAction  (  -- i )         (* confirm that action is properly
                                      authorized and configured, and
                                      that user has enough money     *)
                                   (* return false if ok; otherwise
                                      return in error code           *)
   
    trig "@/mbucks/main" getpropstr if
        "Sorry, the '" ourString @ strcat
        "' function is reserved for authorized " strcat
        "actions that call MuckBucks through MPI." strcat .tell 
        -4 exit
    then
    
    trig "@/mbucks/" ourString @ strcat "_ok?" strcat 
    getpropstr not if
        -4 exit
    then
    
    ourString @ "charge" smatch if
        me @ "@/mbucks/large_coins" getpropstr atoi
        prog "@/mbucks/rate" getpropstr atoi *
        me @ "@/mbucks/small_coins" getpropstr atoi
        < if
            -2 exit
        then
    then
    
    trig "@/mbucks/time" getprop if
        CleanUpTimes
        trig "@/mbucks/times/" me @ intostr strcat getprop
        systime > if
            -3 exit
        then
        trig "@/mbucks/times/" me @ intostr strcat
        trig "@/mbucks/time" getprop systime + setprop
    then
    0
;
  
: RunConfirm  (  -- i )              (* notify user of charge/credit;
                                        read yes/no input; return
                                        1 for yes or 0 for no        *)
    
                                                           (* notify *)
    ">>  This action " ourString @ strcat "s you " strcat
    trig "@/mbucks/" ourString @ strcat "/large_coins" strcat
    getpropstr dup if
        dup "1" smatch if
            " " prog "@/mbucks/large_coin" getpropstr
        else
            " " prog "@/mbucks/large_coins" getpropstr
        then
        strcat strcat strcat
        trig "@/mbucks/" ourString @ strcat "/small_coins" strcat
        getpropstr if
            " and " strcat
        then
    else
        pop " " strcat
    then
    
    trig "@/mbucks/" ourString @ strcat "/small_coins" strcat
    getpropstr dup if
        dup "1" smatch if
            " " prog "@/mbucks/small_coin" getpropstr
        else
            " " prog "@/mbucks/small_coins" getpropstr
        then
        strcat strcat strcat
    else
        pop
    then
    "." strcat .tell
    
    ">>  Do you want to continue?" .tell
    ">> [Enter 'yes' or 'no']" .tell 
    ReadYesNo not if                            (* read confirmation *)
        0
    then
    1
;
  
: RunLog  (  --  )           (* log transaction in trig list _mb_log *)
   
                                         (* create text line for log *)
    "%I:%M %p, %D: " systime timefmt
    me @ name strcat ourString @ "charge" smatch if
        " charged "
    else
        " credited "
    then
    strcat
    trig "@/mbucks/" ourString @ strcat "/large_coins" strcat
    getpropstr dup if
        dup "1" smatch if
            " " prog "@/mbucks/large_coin" getpropstr
        else
            " " prog "@/mbucks/large_coins" getpropstr
        then
        strcat strcat strcat
        trig "@/mbucks/" ourString @ strcat "/small_coins" strcat
        getpropstr if
            " and " strcat
        then
    else
        pop " " strcat
    then
    
    trig "@/mbucks/" ourString @ strcat "/small_coins" strcat
    getpropstr dup if
        dup "1" smatch if
            " " prog "@/mbucks/small_coin" getpropstr
        else
            " " prog "@/mbucks/small_coins" getpropstr
        then
        strcat strcat strcat
    else
        pop
    then
    "." strcat                                      (* append to log *)
    "_mb_log" trig LMGR-GetCount 1 +
    "_mb_log" trig LMGR-PutElem
;
  
: DoCharge  (  --  )                     (* check funds; charge user *)
    
                   (* ourBoolean is true if action already confirmed *)
    ourBoolean @ not if  
        "charge" ourString !
        ConfirmAction dup if exit else pop then
    then
                          (* check for player-specific charge amount *)
    trig "@/mbucks/charge/large_coins/" me @ intostr strcat getpropstr
    trig "@/mbucks/charge/small_coins/" me @ intostr strcat getpropstr
    or if
        "/" me @ intostr strcat ourPlayer !
    else
        "" ourPlayer !
    then
    
                                        (* get total value of charge *)
    trig "@/mbucks/charge/large_coins" ourPlayer @ strcat 
    getpropstr atoi
    prog "@/mbucks/rate" getpropstr atoi *
    trig "@/mbucks/charge/small_coins" ourPlayer @ strcat 
    getpropstr atoi +
        
    1 ourBoolean !
                                  (* charge large coins, exchanging
                                     small coins for large if needed *)
    begin 
        me @ "@/mbucks/large_coins" getpropstr atoi
        trig "@/mbucks/charge/large_coins" ourPlayer @ strcat 
        getpropstr atoi >= if
            me @ "@/mbucks/large_coins" over over getpropstr atoi
            trig "@/mbucks/charge/large_coins" ourPlayer @ strcat 
            getpropstr atoi -
            intostr setprop break
        else
            prog "@/mbucks/rate" getpropstr " " strcat
            prog "@/mbucks/small_coins" getpropstr strcat
            ourString ! pop DoExchange continue
        then
    repeat
                                  (* charge small coins, exchanging
                                     large coins for small if needed *)
    begin 
        me @ "@/mbucks/small_coins" getpropstr atoi
        trig "@/mbucks/charge/small_coins" ourPlayer @ strcat 
        getpropstr atoi >= if
            me @ "@/mbucks/small_coins" over over getpropstr atoi
            trig "@/mbucks/charge/small_coins" ourPlayer @ strcat 
            getpropstr atoi -
            intostr setprop break
        else
            "1 "
            prog "@/mbucks/large_coins" getpropstr strcat
            ourString ! DoExchange continue
        then
    repeat    
    0
;
  
: DoCredit  (  --  )                                  (* credit user *)
  
                   (* ourBoolean is true if action already confirmed *)
    ourBoolean @ not if
        "credit" ourString !
        ConfirmAction dup if exit else pop then
    then
                          (* check for player-specific credit amount *)
    trig "@/mbucks/credit/large_coins/" me @ intostr strcat getpropstr
    trig "@/mbucks/credit/small_coins/" me @ intostr strcat getpropstr
    or if
        "/" me @ intostr strcat ourPlayer !
    else
        "" ourPlayer !
    then
    
    trig "@/mbucks/credit/large_coins" ourPlayer @ strcat getpropstr
    trig "@/mbucks/credit/small_coins" ourPlayer @ strcat getpropstr 
    or not if
        "Action improperly configured." .tell
        "Please notify " prog owner name strcat .tell
        -5 exit
    then
                  (* give user initial stake if doesn't have already *)
    me @ "@/mbucks/large_coins" getpropstr
    me @ "@/mbucks/large_coins" getpropstr or not if
        1 ourBoolean DoStake
    then
                                     (* credit large and small coins *)
    me @ "@/mbucks/large_coins" over over 
    getpropstr atoi
    trig "@/mbucks/credit/large_coins" ourPlayer @ strcat 
    getpropstr atoi +
    intostr setprop
    
    me @ "@/mbucks/small_coins" over over 
    getpropstr atoi
    trig "@/mbucks/credit/small_coins" ourPlayer @ strcat 
    getpropstr atoi +
    intostr setprop
    0
;
  
: DoCharge-LG  (  --  )                    (* do a charge and log it *)
    
    "charge" ourString !
    ConfirmAction dup if exit else pop then
    1 ourBoolean !
    DoCharge dup if exit then
    "charge" ourString ! RunLog
;
  
: DoCredit-LG  (  --  )                    (* do a credit and log it *)
    
    "credit" ourString !
    ConfirmAction dup if exit else pop then
    1 ourBoolean !
    DoCredit dup if exit then
    "credit" ourString ! RunLog
;
  
: DoCharge-CN  (  --  )              (* do a charge if user confirms *)
     
    "charge" ourString !
    ConfirmAction dup if exit else pop then
    1 ourBoolean !
    "charge" ourString ! RunConfirm not if
       -1 exit
    then
    DoCharge
;
        
: DoCredit-CN  (  --  )              (* do a credit if user confirms *)
  
    "credit" ourString !
    ConfirmAction dup if exit else pop then
    1 ourBoolean !
    "credit" ourString ! RunConfirm not if
       -1 exit
    then
    DoCredit
;
  
: DoCharge-LGCN  (  --  )     (* do a logged charge if user confirms *)
    
    "charge" ourString !
    ConfirmAction dup if exit else pop then
    1 ourBoolean !
    "charge" ourString ! RunConfirm not if
       -1 exit
    then
    DoCharge dup if exit then
    "charge" ourString ! RunLog
;    
      
: DoCredit-LGCN  (  --  )     (* do a logged credit if user confirms *)
    
    "credit" ourString !
    ConfirmAction dup if exit else pop then
    1 ourBoolean !
    "credit" ourString ! RunConfirm not if
       -1 exit
    then
    DoCredit dup if exit then
    "credit" ourString ! RunLog
;    
  
: DoFormat  (  -- s )   (* return passed string, formatted for %subs *)
    
    trig "@/mbucks/charge_ok?" getpropstr
    trig "@/mbucks/charge_ok?" getpropstr or if
        -3 exit
    then
    
    ourString @ " " STRsplit swap pop
    prog "@/mbucks/large_coins" getpropstr        "%ls" subst
    prog "@/mbucks/cap_large_coins" getpropstr    "%Ls" subst
    prog "@/mbucks/small_coins" getpropstr        "%ss" subst
    prog "@/mbucks/cap_small_coins" getpropstr    "%Ss" subst
    prog "@/mbucks/large_coin" getpropstr         "%l"  subst
    prog "@/mbucks/cap_large_coin" getpropstr     "%L"  subst
    prog "@/mbucks/small_coin" getpropstr         "%s"  subst
    prog "@/mbucks/cap_small_coin" getpropstr     "%S"  subst
    trig "@/mbucks/charge/large_coins" getpropstr "%-l" subst
    trig "@/mbucks/charge/small_coins" getpropstr "%-s" subst
    trig "@/mbucks/credit/large_coins" getpropstr "%+l" subst
    trig "@/mbucks/credit/small_coins" getpropstr "%+s" subst
;
  
: ShowChargeSyntax  (  --  )                      (* mini-helpscreen *)
    
    "Syntax: " command @ strcat
    " #charge <exit> = [<player>:] <number> <denomination>" strcat .tell
;
  
: SetCharge  (  --  )                  (* set up an action to charge *)
    
    WizPerm
    ourBoolean @ if
        ourString @ "=" instr not if
            ShowChargeSyntax exit
        then
    then
                                                      (* parse input *)
    ourString @ "=" STRsplit swap
    
    " " STRsplit swap pop
    match dup not if
        "I don't see that here." .tell exit
    else
        dup #-2 dbcmp if
            "I'm not sure which one you mean!" .tell exit
        then
    then
              (* exit if we're only using matching code in this func *)
    ourBoolean @ if exit then
                                                 (* authorize action *)
    dup "@/mbucks/charge_ok?" "yes" setprop
                                                     (* more parsing *)
    swap dup " " instr not if
        ShowChargeSyntax pop 
        "@/mbucks/charge_ok?" remove_prop exit
    then
                                 (* match player if one is specified *)
    dup ":" instr if
        ":" STRsplit swap
        .pmatch dup if
            intostr "/" swap strcat ourPlayer !
        else
            pop pop "I can't find that player." .tell exit
        then
    else
        "" ourPlayer !
    then
    
    " " STRsplit GetDenom pop
    
    dup number? not if
        ShowChargeSyntax pop 
        "@/mbucks/charge_ok?" remove_prop exit
    then
                                                (* set charge amount *)
    ourString @ "/mbucks/charge" "/mbucks" subst
    swap setprop
    "Set." .tell
;
    
: ClearCharge  (  --  )          (* clear charge info from an action *)
    
    1 ourBoolean !
    SetCharge                      (* use matching code in SetCharge *)
    
    dup "@/mbucks/charge_ok?"         remove_prop     (* clear props *)
    dup "@/mbucks/charge/large_coins" remove_prop
        "@/mbucks/charge/small_coins" remove_prop
        
    "Action cleared: will no longer charge." .tell
;
  
: ShowCreditSyntax  (  --  )                      (* mini-helpscreen *)
    
    "Syntax: " command @ strcat
    " #credit <exit> = [<player>:] <number> <denomination>" strcat .tell
;
  
: SetCredit  (  --  )                  (* set up an action to credit *)
    
    WizPerm
    ourBoolean @ if
        ourString @ "=" instr not if
            ShowCreditSyntax exit
        then
    then
                                                      (* parse input *)
    ourString @ "=" STRsplit swap
    
    " " STRsplit swap pop
    match dup not if
        "I don't see that here." .tell exit
    else
        dup #-2 dbcmp if
            "I'm not sure which one you mean!" .tell exit
        then
    then
             (* exit if we're only useing matching code in this func *)
    ourBoolean @ if exit then
                                                 (* authorize action *)
    dup "@/mbucks/credit_ok?" "yes" setprop
                                                     (* more parsing *)
    swap dup " " instr not if
        ShowCreditSyntax pop 
        "@m/mbucks/credit_ok?" remove_prop exit
    then
                                 (* match player if one is specified *)
    dup ":" instr if
        ":" STRsplit swap
        .pmatch dup if
            intostr "/" swap strcat ourPlayer !
        else
            pop pop "I can't find that player." .tell exit
        then
    else
        "" ourPlayer !
    then
    
    " " STRsplit GetDenom pop
    
    dup number? not if
        ShowCreditSyntax pop 
        "@m/mbucks/credit_ok?" remove_prop exit
    then
                                                 (* set credit props *)
    ourString @ "/mbucks/credit" "/mbucks" subst
    ourPlayer @ strcat
    swap setprop
    "Set." .tell
;
  
: ClearCredit  (  --  )          (* clear credit info from an action *)
    
    1 ourBoolean !
    SetCredit                      (* use matching code in SetCredit *)
    
    dup "@/mbucks/credit_ok?"         remove_prop
    dup "@/mbucks/credit/large_coins" remove_prop
        "@/mbucks/credit/small_coins" remove_prop
        
    "Action cleared: will no longer credit." .tell
;
  
: ShowTimeSyntax  (  --  )                        (* mini-helpscreen *)
    
    "Syntax:  <cmd> #time <action> = <time> <units>" .tell
    "Example: pay #time bank = 14 days" .tell pop
;
  
: SetTime  (  --  )     (* set a lockout interval on a credit action *)
    
                                                      (* parse input *)
    ourString @ "=" instr not
    ourString @ " " instr not or if
        ShowTimeSyntax exit
    then
    
    ourString @ " " STRrsplit strip
    swap strip 
    "=" STRrsplit strip
    dup number? not if
        ShowTimeSyntax exit
    then
    atoi ourNumber !
                                    (* calculate interval from units *)
    swap
    dup "minute*" smatch if
        pop ourNumber @ 60 * ourNumber !
    else
    dup "hour*" smatch if
        pop ourNumber @ 3600 * ourNumber !
    else
    dup "day*" smatch if 
        pop ourNumber @ 86400 * ourNumber !
    else
    dup "month*" smatch if
        pop ourNumber @ 2592000 * ourNumber !
    else
        "I don't understand that unit of time." .tell
        "Use minutes, hours, days, or months." .tell pop exit
    then then then then
    
    " " STRrsplit swap pop strip
    match dup not if
        "I don't see that here." .tell pop exit
    else
        dup #-2 dbcmp if
            "I'm don't know which one you mean!" .tell pop exit
        then
    then
                                     (* set interval as # of seconds *)
    "@/mbucks/time" ourNumber @ setprop
    "Set." .tell
;
  
: ClearTime  (  --  )      (* clear time lockout info from an action *)
    
    WizPerm
    ourString @ " " instr not if
        "Syntax: '" command @ strcat "' #!time <action>" strcat 
        .tell exit
    then
                                                      (* find action *)
    ourString @ " " STRsplit swap pop strip
    match dup not if
        "I don't see that here." .tell exit
    else
        dup #-2 dbcmp if
            "I don't know which one you mean!" .tell exit
        then
    then
                                                     (* remove props *)
    ourScratch !
    ourScratch @ "@/mbucks/time" remove_prop
    ourScratch @ "@/mbucks/times/" nextprop ourString !
    begin
        ourString @ while
        ourScratch @ ourString @ over over
        nextprop ourString ! remove_prop
    repeat
    "Cleared." .tell
;
    
: SetBank  (  --  )   (* designate room as an authorized 'stake' loc *)
    
    WizPerm
    ourString @ " " instr not if
        "Syntax: " command @ strcat " #bank <room or 'here'>" strcat
        .tell exit
    then
    
    ourString @ " " STRrsplit swap pop strip
    match dup not if
        "I can't tell where you want to set the bank." .tell exit
    else
        dup #-2 dbcmp if
            "I can't tell where you want to set the bank." .tell 
            pop exit
        then
    then
    
    "@/mbucks/bank" "yes" setprop
    "Set." .tell
;
  
: ClearBank  (  --  )          (* remove 'stake ok' info from a room *)
    
    WizPerm
    ourString @ " " instr not if
        "Syntax: " command @ strcat " #!bank <room or 'here'>" strcat
        .tell exit
    then
    
    ourString @ " " STRrsplit swap pop strip
    match dup not if
        "I can't tell which room is supposed to be cleared." .tell exit
    else
        dup #-2 dbcmp if
            "I can't tell which room is supposed to be cleared." 
            .tell exit
        then
    then
    
    "@/mbucks/bank" remove_prop
    "Cleared." .tell
;
  
: main
    
    "me" match me !
    dup ourString !
    command @ ourScratch !
    
    prog "@/mbucks/large_coins" getpropstr 
    prog "@/mbucks/small_coins" getpropstr and if
        1 ourConfig !
    else
        prog "@/mbucks/large_coins" getpropstr if
            2 ourConfig !
        else
            prog "@/mbucks/small_coins" getpropstr if
                3 ourConfig !
            else
                DoInitialization
                1 ourConfig !
            then
        then
    then       
    
    dup if
        dup "#help"       swap stringpfx if DoHelp exit         else
        dup "charge"      smatch         if DoCharge exit       else
        dup "credit"      smatch         if DoCredit exit       else
        dup "charge-lg"   smatch         if DoCharge-LG exit    else
        dup "credit-lg"   smatch         if DoCredit-LG exit    else
        dup "charge-cn"   smatch         if DoCharge-CN exit    else
        dup "credit-cn"   smatch         if DoCredit-CN exit    else
        dup "charge-lgcn" smatch         if DoCharge-LGCN exit  else
        dup "credit-lgcn" smatch         if DoCredit-LGCN exit  else
        dup "format*"     smatch         if DoFormat exit       else
        dup "#charge*"    smatch         if SetCharge exit      else
        dup "#credit*"    smatch         if SetCredit exit      else
        dup "#time*"      smatch         if SetTime exit        else
        dup "#!time*"     smatch         if ClearTime exit      else
        dup "#!charge*"   smatch         if ClearCharge exit    else
        dup "#!credit*"   smatch         if ClearCredit exit    else
        dup "#tune*"      smatch         if DoTune exit         else
        dup "#rename*"    smatch         if DoRename exit       else
        dup "#alias*"     smatch         if DoAlias exit        else
        dup "#bank*"      smatch         if SetBank exit        else
        dup "#!bank*"     smatch         if ClearBank exit      else
        dup "#!alias*"    smatch         if Do!Alias exit       else
        dup "#defaults"   smatch         if DoDefaults exit     else
        dup "#defname"    smatch         if DoDefaultName exit  else
        dup "#"           swap stringpfx if
            "I don't understand that #argument." .tell exit
        then then then then then then then then then then then then
        then then then then then then then then then then then then
        then 
    then
    
    prog "@/aliases/" command @ strcat getpropstr dup if
        command !
    else
        pop
    then
    
    command @
    dup "pay"      smatch if DoPay      else
    dup "purse"    smatch if DoPurse    else
    dup "stake"    smatch if DoStake    else
    dup "exchange" smatch if DoExchange else
        "Command not understood." .tell
    then then then then
;
.
c
q