A
          / \      _             Play Now                         Nemesis on fb
          | |     | |  _______   _        _   _______    _______   _    _______
          | |\    | | |   ____| |  \    /  | |   ____|  /   ____| | |  /   ____|
 /-------/-------------------------------------------------------------------,
O= Home <=XX|  About  News  Pics  Adventurers  Wizards  Download  Connect     >
 \-------\-------------------------------------------------------------------'
          | |   \   | |  |____  | |      | | |  |____   ___ \  \  | |  ___ \  \
          | |     \_| |_______| |_|      |_| |_______| |_______/  |_| |_______/
          \ /
           V  

Documentation Area

Document Path: /doc/build/money


                      The new money and currency system

  The the money support is much more advanced and complicated than anything
before. I will try to tell you the most basic things and how the system is
working.

  The new system supports different currencies and different currency units.
At the moment, there can be 5 different currencies and each of these currencies
must consist out of 3 different currency units. I will change those values
and make the system more flexible if neccessary though.

  To give you an example: The system started with 2 currencies, the units are
sorted from the highest valuable to the least valuable unit. 

    Nemesis currency   : gold coins, silver coins, copper coins
    Ice Domain currency: guldklumps, kronen, oere

  There is a fixed exchange rate between the currency units. This exchange
rate is usually different from currency to currency. In our example:

    1 gold coin = 12 silver coins = 240 copper coins
    1 guldklump = 12 kronen = 60 oere

  Of course there is also an exchange rate between different currencies. The
exchange rate between Nemesis currency and another currenciy is determined
with an exchange rate between both least valuable currency units. Exchange
rates between different non-Nemesis currencies are calculated by converting
one of them to Nemesis currency first. In our example:

    1 copper coin = 5 oere

  For each currency there is a currency area. All money related things in this
area are done in the currency belonging to that area. Currency areas are only
defined for non-Nemesis currencies, all undefined areas have Nemesis as the
default currency.

  For more beautiful output, all currency unit names are present in a singular
and plural version, for example gold coin - gold coins. All messages generated
by the game respect this and players have to reference their money with the
right name too, for example is it not possible for a player to 
'drop 1 gold coins', he has to 'drop 1 gold coin'.

  To make the life for players a bit easier, all currency units names can be 
aliased to two character names. These two characters are not necessarily the
first two characters of the unit name, but are rather logical aliases if
possible, for example:

    gold coins == gc; silver coins == sc; copper coins == cc

  All those things are defined in the central database: "/include/money.h".
Take a look at this file to inform yourself about current settings regarding
exchange rates or money aliases.

  Now, how the currencies are implemented: Up to now, the amount of money a
player owns is stored as an integer value in a variable called 'money' inside
the player. Of course it is not possible to store the different amounts of
money units of each currency in different variables. This would take 15 
different variables if we have 5 currencies. Also exchanges would be very
complicated and many calls to the player obj would be necessary to get the
information needed.

  Therefore the money a player owns is stored in a big array, actuallay a big
array of arrays. There is an array of currencies and for each currency there
is an array of currency units. The values in these arrays are all integers.
Let's look at an example:

  Assumed that currency No. 1 is Nemesis currency and currency No. 2 is Ice
Domain currency and a player has a money array like this:

({ ({ 4, 2, 0 }), ({ 0, 3, 5 }), ({ 0, 0, 0 }), ({ 0, 0, 0 }), ({ 0, 0, 0 }) })

  This player then would own: 

    4 gold coins, 2 silver coins, 3 kronen and 5 oere

  Note that the different currency arrays are internally sorted from the most
valuable currency unit at the left to the least valuable unit at the right.

  The interface to the player are the 2 functions:

  add_money(money, currency)

    If the money argument is an integer the amount of money is added to the
    least valuable currency unit of the currency given. If the money argument
    is an array with three elements, such as ({ x, y, z }), all elements
    of this array are added to the coresponding part of the money array in
    the player.

  query_money(currency)

    For compatiblilty reasons this function returns the amount copper coins if
    the currency argument is omitted. Else it returns an 3-elements array of
    the type ({ x, y, z }), which is the corresponding part of the money
    array in the player. Important note: 1 is subtracted from the currency
    argument before the array is returned. Examples:
      query_money(0) returns the amount of copper coins
      query_money(1) returns the currency array of currency 0 (Nemesis curr)
      query_money(2) returns the currency array of currency 1 (Ice curr)

  You can call these functions to query/add money to currencies directly, but
this is not very useful in the most cases. Therefore the system provides a
whole set of functions to convert between currencies and to give beautiful
output. 

  All those functions are realized in a special daemon, the money daemon,
which is located in "etc/moneyd" normally, but in any case you should use the
MONEY_D define from "include/daemon.h" to keep dynamic in case the location of
the money daemon changes, so the proper way to call the money daemon would be:

  #include <daemon.h>
  ...
    MONEY_D->function(args);
  ...

