# Money in a game

I’d like to give my character some cash and coins, which they can use to buy things. That seems like a basic thing a character should be able to do, but could potentially become unnecessarily complicated.

[code]> look at money

You have 3 dollars and 50 cents.

give cashier 3 quarters

You only have 2 quarters.

give cashier 2.43 dollars

The cashier hands you 2 cents as change.[/code]

That seems too complicated. Is there an established method of doing this that is part of TADS 3? Or is there some way to simplify this so the people playing still feel they’re using money to buy things?

Getting Started in TADS 3 has a section on money.

I would follow that example and limit the currency options to bills, gold coins, or some other indivisible unit. You sacrifice some realism, but it’s easier to make change and describe the transaction in a readable way.

Here is how I handle money in Tads3. I use the typical RPG money system of gold, silver and copper.

To use this you need to include the bignum.h file (in gameMain usually):

``````#ifndef _BIGNUM_H_
#include <bignum.h>
#endif``````

Then in gameMain I put these:

``````  myBank = new BigNumber('0') // BigNumber('99123456') // the "master" bank variable for the player
myCarriedMoney = new BigNumber('50') // 12345678') // '50') // temp variable mostly - recalculated "on the fly" based on carried gold, silver and copper
myCarriedGold = new BigNumber('2550')    // actual
myCarriedSilver = new BigNumber('320')  // actual
myCarriedCopper = new BigNumber('50') // actual

getPlayerBank(){
// local gold = new BigNumber(myBank.getAbs() / 10000).getFloor();// floor is biggest int less than this number. i.e. 12.3456 = 12
local gold = new BigNumber(gameMain.myBank.getAbs() / 10000).getFloor();
local silver = new BigNumber(gameMain.myBank.getAbs() / 100).getFloor();
silver = silver.divideBy(100)[2];// second list item is remainder
local copper = new BigNumber(gameMain.myBank.getAbs()).getFloor();
copper = copper.divideBy(100)[2];// second list item is remainder
local s0 = 'In the bank you have: ' +
gold + 'g ' +
silver + 's ' +
copper + 'c ' + '';
// '\bTotal (converting all to copper): ' + gameMain.myBank + 'c ';
return s0;
}

getPlayerCarriedMoney(){
//       --... // local gold = new BigNumber(myBank.getAbs() / 10000).getFloor();// floor is biggest int less than this number. i.e. 12.3456 = 12
// local gold = new BigNumber(gameMain.myCarriedMoney.getAbs() / 10000).getFloor();
// local silver = new BigNumber(gameMain.myCarriedMoney.getAbs() / 100).getFloor();
// silver = silver.divideBy(100)[2];// second list item is remainder
// local copper = new BigNumber(gameMain.myCarriedMoney.getAbs()).getFloor();
// copper = copper.divideBy(100)[2];// second list item is remainder
local gold = 0;
if(gameMain.myCarriedGold > 0) gold = new BigNumber(gameMain.myCarriedGold.getAbs()).getFloor();
local silver = 0;
if(gameMain.myCarriedSilver > 0) silver = new BigNumber(gameMain.myCarriedSilver.getAbs()).getFloor();
local copper = 0;
if(gameMain.myCarriedCopper > 0) copper = new BigNumber(gameMain.myCarriedCopper.getAbs()).getFloor();
local s0 = 'Your carried money is: ' +
gold + 'g ' +
silver + 's ' +
copper + 'c';
return s0;
}    ``````

…and to actually interact with your bank money I’d have an NPC banker and some input screens with lists of things you can do:

1. exit
2. deposit
3. withdraw

If you select “2” for withdraw, then ask how much gold, silver and copper. Then using some math, convert it and move it all over, subtracting from bank variable “myBank” and adding to variable “myCarriedMoney.”

Here is my banker NPC code (disregard the CromexxHuman and Male classes and go with Actor or Person here):

