/* Callisto written for the Amiga by Russell Wallace 1987. Please distribute
 * this source code along with executable program. Compile with long integer
 * compile option and link with files printlong_unsigned.o scanlong_unsigned.o
 * input.o console.o. This code may be freely used as a source of example
 * routines. Requires instructions text file in same directory. */

/* Version 1.01 recompiled with Aztec C 9 July 1988 */

#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MINUS(a,b) ((a)>(b)?((a)-(b)):(0))	/* Avoid negative result */

#define NOTENOUGHCASH "Sorry, but you only have enough money for "
#define NOTENOUGHWORKERS "Sorry, but there are only enough workers for "
#define PLAYERS 6
#define MAXLONG 4000000000	/* To avoid overflow risk, won't allow anything */
							/* to exceed this quantity */
			/* Costs of various items */
#define SHIP 350
#define ARM 240
#define MINE 170
#define DOME 400
#define RECYCLER 550
#define LASER 730
#define BOMB 4
#define FUEL 2
#define PILOT 6
			/* Labour requirements for various items */
#define LARM 8
#define LMINE 10
#define LDOME 30
#define LRECYCLER 22
#define LLASER 12
			/* Payment for various things */
#define ORE 2
#define DSHIP 70
#define DLASER 60
#define DRECYCLER 62
#define DDOME 70

#define INLEN 12

long rnd ();

struct variables
{
	unsigned long domes,recyclers,fuel,ships,armed,lasers,mines,bombs;
	unsigned long ore,money,pilots,pop;
} player[PLAYERS];	/* Set up variables for each player */

unsigned long innum;	/* Number input by player */
char string[INLEN];		/* Raw form of input number */
unsigned long shipd,laserd,recyclerd,domed;	/* Numbers of targets destroyed */
unsigned long popk;		/* Number of people killed */
unsigned long shipl;	/* Number of attackers shot down */
unsigned long bombu;	/* Number of bombs expended */
unsigned long payment;	/* Payment for attack */
unsigned long year;

char *enemyname[]=
{
	"Consolidated Minerals",
	"Global Resources Inc.",
	"Eastern Mining Co-operative",
	"European Petroleum & Minerals",
	"General Metals"
};

