From 1b5919c1c68a379cdc20a56dcb6dfa650f3eac87 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Wed, 6 Jan 2016 05:26:54 -0500 Subject: finished with title compression stuff for now (maybe forever) --- Makefile | 28 +- dfoxtest.c | 210 ---- oldcurses.c | 161 --- taipan.c_before_timed_getch | 2877 ------------------------------------------- titlecomp.pl | 3 + titlecompression.txt | 52 +- 6 files changed, 64 insertions(+), 3267 deletions(-) delete mode 100644 dfoxtest.c delete mode 100644 oldcurses.c delete mode 100644 taipan.c_before_timed_getch diff --git a/Makefile b/Makefile index 0026fd5..7873ad9 100644 --- a/Makefile +++ b/Makefile @@ -78,10 +78,6 @@ taipan.atr: all axe -w AUTORUN.SYS taipan.atr rm -f AUTORUN.SYS -# old title -#$(XEX): taimain.xex taifont.xex title.xex -# cat taifont.xex title.xex taimain.xex > $(XEX) - # The game binary is a multi-part binary load file. This rule # depends on all the pieces, and just concatenates them. $(XEX): taimain.xex taifont.xex newtitle.xex comptitle.xex @@ -92,14 +88,17 @@ $(XEX): taimain.xex taifont.xex newtitle.xex comptitle.xex # using GTIA narrow playfield. The original title screen for the Apple # is a 280x192 bitmap with a few blank lines at the top & bottom. I # squished it horizontally to 256 pixels and got rid of the blank lines, -# to save load time. +# to save load time. Note that titledata.xex is no longer built into +# the game binary as-is: it's now used as input for creating +# comptitle.xex, the compressed title screen. titledata.xex: newtitle.pl newtitle.png perl newtitle.pl > titledata.xex -# compressed title, for faster loading. +# compressed title, for faster loading. see titlecompression.txt +# for gory details. comptitle.xex: titledata.xex titlecomp.pl comptitle.s.in perl titlecomp.pl 151 < titledata.xex - cl65 -o comptitle.xex -t none comptitle.s + cl65 -l comptitle.lst -o comptitle.xex -t none comptitle.s # Init segment that loads after the title screen data. It sets up # a custom display list and sets the GTIA for narrow playfield, @@ -115,11 +114,6 @@ newtitle.xex: newtitle.s ver.dat ver.dat: mkver.pl perl mkver.pl $(VERSION) > ver.dat -# former textmode title screen, was generated by TITLE.LST. Replaced -# by graphical title screen. -#title.xex: TITLE.DAT -# perl title.pl TITLE.DAT > title.xex - # The main executable. All the C and asm code goes here, except the init # segment in newtitle.s. taimain.xex: taipan.c rand.s draw_lorcha.s timed_getch.s jsleep.s portstat.s clrtobot.s @@ -186,3 +180,13 @@ lorchatest: lorchatest.c draw_lorcha.s taifont.xex cl65 -t atari -O -T -o lorchatest1.xex lorchatest.c draw_lorcha.s cat taifont.xex lorchatest1.xex > lorchatest.xex atari800 -nobasic lorchatest.xex + +# former textmode title screen, was generated by TITLE.LST. Replaced +# by graphical title screen. +#title.xex: TITLE.DAT +# perl title.pl TITLE.DAT > title.xex + +# old title +#$(XEX): taimain.xex taifont.xex title.xex +# cat taifont.xex title.xex taimain.xex > $(XEX) + diff --git a/dfoxtest.c b/dfoxtest.c deleted file mode 100644 index aefdd5d..0000000 --- a/dfoxtest.c +++ /dev/null @@ -1,210 +0,0 @@ -/*#include */ -typedef char int8_t; -typedef unsigned char uint8_t; -typedef short int16_t; -typedef unsigned short uint16_t; -typedef long int32_t; -typedef unsigned long uint32_t; - -/*#include */ -typedef uint8_t bool; -#ifndef true - #define true 1 -#endif -#ifndef false - #define false 0 -#endif - -/*Defines*/ -#define PREFIX_BITS_PER_BYTE 8 - /*Neither can be greater than 128*/ -#define PREFIX_intsize 4 -#define PREFIX_INTSIZE 8 - -/*Typedefs*/ -typedef uint8_t PREFIX_INT[PREFIX_INTSIZE]; -typedef int32_t PREFIX_int; - -/*Globals*/ -bool PREFIX_math_overflow; - -/*==============================================================CODE=============================================================*/ - -void PREFIX_zero_int(PREFIX_INT *ptr) -{ - uint8_t i; - - for (i = 0; i < PREFIX_INTSIZE; i++) - *ptr[i] = 0; -} - -bool PREFIX_isneg(PREFIX_INT *in) -{ - return (*in[PREFIX_INTSIZE-1] & 0x80) >> 7; -} - -void PREFIX_int_to_buf(PREFIX_INT *dest, PREFIX_int *src) -{ - PREFIX_int tmp; - uint8_t i; - - tmp = *src; - for (i = 0; i < PREFIX_intsize; i++) - *dest[i] = (tmp >> (i*PREFIX_BITS_PER_BYTE)) & 0xFF; -} - -void PREFIX_buf_to_int(PREFIX_int *dest, PREFIX_INT *src) -{ - PREFIX_int ret; - uint8_t i; - - ret = 0; - for (i = 0; i < PREFIX_INTSIZE; i++) { - ret += dest[i]; - ret <<= PREFIX_BITS_PER_BYTE; - } - - *src[0] = ret; -} - -void PREFIX_buf_to_buf(PREFIX_INT *dest, PREFIX_INT *src) -{ - uint8_t i; - - for (i = 0; i < PREFIX_INTSIZE; i++) - *dest[i] = *src[i]; -} - -/*True to*/ -bool PREFIX_equal(PREFIX_INT *left, PREFIX_INT *right) -{ - uint8_t i; - - for (i = 0; i < PREFIX_INTSIZE; i++) - if (*left[i] != *right[i]) - return false; - - return true; -} - -/*greater than*/ -bool PREFIX_greater(PREFIX_INT *left, PREFIX_INT *right) -{ - uint8_t i; - bool left_neg, right_neg; - - left_neg = PREFIX_isneg(left); - right_neg = PREFIX_isneg(right); - - if (left_neg || right_neg) { - if (left_neg && !right_neg) - return false; - else if (!left_neg && right_neg) - return true; - } - - for (i = 0; i < PREFIX_INTSIZE; i++) { - if (*left[i] > *right[i]) - return true; - else if (*left[i] < *right[i]) - return false; - } - - return false; -} - -/*less than*/ -bool PREFIX_less(PREFIX_INT *left, PREFIX_INT *right) -{ - return !PREFIX_greater(left, right); -} - -/*greater than or equal to*/ -bool PREFIX_greater_or_equal(PREFIX_INT *left, PREFIX_INT *right) -{ - return PREFIX_greater(left, right) || PREFIX_equal(left, right); -} - -/*less than or equal to*/ -bool PREFIX_less_or_equal(PREFIX_INT *left, PREFIX_INT *right) -{ - return PREFIX_less(left, right) || PREFIX_equal(left, right); -} - -/*Negatives not currently supported*/ -void PREFIX_add(PREFIX_INT *dest, PREFIX_INT *src) -{ - /*operator*/ - uint16_t op; - uint8_t i; - bool dest_neg, src_neg; - - src_neg = PREFIX_isneg(src); - dest_neg = PREFIX_isneg(dest); - - if (src_neg || dest_neg) { - PREFIX_math_overflow = true; - return; - } - - for (i = 0; i < PREFIX_INTSIZE; i++) { - op = *src[i]; - op += *dest[i]; - *dest[i] = op & 0xFF; - - op >>= PREFIX_BITS_PER_BYTE; - } - - if (i == PREFIX_INTSIZE && op != 0) - PREFIX_math_overflow = true; - - if (!(src_neg || dest_neg) && PREFIX_isneg(dest)) - PREFIX_math_overflow = true; -} - -/*Negatives not currently supported*/ -void PREFIX_sub(PREFIX_INT *dest, PREFIX_INT *src) -{ - uint16_t op; - uint8_t i, j; - bool dest_neg, src_neg; - uint16_t tmp_op = 0; - bool got_borrow = false; - PREFIX_INT tmp_dest, tmp_src; - - src_neg = PREFIX_isneg(src); - dest_neg = PREFIX_isneg(dest); - - if (src_neg || dest_neg) - PREFIX_math_overflow = true; - - PREFIX_buf_to_buf(&tmp_src, src); - PREFIX_buf_to_buf(&tmp_dest, dest); - for (i = 0; i < PREFIX_INTSIZE; i++) { - op = tmp_dest[i]; - - if (tmp_src[i] > tmp_dest[i]) { - if (i == (PREFIX_INTSIZE-1)) - PREFIX_math_overflow = true; - - for (j = (i+1); j < PREFIX_INTSIZE; j++ ) - if (tmp_dest[j]) { - tmp_dest[j] -= 1; - tmp_op = 0xFF; - got_borrow = true; - break; - } - - if (!got_borrow) - PREFIX_math_overflow = true; - - for (j--; j > i; j--) - tmp_src[j] = 0xFF; - - op += tmp_op; - } - - op -= tmp_src[i]; - *dest[i] = op & 0xFF; - } -} diff --git a/oldcurses.c b/oldcurses.c deleted file mode 100644 index c8702d5..0000000 --- a/oldcurses.c +++ /dev/null @@ -1,161 +0,0 @@ -/* curses wrappers. - The original code uses curses, cc65 uses conio, so I wrote this stuff - to map the curses calls to conio ones. - Later on, just replaced all the curses stuff (except clrtobot() and - clrtoeol()) with direct calls to conio. - Keeping this around in case it's useful for porting some other - curses app to cc65. */ - -/* original plan was to use time() or _systime(). It turns out that - these are not implemented on the Atari, and always return -1. - So, use the OS's countdown timer instead. - Anyone porting to another cc65 platform needs to rewrite this. - - TODO: rewrite in terms of clock() and CLOCKS_PER_SEC. Will make it - less atari-specific, plus auto-adjust for pal/ntsc. - - TODO: there is atari-specific stuff elsewhere in the code :( - */ - -static unsigned int tmout_jiffies = 0; - -/* -void timeout(unsigned int msec) { - if(msec > 0) - tmout_jiffies = (msec / 100) * 6; // TODO: should be 5 for PAL - else - tmout_jiffies = 0; -} -*/ - -/* set timer with interrupts disabled, to avoid race condition - where an interrupt happens between setting the high & low bytes. */ -void start_timer() { - __asm__("SEI"); - POKE(541, tmout_jiffies / 256); - POKE(540, tmout_jiffies % 256); - __asm__("CLI"); -} - -/* this getch() works like curses, except it always acts - like it's in cbreak mode. */ -int getch() { - int ret = -1; - - if(tmout_jiffies == 0) return cgetc(); - - start_timer(); - - do { - if(kbhit()) { - ret = cgetc(); - break; - } - } while (timer_running()); - - return ret; -} - -#define timer_running() (PEEK(541) || PEEK(540)) -#define timer_expired() (!timer_running()) - -int flushinp() { - POKE(764, 255); - return 0; -} - -void clrtobot() { - unsigned char rows, cols, y, oldx, oldy; - oldx = wherex(); - oldy = wherey(); - screensize(&cols, &rows); - cclear(cols - wherex()); /* leaves cursor at start of next line */ - for(y = wherey(); y < rows; y++) - cclearxy(0, y, cols); - gotoxy(oldx, oldy); -} - -/* TODO: rewrite in asm */ -void clrtobot() { - unsigned char rows, cols, y, oldx, oldy; - oldx = wherex(); - oldy = wherey(); - screensize(&cols, &rows); - cclear(cols - wherex()); /* leaves cursor at start of next line */ - for(y = wherey(); y < rows; y++) - cclearxy(0, y, cols); - gotoxy(oldx, oldy); -} - -/* TODO: rewrite in asm */ -void clrtoeol() { - unsigned char cols, rows, oldx, oldy; - oldx = wherex(); - oldy = wherey(); - screensize(&cols, &rows); - cclear(cols - wherex()); - gotoxy(oldx, oldy); -} - - -/* one-to-one mapping between some of the conio API and the curses one */ -#define clear() clrscr() -#define printw cprintf -#define curs_set(x) cursor(x) - -/* conio doesn't do deferred printing, refresh() is a no-op */ -#define refresh() - -/* we don't support cooked or raw mode, cbreak() is a no-op */ -#define cbreak() - -/* we don't support echo mode */ -#define noecho() - -/* curses move() takes args in the opposite order to gotoxy() */ -#define move(a, b) gotoxy(b, a) - -/* our attrset() only supports A_NORMAL and A_REVERSE, since - the Atari can only print normal and reverse video. */ -#define A_NORMAL 0 -#define A_REVERSE 1 -#define attrset(x) revers(x) - -/**** atari-specific stuff */ - -/* original plan was to use time() or _systime(). It turns out that - these are not implemented on the Atari, and always return -1. - So, use the OS's countdown timer instead. - Anyone porting to another cc65 platform needs to rewrite this. - - TODO: rewrite in terms of clock() and CLOCKS_PER_SEC. Will make it - less atari-specific, plus auto-adjust for pal/ntsc. - - TODO: there is atari-specific stuff elsewhere in the code :( - */ - -static unsigned int tmout_jiffies = 0; - -void timeout(unsigned int msec) { - if(msec > 0) - tmout_jiffies = (msec / 100) * 6; // TODO: should be 5 for PAL - else - tmout_jiffies = 0; -} - -/* set timer with interrupts disabled, to avoid race condition - where an interrupt happens between setting the high & low bytes. */ -void start_timer() { - __asm__("SEI"); - POKE(541, tmout_jiffies / 256); - POKE(540, tmout_jiffies % 256); - __asm__("CLI"); -} - -#define timer_running() (PEEK(541) || PEEK(540)) -#define timer_expired() (!timer_running()) - -int flushinp() { - POKE(764, 255); - return 0; -} diff --git a/taipan.c_before_timed_getch b/taipan.c_before_timed_getch deleted file mode 100644 index 7b399eb..0000000 --- a/taipan.c_before_timed_getch +++ /dev/null @@ -1,2877 +0,0 @@ -#include -#include -#include -#include -#include -#include - -/* define this for testing sea_battle(). it causes a pirate - attack every time you leave port. Don't leave defined for - a relese!! */ -// #define COMBAT_TEST - -/**** atari-specific stuff */ - -/* values returned by cgetc() for backspace & enter keys */ -#define BKSP 0x7e -#define ENTER 0x9b - -/* timed_getch() args for seconds, based on jiffy clock of - target system. No adjustment made for PAL, sorry. */ -#define TMOUT_1S 60 -#define TMOUT_3S 180 -#define TMOUT_5S 300 - -/* original plan was to use time() or _systime(). It turns out that - these are not implemented on the Atari, and always return -1. - So, use the OS's countdown timer instead. - Anyone porting to another cc65 platform needs to rewrite this. - - TODO: rewrite in terms of clock() and CLOCKS_PER_SEC. Will make it - less atari-specific, plus auto-adjust for pal/ntsc. - - TODO: there is atari-specific stuff elsewhere in the code :( - */ - -static unsigned int tmout_jiffies = 0; - -void timeout(unsigned int msec) { - if(msec > 0) - tmout_jiffies = (msec / 100) * 6; // TODO: should be 5 for PAL - else - tmout_jiffies = 0; -} - -/* set timer with interrupts disabled, to avoid race condition - where an interrupt happens between setting the high & low bytes. */ -void start_timer() { - __asm__("SEI"); - POKE(541, tmout_jiffies / 256); - POKE(540, tmout_jiffies % 256); - __asm__("CLI"); -} - -#define timer_running() (PEEK(541) || PEEK(540)) -#define timer_expired() (!timer_running()) - -int flushinp() { - POKE(764, 255); - return 0; -} - -void atari_text_setup() { - POKE(710, 0xc0); // green background - POKE(709, 0x0c); // bright text - POKE(756, 0xb8); // use our custom font -} - -void jsleep(unsigned int jiffies) { - tmout_jiffies = jiffies; - start_timer(); - while(timer_running()) - ; - timeout(-1); -} - - -/* Atari-specific random number functions from rand.s. - Non-Atari platforms can probably just: -#define initrand() _randomize(); -#define randi() rand(); -#define randl() (unsigned long)((randi() << 16) | randi()) -*/ - -#define initrand() /* no-op on Atari */ -extern unsigned int __fastcall__ randi(void); -extern unsigned long __fastcall__ randl(void); - -/* used to use this: -unsigned long randl() { - unsigned long r = rand(); - char *buf = (char *)r; - buf[2] = PEEK(53770); - buf[3] = PEEK(53770); - return r; -} -*/ - - -/**** End of atari-specific stuff */ - -unsigned long randclamp(unsigned long clamp) { - return randl() % clamp; -} - -/* TODO: rewrite in asm */ -void clrtobot() { - unsigned char rows, cols, y, oldx, oldy; - oldx = wherex(); - oldy = wherey(); - screensize(&cols, &rows); - cclear(cols - wherex()); /* leaves cursor at start of next line */ - for(y = wherey(); y < rows; y++) - cclearxy(0, y, cols); - gotoxy(oldx, oldy); -} - -/* TODO: rewrite in asm */ -void clrtoeol() { - unsigned char cols, rows, oldx, oldy; - oldx = wherex(); - oldy = wherey(); - screensize(&cols, &rows); - cclear(cols - wherex()); - gotoxy(oldx, oldy); -} - -/* TODO: rewrite in asm */ -/* this getch() works like curses, except it always acts - like it's in cbreak mode. */ -int getch() { - int ret = -1; - - if(tmout_jiffies == 0) return cgetc(); - - start_timer(); - - do { - if(kbhit()) { - ret = cgetc(); - break; - } - } while (timer_running()); - - return ret; -} - -/* TODO: rewrite in asm */ -int lc(int a) { - if(a >= 'A' && a <= 'Z') a ^= 0x20; - return a; -} - -/* TODO: rewrite in asm */ -/* wrapper for getch() that returns letters as lowercase only - (and everything else normally). Avoids a bunch of reduntant - if(foo == 'A' || foo == 'a') tests. */ -int lcgetch() { - return lc(getch()); -} - -/* taipan functions (modified as little as possible) */ -#define GENERIC 1 -#define LI_YUEN 2 - -void splash_intro(void); -int get_one(void); -unsigned long get_num(int maxlen); -void name_firm(void); -void cash_or_guns(void); -void set_prices(void); -void port_stats(void); -int port_choices(void); -void new_ship(void); -void new_gun(void); -void li_yuen_extortion(void); -void elder_brother_wu(void); -void good_prices(void); -void buy(void); -void sell(void); -void visit_bank(void); -void transfer(void); -void quit(void); -void overload(void); -void fancy_numbers(unsigned long num, char *fancy); -int sea_battle(int id, int num_ships); -extern void __fastcall__ draw_lorcha(int which, int displacement, int mask); -void clear_lorcha(int x, int y); -void draw_blast(int x, int y); -void sink_lorcha(int which); -void fight_stats(int ships, int orders); -void mchenry(void); -void retire(void); -void final_stats(void); - -char firm[23], - fancy_num[24]; - -char *item[] = { "Opium", "Silk", "Arms", "General Cargo" }; - -char *location[] = { "At sea", "Hong Kong", "Shanghai", "Nagasaki", - "Saigon", "Manila", "Singapore", "Batavia" }; - -char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - -char *st[] = { "Critical", " Poor", " Fair", - " Good", " Prime", "Perfect" }; - -unsigned long cash = 0, - bank = 0, - debt = 0, - booty = 0, - ec = 20, - ed = 1; // used to be a float, 0.5 - -unsigned long price[4]; - -int base_price[4][8] = { {1000, 11, 16, 15, 14, 12, 10, 13}, - {100, 11, 14, 15, 16, 10, 13, 12}, - {10, 12, 16, 10, 11, 13, 14, 15}, - {1, 10, 11, 12, 13, 14, 15, 16} }; - -int hkw_[4], - hold_[4]; - -int hold = 0, - capacity = 60, - guns = 0, - bp = 0, - damage = 0, - month = 1, - year = 1860, - li = 0, - port = 1, - wu_warn = 0, - wu_bailout = 0; - -/* print an int or long as a string, conio-style */ -void cprintulong(unsigned long ul) { - cputs(ultoa(ul, fancy_num, 10)); -} - -#ifdef COMBAT_TEST -void show_damage(void) { - cputc(' '); - cprintulong(damage); - cputc('/'); - cprintulong(capacity); -} -#else -#define show_damage() -#endif - -// fancy_numbers() will get replaced sooner or later. -// void cprintfancy(unsigned long ul) { -// } - -void at_sea() { - gotoxy(30, 6); - cputc(' '); - revers(1); - cputs(location[0]); - revers(0); - cputc(' '); - cputc(' '); -} - -/* this bit of code was duplicated a *bunch* of times, - making it a function makes the binary 2K smaller. */ - -void prepare_report() { - gotoxy(0, 16); - clrtobot(); -} - -void compradores_report() { - prepare_report(); - cputs("Comprador's Report\r\n\n"); -} - -void captains_report() { - prepare_report(); - cputs(" Captain's Report\r\n\n"); -} - - -void overload(void) -{ - compradores_report(); - cputs("Your ship is overloaded, Taipan!!"); - timeout(5000); - getch(); - timeout(-1); - return; -} - -void new_ship(void) -{ - int choice = 0, - time; - - // float amount; - unsigned long amount; - - time = ((year - 1860) * 12) + month; - amount = randi()%(1000 * (time + 5) / 6) * (capacity / 50) + 1000; - - if (cash < amount) - { - return; - } - - fancy_numbers(amount, fancy_num); - - compradores_report(); - cputs("Do you wish to trade in your "); - if (damage > 0) - { - revers(1); - cputs("damaged"); - revers(0); - } else { - cputs("fine"); - } - cputs("\r\nship for one with 50 more capacity by\r\n"); - cputs("paying an additional "); - cputs(fancy_num); - cputs(", Taipan? "); - - while ((choice != 'Y') && (choice != 'y') && - (choice != 'N') && (choice != 'n')) - { - choice = get_one(); - } - - if ((choice == 'Y') || (choice == 'y')) - { - cash -= amount; - hold += 50; - capacity += 50; - damage = 0; - } - - if ((randi()%2 == 0) && (guns < 1000)) - { - port_stats(); - new_gun(); - } - - port_stats(); - - return; -} - -void new_gun(void) -{ - int choice = 0, - time; - - unsigned long amount; - - time = ((year - 1860) * 12) + month; - amount = randi()%(1000 * (time + 5) / 6) + 500; - - if ((cash < amount) || (hold < 10)) - { - return; - } - - fancy_numbers(amount, fancy_num); - - compradores_report(); - cputs("Do you wish to buy a ship's gun\r\n for "); - cputs(fancy_num); - cputs(", Taipan? "); - - while ((choice != 'Y') && (choice != 'y') && - (choice != 'N') && (choice != 'n')) - { - choice = get_one(); - } - - if ((choice == 'Y') || (choice == 'y')) - { - cash -= amount; - hold -= 10; - guns += 1; - } - - port_stats(); - - return; -} - -void fancy_numbers(unsigned long num, char *fancy) -{ - /* note to self: find out why there's graphic corruption - if this isn't static. It's caused by sprintf() or strcpy() - writing to the font, which is supposed to be above cc65's max - usable address! */ - static char number[18]; - char mil = 0; - unsigned int num1, num2; - - if (num >= 100000000L) - { - num1 = (num / 1000000L); - ultoa(num1, fancy, 10); - mil = 1; - } else if (num >= 10000000L) { - num1 = (num / 1000000L); - num2 = ((num % 1000000L) / 100000L); - ultoa(num1, fancy, 10); - if (num2 > 0) - { - strcat(fancy, "."); - ultoa(num2, number, 10); - strcat(fancy, number); - } - mil = 1; - } else if (num >= 1000000L) { - num1 = (num / 1000000L); - num2 = ((num % 1000000L) / 10000L); - ultoa(num1, fancy, 10); - if (num2 > 0) - { - strcat(fancy, "."); - ultoa(num2, number, 10); - strcat(fancy, number); - } - mil = 1; - } else { - ultoa(num, fancy, 10); - } - - if(mil) strcat(fancy, " Million"); -} - -/* -void fancytest(void) { - fancy_numbers(1000, fancy_num); - cputs(fancy_num); - cputs("\r\n"); - fancy_numbers(10000, fancy_num); - cputs(fancy_num); - cputs("\r\n"); - fancy_numbers(100000, fancy_num); - cputs(fancy_num); - cputs("\r\n"); - fancy_numbers(1000000, fancy_num); - cputs(fancy_num); - cputs("\r\n"); - fancy_numbers(4294967295, fancy_num); // LONG_MAX - cputs(fancy_num); - cputs("\r\n"); - cgetc(); -} -*/ - -void fight_stats(int ships, int orders) -{ - // char ch_orders[12]; - - /* - if (orders == 0) - { - strcpy(ch_orders, "\0"); - } else if (orders == 1) { - strcpy(ch_orders, "Fight "); - } else if (orders == 2) { - strcpy(ch_orders, "Run "); - } else { - strcpy(ch_orders, "Throw Cargo"); - } - */ - - gotoxy(0, 0); - - /* - if (ships >= 1000) - { - printw("%d", ships); - } else if (ships >= 100) { - printw(" %d", ships); - } else if (ships >= 10) { - printw(" %d", ships); - } else { - printw(" %d", ships); - } - */ - - if(ships < 1000) cputc(' '); - if(ships < 100) cputc(' '); - if(ships < 10) cputc(' '); - cprintulong(ships); - - // gotoxy(0, 5); - cputs(" ship"); - if(ships != 1) cputc('s'); - cputs(" attacking, Taipan! \r\n"); - - /* - if (ships == 1) - { - printw("ship attacking, Taipan! \n"); - } else { - printw("ships attacking, Taipan!\n"); - } - */ - - // printw("Your orders are to: %s", ch_orders); - cputs("Your orders are to: "); - if(orders == 1) - cputs("Fight "); - else if(orders == 2) - cputs("Run "); - else if(orders == 3) - cputs("Throw Cargo"); - - /* - move(0, 50); - printw("| We have"); - move(1, 50); - printw("| %d guns", guns); - move(2, 50); - printw("----------"); - move(16, 0); - */ - - revers(1); - gotoxy(30, 0); - cputs(" We have"); - gotoxy(30, 1); - cputc(' '); - if(guns < 1000) cputc(' '); - if(guns < 100) cputc(' '); - if(guns < 10) cputc(' '); - cprintulong(guns); - cputs(" guns"); - revers(0); - return; -} - -void sink_lorcha(int which) { - int i; - for(i = 1; i < 8; i++) { - draw_lorcha(which, i, 0); - jsleep(5); - } -} - -/* print an inverse video plus if there are offscreen ships, - or clear it to a space if not. */ -void plus_or_space(unsigned char b) { - gotoxy(39, 15); - cputc(b ? 0xab : ' '); -} - -int sea_battle(int id, int num_ships) { - /* These locals seem to eat too much stack and - cause weird behaviour, so they're static now. */ - static int ships_on_screen[10]; - static int orders, - num_on_screen, - time, - s0, - ok, - ik, - i, - input, - status; - - orders = 0; - num_on_screen = 0; - time = ((year - 1860) * 12) + month; - s0 = num_ships; - ok = 0; - ik = 1; - - booty = (time / 4 * 1000 * num_ships) + randi()%1000 + 250; - - for(i = 0; i <= 9; i++) { - ships_on_screen[i] = 0; - } - - clrscr(); - flushinp(); - fight_stats(num_ships, orders); - - while(num_ships > 0) { - status = 100 - ((damage * 100 / capacity)); - if(status <= 0) { - return 4; - } - flushinp(); - gotoxy(0, 3); - clrtoeol(); - - cputs("Current seaworthiness: "); - cputs(st[status / 20]); - cputs(" ("); - cprintulong(status); - cputs("%)"); - gotoxy(0, 4); - show_damage(); - - for(i = 0; i <= 9; i++) { - if (num_ships > num_on_screen) { - if (ships_on_screen[i] == 0) { - jsleep(5); - ships_on_screen[i] = (randi() % ec) + 20; - draw_lorcha(i, 0, 0); - num_on_screen++; - } - } - } - - plus_or_space(num_ships > num_on_screen); - - gotoxy(0, 16); - cputs("\r\n"); - timeout(3000); - input = lcgetch(); - timeout(-1); - - if(input == 'f') { - orders = 1; - } else if(input == 'r') { - orders = 2; - } else if (input == 't') { - orders = 3; - } - - if(orders == 0) { - timeout(3000); - input = lcgetch(); - timeout(-1); - - if (input == 'f') - { - orders = 1; - } else if ((input == 'R') || (input == 'r')) { - orders = 2; - } else if ((input == 'T') || (input == 't')) { - orders = 3; - } else { - gotoxy(0, 3); - clrtoeol(); - cputs("Taipan, what shall we do??\r\n(f=fight, r=run, t=throw cargo)"); - timeout(-1); - while ((input != 'f') && - (input != 'r') && - (input != 't')) - { - input = lcgetch(); - } - gotoxy(0, 3); - clrtoeol(); - gotoxy(0, 4); - clrtoeol(); - if (input == 'f') { - orders = 1; - } else if (input == 'r') { - orders = 2; - } else { - orders = 3; - } - } - } - - fight_stats(num_ships, orders); - if((orders == 1) && (guns > 0)) { - static int targeted, sk; - sk = 0; - - ok = 3; - ik = 1; - gotoxy(0, 3); - clrtoeol(); - cputs("Aye, we'll fight 'em, Taipan."); - timeout(3000); - input = getch(); - timeout(-1); - - gotoxy(0, 3); - clrtoeol(); - cputs("We're firing on 'em, Taipan!"); - timeout(1000); - input = getch(); - timeout(-1); - - for(i = 1; i <= guns; i++) { - if ((ships_on_screen[0] == 0) && (ships_on_screen[1] == 0) && - (ships_on_screen[2] == 0) && (ships_on_screen[3] == 0) && - (ships_on_screen[4] == 0) && (ships_on_screen[5] == 0) && - (ships_on_screen[6] == 0) && (ships_on_screen[7] == 0) && - (ships_on_screen[8] == 0) && (ships_on_screen[9] == 0)) - { - static int j; - - for (j = 0; j <= 9; j++) { - if (num_ships > num_on_screen) { - if(ships_on_screen[j] == 0) { - ships_on_screen[j] = randclamp(ec) + 20; - draw_lorcha(j, 0, 0); - num_on_screen++; - } - } - } - } - - plus_or_space(num_ships > num_on_screen); - - gotoxy(0, 16); - cputc('\r'); - cputc('\n'); - - targeted = randi()%10; - while(ships_on_screen[targeted] == 0) { - targeted = randi()%10; - } - - draw_lorcha(targeted, 0, 0x80); - jsleep(5); - - draw_lorcha(targeted, 0, 0); - jsleep(5); - - draw_lorcha(targeted, 0, 0x80); - jsleep(5); - - draw_lorcha(targeted, 0, 0); - jsleep(5); - - ships_on_screen[targeted] -= randi()%30 + 10; - - if(ships_on_screen[targeted] <= 0) { - num_on_screen--; - num_ships--; - sk++; - ships_on_screen[targeted] = 0; - - sink_lorcha(targeted); - - plus_or_space(num_ships > num_on_screen); - /* - if(num_ships == num_on_screen) { - gotoxy(39, 7); - cputc(' '); - } - */ - - fight_stats(num_ships, orders); - } - - if(num_ships == 0) { - i += guns; - } else { - jsleep(10); - } - } - gotoxy(0, 3); - clrtoeol(); - if(sk > 0) { - cputs("Sunk "); - cprintulong(sk); - cputs(" of the buggers, Taipan!"); - } else { - cputs("Hit 'em, but didn't sink 'em, Taipan!"); - } - timeout(3000); - input = getch(); - timeout(-1); - - // if ((randi()%s0 > (num_ships * .6 / id)) && (num_ships > 2)) - if((randi()%s0 > ((num_ships / 2) / id)) && (num_ships > 2)) { - static int ran; - ran = randi()%(num_ships / 3 / id) + 1; - - num_ships -= ran; - fight_stats(num_ships, orders); - gotoxy(0, 3); - clrtoeol(); - cprintulong(ran); - cputs(" ran away, Taipan!"); - - if(num_ships <= 10) { - for(i = 9; i >= 0; i--) { - if ((num_on_screen > num_ships) && (ships_on_screen[i] > 0)) { - ships_on_screen[i] = 0; - num_on_screen--; - - draw_lorcha(i, 7, 0); - jsleep(5); - } - } - if(num_ships == num_on_screen) { - plus_or_space(0); - } - } - - gotoxy(0, 16); - - timeout(3000); - input = lcgetch(); - timeout(-1); - - if(input == 'f') { - orders = 1; - } else if(input == 'r') { - orders = 2; - } else if(input == 't') { - orders = 3; - } - } - } else if ((orders == 1) && (guns == 0)) { - gotoxy(0, 3); - clrtoeol(); - cputs("We have no guns, Taipan!!"); - timeout(3000); - input = getch(); - timeout(-1); - } else if (orders == 3) { - static int choice; - static long amount, total; - choice = 0; - amount = 0; - total = 0; - - gotoxy(0, 18); - cputs("You have the following on board, Taipan:"); - gotoxy(4, 19); - cputs(item[0]); - cputs(": "); - cprintulong(hold_[0]); - gotoxy(24, 19); - cputs(item[1]); - cputs(": "); - cprintulong(hold_[1]); - gotoxy(5, 20); - cputs(item[2]); - cputs(": "); - cprintulong(hold_[2]); - gotoxy(21, 20); - cputs(item[3]); - cputs(": "); - cprintulong(hold_[3]); - - gotoxy(0, 3); - clrtoeol(); - cputs("What shall I throw overboard, Taipan? "); - - /* TODO: this, buy(), sell() have common code */ - while ((choice != 'o') && - (choice != 's') && - (choice != 'a') && - (choice != 'g') && - (choice != '*')) - { - choice = lc(get_one()); - } - - if(choice == 'o') { - choice = 0; - } else if(choice == 's') { - choice = 1; - } else if(choice == 'a') { - choice = 2; - } else if(choice == 'g') { - choice = 3; - } else { - choice = 4; - } - - if(choice < 4) { - gotoxy(0, 3); - clrtoeol(); - cputs("How much, Taipan? "); - - amount = get_num(9); - if((hold_[choice] > 0) && ((amount == -1) || (amount > hold_[choice]))) - { - amount = hold_[choice]; - } - total = hold_[choice]; - } else { - total = hold_[0] + hold_[1] + hold_[2] + hold_[3]; - } - - if(total > 0) { - gotoxy(0, 3); - clrtoeol(); - cputs("Let's hope we lose 'em, Taipan!"); - if (choice < 4) { - hold_[choice] -= amount; - hold += amount; - ok += (amount / 10); - } else { - hold_[0] = 0; - hold_[1] = 0; - hold_[2] = 0; - hold_[3] = 0; - hold += total; - ok += (total / 10); - } - gotoxy(0, 18); - clrtobot(); - - timeout(3000); - input = getch(); - timeout(-1); - } else { - gotoxy(0, 3); - clrtoeol(); - cputs("There's nothing there, Taipan!"); - gotoxy(0, 18); - clrtobot(); - - timeout(3000); - input = getch(); - timeout(-1); - } - } - - if((orders == 2) || (orders == 3)) { - if(orders == 2) { - gotoxy(0, 3); - clrtoeol(); - cputs("Aye, we'll run, Taipan."); - timeout(3000); - input = getch(); - timeout(-1); - } - - ok += ik++; - if(randi()%ok > randi()%num_ships) { - flushinp(); - gotoxy(0, 3); - clrtoeol(); - cputs("We got away from 'em, Taipan!"); - timeout(3000); - input = getch(); - timeout(-1); - num_ships = 0; - } else { - gotoxy(0, 3); - clrtoeol(); - cputs("Couldn't lose 'em."); - timeout(3000); - input = getch(); - timeout(-1); - - if((num_ships > 2) && (randi()%5 == 0)) { - static int lost; - lost = (randi()%num_ships / 2) + 1; - - num_ships -= lost; - fight_stats(num_ships, orders); - gotoxy(0, 3); - clrtoeol(); - // printw("But we escaped from %d of 'em!", lost); - cputs("But we escaped from "); - cprintulong(lost); - cputs(" of 'em!"); - - if(num_ships <= 10) { - for(i = 9; i >= 0; i--) { - if((num_on_screen > num_ships) && (ships_on_screen[i] > 0)) { - ships_on_screen[i] = 0; - num_on_screen--; - - draw_lorcha(i, 7, 0); - jsleep(5); - } - } - plus_or_space(num_ships > num_on_screen); - /* - if(num_ships == num_on_screen) { - gotoxy(39, 7); - cputc(' '); - } - */ - } - - gotoxy(0, 16); - - timeout(3000); - input = lcgetch(); - timeout(-1); - - if(input == 'f') { - orders = 1; - } else if(input == 'r') { - orders = 2; - } else if(input == 't') { - orders = 3; - } - } - } - } - - if(num_ships > 0) { - gotoxy(0, 3); - clrtoeol(); - cputs("They're firing on us, Taipan!"); - - timeout(3000); - input = getch(); - timeout(-1); - flushinp(); - - for(i = 0; i < 3; i++) { - POKE(710, 0xcc); - jsleep(10); - POKE(710, 0xc0); - jsleep(10); - } - - fight_stats(num_ships, orders); - for(i = 0; i <= 9; i++) { - if(ships_on_screen[i] > 0) { - draw_lorcha(i, 0, 0); - } - } - - plus_or_space(num_ships > num_on_screen); - - gotoxy(0, 3); - clrtoeol(); - cputs("We've been hit, Taipan!!"); - - timeout(3000); - input = getch(); - timeout(-1); - - i = (num_ships > 15) ? 15 : num_ships; - - // is this really correct? - // if ((guns > 0) && ((randi()%100 < (((float) damage / capacity) * 100)) || - // ((((float) damage / capacity) * 100) > 80))) - - if((guns > 0) && ((randi()%100 < ((damage * 100) / capacity)) || - (((damage * 100) / capacity)) > 80)) - { - i = 1; - guns--; - hold += 10; - fight_stats(num_ships, orders); - gotoxy(0, 3); - clrtoeol(); - cputs("The buggers hit a gun, Taipan!!"); - fight_stats(num_ships, orders); - - timeout(3000); - input = getch(); - timeout(-1); - } - - // damage = damage + ((ed * i * id) * ((float) randi() / RAND_MAX)) + (i / 2); - // remember, ed is now scaled by 2 (used to be 0.5, now 1) - // damage = damage + randclamp((ed * i * id)/2) + (i / 2); - // damage = damage + randclamp((ed * i * id)/2) + (i / 2); // b0rked - - damage = damage + (randi() % ((ed * i * id)/2)) + (i / 2); - if(damage > capacity) damage = capacity; /* just in case */ - if((id == GENERIC) && (randi()%20 == 0)) { - return 2; - } - } - } - - if(orders == 1) { - clrscr(); - fight_stats(num_ships, orders); - gotoxy(0, 3); - clrtoeol(); - cputs("We got 'em all, Taipan!"); - timeout(3000); - getch(); - timeout(-1); - - return 1; - } else { - return 3; - } -} - -/* TODO: rewrite in asm */ -int get_one(void) -{ - int input, - choice = 0, - character = 0; - - // Atari cursor doesn't change visibility until a character - // is printed... can't use cputc() here as it escapes the - // character (prints graphics char instead of actually backspacing) - putchar(' '); - cursor(1); - putchar(BKSP); - - while ((input = getch()) != '\n') - { - if (((input == BKSP) || (input == 127)) && (character == 0)) - { - } else if ((input == BKSP) || (input == 127)) { - putchar(BKSP); - character--; - } else if (character >= 1) { - } else if (input == '\33') { - flushinp(); - } else { - putchar(input); - choice = input; - character++; - } - } - cursor(0); - - return choice; -} - -/* TODO: rewrite in asm. Maybe. */ -unsigned long get_num(int maxlen) -{ - /* see comment in fancy_numbers for why this is static */ - static char number[20]; - - int input, - character = 0; - - long amount; - - putchar(' '); - cursor(1); - putchar(BKSP); - while ((input = getch()) != '\n') - { - if (((input == BKSP) || (input == 127)) && (character == 0)) - { - } else if ((input == BKSP) || (input == 127)) { - putchar(BKSP); - number[character] = '\0'; - character--; - } else if (character >= maxlen) { - } else if (input == '\33') { - flushinp(); - } else if (((input == 'A') || (input == 'a')) && - (character == 0) && (maxlen > 1)) { - putchar(input); - number[character] = input; - character++; - } else if ((input < 48) || (input > 57)) { - } else { - putchar(input); - number[character] = input; - character++; - } - } - - number[character] = '\0'; - if ((strcmp(number, "A") == 0) || (strcmp(number, "a") == 0)) - { - amount = -1; - } else { - amount = strtol(number, (char **)NULL, 10); - } - - cursor(0); - return amount; -} - -/* TODO: rewrite in asm */ -void cash_or_guns(void) -{ - int choice = 0; - - clrscr(); - cputs("Do you want to start . . .\r\n\r\n"); - cputs(" 1) With cash (and a debt)\r\n\r\n"); - cputs(" >> or <<\r\n\r\n"); - cputs(" 2) With five guns and no cash\r\n"); - cputs(" (But no debt!)\r\n"); - - while ((choice != '1') && (choice != '2')) - { - gotoxy(10, 10); - cursor(1); - cputc('?'); - choice = get_one(); - cursor(0); - } - - cputc(choice); - if (choice == '1') - { - cash = 400; - debt = 5000; - hold = 60; - guns = 0; - li = 0; - bp = 10; - } else { - cash = 0; - debt = 0; - hold = 10; - guns = 5; - li = 1; - bp = 7; - } - - return; -} - -void set_prices(void) -{ - price[0] = base_price[0][port] / 2 * (randi()%3 + 1) * base_price[0][0]; - price[1] = base_price[1][port] / 2 * (randi()%3 + 1) * base_price[1][0]; - price[2] = base_price[2][port] / 2 * (randi()%3 + 1) * base_price[2][0]; - price[3] = base_price[3][port] / 2 * (randi()%3 + 1) * base_price[3][0]; - return; -} - - -void port_stats(void) -{ - int i, in_use, - status = 100 - ((damage * 100) / capacity); - - clrscr(); - gotox(12 - strlen(firm) / 2); /* TODO: store in global */ - cputs("Firm: "); - cputs(firm); - cputs(", Hong Kong"); - - show_damage(); - chlinexy(1, 1, 26); - chlinexy(1, 7, 26); - chlinexy(1, 13, 26); - cvlinexy(0, 2, 5); - cvlinexy(27, 2, 5); - cvlinexy(0, 8, 5); - cvlinexy(27, 8, 5); - chlinexy(0, 15, 40); - - cputcxy(0, 1, 17); // upper left corner - cputcxy(0, 7, 1); // |- - cputcxy(0, 13, 26); // lower left corner - - cputcxy(27, 1, 5); // upper right corner - cputcxy(27, 7, 4); // -| - cputcxy(27, 13, 3); // lower right corner - - cputsxy(1, 2, "Hong Kong Warehouse"); - cputsxy(4, 3, "Opium In use"); - cputsxy(4, 4, "Silk "); - in_use = hkw_[0] + hkw_[1] + hkw_[2] + hkw_[3]; - // printw("%d", in_use); - cprintulong(in_use); - cputsxy(4, 5, "Arms Vacant"); - cputsxy(4, 6, "General "); - // printw("%d", (10000 - in_use)); - cprintulong(10000 - in_use); - - gotoxy(12, 3); - // printw("%d", hkw_[0]); - cprintulong(hkw_[0]); - - gotoxy(12, 4); - // printw("%d", hkw_[1]); - cprintulong(hkw_[1]); - - gotoxy(12, 5); - // printw("%d", hkw_[2]); - cprintulong(hkw_[2]); - - gotoxy(12, 6); - // printw("%d", hkw_[3]); - cprintulong(hkw_[3]); - - cputsxy(1, 8, "Hold "); - if (hold >= 0) - { - // printw("%d", hold); - cprintulong(hold); - } else { - revers(1); - cputs("Overload"); - revers(0); - } - - cputsxy(16, 8, "Guns "); - // printw("%d", guns); - cprintulong(guns); - - cputsxy(4, 9, "Opium "); - // printw("%d", hold_[0]); - cprintulong(hold_[0]); - - cputsxy(4, 10, "Silk "); - // printw("%d", hold_[1]); - cprintulong(hold_[1]); - - cputsxy(4, 11, "Arms "); - // printw("%d", hold_[2]); - cprintulong(hold_[2]); - - cputsxy(4, 12, "General "); - // printw("%d", hold_[3]); - cprintulong(hold_[3]); - - cputsxy(32, 2, "Date"); - cputsxy(29, 3, "15 "); - revers(1); - cputs(months[month - 1]); - revers(0); - cputc(' '); - // printw(" %d", year); - cprintulong(year); - - cputsxy(30, 5, "Location"); - revers(1); - cputsxy(30, 6, location[port]); - revers(0); - - cputsxy(32, 8, "Debt"); - revers(1); - gotoxy(33, 9); - fancy_numbers(debt, fancy_num); - cputs(fancy_num); - revers(0); - - cputsxy(29, 11, "Ship Status"); - gotoxy(29, 12); - i = status / 20; - if (i < 2) { - revers(1); - } - cputs(st[i]); - cputc(':'); - cprintulong(status); - revers(0); - - cputsxy(0, 14, "Cash: "); - fancy_numbers(cash, fancy_num); - cputs(fancy_num); - cputsxy(20, 14, "Bank: "); - fancy_numbers(bank, fancy_num); - cputs(fancy_num); -} - -/* Unlike the Linux port, splash_intro() doesn't have to draw - the "programmed by" etc. text in the intro screen, as it's - preloaded into screen memory as a xex segment... but it - means splash_intro() can *only* be called by main(), once, - at program startup. So we don't get a 2nd splash intro - when answering Y to 'Play Again?". - Ideally, I should redefine enough graphics characters - that I can draw a nice ATASCII trading ship. But for - now I'll just draw 6 of the lorchas. - */ -void splash_intro(void) -{ - int i; - - for(i=0; i<3; i++) draw_lorcha(i, 0, 0); - for(i=5; i<8; i++) draw_lorcha(i, 0, 0); - - while(!kbhit()) - ; - - // for(i=0; i<3; i++) sink_lorcha(i); - // for(i=5; i<8; i++) sink_lorcha(i); - - flushinp(); - return; -} - -void mchenry(void) -{ - int choice = 0; - - compradores_report(); - cputs("Taipan, Mc Henry from the Hong Kong\r\n"); - cputs("Shipyards has arrived!! He says, \"I see\r\n"); - cputs("ye've a wee bit of damage to yer ship.\r\n"); - cputs("Will ye be wanting repairs? "); - - while ((choice != 'Y') && (choice != 'y') && - (choice != 'N') && (choice != 'n')) - { - choice = get_one(); - } - - if ((choice == 'Y') || (choice == 'y')) - { - // int percent = ((float) damage / capacity) * 100, - // this is likely wrong: - int percent = ((damage * 100) / (capacity * 100)) * 100, - time = ((year - 1860) * 12) + month; - - /* - long br = ((((60 * (time + 3) / 4) * (float) randi() / RAND_MAX) + - 25 * (time + 3) / 4) * capacity / 50), - repair_price = (br * damage) + 1, - amount; - */ - - long br, repair_price, amount; - br = ((randclamp(60 * (time + 3) / 4) + 25 * (time + 3) / 4) * capacity / 50); - repair_price = (br * damage) + 1; - - gotoxy(0, 18); - clrtobot(); - cputs("Och, 'tis a pity to be "); - cprintulong(percent); - cputs("% damaged.\r\nWe can fix yer whole ship for "); - cprintulong(repair_price); - cputs(",\r\nor make partial repairs if you wish.\r\n"); - cputs("How much will ye spend? "); - - for (;;) { - gotoxy(24, 21); - amount = get_num(9); - if(amount == -1) { - if(repair_price > cash) - amount = repair_price; - else - amount = cash; - } - if(amount <= cash) { - cash -= amount; - // damage -= (int)((amount / br) + .5); - damage -= (int)(amount / br); - damage = (damage < 0) ? 0 : damage; - port_stats(); - break; - } - } - } - - return; -} - -void retire(void) -{ - compradores_report(); - revers(1); - cputs(" \r\n"); - cputs(" Y o u ' r e a \r\n"); - cputs(" \r\n"); - cputs(" M I L L I O N A I R E ! \r\n"); - cputs(" \r\n"); - revers(1); - timeout(5000); - getch(); - timeout(-1); - - final_stats(); -} - -void final_stats(void) -{ - int years = year - 1860, - time = ((year - 1860) * 12) + month, - choice = 0; - - clrscr(); - cputs("Your final status:\r\n\r\n"); - cash = cash + bank - debt; - fancy_numbers(cash, fancy_num); - cputs("Net cash: "); - cputs(fancy_num); - cputs("\r\nShip size: "); - cprintulong(capacity); - cputs(" units with "); - cprintulong(guns); - cputs(" guns\r\n\r\n"); - cputs("You traded for "); - cprintulong(years); - cputs(" year"); - if (years != 1) - { - cputc('s'); - } - cputs(" and "); - cprintulong(month); - cputs(" month"); - if (month > 1) - { - cputc('s'); - } - cputs("\r\n\r\n"); - cash = cash / 100 / time; - revers(1); - cputs("Your score is "); - cprintulong(cash); - cputs(".\r\n"); - revers(0); - if ((cash < 100) && (cash >= 0)) - { - cputs("Have you considered a land based job?\r\n\r\n\r\n"); - } else if (cash < 0) { - cputs("The crew has requested that you stay on\r\n"); - cputs("shore for their safety!!\r\n\r\n"); - } else { - cputs("\r\n\r\n\r\n"); - } - cputs("Your Rating:\r\n"); - cputs(" _______________________________\r\n"); - cputs("|"); - if (cash > 49999) - { - revers(1); - } - cputs("Ma Tsu"); - revers(0); - cputs(" 50,000 and over |\r\n"); - cputs("|"); - if ((cash < 50000) && (cash > 7999)) - { - revers(1); - } - cputs("Master Taipan"); - revers(0); - cputs(" 8,000 to 49,999|\r\n"); - cputs("|"); - if ((cash < 8000) && (cash > 999)) - { - revers(1); - } - cputs("Taipan"); - revers(0); - cputs(" 1,000 to 7,999|\r\n"); - cputs("|"); - if ((cash < 1000) && (cash > 499)) - { - revers(1); - } - cputs("Compradore"); - revers(0); - cputs(" 500 to 999|\r\n"); - cputs("|"); - if (cash < 500) - { - revers(1); - } - cputs("Galley Hand"); - revers(0); - cputs(" less than 500|\r\n"); - cputs("|_______________________________|\r\n\r\n"); - - while ((choice != 'Y') && (choice != 'y') && - (choice != 'N') && (choice != 'n')) - { - gotoxy(0, 22); - cputs("Play again? "); - choice = get_one(); - } - - if ((choice == 'Y') || (choice == 'y')) - { - bank = 0; - hkw_[0] = 0; - hkw_[1] = 0; - hkw_[3] = 0; - hkw_[4] = 0; - hold_[0] = 0; - hold_[1] = 0; - hold_[2] = 0; - hold_[3] = 0; - hold = 0; - capacity = 60; - damage = 0; - month = 1; - year = 1860; - port = 1; - - // splash_intro(); - name_firm(); - cash_or_guns(); - set_prices(); - - return; - } - - clrscr(); - - exit(0); -} - -void transfer(void) -{ - int i, in_use; - - long amount = 0; - - if ((hkw_[0] == 0) && (hold_[0] == 0) && - (hkw_[1] == 0) && (hold_[1] == 0) && - (hkw_[2] == 0) && (hold_[2] == 0) && - (hkw_[3] == 0) && (hold_[3] == 0)) - { - gotoxy(0, 22); - clrtobot(); - cputs("You have no cargo, Taipan.\r\n"); - - timeout(5000); - getch(); - timeout(-1); - return; - } - - for (i = 0; i < 4; i++) - { - if (hold_[i] > 0) - { - for (;;) - { - compradores_report(); - cputs("How much "); - cputs(item[i]); - cputs(" shall I move\r\nto the warehouse, Taipan? "); - - amount = get_num(9); - if (amount == -1) - { - amount = hold_[i]; - } - if (amount <= hold_[i]) - { - in_use = hkw_[0] + hkw_[1] + hkw_[2] + hkw_[3]; - if ((in_use + amount) <= 10000) - { - hold_[i] -= amount; - hkw_[i] += amount; - hold += amount; - break; - } else if (in_use == 10000) { - gotoxy(0, 21); - cputs("Your warehouse is full, Taipan!"); - } else { - gotoxy(0, 21); - cputs("Your warehouse will only hold an\r\nadditional "); - cprintulong(10000 - in_use); - cputs("%d, Taipan!"); - - timeout(5000); - getch(); - timeout(-1); - } - } else { - gotoxy(0, 18); - clrtobot(); - cputs("You have only "); - cprintulong(hold_[i]); - cputs(", Taipan.\r\n"); - - timeout(5000); - getch(); - timeout(-1); - } - } - port_stats(); - } - - if (hkw_[i] > 0) - { - for (;;) - { - compradores_report(); - cputs("How much "); - cputs(item[i]); - cputs("shall I move\r\naboard ship, Taipan? "); - - amount = get_num(9); - if (amount == -1) - { - amount = hkw_[i]; - } - if (amount <= hkw_[i]) - { - hold_[i] += amount; - hkw_[i] -= amount; - hold -= amount; - break; - } else { - gotoxy(0, 18); - clrtobot(); - cputs("You have only "); - cprintulong(hkw_[i]); - cputs(", Taipan.\r\n"); - - timeout(5000); - getch(); - timeout(-1); - } - } - port_stats(); - } - } - - return; -} - -void quit(void) -{ - int choice = 0, - result = 0; - - compradores_report(); - cputs("Taipan, do you wish me to go to:\r\n"); - cputs("1) Hong Kong, 2) Shanghai, 3) Nagasaki,\r\n"); - cputs("4) Saigon, 5) Manila, 6) Singapore, or\r\n"); - cputs("7) Batavia ? "); - - for (;;) - { - gotoxy(13, 21); - clrtobot(); - - choice = get_num(1); - - if (choice == port) - { - cputs("\r\n\nYou're already here, Taipan."); - timeout(5000); - getch(); - timeout(-1); - } else if ((choice >= 1) && (choice <= 7)) { - port = choice; - break; - } - } - - at_sea(); - captains_report(); - -#ifdef COMBAT_TEST - if(1) -#else - if (randi()%bp == 0) -#endif - { - int num_ships = randi()%((capacity / 10) + guns) + 1; - - if (num_ships > 9999) - { - num_ships = 9999; - } - cprintulong(num_ships); - cputs(" hostile ships approaching, Taipan!\r\n"); - - timeout(3000); - getch(); - timeout(-1); - - result = sea_battle(GENERIC, num_ships); - } - - if (result == 2) - { - port_stats(); - at_sea(); - - captains_report(); - cputs("Li Yuen's fleet drove them off!"); - - timeout(3000); - getch(); - timeout(-1); - } - - if (((result == 0) && (randi()%(4 + (8 * li))) == 0) || (result == 2)) - { - gotoxy(0, 18); - clrtobot(); - cputs("Li Yuen's pirates, Taipan!!\r\n\n"); - - timeout(3000); - getch(); - timeout(-1); - - if (li > 0) - { - cputs("Good joss!! They let us be!!\r\n"); - - timeout(3000); - getch(); - timeout(-1); - - return; - } else { - int num_ships = randi()%((capacity / 5) + guns) + 5; - - cprintulong(num_ships); - cputs("ships of Li Yuen's pirate\r\n"); - cputs("fleet, Taipan!!\r\n"); - - timeout(3000); - getch(); - timeout(-1); - - sea_battle(LI_YUEN, num_ships); - } - } - - if (result > 0) - { - port_stats(); - at_sea(); - - captains_report(); - if (result == 1) - { - fancy_numbers(booty, fancy_num); - cputs("We captured some booty.\r\n"); - cputs("It's worth "); - cputs(fancy_num); - cputc('!'); - cash += booty; - } else if (result == 3) { - cputs("We made it!"); - } else { - cputs("The buggers got us, Taipan!!!\r\n"); - cputs("It's all over, now!!!"); - - timeout(5000); - getch(); - timeout(-1); - - final_stats(); - return; - } - - timeout(3000); - getch(); - timeout(-1); - } - - if (randi()%10 == 0) - { - gotoxy(0, 18); - clrtobot(); - cputs("Storm, Taipan!!\r\n\n"); - timeout(3000); - getch(); - timeout(-1); - - if (randi()%30 == 0) - { - cputs(" I think we're going down!!\r\n\n"); - timeout(3000); - getch(); - timeout(-1); - - // if (((damage / capacity * 3) * ((float) randi() / RAND_MAX)) >= 1) - if(randclamp(damage / capacity * 3) >= 1) - { - cputs("We're going down, Taipan!!\r\n"); - timeout(5000); - getch(); - timeout(-1); - - final_stats(); - } - } - - cputs(" We made it!!\r\n\n"); - timeout(3000); - getch(); - timeout(-1); - - if (randi()%3 == 0) - { - int orig = port; - - while (port == orig) - { - port = randi()%7 + 1; - } - - gotoxy(0, 18); - clrtobot(); - cputs("We've been blown off course\r\nto "); - cputs(location[port]); - timeout(3000); - getch(); - timeout(-1); - } - } - - month++; - if (month == 13) - { - month = 1; - year++; - ec += 10; - ed += 1; - } - - /* debt calculation original formula was: - - debt = debt + (debt * .1); - - int-based formula is the same, except it would never - increase if debt is <= 10, so we fudge it with debt++ - in that case. Which means small debts accrue interest - *much* faster, but that shouldn't affect gameplay much. - There needs to be some overflow detection though... or - maybe we let the overflow through, and the player can - think of it as Wu forgiving the debt after enough years - go by (or, he lost the paperwork?). Most likely though, - the player gets his throat cut long before the amount - overflows. - - */ - - if(debt) { - if(debt > 10) - debt += (debt / 10); - else - debt++; - } - - /* bank calculation original formula was: - bank = bank + (bank * .005); - int-based formula is the same, except when bank <= 200, - it's linear. - */ - if(bank) { - if(bank > 200) - bank += (bank / 200); - else - bank++; - } - - set_prices(); - - gotoxy(0, 18); - clrtobot(); - cputs("Arriving at "); - cputs(location[port]); - cputs("..."); - timeout(3000); - getch(); - timeout(-1); - - return; -} - -void li_yuen_extortion(void) -{ - int time = ((year - 1860) * 12) + month, - choice = 0; - - /* - float i = 1.8, - j = 0, - amount = 0; - */ - unsigned long amount = 0; - unsigned int i = 2, j = 0; - - if (time > 12) - { - j = randi()%(1000 * time) + (1000 * time); - i = 1; - } - - // amount = ((cash / i) * ((float) randi() / RAND_MAX)) + j; - amount = randclamp((cash >> (i - 1))) + j; - - fancy_numbers(amount, fancy_num); - - compradores_report(); - cputs("Li Yuen asks "); - cputs(fancy_num); - cputs(" in donation\r\nto the temple of Tin Hau, the Sea\r\n"); - - while ((choice != 'Y') && (choice != 'y') && - (choice != 'N') && (choice != 'n')) - { - gotoxy(0, 20); - cputs("Goddess. Will you pay? "); - choice = get_one(); - } - - if ((choice == 'Y') || (choice == 'y')) - { - if (amount <= cash) - { - cash -= amount; - li = 1; - } else { - gotoxy(0, 18); - clrtobot(); - cputs("Taipan, you do not have enough cash!!\r\n\r\n"); - - timeout(3000); - getch(); - timeout(-1); - - cputs("Do you want Elder Brother Wu to make up\r\n"); - cputs("the difference for you? "); - choice = 0; - while ((choice != 'Y') && (choice != 'y') && - (choice != 'N') && (choice != 'n')) - { - choice = get_one(); - } - - if ((choice == 'Y') || (choice == 'y')) - { - amount -= cash; - debt += amount; - cash = 0; - li = 1; - - gotoxy(0, 18); - clrtobot(); - cputs("Elder Brother has given Li Yuen the\r\n"); - cputs("difference between what he wanted and\r\n"); - cputs("your cash on hand and added the same\r\n"); - cputs("amount to your debt.\r\n"); - - timeout(5000); - getch(); - timeout(-1); - } else { - cash = 0; - - cputs("Very well. Elder Brother Wu will not pay\r\n"); - cputs("Li Yuen the difference. I would be very\r\n"); - cputs("wary of pirates if I were you, Taipan.\r\n"); - - timeout(5000); - getch(); - timeout(-1); - } - } - } - - port_stats(); - - return; -} - -void elder_brother_wu(void) -{ - int choice = 0; - - long wu = 0; - - compradores_report(); - cputs("Do you have business with Elder Brother\r\n"); - cputs("Wu, the moneylender? "); - - for (;;) - { - gotoxy(21, 19); - - choice = get_one(); - if ((choice == 'N') || (choice == 'n') || choice == 0) - { - break; - } else if ((choice == 'Y') || (choice == 'y')) { - if (((int)cash == 0) && ((int)bank == 0) && (guns == 0) && - (hold_[0] == 0) && (hkw_[0] == 0) && - (hold_[1] == 0) && (hkw_[1] == 0) && - (hold_[2] == 0) && (hkw_[2] == 0) && - (hold_[3] == 0) && (hkw_[3] == 0)) - { - int i = randi()%1500 + 500, - j; - - wu_bailout++; - j = randi()%2000 * wu_bailout + 1500; - - for (;;) - { - compradores_report(); - cputs("Elder Brother is aware of your plight,\r\n"); - cputs("Taipan. He is willing to loan you an\r\n"); - cputs("additional "); - cprintulong(i); - cputs(" if you will pay back\r\n"); - cprintulong(j); - cputs(". Are you willing, Taipan? "); - - choice = get_one(); - if ((choice == 'N') || (choice == 'n')) - { - compradores_report(); - cputs("Very well, Taipan, the game is over!\r\n"); - - timeout(5000); - getch(); - timeout(-1); - - final_stats(); - } else if ((choice == 'Y') || (choice == 'y')) { - cash += i; - debt += j; - port_stats(); - - compradores_report(); - cputs("Very well, Taipan. Good joss!!\r\n"); - - timeout(5000); - getch(); - timeout(-1); - - return; - } - } - } else if ((cash > 0) && (debt != 0)) { - for (;;) - { - compradores_report(); - cputs("How much do you wish to repay\r\n"); - cputs("him? "); - - wu = get_num(9); - if (wu == -1) - { - wu = cash; - } - if (wu <= cash) - { - if(wu > debt) wu = debt; - cash -= wu; - debt -= wu; - /* // currently debt is unsigned so the negative debt - // bug (or feature) is unimplemented. - if ((wu > debt) && (debt > 0)) - { - debt -= (wu + 1); - } else { - debt -= wu; - } - */ - break; - } else { - gotoxy(0, 18); - clrtobot(); - fancy_numbers(cash, fancy_num); - cputs("Taipan, you only have "); - cputs(fancy_num); - cputs("\r\nin cash.\r\n"); - - timeout(5000); - getch(); - timeout(-1); - } - } - } - port_stats(); - - for (;;) - { - compradores_report(); - cputs("How much do you wish to\r\n"); - cputs("borrow? "); - - wu = get_num(9); - if (wu == -1) - { - wu = (cash * 2); - } - if (wu <= (cash * 2)) - { - cash += wu; - debt += wu; - break; - } else { - cputs("\r\n\r\nHe won't loan you so much, Taipan!"); - - timeout(5000); - getch(); - timeout(-1); - } - } - port_stats(); - - break; - } - } - - if ((debt > 20000) && (cash > 0) && (randi()%5 == 0)) - { - int num = randi()%3 + 1; - - cash = 0; - port_stats(); - - compradores_report(); - cputs("Bad joss!!\r\n"); - cprintulong(num); - cputs(" of your bodyguards have been killed\r\n"); - cputs("by cutthroats and you have been robbed\r\n"); - cputs("of all of your cash, Taipan!!\r\n"); - - timeout(5000); - getch(); - timeout(-1); - } - - return; -} - -void good_prices(void) -{ - /* see comment in fancy_numbers for why this is static */ - static char item[14]; - - int i = randi()%4, - j = randi()%2; - - if (i == 0) - { - strcpy(item, "Opium"); - } else if (i == 1) { - strcpy(item, "Silk"); - } else if (i == 2) { - strcpy(item, "Arms"); - } else { - strcpy(item, "General Cargo"); - } - - compradores_report(); - cputs("Taipan!! The price of "); - cputs(item); - cputs("\r\n has "); - if (j == 0) - { - price[i] = price[i] / 5; - cputs("dropped"); - } else { - price[i] = price[i] * (randi()%5 + 5); - cputs("risen"); - } - cputs(" to "); - cprintulong(price[i]); - cputs("!!\r\n"); - - timeout(3000); - getch(); - timeout(-1); -} - -int port_choices(void) -{ - int choice = 0; - - compradores_report(); - cputs("Taipan, present prices per unit here are"); /* NB: exactly 40 cols */ - cputs(" Opium: Silk:\r\n"); - cputs(" Arms: General:\r\n"); - gotoxy(11, 19); - cprintulong(price[0]); - gotoxy(29, 19); - cprintulong(price[1]); - gotoxy(11, 20); - cprintulong(price[2]); - gotoxy(29, 20); - cprintulong(price[3]); - - for (;;) - { - gotoxy(0, 22); - clrtobot(); - - cursor(1); - if (port == 1) - { - if ((cash + bank) >= 1000000) - { - cputs("Shall I Buy, Sell, Visit bank, Transfer\r\n"); - cputs("cargo, Quit trading, or Retire? "); - - choice = cgetc(); - if ((choice == 'B') || (choice == 'b') || - (choice == 'S') || (choice == 's') || - (choice == 'V') || (choice == 'v') || - (choice == 'T') || (choice == 't') || - (choice == 'Q') || (choice == 'q') || - (choice == 'R') || (choice == 'r')) - { - break; - } - } else { - cputs("Shall I Buy, Sell, Visit bank, Transfer\r\n"); - cputs("cargo, or Quit trading? "); - - choice = cgetc(); - if ((choice == 'B') || (choice == 'b') || - (choice == 'S') || (choice == 's') || - (choice == 'V') || (choice == 'v') || - (choice == 'T') || (choice == 't') || - (choice == 'Q') || (choice == 'q')) - { - break; - } - } - } else { - cputs("Shall I Buy, Sell, or Quit trading? "); - - choice = cgetc(); - if ((choice == 'B') || (choice == 'b') || - (choice == 'S') || (choice == 's') || - (choice == 'Q') || (choice == 'q')) - { - break; - } - } - cursor(1); - } - - return choice; -} - - -/* TODO: rewrite in asm, or at least better C */ -void name_firm(void) -{ - int input, - character = 0; - - clrscr(); - - chlinexy(1, 7, 38); - chlinexy(1, 16, 38); - cvlinexy(0, 8, 8); - cvlinexy(39, 8, 8); - cputcxy(0, 7, 17); // upper left corner - cputcxy(0, 16, 26); // lower left corner - cputcxy(39, 7, 5); // upper right corner - cputcxy(39, 16, 3); // lower right corner - - gotoxy(6, 9); - cputs("Taipan,"); - gotoxy(2, 11); - cputs("What will you name your"); - gotoxy(6, 13); - cursor(1); - cputs("Firm:"); - chlinexy(12, 14, 22); - - gotoxy(12, 13); - - while (((input = getch()) != ENTER) && (character < 22)) - { - if (((input == BKSP) || (input == 127)) && (character == 0)) - { - // nop - } else if ((input == BKSP) || (input == 127)) { - gotox(12 + character - 1); - cputc(' '); - gotox(12 + character - 1); - firm[character] = '\0'; - character--; - } else if (input == '\33') { - flushinp(); - } else { - cputc(input); - firm[character] = input; - character++; - } - } - - cursor(0); - firm[character] = '\0'; - - return; -} - -void buy(void) -{ - /* see comment in fancy_numbers for why this is static */ - // static char space[5]; - - int choice = 0; - - long afford, - amount; - - for (;;) - { - gotoxy(0, 22); - clrtobot(); - - cputs("What do you wish me to buy, Taipan? "); - - /* TODO: buy() sell() and throwing cargo, common code in get_item() */ - choice = lc(get_one()); - if(choice == 'o') { - choice = 0; - break; - } else if (choice == 's') { - choice = 1; - break; - } else if (choice == 'a') { - choice = 2; - break; - } else if (choice == 'g') { - choice = 3; - break; - } - } - - for (;;) - { - gotoxy(31, 21); - clrtobot(); - - afford = cash / price[choice]; - revers(1); - cputs(" You can "); - revers(0); - gotoxy(0, 22); - cputs("How much "); - cputs(item[choice]); - cputs(" shall"); - gotoxy(31, 22); - revers(1); - cputs(" afford "); - gotoxy(31, 23); - cputs(" "); - gotoxy(31, 23); - - /* TODO: is this really right? */ - if(afford < 100) cputc(' '); - if(afford < 10000) cputc(' '); - if(afford < 1000000) cputc(' '); - if(afford < 100000000) cputc(' '); - - /* - if (afford < 100) - { - strcpy(space, " "); - } else if (afford < 10000) { - strcpy(space, " "); - } else if (afford < 1000000) { - strcpy(space, " "); - } else if (afford < 100000000) { - strcpy(space, " "); - } else { - strcpy(space, ""); - } - cputs(space); - */ - - cprintulong(afford); - revers(0); - - gotoxy(0, 23); - cputs("I buy, Taipan: "); - - amount = get_num(9); - if(amount == -1) { - amount = afford; - } - if(amount <= afford) { - break; - } - } - - cash -= (amount * price[choice]); - hold_[choice] += amount; - hold -= amount; - - return; -} - -void sell(void) { - int choice = 0; - - long amount; - - for (;;) - { - gotoxy(0, 22); - clrtobot(); - - cputs("What do you wish me to sell, Taipan? "); - - /* TODO: buy() sell() and throwing cargo, common code in get_item() */ - choice = lc(get_one()); - if(choice == 'o') { - choice = 0; - break; - } else if(choice == 's') { - choice = 1; - break; - } else if(choice == 'a') { - choice = 2; - break; - } else if(choice == 'g') { - choice = 3; - break; - } - } - - for (;;) - { - gotoxy(0, 22); - clrtobot(); - - cputs("How much "); - cputs(item[choice]); - cputs(" shall\r\n"); - cputs("I sell, Taipan: "); - - amount = get_num(9); - - if (amount == -1) - { - amount = hold_[choice]; - } - if (hold_[choice] >= amount) - { - hold_[choice] -= amount; - break; - } - } - - cash += (amount * price[choice]); - hold += amount; - - return; -} - -void visit_bank(void) -{ - long amount = 0; - - for (;;) - { - compradores_report(); - cputs("How much will you deposit? "); - - amount = get_num(9); - if (amount == -1) - { - amount = cash; - } - if (amount <= cash) - { - cash -= amount; - bank += amount; - break; - } else { - gotoxy(0, 18); - clrtobot(); - fancy_numbers(cash, fancy_num); - cputs("Taipan, you only have "); - cputs(fancy_num); - cputs("\r\nin cash.\r\n"); - - timeout(5000); - getch(); - timeout(-1); - } - } - port_stats(); - - for (;;) - { - compradores_report(); - cputs("How much will you withdraw? "); - - amount = get_num(9); - if (amount == -1) - { - amount = bank; - } - if (amount <= bank) - { - cash += amount; - bank -= amount; - break; - } else { - fancy_numbers(cash, fancy_num); - cputs("Taipan, you only have "); - cputs(fancy_num); - cputs("\r\nin the bank."); - - timeout(5000); - getch(); - timeout(-1); - } - } - port_stats(); - - return; -} - -/* -void debttest() { - int i; - - debt = 1; - - for(i=0; i<20; i++) { - debt = debt + (debt >> 8) + (debt >> 10); - // debt = debt + (debt >> 4) + (debt >> 5) + (debt >> 7) - (debt >> 9); // debt *= 0.09961 - printf("%lu\n", debt); - } - - cgetc(); - clrscr(); - debt = 1; - - for(i=0; i<20; i++) { - if(debt > 200) - debt += (debt / 200); - else - debt++; - - printf("%lu\n", debt); - } - cgetc(); - -} -*/ - -/* N.B. cc65 is perfectly OK with main(void), and it avoids - warnings about argv/argc unused. */ -int main(void) { - int choice; - - /* - { - int status; - capacity = 60; - damage = 47; - status = 100 - ((damage * 100) / capacity); - cprintulong(status); -hang: goto hang; - } - */ - - /* - _randomize(); - while(1) { - clrscr(); - cprintulong(randi()); - cputs("\r\n"); - cprintulong(rand()); - cgetc(); - } - */ - - // fancytest(); - // debttest(); - atari_text_setup(); - initrand(); - splash_intro(); - name_firm(); - cash_or_guns(); - set_prices(); - - for (;;) - { - choice = 0; - - port_stats(); - - if ((port == 1) && (li == 0) && (cash > 0)) - { - li_yuen_extortion(); - } - - if ((port == 1) && (damage > 0)) - { - mchenry(); - } - - if ((port == 1) && (debt >= 10000) && (wu_warn == 0)) - { - int braves = randi()%100 + 50; - - compradores_report(); - cputs("Elder Brother Wu has sent "); - cprintulong(braves); - cputs(" braves\r\n"); - cputs("to escort you to the Wu mansion, Taipan.\r\n"); - - timeout(3000); - getch(); - timeout(-1); - - gotoxy(0, 18); - clrtobot(); - cputs("Elder Brother Wu reminds you of the\r\n"); - cputs("Confucian ideal of personal worthiness,\r\n"); - cputs("and how this applies to paying one's\r\n"); - cputs("debts.\r\n"); - - timeout(3000); - getch(); - timeout(-1); - - gotoxy(0, 18); - clrtobot(); - cputs("He is reminded of a fabled barbarian\r\n"); - cputs("who came to a bad end, after not caring\r\n"); - cputs("for his obligations.\r\n\r\n"); - cputs("He hopes no such fate awaits you, his\r\n"); - cputs("friend, Taipan.\r\n"); - - timeout(5000); - getch(); - timeout(-1); - - wu_warn = 1; - } - - if (port == 1) - { - elder_brother_wu(); - } - - if (randi()%4 == 0) - { - if (randi()%2 == 0) - { - new_ship(); - } else if (guns < 1000) { - new_gun(); - } - } - - if ((port != 1) && (randi()%18 == 0) && (hold_[0] > 0)) - { - // float fine = ((cash / 1.8) * ((float) randi() / RAND_MAX)) + 1; - // the 1.8 is now a 2 - unsigned long fine = randclamp(cash >> 1) + 1; - - hold += hold_[0]; - hold_[0] = 0; - cash -= fine; - - port_stats(); - - fancy_numbers(fine, fancy_num); - compradores_report(); - cputs("Bad Joss!!\r\n"); - cputs("The local authorities have seized your\r\n"); - cputs("Opium cargo and have also fined you\r\n"); - cputs(fancy_num); - cputs(", Taipan!\r\n"); - - timeout(5000); - getch(); - timeout(-1); - } - - if ((randi()%50 == 0) && - ((hkw_[0] + hkw_[1] + hkw_[2] + hkw_[3]) > 0)) - { - int i; - - for (i = 0; i < 4; i++) - { - // hkw_[i] = ((hkw_[i] / 1.8) * ((float) randi() / RAND_MAX)); - // the 1.8 is now a 2 - hkw_[i] = randclamp(hkw_[i] >> 1); - } - - port_stats(); - - compradores_report(); - cputs("Messenger reports large theft\r\n"); - cputs("from warehouse, Taipan.\r\n"); - - timeout(5000); - getch(); - timeout(-1); - } - - if (randi()%20 == 0) - { - if (li > 0) { li++; } - if (li == 4) { li = 0; } - } - - if ((port != 1) && (li == 0) && (randi()%4 != 0)) - { - compradores_report(); - cputs("Li Yuen has sent a Lieutenant,\r\n"); - cputs("Taipan. He says his admiral wishes\r\n"); - cputs("to see you in Hong Kong, posthaste!\r\n"); - - timeout(3000); - getch(); - timeout(-1); - } - - if (randi()%9 == 0) - { - good_prices(); - } - - if ((cash > 25000) && (randi()%20 == 0)) - { - // float robbed = ((cash / 1.4) * ((float) randi() / RAND_MAX)); - // line below changes the 1.4 to 1.5 - unsigned long robbed = randclamp((cash >> 2) + (cash >> 1)); - - cash -= robbed; - port_stats(); - - fancy_numbers(robbed, fancy_num); - compradores_report(); - cputs("Bad Joss!!\r\n"); - cputs("You've been beaten up and\r\n"); - cputs("robbed of "); - cputs(fancy_num); - cputs(" in cash, Taipan!!\r\n"); - - timeout(5000); - getch(); - timeout(-1); - } - - for (;;) - { - while ((choice != 'Q') && (choice != 'q')) - { - switch (choice = port_choices()) - { - case 'B': - case 'b': - buy(); - break; - - case 'S': - case 's': - sell(); - break; - - case 'V': - case 'v': - visit_bank(); - break; - - case 'T': - case 't': - transfer(); - break; - - case 'R': - case 'r': - retire(); - } - - port_stats(); - } - - choice = 0; - if (hold >= 0) - { - quit(); - break; - } else { - overload(); - } - } - } - - cgetc(); - POKE(709, 0); -hangmain: - goto hangmain; - return 0; -} diff --git a/titlecomp.pl b/titlecomp.pl index 94d36b1..c8da677 100644 --- a/titlecomp.pl +++ b/titlecomp.pl @@ -38,6 +38,9 @@ for(split //, $data . chr(0xff)) { if($_ eq "\0") { if($run) { $run++; + if($run == 256) { + die "can't handle runs >= 256, sorry\n"; + } } else { $run = 1; } diff --git a/titlecompression.txt b/titlecompression.txt index ecbf9df..5f6bf3f 100644 --- a/titlecompression.txt +++ b/titlecompression.txt @@ -1,5 +1,26 @@ The title screen uses a crude form of compression, which I'll call ZRLE -(zero-run length encoding). +(zero-run length encoding). It's a special-purpose compression scheme +I came up with, to meet the following requirements: + +- Must be able to compress the Taipan title screen by at least 33% + (66% compression ratio, where the decompress code is counted as part + of the compressed file size). + +- Must be able to decompress in less than 250 bytes of 6502 asm + code (2 single-density sectors on disk). + +- Must decompress title screen in less than 1/4 sec on the Atari. + +All 3 requirements are exceeded slightly: the screen is compressed at a +60% ratio, the decompressor is around 160 bytes of object code, and it +runs in approximately 1/5 of a second. + +Things that are NOT requirements: it doesn't need to compress anything +else well, just the title screen. It turns out that it works pretty well +for bitmapped graphics like the Atari uses, or any kind of file that's +likely to have large areas of all 0 bytes, but e.g. it can't compress an +ASCII or ATASCII text file at all (because there are pretty much never +any null bytes in a human-readable text file). Theory of operation: -------------------- @@ -11,7 +32,11 @@ won't work on arbitrary input data. Why? ZRLE relies on some byte values being unused in the input. In other words, if the file contains every byte value 0 to 255, it can't be compressed with ZRLE. There needs to be at least one unused value per length of -null run found in the file. +null run found in the file, because the unused values are used as markers +telling the decoder how long each run of 0-bytes is. + +There is no "escape" byte in ZRLE, or any sort of block structure. Each +byte is either a byte of pixels, or a marker indicating a run of 0-bytes. In other words, if the input looks like: @@ -51,7 +76,7 @@ table is written to the file comptitle.s, along with the rest of the decoder code from comptitle.s.in. This means the decoder is specific to the encoded file, not general-purpose. If we weren't on such a limited system as the Atari 800, the table would be part of the output file, -not the code (like the "dictionary" in a deflated file, etc). +not the code (like the "dictionary" in a gzipped file, etc). Each byte value in the compressed data either represents itself (is a plain data value) or some number of consecutive zero bytes (is a code @@ -88,10 +113,10 @@ copied from compressed input to decompressed output as-is)... the value 05 represents a run of 2 consecutive zeroes, and 06 represents a run of 3 zeroes. -The decoder will look at the input, one byte at a time, and consult -the table to decide what to do with each byte. "Output" here is the -decompressed data, which you can think of as a list of bytes, which -starts out empty. +The decoder will look at the input (the compressed data), one byte at a +time, and consult the table to decide what to do with each byte. "Output" +here is the decompressed data, which you can think of as a list of bytes, +which starts out empty. input| table lookup | | data | result | action | output after action is taken @@ -117,6 +142,19 @@ In this dumb example, it's pretty obvious that the encoded data plus the table size is bigger than the original input, so the "compression" is actually making it bigger. But it serves to illustrate, I hope. +The table lookup values are stored as one byte, so a run of up to 255 +consecutive zeroes can be stored as a single byte. A run of more than +that could be stored as 2 or more runs in a row, but the current encoder +just aborts if it encounters a run of 256 zeroes (Taipan doesn't need +it so I didn't implement it). + +The astute reader will have noticed a that a table lookup result of 1, +meaning a zero-run of length 1, is possible to encode, but useless +(might as well just store the single zero as plain data), and including +an entry in the table for 1 makes the table larger by one entry for +no purpose. The actual encoder (see below) doesn't encode zero-runs +of length 1, it just stores a 0 as-is. + For Taipan, the title screen (newtitle.png) contains large black areas. A black pixel is a 0 bit. The title screen data ends up packed 8 pixels per byte (1 bit per pixel), so those black areas are represented by runs -- cgit v1.2.3