diff options
author | B. Watson <yalhcru@gmail.com> | 2015-12-29 23:10:50 -0500 |
---|---|---|
committer | B. Watson <yalhcru@gmail.com> | 2015-12-29 23:10:50 -0500 |
commit | 2300d2813a524cbfeabac794335e7abe99263df6 (patch) | |
tree | d729ca4f99634788cbb3a2101a5b5854a4bc2d06 | |
download | taipan-2300d2813a524cbfeabac794335e7abe99263df6.tar.gz |
initial commit
-rw-r--r-- | LORCHA.DAT | bin | 0 -> 49 bytes | |||
-rw-r--r-- | LORCHA.LST | 2 | ||||
-rw-r--r-- | Makefile | 69 | ||||
-rw-r--r-- | README.txt | 234 | ||||
-rw-r--r-- | SOUND1.LST | 1 | ||||
-rw-r--r-- | TITLE.DAT | bin | 0 -> 960 bytes | |||
-rw-r--r-- | TITLE.LST | 5 | ||||
-rw-r--r-- | apple_call_addresses | 11 | ||||
-rw-r--r-- | applesounds.wav | bin | 0 -> 14449964 bytes | |||
-rw-r--r-- | bank.pl | 13 | ||||
-rwxr-xr-x | bitmapdump.pl | 34 | ||||
-rw-r--r-- | characters | 55 | ||||
-rw-r--r-- | convfont.c | 146 | ||||
-rw-r--r-- | custom.cfg | 43 | ||||
-rw-r--r-- | debt.pl | 17 | ||||
-rw-r--r-- | dfoxtest.c | 210 | ||||
-rw-r--r-- | draw_lorcha.s | 100 | ||||
-rw-r--r-- | findfont.pl | 49 | ||||
-rw-r--r-- | font | bin | 0 -> 768 bytes | |||
-rw-r--r-- | fontrip.txt | 41 | ||||
-rw-r--r-- | fonttmp | 109 | ||||
-rw-r--r-- | jsleep.s | 11 | ||||
-rw-r--r-- | lorcha.c | 9 | ||||
-rw-r--r-- | lorcha.pl | 20 | ||||
-rw-r--r-- | lorchatest.c | 37 | ||||
-rw-r--r-- | oldcurses.c | 138 | ||||
-rw-r--r-- | rand.s | 23 | ||||
-rw-r--r-- | romfont | bin | 0 -> 1024 bytes | |||
-rw-r--r-- | taifont | bin | 0 -> 1024 bytes | |||
-rw-r--r-- | taipan-applesoft.txt | 961 | ||||
-rw-r--r-- | taipan-orig.c | 2735 | ||||
-rw-r--r-- | taipan.c | 2706 | ||||
-rw-r--r-- | taipan.c_before_timed_getch | 2877 | ||||
-rw-r--r-- | taipan.dsk | bin | 0 -> 143360 bytes | |||
-rw-r--r-- | timed_getch.pl | 48 | ||||
-rw-r--r-- | timed_getch.s | 26 | ||||
-rw-r--r-- | title.pl | 7 |
37 files changed, 10737 insertions, 0 deletions
diff --git a/LORCHA.DAT b/LORCHA.DAT Binary files differnew file mode 100644 index 0000000..e11c6fe --- /dev/null +++ b/LORCHA.DAT 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,02 ? CHR$(125)3 POSITION 0,05 OPEN #1,8,0,"H:LORCHA.DAT"10 ? " = ="20 ? " "30 ? " "40 ? " "50 ? " + | |"60 ? " "70 ? " "100 FOR Y=0 TO 6105 FOR X=0 TO 6110 ? #1;CHR$(PEEK(40000+X+Y*40));120 NEXT X130 NEXT Y140 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 320 FOR J=20 TO 10 STEP -130 SOUND 0,J,10,1040 NEXT J50 NEXT I1000 END
\ No newline at end of file diff --git a/TITLE.DAT b/TITLE.DAT Binary files differnew file mode 100644 index 0000000..83616aa --- /dev/null +++ b/TITLE.DAT 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 lowercase5 POKE 752,110 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,0100 ? " + + + + "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 I920 CLOSE #11000 POSITION 0,181010 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 Binary files differnew file mode 100644 index 0000000..d5642a6 --- /dev/null +++ b/applesounds.wav @@ -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<length($data); $offs += ($height * $width)) { + for my $char (0..$width-1) { + printf("%7x ", $offs + ($char * $height)); + printf("%2x", $offs / $height + $char); + } + + print "\n"; + + for my $line (0..$height-1) { + for my $char (0..$width-1) { + my $index = $offs + $line + ($char * $height); + if($index < length($data)) { + my $bitmap = sprintf(" %08b", ord(substr($data, $index, 1))); + $bitmap =~ s/0/./g; + $bitmap =~ s/1/X/g; + print $bitmap; + } + } + print "\n"; + } +} diff --git a/characters b/characters new file mode 100644 index 0000000..19f3ced --- /dev/null +++ b/characters @@ -0,0 +1,55 @@ + +Apple II lorcha is 49x40 pixels. The apple uses a 7x8 matrix for +characters, so 7x5 characters. For the Atari with its 8x8 matrix, I cut +off the leftmost column so it's now 48x40, or 6x5 = 30 characters. Of +these, 6 are inverse spaces, 4 are regular spaces, and 3 are dups of +other characters in the lorcha. Means we need 17 custom characters to +draw an undamaged lorcha, leaving only 2 or 3 for showing damage. + +X = inverse space, . = space, A-Q are our custom chars. + + 012345 + ______ +0 |.ABC.D +1 |.EXFGH +2 |.EXFGI +3 |JKLMNO +4 |PXXXXQ + +name, screencode, 8 bytes of pixels, ascii +A, 2, 00 00 00 00 00 00 ff 3f, " +B, 3, 18 1f 1f 10 10 10 ff ff, # +C, 4, 00 f0 fc 3e 00 00 fc f0, $ +D, 6, 00 00 40 70 7c 47 40 40, & +E, 1b, 1f 3f ff 3f 1f 3f ff 7f, ; +F, 1c, c0 f0 fc f0 c0 f0 fc f0, < +G, 1d, 07 03 07 0f 07 03 07 f0, = +H, 1e, fe fc fe ff fe f8 fe ff, > +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 <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <unistd.h> +#include <stdio.h> + +/* 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:;>=<? + + atari screen code order: + 0-31: space !"#$%&'()*+,-./0-9:;>=<? + 32-63: @A-Z[\]^_ + 64-95 graphics chars + 96-127: `a-z and 4 graphics chars +*/ + +/* custom characters for ship graphics. 1st byte + is screencode, the other 8 are the pixel data. */ +char shipdata[][9] = { + {0x02 , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f}, + {0x03 , 0x18, 0x1f, 0x1f, 0x10, 0x10, 0x10, 0xff, 0xff}, + {0x04 , 0x00, 0xf0, 0xfc, 0x3e, 0x00, 0x00, 0xfc, 0xf0}, + {0x06 , 0x00, 0x00, 0x40, 0x70, 0x7c, 0x47, 0x40, 0x40}, + {0x1b , 0x1f, 0x3f, 0xff, 0x3f, 0x1f, 0x3f, 0xff, 0x7f}, + {0x1c , 0xc0, 0xf0, 0xfc, 0xf0, 0xc0, 0xf0, 0xfc, 0xf0}, + {0x1d , 0x07, 0x03, 0x07, 0x0f, 0x07, 0x03, 0x07, 0x0f}, + {0x1e , 0xfe, 0xfc, 0xfe, 0xff, 0xfe, 0xf8, 0xfe, 0xff}, + {0x20 , 0xfe, 0xf8, 0xfe, 0xff, 0xfe, 0xfe, 0xfd, 0xf9}, + {0x3b , 0xc0, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f}, + {0x3c , 0x00, 0x00, 0xc0, 0xff, 0x8f, 0x8e, 0x8e, 0xfe}, + {0x3d , 0x38, 0x38, 0x38, 0x38, 0xff, 0x38, 0x38, 0x38}, + {0x3e , 0x00, 0x00, 0x00, 0x01, 0xff, 0xe3, 0xe3, 0xe3}, + {0x3f , 0x00, 0x00, 0x00, 0xff, 0x8e, 0x8e, 0x8e, 0xff}, + {0x40 , 0xc3, 0xcf, 0xff, 0xff, 0x3f, 0x3e, 0x3c, 0xf8}, + {0x46 , 0x1f, 0x0f, 0x07, 0x07, 0x07, 0x03, 0x03, 0x03}, + {0x47 , 0xf0, 0xe0, 0xc0, 0xc0, 0xc0, 0xc0, 0x80, 0x80}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +/* this ends up in LORCHA.DAT */ +char shipshape[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x03, 0x04, 0x00, 0x06, + 0x00, 0x00, 0x1b, 0x80, 0x1c, 0x1d, 0x1e, + 0x00, 0x00, 0x1b, 0x80, 0x1c, 0x1d, 0x20, + 0x00, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, + 0x00, 0x46, 0x80, 0x80, 0x80, 0x80, 0x47, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +void bitswap(unsigned char *b, int lim) { + unsigned char j, k; + // fprintf(stderr, "bitswap(%x, %d)\n", b, lim); + do { + k = b[lim]; + j = 0; + j |= (k & 0x01 ? 0x80 : 0); + j |= (k & 0x02 ? 0x40 : 0); + j |= (k & 0x04 ? 0x20 : 0); + j |= (k & 0x08 ? 0x10 : 0); + j |= (k & 0x10 ? 0x08 : 0); + j |= (k & 0x20 ? 0x04 : 0); + j |= (k & 0x40 ? 0x02 : 0); + j |= (k & 0x80 ? 0x01 : 0); + b[lim] = j; + } while(--lim > 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__; +} + @@ -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 <stdint.h>*/ +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 <stdbool.h>*/ +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; + } +} + Binary files differdiff --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. @@ -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 <peekpoke.h> + +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; +} @@ -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 + Binary files differBinary files differdiff --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 <jlink@ilbbs.com> + * + * Apple ][ program coded by: + * Ronald J. Berg + * ------------------------------------------------------------------------ */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <curses.h> + +#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 <conio.h> +#include <time.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <peekpoke.h> + +/* 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 <conio.h> +#include <time.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <peekpoke.h> + +/* 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 Binary files differnew file mode 100644 index 0000000..5736ca9 --- /dev/null +++ b/taipan.dsk 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 $_; |