Money

This example replicates the former British currency system of 12 pennies to one shilling and 20 shillings to one pound. It's easily adaptable to any other combination of up to three denominations, and could fairly readily be extended to more, if anyone thinks that's a reasonable monetary system in an adventure game.

Note that the examine verb has been extended so that if it gets applied to anything which has a price property, and which the player doesn't currently own, it will (a) tell you how much the item costs, and also (b) let you know whether you have enough money about your person to buy it.

It's left as an "exercise for the reader" to make the howmuchsub routine output "tuppence", "thruppence" and "sixpence" etc. where appropriate.

Anyone who's really keen could decide to include farthings, groats, florins, crowns, half-crowns, sovereigns and even guineas if they wanted to.

The howmuchsub and workoutchange functions contain the "magic numbers" 240, 20 and 12, because although the class definitions of pound and shilling declare their values (which are successfully used by the wealth function), you can only refer to these values for created instances of the classes; you can't actually reference the properties of the class itself.

Note also that the buy routine will get rid of as much small change as it can, in favour of spending larger denominations.

class money
  with name "money";

class banknote
  class money,
  with name "note" "notes//p";

class coin
  class money,
  with name "coin" "coins//p";

class pound(20)
  class banknote,
  with name "pound" "pounds//p",
  short_name "Pound note",
  value 240,
  description "A fairly standard banknote with a picture of some presumably historically significant person in the middle, and a serial number in the top left and bottom right corners.";

class shilling(50)
  class coin,
  with name "shilling" "shillings//p",
  short_name "Shilling",
  value 12,
  description "It's a silvery shilling.  What more can I say?";

class penny(50)
  class coin,
  with name "penny" "p" "pennies//p" "pence//p",
  short_name "Penny",
  plural "Pennies",
  value 1,
  description "It's quite a large copper-coloured coin.";

! How many of a certain type of object does an object contain (including its sub-objects)?
[ possessions p t c n;
! p=parent object to be scanned
! t=type of owned object to look for
! c=child objects as they are found (of any sort)
! n=running total of how many t's there are
  n=0;
  for (c=child(p) : C : c=sibling(c)) {
    if(c ofclass t) n++;
    else n=n+possessions(c,t);
  }
  return n;
];

! What total value of money does an object contain (including sub-objects)?
[ wealth p c m;
! p=parent object to be scanned
! c=child objects as they are found
! m=running total of monetary value
  m=0;
  for (c=child(p) : c : c=sibling(c)) {
    if(c ofclass money) m=m+c.value;
    else m=m+wealth(c);
  }
  return m;
];

! Remove n items of type t from object p or its descendants
! We favour taking them from the top level first where possible.
! We also assume we know in advance that the parent object has enough of them to start with.

[ takething p n t c s;
! p=parent object to be scanned
! n=how many to remove
! t=type of thing to look for
! c=child objects as they are found
! s=next sibling
  if (n==0) return 0;
! Look at all direct children first
  for (c=child(p) : c : c=s) {
    s=sibling(c);
    if(c ofclass t && n>0) { t.destroy(c); if(--n == 0) return 0; }
  }
! Then do grandchildren
  for (c=child(p) : c : c=sibling(c)) {
    if(children(c)>0 && n>0) { if ((n=takething(c,n,t)) == 0) return 0; }
  }
  return n;
];

! Create n object instances of type t and give them to p
! If n is more than are available, just make that many instead
[ givething n t p;
  n=min(n,t.remaining());
  while(n--) { move t.create() to p; }
];

[ forsale; if (noun ofclass object && noun in location && noun provides price) rtrue; rfalse; ];

extend "take" first * noun=forsale -> payfirst;

extend "get" first * noun=forsale -> buy;

extend "pay" * "for" noun=forsale -> buy;

extend "examine" first * noun=forsale -> howmuch;

[ payfirstsub;
  "It is generally-accepted practice in an establishment such as this to pay for something rather than just taking it."; ];