main ()
{
	register int i,target;
	short readfile ();
	if (!(start ()))
		exit (5);	/* Try to set up window */
NEWGAME:
	print ("\fWelcome to Callisto by Russell Wallace. Do you want instructions? (Type Y or N):");
	if (yesno ())
	{
		readfile ("Callisto.instructions.doc");
print ("\nThis version 1.01 has been recompiled with Aztec C on July 9th 1988 and will automatically open a window the size of the ");
print ("Workbench screen on a PAL or NTSC Amiga.\n");
		anykey ();
	}
	year=0;
	for (i=0;i<PLAYERS;i++)
	{
		player[i].domes=RangeRand (2)+3;
		player[i].recyclers=RangeRand (2)+3;
		player[i].fuel=RangeRand (50)+50;
		player[i].ships=RangeRand (1)+1;
		player[i].armed=0;
		player[i].lasers=0;
		player[i].mines=0;
		player[i].bombs=0;
		player[i].ore=0;
		player[i].money=RangeRand (200)+700;
		player[i].pilots=RangeRand (2)+8;
		player[i].pop=RangeRand (20)+80;
	}
LOOP:
	resetscroll ();
	print ("\fStatus report for year ");
	printlong (++year);
	print ("\n\nCredit: ");
	printlong (player[0].money);
	print ("\nPopulation: ");
	printlong (player[0].pop);
	print ("\nPilots: ");
	printlong (player[0].pilots);
	print ("\nDomes: ");
	printlong (player[0].domes);
	print ("\nRecycling units: ");
	printlong (player[0].recyclers);
	print ("\nFuel reserves: ");
	printlong (player[0].fuel);
	print ("\nSpacecraft: ");
	printlong (player[0].ships);
	print ("\nShips armed: ");
	printlong (player[0].armed);
	print ("\nLaser batteries: ");
	printlong (player[0].lasers);
	print ("\nNuclear warheads: ");
	printlong (player[0].bombs);
	print ("\nMines: ");
	printlong (player[0].mines);
	print ("\nOre: ");
	printlong (player[0].ore);
	writechar ((long)'\n');
	anykey ();

	print ("\fEnter number of domes to build: ");
	howmany (DOME,LDOME,NOTENOUGHWORKERS);
	player[0].domes+=innum;
	print ("Enter number of recycling units to build: ");
	howmany (RECYCLER,LRECYCLER,NOTENOUGHWORKERS);
	player[0].recyclers+=innum;
	print ("Enter number of laser batteries to build: ");
	howmany (LASER,LLASER,NOTENOUGHWORKERS);
	player[0].lasers+=innum;
	print ("Enter number of spacecraft to buy: ");
	howmany (SHIP,0,0L);
	player[0].ships+=innum;
	print ("Enter number of spacecraft to arm: ");
	howmany (ARM,LARM,NOTENOUGHWORKERS);
	player[0].armed+=innum;
	print ("Enter number of tons of fuel to buy: ");
	howmany (FUEL,0,0L);
	player[0].fuel+=innum;
	print ("Enter number of warheads to buy: ");
	howmany (BOMB,0,0L);
	player[0].bombs+=innum;
	print ("Enter number of pilots to train: ");
	howmany (PILOT,(player[0].pop/2)-1-player[0].pilots,"Sorry, but you can't spare that many people - maximum number is ");
	player[0].pilots+=innum;
	print ("Enter number of mines to set up: ");
	howmany (MINE,LMINE,NOTENOUGHWORKERS);
	i=MIN (player[0].ships,player[0].pilots);
	i=MIN (i,player[0].fuel/8);
	if (innum>i)
	{
		player[0].money+=((innum-i)*MINE);
		innum=i;
		print ("You can't send that many spaceships - only ");
		printlong (innum);
		print (" mines can be set up.\n");
	}
	player[0].mines+=innum;
	player[0].fuel=MINUS (player[0].fuel,innum*8);
	print ("Do you want to send ships to recover the accumulated ore and sell it to Earth? ");
	if (yesno ())
	{
		innum=player[0].ore/20;
		innum=MIN (innum,player[0].ships);
		innum=MIN (innum,player[0].pilots);
		innum=MIN (innum,player[0].fuel/8);
		if (innum)
		{
			printnoun (innum,"ship");
			print ("sent to recover ore. This was sold for ");
			player[0].fuel-=innum*8;
			innum*=20;
			player[0].ore-=innum;
			player[0].money+=innum*ORE;
			printlong (innum*ORE);
			print (" credits.\n");
		}
			else
				print ("No ore recovered yet.\n");
	}
	target=0;
	for (i=1;i<PLAYERS;i++)
		if (player[i].pop)	/* If somebody has some people left, there's */
			target++;		/* someone to attack */
	if (target) {
	print ("Do you want to launch an attack? ");
	if (yesno ())
	{
		print ("Enter player to attack:\n");
		for (i=1;i<PLAYERS;i++)
			if (player[i].pop)
		{
			printlong (i);
			print (". ");
			print (enemyname [i-1]);
			writechar ((long)'\n');
		}
NOBODYTHERE:
		do
		{
			print ("Type number: ");
			getnum ();
		}
		while (innum<1 || innum>PLAYERS-1);
		if (player[innum].pop==0)
		{
			print ("That base has already been wiped out.\n");
			goto NOBODYTHERE;
		}
		target=innum;
		print ("How many ships to send? ");
		getnum ();
		i=MIN (player[0].armed,player[0].pilots);
		i=MIN (i,player[0].bombs/2);
		i=MIN (i,player[0].fuel/8);
		if (innum<i)
			i=innum;
		if (!innum)
			goto NOATTACK;
		if (innum>i)
		{
			innum=i;
			if (i==0)
			{
				print ("Sorry, but you can't send any right now.");
				goto NOATTACK;
			}
			print ("Sorry, but you can only send ");
			printlong (i);
			print (".\n");
			anykey ();
		}
		attack (0,target,i);
		print ("\fThese are the results of the attack: ");
		attackreport (i,target);
		print ("Total payment for damage done is ");
		printlong (payment);
		player[0].money+=payment;
		print (" credits.\nTotal cost of attack was ");
		printlong (shipl*(SHIP+ARM+PILOT)+bombu*BOMB);
		print (" credits.\n");
	} }
NOATTACK:
	anykey ();
	if (otherplayers ())	/* Do computer-controlled player moves */
		goto END;
	anykey ();
	if (events ())			/* Do events e.g. mining, pop. increase etc. */
		goto END;		/* if returns 1, game over */
	anykey ();
	goto LOOP;

END:
	print ("\n\nDo you want to play again? ");
	if (yesno ())
		goto NEWGAME;
			else
				finish ();
}

