#include #include #include // #include // finally got rid of it! #include #include #include #include "sounds.h" /* define this to use cc65's rand() instead of POKEY's RANDOM register. Leave disabled for now as POKEY never returns 0 (it's an LFSR, I should have known that would happen...) */ #define POKEY_RANDOM /* define this for testing sea_battle(). it causes a pirate attack every time you leave port. Don't leave defined for a release!! */ // #define COMBAT_TEST /* define this to show internals of damage calculation */ // #define DAMAGE_TEST /* define this to test the mchenry() routine by entering damage and capacity numbers directly */ // #define MCHENRY_TEST /* define this to start the game in the year 1869, with 1000 capacity, 20 guns, and 1 billion cash and bank. */ // #define TIMEWARP /* define this to start the game in a 99% damaged ship */ // #define ALMOST_DEAD /**** 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 #define TMOUT_5M 18000 /* wait up to j jiffies for a keypress. returns -1 if no key pressed */ extern int __fastcall__ timed_getch(unsigned int j); /* custom Atari-aware cgetc() wrapper. returns only non-inverse plain ASCII characters, except EOL and BS. Unlike the real cgetc(), it's an unsigned char, and can't return -1 for failure (but, it will never fail. real cgetc() never fails either, even if user hits Break) */ extern unsigned char agetc(void); /* wrapper for agetc(): lowercases letters */ extern unsigned char lcgetc(void); /* wrapper for agetc(): returns only numbers, a, enter, backspace */ extern unsigned char numgetc(void); /* wrapper for agetc(): returns only y or n. dflt is 'y' or 'n' to set the default answer if the user presses Enter, or 0 for no default (waits until user presses either y or n) */ extern unsigned char __fastcall__ yngetc(char dflt); /* sleep for j jiffies (no PAL adjustment at the moment) */ extern void __fastcall__ jsleep(unsigned int j); #define flushinp() (POKE(764,255)) /* Atari-specific random number functions from rand.s. Non-Atari platforms can probably just: #define initrand() _randomize() #define randi() ((unsigned int)rand()) #define randl() (unsigned long)((randi() << 16) | randi()) */ #ifdef POKEY_RANDOM #define initrand() /* no-op on Atari */ /* random positive int, 0 to 32767 */ extern unsigned int __fastcall__ randi(void); #else #define initrand() _randomize() #define randi() ((unsigned int)rand()) #endif /* random long, 0 to 2**32-1 */ extern unsigned long __fastcall__ randl(void); /* defined in portstat.s, this is the contents of PORTSTAT.DAT. used to quickly redraw the port stats screen. If ever PORTSTAT.DAT needs to be regenerated, use mkportstats.c */ extern const char *port_stat_screen; /* boolean, whether or not port_stats() needs to redraw the static parts of the port stats screen (by copying port_stat_screen into screen RAM) */ char port_stat_dirty = 1; /* asm curses/conio funcs from clrtobot.s. Old C versions moved to oldcurses.c for reference. */ extern void clrtobot(void); extern void clrtoeol(void); /* asm funcs from draw_lorcha.s for drawing/animating enemy ships. used by sea_battle() */ extern void __fastcall__ draw_lorcha(int which); extern void __fastcall__ flash_lorcha(int which); extern void __fastcall__ damage_lorcha(int which); extern void __fastcall__ sink_lorcha(int which); extern void __fastcall__ clear_lorcha(int which); /* used to set the background/text colors here, but now the title screen does it (newtitle.s) */ void atari_text_setup() { jsleep(1); POKE(560, PEEK(212)); // restore the POKE(561, PEEK(213)); // display list jsleep(1); POKE(559, 34); // turn on the screen (normal playfield) jsleep(1); POKE(756, 0xb8); // use our custom font POKE(731, 1); // disable keyclick on XL/XE (does nothing on 400/800) } /* this didn't work out, bummer. */ // extern void __fastcall__ waitvcount(unsigned char c); /**** End of atari-specific stuff. Supposed to be, anyway. */ /* old version of this used to just 'return randl()%clamp'. If clamp were 0, the return value would be the unclamped result from randl() (x % 0 == x, in cc65). If it were 1, the return value would always be 1 (no randomness there). */ unsigned long randclamp(unsigned long clamp) { unsigned long r = randl(); if(clamp == 0) return clamp; if(clamp == 1) return r & 0x01; return r % clamp; } /* print 'count' spaces, but leave the cursor where it was. TODO: rewrite in asm. */ void cblank(unsigned char count) { char oldx = wherex(); char oldy = wherey(); while(count--) cputc(' '); gotoxy(oldx, oldy); } /* conio doesn't back up the cursor if you cputc(BKSP), it prints the graphics character instead. Could use putchar(), but using stdio links a bunch of extra support code. So: */ /* TODO: rewrite in asm */ void backspace() { gotox(wherex()-1); cblank(1); } /* get an inventory item, return its index into items[]. if allow_all is true, allows '*', which is used for 'throw cargo' in sea_battle. */ unsigned char get_item(unsigned char allow_all) { unsigned char i; for(;;) { i = lcgetc(); switch(i) { case 'o': return 0; case 's': return 1; case 'a': return 2; case 'g': return 3; case '*': if(allow_all) return 4; /* else fall thru */ default: break; } } } /* modified ultoa() with hardcoded radix */ extern char *ultostr(unsigned long value, char* s); #define ultoa(x, y, z) ultostr(x, y) /* taipan functions (modified as little as possible) */ #define GENERIC 1 #define LI_YUEN 2 /* title screen now a separate xex segment (see Makefile for details) */ // void splash_intro(void); // int get_one(void); #define get_one() agetc(); long get_num(void); 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); void fight_stats(int ships, int orders); void mchenry(void); void retire(void); void final_stats(void); void you_only_have(unsigned char in_bank); void cprintfancy_ctr(unsigned long num, unsigned char center); #define cprintfancy(num) cprintfancy_ctr(num, 0) unsigned char firmpos; /* char firm[23], fancy_num[24]; */ char *firm = (char *) 0x680; char *fancy_buf = (char *) 0x600; 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]; /* this really can go negative */ int hold = 0; /* these being negative would be a Bad Thing */ unsigned int guns = 0, month = 1, year = 1860, ec = 20, ed = 1; /* ec+=20, ed++ every game-year (12 turns). player would have to play until 15 Jan 5168 to overflow ec. */ unsigned char port = 1, bp = 0, li = 0, wu_warn = 0, wu_bailout = 0; // these need to be longs to avoid int overflow when // displaying ship status. long damage = 0, capacity = 60, newdamage; /* print an int or long as a string, conio-style */ void cprintulong(unsigned long ul) { cputs(ultoa(ul, fancy_buf, 10)); } void at_sea() { gotoxy(30, 6); cputc(' '); revers(1); cputs(location[0]); revers(0); cputc(' '); 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!!"); good_joss_sound(); timed_getch(TMOUT_5S); 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); cprintfancy(amount); cputs(", Taipan? "); choice = yngetc(0); if(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\nfor "); // cputs(fancy_num); cprintfancy(amount); cputs(", Taipan? "); choice = yngetc(0); if(choice == 'y') { cash -= amount; hold -= 10; guns += 1; } port_stats(); return; } /* replaces old fancy_numbers. same logic, but stuff is just printed on the screen rather than being kept in a buffer. center is a boolean. Since we no longer return a string, port_stats() can't use strlen() to position the debt amount... so we have to do it here. Complicates the logic. One minor difference between this and fancy_numbers() is that we print "1.10 Million" rather than "1.1 Million" (extra zero). I don't think anyone's going to complain. */ void cprintfancy_ctr(unsigned long num, unsigned char center) { char mil = 1; char *fancy_buf = (char*)0x600; unsigned long tmp; if(num >= 100000000L) { /* 100 million and up: |1000 Million| |100 Million | */ if(center) revers(1); cputs(ultoa(num / 1000000L, fancy_buf, 10)); } else if (num >= 10000000L) { /* 10 million to 99 million: | 10 Million | |10.1 Million|*/ tmp = (num % 1000000L) / 100000L; if(center && !tmp) cputc(' '); if(center) revers(1); cputs(ultoa(num / 1000000L, fancy_buf, 10)); if(tmp) { cputc('.'); cputs(ultoa(tmp, fancy_buf, 10)); } } else if (num >= 1000000L) { /* 1 million to 9 million: | 1 Million | |1.10 Million| // always has 0, never 1.1 |1.23 Million| */ tmp = (num % 1000000L) / 10000L; if(center && !tmp) cputc(' '); if(center) revers(1); cputs(ultoa(num / 1000000L, fancy_buf, 10)); if(tmp) { cputc('.'); cputs(ultoa(tmp, fancy_buf, 10)); } } else { /* 0 to 999999: | 999999 | | 99999 | | 9999 | | 999 | | 99 | | 9 | */ mil = 0; if(center) { cputc(' '); cputc(' '); cputc(' '); tmp = 0; for(tmp = 100L; tmp < 1000000L; tmp *= 100L) if(num < tmp) cputc(' '); } if(center) revers(1); cputs(ultoa(num, fancy_buf, 10)); } if(mil) cputs(" Million"); revers(0); } /* void fancy_numbers(unsigned long num, char *fancy) { 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 fight_stats(int ships, int orders) { cursor(0); gotoxy(0, 0); if(ships < 1000) cputc(' '); if(ships < 100) cputc(' '); if(ships < 10) cputc(' '); cprintulong(ships); cputs(" ship"); if(ships != 1) cputc('s'); cputs(" attacking, Taipan! \r\n"); cputs("Your orders are to: "); if(orders == 1) cputs("Fight "); else if(orders == 2) cputs("Run "); else if(orders == 3) cputs("Throw Cargo"); 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; } /* 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; port_stat_dirty = 1; 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(); cursor(0); flushinp(); fight_stats(num_ships, orders); while(num_ships > 0) { if(damage >= capacity) return 4; status = 100L - ((damage * 100L / capacity)); /* // I think this is a problem: if(status <= 0) { return 4; } */ flushinp(); gotoxy(0, 3); clrtoeol(); cputs("Current seaworthiness: "); cputs(st[status / 20]); cputs(" ("); cprintulong(status); cputs("%)"); gotoxy(0, 4); 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); num_on_screen++; } } } plus_or_space(num_ships > num_on_screen); gotoxy(0, 16); cputs("\r\n"); input = timed_getch(TMOUT_3S); if(input == 'f') { orders = 1; } else if(input == 'r') { orders = 2; } else if (input == 't') { orders = 3; } if(orders == 0) { input = timed_getch(TMOUT_3S); if (input == 'f') { orders = 1; } else if(input == 'r') { orders = 2; } else if(input == 't') { orders = 3; } else { gotoxy(0, 3); clrtoeol(); cputs("Taipan, what shall we do??\r\n(Fight, Run, Throw cargo)"); under_attack_sound(); // timeout(-1); while ((input != 'f') && (input != 'r') && (input != 't')) { input = lcgetc(); } 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."); timed_getch(TMOUT_3S); gotoxy(0, 3); clrtoeol(); cputs("We're firing on 'em, Taipan!"); timed_getch(TMOUT_1S); 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); 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; } /* flash_lorcha must be called an even number of times to leave the lorcha in an unflashed state after. */ flash_lorcha(targeted); jsleep(2); flash_lorcha(targeted); jsleep(2); flash_lorcha(targeted); jsleep(2); flash_lorcha(targeted); jsleep(2); flash_lorcha(targeted); jsleep(2); flash_lorcha(targeted); jsleep(2); damage_lorcha(targeted); ships_on_screen[targeted] -= randi()%30 + 10; if(ships_on_screen[targeted] <= 0) { num_on_screen--; num_ships--; sk++; ships_on_screen[targeted] = 0; bad_joss_sound(); /* not sure this should be here */ sink_lorcha(targeted); plus_or_space(num_ships > num_on_screen); 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!"); bad_joss_sound(); } else { cputs("Hit 'em, but didn't sink 'em, Taipan!"); } timed_getch(TMOUT_3S); // 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!"); bad_joss_sound(); 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--; clear_lorcha(i); jsleep(5); } } if(num_ships == num_on_screen) { plus_or_space(0); } } gotoxy(0, 16); timed_getch(TMOUT_3S); 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!!"); timed_getch(TMOUT_3S); } else if (orders == 3) { static int choice; static long amount, total; choice = 0; amount = 0; total = 0; gotoxy(0, 3); cputs("You have the following on board, Taipan:"); gotoxy(4, 4); cputs(item[0]); cputs(": "); cprintulong(hold_[0]); gotoxy(24, 4); cputs(item[1]); cputs(": "); cprintulong(hold_[1]); gotoxy(5, 5); cputs(item[2]); cputs(": "); cprintulong(hold_[2]); gotoxy(21, 5); cputs(item[3]); cputs(": "); cprintulong(hold_[3]); gotoxy(0, 6); clrtoeol(); cputs("What shall I throw overboard, Taipan? "); choice = get_item(1); if(choice < 4) { gotoxy(0, 6); clrtoeol(); cputs("How much, Taipan? "); amount = get_num(); 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]; } gotoxy(0, 4); clrtoeol(); gotoxy(0, 5); clrtoeol(); gotoxy(0, 6); clrtoeol(); if(total > 0) { gotoxy(0, 3); clrtoeol(); cputs("Let's hope we lose 'em, Taipan!"); bad_joss_sound(); 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(); timed_getch(TMOUT_3S); } else { gotoxy(0, 3); clrtoeol(); cputs("There's nothing there, Taipan!"); good_joss_sound(); timed_getch(TMOUT_3S); } } if((orders == 2) || (orders == 3)) { if(orders == 2) { gotoxy(0, 3); clrtoeol(); cputs("Aye, we'll run, Taipan."); timed_getch(TMOUT_3S); } ok += ik++; if(randi()%ok > randi()%num_ships) { flushinp(); gotoxy(0, 3); clrtoeol(); cputs("We got away from 'em, Taipan!"); good_joss_sound(); timed_getch(TMOUT_3S); num_ships = 0; } else { gotoxy(0, 3); clrtoeol(); cputs("Couldn't lose 'em."); timed_getch(TMOUT_3S); 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(); 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--; clear_lorcha(i); jsleep(5); } } plus_or_space(num_ships > num_on_screen); } gotoxy(0, 16); timed_getch(TMOUT_3S); 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!"); timed_getch(TMOUT_3S); flushinp(); /* screen flash doesn't change the hue bit of COLOR2 register, since we now support changing it on the title screen. */ for(i = 0; i < 3; i++) { unsigned char color = PEEK(710) & 0xf0; unsigned char textcolor = PEEK(709); POKE(709,0); POKE(710, color | 0x0c); jsleep(10); POKE(710, color & 0xf0); jsleep(10); POKE(709,textcolor); } fight_stats(num_ships, orders); /* XXX: I don't think this is needed at all! for(i = 0; i <= 9; i++) { if(ships_on_screen[i] > 0) { draw_lorcha(i); } } */ plus_or_space(num_ships > num_on_screen); gotoxy(0, 3); clrtoeol(); cputs("We've been hit, Taipan!!"); under_attack_sound(); timed_getch(TMOUT_3S); 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 * 100L) / capacity)) || (((damage * 100L) / capacity)) > 80)) { i = 1; guns--; hold += 10; fight_stats(num_ships, orders); gotoxy(0, 3); clrtoeol(); cputs("The buggers hit a gun, Taipan!!"); under_attack_sound(); fight_stats(num_ships, orders); timed_getch(TMOUT_3S); } // 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) // broken because sometimes works out to 0 or 1. If it's 0, // randi()%0 is just randi() (ouch)... on a modern platform, // this would trigger a floating point exception or similar. // cc65 runtime can't detect it... // If ((ed * i * id)/2)) works out to 1, anything%1 is 0. // damage = damage + (randi() % ((ed * i * id)/2)) + (i / 2); // The answer is to avoid to % operator if the 2nd arg would be // 0 or 1: the intended result would just be 0 or 1 anyway. newdamage = ((ed * i * id)/2) + (i / 2); if(newdamage <= 0) newdamage = 1; // how the hell could this happen? if(newdamage > 1) newdamage = randi() % newdamage; damage += newdamage; if(damage > capacity) damage = capacity; /* just in case */ /* the above is still somehow broken. When fighting lots of ships, late in the game, we still get ship status over 100% in the fight screen, and mchenry says we're 4 billion percent damaged (and memory gets all kinds of corrupted after that). I do NOT understand what's going on here. It looks like damage is still somehow going negative, but that shouldn't be possible. */ if(damage < 0) damage = capacity; /* band-aid! */ #ifdef DAMAGE_TEST gotoxy(0, 23); clrtoeol(); cprintulong(ed); cputc(' '); cprintulong(i); cputc(' '); cprintulong(id); cputc(' '); cprintulong(damage); cputc(' '); cprintulong(newdamage); cgetc(); #endif 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!"); bad_joss_sound(); timed_getch(TMOUT_3S); return 1; } else { return 3; } } #if 0 /* TODO: rewrite in asm get_one() gets one character of input, but waits for Enter to be pressed, and allows backspacing over it. Honestly I don't see the value in using this instead of cgetc(). Plenty of prompts in the game (e.g. Buy/Sell/Quit Trading), and all the stuff in combat, just accept one character without need to press Enter. This is called from: mchenry() (y or n) cash_or_guns() (1 or 2) final_stats() (play again, y or n) li_yuen_extortion() (y or n, twice) elder_brother_wu() (y or n, twice) buy() (for picking item) sell() (idem) */ 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) /* these 2 lines make the cursor visible */ cursor(1); cblank(1); while ((input = cgetc()) != '\n') { if (((input == BKSP) || (input == 127)) && (character == 0)) { } else if ((input == BKSP) || (input == 127)) { backspace(); character--; } else if (character >= 1) { } else if (input == '\33') { flushinp(); } else { cputc(input); choice = input; character++; } } cursor(0); return choice; } /* new version. Still too much code! */ int get_one(void) { cursor(1); cblank(1); return agetc(); } #endif /* TODO: rewrite in asm. Maybe. */ #if 0 unsigned long get_num(int maxlen) { static char number[20]; int input, character = 0; long amount; /* new: */ cursor(1); cblank(1); while ((input = cgetc()) != '\n') { if (((input == BKSP) || (input == 127)) && (character == 0)) { } else if ((input == BKSP) || (input == 127)) { backspace(); number[character] = '\0'; character--; } else if (character >= maxlen) { } else if (input == '\33') { flushinp(); } else if (((input == 'A') || (input == 'a')) && (character == 0) && (maxlen > 1)) { cputc(input); number[character] = input; character++; } else if ((input < 48) || (input > 57)) { } else { cputc(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; } #else long get_num(void) { static char number[20]; unsigned char count = 0; int input; cursor(1); cblank(1); while((input = numgetc()) != '\n') { if(input == BKSP) { if(!count) continue; backspace(); number[count] = '\0'; count--; } else if(input == 'a') { if(!count) return -1; } else { cputc(input); number[count++] = input; } } cursor(0); number[count] = '\0'; return strtol(number, (char **)NULL, 10); } #endif /* 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); capacity = 60; damage = 0; if (choice == '1') { cash = 400; debt = 5000; hold = 60; guns = 0; li = 0; bp = 10; } else { #ifdef TIMEWARP year = 1869; cash = 1000000000L; bank = 1000000000L; debt = 0; capacity = 1000; hold = 800; guns = 20; li = 1; bp = 7; ed = 9; ec = 90; #else cash = 0; debt = 0; hold = 10; guns = 5; li = 1; bp = 7; #endif #ifdef ALMOST_DEAD damage = capacity - 1; #endif } 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 * 100L) / capacity); /* all the static text that used to be in port_stats() has been moved to mkportstats.c, which creates a .xex file which will get prepended to taipan.xex and loaded into a chunk of memory cc65 won't use. When it's time to print it, it'll get memcpy()ed into *SAVMSC. */ #if 0 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 "); cputsxy(4, 5, "Arms Vacant"); cputsxy(4, 6, "General "); cputsxy(1, 8, "Hold "); cputsxy(16, 8, "Guns "); cputsxy(4, 9, "Opium "); cputsxy(4, 10, "Silk "); cputsxy(4, 11, "Arms "); cputsxy(4, 12, "General "); cputsxy(32, 2, "Date"); cputsxy(29, 3, "15 "); cputsxy(30, 5, "Location"); cputsxy(32, 8, "Debt"); cputsxy(29, 11, "Ship Status"); cputsxy(0, 14, "Cash: "); cputsxy(20, 14, "Bank: "); #else if(port_stat_dirty) { void *p = (void *)(PEEK(88) + 256 * PEEK(89)); /* don't update the top of the screen while ANTIC is reading from it (prevents tearing)... but it doesn't work :( */ // waitvcount(84); memcpy(p, &port_stat_screen, 640); port_stat_dirty = 0; } #endif /* dynamic stuff: */ // waitvcount(84); cursor(0); gotoxy(firmpos, 0); cputs("Firm: "); cputs(firm); cputs(", Hong Kong"); gotoxy(21, 4); in_use = hkw_[0] + hkw_[1] + hkw_[2] + hkw_[3]; cblank(5); cprintulong(in_use); gotoxy(21, 6); cblank(5); cprintulong(10000 - in_use); gotoxy(12, 3); cblank(5); cprintulong(hkw_[0]); gotoxy(12, 4); cblank(5); cprintulong(hkw_[1]); gotoxy(12, 5); cblank(5); cprintulong(hkw_[2]); gotoxy(12, 6); cblank(5); cprintulong(hkw_[3]); gotoxy(7, 8); if (hold >= 0) { cblank(8); cprintulong(hold); } else { revers(1); cputs("Overload"); revers(0); } gotoxy(22, 8); cblank(5); cprintulong(guns); gotoxy(12, 9); cblank(9); cprintulong(hold_[0]); gotoxy(12, 10); cblank(9); cprintulong(hold_[1]); gotoxy(12, 11); cblank(9); cprintulong(hold_[2]); gotoxy(12, 12); cblank(9); cprintulong(hold_[3]); gotoxy(32, 3); revers(1); cputs(months[month - 1]); revers(0); cputc(' '); cprintulong(year); gotoxy(30, 6); cblank(10); revers(1); if(port == 4 || port == 5) cputc(' '); cputs(location[port]); revers(0); gotoxy(28, 9); // fancy_numbers(debt, fancy_num); // gotox(34 - strlen(fancy_num) / 2); clrtoeol(); // revers(1); cprintfancy_ctr(debt, 1); // cputs(fancy_num); // revers(0); gotoxy(29, 12); clrtoeol(); i = status / 20; if (i < 2) { revers(1); } cputs(st[i]); cputc(':'); cprintulong(status); revers(0); gotoxy(6, 14); cblank(14); // fancy_numbers(cash, fancy_num); // cputs(fancy_num); cprintfancy(cash); gotoxy(26, 14); cblank(13); // fancy_numbers(bank, fancy_num); // cputs(fancy_num); cprintfancy(bank); } void mchenry(void) { static int choice; 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? "); choice = yngetc('y'); if(choice == 'y') { static int percent, time; static long br, repair_price, amount; // int percent = ((float) damage / capacity) * 100, percent = (damage * 100L / capacity); 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; */ 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(); if(amount == -1) { if(cash > repair_price) 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) { cursor(0); 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(0); timed_getch(TMOUT_5S); final_stats(); } void final_stats(void) { /* TODO: write cprintlong() to print signed value */ long finalcash; int years = year - 1860, time = ((year - 1860) * 12) + month, choice = 0; port_stat_dirty = 1; clrscr(); cputs("Your final status:\r\n\r\n"); finalcash = cash + bank - debt; // fancy_numbers(finalcash, fancy_num); cputs("Net cash: "); // cputs(fancy_num); cprintfancy(finalcash); 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"); finalcash = finalcash / 100 / time; revers(1); cputs("Your score is "); cprintulong(finalcash); cputs(".\r\n"); revers(0); if ((finalcash < 100) && (finalcash >= 0)) { cputs("Have you considered a land based job?\r\n\r\n\r\n"); } else if (finalcash < 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"); cputc(17); // upper left corner chline(31); cputc(5); // upper right corner cputs("\r\n"); cputc('|'); if (finalcash > 49999L) { revers(1); } cputs("Ma Tsu"); revers(0); cputs(" 50,000 and over |\r\n"); cputc('|'); if ((finalcash < 50000L) && (finalcash > 7999L)) { revers(1); } cputs("Master Taipan"); revers(0); cputs(" 8,000 to 49,999|\r\n"); cputc('|'); if ((finalcash < 8000L) && (finalcash > 999L)) { revers(1); } cputs("Taipan"); revers(0); cputs(" 1,000 to 7,999|\r\n"); cputc('|'); if ((finalcash < 1000) && (finalcash > 499)) { revers(1); } cputs("Compradore"); revers(0); cputs(" 500 to 999|\r\n"); cputc('|'); if (finalcash < 500) { revers(1); } cputs("Galley Hand"); revers(0); cputs(" less than 500|\r\n"); cputc(26); // lower left corner chline(31); cputc(3); // lower right corner gotoxy(0, 22); cputs("Play again? "); choice = yngetc(0); /* while ((choice != 'Y') && (choice != 'y') && (choice != 'N') && (choice != 'n')) { gotoxy(0, 22); cputs("Play again? "); choice = get_one(); } */ if(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; 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"); good_joss_sound(); timed_getch(TMOUT_5S); 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(); 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!"); good_joss_sound(); } else { gotoxy(0, 21); cputs("Your warehouse will only hold an\r\nadditional "); cprintulong(10000 - in_use); cputs(", Taipan!"); good_joss_sound(); timed_getch(TMOUT_5S); } } else { gotoxy(0, 18); clrtobot(); cputs("You have only "); cprintulong(hold_[i]); cputs(", Taipan.\r\n"); good_joss_sound(); timed_getch(TMOUT_5S); } } 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(); 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"); timed_getch(TMOUT_5S); } } port_stats(); } } return; } void quit(void) { unsigned char choice; int result = 0, damagepct, sunk; 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 = numgetc() - '0'; if (choice == port) { cputs("\r\n\nYou're already here, Taipan."); good_joss_sound(); timed_getch(TMOUT_5S); } 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 ship"); if(num_ships != 1) cputc('s'); cputs(" approaching, Taipan!\r\n"); under_attack_sound(); timed_getch(TMOUT_3S); result = sea_battle(GENERIC, num_ships); gotoxy(0,23); /* to avoid disappearing U in "In use" */ } if (result == 2) { port_stats(); at_sea(); captains_report(); cputs("Li Yuen's fleet drove them off!"); timed_getch(TMOUT_3S); } if (((result == 0) && (randi()%(4 + (8 * li))) == 0) || (result == 2)) { gotoxy(0, 18); clrtobot(); cputs("Li Yuen's pirates, Taipan!!\r\n\n"); bad_joss_sound(); timed_getch(TMOUT_3S); if (li > 0) { cputs("Good joss!! They let us be!!\r\n"); bad_joss_sound(); timed_getch(TMOUT_3S); return; } else { static int num_ships; num_ships = randi()%((capacity / 5) + guns) + 5; cprintulong(num_ships); cputs(" ships of Li Yuen's pirate\r\n"); cputs("fleet, Taipan!!\r\n"); under_attack_sound(); timed_getch(TMOUT_3S); sea_battle(LI_YUEN, num_ships); gotoxy(0,23); /* to avoid disappearing U in "In use" */ } } 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); cprintfancy(booty); cputc('!'); cash += booty; good_joss_sound(); } else if (result == 3) { cputs("We made it!"); good_joss_sound(); } else { cputs("The buggers got us, Taipan!!!\r\n"); cputs("It's all over, now!!!"); timed_getch(TMOUT_5S); final_stats(); return; } timed_getch(TMOUT_3S); } if (randi()%10 == 0) { gotoxy(0, 18); clrtobot(); cputs("Storm, Taipan!!\r\n\n"); bad_joss_sound(); timed_getch(TMOUT_3S); if (randi()%30 == 0) { cputs(" I think we're going down!!\r\n\n"); timed_getch(TMOUT_3S); // if (((damage / capacity * 3) * ((float) randi() / RAND_MAX)) >= 1) // in the float version, damage/capacity*3 is your damage percentage, // scaled 0 (0%) to 3 (100%). So if you're less than 34% damaged, // you have no chance of sinking. If you're 34%-66% damaged, you // have a 1 in 3 chance. If you're over 66%, you have a 2 in // 3 chance. damagepct = damage * 100L / capacity; if(damagepct < 34) sunk = 0; else if(damagepct < 67) sunk = randclamp(3) == 0; else sunk = randclamp(3) != 0; if(sunk) { cputs("We're going down, Taipan!!\r\n"); under_attack_sound(); timed_getch(TMOUT_5S); final_stats(); } } cputs(" We made it!!\r\n\n"); bad_joss_sound(); timed_getch(TMOUT_3S); 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]); timed_getch(TMOUT_3S); } } 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("..."); timed_getch(TMOUT_3S); 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); cprintfancy(amount); 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"); timed_getch(TMOUT_3S); 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"); timed_getch(TMOUT_5S); } 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"); timed_getch(TMOUT_5S); } } } port_stats(); return; } void you_only_have(unsigned char in_bank) { gotoxy(0, 18); clrtobot(); cputs("Taipan, you only have "); cprintfancy(in_bank ? bank : cash); cputs("\r\nin "); cputs(in_bank ? "the bank" : "cash"); cputs(".\r\n"); good_joss_sound(); timed_getch(TMOUT_5S); } 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(); choice = yngetc('n'); if ((choice == 'n') || choice == 0) { break; } else if (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') { compradores_report(); cputs("Very well, Taipan, the game is over!\r\n"); under_attack_sound(); timed_getch(TMOUT_5S); final_stats(); } else if (choice == 'y') { cash += i; debt += j; port_stats(); compradores_report(); cputs("Very well, Taipan. Good joss!!\r\n"); bad_joss_sound(); timed_getch(TMOUT_5S); 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(); 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 { you_only_have(0); } } } port_stats(); for (;;) { compradores_report(); cputs("How much do you wish to\r\n"); cputs("borrow? "); wu = get_num(); 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!"); good_joss_sound(); timed_getch(TMOUT_5S); } } port_stats(); // break; /* do NOT let him steal the money back on the SAME TURN he loans it to you! */ return; } } 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"); under_attack_sound(); timed_getch(TMOUT_5S); } return; } void good_prices(void) { // static char item[14]; unsigned char i = randi()%4; /* 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(item[i]); cputs("\r\n has "); if(randi()&1) { price[i] *= (randi()%5 + 5); cputs("risen"); } else { price[i] /= 5; /* somehow general cargo dropped to 0 once. stop it. */ if(price[i] < 1) price[i] = 1; cputs("dropped"); } cputs(" to "); /* 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"); good_joss_sound(); timed_getch(TMOUT_3S); } #if 0 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(0); if (port == 1) { if ((cash + bank) >= 1000000) { cputs("Shall I Buy, Sell, Visit bank, Transfer\r\n"); cputs("cargo, Quit trading, or Retire?"); cursor(1); cputc(' '); choice = agetc(); if ((choice == 'b') || (choice == 's') || (choice == 'v') || (choice == 't') || (choice == 'q') || (choice == 'r')) { break; } } else { cputs("Shall I Buy, Sell, Visit bank, Transfer\r\n"); cputs("cargo, or Quit trading?"); cursor(1); cputc(' '); choice = agetc(); if ((choice == 'b') || (choice == 's') || (choice == 'v') || (choice == 't') || (choice == 'q')) { break; } } } else { cputs("Shall I Buy, Sell, or Quit trading?"); cursor(1); cputc(' '); choice = agetc(); if ((choice == 'b') || (choice == 's') || (choice == 'q')) { break; } } cursor(0); } return choice; } #else int port_choices(void) { int choice = 0; char retire_ok; 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]); gotoxy(0, 22); clrtobot(); cursor(0); retire_ok = (port == 1 && ((cash + bank) >= 1000000)); cputs("Shall I Buy, Sell, "); if(port ==1) { cputs("Visit bank, Transfer\r\ncargo, "); } if(!retire_ok) cputs("or "); cputs("Quit trading"); if(retire_ok) cputs(", or Retire"); cursor(1); cputs("? "); for(;;) { choice = lcgetc(); if(choice == 'b' || choice == 's' || choice == 'q') break; if(port == 1) { if(retire_ok && choice == 'r') break; if(choice == 't' || choice == 'v') break; } } cursor(0); return choice; } #endif /* TODO: rewrite in asm, or at least better C */ void name_firm(void) { unsigned char input, firmlen = 0; cursor(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 = agetc()) && (firmlen < 22)) { if(input == ENTER) { if(firmlen) break; else bad_joss_sound(); } else if(input == BKSP) { if(firmlen) { backspace(); firm[firmlen--] = '\0'; } } else { cputc(input); firm[firmlen++] = input; } } cursor(0); firm[firmlen] = '\0'; firmpos = 12 - firmlen / 2; return; } void buy(void) { /* see comment in fancy_numbers for why this is static */ // static char space[5]; int choice = 0; long afford, amount; gotoxy(0, 22); clrtobot(); cputs("What do you wish me to buy, Taipan? "); choice = get_item(0); /* for (;;) { gotoxy(0, 22); clrtobot(); cputs("What do you wish me to buy, Taipan? "); choice = tolower(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(); 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; gotoxy(0, 22); clrtobot(); cputs("What do you wish me to sell, Taipan? "); choice = get_item(0); /* for (;;) { gotoxy(0, 22); clrtobot(); cputs("What do you wish me to sell, Taipan? "); choice = tolower(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(); 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(); if (amount == -1) { amount = cash; } if (amount <= cash) { cash -= amount; bank += amount; break; } else { you_only_have(0); } } port_stats(); for (;;) { compradores_report(); cputs("How much will you withdraw? "); amount = get_num(); if (amount == -1) { amount = bank; } if (amount <= bank) { cash += amount; bank -= amount; break; } else { you_only_have(1); } } port_stats(); return; } /* N.B. cc65 is perfectly OK with main(void), and it avoids warnings about argv/argc unused. */ int main(void) { int choice; #ifdef MCHENRY_TEST { while(1) { clrscr(); cputs("dmg? "); damage = get_num(); cputs("\r\n"); cputs("cap? "); capacity = get_num(); mchenry(); } } #endif atari_text_setup(); initrand(); 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"); timed_getch(TMOUT_3S); 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"); timed_getch(TMOUT_3S); 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"); timed_getch(TMOUT_5S); 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 = 0; if(cash > 0) 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); cprintfancy(fine); cputs(", Taipan!\r\n"); timed_getch(TMOUT_5S); } 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"); timed_getch(TMOUT_5S); } 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"); bad_joss_sound(); timed_getch(TMOUT_3S); } 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); cprintfancy(robbed); cputs(" in cash, Taipan!!\r\n"); under_attack_sound(); timed_getch(TMOUT_5S); } 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; }