[ howmuchsub l s d;
  <examine noun>;
  print "The price of ", (the) noun, " is ";
  l=noun.price/240;
  s=(noun.price-240*o)/12;
  d=noun.price-240*o-12*h;

  if (l) { print l, " Pound"; if (l>1) print "s"; if (s+d) { if (s && d) print ", "; else print " and "; } } else print " exactly";
  if (s) { print s, " Shilling"; if (s>1) print "s"; if (d) print " and "; }
  if (d) print d, " Pen"; if (d==1) print "ny"; else print "ce";
  print ". ";

  if (wealth(player)==0) "It's a pity you don't have any money right now.";
  if (noun.price>wealth(player)) "It's a pity you don't have enough money right now.";
  if (noun.price*10>wealth(player)*8) "You can just about afford that.";
  "Easily affordable!";
];

[ workoutchange cost p a b c x y z n r;
! cost=cost of item
! p=player reference
! abc=cost breakdown
! xyz=possessions
! n=temporary variable
! return value to indicate whether any change was given
! need to find out how many of each denomination p owns, remove the necessary, and give back change
! We spend as many small coins as we can and as few notes as possible...

! What's the price of what we want to buy?
  a=cost/240;
  b=(cost-a*240)/12;
  c=cost-a*240-b*12;

! How much money have we got, in the various denominations?
  x=possessions(p,pound);
  y=possessions(p,shilling);
  z=possessions(p,penny);

! Let's assume for now that we don't need change
  r=false;

!Have we got enough small coins to pay the penny part?
  if (z>=c) {
    z=z-c;
    takething(p,c,penny);
    c=0;
  }
! No - spend something bigger and get change
  else {
! Can we give a shilling?
    if (y>0) {
      y--;
      z=z+12-c;
      takething(p,1,shilling);
      givething(12-c,penny,p);
      r=true;
      c=0;
    }
! No - it'll have to be a pound
    else {
      x--;
      y=y+(240-c)/20;
      z=(240-c)%20;
      takething(p,1,pound);
      givething((240-c)/20,shilling,p);
      givething((240-c)%20,penny,p);
      r=true;
      c=0;
    }
  }

! So, we've paid the necessary pennies - can we get rid of any more and avoid giving away shillings / pounds?
! Give 12 pennies if we can, save one shilling
  n=min(z/12,b);
  z=z-n*12;
  b=b-n;
  takething(p,n*12,penny);

! Give 240 pennies if possible(!), save one pound
  n=min(z/240,a);
  z=z-n*240;
  a=a-n;
  takething(p,n*240,penny);

! Finished fiddling with pennies
! Do we have enough shillings to just hand over the number needed?
  if (y>=b) {
    y=y-b;
    takething(p,b,shilling);
    b=0;
  }
! No - spend a pound and get change
  else {
    x--;
    y=y+20-b;
    takething(p,1,pound);
    givething(20-b,shilling,p);
    r=true;
    b=0;
  }

! Can we get rid of any more shillings to avoid losing pounds?
  n=min(y/20,a);
  y=y-n*20;
  a=a-n;
  takething(p,n*20,shilling);

! Finally spend any pounds we still need to
  x=x-a;
  taketing(p,a,pound);
  a=0;

! Return false if we had the right money to pay with, or true if we get change in return
  return r;
];

[ buysub;
  if (location hasnt shop) {
    print "You can't buy anything here";
    if (wealth(player) == 0) print ", and besides, you have no money anyway";
    ".";
  }
  if (noun notin location) {
    print "You can buy things here, but ", (a) noun, " is not one of them (or, possibly, they're just out of stock)";
    if (wealth(player) == 0) print ", and besides, you have no money anyway";
    ".";
  }
  if (noun.price == false) {
    print "They have ", (a) noun, " here, but not for sale";
    if (wealth(player) == 0) print ", and besides, you have no money anyway";
    ".";
  }
  if (noun.price > wealth(player)) "You can see ",(a) noun," on sale here, but you don't have enough money.";

  move noun to player; print "The sales assistant takes your money and puts it safely into the till";
  if(workoutchange(noun.price,player)) print ". You put the change into your pocket";
    " and you take your new purchase.";
];

! Why doesn't Inform already have max() and min() functions!?
! Hmm, probably for the same reason that it has a++ and a-- but is lacking a+= and a-=

[ min a b; if(a<b) return a; return b; ];

Go up
Return to main index.