attack (attacker,defender,force)	/* Do attack */
int attacker,defender;
unsigned long force;	/* Number of ships involved */
{
	register unsigned long rembombs;
	bombu=force*2;
	player[attacker].fuel-=force*8;
	shipl=MIN (rnd (player[defender].lasers/8),force);
	force-=shipl;			/* some ships shot down on approach */
	rembombs=MINUS (force*2,rnd (player[defender].lasers));
							/* some bombs intercepted by lasers */
	rembombs=rembombs*4/5;	/* some bombs miss targets */
	domed=MIN (rnd (rembombs/2),player[defender].domes);
	rembombs-=domed;
	player[defender].domes-=domed;
	laserd=MIN (rnd (rembombs/3),player[defender].lasers);
	rembombs-=laserd;
	player[defender].lasers-=laserd;
	shipd=MIN (rnd (rembombs),player[defender].ships);
	rembombs-=shipd;
	player[defender].ships-=shipd;
	player[defender].armed=MINUS (player[defender].armed,rnd (shipd/2));
	recyclerd=MIN (rembombs,player[defender].recyclers);
	player[defender].recyclers-=recyclerd;
	shipl+=MIN (rnd (player[defender].lasers/8),force);
	player[attacker].ships-=shipl;
	player[attacker].pilots-=shipl;
	player[attacker].armed-=shipl;
	player[attacker].bombs-=bombu;
	popk=MIN (domed*(70+rnd(110))+rnd ((force+3)*3),player[defender].pop);
	player[defender].pop-=popk;
	payment=shipd*DSHIP+laserd*DLASER+recyclerd*DRECYCLER+domed*DDOME+popk/10;
	if (player[defender].domes&player[defender].recyclers==0)
		player[defender].pop=0;
}

attackreport (n,defender)	/* Report attack by n ships */
unsigned long n;
int defender;
{
	if (shipl)
	{
		printnoun (shipl,"attacking ship");
		print ("shot down from a total of ");
		printlong (n);
		print (". ");
	}
	if (shipd)
	{
		printnoun (shipd,"ship");
		print ("bombed on the ground. ");
	}
	if (domed)
	{
		printnoun (domed,"dome");
		print ("destroyed. ");
	}
	if (recyclerd)
	{
		printnoun (recyclerd,"recycler");
		print ("blown up. ");
	}
	if (laserd)
	{
		printnoun (laserd,"laser");
		print ("taken out. ");
	}
	if (popk)
	{
		print ("Number of people killed = ");
		printlong (popk);
		writechar ((long)'.');
	}
	writechar ((long)'\n');
	if (player[defender].pop==0)
		print ("All life on base extinguished.\n");
	if (!(shipd|domed|recyclerd|laserd|popk))
		print ("The attack did no damage.\n");
}

printnoun (number,string)	/* Print noun with plural or singular ending */
long number;
char *string;
{
	printlong (number);
	writechar ((long)' ');
	print (string);
	if (number!=1)
		print ("s were ");
			else
				print (" was ");
}

int yesno ()	/* Get yes or no answer from keyboard */
{
	register int i;
	resetscroll ();
REPEATYN:
	do
		i=checkinput ();
	while (i==0);
	if (i==1000)
	{
		finish ();
		exit (0);
	}
	if (i=='y' || i=='Y')
	{
		print ("YES\n");
		return (1);
	}
	if (i=='n' || i=='N')
	{
		print ("NO\n");
		return (0);
	}
		else
			goto REPEATYN;
}