``````banker: CromexxHuman, Male
name = 'banker'
vocabWords = '(the) (bank) banker Roy Dribbik male/man/dude/guy/fellow/teller/tellar'
properName = 'Roy Dribbik'
isProperName = true
isawake = true
isQualifiedName = (isProperName)
location = erifAveBankInterior
firstPlayerMeetingNPCObject = nil // player object
firstPlayerMeetingNPCGender = nil // sex 'male' or 'female'
desc {
"Roy Dribbik, the banker, is dressed in a forest green suit with black pinstripes.
He\'s wearing a matching tie and thin little reading glasses and even has a banker\'s
visor on his head. He\'s got grey eyes and white tufts of hair around the ears but is
otherwise bald. He looks like he\'s in his fifties or sixties. ";
}
showBank(){
local sa = gameMain.getPlayerBank();
local sb = gameMain.getPlayerCarriedMoney();
"<table border=1 width=350 bgcolor=#000000>
<tr><td><font color=#000000>\b<<sa>></font></td></tr>
<tr><td><font color=#000000>\b<<sb>></font></td></tr>
<tr><td><font color=#000000>\b(1) Deposit\b(2) Withraw\b(3) Goodbye</font></td></tr>
</table>";
local iDone = 0;
local s0 = '';
do{
"<br> >";
s0 = inputManager.getInputLine(nil, nil);
s0 = s0.toLower();
if((s0 == 'deposit') ||
(s0 == 'withdraw') ||
(s0 == 'goodbye') ||
(s0 == 'bye') ||
(s0 == '1') ||
(s0 == '2') ||
(s0 == '3')){
iDone++;
}else{
}
"\n";
}while(iDone==0);
if((s0=='3') || (s0=='goodbye') || (s0 =='bye')){
if(banker.agendaList != nil){
foreach(local obj in banker.agendaList){
banker.removeFromAgenda(obj);// clear the agenda list
}
}
// banker.setCurState(bankerBye);
// gTranscript.showReports(true);// Per Mike Roberts "...'true' turns off the transcript for the remainder of the turn, so if you're doing a series of timeDelay() pauses, you only need to do this once, before the first pause."
// nestedActorAction(gPlayerChar,Goodbye);//  exit;
// if(banker.curState != nil){
//    "....bye!";
banker.curState.endConversation(gPlayerChar,endConvBye);// endConvBye);// see: endConvxxx in adv3.h - see: actor.t
// gPlayerChar.curState.endConversation(banker,endConvBye);
// gPlayerChar.endConversation();
// banker.setCurState(bankerWorking);
// }
// gPlayerChar.sayGoodbye(banker);// actor);
//    libGlobal.totalTurns--;
//    local tokList = Tokenizer.tokenize('goodbye');
//    executeCommand(gPlayerChar,gPlayerChar,tokList,nil);// gActor, gActor
}else if((s0=='2') || (s0=='withdraw')){
banker.withdraw();
}else{
banker.deposit();
}
}

deposit(){
local gold = new BigNumber(gameMain.myCarriedGold.getAbs()).getFloor();
local silver = new BigNumber(gameMain.myCarriedSilver.getAbs()).getFloor();
local copper = new BigNumber(gameMain.myCarriedCopper.getAbs()).getFloor();
if(gold + silver + copper == 0){
"You have no money in your posession to deposit!\b";
inputManager.pauseForMore(true);
banker.showBank();
}else{
if(copper != 0){
banker.depositAmount(copper,'copper');
}
if(silver != 0){
banker.depositAmount(silver,'silver');
}
if(gold != 0){
banker.depositAmount(gold,'gold');
}
inputManager.pauseForMore(true);
banker.showBank();
}
}

/*
*   where oMoneyObject = gameMain.myCarriedGold, ...silver, ... copper
*                sName = 'gold', 'silver' or 'copper'
*/
depositAmount(oMoneyObject,sName){
oMoneyObject = new BigNumber(oMoneyObject.getAbs()).getFloor();
local iDone = 0;
local s0 = '';
local oBigNumborf;
local iFailDigitTest = 0;
local digitPat = new RexPattern('<digit>');// matches a single digit in a string
do{
"<br>Please enter amount of <<sName>> to deposit >";
s0 = inputManager.getInputLine(nil, nil);
if(s0 == nil) s0 = '0';
if(s0 == '') s0 = '0';
s0 = s0.toLower();
// rex search for <digit> ... this stops cheaters from trying to trick the parser with math tokens, etc.
iFailDigitTest = 0;// reset flag
for(local i = 0; i <= s0.length;i++){
if(! rexMatch(digitPat,s0,i)) iFailDigitTest++;
}
oBigNumborf = new BigNumber(s0);
// getFraction() returns everything to the right of the decimal point, if this is > 0 it is a fraction
if(oBigNumborf.getFraction() > 0){
}else if(iFailDigitTest > 0){
"Please enter a whole number without any alphabetical or special characters (including the avoidance of decimal points). ";
}else if(toInteger(s0) > oMoneyObject){
"That\'s more <<sName>> than you have available.<br>";
}else if(toInteger(s0) < 0){
"You can\'t deposit negative amounts of <<sName>>.<br>";
}else if(toInteger(s0) >= 0){
"The banker says,\"Depositing <<s0>> <<sName>> to your account.\"<br>";
if(sName == 'copper'){
gameMain.myBank += toInteger(s0);// copper gets directly added
gameMain.myCarriedCopper -= toInteger(s0);
}else if(sName == 'silver'){
gameMain.myBank += (toInteger(s0) * 100); // silver converted to copper is * 100
gameMain.myCarriedSilver -= toInteger(s0);
}else if(sName == 'gold'){
gameMain.myBank += (toInteger(s0) * 10000);// gold converted to copper is * 10000
gameMain.myCarriedGold -= toInteger(s0);
}
iDone++;
}else{
"You must enter a numeric amount from 0 to <<toInteger(s0)>>.<br>";
}
"\n";
}while(iDone==0);
}

