2011-07-15 20:43:19 -04:00
|
|
|
/************************************************************************
|
|
|
|
* *
|
|
|
|
* Star Traders: A Game of Interstellar Trading *
|
|
|
|
* Copyright (C) 1990-2011, John Zaitseff *
|
|
|
|
* *
|
|
|
|
************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
Author: John Zaitseff <J.Zaitseff@zap.org.au>
|
|
|
|
$Id$
|
|
|
|
|
|
|
|
This file, exch.c, contains the implementation of functions dealing
|
|
|
|
with the Interstellar Stock Exchange and Trading Bank as used in Star
|
|
|
|
Traders.
|
|
|
|
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify it
|
|
|
|
under the terms of the GNU General Public License as published by the
|
|
|
|
Free Software Foundation, either version 3 of the License, or (at your
|
|
|
|
option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see http://www.gnu.org/licenses/.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "trader.h"
|
|
|
|
|
|
|
|
|
2011-07-18 06:16:55 -04:00
|
|
|
/************************************************************************
|
|
|
|
* Internal function declarations *
|
|
|
|
************************************************************************/
|
|
|
|
|
|
|
|
void visit_bank (void);
|
|
|
|
void trade_shares (int num, bool *bid_used);
|
|
|
|
|
|
|
|
|
2011-07-15 20:43:19 -04:00
|
|
|
/************************************************************************
|
|
|
|
* Stock Exchange function definitions *
|
|
|
|
************************************************************************/
|
|
|
|
|
2011-07-18 06:16:55 -04:00
|
|
|
/*-----------------------------------------------------------------------
|
|
|
|
Function: exchange_stock - Visit the Interstellar Stock Exchange
|
|
|
|
Arguments: (none)
|
|
|
|
Returns: (nothing)
|
|
|
|
|
|
|
|
This function allows the current player (in current_player) to buy,
|
|
|
|
sell and bid for shares in companies that appear on the galaxy map.
|
|
|
|
*/
|
|
|
|
|
2011-07-15 20:43:19 -04:00
|
|
|
void exchange_stock (void)
|
|
|
|
{
|
2011-07-18 06:16:55 -04:00
|
|
|
selection_t selection = SEL_NONE;
|
|
|
|
bool bid_used = false;
|
|
|
|
bool all_off_map;
|
|
|
|
int i, line;
|
|
|
|
|
|
|
|
|
|
|
|
if (quit_selected || abort_game || ! player[current_player].in_game) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
newtxwin(17, 80, LINE_OFFSET + 1, COL_CENTER(80));
|
|
|
|
|
|
|
|
while (selection != SEL_EXIT) {
|
|
|
|
selection = SEL_NONE;
|
|
|
|
|
|
|
|
// Display (or refresh) the Stock Exchange window
|
|
|
|
wbkgd(curwin, ATTR_NORMAL_WINDOW);
|
|
|
|
werase(curwin);
|
|
|
|
box(curwin, 0, 0);
|
|
|
|
|
|
|
|
center(curwin, 1, ATTR_WINDOW_TITLE, " Interstellar Stock Exchange ");
|
|
|
|
center2(curwin, 2, ATTR_NORMAL_WINDOW, ATTR_HIGHLIGHT_STR, "Player: ",
|
|
|
|
"%s", player[current_player].name);
|
|
|
|
|
|
|
|
all_off_map = true;
|
|
|
|
for (i = 0; i < MAX_COMPANIES; i++) {
|
|
|
|
if (company[i].on_map) {
|
|
|
|
all_off_map = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (all_off_map) {
|
|
|
|
center(curwin, 8, ATTR_NORMAL_WINDOW, "No companies on the map");
|
|
|
|
} else {
|
|
|
|
char *buf = malloc(BUFSIZE);
|
|
|
|
if (buf == NULL) {
|
|
|
|
err_exit("out of memory");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle the locale's currency symbol
|
|
|
|
struct lconv *lc = localeconv();
|
|
|
|
assert(lc != NULL);
|
|
|
|
snprintf(buf, BUFSIZE, "share (%s)", lc->currency_symbol);
|
|
|
|
|
|
|
|
wattrset(curwin, ATTR_WINDOW_SUBTITLE);
|
2011-07-18 21:10:09 -04:00
|
|
|
mvwprintw(curwin, 4, 2, " %-22s %12s %10s %10s %10s ",
|
|
|
|
"", "Price per", "", "Shares", "Shares");
|
|
|
|
mvwprintw(curwin, 5, 2, " %-22s %12s %10s %10s %10s ",
|
|
|
|
"Company", buf, "Return (%)", "issued", "left");
|
2011-07-18 06:16:55 -04:00
|
|
|
wattrset(curwin, ATTR_NORMAL_WINDOW);
|
|
|
|
|
|
|
|
for (line = 6, i = 0; i < MAX_COMPANIES; i++) {
|
|
|
|
if (company[i].on_map) {
|
|
|
|
mvwaddch(curwin, line, 2, PRINTABLE_MAP_VAL(COMPANY_TO_MAP(i)) |
|
|
|
|
ATTR_MAP_CHOICE);
|
|
|
|
strfmon(buf, BUFSIZE, "%!12n", company[i].share_price);
|
2011-07-18 21:10:09 -04:00
|
|
|
mvwprintw(curwin, line, 4, "%-22s %12s %10.2f %'10ld %'10ld ",
|
|
|
|
company[i].name, buf, company[i].share_return
|
|
|
|
* 100.0, company[i].stock_issued,
|
|
|
|
company[i].max_stock - company[i].stock_issued);
|
2011-07-18 06:16:55 -04:00
|
|
|
line++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
wrefresh(curwin);
|
|
|
|
|
|
|
|
// Show menu of choices for the player
|
|
|
|
newtxwin(6, 80, LINE_OFFSET + 18, COL_CENTER(80));
|
|
|
|
wbkgd(curwin, ATTR_NORMAL_WINDOW);
|
|
|
|
box(curwin, 0, 0);
|
|
|
|
|
|
|
|
wmove(curwin, 3, 2);
|
|
|
|
attrpr(curwin, ATTR_KEYCODE_STR, "<1>");
|
|
|
|
waddstr(curwin, " Display stock portfolio");
|
|
|
|
|
|
|
|
wmove(curwin, 4, 2);
|
|
|
|
attrpr(curwin, ATTR_KEYCODE_STR, "<2>");
|
|
|
|
waddstr(curwin, " Display galaxy map");
|
|
|
|
|
|
|
|
wmove(curwin, 3, 40);
|
|
|
|
attrpr(curwin, ATTR_KEYCODE_STR, "<3>");
|
|
|
|
waddstr(curwin, " Visit the Trading Bank");
|
|
|
|
|
|
|
|
wmove(curwin, 4, 40);
|
|
|
|
attrpr(curwin, ATTR_KEYCODE_STR, "<4>");
|
|
|
|
waddstr(curwin, " Exit the Stock Exchange");
|
|
|
|
|
2011-07-18 10:38:06 -04:00
|
|
|
mvwaddstr(curwin, 1, 18, "Enter selection ");
|
2011-07-18 06:16:55 -04:00
|
|
|
waddstr(curwin, "[");
|
|
|
|
attrpr(curwin, ATTR_HIGHLIGHT_STR, "Company letter");
|
|
|
|
waddstr(curwin, "/");
|
|
|
|
attrpr(curwin, ATTR_KEYCODE_STR, "1");
|
|
|
|
waddstr(curwin, "-");
|
|
|
|
attrpr(curwin, ATTR_KEYCODE_STR, "4");
|
|
|
|
waddstr(curwin, "]: ");
|
|
|
|
|
|
|
|
curs_set(CURS_ON);
|
|
|
|
wrefresh(curwin);
|
|
|
|
|
|
|
|
// Get the actual selection made by the player
|
|
|
|
while (selection == SEL_NONE) {
|
|
|
|
int key = toupper(gettxchar(curwin));
|
|
|
|
|
|
|
|
if (IS_COMPANY_KEY(key)) {
|
|
|
|
if (company[KEY_TO_COMPANY(key)].on_map) {
|
|
|
|
selection = KEY_TO_COMPANY(key);
|
|
|
|
} else {
|
|
|
|
beep();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (key) {
|
|
|
|
case '1':
|
|
|
|
curs_set(CURS_OFF);
|
|
|
|
show_status(current_player);
|
|
|
|
curs_set(CURS_ON);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '2':
|
|
|
|
curs_set(CURS_OFF);
|
|
|
|
show_map(true);
|
|
|
|
curs_set(CURS_ON);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '3':
|
|
|
|
selection = SEL_BANK;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '4':
|
|
|
|
case ' ':
|
2011-07-18 21:10:09 -04:00
|
|
|
case KEY_CTRL('C'):
|
|
|
|
case KEY_CTRL('G'):
|
|
|
|
case KEY_CTRL('\\'):
|
2011-07-18 06:16:55 -04:00
|
|
|
selection = SEL_EXIT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
beep();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
curs_set(CURS_OFF);
|
|
|
|
|
|
|
|
deltxwin(); // "Enter selection" window
|
|
|
|
txrefresh();
|
|
|
|
|
|
|
|
if (selection == SEL_BANK) {
|
|
|
|
// Visit the Interstellar Trading Bank
|
|
|
|
visit_bank();
|
|
|
|
} else if (selection == SEL_EXIT) {
|
|
|
|
// Exit the Stock Exchange: nothing more to do
|
|
|
|
;
|
|
|
|
} else {
|
|
|
|
trade_shares(selection, &bid_used);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
deltxwin(); // "Stock Exchange" window
|
|
|
|
txrefresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------
|
|
|
|
Function: visit_bank - Visit the Interstellar Trading Bank
|
|
|
|
Arguments: (none)
|
|
|
|
Returns: (nothing)
|
|
|
|
|
|
|
|
This function allows the current player to borrow or repay money from
|
|
|
|
the Interstellar Trading Bank.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void visit_bank (void)
|
|
|
|
{
|
2011-07-18 10:38:06 -04:00
|
|
|
double credit_limit;
|
|
|
|
int key;
|
|
|
|
bool done;
|
|
|
|
double val, max;
|
|
|
|
char *buf;
|
|
|
|
|
|
|
|
struct lconv *lc = localeconv();
|
|
|
|
assert(lc != NULL);
|
|
|
|
|
|
|
|
|
|
|
|
buf = malloc(BUFSIZE);
|
|
|
|
if (buf == NULL) {
|
|
|
|
err_exit("out of memory");
|
|
|
|
}
|
|
|
|
|
|
|
|
credit_limit = (total_value(current_player) - player[current_player].debt)
|
|
|
|
* CREDIT_LIMIT_RATE;
|
|
|
|
if (credit_limit < 0.0) {
|
|
|
|
credit_limit = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Show the informational part of the Bank
|
|
|
|
newtxwin(10, 76, LINE_OFFSET + 5, COL_CENTER(76));
|
|
|
|
wbkgd(curwin, ATTR_NORMAL_WINDOW);
|
|
|
|
box(curwin, 0, 0);
|
|
|
|
|
|
|
|
center(curwin, 1, ATTR_WINDOW_TITLE, " Interstellar Trading Bank ");
|
|
|
|
|
|
|
|
strfmon(buf, BUFSIZE, "%18n", player[current_player].cash);
|
|
|
|
center2(curwin, 3, ATTR_NORMAL_WINDOW, ATTR_HIGHLIGHT_STR,
|
|
|
|
"Current cash: ", " %s ", buf);
|
|
|
|
|
|
|
|
strfmon(buf, BUFSIZE, "%18n", player[current_player].debt);
|
|
|
|
center2(curwin, 4, ATTR_NORMAL_WINDOW, ATTR_HIGHLIGHT_STR,
|
|
|
|
"Current debt: ", " %s ", buf);
|
|
|
|
|
|
|
|
center2(curwin, 5, ATTR_NORMAL_WINDOW, ATTR_HIGHLIGHT_STR,
|
|
|
|
"Interest rate: ", " %17.2f%% ", interest_rate * 100.0);
|
|
|
|
|
|
|
|
strfmon(buf, BUFSIZE, "%18n", credit_limit);
|
|
|
|
center2(curwin, 7, ATTR_HIGHLIGHT_STR, ATTR_WINDOW_TITLE,
|
|
|
|
"Credit limit: ", " %s ", buf);
|
|
|
|
|
|
|
|
wrefresh(curwin);
|
|
|
|
|
|
|
|
// Show menu of choices for the player
|
|
|
|
newtxwin(7, 76, LINE_OFFSET + 15, COL_CENTER(76));
|
|
|
|
wbkgd(curwin, ATTR_NORMAL_WINDOW);
|
|
|
|
box(curwin, 0, 0);
|
|
|
|
|
|
|
|
center2(curwin, 3, ATTR_KEYCODE_STR, ATTR_NORMAL_WINDOW,
|
|
|
|
"<1>", " Borrow money ");
|
|
|
|
center2(curwin, 4, ATTR_KEYCODE_STR, ATTR_NORMAL_WINDOW,
|
|
|
|
"<2>", " Repay debt ");
|
|
|
|
center2(curwin, 5, ATTR_KEYCODE_STR, ATTR_NORMAL_WINDOW,
|
|
|
|
"<3>", " Exit from the Bank");
|
|
|
|
|
|
|
|
mvwaddstr(curwin, 1, 24, "Enter selection ");
|
|
|
|
waddstr(curwin, "[");
|
|
|
|
attrpr(curwin, ATTR_KEYCODE_STR, "1");
|
|
|
|
waddstr(curwin, "-");
|
|
|
|
attrpr(curwin, ATTR_KEYCODE_STR, "3");
|
|
|
|
waddstr(curwin, "]: ");
|
|
|
|
|
|
|
|
curs_set(CURS_ON);
|
|
|
|
wrefresh(curwin);
|
|
|
|
|
|
|
|
do {
|
|
|
|
key = gettxchar(curwin);
|
|
|
|
|
2011-07-18 21:10:09 -04:00
|
|
|
switch (key) {
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case ' ':
|
|
|
|
case KEY_CTRL('C'):
|
|
|
|
case KEY_CTRL('G'):
|
|
|
|
case KEY_CTRL('\\'):
|
|
|
|
done = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2011-07-18 10:38:06 -04:00
|
|
|
beep();
|
2011-07-18 21:10:09 -04:00
|
|
|
break;
|
2011-07-18 10:38:06 -04:00
|
|
|
}
|
|
|
|
} while (! done);
|
|
|
|
|
|
|
|
curs_set(CURS_OFF);
|
|
|
|
wechochar(curwin, key | A_BOLD);
|
|
|
|
|
|
|
|
switch (key) {
|
|
|
|
case '1':
|
|
|
|
// Borrow money from the Bank
|
|
|
|
if (credit_limit == 0.0) {
|
|
|
|
newtxwin(7, 50, LINE_OFFSET + 8, COL_CENTER(50));
|
|
|
|
wbkgd(curwin, ATTR_ERROR_WINDOW);
|
|
|
|
box(curwin, 0, 0);
|
|
|
|
|
|
|
|
center(curwin, 1, ATTR_ERROR_TITLE, " Insufficient Credit Limit ");
|
|
|
|
center(curwin, 3, ATTR_ERROR_STR,
|
|
|
|
"The Bank will not lend you any more money");
|
|
|
|
|
|
|
|
wait_for_key(curwin, 5, ATTR_WAITERROR_STR);
|
|
|
|
deltxwin();
|
|
|
|
} else {
|
|
|
|
int x, y, n;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
|
|
wbkgd(curwin, ATTR_NORMAL_WINDOW);
|
|
|
|
werase(curwin);
|
|
|
|
box(curwin, 0, 0);
|
|
|
|
|
|
|
|
mvwprintw(curwin, 3, 10, "How much do you wish to borrow? ");
|
|
|
|
|
|
|
|
wattron(curwin, A_BOLD);
|
|
|
|
if (lc->p_cs_precedes == 1) {
|
|
|
|
wprintw(curwin, "%s%s", lc->currency_symbol,
|
|
|
|
(lc->p_sep_by_space == 1) ? " " : "");
|
|
|
|
n = 10;
|
|
|
|
} else {
|
|
|
|
getyx(curwin, y, x);
|
|
|
|
n = strlen(lc->currency_symbol) + 10 + (lc->p_sep_by_space == 1);
|
|
|
|
mvwprintw(curwin, y, getmaxx(curwin) - n, "%s%s",
|
|
|
|
(lc->p_sep_by_space == 1) ? " " : "",
|
|
|
|
lc->currency_symbol);
|
|
|
|
wmove(curwin, y, x);
|
|
|
|
}
|
|
|
|
wattroff(curwin, A_BOLD);
|
|
|
|
x = getcurx(curwin);
|
|
|
|
|
|
|
|
ret = gettxdouble(curwin, &val, 0.0, credit_limit +
|
|
|
|
ROUNDING_AMOUNT, 0.0, credit_limit, 3, x,
|
|
|
|
getmaxx(curwin) - x - n, ATTR_INPUT_FIELD);
|
|
|
|
|
|
|
|
if (ret == OK) {
|
|
|
|
player[current_player].cash += val;
|
|
|
|
player[current_player].debt += val * (interest_rate + 1.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '2':
|
|
|
|
// Repay a debt
|
|
|
|
if (player[current_player].debt == 0.0) {
|
|
|
|
newtxwin(7, 50, LINE_OFFSET + 8, COL_CENTER(50));
|
|
|
|
wbkgd(curwin, ATTR_ERROR_WINDOW);
|
|
|
|
box(curwin, 0, 0);
|
|
|
|
|
|
|
|
center(curwin, 1, ATTR_ERROR_TITLE, " No Debt ");
|
|
|
|
center(curwin, 3, ATTR_ERROR_STR, "You have no debt to repay");
|
|
|
|
|
|
|
|
wait_for_key(curwin, 5, ATTR_WAITERROR_STR);
|
|
|
|
deltxwin();
|
|
|
|
} else if (player[current_player].cash == 0.0) {
|
|
|
|
newtxwin(7, 60, LINE_OFFSET + 8, COL_CENTER(60));
|
|
|
|
wbkgd(curwin, ATTR_ERROR_WINDOW);
|
|
|
|
box(curwin, 0, 0);
|
|
|
|
|
|
|
|
center(curwin, 1, ATTR_ERROR_TITLE, " No Cash ");
|
|
|
|
center(curwin, 3, ATTR_ERROR_STR, "You have no cash with which to repay the debt!");
|
|
|
|
|
|
|
|
wait_for_key(curwin, 5, ATTR_WAITERROR_STR);
|
|
|
|
deltxwin();
|
|
|
|
} else {
|
|
|
|
int x, y, n;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
|
|
wbkgd(curwin, ATTR_NORMAL_WINDOW);
|
|
|
|
werase(curwin);
|
|
|
|
box(curwin, 0, 0);
|
|
|
|
|
|
|
|
mvwprintw(curwin, 3, 10, "How much do you wish to repay? ");
|
|
|
|
|
|
|
|
wattron(curwin, A_BOLD);
|
|
|
|
if (lc->p_cs_precedes == 1) {
|
|
|
|
wprintw(curwin, "%s%s", lc->currency_symbol,
|
|
|
|
(lc->p_sep_by_space == 1) ? " " : "");
|
|
|
|
n = 10;
|
|
|
|
} else {
|
|
|
|
getyx(curwin, y, x);
|
|
|
|
n = strlen(lc->currency_symbol) + 10 + (lc->p_sep_by_space == 1);
|
|
|
|
mvwprintw(curwin, y, getmaxx(curwin) - n, "%s%s",
|
|
|
|
(lc->p_sep_by_space == 1) ? " " : "",
|
|
|
|
lc->currency_symbol);
|
|
|
|
wmove(curwin, y, x);
|
|
|
|
}
|
|
|
|
wattroff(curwin, A_BOLD);
|
|
|
|
x = getcurx(curwin);
|
|
|
|
|
|
|
|
max = MIN(player[current_player].cash + ROUNDING_AMOUNT,
|
|
|
|
player[current_player].debt + ROUNDING_AMOUNT);
|
|
|
|
|
|
|
|
ret = gettxdouble(curwin, &val, 0.0, max, 0.0, max, 3, x,
|
|
|
|
getmaxx(curwin) - x - n, ATTR_INPUT_FIELD);
|
|
|
|
|
|
|
|
if (ret == OK) {
|
|
|
|
player[current_player].cash -= val;
|
|
|
|
player[current_player].debt -= val;
|
|
|
|
|
|
|
|
if (player[current_player].cash < ROUNDING_AMOUNT) {
|
|
|
|
player[current_player].cash = 0.0;
|
|
|
|
}
|
|
|
|
if (player[current_player].debt < ROUNDING_AMOUNT) {
|
|
|
|
player[current_player].debt = 0.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
deltxwin(); // "Enter selection" window
|
|
|
|
deltxwin(); // Trading Bank window
|
|
|
|
txrefresh();
|
|
|
|
|
|
|
|
free(buf);
|
2011-07-18 06:16:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------
|
|
|
|
Function: trade_stock - Trade stock in a particular company
|
|
|
|
Arguments: num - Company with which to trade
|
|
|
|
bid_used - Has the player used up their bid?
|
|
|
|
Returns: (nothing)
|
|
|
|
|
|
|
|
This function allows the current player to buy and sell stock in
|
|
|
|
company num. The variable *bid_used is set to true if the player tries
|
|
|
|
to bid for more shares to be released by the company.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void trade_shares (int num, bool *bid_used)
|
|
|
|
{
|
|
|
|
assert(num >= 0 && num < MAX_COMPANIES);
|
|
|
|
|
2011-07-15 20:43:19 -04:00
|
|
|
// @@@ To be written
|
|
|
|
}
|