getnum ()	/* Get typed-in number from user, put in innum global variable */
{
AGAIN:
	string[0]='\0';
	if (input (string,INLEN)==-1000)
	{
		finish ();	/* If close window, terminate program */
		exit (0);
	}
	if (string[0]=='\0')
	{
		innum=0;
		return ();
	}
	innum=scanlong (string);
	if (innum==32767 && (string[0]<'0' || string[0]>'9'))
	{
		print ("Invalid input-please try again: ");
		goto AGAIN;
	}
}

howmany (cost,lab,error)	/* Ask how many items at given cost */
int cost,lab;				/* and labour requirements */
char *error;	/* Error message for not enough of something */
{
	getnum ();
	if (innum*cost>player[0].money)
	{
		innum=player[0].money/cost;
		if (innum*lab>player[0].pop/2)
		{
			innum=player[0].pop/(lab+lab);
			print (error);
			printlong (innum);
			print (".\n");
		}
			else
			{
				print (NOTENOUGHCASH);
				printlong (innum);
				print (".\n");
			}
	}
		else
			if (innum*lab>player[0].pop/2)
			{
				innum=player[0].pop/(lab+lab);
				print (error);
				printlong (innum);
				print (".\n");
			}
	player[0].money-=innum*cost;
}

int otherplayers ()
{
	register short i;
	register int x,target;
	for (i=1;i<PLAYERS;i++)
	{
		if (player[i].fuel<=player[i].ships*24+player[i].domes+player[i].recyclers)
		{
			x=MIN (player[i].ships*24+player[i].domes+player[i].recyclers,player[i].money/FUEL);
			player[i].money-=(x*FUEL);
			player[i].fuel+=x;
		}
		if (player[i].mines<player[i].ships*2)
		{
			x=MIN (player[i].ships,player[i].pilots);
			x=MIN (x,player[i].money/MINE);
			x=MIN (x,player[i].pop/2/LMINE);
			x=MIN (x,player[i].fuel/8);
			player[i].mines+=x;
			player[i].fuel-=x*8;
			player[i].money-=x*MINE;
		}
		if (player[i].pilots<player[i].ships)
		{
			x=MIN (player[i].ships-player[i].pilots,player[i].pop/2-1-player[i].pilots);
			x=MIN (x,player[i].money/PILOT);
			player[i].pilots+=x;
			player[i].money-=x*PILOT;
		}
		switch (RangeRand (6))	/* choose randomly what to buy */
		{
			case (0):	/* spaceship */
				x=player[i].money/SHIP;
				player[i].ships+=x;
				player[i].money-=x*SHIP;
				break;
			case (1):	/* arm spaceship */
				x=MIN (player[i].money/ARM,player[i].pop/2/LARM);
				x=MIN (x,player[i].ships-player[i].armed);
				player[i].armed+=x;
				player[i].money-=x*ARM;
				break;
			case (2):	/* dome */
				x=MIN (player[i].money/DOME,player[i].pop/2/LDOME);
				player[i].domes+=x;
				player[i].money-=x*DOME;
				break;
			case (3):	/* recycler */
				x=MIN (player[i].money/RECYCLER,player[i].pop/2/LRECYCLER);
				player[i].recyclers+=x;
				player[i].money-=x*RECYCLER;
				break;
			case (4):	/* laser */
				x=MIN (player[i].money/LASER,player[i].pop/2/LLASER);
				player[i].lasers+=x;
				player[i].money-=x*LASER;
				break;
			case (5):	/* bomb */
				x=player[i].money/BOMB;
				player[i].bombs+=x;
				player[i].money-=x*BOMB;
				break;
		}
		x=MIN (player[i].ore/20,player[i].ships);	/* Collect ore */
		x=MIN (x,player[i].pilots);
		x=MIN (x,player[i].fuel/8);
		player[i].ore-=x*20;
		player[i].money+=x*20*ORE;
		player[i].fuel-=x*8;

		target=RangeRand (PLAYERS);
		if (player[target].pop && target!=i)
		{
			x=MIN (player[i].armed,player[i].pilots);
			x=MIN (x,player[i].bombs/2);
			x=MIN (x,player[i].fuel/8);
		if (x)
		{
			attack (i,target,x);
			print (enemyname[i-1]);
			print (" has launched an attack on ");
			if (target==0)	/* victim is YOU */
				print ("your base");
					else
						print (enemyname[target-1]);
			print (" with the following results: ");
			attackreport (x,target);
			if (player[0].pop==0 && target==0)
				return (1);		/* Attack has wiped you out */
		}
		}
	}
	return (0);
}