withdraw(){
local gold = new BigNumber(gameMain.myBank.getAbs() / 10000).getFloor();
local silver = new BigNumber(gameMain.myBank.getAbs() / 100).getFloor();
silver = silver.divideBy(100)[2];// second list item is remainder
local copper = new BigNumber(gameMain.myBank.getAbs()).getFloor();
copper = copper.divideBy(100)[2];// second list item is remainder
if(gold + silver + copper == 0){
"You have no money in the bank to withdraw!\b";
inputManager.pauseForMore(true);
banker.showBank();
}else{
if(copper != 0){
banker.withdrawAmount(copper,'copper');
}
if(silver != 0){
banker.withdrawAmount(silver,'silver');
}
if(gold != 0){
banker.withdrawAmount(gold,'gold');
}
inputManager.pauseForMore(true);
banker.showBank();
}
}

/*
*   where oMoneyObject = gameMain.myCarriedGold, ...silver, ... copper
*                sName = 'gold', 'silver' or 'copper'
*/
withdrawAmount(oMoneyObject,sName){
local iDone = 0;
local s0 = '';
local oBigNum;
local iFailDigitTest = 0;
local digitPat = new RexPattern('<digit>');// matches a single digit in a string
do{
"<br>Please enter amount of <<sName>> to withdraw >";
s0 = inputManager.getInputLine(nil, nil);
if(s0 == nil) s0 = '0';
if(s0 == '') s0 = '0';
s0 = s0.toLower();
// rex search for <digit> ... this stops cheaters from trying to trick the parser with math tokens, etc.
iFailDigitTest = 0;// reset flag
for(local i = 0; i <= s0.length;i++){
if(! rexMatch(digitPat,s0,i)) iFailDigitTest++;
}
oBigNum = new BigNumber(s0);
// getFraction() returns everything to the right of the decimal point, if this is > 0 it is a fraction
if(oBigNum.getFraction() > 0){
}else if(iFailDigitTest > 0){
"Please enter a whole number without any alphabetical or special characters (including the avoidance of decimal points). ";
}else if(toInteger(s0) > oMoneyObject){
"That\'s more <<sName>> than you have in the bank.<br>";
}else if(toInteger(s0) < 0){
"You can\'t withdraw negative amounts of <<sName>>.<br>";
}else if(toInteger(s0) >= 0){
"The banker says,\"Withdrawing <<s0>> <<sName>> from your account.\"<br>";
if(sName == 'copper'){
gameMain.myBank -= toInteger(s0);
gameMain.myCarriedCopper += toInteger(s0);
}else if(sName == 'silver'){
gameMain.myBank -= (toInteger(s0) * 100); // silver converted to copper is * 100
gameMain.myCarriedSilver += toInteger(s0);
}else if(sName == 'gold'){
gameMain.myBank -= (toInteger(s0) * 10000);// gold converted to copper is * 10000
gameMain.myCarriedGold += toInteger(s0);
}
iDone++;
}else{
"You must enter a numeric amount from 0 to <<toInteger(s0)>>.<br>";
}
"\n";
}while(iDone==0);
}
;
+ bankerTalking: InConversationState
stateDesc = nil
attentionSpan = 4 // after 4 turns he goes back to wandering
nextState = bankerWorking
specialDesc = "The banker, Mr. Dribbik, stands behind his teller window talking to you. "
;
specialDesc = "A banker is here. "
// stateDesc = "A banker is here. "
isInitState = true
;
+++ bankerHello: HelloTopic
topicResponse {
"\"Hello there,\" you say to the banker.\b\"Welcome to my bank,\" he says.
}
// <a plain href='<<libMessages.commandFullScore>>'>";
;
+++ bankerBye: ByeTopic
topicResponse {
"\"Bye,\" you say to the banker.\b\"Thank you for coming to my bank. See you again,\" he says. ";
//  gTranscript.showReports(true);// Per Mike Roberts "...'true' turns off the transcript for the remainder of the turn, so if you're doing a series of timeDelay() pauses, you only need to do this once, before the first pause."
//  banker.setCurState(bankerWorking);
}
isActive = bankerHello.isActive
;
+++ bankerImpBye: ImpByeTopic
topicResponse {
"The banker shrugs and waits for the next customer. ";
}
;
topicResponse {