From 2300d2813a524cbfeabac794335e7abe99263df6 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Tue, 29 Dec 2015 23:10:50 -0500 Subject: initial commit --- LORCHA.DAT | Bin 0 -> 49 bytes LORCHA.LST | 2 + Makefile | 69 ++ README.txt | 234 ++++ SOUND1.LST | 1 + TITLE.DAT | Bin 0 -> 960 bytes TITLE.LST | 5 + apple_call_addresses | 11 + applesounds.wav | Bin 0 -> 14449964 bytes bank.pl | 13 + bitmapdump.pl | 34 + characters | 55 + convfont.c | 146 +++ custom.cfg | 43 + debt.pl | 17 + dfoxtest.c | 210 ++++ draw_lorcha.s | 100 ++ findfont.pl | 49 + font | Bin 0 -> 768 bytes fontrip.txt | 41 + fonttmp | 109 ++ jsleep.s | 11 + lorcha.c | 9 + lorcha.pl | 20 + lorchatest.c | 37 + oldcurses.c | 138 +++ rand.s | 23 + romfont | Bin 0 -> 1024 bytes taifont | Bin 0 -> 1024 bytes taipan-applesoft.txt | 961 +++++++++++++++ taipan-orig.c | 2735 ++++++++++++++++++++++++++++++++++++++++ taipan.c | 2706 ++++++++++++++++++++++++++++++++++++++++ taipan.c_before_timed_getch | 2877 +++++++++++++++++++++++++++++++++++++++++++ taipan.dsk | Bin 0 -> 143360 bytes timed_getch.pl | 48 + timed_getch.s | 26 + title.pl | 7 + 37 files changed, 10737 insertions(+) create mode 100644 LORCHA.DAT create mode 100644 LORCHA.LST create mode 100644 Makefile create mode 100644 README.txt create mode 100644 SOUND1.LST create mode 100644 TITLE.DAT create mode 100644 TITLE.LST create mode 100644 apple_call_addresses create mode 100644 applesounds.wav create mode 100644 bank.pl create mode 100755 bitmapdump.pl create mode 100644 characters create mode 100644 convfont.c create mode 100644 custom.cfg create mode 100644 debt.pl create mode 100644 dfoxtest.c create mode 100644 draw_lorcha.s create mode 100644 findfont.pl create mode 100644 font create mode 100644 fontrip.txt create mode 100644 fonttmp create mode 100644 jsleep.s create mode 100644 lorcha.c create mode 100644 lorcha.pl create mode 100644 lorchatest.c create mode 100644 oldcurses.c create mode 100644 rand.s create mode 100644 romfont create mode 100644 taifont create mode 100644 taipan-applesoft.txt create mode 100644 taipan-orig.c create mode 100644 taipan.c create mode 100644 taipan.c_before_timed_getch create mode 100644 taipan.dsk create mode 100644 timed_getch.pl create mode 100644 timed_getch.s create mode 100644 title.pl diff --git a/LORCHA.DAT b/LORCHA.DAT new file mode 100644 index 0000000..e11c6fe Binary files /dev/null and b/LORCHA.DAT differ diff --git a/LORCHA.LST b/LORCHA.LST new file mode 100644 index 0000000..b494971 --- /dev/null +++ b/LORCHA.LST @@ -0,0 +1,2 @@ +1 POKE 82,0›2 ? CHR$(125)›3 POSITION 0,0›5 OPEN #1,8,0,"H:LORCHA.DAT"›10 ? " = ="›20 ? "   ‹  "›30 ? "      "›40 ? "Š  ˆ   "›50 ? " + | |"›60 ? " ””””” "›70 ? "Š     ˆ"›100 FOR Y=0 TO 6›105 FOR X=0 TO 6›110 ? #1;CHR$(PEEK(40000+X+Y*40));›120 NEXT X›130 NEXT Y›140 CLOSE #1› \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1d254e1 --- /dev/null +++ b/Makefile @@ -0,0 +1,69 @@ + +CC=cl65 +SYS=atari +# COPT=-Oirs +COPT=-O -l -T +CFLAGS=-t $(SYS) -C custom.cfg -I. -L. $(COPT) +AS=ca65 +ASFLAGS=-l +AR=ar65 + +# A few files have no make rules here. LORCHA.DAT is generated as a +# side-effect of generating taifont.xex. It's a 49-byte (7x7) blob of +# Atari "internal" screen codes. + +# TITLE.DAT is generated by Atari BASIC code in TITLE.LST, +# except last time I modified it, I exited the emulator without +# saving the damn BASIC source, so TITLE.LST is outdated. TITLE.DAT +# is a complete 960-byte dump of GR.0 screen memory, which gets +# turned into title.xex and prepended to the main .xex. When +# it loads, it goes straight to screen memory. + +# romfont is the 1K font extracted from the Atari 800 OS, with a +# command like: +# dd if=atariosb.rom of=1 bs=256 skip=8 count=4 +# ...where atariosb.rom comes from e.g. the PC-Xformer 2.5 zip file. + +XEX=taipan.xex + +all: $(XEX) + +$(XEX): taimain.xex taifont.xex title.xex + cat taifont.xex title.xex taimain.xex > $(XEX) + +title.xex: TITLE.DAT + perl title.pl TITLE.DAT > title.xex + +taimain.xex: taipan.c rand.s draw_lorcha.s + cl65 --mapfile taipan.map -t atari -O -l -T -o taimain.xex taipan.c rand.s draw_lorcha.s timed_getch.s jsleep.s + +taifont.xex: convfont romfont font + cat romfont font | ./convfont -x > taifont.xex + touch draw_lorcha.s + +taifont: convfont romfont font + cat romfont font | ./convfont > taifont + +convfont: convfont.c + gcc -Wall -o convfont convfont.c + +test: all + atari800 -nobasic $(XEX) + +.s.o: + $(AS) $(ASFLAGS) -o $@ $< + +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +clean: + rm -f *.o *.lst convfont + +distclean: clean + rm -f *~ core *.xex .*.swp 1.* 2.* 1 2 map map.* *.map + +%.xex: %.c + $(CC) --mapfile map $(CFLAGS) -o $@ $< + +lorchatest.xex: lorchatest.c draw_lorcha.s lorcha_data.inc + cl65 -t atari -O -l -T -o lorchatest.xex lorchatest.c draw_lorcha.s diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..b31622b --- /dev/null +++ b/README.txt @@ -0,0 +1,234 @@ +Taipan for Atari 800 + +This is a work in progress. It's a port of the C version for Linux +and curses, with the font and screen layout from the original Apple +II version. + +Currently the game is playable but incomplete, and has a few known bugs +(see "Bugs" section below) and probably a few unknown ones too. + +Linux/curses port can be found here: +http://www.ibiblio.org/pub/linux/games/textrpg/ + +Original Apple II BASIC source, plus a browser version of the game, +can be found here: http://www.taipangame.com/ + +What's missing: + +- Sound. The Linux/curses port doesn't have any, but I'm planning to + have the Atari mimic the sounds from the Apple II version, plus + maybe a few more (cannon shots and explosions during combat). + +- Large integer (or floating point) support. Cash, Bank, and Debt + amounts will roll over to 0 if they exceed the max value for a 32-bit + unsigned integer (around 4 billion). I'm not sure if this is a real + problem for anyone (it takes a *long* time to get over a billion in + this game). + +- The title screen and ship graphics are pretty crude compared to + the Apple II original (though they're nicer than the curses ones). + +Building: + +Prerequisites are GNU make, cc65 (I use version 2.13.0, any recent one +should do), perl (any recent-ish version), and a *nix-like environment (I +use Slackware Linux, if you're on Windows you might try Cygwin or Msys). + +If you plan to edit the title screen or ship graphics, you'll need +the Atari800 emulator. It's also handy for actually playing the game, +whether you build it or use the provided binary. + +Running: + +The game binary is called "taipan.xex". It's a standard Atari DOS +'binary load' file. You can load it on a real Atari computer: any +400/800/XL/XE model should be fine, so long as it has at least 48K of +RAM. Use a SIO2PC cable and software like Atariserver (Linux) or APE +(Windows) to download the game to the Atari. If you can come up with +a way to actually copy it to a real floppy disk, you probably want a +bootable DOS disk with Taipan renamed to AUTORUN.SYS. + +It's also possible to run Taipan in an emulator, such as Atari800 +or Altirra. + +License: + +The legal status of this is quite murky. The original game is still +copyrighted, though quite a few clones of it have been made for various +platforms over the years with no complaints from the copyright holder. + +This Atari build includes font data ripped straight from the original +Apple game, plus more font data ripped from the Atari 800's OS ROM. + +The Linux port of taipan, according to its .lsm file, is GPL. My C code +is definitely a derivative work, so it's GPL also. The assembly code and +title/ship graphics are my own work, and I release them under the GPL +(version 2). + +Notes: + +The Atari executable file format allows for concatenating executables. +The result is still a valid executable. I use this to load the splash +screen and custom font directly into memory before the main program loads. +The Makefile documents how all this works, but it might seem pretty +hairy if you're new to the Atari, Makefiles, and/or Perl. + +The Apple version of the game was expected to be run on a monochrome +monitor. Like many other ports from the Apple to the Atari, there will +be color artifacts when using a composite monitor. For best results, +use a monochrome monitor. If you can't, at least try using a color +monitor with S-Video (separate chroma/luma) inputs. If all else fails, +try turning the color knob all the way down (and the contrast as high +as you can stand it). In emulators, you can just disable artifacting. + +On PAL systems, the ship explosions and sinking animations will be a bit +slower, and the prompt timeouts will be a bit longer. I don't think this +is a real issue (it's not like Taipan is a fast-paced arcade game). + +Bugs! At least these: + +- Can't change orders in mid-battle. Whatever you pick for your first + turn, you're stuck with. Not sure why yet. + +- The damage calculation is messed up. You can get killed in your first + battle, one shot can occasionally destroy a 100% healthy ship. + +- When the lower-left ship get sunk, a little graphical "turd" gets + left behind. + +- This may or may not be fixed: Occasionally the font gets partly + corrupted in memory, by some rogue pointer or cc65 bug. The usual + symptom is that the vertical bars of the "box" in the port status + display get messed up. Still investigating this one. It ain't like + the Atari has valgrind :) + +- The "negative interest" bug is currently missing, due to using + unsigned values for debt. Plus, it's cheating. It'll get added back when + I either start using big numbers (floats or 64-bit ints or whatever), + or just decide to live with the limits of 32-bit ints. + +- The retirement display still uses ASCII | and - to draw a box. They + should be using ATASCII line-drawing characters instead (like the port + status does). + +- Retirement score calculations are a bit off, due to using integer math. + +- Not really a bug, but, the interest calculations for debt and the bank + are slightly different, due to using integer math. Very small bank or + debt amounts will grow much faster than they should, then stabilize + and converge towards the correct values over time. This only happens + when you have less than 10 in debt, or less than 200 in the bank, + which (at least for me) are pretty rare situations. + +- A few things in the screen layout are slightly off comapred to + the Apple version. Would really like to get it exact. + +- The cursor isn't getting disabled in a few places, and at some + prompts it's not visible until you actually type something. + +- Escape key should actually work, when typing at prompts. + +- fancy_numbers() maybe should round when it's showing a decimal point. + If you have e.g. 1,190,000, that should show as 1.2 million, not 1.1... + or maybe not (need to double-check against the Apple version). + +Differences between the Apple II original and Linux port: + +1. Linux has an 80-column screen layout, Apple is 40. +2. Apple version uses a custom font (actually, two, but I'm ignoring that). +3. Apple has sound, Linux does not. +4. Apple has graphical title screen, Linux has ASCII art. +5. Apple has graphical ships during battles, Linux has ASCII art. +6. On Apple, price of General Cargo isn't always an integer (e.g. 6.5). + As a consequence, the cash and bank amounts aren't always ints either. +7. On Apple, some Y/N prompts (like 'Do you have business with Elder Brother + Wu') you can press Enter for No. Linux port waits until you hit Y or N. +8. On Apple, ships show damage (get holes in them) as they get shot up. +9. On Linux, you can overpay McHenry (though you get no benefit from it). + On Apple, payment amount gets clamped to the repair price, so you can + e.g. be asked to pay 50,000 when you have 70,000 and safely enter A + (you'll end up 100% repaired and still have 20,000 cash). +10. On Apple, dead enemy ships sink one scanline at a time, and there are + at least 2 sinking speeds. On Linux, it's one character at a time. + +The plan for the Atari port is to mimic the Apple version as closely as +possible... except #6 above. It doesn't really add anything to the game, +and it complicates the code more than I want to deal with. Also #10 +will probably not happen (to me, the slow ship-sinking of the Apple +version is annoying anyway). + +Right now, items 1, 2, 5, 7, and 9 are implemented Apple-style; and 3, +6, 8, 10 are Linux-style. 4 is kinda halfway between (the graphics are +6 of the enemy ships rather than a hi-res single ship). + +Other things that need doing to the code: + +- Size optimization. Right now, the executable is almost 32K of code. I'd + like it to at least fit on a 16K cartridge. A lot of the C code is + redundant, and some things can be rewritten in asm if need be. I've + already eliminated all uses of printf() and its ilk, which removed 2K + of library code from the executable. + +- In aid of the above: split splash_intro(), cash_or_guns(), name_firm() into + separate .xex segments. Have to write a linker script to generate an + init header rather than a run header (or, write in raw asm and forget + the linker). Use cassette buffer and/or page 6 to pass variables to the main + program. name_firm() is 1/2K, cash_or_guns() is 1/4K, rewrite in asm and + they may both fit in page 6. + +- Another memory saver: keep some variables in page 6 and/or the tape + buffer. Also, find out how much page zero cc65 leaves us to + work with, maybe enough contiguous bytes for e.g. the fancy_num[] + buffer. draw_lorcha is using FR0 at $D4, but using it for fancy numbers + wouldn't conflict... it looks like cc65 uses 26 bytes of ZP from + $80-$99, so we have quite a bit free. + +- A thought: if memory gets too tight, switch to a boot disk rather than a + .xex file, and load code from disk at runtime (e.g. sea_battle() could be + loaded on top of some other routines, then the other routines reloaded + when the fight is over). That, or use a bankswitched cartridge. + +- Temporarily add a "god mode" to allow me to test situations that would take + a lot of regular gameplay to reach. + +- The title screen could be rearranged a bit and use a custom display list + to put all the text on top and bottom, with a GR.8 ship in the + middle. The Apple version's ship is a 176x145 bitmap, 22 bytes wide, + or 3190 bytes total on disk. Might use a narrow playfield to display + it? Or use GR.15 for a greyscale (greenscale) image? + +Future Ideas: + +I may do a "Taipan Plus" at some point. The regular Taipan game will be +faithful to the original, and the Plus version could have some or all of: + +- More ports to dock at, some of which might have their own warehouses, + repair yards, etc. + +- More trade goods, not all of which are available at all ports. + +- Actual market trends, rather than a base price + random number. There + might be news events that cause prices to go up/down (e.g. Arms are + up at Saigon because there's a gang war in progress, Opium is up at + some port but the chances of getting busted are higher). + +- Ability to control a fleet of ships. Each one will either be a cargo + ship or a warship. + +- A "Turbo Combat" feature like one of the phone versions I've seen. You + set your orders and hit Turbo, and it finishes the fight instantly, + but you can't change your mind about your orders (fight until you win + or die, or run until you escape or die). + +- Special missions. Someone at some port needs you to transport documents + or whatever, to some other port... you will almost certainly be attacked + by whoever's trying to get the documents though. + +- Rival trading companies. Their activities can influence prices, and + you can fight them and possibly salvage actual cargo. + +- Variable passage of time. Distant ports take longer to get to. Also, + winds or ship damage can slow you down. + +I dunno how many of the above will fit in the Atari's RAM. Probably have +to rewrite the whole game from scratch in assembly before adding features. diff --git a/SOUND1.LST b/SOUND1.LST new file mode 100644 index 0000000..fbfcdaa --- /dev/null +++ b/SOUND1.LST @@ -0,0 +1 @@ +10 FOR I=1 TO 3›20 FOR J=20 TO 10 STEP -1›30 SOUND 0,J,10,10›40 NEXT J›50 NEXT I›1000 END › \ No newline at end of file diff --git a/TITLE.DAT b/TITLE.DAT new file mode 100644 index 0000000..83616aa Binary files /dev/null and b/TITLE.DAT differ diff --git a/TITLE.LST b/TITLE.LST new file mode 100644 index 0000000..c17e1c5 --- /dev/null +++ b/TITLE.LST @@ -0,0 +1,5 @@ +1 REM POKE 764,60 for lowercase›5 POKE 752,1›10 POKE 82,0:? CHR$(125);›50 POSITION 0,0:? " Created by:"›60 POSITION 0,1:? " ม๒๔ รแ๎ๆ้์"›70 POSITION 0,3:? " "›72 POSITION 0,5:? " Atari 8-bit"›73 POSITION 0,6:? " Program by "›74 POSITION 0,7:? " ยฎ ืแ๔๓๏๎"›79 POSITION 0,9:? " "›80 POSITION 0,11:? " Copyright "›82 POSITION 0,12:? " (C) 1982 by"›84 POSITION 0,13:? " ม๖แ์แ๎ใ่ๅ"›85 POSITION 0,14:? " ะ๒๏ไ๕ใ๔้๏๎๓"›86 POSITION 0,15:? " ษ๎ใฎ "›88 POSITION 0,17:? " "›90 POSITION 0,19:? " Press มฮู "›92 POSITION 0,20:? " key to "›94 POSITION 0,21:? " start "›99 POSITION 0,0›100 ? "      +        +   + Œ  + Œ‹"›110 ? "                       "›120 ? "           ˆ         ‰"›130 ? "                    "›140 ? ""›150 ? " A GAME BASED ON THE CHINA"›160 ? " TRADE OF THE 1800'S"›900 OPEN #1,8,0,"H:TITLE.DAT"›910 FOR I=0 TO 959:? #1;CHR$(PEEK(40000+I));:NEXT I›920 CLOSE #1›1000 POSITION 0,18›1010 POKE 752,0› \ No newline at end of file diff --git a/apple_call_addresses b/apple_call_addresses new file mode 100644 index 0000000..8e536b7 --- /dev/null +++ b/apple_call_addresses @@ -0,0 +1,11 @@ +-958 fc42 +2200 898 +2224 8b0 +2368 940 +2512 9d0 +2518 9d6 +2521 9d9 +2524 9dc +2560 a00 +2680 a78 +6147 1803 diff --git a/applesounds.wav b/applesounds.wav new file mode 100644 index 0000000..d5642a6 Binary files /dev/null and b/applesounds.wav differ diff --git a/bank.pl b/bank.pl new file mode 100644 index 0000000..927012c --- /dev/null +++ b/bank.pl @@ -0,0 +1,13 @@ +#!/usr/bin/perl -w + +my $debt = shift || 1000; +my $idebt = $debt; + +my $months = shift || 100; +for(1..$months) { + $debt += $debt * 0.005; + $idebt += ($idebt >> 8) + ($idebt >> 10); + # print "$debt\t$idebt\n"; + $pct = $idebt * 100 / $debt; + printf("%.2d\t$idebt\t%.1d%%\n", $debt, $pct); +} diff --git a/bitmapdump.pl b/bitmapdump.pl new file mode 100755 index 0000000..fb44a6f --- /dev/null +++ b/bitmapdump.pl @@ -0,0 +1,34 @@ +#!/usr/bin/perl -w + +# browse through a binary file looking for bitmapped graphics, +# especially fonts + +# user is expected to pipe through less + +$height = 8; # how many rows to display per block +$width = 8; # how many blocks to display per line + +undef $/; +$data = <>; + +for($offs=0; $offs +I, 20, fe f8 fe ff fe fe fd f9, @ +J, 3b, c0 fe ff ff ff ff 7f 1f, [ +K, 3c, 00 00 c0 ff 8f 8e 8e fe, \ +L, 3d, 38 38 38 38 ff 38 38 38, ] +M, 3e, 00 00 00 01 ff e3 e3 e3, ^ +N, 3f, 00 00 00 ff 8d 8d 8d ff, _ +O, 40, c3 cf ff ff 3f 3e 3c f8, 0x00 +P, 46, 1f 0f 07 07 07 03 03 03, 0x06 +Q, 47, f0 e0 c0 c0 c0 c0 80 80, 0x07 + +as screen data: + +80 02 03 04 00 06 +80 1b 80 1c 1d 1e +80 1b 80 1c 1d 20 +3b 3c 3d 3e 3f 46 +40 80 80 80 80 47 + +remaining unused characters, to be used for damage: +50 5b 5d 5e 5f 60 7b 7d 7e 7f + +For damage, we can poke holes in the big sail with an inverse +ball (^T). A couple of the portholes can be replaced with a +custom 'crater' character. The right sail loses its flag (D) +and its upper-right gets replaced with a custom char. The +left sail can lose either of its F characters and have them +replaced with a piece with a bite taken out of it. + diff --git a/convfont.c b/convfont.c new file mode 100644 index 0000000..a2a2418 --- /dev/null +++ b/convfont.c @@ -0,0 +1,146 @@ +#include +#include +#include + +#include +#include + +/* usage: + # extract the 1K atari ROM font: + dd if=atariosb.rom of=romfont bs=256 skip=8 count=4 + + # extract the Apple II Taipan font: + dd if=taipan.dsk of=font bs=256 skip=54 count=3 + + # create the Atari 8-bit Taipan font: + cat romfont font | ./convfont > taifont.raw + + # or, create the Atari 8-bit Taipan font as a binary load: + cat romfont font | ./convfont -x > taifont.xex + */ + +/* + taipan font file order: + 0-31: `a-z{|}" block + 32-63: @A-Z[\]^_ + 64-95: space !"#$%&'()*+,-./0-9:;>== 0); +} + +void clear0bits(unsigned char *b, int lim) { + do { + *b++ &= 0xfe; + } while(--lim > 0); +} + +int main(int argc, char **argv) { + int i, j; + unsigned char font[1024], xex[6]; + + read(0, font, 1024); + read(0, font + (96 * 8), 32 * 8); + bitswap(font + (96 * 8), 32 * 8); + read(0, font + (32 * 8), 32 * 8); + bitswap(font + (32 * 8), 32 * 8); + read(0, font + (0 * 8), 32 * 8); + bitswap(font + (0 * 8), 32 * 8); + + /* this stuff is from visual inspection via bitmapdump.pl */ + clear0bits(font + 0x1f8, 7); + clear0bits(font + 0x301, 7); + clear0bits(font + 0x308, 8); + clear0bits(font + 0x330, 8); + clear0bits(font + 0x3a0, 8); + clear0bits(font + 0x3d8, 8); + clear0bits(font + 0x3e8, 8); + clear0bits(font + 0x040, 16); + clear0bits(font + 0x1e8, 8); + + /* fix the vertical bar */ + font[0x3e0] = + font[0x3e1] = + font[0x3e2] = + font[0x3e3] = + font[0x3e4] = + font[0x3e5] = + font[0x3e6] = + font[0x3e7] = 0x18; + + /* stick ship data where it goes */ + for(i=0; shipdata[i][0]; i++) { + for(j=0; j<8; j++) { + font[ shipdata[i][0] * 8 + j ] = shipdata[i][j+1]; + } + } + + if(argc > 1) { + xex[0] = xex[1] = 0xff; + xex[2] = 0x00; xex[3] = 0xb8; /* load address $B800 */ + xex[4] = 0xff; xex[5] = 0xbb; /* end address $bbff */ + write(1, xex, 6); + } + write(1, font, 1024); + + i = open("LORCHA.DAT", O_WRONLY | O_CREAT); + write(i, shipshape, sizeof(shipshape)); + close(i); + + return 0; +} diff --git a/custom.cfg b/custom.cfg new file mode 100644 index 0000000..7f0db8c --- /dev/null +++ b/custom.cfg @@ -0,0 +1,43 @@ +FEATURES { +STARTADDRESS: default = $2E00; +} +SYMBOLS { +__STACKSIZE__ : value = $800, weak = yes; +__RESERVED_MEMORY__: value = $0, weak = yes; +} +MEMORY { +ZP: start = $0082, size = $007E, type = rw, define = yes; +HEADER: start = $0000, size = $0006, file = %O; +RAM: start = %S, size = $B7FF - __STACKSIZE__ - %S, file = %O; +TRAILER: start = $0000, size = $0006, file = %O; +} +SEGMENTS { +EXEHDR: load = HEADER, type = ro; +STARTUP: load = RAM, type = ro, define = yes; +LOWCODE: load = RAM, type = ro, define = yes, optional = yes; +INIT: load = RAM, type = ro, optional = yes; +CODE: load = RAM, type = ro, define = yes; +RODATA: load = RAM, type = ro; +DATA: load = RAM, type = rw; +ZPSAVE: load = RAM, type = bss, define = yes; +BSS: load = RAM, type = bss, define = yes; +HEAP: load = RAM, type = bss, optional = yes; +ZEROPAGE: load = ZP, type = zp; +EXTZP: load = ZP, type = zp, optional = yes; +AUTOSTRT: load = TRAILER, type = ro; +} +FEATURES { +CONDES: segment = INIT, +type = constructor, +label = __CONSTRUCTOR_TABLE__, +count = __CONSTRUCTOR_COUNT__; +CONDES: segment = RODATA, +type = destructor, +label = __DESTRUCTOR_TABLE__, +count = __DESTRUCTOR_COUNT__; +CONDES: type = interruptor, +segment = RODATA, +label = __INTERRUPTOR_TABLE__, +count = __INTERRUPTOR_COUNT__; +} + diff --git a/debt.pl b/debt.pl new file mode 100644 index 0000000..85a1841 --- /dev/null +++ b/debt.pl @@ -0,0 +1,17 @@ +#!/usr/bin/perl -w + +my $debt = shift || 1000; +my $idebt = $debt; + +my $months = shift || 100; +for(1..$months) { + $debt += $debt * 0.1; + if($idebt > 16) { + $idebt += (($idebt >> 4) + ($idebt >> 5) + ($idebt >> 7) - ($idebt >> 9)); + } else { + $idebt++; + } + # print "$debt\t$idebt\n"; + $pct = $idebt * 100 / $debt; + printf("%.2d\t$idebt\t%.1d%%\n", $debt, $pct); +} diff --git a/dfoxtest.c b/dfoxtest.c new file mode 100644 index 0000000..aefdd5d --- /dev/null +++ b/dfoxtest.c @@ -0,0 +1,210 @@ +/*#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/draw_lorcha.s b/draw_lorcha.s new file mode 100644 index 0000000..0f1a376 --- /dev/null +++ b/draw_lorcha.s @@ -0,0 +1,100 @@ + + .export _draw_lorcha + .import popax + +; TODO: maybe replace position tables with mul40? see +; libsrc/atari/mul40.s, which is getting linked anyway +; because conio uses it. + +; offset from start of screen for each ship position (0-9) +lorcha_pos_lo: + .byte <320, <328, <336, <344, <352 + .byte <640, <648, <656, <664, <672 +lorcha_pos_hi: + .byte >320, >328, >336, >344, >352 + .byte >640, >648, >656, >664, >672 + +; Atari OS's pointer to start of screen RAM + SAVMSC = $58 + +; ZP working variables start at $d4, aka FR0 (floating point reg 0). + mask = $d4 + displacement = $d5 + destptr = $d6 + +; Lorcha (boat), a type of sailing vessel having a Chinese +; junk rig on a Portuguese or European style hull. +; Our lorcha is a 7x7 block of ATASCII characters. We're storing +; directly to screen RAM, so we use 'internal' codes. +; To edit the graphics, load up LORCHA.LST in atari800, with the H: +; device enabled, writable, set to current directory. Then edit the +; quoted strings in lines 10-70, and RUN the program. It will +; generate a new LORCHA.DAT, which must be 49 bytes long (so don't +; change the lengths of the strings in the BASIC code!) +lorcha_data: + .incbin "LORCHA.DAT" + +; void __fastcall__ draw_lorcha(int which, int displacement, int mask); +_draw_lorcha: + sta mask ; stash mask (0 = normal, $80 = inverse) + jsr popax ; get displacement (0 = whole ship, 1..6 = sinking, 7 = blank) + sta displacement + jsr popax ; which ship position? + tax + +; setup pointer to screen offset of upper left of ship + lda lorcha_pos_lo,x + clc + adc SAVMSC + sta destptr + lda lorcha_pos_hi,x + clc + adc SAVMSC+1 + sta destptr+1 + + ; first, draw any blank lines + ldx displacement ; are there any? + beq shiplineloop ; no, draw ship +blanklineloop: + lda #0 ; screen code for space + ;ora mask ; apply mask ; don't need this here + ldy #6 ; ship is 7 columns wide (we count 6 to -1) +blankcolloop: + sta (destptr),y + dey + bpl blankcolloop + jsr bump_pointer ; add 40 (1 line) to dest pointer. + dex + bne blanklineloop + + ; X is now 0, however we got here. +shiplineloop: + lda displacement + cmp #7 + beq done + ldy #0 +shipcolloop: + lda lorcha_data,x + eor mask + sta (destptr),y + iny + inx + cpy #7 + bne shipcolloop + jsr bump_pointer + inc displacement + clc + bcc shiplineloop + +done: + rts + +bump_pointer: + lda destptr + clc + adc #40 + sta destptr + lda destptr+1 + adc #0 + sta 215 + rts diff --git a/findfont.pl b/findfont.pl new file mode 100644 index 0000000..ead5392 --- /dev/null +++ b/findfont.pl @@ -0,0 +1,49 @@ +#!/usr/bin/perl -w + +# we're looking for this: + +# 00111000 +# 01000100 +# 10101000 +# 10100000 +# 10100000 +# 01000100 +# 00111000 + +#@want = ( +#0b10101000, +#0b10100000, +#); + +@want = ( +0b00000101, +0b00000101, +); + +#@want = ( +#0b00111000, +#0b01000100, +#0b10101000, +#0b10100000, +#0b10100000, +#0b01000100, +#0b00111000, +#); + +# or possibly a version of it shifted 1 or 2 bits to the right + +undef $/; +$img = <>; +@bytes = map { ord($_) } split //, $img; +for($i = 0; $i < @bytes - 7; $i++) { + my $found = 1; + for($j = 0; $j < @want; $j++) { + if($bytes[$i+$j] != $want[$j]) { + $found = 0; + } + } + if($found) { + printf "offset: %x\n", $i; + } +} + diff --git a/font b/font new file mode 100644 index 0000000..0bf5eed Binary files /dev/null and b/font differ diff --git a/fontrip.txt b/fontrip.txt new file mode 100644 index 0000000..0fa8fea --- /dev/null +++ b/fontrip.txt @@ -0,0 +1,41 @@ + +process of ripping the font was something like... + +look at a screenshot of the Apple 2 version, pick a byte that occurs on +2 successive lines in a couple of the characters (I chose the 10100000 +that shows up in capital C and O). + +write perl script to find occurrences of 2 of that byte (findfont.pl) +and print offsets of each one. + +use bitmapdump.pl to eyeball the file at those locations, until I found +the letter C, then look up & down to find where the visible characters +start and end. Use dd to grab just those bytes into another file. + +..except, the above was fruitless until I got the idea to search for the +byte with its bits reversed (00000101). Turns out, the font is stored +in the disk image with the bits reversed. Maybe if I knew the Apple +architecture, I'd have already known that? + +Anyway. To create an atari font, I mapped out the order the characters +appear in the dump, and wrote convfont.c to overlay them onto the Atari +ROM font (created from atariosb.rom with dd)... and for some reason, +a few of the characters have vertical bars in the rightmost position, +caused by bit 7 (in the original reversed format) being set. convfont.c +just zeroes those out as needed. + +The end result is taifont.xex, containing 1024 bytes of data to be +loaded at $BB00. + +To get it to work with a cc65 program, I had to create a linker script +(custom.cfg). Ran this: + +ld65 --dump-config atari > custom.cfg + +Then edited it... all I changed was the ending address, which is +now $B7FF (one byte below the font's load address). After compiling a +taipan.xex with this config, I append taifont.xex and taipan.xex to make +taipantext.xex (the finished game). + +The only thing the C code needs to do is POKE(756, 0xbb) in main() +before printing any text. diff --git a/fonttmp b/fonttmp new file mode 100644 index 0000000..9285c9c --- /dev/null +++ b/fonttmp @@ -0,0 +1,109 @@ + + X....X.. X....... ......X. ........ ..X..... ........ X..XX... ........ + X...X... X....... ......X. ........ ..X..... ........ X.X..X.. ........ + X..X.... X...XXX. ...XX.X. ...XXX.. ..X.XX.. ...XXX.. X....X.. ..X.XX.. + X....... X..X.... ..X..XX. ..X...X. ..XX..X. ..X...X. X..XXXXX ..XX..X. + X....... X..XXXX. ..X...X. ......X. ..X...X. ..XXXXX. X....X.. ..XX..X. + X....... X..X...X ..X..XX. ..X...X. ..XX..X. ......X. X....X.. ..X.XX.. + X....... X.X.XXX. ...XX.X. ...XXX.. ..X.XX.. ...XXX.. X....X.. ..X..... + X....... X....... ........ ........ ........ ........ X....... ...XXX.. + + ......X. ....X... ..X..... ......X. ....XX.. ........ ........ ........ + ......X. ........ ........ ......X. ....X... ........ ........ ........ + ...XX.X. ....XX.. ..XX.... ..X...X. ....X... ...XXXXX ...XX.X. ...XXX.. + ..X..XX. ....X... ..X..... ...X..X. ....X... ..X.X.X. ..X..XX. ..X...X. + ..X...X. ....X... ..X..... ....X.X. ....X... ..X.X.X. ..X...X. ..X...X. + ..X...X. ....X... ..X..... ...X.XX. ....X... ..X.X.X. ..X...X. ..X...X. + ..X...X. ...XXX.. ..X...X. ..X...X. ...XXX.. ..X.X.X. ..X...X. ...XXX.. + ........ ........ ...XXX.. ........ ........ ........ ........ ........ + + ........ ........ ........ ........ X....X.. ........ ........ ........ + ........ ........ ........ ........ X....X.. ........ ........ ........ + ...XX.X. ..X.XX.. ...XX.X. ..XXXX.. X..XXXXX ..X...X. ..X...X. ..X...X. + ..X..XX. ..XX..X. ..X..XX. ......X. X....X.. ..X...X. ..X...X. ..X.X.X. + ..X..XX. ..XX..X. ......X. ...XXX.. X....X.. ..X...X. ..X...X. ..X.X.X. + ...XX.X. ..X.XX.. ......X. ..X..... X.X..X.. ..XX..X. ...X.X.. ..X.X.X. + ......X. ..X..... ......X. ...XXXX. X..XX... ..X.XX.. ....X... ..XX.XX. + ......X. ..X..... ........ ........ X....... ........ ........ ........ + + ........ ........ ........ X.XXX... ....X... X...XXX. ...X.X.. XXXXXXXX + ........ ........ ........ X....X.. ....X... X..X.... ....X.X. XXXXXXXX + ..X...X. ..X...X. ..XXXXX. X....X.. ....X... X..X.... ........ XXXXXXXX + ...X.X.. ..X...X. ...X.... X.....X. ....X... X.X..... ........ XXXXXXXX + ....X... ..X...X. ....X... X....X.. ....X... X..X.... ........ XXXXXXXX + ...X.X.. ..XXXX.. .....X.. X....X.. ....X... X..X.... ........ XXXXXXXX + ..X...X. ..X..... ..XXXXX. X.XXX... ....X... X...XXX. ........ XXXXXXXX + ........ ...XXX.. ........ X....... ....X... X....... ........ XXXXXXXX + + ...XXX.. .....XX. ...XXX.X ...XXX.. ....XX.X ...XXX.X ...XXX.X ...XXX.. + ..X...X. ....X..X ..X...X. ..X...X. ...X..XX ..X...X. ..X...X. ..X...X. + ..X.X.X. ...X.X.. ..X....X ...X.X.X ..X....X .......X ......X. .......X + ..XX.XX. ..X...X. ...XXXXX .....X.X ..X....X ....XXXX ....XXXX ..XXXX.X + ...XX.X. ..XXXXX. ..X....X .....X.X ..X....X .......X ......X. ..X...XX + ......X. ..X...X. ..X...X. ..X...X. ...X..XX ..X...X. ......X. ..XXXXX. + ..XXXX.. .X.....X ...XXX.X ...XXX.. ....XX.X ...XXX.X .......X ..X..... + ........ ........ ........ ........ ........ ........ ........ ..XX.... + + .X.....X .X...... .X...... ..X....X ....XX.. ...X.X.X .X.....X ...XXX.. + ..X...X. ..XXXXX. ..X..... ...X..X. ...X..X. ..X.X.X. ..X..XX. ..X...X. + ..X...X. ....X..X ..X..... ....X.X. ......X. ..X.X.X. ..X.X.X. .X...X.X + ..XXXXX. ....X... ..X..... .....XXX ......X. ..X.X.XX ..X.X.XX .X...X.X + ..X...X. .X..X... ..X...X. ....X.X. ......X. ..X.X.X. ..X.X.X. .X...X.X + ..X...X. ..XXXXX. ...X...X ...X..X. .X....X. ..X.X.X. ..X.X.X. ..X...X. + .X.....X .......X ....XXX. ..X....X ..XXXX.X .......X ...X...X ...XXX.. + ........ ........ ........ ........ ........ ........ ........ ........ + + ...XXX.X ...XXX.. ...XXX.X .X...... .X...... .X....X. ..XX..X. .......X + ..X...X. ..X...X. ..X...X. ..XXXXX. ..XXXXXX ..X..X.X .X..XX.X ..XXX.X. + ..X...X. .X...X.X ..X...X. .......X .....XX. ..X..X.. .X...X.. .X..X.X. + ...XXXXX .X...X.X ...XXXXX ..XXXXX. .....X.X ..X..X.. .X...X.. .X..X.X. + ......X. .X.X.X.X ...X..X. .X...... .....X.X ..X..X.. .X...X.. .X..X.X. + ......X. ..X...X. ..X...X. ..XXXXX. .X....X. ..X..X.. ..X.X... ..X.X.X. + .......X .X.XXX.. ..X....X .......X ..XXXX.. ...XX... ...X.... ...X.X.. + ........ .X...... ........ ........ ........ ........ ........ ........ + + .X....X. .X....X. ..XXXXX. ...XXXX. ........ X..XXXX. ....X... X....... + ..X..X.X ..X..X.X ..X..... ......X. .......X X..X.... ...XXX.. X....... + ...XX... ..X..X.. ...X.... ......X. ......X. X..X.... ..X.X.X. X....... + ....X... ..X..X.. ....X... ......X. .....X.. X..X.... ....X... X....... + ....XX.. .X.XX... .....X.. ......X. ....X... X..X.... ....X... X....... + .X.X..X. .X.....X ......X. ......X. ...X.... X..X.... ....X... X....... + ..X....X ..XXXXX. ..XXXXX. ...XXXX. ..X..... X..XXXX. ....X... X....... + ........ ........ ........ ........ ........ X....... ........ XXXXXXXX + + ........ ....X... ..XX.XX. X..X.X.. ....X... ........ X...X... ....XX.. + ........ ....X... ..XX.XX. X..X.X.. ..XXXX.. ..X...XX X..X.X.. ....XX.. + ........ ....X... ..XX.XX. X.XXXXX. ....X.X. ...X..XX X..X.X.. ....XX.. + ........ ....X... ........ X..X.X.. ...XXX.. ....X... X....X.. ........ + ........ ........ ........ X.XXXXX. ..X.X... .....X.. X...X.X. ........ + ........ ........ ........ X..X.X.. ...XXXX. ..XX..X. X..X..X. ........ + ........ ....X... ........ X..X.X.. ....X... ..XX...X X.X.XX.. ........ + ........ ........ ........ X....... ........ ........ X....... ........ + + X..X.... X....X.. ....X... ........ ........ ........ ........ ........ + X...X... X...X... ..X.X.X. ....X... ........ ........ ........ ..X..... + X....X.. X..X.... ...XXX.. ....X... ........ ........ ........ ...X.... + X....X.. X..X.... ..XXXXX. ..XXXXX. ........ ..XXXXX. ........ ....X... + X....X.. X..X.... ...XXX.. ....X... ........ ........ ........ .....X.. + X...X... X...X... ..X.X.X. ....X... ....X... ........ ....XX.. ......X. + X..X.... X....X.. ....X... ........ ....X... ........ ....XX.. .......X + X....... X....... ........ ........ .....X.. ........ ........ ........ + + ........ ........ ........ ........ ........ ........ ........ ........ + ...XXX.. ....X... ...XXX.. ...XXX.. ...X..X. ..XXXXX. ...XXX.. ..XXXXX. + ..X...X. ....XX.. ..X...X. ..X...X. ...X..X. ......X. ......X. ..X...X. + ..X...X. ....X... ..XX.... ...XX... ...X..X. ...XXXX. ...XXXX. ...X.... + ..X...X. ....X... ....XX.. ..X..... ..XXXXX. ..X..... ..X...X. ....X... + ..X...X. ....X... ......X. ..X...X. ...X.... ..X...X. ..X...X. ....X... + ...XXX.. ..XXXXX. ..XXXXX. ...XXX.. ...X.... ...XXX.. ...XXX.. ....X... + ........ ........ ........ ........ ........ ........ ........ ........ + + ........ ........ ........ ........ ..XX.... ........ .....XX. ...XXX.. + ...XXX.. ...XXX.. ........ ........ ....X... ........ ....X... ..X...X. + ..X...X. ..X...X. ....X... ....X... .....X.. ..XXXXX. ...X.... ..X..... + ...XXX.. ..XXXX.. ........ ........ ......X. ........ ..X..... ...X.... + ..X...X. ..X..... ........ ........ .....X.. ..XXXXX. ...X.... ....X... + ..X...X. ...X.... ....X... ....X... ....X... ........ ....X... ........ + ...XXX.. ....XX.. ........ ....X... ..XX.... ........ .....XX. ....X... + ........ ........ ........ .....X.. ........ ........ ........ ........ + diff --git a/jsleep.s b/jsleep.s new file mode 100644 index 0000000..e052ddd --- /dev/null +++ b/jsleep.s @@ -0,0 +1,11 @@ + + .export _jsleep + .import _set_jiffy_timer + +_jsleep: + jsr _set_jiffy_timer +wait: + lda 540 + ora 541 + bne wait + rts diff --git a/lorcha.c b/lorcha.c new file mode 100644 index 0000000..156c735 --- /dev/null +++ b/lorcha.c @@ -0,0 +1,9 @@ +char lorcha[] = { + 0x20, 0x20, 0x01, 0x3d, 0x20, 0x01, 0x3d, + 0x0b, 0xa0, 0xa0, 0x8b, 0x0b, 0xa0, 0x0f, + 0x0b, 0xa0, 0xa0, 0xa0, 0x0b, 0xa0, 0x0f, + 0x8a, 0xa0, 0xa0, 0x88, 0x0b, 0xa0, 0x20, + 0x0a, 0x20, 0x7c, 0x20, 0x20, 0x7c, 0x08, + 0xa0, 0x94, 0x94, 0x94, 0x94, 0x94, 0xa0, + 0x8a, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x88, +} diff --git a/lorcha.pl b/lorcha.pl new file mode 100644 index 0000000..8d09a71 --- /dev/null +++ b/lorcha.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl -w + +# LORCHA.LST is a LISTed BASIC program that draws a lorcha +# and spits out the screen data to H:LORCHA.DAT (for which +# you will need atari800's H: device pointed at the current +# directory). + +# Usage: +# atari800 -basic LORCHA.LST +# perl lorcha.pl LORCHA.DAT > lorcha_data.inc + +use bytes; +undef $/; +$data = <>; +print "lorcha_data:\n"; +for(0..6) { + print " .byte "; + print join ", ", map { sprintf '$%02x', ord $_ } split "", substr($data, $_ * 6, 7); + print "\n"; +} diff --git a/lorchatest.c b/lorchatest.c new file mode 100644 index 0000000..bc53d07 --- /dev/null +++ b/lorchatest.c @@ -0,0 +1,37 @@ +#include + +extern void __fastcall__ draw_lorcha(int which, int displacement, int mask); + +void jsleep(int jiffies) { + POKE(20,0); + while(PEEK(20) < jiffies) + ; +} + +int main(void) { + int i, j; + + /* draw all 10 ships in normal state */ + for(i=0; i<10; i++) { + draw_lorcha(i, 0, 0); + } + + /* explode and sink all 10 ships */ + for(i=0; i<10; i++) { + /* blast effect */ + for(j=0; j<8; j++) { + draw_lorcha(i, 0, 0x80); + jsleep(2); + draw_lorcha(i, 0, 0); + jsleep(2); + } + + /* sinking */ + for(j=0; j<8; j++) { + draw_lorcha(i, j, 0); + jsleep(4); + } + } + +hang: goto hang; +} diff --git a/oldcurses.c b/oldcurses.c new file mode 100644 index 0000000..44ff6b3 --- /dev/null +++ b/oldcurses.c @@ -0,0 +1,138 @@ +/* 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); +} + +/* 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/rand.s b/rand.s new file mode 100644 index 0000000..c48478e --- /dev/null +++ b/rand.s @@ -0,0 +1,23 @@ + + .export _randi, _randl + .importzp sreg + +RANDOM = 53770 ; POKEY LFSR read address, defined in the Atari OS + +; void __fastcall__ randi(void); +_randi: + lda #0 + sta sreg + beq randl1 + +; void __fastcall__ randl(void); +_randl: + lda RANDOM +randl1: + sta sreg + lda RANDOM + sta sreg+1 + lda RANDOM + ldx RANDOM + rts + diff --git a/romfont b/romfont new file mode 100644 index 0000000..2e680b2 Binary files /dev/null and b/romfont differ diff --git a/taifont b/taifont new file mode 100644 index 0000000..2da7e59 Binary files /dev/null and b/taifont differ diff --git a/taipan-applesoft.txt b/taipan-applesoft.txt new file mode 100644 index 0000000..5cf95aa --- /dev/null +++ b/taipan-applesoft.txt @@ -0,0 +1,961 @@ + +]LIST + + 10 CLEAR :WK$ = "*":CH$ = "*":C + H% = 0:WU% = 0:R1% = 0:I = 0 + :J = 0:K = 0:II = 0:IJ = 0:I + K = 0:T = 300:LT = LOG (10) + :T$ = "Taipan": GOTO 10000 + 90 REM + 92 FOR II = 1 TO T:II = II + ( PEEK + ( - 16384) > 127) * 9999: NEXT + II + 94 FOR II = 1 TO T / 2:II = II + + ( PEEK ( - 16384) > 127) * 9 + 999: NEXT II + 96 FOR II = 1 TO T / 2:II = II + + ( PEEK ( - 16384) > 127) * 9 + 999: NEXT II + 98 POKE - 16368,0: RETURN + 100 CALL 2560: RETURN + 150 WK$ = "" + " ": CALL + 2680:W = VAL (WK$):R1% = LEFT$ + (WK$,1) = "A": RETURN + 200 REM + 210 PRINT FS$;HM$;CS$; SPC( 12 - + LEN (H$) / 2): PRINT "Firm: + ";CA$;H$;CS$;", ";:X = USR + (1): PRINT + 220 VTAB 2: PRINT CG$;"[";: & 4 + 5,26: PRINT "]": FOR II = 1 TO + 5: PRINT "!"; TAB( 28);"!": NEXT + II: PRINT "(";: & 61,26: PRINT + ")": FOR II = 1 TO 5: PRINT + "!"; TAB( 28);"!": NEXT II: PRINT + "<";: & 58,26: PRINT ">";CS$ + + 230 VTAB 3: HTAB 2:X = USR (1) + + USR (2): VTAB 4: HTAB 21 + : PRINT "In use:": VTAB 6: HTAB + 21: PRINT "Vacant:": VTAB 9: + HTAB 2: PRINT "Hold + Guns"; + 240 FOR II = 3 TO 9 STEP 6: FOR + IJ = 1 TO 4: VTAB II + IJ: HTAB + 5: PRINT LEFT$ (CO$(IJ),7); + : NEXT IJ,II + 250 VTAB 3: HTAB 33: PRINT "Dat + e": VTAB 6: HTAB 31:X = USR + (3): VTAB 9: HTAB 33: PRINT + "Debt": VTAB 12: HTAB 29: PRINT + " Ship status": + 260 VTAB 16: HTAB 1: PRINT CG$; + : & 45,40: PRINT CS$ + 270 RETURN + 300 REM + 310 VTAB 4: HTAB 30: PRINT "15 + ";YE: VTAB 4: HTAB 33: PRINT + IV$; MID$ ("JanFebMarAprMayJ + unJulAugSepOctNovDec",(MO - + 1) * 3 + 1,3);NV$ + 311 VTAB 7: HTAB 31: PRINT " + ": VTAB 7: HTAB 35 - LEN + (LO$(LO)) / 2 + .5: PRINT IV + $;LO$(LO);NV$ + 312 VTAB 10: HTAB 29: PRINT " + ": VTAB 10:WW = DW + : GOSUB 600: HTAB 35 - LEN + (WW$) / 2: PRINT IV$;WW$;NV$ + + 313 WW = 100 - INT (DM / SC * 1 + 00 + .5):WW = WW * (WW > 0): + W = INT (WW / 20): VTAB 13: + HTAB 30: IF W < 2 THEN PRINT + IV$; + 314 PRINT ST$(W);":";WW;: IF PEEK + (36) > 30 THEN PRINT TAB( + 40);" "; + 315 PRINT NV$; + 316 VTAB 5: HTAB 22: PRINT " + ";: HTAB 22: PRINT WS: VTAB + 7: HTAB 22: PRINT " ";: HTAB + 22: PRINT WC - WS + 320 POKE 32,12: FOR II = 1 TO 2 + : POKE 33,(II - 1) * 9 + 6:I + K = II * 6 - 3: POKE 34,IK: POKE + 35,IK + 4: PRINT HM$: FOR IJ + = 1 TO 4: VTAB IK + IJ: HTAB + 1: PRINT ST(II,IJ);: NEXT IJ + ,II + 330 PRINT FS$: VTAB 15: HTAB 1: + WW = CA: GOSUB 600: PRINT "C + ash:";WW$; TAB( 21);:WW = BA + : GOSUB 600: PRINT "Bank:";W + W$; TAB( 40);" ": VTAB 9: HTAB + 22: PRINT GN;: HTAB 7: PRINT + " ";: HTAB 7 + 340 IF MW < 0 THEN PRINT IV$;" + Overload";NV$ + 350 IF MW > = 0 THEN PRINT MW + ; + 360 RETURN + 400 REM + 410 POKE 32,0: POKE 33,40: POKE + 34,18: POKE 35,24: PRINT HM$ + ;: RETURN + 480 VTAB 17: HTAB 1:X = USR (4 + ): RETURN + 490 VTAB 17: HTAB 1:X = USR (5 + ): RETURN + 500 REM + 510 GOSUB 400:X = USR (6): GOSUB + 150: IF R1% THEN W = CA + 530 IF CA > = W THEN CA = CA - + W:BA = BA + W: GOSUB 300: GOTO + 550 + 540 PRINT : PRINT : PRINT T$;:X + = USR (8): PRINT CA: PRINT + "in cash.": CALL 2518: GOSUB + 94: GOTO 510 + 550 GOSUB 400:X = USR (7): GOSUB + 150: IF R1% THEN W = BA + 570 IF BA > = W THEN BA = BA - + W:CA = CA + W: GOSUB 300: GOTO + 590 + 580 PRINT : PRINT : PRINT T$;:X + = USR (8): PRINT BA: PRINT + "in the bank.": CALL 2518: GOSUB + 94: GOTO 550 + 590 RETURN + 600 IF WW < 1E6 THEN WW$ = STR$ + ( INT (WW)): RETURN + 610 II = INT ( LOG (WW) / LT):I + J = INT (II / 3) * 3:IK = 1 + 0 ^ (II - 2):WW$ = LEFT$ ( STR$ + ( INT (WW / IK + .5) * IK / + 10 ^ IJ),4) + " " + 620 IF IJ = 3 THEN W$ = "Thousa + nd" + 630 IF IJ = 6 THEN W$ = "Millio + n" + 640 IF IJ = 9 THEN W$ = "Billio + n" + 650 IF IJ = 12 THEN W$ = "Trill + ion" + 680 WW$ = WW$ + W$ + 690 RETURN + 1000 REM + 1010 IF D < > 0 THEN GOSUB 49 + 0: GOSUB 400:X = USR (9): PRINT + LO$(D): GOSUB 96:BA = INT ( + BA + BA * .005):DW = INT (D + W + DW * .1):TI = TI + 1:MO = + MO + 1:LO = D + 1020 IF MO > 12 THEN YE = YE + + 1:MO = 1:EC = EC + 10:ED = E + D + .5: FOR I = 1 TO 7: FOR + J = 1 TO 4:BP%(I,J) = BP%(I, + J) + FN R(2): NEXT J,I + 1030 GOSUB 400: GOSUB 480: GOSUB + 300: IF LO < > 1 THEN 1500 + 1040 IF LI < > 0 OR CA = 0 THEN + 1120 + 1050 WW = 0:W = 1.8: IF TI > 12 THEN + WW = FN R(1000 * TI) + 1000 + * TI:W = 1 + 1060 I = FN R(CA / W) + WW:WW = + I: GOSUB 600: GOSUB 400:X = + USR (10): PRINT WW$;" ";:X = + USR (11):CH$ = "NY": GOSUB + 100: IF CH% < > 2 THEN 1120 + + 1065 LI = 1:CA = CA - I: IF CA > + 0 THEN 1100 + 1070 GOSUB 400: PRINT T$;:X = USR + (12): CALL 2512: PRINT : PRINT + :X = USR (13):CH$ = "YN": GOSUB + 100 + 1080 IF CH% = 1 THEN DW = DW - + CA:CA = 0: GOSUB 400:X = USR + (14): CALL 2521: GOSUB 94 + 1090 IF CH% = 2 THEN CA = 0:LI = + 0: GOSUB 400:X = USR (15): PRINT + T$;".": CALL 2518: GOSUB 94 + 1100 GOSUB 300 + 1120 IF DM = 0 THEN 1210 + 1130 GOSUB 400: PRINT T$;:X = USR + (16):CH$ = "YN": GOSUB 100: IF + CH% = 2 THEN 1210 + 1140 BR = INT (( FN R(60 * (TI + + 3) / 4) + 25 * (TI + 3) / 4) + * SC / 50) + 1142 WW = INT (DM / SC * 100 + + .5) + 1145 GOSUB 400:X = USR (17): PRINT + WW;"% damaged.": PRINT :WW = + BR * DM + 1: GOSUB 600:X = USR + (18): PRINT WW$;"," + 1150 X = USR (19): GOSUB 150: IF + R1% = 1 THEN W = BR * DM + 1 + : IF CA < W THEN W = CA + 1155 IF CA < W THEN GOSUB 400: + PRINT T$;:X = USR (12): GOSUB + 96: GOTO 1142 + 1160 WW = INT (W / BR + .5):DM = + DM - WW:CA = CA - W:DM = INT + (DM * (DM > 0)): GOSUB 300: GOSUB + 400 + 1210 IF DW < 10000 OR WN OR D = + 0 THEN 1300 + 1220 GOSUB 400: PRINT "Elder Br + other Wu has sent "; FN R(10 + 0) + 50;" braves": PRINT "to + escort you to the Wu mansio + n, ";T$;".":WN = 1: GOSUB 94 + + 1230 GOSUB 400:X = USR (20): GOSUB + 92 + 1240 GOSUB 400:X = USR (21): PRINT + T$;".";: GOSUB 92 + 1300 REM + 1310 GOSUB 400:X = USR (22):CH + $ = "NY":WU% = 1: GOSUB 100: + WU% = 0: IF CH% < > 2 THEN + 1500 + 1320 W = 0: FOR I = 1 TO 2: FOR + J = 1 TO 4:W = W + ST(I,J): NEXT + J,I: IF CA OR BA OR W OR GN THEN + 1360 + 1330 BL% = BL% + 1:I = INT ( FN + R(1500) + 500):J = FN R(200 + 0) * BL% + 1500: GOSUB 400: PRINT + "Elder Brother is aware of y + our plight, ";T$;". He is + willing to loan you an add + itional ";I;" if you will pa + y back" + 1340 PRINT J;". Are you willing + , ";T$;"? ";:CH$ = "YN": GOSUB + 100: IF CH% = 2 THEN GOSUB + 400: PRINT : PRINT "Very wel + l, Taipan, the game is over! + ": CALL 2512: GOTO 2698 + 1350 CA = CA + I:DW = DW + J: GOSUB + 400: PRINT "Very well, ";T$; + ". Good joss!!": CALL 2521: + GOSUB 300: GOSUB 96: GOTO 1 + 500 + 1360 IF DW = 0 OR CA = 0 THEN 1 + 400 + 1370 GOSUB 400:X = USR (23): GOSUB + 150: IF R1% THEN W = CA: IF + CA > DW THEN W = DW + 1380 IF CA > = W THEN CA = CA - + W:DW = DW - W: GOSUB 300: GOTO + 1400 + 1390 PRINT : PRINT : PRINT T$;" + , you have only ";CA: PRINT + "in cash.": CALL 2518: GOSUB + 94: GOTO 1370 + 1400 GOSUB 400:X = USR (24): GOSUB + 150: IF R1% THEN W = 2 * CA + 1420 IF CA * 2 > = W THEN CA = + CA + W:DW = DW + W: GOSUB 30 + 0: GOTO 1450 + 1430 PRINT : PRINT : PRINT "He + won't loan you so much, ";T$ + ;"!": CALL 2518: GOSUB 94: GOTO + 1400 + 1450 REM + 1460 IF DW > 20000 AND NOT ( FN + R(5)) THEN GOSUB 400: PRINT + "Bad joss!!": PRINT FN R(3) + + 1;" of your bodyguards ha + ve been killed": PRINT "by c + utthroats and you have been + robbed of all your cash, "; + T$;"!!": CALL 2512:CA = 0: GOSUB + 300: GOSUB 94 + 1500 REM + 1610 I = INT (1000 + FN R(1000 + * (TI + 5) / 6)) * ( INT (S + C / 50) * (DM > 0) + 1): IF + CA < I OR FN R(4) THEN 1700 + + 1615 W$ = CHR$ (15) + CHR$ (15 + ) + "damaged_______" + CHR$ (15) + + CHR$ (16) + "fine":WW = I: GOSUB + 600 + 1620 GOSUB 400: PRINT "Do you w + ish to trade in your "; MID$ + (W$,(DM = 0) * 25 + 1,25): PRINT + "ship for one with 50 more c + apacity by paying an addit + ional ";WW$;", ";T$;"? "; + 1630 CH$ = "YN": GOSUB 100: IF C + H% = 1 THEN CA = CA - I:MW = + MW + 50:SC = SC + 50:DM = 0: + GOSUB 300 + 1700 REM + 1710 I = INT ( FN R(1000 * (TI + + 5) / 6) + 500): IF CA < I OR + FN R(3) THEN 1900 + 1720 WW = I: GOSUB 600: GOSUB 40 + 0: PRINT "Do you wish to buy + a ship's gun": PRINT "for " + ;WW$;", ";T$;"? ";:CH$ = "NY + ": GOSUB 100: IF CH% = 1 THEN + 1900 + 1730 IF MW > = 10 THEN CA = CA + - I:GN = GN + 1:MW = MW - 1 + 0: GOSUB 300: GOTO 1900 + 1740 PRINT : PRINT : PRINT "You + r ship would be overburdened + , ";T$;"!": CALL 2518: GOSUB + 94 + 1900 IF ST(2,1) = 0 OR LO = 1 OR + FN R(18) THEN 2000 + 1910 I = FN R(CA / 1.8):WW = I: + GOSUB 600: GOSUB 400: CALL + 2512:X = USR (25) + USR (2 + 6): PRINT WW$;", ";T$;"!":MW + = MW + ST(2,1):ST(2,1) = 0: + CA = CA - I: GOSUB 300: GOSUB + 94 + 2000 W = 0: FOR J = 1 TO 4:W = W + + ST(1,J): NEXT J: IF W = 0 + OR FN R(50) THEN 2100 + 2030 GOSUB 400: CALL 2512:X = USR + (25) + USR (27): PRINT T$;" + !": FOR J = 1 TO 4:W = ST(1, + J):WW = FN R(W / 1.8):WS = + WS - W + WW:ST(1,J) = WW: NEXT + J: GOSUB 300: GOSUB 96 + 2100 FOR I = 1 TO 4:CP(I) = BP% + (LO,I) / 2 * ( FN R(3) + 1) * + 10 ^ (4 - I): NEXT I + 2310 LI = LI AND FN R(20): IF L + I = 0 AND LI% > 0 THEN LI% = + LI% + 1: IF LI% > 4 THEN LI% + = 0 + 2330 IF LI = 0 AND LO < > 1 AND + FN R(4) THEN GOSUB 400:X = + USR (28): CALL 2521: GOSUB + 94 + 2410 IF FN R(9) THEN 2500 + 2420 GOSUB 400:I = FN R(4) + 1 + :J = FN R(2):K = FN R(2) * + 5: PRINT T$;"!! The price o + f ";CO$(I) + 2430 IF J = 0 THEN CP(I) = INT + (CP(I) / 5): PRINT "has drop + ped to ";CP(I);"!!": CALL 25 + 18 + 2440 IF J = 1 THEN CP(I) = CP(I + ) * ( FN R(5) + 5):WW = CP(I + ): GOSUB 600: PRINT "has ris + en to ";WW$;"!!": CALL 2518 + 2450 GOSUB 94 + 2500 REM + 2501 GOSUB 400: IF CA > 25000 AND + NOT ( FN R(20)) THEN I = FN + R(CA / 1.4):WW = I: GOSUB 60 + 0:X = USR (25): PRINT "You' + ve been beaten up and robbed + of": PRINT WW$;" in cash, " + ;T$;"!!": CALL 2512:CA = CA - + I: GOSUB 300: GOSUB 94: VTAB + 22: HTAB 1: PRINT CE$ + 2510 GOSUB 400: PRINT T$;:X = USR + (29) + 2515 FOR I = 1 TO 3 STEP 2: PRINT + TAB( 4); LEFT$ (CO$(I),7);" + : ";CP(I); TAB( 18); LEFT$ ( + CO$(I + 1),7);": ";CP(I + 1) + : NEXT I + 2520 I = CA + BA - DW: VTAB 22: HTAB + 1: PRINT CE$ + 2522 IF LO < > 1 THEN X = USR + (30):CH$ = "BSQ" + 2524 IF LO = 1 AND I < 1E6 THEN + X = USR (31) + USR (32):CH + $ = "BSQTV" + 2526 IF LO = 1 AND I > = 1E6 THEN + X = USR (31) + USR (33):CH + $ = "BSQTVR" + 2528 GOSUB 100: ON CH% GOTO 253 + 0,2570,2700,2620,2680,2695 + 2530 VTAB 23: HTAB 1: PRINT CE$ + ;"What do you wish me to buy + , ";T$;"? ";:CH$ = "OSAG": GOSUB + 100:CO$ = CO$(CH%):CP = CP(C + H%) + 2540 VTAB 22: HTAB 1: PRINT CE$ + ,IV$;: HTAB 31: PRINT " You + can ";: VTAB 23: HTAB 31: PRINT + " afford ";: VTAB 24: HTAB + 31: PRINT " ";:W = INT + (CA / CP): IF W > 1E9 THEN W + = 1E9 - 1 + 2542 HTAB 36 - LEN ( STR$ (W)) + / 2: PRINT W;NV$;: VTAB 23: + HTAB 1: PRINT "How much ";C + O$;" shall": PRINT "I buy, " + ;T$;"? ";: GOSUB 150: IF R1% + THEN W = INT (CA / CP): IF + W > 1E9 THEN W = 1E9 - 1 + 2550 IF W < 0 OR CA < W * CP THEN + CALL 2524: GOTO 2540 + 2560 MW = MW - W:CA = CA - W * C + P:ST(2,CH%) = ST(2,CH%) + W: + GOSUB 300: VTAB 22: HTAB 1: + CALL - 958: GOTO 2520 + 2570 VTAB 23: HTAB 1: PRINT CE$ + ;"What do you wish me to sel + l, ";T$;"? ";:CH$ = "OSAG": GOSUB + 100:CO$ = CO$(CH%):CP = CP(C + H%) + 2580 VTAB 22: HTAB 1: PRINT CE$ + : PRINT "How much ";CO$;" sh + all": PRINT "I sell, ";T$;"? + ";: GOSUB 150: IF R1% THEN + W = ST(2,CH%) + 2590 IF W < 0 OR ST(2,CH%) < W THEN + CALL 2524: GOTO 2580 + 2600 MW = MW + W:CA = CA + W * C + P:ST(2,CH%) = ST(2,CH%) - W: + GOSUB 300: VTAB 22: HTAB 1: + PRINT CE$;: GOTO 2520 + 2620 REM + 2622 W = 0: FOR I = 1 TO 2: FOR + J = 1 TO 4:W = W + ST(I,J): NEXT + J,I: IF W = 0 THEN VTAB 22: + HTAB 1: PRINT CE$;"You have + no cargo, ";T$;".": CALL 25 + 18: GOSUB 94: GOTO 2520 + 2624 FOR J = 1 TO 4: FOR K = 1 TO + 2:I = 3 - K: IF ST(I,J) = 0 THEN + 2634 + 2626 GOSUB 400: PRINT "How much + ";CO$(J);" shall I move": PRINT + MID$ ("to the warehouseaboa + rd ship",K * 16 - 15,16);", + ";T$;"? ";: GOSUB 150: IF R1 + % THEN W = ST(I,J): IF W > ( + WC - WS) AND K = 1 THEN W = + (WC - WS) + 2627 IF K = 2 THEN 2630 + 2628 IF W > 0 AND WS = WC THEN + PRINT : PRINT : PRINT "Your + warehouse is full, ";T$;"!" + : CALL 2518: GOSUB 94: GOTO + 2626 + 2629 IF W > (WC - WS) THEN PRINT + : PRINT : PRINT "Your wareho + use will only hold an": PRINT + "additional ";WC - WS;", ";T + $;"!";: CALL 2518: GOSUB 94: + GOTO 2626 + 2630 IF W > ST(I,J) THEN PRINT + : PRINT : PRINT "You have on + ly ";ST(I,J);", ";T$;".": CALL + 2518: GOSUB 94: GOTO 2626 + 2632 ST(I,J) = ST(I,J) - W:ST(K, + J) = ST(K,J) + W:MW = MW + SGN + (I - K) * W:WS = WS + SGN ( + I - K) * W: GOSUB 300 + 2634 NEXT K,J: GOTO 2500 + 2680 REM + 2690 GOSUB 500: GOTO 2500 + 2695 OK = 16 + 2696 GOSUB 400: PRINT IV$; TAB( + 26): PRINT : PRINT " Y o u ' + r e a"; TAB( 26): PRINT + : PRINT TAB( 26): PRINT : PRINT + " M I L L I O N A I R E ! ": + PRINT TAB( 26): PRINT NV$: + GOSUB 96 + 2698 : GOSUB 20000 + 2699 PRINT "Play again? ";:CH$ = + "NY": GOSUB 100: ON CH% GOTO + 63999: RUN + 2700 REM + 2810 IF MW < 0 THEN GOSUB 400: + PRINT "You're ship is overl + oaded, ";T$;"!!": CALL 2518: + GOSUB 94: GOTO 2500 + 3010 GOSUB 400: PRINT T$;", do + you wish to go to:": PRINT " + 1) Hong Kong, 2) Shanghai, 3 + ) Nagasaki, 4) Saigon, 5) Ma + nila, 6) Singapore, or 7) B + atavia ? "; + 3020 CH$ = "1234567": GOSUB 100: + D = CH%: IF D = LO THEN PRINT + : PRINT : PRINT "You're alre + ady here, ";T$;".";: CALL 25 + 18: GOSUB 94: GOTO 3010 + 3030 LO = 0: GOSUB 300: GOSUB 40 + 0: GOSUB 490 + 3100 REM + 3110 IF FN R(BP) THEN 3200 + 3120 SN = FN R(SC / 10 + GN) + + 1: GOSUB 400: CALL 2512: PRINT + SN;" hostile ship"; MID$ ("s + ",(SN = 1) + 1,1);" approach + ing, ";T$;"!!": GOSUB 96:F1 = + 1: GOTO 5000 + 3200 REM + 3210 IF FN R(4 + 8 * LI) THEN + 3300 + 3220 GOSUB 400: PRINT "Li Yuen' + s pirates, ";T$;"!!": CALL 2 + 521: GOSUB 94: IF LI THEN PRINT + : PRINT "Good joss!! They le + t us be!!": CALL 2521: GOSUB + 94: GOTO 3300 + 3230 SN = FN R(SC / 5 + GN) + 5 + : GOSUB 400: PRINT SN;" ship + s of Li Yuen's pirate": PRINT + "fleet, ";T$;"!!": CALL 2512 + : GOSUB 94:F1 = 2: GOTO 5000 + + 3300 REM + 3310 IF FN R(10) THEN 3350 + 3320 GOSUB 400: PRINT "Storm, " + ;T$;"!!": CALL 2521: GOSUB 9 + 4: IF NOT ( FN R(30)) THEN + PRINT : PRINT " I think w + e're going down!!": CALL 252 + 1: GOSUB 94: IF FN R(DM / S + C * 3) THEN PRINT : PRINT " + We're going down, Taipan!!": + CALL 2512:OK = 1: GOTO 2698 + + 3330 PRINT : PRINT " We made + it!!": CALL 2521: GOSUB 94: + IF FN R(3) THEN 3350 + 3340 LO = FN R(7) + 1: ON (LO = + D) GOTO 3340: GOSUB 400: PRINT + "We've been blown off course + ": PRINT "to ";LO$(LO):D = L + O: GOSUB 94 + 3350 LO = D: GOTO 1000 + 5000 REM + 5030 LC = 0:CMD = 0: PRINT FS$;H + M$ + 5050 VTAB 1: HTAB 1: PRINT " + ships attacking, ";T$;"!": + VTAB 1: HTAB 32: PRINT CG$; + "!": VTAB 2: HTAB 32: PRINT + "!": VTAB 3: HTAB 32: PRINT + "<::::::::";CS$: VTAB 2: HTAB + 37: PRINT "guns": VTAB 1: HTAB + 34: PRINT "We have"; + 5060 PRINT "Your orders are to: + " + 5080 FOR I = 0 TO 9:AM%(I,0) = + 0:AM%(I,1) = 0: NEXT I:SA = + SN:S0 = SN:BT = FN R(TI / 4 + * 1000 * SN ^ 1.05) + FN R + (1000) + 250:SS = 0 + 5090 REM + 5100 GOSUB 5760: GOSUB 5700:LC = + CMD: VTAB 12: HTAB 40: PRINT + MID$ ("+ ", NOT (SA) + 1,1) + + 5160 DM = INT (DM):WW = 100 - INT + (DM / SC * 100): IF WW < 0 THEN + WW = 0 + 5162 VTAB 4: PRINT "Current sea + worthiness: ";ST$( INT (WW / + 20));" (";WW;"%)": GOSUB 560 + 0: VTAB 4: PRINT CL$ + 5165 IF WW = 0 THEN OK = 0: GOTO + 5900 + 5175 GOSUB 5600 + 5180 ON CMD GOTO 5200,5300,5400 + + 5190 VTAB 4: PRINT T$;", what s + hall we do??": CALL 2512: GOSUB + 5600: ON (CMD = 0) + 1 GOTO + 5500,5180 + 5200 REM + 5205 VTAB 4: HTAB 1: PRINT CL$: + VTAB 4: PRINT "Aye, we'll r + un, ";T$;"!": GOSUB 96: VTAB + 4: PRINT CL$ + 5207 IF LC = 1 OR LC = 3 THEN O + K = OK + IK:IK = IK + 1 + 5208 IF LC = 0 OR LC = 2 THEN O + K = 3:IK = 1 + 5210 IF FN R(OK) > FN R(SN) THEN + VTAB 4: PRINT "We got away + from 'em, ";T$;"!!": CALL 25 + 18: GOSUB 96: VTAB 4: PRINT + CL$:OK = 3: GOTO 5900 + 5220 VTAB 4: PRINT "Can't lose + 'em!!": GOSUB 5600: VTAB 4: PRINT + CL$ + 5230 IF SN > 2 AND FN R(5) = 0 + THEN W = FN R(SN / 2) + 1: + SN = SN - W:SA = SA - W: GOSUB + 5680: GOSUB 5750: VTAB 4: PRINT + "But we escaped from ";W;" o + f 'em, ";T$;"!": GOSUB 5600: + VTAB 4: PRINT CL$ + 5240 GOTO 5500 + 5300 REM + 5302 IF GN = 0 THEN VTAB 4: HTAB + 1: PRINT "We have no guns, " + ;T$;"!!": GOSUB 5600: VTAB 4 + : PRINT CL$: GOTO 5500 + 5305 VTAB 4: HTAB 1: PRINT CL$: + VTAB 4: PRINT "Aye, we'll f + ight 'em, ";T$;"!": GOSUB 56 + 00: VTAB 4: PRINT CL$ + 5310 SK = 0: VTAB 4: PRINT "We'r + e firing on 'em, ";T$;"!": FOR + K = 1 TO GN: IF SN = 0 THEN + 5340 + 5320 I = FN R(10): IF AM%(I,0) = + 0 THEN 5320 + 5330 GOSUB 5840:AM%(I,1) = AM%( + I,1) + FN R(30) + 10: IF AM + %(I,1) > AM%(I,0) THEN AM%(I + ,0) = 0:AM%(I,1) = 0: GOSUB + 5860: GOSUB 5820:SK = SK + 1 + :SN = SN - 1:SS = SS - 1: GOSUB + 5750: IF SS = 0 THEN GOSUB + 5700 + 5340 NEXT K: IF SK > 0 THEN VTAB + 4: HTAB 1: PRINT "Sunk ";SK; + " of the buggers, ";T$;"!": CALL + 2521: GOSUB 5600: VTAB 4: PRINT + CL$ + 5350 IF SK = 0 THEN VTAB 4: HTAB + 1: PRINT "Hit 'em, but didn' + t sink 'em, ";T$;"!": GOSUB + 5600: VTAB 4: PRINT CL$ + 5360 IF FN R(S0) < SN * .6 / F + 1 OR SN = 0 OR SN = S0 OR SN + < 3 THEN 5500 + 5362 W = FN R(SN / 3 / F1) + 1: + SN = SN - W:SA = SA - W: GOSUB + 5680 + 5390 VTAB 4: PRINT W;" ran away + , ";T$;"!": GOSUB 5750: CALL + 2521: GOSUB 5600: VTAB 4: PRINT + CL$: GOTO 5500 + 5400 REM + 5410 GOSUB 400: PRINT "You have + the following on board, ";T + $;":";: FOR J = 1 TO 4: VTAB + 20 + (J = 3 OR J = 4): HTAB + 1 + 19 * (J = 2 OR J = 4): PRINT + RIGHT$ (" " + LEFT$ + (CO$(J),7),9);": ";ST(2,J): NEXT + J + 5420 VTAB 4: PRINT "What shall + I throw overboard, ";T$;"? " + ;:CH$ = "OSAG*": GOSUB 100: VTAB + 4: HTAB 1: PRINT CL$ + 5430 IF CH% = 5 THEN II = 1:IJ = + 4:IK = 1E9: GOTO 5450 + 5440 VTAB 4: PRINT "How much, " + ;T$;"? ";: GOSUB 150:II = CH + %:IJ = CH%: IF R1% THEN W = + ST(2,II) + 5450 WW = 0: FOR J = II TO IJ:IK + = ST(2,J): IF W > IK THEN W + = IK + 5460 ST(2,J) = ST(2,J) - W:WW = + WW + W:MW = MW + W: NEXT J: VTAB + 4: HTAB 1: PRINT CL$ + 5470 IF WW = 0 THEN VTAB 4: PRINT + "There's nothing there, ";T$ + ;"!": CALL 2518: GOSUB 5600: + VTAB 4: PRINT CL$ + 5480 GOSUB 400: IF WW > 0 THEN + RF = RF + WW / 3:OK = OK + W + W / 10: VTAB 4: PRINT "Let's + hope we lose 'em, ";T$;"!": + CALL 2521: GOSUB 5600: VTAB + 4: PRINT CL$: GOTO 5210 + 5500 REM + 5505 IF SN = 0 THEN VTAB 4: PRINT + "We got 'em all, ";T$;"!!": CALL + 2521: GOSUB 5600:OK = 1: GOTO + 5900 + 5510 VTAB 4: PRINT "They're fir + ing on us, ";T$;"!": GOSUB 5 + 600: VTAB 4: PRINT CL$ + 5540 FOR I = 1 TO 10: POKE - 1 + 6298,0: POKE - 16299,0: POKE + - 16297,0: POKE - 16300,0: + FOR J = 1 TO 10: NEXT J,I + 5542 VTAB 4: PRINT "We've been + hit, ";T$;"!!": CALL 2512 + 5545 I = SN: IF I > 15 THEN I = + 15 + 5550 IF GN THEN IF FN R(100) < + (DM / SC) * 100 OR (DM / SC) + * 100 > 80 THEN I = 1: GOSUB + 5600: VTAB 4: PRINT CL$: VTAB + 4: PRINT "The buggers hit a + gun, ";T$;"!!": CALL 2512:GN + = GN - 1:MW = MW + 10: GOSUB + 5600: VTAB 4: PRINT CL$ + 5555 DM = DM + FN R(ED * I * F1 + ) + I / 2 + 5560 IF NOT ( FN R(20)) AND F1 + = 1 THEN OK = 2: GOTO 5900 + 5590 GOTO 5090 + 5600 VTAB 2: HTAB 21: FOR II = + 1 TO T / 3 + 5610 W = PEEK ( - 16384): IF W < + 128 THEN NEXT II: PRINT : RETURN + + 5620 IF W = 210 THEN CMD = 1: PRINT + "Run " + 5630 IF W = 198 THEN CMD = 2: PRINT + "Fight " + 5640 IF W = 212 THEN CMD = 3: PRINT + "Throw cargo" + 5650 POKE - 16368,0: PRINT + 5670 RETURN + 5680 IF SA > = 0 THEN RETURN + + 5681 I = 9: FOR IJ = SA TO - 1 + 5682 IF AM%(I,0) = 0 THEN I = I + - 1: GOTO 5682 + 5683 AM%(I,0) = 0:AM%(I,1) = 0: GOSUB + 5880: GOSUB 5820:I = I - 1:S + S = SS - 1: NEXT IJ: RETURN + + 5700 REM + 5710 FOR I = 0 TO 9: IF AM%(I,0 + ) THEN 5740 + 5720 SA = SA - 1: IF SA < 0 THEN + SA = 0: RETURN + 5730 AM%(I,0) = FN R(EC) + 20:A + M%(I,1) = 0: GOSUB 5800:SS = + SS + 1 + 5740 NEXT I: RETURN + 5750 REM + 5760 VTAB 1: HTAB 1: PRINT RIGHT$ + (" " + STR$ (SN),4) + 5770 VTAB 2: HTAB 33: PRINT RIGHT$ + (" " + STR$ (GN),3): RETURN + + 5800 GOSUB 5880: HTAB X: VTAB Y + : PRINT SH$: RETURN + 5820 GOSUB 5880: HTAB X: VTAB Y + : PRINT SB$: RETURN + 5840 GOSUB 5880: POKE 2493,(Y + + 4) * 8 - 1: POKE 2494,X - 1: + FOR J = 0 TO 1:IJ = FN R(6 + ):II = DL%(IJ,J): HTAB X + INT + (II / 10): VTAB Y + II - INT + (II / 10) * 10: PRINT DM$(IJ + ,J): NEXT J: CALL 2368: RETURN + + 5860 GOSUB 5880: POKE 2361,(Y + + 4) * 8 - 1: POKE 2362,X - 1: + POKE 2300, FN R( FN R(192)) + : CALL 2224: RETURN + 5880 X = (I - INT (I / 5) * 5) * + 8 + 1:Y = INT (I / 5) * 6 + + 7: RETURN + 5900 GOSUB 200: GOSUB 300: GOSUB + 400 + 5910 IF OK = 0 THEN PRINT "The + buggers got us, ";T$;"!!!": + PRINT "It's all over, now!! + !":OK = 1: GOTO 2698 + 5920 IF OK = 1 THEN GOSUB 400: + PRINT "We've captured some + booty":WW = BT: GOSUB 600: PRINT + "It's worth ";WW$;"!": CALL + 2518:CA = CA + BT: GOSUB 96: + GOTO 3300 + 5930 IF OK = 2 THEN PRINT "Li + Yuen's fleet drove them off! + ": GOSUB 96: GOTO 3220 + 5940 IF OK = 3 THEN PRINT "We + made it, ";T$;"!": CALL 2518 + : GOSUB 96: GOTO 3300 + 10000 REM + 10010 CALL 6147: POKE 1013,76: POKE + 1014,224: POKE 1015,9: POKE + 10,76: POKE 11,16: POKE 12,1 + 1: POKE 1010,102: POKE 1011, + 213: POKE 1012,112: DIM LO$( + 7),CO$(4),CP(4),BP%(7,4),ST( + 2,4),AM%(9,1),DM$(5,1),DL%(5 + ,1),ST$(5) + 10020 DEF FN R(X) = INT ( USR + (0) * X) + 10040 HM$ = CHR$ (16):CS$ = CHR$ + (1) + "0":CA$ = CHR$ (1) + + "1":CG$ = CHR$ (1) + "2":BD + $ = CHR$ (2):CD$ = CHR$ (3 + ):DD$ = CHR$ (4):IV$ = CHR$ + (9):NV$ = CHR$ (14):FS$ = CHR$ + (25):CE$ = CHR$ (6):CL$ = CHR$ + (5) + 10045 IF PEEK (2367) = 236 THEN + 10070 + 10050 POKE - 16368,0 + 10060 FOR I = 1 TO 400:CH% = PEEK + ( - 16384):X = USR (0): IF + CH% < 128 THEN NEXT + 10062 VTAB 20: HTAB 31: PRINT I + V$;CA$;"'ESC'";: FOR I = 1 TO + 20:X = USR (0): IF PEEK ( - + 16384) < > 155 THEN NEXT : + VTAB 20: HTAB 31: PRINT NV$ + ;CA$ + "'ESC'";: FOR I = 1 TO + 20:X = USR (0): IF PEEK ( - + 16384) < > 155 THEN NEXT : + GOTO 10062 + 10070 POKE 2367,236: POKE - 16 + 368,0: PRINT NV$;FS$;HM$ + 10110 VTAB 8: HTAB 1: PRINT CG$ + ;"[";: & 45,38: PRINT "]";: FOR + I = 1 TO 8: PRINT "!"; TAB( + 40);"!";: NEXT I: PRINT "<"; + : & 58,38: PRINT ">";CS$ + 10120 VTAB 10: HTAB 7: PRINT CS + $;T$;",": VTAB 12: HTAB 3: PRINT + "What will you name your": VTAB + 15: HTAB 13: & 45,22: VTAB 1 + 4: HTAB 7: PRINT "Firm: ";CA + $;: & 32,27: VTAB 14: HTAB 1 + 3: POKE 33,39: CALL 2200: POKE + 33,40:WK$ = MID$ (WK$,1): IF + WK$ = "" THEN CALL 2521: GOTO + 10120 + 10130 IF LEN (WK$) > 22 THEN PRINT + : VTAB 18: PRINT IV$;: & 32, + 42: PRINT "Please limit your + Firm's name to 22 chara + cters or less.";: & 32,59: PRINT + NV$: CALL 2518: GOSUB 92: VTAB + 18: PRINT CE$: GOTO 10120 + 10140 H$ = WK$: PRINT HM$;CS$: VTAB + 6: PRINT "Do you want to sta + rt . . .": PRINT : PRINT : PRINT + " 1) With cash (and a debt) + ": PRINT : PRINT : PRINT ,"> + > or <<": PRINT : PRINT : PRINT + " 2) With five guns and no + cash": PRINT ,"(But no debt! + )" + 10150 PRINT : PRINT : PRINT TAB( + 10);" ?";:CH$ = "12": GOSUB + 100:MO = 1:YE = 1860:SC = 60 + :BA = 0:LO = 1:TI = 1:WC = 1 + 0000:WS = 0 + 10160 IF CH% = 1 THEN DW = 5000 + :CA = 400:MW = 60:GN = 0:BP = + 10 + 10170 IF CH% = 2 THEN DW = 0:CA + = 0:MW = 10:GN = 5:BP = 7 + 10180 FOR I = 0 TO 7: READ LO$( + I): NEXT I: DATA At sea,Hong + Kong,Shanghai,Nagasaki,Saig + on,Manila,Singapore,Batavia + 10190 FOR I = 1 TO 4: READ CO$( + I): FOR J = 1 TO 7: READ BP% + (J,I): NEXT J,I + 10200 DATA Opium,11,16,15,14,1 + 2,10,13,Silk,11,14,15,16,10, + 13,12,Arms,12,16,10,11,13,14 + ,15,General Cargo,10,11,12,1 + 3,14,15,16 + 10210 FOR I = 0 TO 5: READ ST$( + I): NEXT I: DATA "Critical", + " Poor"," Fair"," Good"," + Prime","Perfect" + 10250 SH$ = BD$ + CG$ + "ABCDEFG + " + CD$ + "HIJKLMN" + CD$ + + "OIJKLPQ" + CD$ + "RSTUVWX" + + CD$ + "YJJJJJZ" + DD$ + 10260 SB$ = BD$: FOR II = 1 TO 5 + :SB$ = SB$ + " " + CD$ + : NEXT II:SB$ = SB$ + DD$ + 10270 FOR I = 0 TO 5: FOR J = 0 + TO 1:CH$ = BD$ + CG$ + 10280 READ WK$:CH$ = CH$ + WK$: + IF RIGHT$ (CH$,1) = "*" THEN + CH$ = MID$ (CH$,1, LEN (CH$ + ) - 1) + CD$: GOTO 10280 + 10290 DM$(I,J) = CH$ + DD$: READ + DL%(I,J): NEXT J,I + 10300 DATA cde,20,r,3,fg*,mn,50 + ,tu,23,ij,11,vw,43,0,22,x*,z + ,63,kl,32,12,14,pq,52,345,34 + + 10310 EC = 20:ED = .5 + 10990 GOSUB 200: GOTO 1000 + 20000 REM + 20010 WW = CA + BA - DW: GOSUB 6 + 00:WW = INT ((CA + BA - DW) + / 100 / TI ^ 1.1) + 20020 PRINT FS$;HM$;CS$;: PRINT + "Your final status:": PRINT + : PRINT "Net Cash: ";WW$: PRINT + : PRINT "Ship size: ";SC;" u + nits with ";GN;" guns": PRINT + + 20030 PRINT "You traded for "; INT + (TI / 12);" year"; MID$ ("s" + ,(TI > 11 AND TI < 24) + 1,1 + );" and ";TI - INT (TI / 12 + ) * 12;" month"; MID$ ("s",( + (TI - INT (TI / 12) * 12) = + 1) + 1,1): PRINT : PRINT IV$ + ;"Your score is ";WW;".";NV$ + + 20040 VTAB 14: PRINT "Your Rati + ng:": PRINT CG$;"[";: & 45,3 + 1: PRINT "]": FOR I = 1 TO 5 + : PRINT "!";: HTAB 33: PRINT + "!": NEXT I: PRINT "<";: & 5 + 8,31: PRINT ">";CS$: VTAB 16 + + 20050 HTAB 2: IF WW > 49999 THEN + PRINT IV$; + 20060 PRINT "Ma Tsu";NV$;" + 50,000 and over " + 20070 HTAB 2: IF WW < 50000 AND + WW > 7999 THEN PRINT IV$; + 20080 PRINT "Master ";T$;NV$;" + 8,000 to 49,999" + 20090 HTAB 2: IF WW < 8000 AND + WW > 999 THEN PRINT IV$; + 20100 PRINT T$;NV$;" 1 + ,000 to 7,999" + 20110 HTAB 2: IF WW < 1000 AND + WW > 499 THEN PRINT IV$; + 20120 PRINT "Compradore";NV$;" + 500 to 999" + 20130 HTAB 2: IF WW < 500 THEN + PRINT IV$; + 20140 PRINT "Galley Hand";NV$;" + less than 500" + 20170 VTAB 11 + 20180 IF WW < 99 AND WW > = 0 THEN + PRINT "Have you considered + a land based job?": PRINT + 20190 IF WW < 0 THEN PRINT "Th + e crew has requested that yo + u stay on shore for their sa + fety!!": PRINT + 20900 VTAB 23: RETURN + 63999 PRINT FS$;HM$: TEXT : HOME + : POKE 103,1: POKE 104,8: END + + +] \ No newline at end of file diff --git a/taipan-orig.c b/taipan-orig.c new file mode 100644 index 0000000..2b0bc71 --- /dev/null +++ b/taipan-orig.c @@ -0,0 +1,2735 @@ +/* ------------------------------------------------------------------------ * + * Taipan version 0.9 + * A text/ncurses game for Linux. + * + * Created by: + * Art Canfil + * + * Programmed by: + * Jay Link + * + * Apple ][ program coded by: + * Ronald J. Berg + * ------------------------------------------------------------------------ */ + +#include +#include +#include +#include +#include + +#define GENERIC 1 +#define LI_YUEN 2 + +void splash_intro(void); +int get_one(void); +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(float num, char *fancy); +int sea_battle(int id, int num_ships); +void draw_lorcha(int x, int y); +void clear_lorcha(int x, int y); +void draw_blast(int x, int y); +void sink_lorcha(int x, int y); +void fight_stats(int ships, int orders); +void mchenry(void); +void retire(void); +void final_stats(void); + +char firm[23], + fancy_num[13]; + +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" }; + +float cash = 0, + bank = 0, + debt = 0, + booty = 0, + ec = 20, + ed = .5; + +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; + +int main(void) +{ + int choice; + + srand(getpid()); + + initscr(); + cbreak(); + noecho(); + + 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 = rand()%100 + 50; + + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Elder Brother Wu has sent %d braves\n", braves); + printw("to escort you to the Wu mansion, Taipan.\n"); + + refresh(); + timeout(3000); + getch(); + timeout(-1); + + move(18, 0); + clrtobot(); + printw("Elder Brother Wu reminds you of the\n"); + printw("Confucian ideal of personal worthiness,\n"); + printw("and how this applies to paying one's\n"); + printw("debts.\n"); + + refresh(); + timeout(3000); + getch(); + timeout(-1); + + move(18, 0); + clrtobot(); + printw("He is reminded of a fabled barbarian\n"); + printw("who came to a bad end, after not caring\n"); + printw("for his obligations.\n\n"); + printw("He hopes no such fate awaits you, his\n"); + printw("friend, Taipan.\n"); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + + wu_warn = 1; + } + + if (port == 1) + { + elder_brother_wu(); + } + + if (rand()%4 == 0) + { + if (rand()%2 == 0) + { + new_ship(); + } else if (guns < 1000) { + new_gun(); + } + } + + if ((port != 1) && (rand()%18 == 0) && (hold_[0] > 0)) + { + float fine = ((cash / 1.8) * ((float) rand() / RAND_MAX)) + 1; + + hold += hold_[0]; + hold_[0] = 0; + cash -= fine; + + port_stats(); + + fancy_numbers(fine, fancy_num); + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Bad Joss!!\n"); + printw("The local authorities have seized your\n"); + printw("Opium cargo and have also fined you\n"); + printw("%s, Taipan!\n", fancy_num); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + } + + if ((rand()%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) rand() / RAND_MAX)); + } + + port_stats(); + + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Messenger reports large theft\n"); + printw("from warehouse, Taipan.\n"); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + } + + if (rand()%20 == 0) + { + if (li > 0) { li++; } + if (li == 4) { li = 0; } + } + + if ((port != 1) && (li == 0) && (rand()%4 != 0)) + { + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Li Yuen has sent a Lieutenant,\n"); + printw("Taipan. He says his admiral wishes\n"); + printw("to see you in Hong Kong, posthaste!\n"); + + refresh(); + timeout(3000); + getch(); + timeout(-1); + } + + if (rand()%9 == 0) + { + good_prices(); + } + + if ((cash > 25000) && (rand()%20 == 0)) + { + float robbed = ((cash / 1.4) * ((float) rand() / RAND_MAX)); + + cash -= robbed; + port_stats(); + + fancy_numbers(robbed, fancy_num); + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Bad Joss!!\n"); + printw("You've been beaten up and\n"); + printw("robbed of %s in cash, Taipan!!\n", fancy_num); + + refresh(); + 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(); + } + } + } + + clear(); + refresh(); + nocbreak(); + endwin(); + + return EXIT_SUCCESS; +} + +void splash_intro(void) +{ + flushinp(); + clear(); + printw("\n"); + printw(" _____ _ ___ ____ _ _ _ ===============\n"); + printw(" |_ _|/ \\ |_ _| _ \\ / \\ | \\ | | Created by:\n"); + printw(" | | / _ \\ | || |_) / _ \\ | \\| | Art Canfil\n"); + printw(" | |/ ___ \\ | || __/ ___ \\| |\\ |\n"); + printw(" |_/_/ \\_\\___|_| /_/ \\_\\_| \\_| ===============\n"); + printw(" Programmed by:\n"); + printw(" A game based on the China trade of the 1800's Jay Link\n"); + printw("\n"); + printw(" ~~| , jlink@ilbbs.com\n"); + printw(" ,|`-._/|\n"); + printw(" .' | /||\\ ===============\n"); + printw(" .' | ./ ||`\\ Copyright (c)\n"); + printw(" / `-. |/._ || \\ 1978 - 2002\n"); + printw(" / `|| `|;-._\\ Art Canfil\n"); + printw(" | || || \\\n"); + printw("~^~_-~^~=~^~~^= / || ||__ \\~^=~^~-~^~_~^~= ===============\n"); + printw(" ~=~^~ _~^~ =~ `--------|`---|| `\"-`___~~^~ =_~^= Press "); + attrset(A_REVERSE); + printw("ANY"); + attrset(A_NORMAL); + printw(" key\n"); + printw("~ ~^~=~^_~^~ =~ \\~~~~~~~'~~~~'~~~~/~~`` ~=~^~ ~^= to start.\n"); + printw(" ~^=~^~_~-=~^~ ^ `--------------'~^~=~^~_~^=~^~=~\n"); + curs_set(0); + refresh(); + + getch(); + curs_set(1); + return; +} + +int get_one(void) +{ + int input, + choice = 0, + character = 0; + + while ((input = getch()) != '\n') + { + if (((input == 8) || (input == 127)) && (character == 0)) + { + refresh(); + } else if ((input == 8) || (input == 127)) { + printw("%c", 8); + printw(" "); + printw("%c", 8); + character--; + refresh(); + } else if (character >= 1) { + refresh(); + } else if (input == '\33') { + flushinp(); + refresh(); + } else { + printw("%c", input); + choice = input; + character++; + refresh(); + } + } + + return choice; +} + +long get_num(int maxlen) +{ + char number[maxlen + 1]; + + int input, + character = 0; + + long amount; + + while ((input = getch()) != '\n') + { + if (((input == 8) || (input == 127)) && (character == 0)) + { + refresh(); + } else if ((input == 8) || (input == 127)) { + printw("%c", 8); + printw(" "); + printw("%c", 8); + number[character] = '\0'; + character--; + refresh(); + } else if (character >= maxlen) { + refresh(); + } else if (input == '\33') { + flushinp(); + refresh(); + } else if (((input == 'A') || (input == 'a')) && + (character == 0) && (maxlen > 1)) { + printw("%c", input); + number[character] = input; + character++; + refresh(); + } else if ((input < 48) || (input > 57)) { + refresh(); + } else { + printw("%c", input); + number[character] = input; + character++; + refresh(); + } + } + + number[character] = '\0'; + if ((strcmp(number, "A") == 0) || (strcmp(number, "a") == 0)) + { + amount = -1; + } else { + amount = strtol(number, (char **)NULL, 10); + } + + return amount; +} + +void name_firm(void) +{ + int input, + character = 0; + + clear(); + move (7, 0); + printw(" _______________________________________\n"); + printw("| Taipan, |\n"); + printw("| |\n"); + printw("| What will you name your |\n"); + printw("| |\n"); + printw("| Firm: |\n"); + printw("| ---------------------- |\n"); + printw("|_______________________________________|\n"); + + move(12, 12); + refresh(); + + while (((input = getch()) != '\n') && (character < 22)) + { + if (((input == 8) || (input == 127)) && (character == 0)) + { + refresh(); + } else if ((input == 8) || (input == 127)) { + printw("%c", 8); + printw(" "); + printw("%c", 8); + firm[character] = '\0'; + character--; + refresh(); + } else if (input == '\33') { + flushinp(); + refresh(); + } else { + printw("%c", input); + firm[character] = input; + character++; + refresh(); + } + } + + firm[character] = '\0'; + + return; +} + +void cash_or_guns(void) +{ + int choice = 0; + + clear(); + move (5, 0); + printw("Do you want to start . . .\n\n"); + printw(" 1) With cash (and a debt)\n\n"); + printw(" >> or <<\n\n"); + printw(" 2) With five guns and no cash\n"); + printw(" (But no debt!)\n"); + + while ((choice != '1') && (choice != '2')) + { + move (15, 0); + clrtoeol(); + printw(" ?"); + refresh(); + choice = get_one(); + } + + 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 * (rand()%3 + 1) * base_price[0][0]; + price[1] = base_price[1][port] / 2 * (rand()%3 + 1) * base_price[1][0]; + price[2] = base_price[2][port] / 2 * (rand()%3 + 1) * base_price[2][0]; + price[3] = base_price[3][port] / 2 * (rand()%3 + 1) * base_price[3][0]; + return; +} + +void port_stats(void) +{ + int in_use, + status = 100 - (((float) damage / capacity) * 100), + spacer, + i; + + clear(); + spacer = 12 - (strlen(firm) / 2); + for (i = 1; i <= spacer; i++) + { + printw(" "); + } + printw("Firm: %s, Hong Kong\n", firm); + printw(" ______________________________________\n"); + printw("|Hong Kong Warehouse | Date\n"); + printw("| Opium In Use: |\n"); + printw("| Silk |\n"); + printw("| Arms Vacant: | Location\n"); + printw("| General |\n"); + printw("|______________________________________|\n"); + printw("|Hold Guns | Debt\n"); + printw("| Opium |\n"); + printw("| Silk |\n"); + printw("| Arms | Ship Status\n"); + printw("| General |\n"); + printw("|______________________________________|\n"); + printw("Cash: Bank:\n"); + printw("________________________________________\n"); + + move(3, 12); + printw("%d", hkw_[0]); + move(4, 12); + printw("%d", hkw_[1]); + move(5, 12); + printw("%d", hkw_[2]); + move(6, 12); + printw("%d", hkw_[3]); + move(8, 6); + if (hold >= 0) + { + printw("%d", hold); + } else { + attrset(A_REVERSE); + printw("Overload"); + attrset(A_NORMAL); + } + move(9, 12); + printw("%d", hold_[0]); + move(10, 12); + printw("%d", hold_[1]); + move(11, 12); + printw("%d", hold_[2]); + move(12, 12); + printw("%d", hold_[3]); + + move(14, 5); + fancy_numbers(cash, fancy_num); + printw("%s", fancy_num); + + in_use = hkw_[0] + hkw_[1] + hkw_[2] + hkw_[3]; + move(4, 21); + printw("%d", in_use); + move(6, 21); + printw("%d", (10000 - in_use)); + + move(8, 25); + printw("%d", guns); + + move(14, 25); + fancy_numbers(bank, fancy_num); + printw("%s", fancy_num); + + move(3, 42); + printw("15 "); + attrset(A_REVERSE); + printw("%s", months[month - 1]); + attrset(A_NORMAL); + printw(" %d", year); + + move(6, 43); + spacer = (9 - strlen(location[port])) / 2; + for (i = 1; i <= spacer; i++) + { + printw(" "); + } + attrset(A_REVERSE); + printw("%s", location[port]); + attrset(A_NORMAL); + + move(9, 41); + fancy_numbers(debt, fancy_num); + spacer = (12 - strlen(fancy_num)) / 2; + for (i = 1; i <= spacer; i++) + { + printw(" "); + } + attrset(A_REVERSE); + printw("%s", fancy_num); + attrset(A_NORMAL); + + i = status / 20; + if (i < 2) + { + attrset(A_REVERSE); + move(12, 51); + printw(" "); + } + move(12, 42); + printw("%s:%d", st[i], status); + attrset(A_NORMAL); +} + +int port_choices(void) +{ + int choice = 0; + + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Taipan, present prices per unit here are\n"); + printw(" Opium: Silk:\n"); + printw(" Arms: General:\n"); + move(19, 11); + printw("%ld", price[0]); + move(19, 29); + printw("%ld", price[1]); + move(20, 11); + printw("%ld", price[2]); + move(20, 29); + printw("%ld", price[3]); + + for (;;) + { + move (22, 0); + clrtobot(); + + if (port == 1) + { + if ((cash + bank) >= 1000000) + { + printw("Shall I Buy, Sell, Visit bank, Transfer\n"); + printw("cargo, Quit trading, or Retire? "); + refresh(); + + choice = get_one(); + 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 { + printw("Shall I Buy, Sell, Visit bank, Transfer\n"); + printw("cargo, or Quit trading? "); + refresh(); + + choice = get_one(); + if ((choice == 'B') || (choice == 'b') || + (choice == 'S') || (choice == 's') || + (choice == 'V') || (choice == 'v') || + (choice == 'T') || (choice == 't') || + (choice == 'Q') || (choice == 'q')) + { + break; + } + } + } else { + printw("Shall I Buy, Sell, or Quit trading? "); + refresh(); + + choice = get_one(); + if ((choice == 'B') || (choice == 'b') || + (choice == 'S') || (choice == 's') || + (choice == 'Q') || (choice == 'q')) + { + break; + } + } + } + + return choice; +} + +void buy(void) +{ + char space[5]; + + int choice = 0; + + long afford, + amount; + + for (;;) + { + move(22, 0); + clrtobot(); + + printw("What do you wish me to buy, Taipan? "); + refresh(); + + choice = get_one(); + if ((choice == 'O') || (choice == 'o')) + { + choice = 0; + break; + } else if ((choice == 'S') || (choice == 's')) { + choice = 1; + break; + } else if ((choice == 'A') || (choice == 'a')) { + choice = 2; + break; + } else if ((choice == 'G') || (choice == 'g')) { + choice = 3; + break; + } + } + + for (;;) + { + move(21, 42); + clrtobot(); + + afford = cash / price[choice]; + attrset(A_REVERSE); + printw(" You can "); + attrset(A_NORMAL); + move(22, 0); + printw("How much %s shall", item[choice]); + move(22, 42); + attrset(A_REVERSE); + printw(" afford "); + move(23, 42); + printw(" "); + move(23, 42); + 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, ""); + } + printw("%s%ld", space, afford); + attrset(A_NORMAL); + + move(23, 0); + printw("I buy, Taipan: "); + refresh(); + + 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 (;;) + { + move(22, 0); + clrtobot(); + + printw("What do you wish me to sell, Taipan? "); + refresh(); + + choice = get_one(); + if ((choice == 'O') || (choice == 'o')) + { + choice = 0; + break; + } else if ((choice == 'S') || (choice == 's')) { + choice = 1; + break; + } else if ((choice == 'A') || (choice == 'a')) { + choice = 2; + break; + } else if ((choice == 'G') || (choice == 'g')) { + choice = 3; + break; + } + } + + for (;;) + { + move(22, 0); + clrtobot(); + + printw("How much %s shall\n", item[choice]); + printw("I sell, Taipan: "); + refresh(); + + 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 (;;) + { + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("How much will you deposit? "); + refresh(); + + amount = get_num(9); + if (amount == -1) + { + amount = cash; + } + if (amount <= cash) + { + cash -= amount; + bank += amount; + break; + } else { + move(18, 0); + clrtobot(); + fancy_numbers(cash, fancy_num); + printw("Taipan, you only have %s\n", fancy_num); + printw("in cash.\n"); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + } + } + port_stats(); + + for (;;) + { + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("How much will you withdraw? "); + refresh(); + + amount = get_num(9); + if (amount == -1) + { + amount = bank; + } + if (amount <= bank) + { + cash += amount; + bank -= amount; + break; + } else { + fancy_numbers(cash, fancy_num); + printw("Taipan, you only have %s\n", fancy_num); + printw("in the bank."); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + } + } + port_stats(); + + return; +} + +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)) + { + move(22, 0); + clrtobot(); + printw("You have no cargo, Taipan.\n"); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + return; + } + + for (i = 0; i < 4; i++) + { + if (hold_[i] > 0) + { + for (;;) + { + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("How much %s shall I move\n", item[i]); + printw("to the warehouse, Taipan? "); + refresh(); + + 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) { + move (21, 0); + printw("Your warehouse is full, Taipan!"); + } else { + move (21, 0); + printw("Your warehouse will only hold an\n"); + printw("additional %d, Taipan!", (10000 - in_use)); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + } + } else { + move(18, 0); + clrtobot(); + printw("You have only %d, Taipan.\n", hold_[i]); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + } + } + port_stats(); + } + + if (hkw_[i] > 0) + { + for (;;) + { + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("How much %s shall I move\n", item[i]); + printw("aboard ship, Taipan? "); + refresh(); + + amount = get_num(9); + if (amount == -1) + { + amount = hkw_[i]; + } + if (amount <= hkw_[i]) + { + hold_[i] += amount; + hkw_[i] -= amount; + hold -= amount; + break; + } else { + move(18, 0); + clrtobot(); + printw("You have only %d, Taipan.\n", hkw_[i]); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + } + } + port_stats(); + } + } + + return; +} + +void quit(void) +{ + int choice = 0, + result = 0; + + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Taipan, do you wish me to go to:\n"); + printw("1) Hong Kong, 2) Shanghai, 3) Nagasaki,\n"); + printw("4) Saigon, 5) Manila, 6) Singapore, or\n"); + printw("7) Batavia ? "); + refresh(); + + for (;;) + { + move(21, 13); + clrtobot(); + + choice = get_num(1); + + if (choice == port) + { + printw("\n\nYou're already here, Taipan."); + refresh(); + timeout(5000); + getch(); + timeout(-1); + } else if ((choice >= 1) && (choice <= 7)) { + port = choice; + break; + } + } + + move(6, 43); + printw(" "); + attrset(A_REVERSE); + printw("%s", location[0]); + attrset(A_NORMAL); + printw(" "); + + move(16, 0); + clrtobot(); + printw(" Captain's Report\n\n"); + + if (rand()%bp == 0) + { + int num_ships = rand()%((capacity / 10) + guns) + 1; + + if (num_ships > 9999) + { + num_ships = 9999; + } + printw("%d hostile ships approaching, Taipan!\n", num_ships); + refresh(); + + timeout(3000); + getch(); + timeout(-1); + + result = sea_battle(GENERIC, num_ships); + } + + if (result == 2) + { + port_stats(); + move(6, 43); + printw(" "); + attrset(A_REVERSE); + printw("%s", location[0]); + attrset(A_NORMAL); + printw(" "); + + move(16, 0); + clrtobot(); + printw(" Captain's Report\n\n"); + printw("Li Yuen's fleet drove them off!"); + refresh(); + + timeout(3000); + getch(); + timeout(-1); + } + + if (((result == 0) && (rand()%(4 + (8 * li))) == 0) || (result == 2)) + { + move(18, 0); + clrtobot(); + printw("Li Yuen's pirates, Taipan!!\n\n"); + refresh(); + + timeout(3000); + getch(); + timeout(-1); + + if (li > 0) + { + printw("Good joss!! They let us be!!\n"); + refresh(); + + timeout(3000); + getch(); + timeout(-1); + + return; + } else { + int num_ships = rand()%((capacity / 5) + guns) + 5; + + printw("%d ships of Li Yuen's pirate\n", num_ships); + printw("fleet, Taipan!!\n"); + refresh(); + + timeout(3000); + getch(); + timeout(-1); + + sea_battle(LI_YUEN, num_ships); + } + } + + if (result > 0) + { + port_stats(); + move(6, 43); + printw(" "); + attrset(A_REVERSE); + printw("%s", location[0]); + attrset(A_NORMAL); + printw(" "); + + move(16, 0); + clrtobot(); + printw(" Captain's Report\n\n"); + if (result == 1) + { + fancy_numbers(booty, fancy_num); + printw("We captured some booty.\n"); + printw("It's worth %s!", fancy_num); + cash += booty; + } else if (result == 3) { + printw("We made it!"); + } else { + printw("The buggers got us, Taipan!!!\n"); + printw("It's all over, now!!!"); + refresh(); + + timeout(5000); + getch(); + timeout(-1); + + final_stats(); + return; + } + + refresh(); + timeout(3000); + getch(); + timeout(-1); + } + + if (rand()%10 == 0) + { + move(18, 0); + clrtobot(); + printw("Storm, Taipan!!\n\n"); + refresh(); + timeout(3000); + getch(); + timeout(-1); + + if (rand()%30 == 0) + { + printw(" I think we're going down!!\n\n"); + refresh(); + timeout(3000); + getch(); + timeout(-1); + + if (((damage / capacity * 3) * ((float) rand() / RAND_MAX)) >= 1) + { + printw("We're going down, Taipan!!\n"); + refresh(); + timeout(5000); + getch(); + timeout(-1); + + final_stats(); + } + } + + printw(" We made it!!\n\n"); + refresh(); + timeout(3000); + getch(); + timeout(-1); + + if (rand()%3 == 0) + { + int orig = port; + + while (port == orig) + { + port = rand()%7 + 1; + } + + move(18, 0); + clrtobot(); + printw("We've been blown off course\n"); + printw("to %s", location[port]); + refresh(); + timeout(3000); + getch(); + timeout(-1); + } + } + + month++; + if (month == 13) + { + month = 1; + year++; + ec += 10; + ed += .5; + } + + debt = debt + (debt * .1); + bank = bank + (bank * .005); + set_prices(); + + move(18, 0); + clrtobot(); + printw("Arriving at %s...", location[port]); + refresh(); + 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; + + if (time > 12) + { + j = rand()%(1000 * time) + (1000 * time); + i = 1; + } + + amount = ((cash / i) * ((float) rand() / RAND_MAX)) + j; + + fancy_numbers(amount, fancy_num); + + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Li Yuen asks %s in donation\n", fancy_num); + printw("to the temple of Tin Hau, the Sea\n"); + + while ((choice != 'Y') && (choice != 'y') && + (choice != 'N') && (choice != 'n')) + { + move (20, 0); + clrtoeol(); + printw("Goddess. Will you pay? "); + refresh(); + choice = get_one(); + } + + if ((choice == 'Y') || (choice == 'y')) + { + if (amount <= cash) + { + cash -= amount; + li = 1; + } else { + move (18, 0); + clrtobot(); + printw("Taipan, you do not have enough cash!!\n\n"); + refresh(); + + timeout(3000); + getch(); + timeout(-1); + + printw("Do you want Elder Brother Wu to make up\n"); + printw("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; + + move (18, 0); + clrtobot(); + printw("Elder Brother has given Li Yuen the\n"); + printw("difference between what he wanted and\n"); + printw("your cash on hand and added the same\n"); + printw("amount to your debt.\n"); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + } else { + cash = 0; + + printw("Very well. Elder Brother Wu will not pay\n"); + printw("Li Yuen the difference. I would be very\n"); + printw("wary of pirates if I were you, Taipan.\n"); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + } + } + } + + port_stats(); + + return; +} + +void elder_brother_wu(void) +{ + int choice = 0; + + long wu = 0; + + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Do you have business with Elder Brother\n"); + printw("Wu, the moneylender? "); + + for (;;) + { + move (19, 21); + clrtoeol(); + refresh(); + + choice = get_one(); + if ((choice == 'N') || (choice == 'n')) + { + 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 = rand()%1500 + 500, + j; + + wu_bailout++; + j = rand()%2000 * wu_bailout + 1500; + + for (;;) + { + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Elder Brother is aware of your plight,\n"); + printw("Taipan. He is willing to loan you an\n"); + printw("additional %d if you will pay back\n", i); + printw("%d. Are you willing, Taipan? ", j); + refresh(); + + choice = get_one(); + if ((choice == 'N') || (choice == 'n')) + { + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Very well, Taipan, the game is over!\n"); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + + final_stats(); + } else if ((choice == 'Y') || (choice == 'y')) { + cash += i; + debt += j; + port_stats(); + + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Very well, Taipan. Good joss!!\n"); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + + return; + } + } + } else if ((cash > 0) && (debt != 0)) { + for (;;) + { + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("How much do you wish to repay\n"); + printw("him? "); + refresh(); + + wu = get_num(9); + if (wu == -1) + { + wu = cash; + } + if (wu <= cash) + { + cash -= wu; + if ((wu > debt) && (debt > 0)) + { + debt -= (wu + 1); + } else { + debt -= wu; + } + break; + } else { + move(18, 0); + clrtobot(); + fancy_numbers(cash, fancy_num); + printw("Taipan, you only have %s\n", fancy_num); + printw("in cash.\n"); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + } + } + } + port_stats(); + + for (;;) + { + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("How much do you wish to\n"); + printw("borrow? "); + refresh(); + + wu = get_num(9); + if (wu == -1) + { + wu = (cash * 2); + } + if (wu <= (cash * 2)) + { + cash += wu; + debt += wu; + break; + } else { + printw("\n\nHe won't loan you so much, Taipan!"); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + } + } + port_stats(); + + break; + } + } + + if ((debt > 20000) && (cash > 0) && (rand()%5 == 0)) + { + int num = rand()%3 + 1; + + cash = 0; + port_stats(); + + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Bad joss!!\n"); + printw("%d of your bodyguards have been killed\n", num); + printw("by cutthroats and you have been robbed\n"); + printw("of all of your cash, Taipan!!\n"); + + refresh(); + timeout(5000); + getch(); + timeout(-1); + } + + return; +} + +void good_prices(void) +{ + char item[14]; + + int i = rand()%4, + j = rand()%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"); + } + + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Taipan!! The price of %s\n", item); + if (j == 0) + { + price[i] = price[i] / 5; + printw("has dropped to %ld!!\n", price[i]); + } else { + price[i] = price[i] * (rand()%5 + 5); + printw("has risen to %ld!!\n", price[i]); + } + + refresh(); + timeout(3000); + getch(); + timeout(-1); +} + +void overload(void) +{ + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Your ship is overloaded, Taipan!!"); + refresh(); + timeout(5000); + getch(); + timeout(-1); + return; +} + +void new_ship(void) +{ + int choice = 0, + time; + + float amount; + + time = ((year - 1860) * 12) + month; + amount = rand()%(1000 * (time + 5) / 6) * (capacity / 50) + 1000; + + if (cash < amount) + { + return; + } + + fancy_numbers(amount, fancy_num); + + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Do you wish to trade in your "); + if (damage > 0) + { + attrset(A_REVERSE); + printw("damaged"); + attrset(A_NORMAL); + } else { + printw("fine"); + } + printw("\nship for one with 50 more capacity by\n"); + printw("paying an additional %s, Taipan? ", fancy_num); + refresh(); + + 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 ((rand()%2 == 0) && (guns < 1000)) + { + port_stats(); + new_gun(); + } + + port_stats(); + + return; +} + +void new_gun(void) +{ + int choice = 0, + time; + + float amount; + + time = ((year - 1860) * 12) + month; + amount = rand()%(1000 * (time + 5) / 6) + 500; + + if ((cash < amount) || (hold < 10)) + { + return; + } + + fancy_numbers(amount, fancy_num); + + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Do you wish to buy a ship's gun\n"); + printw("for %s, Taipan? ", fancy_num); + refresh(); + + 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(float num, char *fancy) +{ + char number[18]; + + if (num >= 100000000) + { + int num1 = (num / 1000000); + sprintf(number, "%d", num1); + strcpy(fancy, number); + strcat(fancy, " Million"); + } else if (num >= 10000000) { + int num1 = (num / 1000000); + int num2 = (((int) num % 1000000) / 100000); + sprintf(number, "%d", num1); + strcpy(fancy, number); + if (num2 > 0) + { + strcat(fancy, "."); + sprintf(number, "%d", num2); + strcat(fancy, number); + } + strcat(fancy, " Million"); + } else if (num >= 1000000) { + int num1 = (num / 1000000); + int num2 = (((int) num % 1000000) / 10000); + sprintf(number, "%d", num1); + strcpy(fancy, number); + if (num2 > 0) + { + strcat(fancy, "."); + sprintf(number, "%d", num2); + strcat(fancy, number); + } + strcat(fancy, " Million"); + } else { + sprintf(number, "%d", (int) num); + strcpy(fancy, number); + } +} + +int sea_battle(int id, int num_ships) +{ + int orders = 0, + num_on_screen = 0, + ships_on_screen[10], + time = ((year - 1860) * 12) + month, + s0 = num_ships, + ok = 0, + ik = 1, + x, y, i, + input, + status; + + booty = (time / 4 * 1000 * num_ships) + rand()%1000 + 250; + + for (i = 0; i <= 9; i++) + { + ships_on_screen[i] = 0; + } + + clear(); + flushinp(); + fight_stats(num_ships, orders); + + while (num_ships > 0) + { + status = 100 - (((float) damage / capacity) * 100); + if (status <= 0) + { + return 4; + } + flushinp(); + move(3, 0); + clrtoeol(); + printw("Current seaworthiness: %s (%d%%)", st[(status / 20)], status); + refresh(); + + x = 10; + y = 6; + for (i = 0; i <= 9; i++) + { + if (i == 5) + { + x = 10; + y = 12; + } + + if (num_ships > num_on_screen) + { + if (ships_on_screen[i] == 0) + { + usleep(100000); + ships_on_screen[i] = + (int)((ec * ((float) rand() / RAND_MAX)) + 20); + draw_lorcha(x, y); + num_on_screen++; + refresh(); + } + + x += 10; + } + } + + if (num_ships > num_on_screen) + { + move(11, 62); + printw("+"); + } else { + move(11, 62); + printw(" "); + } + + move(16, 0); + printw("\n"); + refresh(); + timeout(3000); + input = getch(); + timeout(-1); + + if ((input == 'F') || (input == 'f')) + { + orders = 1; + } else if ((input == 'R') || (input == 'r')) { + orders = 2; + } else if ((input == 'T') || (input == 't')) { + orders = 3; + } + + if (orders == 0) + { + timeout(3000); + input = getch(); + timeout(-1); + + if ((input == 'F') || (input == 'f')) + { + orders = 1; + } else if ((input == 'R') || (input == 'r')) { + orders = 2; + } else if ((input == 'T') || (input == 't')) { + orders = 3; + } else { + move(3, 0); + clrtoeol(); + printw("Taipan, what shall we do?? (f=fight, r=run, t=throw cargo)"); + refresh(); + timeout(-1); + while ((input != 'F') && (input != 'f') && + (input != 'R') && (input != 'r') && + (input != 'T') && (input != 't')) + { + input = getch(); + } + if ((input == 'F') || (input == 'f')) + { + orders = 1; + } else if ((input == 'R') || (input == 'r')) { + orders = 2; + } else { + orders = 3; + } + } + } + + fight_stats(num_ships, orders); + if ((orders == 1) && (guns > 0)) + { + int targeted, + sk = 0; + + ok = 3; + ik = 1; + move(3, 0); + clrtoeol(); + printw("Aye, we'll fight 'em, Taipan."); + refresh(); + timeout(3000); + input = getch(); + timeout(-1); + + move(3, 0); + clrtoeol(); + printw("We're firing on 'em, Taipan!"); + timeout(1000); + input = getch(); + timeout(-1); + refresh(); + + 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)) + { + int j; + + x = 10; + y = 6; + for (j = 0; j <= 9; j++) + { + if (j == 5) + { + x = 10; + y = 12; + } + + if (num_ships > num_on_screen) + { + if (ships_on_screen[j] == 0) + { + usleep(100000); + ships_on_screen[j] = + (int)((ec * ((float) rand() / RAND_MAX)) + 20); + draw_lorcha(x, y); + num_on_screen++; + } + + x += 10; + } + } + } + + move(11, 62); + if (num_ships > num_on_screen) + { + printw("+"); + } else { + printw(" "); + } + + move(16, 0); + printw("\n"); + refresh(); + + targeted = rand()%10; + while (ships_on_screen[targeted] == 0) + { + targeted = rand()%10; + } + + x = (targeted < 5) ? ((targeted + 1) * 10) : ((targeted - 4) * 10); + y = (targeted < 5) ? 6 : 12; + + draw_blast(x, y); + refresh(); + usleep(100000); + + draw_lorcha(x, y); + refresh(); + usleep(100000); + + draw_blast(x, y); + refresh(); + usleep(100000); + + draw_lorcha(x, y); + refresh(); + usleep(100000); + + ships_on_screen[targeted] -= rand()%30 + 10; + + if (ships_on_screen[targeted] <= 0) + { + num_on_screen--; + num_ships--; + sk++; + ships_on_screen[targeted] = 0; + + usleep(100000); + + sink_lorcha(x, y); + + if (num_ships == num_on_screen) + { + move(11, 62); + printw(" "); + } + + fight_stats(num_ships, orders); + refresh(); + } + + if (num_ships == 0) + { + i += guns; + } else { + usleep(500000); + } + } + move(3, 0); + clrtoeol(); + if (sk > 0) + { + printw("Sunk %d of the buggers, Taipan!", sk); + } else { + printw("Hit 'em, but didn't sink 'em, Taipan!"); + } + refresh(); + timeout(3000); + input = getch(); + timeout(-1); + + if ((rand()%s0 > (num_ships * .6 / id)) && (num_ships > 2)) + { + int ran = rand()%(num_ships / 3 / id) + 1; + + num_ships -= ran; + fight_stats(num_ships, orders); + move(3, 0); + clrtoeol(); + printw("%d ran away, Taipan!", ran); + + 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--; + + x = (i < 5) ? ((i + 1) * 10) : ((i - 4) * 10); + y = (i < 5) ? 6 : 12; + clear_lorcha(x, y); + refresh(); + usleep(100000); + } + } + if (num_ships == num_on_screen) + { + move(11, 62); + printw(" "); + refresh(); + } + + } + + move(16, 0); + + refresh(); + timeout(3000); + input = getch(); + timeout(-1); + + if ((input == 'F') || (input == 'f')) + { + orders = 1; + } else if ((input == 'R') || (input == 'r')) { + orders = 2; + } else if ((input == 'T') || (input == 't')) { + orders = 3; + } + } + } else if ((orders == 1) && (guns == 0)) { + move(3, 0); + clrtoeol(); + printw("We have no guns, Taipan!!"); + refresh(); + timeout(3000); + input = getch(); + timeout(-1); + } else if (orders == 3) { + int choice = 0; + + long amount = 0, + total = 0; + + move(18, 0); + printw("You have the following on board, Taipan:"); + move(19, 4); + printw("Opium: %d", hold_[0]); + move(19, 24); + printw("Silk: %d", hold_[1]); + move(20, 5); + printw("Arms: %d", hold_[2]); + move(20, 21); + printw("General: %d", hold_[3]); + + move(3, 0); + clrtoeol(); + printw("What shall I throw overboard, Taipan? "); + refresh(); + + while ((choice != 'O') && (choice != 'o') && + (choice != 'S') && (choice != 's') && + (choice != 'A') && (choice != 'a') && + (choice != 'G') && (choice != 'g') && + (choice != '*')) + { + choice = get_one(); + } + + if ((choice == 'O') || (choice == 'o')) + { + choice = 0; + } else if ((choice == 'S') || (choice == 's')) { + choice = 1; + } else if ((choice == 'A') || (choice == 'a')) { + choice = 2; + } else if ((choice == 'G') || (choice == 'g')) { + choice = 3; + } else { + choice = 4; + } + + if (choice < 4) + { + move(3, 0); + clrtoeol(); + printw("How much, Taipan? "); + refresh(); + + 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) + { + move(3, 0); + clrtoeol(); + printw("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); + } + move(18, 0); + clrtobot(); + refresh(); + + timeout(3000); + input = getch(); + timeout(-1); + } else { + move(3, 0); + clrtoeol(); + printw("There's nothing there, Taipan!"); + move(18, 0); + clrtobot(); + refresh(); + + timeout(3000); + input = getch(); + timeout(-1); + } + } + + if ((orders == 2) || (orders == 3)) + { + if (orders == 2) + { + move(3, 0); + clrtoeol(); + printw("Aye, we'll run, Taipan."); + refresh(); + timeout(3000); + input = getch(); + timeout(-1); + } + + ok += ik++; + if (rand()%ok > rand()%num_ships) + { + flushinp(); + move(3, 0); + clrtoeol(); + printw("We got away from 'em, Taipan!"); + refresh(); + timeout(3000); + input = getch(); + timeout(-1); + num_ships = 0; + } else { + move(3, 0); + clrtoeol(); + printw("Couldn't lose 'em."); + refresh(); + timeout(3000); + input = getch(); + timeout(-1); + + if ((num_ships > 2) && (rand()%5 == 0)) + { + int lost = (rand()%num_ships / 2) + 1; + + num_ships -= lost; + fight_stats(num_ships, orders); + move(3, 0); + clrtoeol(); + printw("But we escaped from %d of 'em!", lost); + + 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--; + + x = (i < 5) ? ((i + 1) * 10) : ((i - 4) * 10); + y = (i < 5) ? 6 : 12; + clear_lorcha(x, y); + refresh(); + usleep(100000); + } + } + if (num_ships == num_on_screen) + { + move(11, 62); + printw(" "); + refresh(); + } + } + + move(16, 0); + + refresh(); + timeout(3000); + input = getch(); + timeout(-1); + + if ((input == 'F') || (input == 'f')) + { + orders = 1; + } else if ((input == 'R') || (input == 'r')) { + orders = 2; + } else if ((input == 'T') || (input == 't')) { + orders = 3; + } + } + } + } + + if (num_ships > 0) + { + move(3, 0); + clrtoeol(); + printw("They're firing on us, Taipan!"); + + refresh(); + timeout(3000); + input = getch(); + timeout(-1); + flushinp(); + for (i = 0; i < 3; i++) + { + for (y = 0; y < 24; y++) + { + for (x = 0; x < 79; x++) + { + move(y, x); + printw("*"); + } + } + refresh(); + usleep(200000); + clear(); + refresh(); + usleep(200000); + } + + fight_stats(num_ships, orders); + x = 10; + y = 6; + for (i = 0; i <= 9; i++) + { + if (i == 5) + { + x = 10; + y = 12; + } + + if (ships_on_screen[i] > 0) + { + draw_lorcha(x, y); + } + + x += 10; + } + + move(11, 62); + if (num_ships > num_on_screen) + { + printw("+"); + } else { + printw(" "); + } + + move(3, 0); + clrtoeol(); + printw("We've been hit, Taipan!!"); + + refresh(); + timeout(3000); + input = getch(); + timeout(-1); + + i = (num_ships > 15) ? 15 : num_ships; + if ((guns > 0) && ((rand()%100 < (((float) damage / capacity) * 100)) || + ((((float) damage / capacity) * 100) > 80))) + { + i = 1; + guns--; + hold += 10; + fight_stats(num_ships, orders); + move(3, 0); + clrtoeol(); + printw("The buggers hit a gun, Taipan!!"); + fight_stats(num_ships, orders); + + refresh(); + timeout(3000); + input = getch(); + timeout(-1); + } + damage = damage + ((ed * i * id) * ((float) rand() / RAND_MAX)) + (i / 2); + if ((id == GENERIC) && (rand()%20 == 0)) + { + return 2; + } + } + } + + if (orders == 1) + { + clear(); + fight_stats(num_ships, orders); + move(3, 0); + clrtoeol(); + printw("We got 'em all, Taipan!"); + refresh(); + timeout(3000); + getch(); + timeout(-1); + + return 1; + } else { + return 3; + } +} + +void draw_lorcha(int x, int y) +{ + move (y, x); + printw("-|-_|_ "); + move ((y + 1), x); + printw("-|-_|_ "); + move ((y + 2), x); + printw("_|__|__/"); + move ((y + 3), x); + printw("\\_____/ "); +} + +void clear_lorcha(int x, int y) +{ + move (y, x); + printw(" "); + move ((y + 1), x); + printw(" "); + move ((y + 2), x); + printw(" "); + move ((y + 3), x); + printw(" "); +} + +void draw_blast(int x, int y) +{ + move (y, x); + printw("********"); + move ((y + 1), x); + printw("********"); + move ((y + 2), x); + printw("********"); + move ((y + 3), x); + printw("********"); +} + +void sink_lorcha(int x, int y) +{ + int delay = rand()%20; + + move (y, x); + printw(" "); + move ((y + 1), x); + printw("-|-_|_ "); + move ((y + 2), x); + printw("-|-_|_ "); + move ((y + 3), x); + printw("_|__|__/"); + refresh(); + usleep(500000); + if (delay == 0) + { + usleep(500000); + } + + move ((y + 1), x); + printw(" "); + move ((y + 2), x); + printw("-|-_|_ "); + move ((y + 3), x); + printw("-|-_|_ "); + refresh(); + usleep(500000); + if (delay == 0) + { + usleep(500000); + } + + move ((y + 2), x); + printw(" "); + move ((y + 3), x); + printw("-|-_|_ "); + refresh(); + usleep(500000); + if (delay == 0) + { + usleep(500000); + } + + move ((y + 3), x); + printw(" "); + refresh(); + usleep(500000); + if (delay == 0) + { + usleep(500000); + } +} + +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"); + } + + move(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); + } + move(0, 5); + if (ships == 1) + { + printw("ship attacking, Taipan! \n"); + } else { + printw("ships attacking, Taipan!\n"); + } + printw("Your orders are to: %s", ch_orders); + move(0, 50); + printw("| We have"); + move(1, 50); + printw("| %d guns", guns); + move(2, 50); + printw("----------"); + move(16, 0); + + return; +} + +void mchenry(void) +{ + int choice = 0; + + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + printw("Taipan, Mc Henry from the Hong Kong\n"); + printw("Shipyards has arrived!! He says, \"I see\n"); + printw("ye've a wee bit of damage to yer ship.\n"); + printw("Will ye be wanting repairs? "); + refresh(); + + while ((choice != 'Y') && (choice != 'y') && + (choice != 'N') && (choice != 'n')) + { + choice = get_one(); + } + + if ((choice == 'Y') || (choice == 'y')) + { + int percent = ((float) damage / capacity) * 100, + time = ((year - 1860) * 12) + month; + + long br = ((((60 * (time + 3) / 4) * (float) rand() / RAND_MAX) + + 25 * (time + 3) / 4) * capacity / 50), + repair_price = (br * damage) + 1, + amount; + + move(18, 0); + clrtobot(); + printw("Och, 'tis a pity to be %d%% damaged.\n", percent); + printw("We can fix yer whole ship for %ld,\n", repair_price); + printw("or make partial repairs if you wish.\n"); + printw("How much will ye spend? "); + refresh(); + + for (;;) + { + move(21, 24); + amount = get_num(9); + if (amount == -1) + { + amount = cash; + } + if (amount <= cash) + { + cash -= amount; + damage -= (int)((amount / br) + .5); + damage = (damage < 0) ? 0 : damage; + port_stats(); + refresh(); + break; + } + } + } + + return; +} + +void retire(void) +{ + move(16, 0); + clrtobot(); + printw("Comprador's Report\n\n"); + attrset(A_REVERSE); + printw(" \n"); + printw(" Y o u ' r e a \n"); + printw(" \n"); + printw(" M I L L I O N A I R E ! \n"); + printw(" \n"); + attrset(A_NORMAL); + refresh(); + timeout(5000); + getch(); + timeout(-1); + + final_stats(); +} + +void final_stats(void) +{ + int years = year - 1860, + time = ((year - 1860) * 12) + month, + choice = 0; + + clear(); + printw("Your final status:\n\n"); + cash = cash + bank - debt; + fancy_numbers(cash, fancy_num); + printw("Net cash: %s\n\n", fancy_num); + printw("Ship size: %d units with %d guns\n\n", capacity, guns); + printw("You traded for %d year", years); + if (years != 1) + { + printw("s"); + } + printw(" and %d month", month); + if (month > 1) + { + printw("s"); + } + printw("\n\n"); + cash = cash / 100 / time; + attrset(A_REVERSE); + printw("Your score is %.0f.\n", cash); + attrset(A_NORMAL); + printw("\n"); + if ((cash < 100) && (cash >= 0)) + { + printw("Have you considered a land based job?\n\n\n"); + } else if (cash < 0) { + printw("The crew has requested that you stay on\n"); + printw("shore for their safety!!\n\n"); + } else { + printw("\n\n\n"); + } + printw("Your Rating:\n"); + printw(" _______________________________\n"); + printw("|"); + if (cash > 49999) + { + attrset(A_REVERSE); + } + printw("Ma Tsu"); + attrset(A_NORMAL); + printw(" 50,000 and over |\n"); + printw("|"); + if ((cash < 50000) && (cash > 7999)) + { + attrset(A_REVERSE); + } + printw("Master Taipan"); + attrset(A_NORMAL); + printw(" 8,000 to 49,999|\n"); + printw("|"); + if ((cash < 8000) && (cash > 999)) + { + attrset(A_REVERSE); + } + printw("Taipan"); + attrset(A_NORMAL); + printw(" 1,000 to 7,999|\n"); + printw("|"); + if ((cash < 1000) && (cash > 499)) + { + attrset(A_REVERSE); + } + printw("Compradore"); + attrset(A_NORMAL); + printw(" 500 to 999|\n"); + printw("|"); + if (cash < 500) + { + attrset(A_REVERSE); + } + printw("Galley Hand"); + attrset(A_NORMAL); + printw(" less than 500|\n"); + printw("|_______________________________|\n\n"); + + while ((choice != 'Y') && (choice != 'y') && + (choice != 'N') && (choice != 'n')) + { + move (22, 0); + clrtoeol(); + printw("Play again? "); + refresh(); + 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; + } + + clear(); + refresh(); + nocbreak(); + endwin(); + + exit(0); +} diff --git a/taipan.c b/taipan.c new file mode 100644 index 0000000..b53ac1c --- /dev/null +++ b/taipan.c @@ -0,0 +1,2706 @@ +#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 release!! */ +// #define COMBAT_TEST + +/**** atari-specific stuff */ + +void atari_text_setup() { + POKE(710, 0xc0); // green background + POKE(709, 0x0c); // bright text + POKE(756, 0xb8); // use our custom font +} + +/* 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); + +/* 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() 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 */ +int lc(int a) { + if(a >= 'A' && a <= 'Z') a ^= 0x20; + return a; +} + +/* TODO: rewrite in asm */ +/* wrapper for cgetc() 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(cgetc()); +} + +/* 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!!"); + 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); + 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"); + timed_getch(TMOUT_3S); + + if(input == 'f') { + orders = 1; + } else if(input == 'r') { + orders = 2; + } else if (input == 't') { + orders = 3; + } + + if(orders == 0) { + timed_getch(TMOUT_3S); + + 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(Fight, Run, 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."); + 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, 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!"); + } + 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!"); + + 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); + + 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, 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(); + + timed_getch(TMOUT_3S); + } else { + gotoxy(0, 3); + clrtoeol(); + cputs("There's nothing there, Taipan!"); + gotoxy(0, 18); + clrtobot(); + + 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!"); + 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(); + // 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); + + 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(); + + 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!!"); + + 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 * 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); + + 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) + // 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!"); + timed_getch(TMOUT_3S); + + 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 = cgetc()) != '\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 = cgetc()) != '\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); + + timed_getch(TMOUT_5M); + + 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(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) +{ + 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); + timed_getch(TMOUT_5S); + + 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"); + + 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(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!"); + + timed_getch(TMOUT_5S); + } + } else { + gotoxy(0, 18); + clrtobot(); + cputs("You have only "); + cprintulong(hold_[i]); + cputs(", Taipan.\r\n"); + + 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(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"); + + timed_getch(TMOUT_5S); + } + } + 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."); + 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 ships approaching, Taipan!\r\n"); + + timed_getch(TMOUT_3S); + + result = sea_battle(GENERIC, num_ships); + } + + 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"); + + timed_getch(TMOUT_3S); + + if (li > 0) + { + cputs("Good joss!! They let us be!!\r\n"); + + timed_getch(TMOUT_3S); + + 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"); + + timed_getch(TMOUT_3S); + + 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!!!"); + + 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"); + 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) + if(randclamp(damage / capacity * 3) >= 1) + { + cputs("We're going down, Taipan!!\r\n"); + timed_getch(TMOUT_5S); + + final_stats(); + } + } + + cputs(" We made it!!\r\n\n"); + 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); + 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 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"); + + timed_getch(TMOUT_5S); + + final_stats(); + } else if ((choice == 'Y') || (choice == 'y')) { + cash += i; + debt += j; + port_stats(); + + compradores_report(); + cputs("Very well, Taipan. Good joss!!\r\n"); + + 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(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"); + + timed_getch(TMOUT_5S); + } + } + } + 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!"); + + timed_getch(TMOUT_5S); + } + } + 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"); + + timed_getch(TMOUT_5S); + } + + 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"); + + timed_getch(TMOUT_3S); +} + +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 = cgetc()) != 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"); + + timed_getch(TMOUT_5S); + } + } + 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."); + + timed_getch(TMOUT_5S); + } + } + 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"); + + 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 = 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"); + + 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"); + + 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); + cputs(" in cash, Taipan!!\r\n"); + + 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; +} diff --git a/taipan.c_before_timed_getch b/taipan.c_before_timed_getch new file mode 100644 index 0000000..7b399eb --- /dev/null +++ b/taipan.c_before_timed_getch @@ -0,0 +1,2877 @@ +#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/taipan.dsk b/taipan.dsk new file mode 100644 index 0000000..5736ca9 Binary files /dev/null and b/taipan.dsk differ diff --git a/timed_getch.pl b/timed_getch.pl new file mode 100644 index 0000000..d9f267e --- /dev/null +++ b/timed_getch.pl @@ -0,0 +1,48 @@ +#!/usr/bin/perl -w + +# replace this: + +# timeout(5000); +# getch(); +# timeout(-1); + +# with this: + +# timed_getch(83); + +# the 83 comes from int(5000/1000*60) + +# indentation levels must be preserved! + +while(<>) { + my $line = $_; + if(/^(\s+)timeout\s*\((\d+)/) { + #warn "found timeout($2) at $.\n"; + my $indent = $1; + my $jiffies = int(($2 / 1000) * 60); + + if($jiffies == 60) { + $jiffies = "TMOUT_1S"; + } elsif($jiffies == 180) { + $jiffies = "TMOUT_3S"; + } elsif($jiffies == 300) { + $jiffies = "TMOUT_5S"; + } else { + warn "no TMOUT_* constant for $jiffies, line $.\n"; + } + + my $next = <>; + if($next =~ /getch\(\)/) { + print $indent . "timed_getch($jiffies);\n"; + my $last = <>; + if($last !~ /timeout\(-1\)/) { + print $last; + } + } else { + print $line; + print $next; + } + } else { + print $line; + } +} diff --git a/timed_getch.s b/timed_getch.s new file mode 100644 index 0000000..8675290 --- /dev/null +++ b/timed_getch.s @@ -0,0 +1,26 @@ + + .export _timed_getch, _set_jiffy_timer + .import _cgetc + +_set_jiffy_timer: + sei + sta 540 + stx 541 + cli + rts + +_timed_getch: + jsr _set_jiffy_timer +wait4key: + lda 540 + ora 541 + beq done + ldx 764 + inx + beq wait4key + jmp _cgetc + +done: + lda #$ff ; return -1 + tax + rts diff --git a/title.pl b/title.pl new file mode 100644 index 0000000..2f402f9 --- /dev/null +++ b/title.pl @@ -0,0 +1,7 @@ +#!/usr/bin/perl -w + +use bytes; +print chr($_) for (0xff, 0xff, 0x40, 0xbc, 0xff, 0xbf); +undef $/; +$_ = <>; +print $_; -- cgit v1.2.3