* Functions for conversion:

  - int* explode_money(int a, int curr, int conv_flag)

    Returns an array of currency units with the least amout of coins in 
    currency curr to amount of money a.
    If conv_flag is set, a conversion from Nemesis currency to curr is made.
    Examples: 
      explode_money(1000, 0, 0) = ({ 4, 2, 0 })
        1000 copper coins are 4 gold coins and 2 silver coins

      explode_money(1000, 1, 0) = ({ 16, 3, 4 })
        1000 oere are 16 guldklumps, 3 kronen and 4 oere

      explode_money(1000, 1, 1) = ({ 3, 4, 0 })
        1000 copper coins are 3 guldklumps and 4 kronen

  - int implode_money(int* a, int curr)

    This function does exactly the opposite of the explode_money function.
    Examples:
      explode_money( ({ 4, 2, 0 }), 0) = 1000
        4 gold coins and 2 silver coins are 1000 copper coins 

      explode_money( ({ 16, 3, 4 }), 1) = 1000
        16 guldklumps, 3 kronen and 4 oere are 1000 oere

  - int convert(int amount, int* curr1, int* curr2)

    Curr1 and curr2 are 2-elemntal arrays, whose first element is the 
    currency number (0-4) and the second element ist the unit number in that
    currency (0-2). It converts the amount between the currency places
    defined with curr1 and curr2. The result is rounded down.
    Examples:
      convert(100, ({ 0, 2 }), ({ 1, 1 }) ) = 4
        100 copper coins are 4 kronen

      convert(105, ({ 0, 2 }), ({ 1, 1 }) ) = 4
        105 cooper coins are 4 kronen

* Functions for querying the database:

  - string money_to_type(int num, int curr)

    Returns the money name of currency unit num in currency curr.
    Example: money_to_type(1, 1) = "kronen"

  - int type_to_money(string type, int curr)

    Returns the unit number if currency unit type in currency curr.
    Example: type_to_money("kronen", 1) == 1

  - int* money_id(string type, int single_flag)

    Returns the currency and unit number to currency unit type stored in a
    2-elementic array. If single_flag is true, the single names are searched.

  - int currency_area(obect ob)

    Returns the currency number to object ob.

  - query_curr_exch(int curr)

    Returns the exchange rates to currency curr in 2-elementic array

* Functions for beautified output:

  - string verbose_money(int* money, int curr)

    Returns a string containing the amounts or the 3-elementic array monet
    and types to currency curr.
    Example: verbose_money( ({ 1, 5, 10 )}, 0) returns
             "1 gold coin, 5 silver coins and 10 copper coins"

  - write_money is used by player.c for the score command
  - show_price1 and show_price2 are used by shops, pubs etc
  - show_intern_course and show_extern_course are used by banks and exchange
    offices

* Functions for subtracting money

  Subtracting money as it is needed in shops is a litte more complicated as
  it looks. Let's assume, a player has 5 gold coins but he wants to buy an
  item worth 3 silver coins. Simple subtracting would be senseless, so there
  are two possibilities. Either implode both values to least valuable currency
  units, subtract the integers and the explode everything back, or make a
  complicated subtracting algorithm that subtracts the price from the money a
  player owns unit per unit and then tries to compensate negative values.
  The first algorithm is simple, but it is not realistic, because all players
  will always carry a normalized amount of money with them, so I implemented
  the second algorithm.

  Let's look at it and see how it works: 
  A player has 4 gold coins, 1 silver coins and 4 copper coins, and he wants
  buy an item worth 3 gold coins, 3 silver coins and 3 copper coins. The
  algorithm now will try to do this:

     4   1   4
   - 3   3   3
   -----------
     1  -2   1

  If there would be only positive values after the subtraction we would be
  ready now. But now we have to compensate the -2 silver coins. Well, how
  to do this, is simple, just exchange the gold coin into silver coins.

     1  -2   1   ==>   0   18   1

  Now we are finished in our example. If we had another negative value we
  would have to try to compensate this one too. If the compensation fails,
  the player is not able to buy that item.

  Well, now we just want to tell the player how much he has payed and how
  much change he gets back. This is also easy, if we subtract the money the
  player had before the transaction and the amount of money he has now.

     0  18   1
   - 4   1   4
   -----------
    -4  17  -3

  Positive numbers indicate, that the player got money back and negative
  numbers show the amount of money he payed. So the transaction would have
  the following output:

  > buy item
  You pay 4 gold coins and 3 copper coins and you get 17 silver coins back.
  You buy item.

  Well the main difference to the old system is the fact that we calculate
  all values when we check if the player is able to buy the item. But normally
  other checks follow after this, for example if the player is able to carry
  the item he wants to buy and they may fail. So we mustn't subtract the money
  in the same function, but store the 'offset' and subtract it later.

  Therefore there are two functions associated with buying/selliing. Normally
  they are used like the following example shows:

   int offset;
   offset=MONEY_D->try_subtract(val, curr);
   if (!offset) return 1;
   [...other checks...]
   MONEY_D->do_subtract(offset, curr);

  Now the discussion of the functions:

  - int* try_subtract(int value, int curr)

    This functions performs the algorithm mentioned above. It returns the so-
    called offset, a 3-elemental array, which is the result of the algorithm.
    Note that this and the next function work with the amount of money,
    this_player() has. It handles all messages that appear if the transaction
    fails.

  - void do_subtract(int* offset, int curr)

    This function subtracts the money from this_player() and makes output like
    it is shown in the example above.

  Now we have discussed the most important things concerning the new currencies
and money. Normally you don't have to care about most of these things, because
the mudlib provides a lot of support, based on these functions. Whenever you
inherit a mudlib object you will use this support automatically.

I hope i could bring you a small insight into this rather complex system. If
you have any questions about it, just ask me.
                                                   - Junky


This page was generated in LPC

Imprint / Impressum