int events ()
{
	register short i;
	long popinc,oreinc,mineslost,starved,crowded;
	resetscroll ();
	print ("\fReport for the year:\n\n");
	if (player[0].pop>MAXLONG||player[0].money>MAXLONG)
	{
		print ("Congratulations! The base has done incredibly well under your management! You can now retire a millionaire. Well done.\n");
		return (1);
	}
	popinc=player[0].pop*(5+RangeRand (5))/100;
	player[0].pop+=popinc;
	oreinc=player[0].mines*(10+RangeRand (10));
	mineslost=0;
	mineslost=rnd (player[0].mines/47);
	starved=MINUS (player[0].pop,player[0].recyclers*80);
	crowded=0;
	if (popinc)
	{
		print ("Population increased by: ");
		printlong (popinc);
		writechar ((long)'\n');
	}
	if (oreinc)
	{
		print ("Quantity of ore accumulated: ");
		printlong (oreinc);
		writechar ((long)'\n');
		player[0].ore+=oreinc;
	}
	if (mineslost)
	{
		printnoun (mineslost,"mine");
		print ("destroyed by storms in Jovian atmosphere.\n");
		player[0].mines-=mineslost;
	}
	if (starved)
	{
		player[0].pop-=starved;
		crowded=MINUS (player[0].pop,player[0].domes*180);
		printnoun (starved,"death");
		print ("caused by lack of food and oxygen due to overload on the recycling systems.\n");
	}
	if (crowded)
	{
		player[0].pop-=crowded;
		printnoun (crowded,"death");
		print ("caused by stress and outbreaks of violence due to overcrowding.\n");
	}
	player[0].fuel=MINUS (player[0].fuel,player[0].domes+player[0].recyclers);
	if (player[0].fuel==0)
	{
		print ("\nFuel stocks exhausted. Life-support systems failure. Human life on base extinguished.\n");
		return (1);		/* Tell main program to terminate game */
	}
	if (player[0].pop==0)
	{
		print ("\nAll life on the base has come to an end.\n");
		return (1);
	}
	for (i=1;i<PLAYERS;i++)
	if (player[i].pop)
	{
		player[i].pop+=player[i].pop*(5+RangeRand (5))/100;
		player[i].ore+=player[i].mines*(10+RangeRand (10));	/* Ore mined */
		player[i].mines-=rnd (player[i].mines/67);
		if (player[i].pop>player[i].domes*180)
			player[i].pop=player[i].domes*180;	/* If too many people, */
		if (player[i].pop>player[i].recyclers*80) /* reduce population */
			player[i].pop=player[i].recyclers*80;
		player[i].fuel=MINUS (player[i].fuel,player[i].domes+player[i].recyclers);
		if (player[i].fuel==0)
			player[i].pop=0;	/* If fuel stocks exhausted, all die */
		if (player[i].pop==0)
		{
			print ("Radio and infra-red transmissions from the base of ");
			print (enemyname[i-1]);
			print (" have ceased.\n");
		}
	}
	return (0);
}

anykey ()
{
	register int i;
	print ("\nPress any key to continue.\n");
	do
		i=checkinput ();
	while (i==0);
	if (i==1000)
	{
		finish ();
		exit (0);
	}
}

long rnd (x)	/* Return random number from 0 to x-1 */
unsigned long x;	/* necessary because RangeRand doesn't work for 0 */
{
	if (x)
		return (RangeRand (x));
			else
				return (0);
}
