This is BattleView.m in view mode; [Download] [Up]
// BattleView V2.0 8/28/93 modified by cat >>>>>>>> tabsize 4 - windowwidth 95 char <<<<<<<<<<
#import "BattleView.h"
/* Resize so that the first line of this file does not break.
Copyright 1992, Stefanos Kiakas. All rights reserved.
You may not delete this notice.
*/
// 8/26/93 sounds added by cat
// 8/27/93 bonusPoints rewritten, performance improved with register, sizeTo:: added by cat
// 8/28/93 Scoring and BonusCity improved, 2 based play
// 8/28/93 PSWs rewritten, hundereds of PSflushgraphics,and focusLocks removed
#define CITYBONUS 100 /* city saved bonus */
#define MSLBONUS 10 /* missiles saved bonus */
#define MSLDSTRCT 100 /* attack missle intercepted ( destroyed ) bonus */
#define BOMBERSCORE 300 /* smartbomb */
#define SMARTSCORE 1000 /* smartbomb */
#define BONUSMARK 10000 /* give bonus city every so many points */
#define RANDTARGET ( random() % TARGETS) /* select a random target to attack */
#define RANDLAUNCH ( ( random() % 440 ) + 20 ) /* x pos. of attack missles */
#define PLACECITY(x) ( random() % x ) /* selct position to place city */
#define RAND(x) ( ( random() % (1+x) ) + x/2 ) /* random from x/2 to 3/2 x */
#define ZERO 0
#define MAXRADIUS 15.0 /* maximum radius of explosions */
#define TERRAINCOLOUR 0.20
#define DELTA_TIME 100
#define TIMEINTERVAL DELTA_TIME/1000.0
#define RADINC 1.0 /* radius increment of explosion circles */
#define MAXDEFLAUNCH 15 /* maximum number of defensive missiles */
#define MAXATCKMSSLS 15 /* maximum number of attack missiles */
#define BOMBERSTART 5 /* Level Bombers start on */
#define BOMBEREVERY 3 /* Add another bomb for bombers every */
#define LAUNCH_X 75.0 /* Coords for the defensive launch */
#define LAUNCH_Y 22.0
#define LAUNCH_X2 425.0 /* Coords for the defensive launch */
#define LAUNCH_Y2 22.0
#define DISP 5.0 /* Defensive missile speed */
#define DISPDELTA 0.2 /* Defensive missile speed increment per level */
#define CDX 18.0 /* center of target from display coordinates */
#define OLAUNCH_Y 390.0; /* Y launch position of attack missles */
#define H_CURSOR 330
#define W_CURSOR 460
#define Y_CURSOR 50
#define X_CURSOR 20
#define H_TRG Y_CURSOR + H_CURSOR
#define W_TRG X_CURSOR + W_CURSOR
#define Y_TRG Y_CURSOR
#define X_TRG X_CURSOR
#define MAX_dFactor 3.0 /* Max speed for incoming missiles */
#define STARTDIFF 0.5 /* start difficulty level */
#define FACTORINC 0.12 /* increment in difficulty level */
#define SCOREXPOS 150
#define SCOREYPOS 250
#import "MissileCommand.h" // The new PSWraps ! by cat
#import <mach/mach.h>
#include <sys/param.h>
#import <libc.h>
#import <math.h>
#import <appkit/appkit.h>
#import <soundkit/Sound.h>
#import <soundkit/soundkit.h>
#import "SoundEffect.h"
unsigned
currentTimeInMs() /* return time in 1/1000th of a second */
{
struct timeval curTime;
gettimeofday(&curTime, NULL);
return ((unsigned)curTime.tv_sec) * 1000 + curTime.tv_usec / 1000;
}
BOOL
testDefensiveBombs(EXP_PNTR DefExplosions, NXPoint testpoint)
{ /* Test to see if incoming missiles are hit */
while (DefExplosions != NULL)
{ /* If the distance to the explosion <=
* Radius... */
/* Do 2 quick tests before we start doing sqrt and ^2 */
if ((fabs(testpoint.x - DefExplosions->currentPoint.x) <= DefExplosions->rad) &&
(fabs(testpoint.y - DefExplosions->currentPoint.y) <= DefExplosions->rad) &&
(sqrt(pow(testpoint.x - DefExplosions->currentPoint.x, 2) + pow(testpoint.y - DefExplosions->currentPoint.y, 2)) <= DefExplosions->rad))
{
return TRUE;
}
DefExplosions = DefExplosions->next;
}
return FALSE;
}
BOOL
testDefensiveBombsvSmart(EXP_PNTR DefExplosions, NXPoint testpoint, NXPoint *heatpoint, float *heatdist)
{ /* Is it dead, or near an explosion */
float thisdist;
*heatdist = 0;
while (DefExplosions != NULL)
{
if ((thisdist = sqrt(pow(testpoint.x - DefExplosions->currentPoint.x, 2) + pow(testpoint.y - DefExplosions->currentPoint.y, 2))) <= DefExplosions->rad)
{
return TRUE;
}
if ((thisdist <= (MAXRADIUS + 2)) && ((*heatdist == 0.0) || (thisdist < *heatdist)))
{ /* If it's close, and there are no previos or
* closer */
*heatdist = thisdist;
*heatpoint = DefExplosions->currentPoint;
}
DefExplosions = DefExplosions->next;
}
return FALSE;
}
@implementation BattleView
- initFrame:(const NXRect *)frm
{
NXPoint mySpot;
[super initFrame:frm];
[self allocateGState];
gameRunning = FALSE;
gamePaused = FALSE;
PSWinit();
redAlertSound = [[SoundEffect alloc] initFromSection:"RedAlert.snd"];
emptyBaseSound = [[SoundEffect alloc] initFromSection:"EmptyBase.snd"];
missileAlertSound = [[SoundEffect alloc] initFromSection:"MissileAlert.snd"];
countCitySound = [[SoundEffect alloc] initFromSection:"City.snd"];
countRocketSound = [[SoundEffect alloc] initFromSection:"Rocket.snd"];
bonusCitySound = [[SoundEffect alloc] initFromSection:"BonusCity.snd"];
exploSound = [[SoundEffect alloc] initFromSection:"Explo.snd" withLimit:8];
surfExploSound = [[SoundEffect alloc] initFromSection:"SurfExplo.snd" withLimit:4];
launchSound = [[SoundEffect alloc] initFromSection:"Launch.snd" withLimit:4];
smartBombSound = [[SoundEffect alloc] initFromSection:"SmartBomb.snd" withLimit:2];
cityBitmap = [NXImage findImageNamed:"city3.tiff"];
launcherBitmap = [NXImage findImageNamed:"mBase.tiff"];
bomberBitmap = [NXImage findImageNamed:"Bomber.tiff"];
smartbombBitmap = [NXImage findImageNamed:"SmartBomb.tiff"];
playSound = TRUE; /* Listen to the neato sound */
gameLevel = 1;
bomber.hotspots[0].x = 44.0; /* Set the Bomber HotSpots (are tested for hits) */
bomber.hotspots[0].y = 2.0;
bomber.hotspots[1].x = 26.0;
bomber.hotspots[1].y = 0.0;
bomber.hotspots[2].x = 12.0;
bomber.hotspots[2].y = 0.0;
bomber.hotspots[3].x = 12.0;
bomber.hotspots[3].y = 10.0;
cursorImage = [NXCursor newFromImage:[NXImage newFromSection:"targetD3.tiff"]];
cBounds.origin.x = X_CURSOR;
cBounds.origin.y = Y_CURSOR;
cBounds.size.width = W_CURSOR;
cBounds.size.height = H_CURSOR;
mySpot.x = 8; /* Set the cursor hot spot */
mySpot.y = 7;
srandom(currentTimeInMs());
[cursorImage setHotSpot:&mySpot];
return self;
}
- sizeTo:(NXCoord)width :(NXCoord)height // this is too easy not to implement (cat)
{
static NXCoord sx=1,sy=1;
[super sizeTo:width :height];
[self scale:sx :sy]; // scale back to 1:1 (scale works relatively)
[self scale:width/500 :height/400];
sx=500/width; // this is for next time to scale back to the original value
sy=400/height;
return self;
}
- switchSound:sender
{
playSound = !playSound;
[SoundEffect setSoundEnabled:playSound];
return self;
}
- pauseGame:sender
{ /* Pause or start the game */
void stepFun();
if(gameRunning && !gamePaused)
{ gameRunning = FALSE;
gamePaused = TRUE;
DPSRemoveTimedEntry(gameTimer);
[[toButtonMatrix cellAt:1 :0] setEnabled:NO]; // start button
[toButtonMatrix display];
return self;
}
if(!gameRunning && gamePaused)
{ gameRunning = TRUE;
gamePaused = FALSE;
gameTimer = DPSAddTimedEntry(TIMEINTERVAL, &stepFun, self, NX_BASETHRESHOLD);
[[toButtonMatrix cellAt:1 :0] setEnabled:YES]; // start button
[toButtonMatrix display];
return self;
}
return self;
}
- resetCursorRects
{
[self addCursorRect:&cBounds cursor:cursorImage];
return self;
}
- newGame:sender
{ /* Initialize a new game, or end existing one */
char levelString[15];
int i; /* Counter */
void stepFun(); /* for the DPStimedentry */
MSLE_NODE_PNTR currentPointdm, fpdm; /* *KJW* didn't name these
* variables */
MA_NODE_PNTR currentPointam, fpam;
EXP_PNTR currentPointe, fpe;
SMART_NODE_PNTR smarthead, smart2free;
if(gamePaused) return self;
if(!gameRunning)
{ gameLevel = 1;
[[toButtonMatrix cellAt:0 :0] setEnabled:YES]; // pause button
[toButtonMatrix display];
sprintf(levelString, "Level %02d", gameLevel);
[levelText setStringValue:levelString];
dFactor = STARTDIFF; /* Set the difficulty factor */
phaseOne = TRUE;
defensiveMissiles.launched = 0; /* Initialize the various lists */
defensiveMissiles.next = NULL;
offensiveMissiles.launched = 0;
offensiveMissiles.next = NULL;
defensiveExplosions.exploding = 0;
defensiveExplosions.next = NULL;
smartBombs.launched = 0;
smartBombs.next = NULL;
bomber.launched = 0;
for (i = 0; i < CITIES; i++) /* Initialize the cities */
cityStatus[i] = TRUE;
/* reset number of missiles */
aTimeBefore = currentTimeInMs();
/* select attack positions */
[self selectTargets];
missiles1 = 20;
missiles2 = 20;
score = 0;
lastBonusAt = 0;
displayBase1 = TRUE;
displayBase2 = TRUE;
[outMissiles setIntValue:missiles1];
[toRightMissiles setIntValue:missiles2];
[window makeKeyAndOrderFront:self];
gameRunning = TRUE;
gameTimer = DPSAddTimedEntry(TIMEINTERVAL, &stepFun, self, NX_BASETHRESHOLD);
[self display];
return self;
}
if (gameRunning)
{ [[toButtonMatrix cellAt:0 :0] setEnabled:NO]; // pause button
[toButtonMatrix setState:NO at:1 :0]; // start button
[toButtonMatrix display];
[highScoreTable putInHighScores: score];
gameRunning = FALSE;
DPSRemoveTimedEntry(gameTimer);
/* remove entries in linked lists */
currentPointdm = defensiveMissiles.next;
while (currentPointdm != NULL)
{ fpdm = currentPointdm;
currentPointdm = currentPointdm->next;
free(fpdm);
}
/* remove attack missiles from list, and free memory */
currentPointam = offensiveMissiles.next;
while (currentPointdm != NULL)
{ fpam = currentPointam;
currentPointam = currentPointam->next;
free(fpam);
}
/* remove air explosions from list and free memory */
currentPointe = defensiveExplosions.next;
while (currentPointe != NULL)
{ fpe = currentPointe;
currentPointe = currentPointe->next;
free(fpe);
}
/* remove surface explosions from list and free memory */
currentPointe = surfaceExplosions.next;
while (currentPointe != NULL)
{ fpe = currentPointe;
currentPointe = currentPointe->next;
free(fpe);
}
/* remove smartbombs and free memory -KJW */
smarthead = smartBombs.next;
smartBombs.launched = 0;
while (smarthead != NULL)
{ smart2free = smarthead;
smarthead = smarthead->next;
free(smart2free);
}
}
return self;
}
- (void)step
{ /* Each frame of movement is done here... */
NXEvent dummyEvent;
unsigned int timeNow, dTime;/* Make sure we've waited the right time */
if(!gameRunning) return;
do
{
[self lockFocus];
timeNow = currentTimeInMs(); /* What time is it now? */
dTime = timeNow - aTimeBefore; /* How long's it been? */
if (dTime < (unsigned)DELTA_TIME) /* If it's too soon, wait */
{ /* This is pathetic. All I want to do is a damn usleep -KJW */
thread_switch(THREAD_NULL, SWITCH_OPTION_WAIT, (int)(DELTA_TIME - dTime));
}
aTimeBefore = timeNow;
/* Do what needs doing */
if (defensiveMissiles.launched != 0) [self missilePath];
if (defensiveExplosions.exploding > 0) [self expDraw];
if (offensiveMissiles.launched > 0) [self attackMissilesPath];
if (surfaceExplosions.exploding > 0) [self surfaceExplosionsDraw];
if (bomber.launched > 0) [self doBomber];
if (smartBombs.launched > 0) [self doSmartBomb];
if(defensiveMissiles.launched == 0 && defensiveExplosions.exploding == 0)
if(offensiveMissiles.launched == 0 && surfaceExplosions.exploding == 0)
if(smartBombs.launched == 0 && bomber.launched == 0)
{ if(!phaseOne)
[self bonusPoints];
[self nextLevel];
}
PSflushgraphics();
NXPing();
[self unlockFocus];
} while([NXApp peekNextEvent:NX_ALLEVENTS into:&dummyEvent] == NULL);
}
- drawSelf:(const NXRect *)r :(int)c
{
NXPoint Pt;
register int i;
PSsetgray(1.0);
NXRectFill(r);
PSsetgray(NX_BLACK);
NXFrameRect(r);
PSsetgray(TERRAINCOLOUR);
PSmoveto(0.0, 0.0);
PSrlineto(0.0, 5.0);
PSrlineto(50.0, 0.0);
PSrlineto(5.0,10.0); // B1
PSrlineto(40.0, 0.0);
PSrlineto(5.0,-10.0);
PSrlineto(80.0, 0.0);
PSrlineto(5.0, 5.0);
PSrlineto(40.0, 0.0);
PSrlineto(5.0, 3.0); // centerhill
PSrlineto(40.0, 0.0);
PSrlineto(5.0, -3.0);
PSrlineto(40.0, 0.0);
PSrlineto(5.0, -5.0);
PSrlineto(80.0, 0.0);
PSrlineto(5.0,10.0); // B2
PSrlineto(40.0, 0.0);
PSrlineto(5.0,-10.0);
PSrlineto(50.0, 0.0);
PSrlineto(0.0, -5.0);
PSclosepath();
PSfill();
[self drawLaunchPad];
for (i = 0; i < CITIES; i++)
if(cityStatus[i])
[self drawCityBM:cityBasearray[i].coord];
Pt.x = 200.0;
Pt.y = 200.0;
return self;
}
- (void)drawCityBM:(NXPoint)dcurrentPoint
{ /* Draw a city at dcurrentPoint */
[[cityBitmap lastRepresentation] drawAt:&dcurrentPoint];
}
- (void)drawLaunchPad
{ /* Draw the Launch pad */
if(displayBase1) [[launcherBitmap lastRepresentation] drawAt:&cityBasearray[10].coord];
if(displayBase2) [[launcherBitmap lastRepresentation] drawAt:&cityBasearray[9].coord];
}
- (void)drawMissile
{ /* Draw the missle figure for the score part */
id missileBitmap = [NXImage findImageNamed:"missileIcon.tiff"];
NXPoint Pt;
Pt.x = 160.0;
Pt.y = 190.0;
[[missileBitmap lastRepresentation] drawAt:&Pt];
}
- rightMouseDown:(NXEvent *)theEvent
{
rflag = YES;
[self mouseDown:theEvent];
rflag = NO;
if(missiles2)
[launchSound play];
else
[emptyBaseSound play];
if(missiles2 < 3 && missiles2 != 0)
[missileAlertSound play];
return self;
}
- mouseDown:(NXEvent *)theEvent
{ /* Deal with the IO... */
NXPoint sPt;
int pntInRect();
register MSLE_NODE_PNTR mLaunch, p;
float h1,h2;
if(!gameRunning) return self;
sPt = theEvent->location;
[self convertPoint:&sPt fromView:nil];
if(defensiveMissiles.launched < MAXDEFLAUNCH && pntInRect(&sPt)) // like &&
if((rflag && missiles2 > 0) || (!rflag && missiles1 > 0))
{
if(rflag)
[toRightMissiles setIntValue:--missiles2];
else
[outMissiles setIntValue:--missiles1];
defensiveMissiles.launched++;
[self lockFocus]; // we really need this one grrrr!
PSWmarkloc(sPt.x,sPt.y);
[self unlockFocus];
// cat was here ...
mLaunch = malloc(sizeof(MSLE_NODE));
mLaunch->destPos = sPt;
if(rflag)
{ mLaunch->currentPos.x = LAUNCH_X2;
mLaunch->currentPos.y = LAUNCH_Y2;
}
else
{ mLaunch->currentPos.x = LAUNCH_X;
mLaunch->currentPos.y = LAUNCH_Y;
}
mLaunch->startPos = mLaunch->currentPos;
mLaunch->next = NULL;
h1=sqrt((sPt.y-LAUNCH_Y)*(sPt.y-mLaunch->currentPos.y) +
(sPt.x-mLaunch->currentPos.x)*(sPt.x-mLaunch->currentPos.x));
h2=DISP+DISPDELTA*gameLevel;
mLaunch->delta_x = h2 * (sPt.x - mLaunch->currentPos.x) / h1;
mLaunch->delta_y = h2 * (sPt.y - mLaunch->currentPos.y) / h1;
if (defensiveMissiles.next == NULL)
defensiveMissiles.next = mLaunch;
else
{
p = defensiveMissiles.next;
while (p->next != NULL)
p = p->next;
p->next = mLaunch;
}
}
if(!rflag)
{ if(missiles1)
[launchSound play];
else
[emptyBaseSound play];
if(missiles1 < 3 && missiles1 != 0)
[missileAlertSound play];
}
return self;
}
- (void)expDraw
{ /* Draw the explosions (and handle the logic) */
register EXP_PNTR currentPoint, pp;
pp = currentPoint = defensiveExplosions.next;
while (currentPoint != NULL)
{
if (currentPoint->rad > currentPoint->maxrad) /* If we've hit radius start to erase */
{ currentPoint->maxReached = YES;
currentPoint->rad += (currentPoint->maxReached) ? -RADINC : RADINC;
pp = currentPoint;
currentPoint = currentPoint->next;
}
else
if ((currentPoint->rad <= 0.0) && currentPoint->maxReached)
{ /* If we've erased the whole thing */
if (defensiveExplosions.next == currentPoint)
{ pp = defensiveExplosions.next = currentPoint->next;
free(currentPoint);
currentPoint = pp;
}
else
{ pp->next = currentPoint->next;
free(currentPoint);
currentPoint = pp->next;
}
defensiveExplosions.exploding--;
}
else
{ /* Just draw the damn thing and increase the radius */
PSWexplode((currentPoint->maxReached) ? NX_WHITE : NX_BLACK,
currentPoint->currentPoint.x,currentPoint->currentPoint.y,
currentPoint->rad);
currentPoint->rad += ((currentPoint->maxReached) ? -RADINC : RADINC);
pp = currentPoint;
currentPoint = currentPoint->next;
}
}
}
- (void)doBomber
{ /* Handle the bomber */
/* I wrote this (so proud ;-) -KJW */
NXPoint Pt; /* Coundn't tell by the var names, eh? */
register int i;
if(bomber.currentPos.x > 0) /* If we've already plotted, erase */
{ PSsetgray(NX_WHITE);
PSrectfill(bomber.currentPos.x,bomber.currentPos.y,44,9);
// cat was here tooo ... but this is nothing to be proud of :=)
}
for (i = 0; i < 4; i++)
{ /* Test all the hotspots */
Pt = bomber.hotspots[i];
Pt.x += bomber.currentPos.x;
Pt.y += bomber.currentPos.y;
if (testDefensiveBombs(defensiveExplosions.next, Pt))
{ /* If hit... */
score += BOMBERSCORE;
[outScore setIntValue:score];
[self airExplosionAt:&Pt withRadius:35.0];
bomber.launched--;
bomber.currentPos.x = 0.0;
return;
}
}
bomber.currentPos.x += bomber.delta_x; /* Move the bomber */
[[bomberBitmap lastRepresentation] drawAt:&bomber.currentPos];
if (bomber.currentPos.x > 480)
{ /* Did we make it to the right side? */
[[bomberBitmap lastRepresentation] drawAt:&bomber.currentPos];
bomber.launched--;
bomber.currentPos.x = 0;
bomber.bombPos.y = 100.0 + RAND(100);
bomber.currentPos.y = bomber.bombPos.y;
if (bomber.launched)
{ /* Want to launch another? */
bomber.bombs = (int)((gameLevel - BOMBERSTART) / BOMBEREVERY + 1) /
(int)((gameLevel + BOMBERSTART) / 10) + 1;
bomber.bombPos.x = 480 / (bomber.bombs + 1);
}
return;
}
if (bomber.currentPos.x > bomber.bombPos.x && (bomber.bombs > 0))
{ /* Ready to toss another bomb? */
NXPoint bombPoint = bomber.currentPos;
bombPoint.x += 5; /* Put the bomb under the bomber (not at the
* back) */
bomber.bombs--;
bomber.bombPos.x += (480 - bomber.currentPos.x) / (bomber.bombs + 1);
offensiveMissiles.next=[self addToIncoming:bomber.currentPos:1 :offensiveMissiles.next];
}
}
- (void)doSmartBomb
{
register SMART_NODE_PNTR CurrentSmartBomb = smartBombs.next;
register SMART_NODE_PNTR PreviousSmartBomb = CurrentSmartBomb;
NXPoint hotPoint, plotPoint; /* hotPoint will be a close defensive
* bomb, plotPoint is where the
* smartbomb appears */
float hotDist, /* distance to a close defensive bomb */
delta_x, delta_y, dx, dy; /* for calcing distance and new
* movement (smarts) */
/*
* set up pointers to point at head of attack missile list
*/
while (CurrentSmartBomb != NULL)
{ if(testDefensiveBombsvSmart(defensiveExplosions.next, CurrentSmartBomb->currentPos, &hotPoint, &hotDist))
{ /* Did we get hit? (and what was close if not (hot)) */
smartBombs.launched--; /* missile has been destroyed */
score += SMARTSCORE;
[outScore setIntValue:score];
[self airExplosionAt:&(CurrentSmartBomb->currentPos)];
/* remove missile from list */
if (CurrentSmartBomb == smartBombs.next)
{ /* Is this the head of the list */
smartBombs.next = CurrentSmartBomb->next;
free(CurrentSmartBomb);
CurrentSmartBomb = PreviousSmartBomb = smartBombs.next;
}
else
{ PreviousSmartBomb->next = CurrentSmartBomb->next;
free(CurrentSmartBomb);
CurrentSmartBomb = PreviousSmartBomb;
}
}
else
{ /* Plot (erase) the bomb where the numbers say it is (not lower left of tiff */
plotPoint = CurrentSmartBomb->currentPos;
plotPoint.x -= 3;
plotPoint.y -= 2;
//[smartbombBitmap composite:NX_XOR toPoint:&plotPoint];
PSsetgray(NX_WHITE);
PSrectfill(plotPoint.x,plotPoint.y,5,5);
/* calculate next point in trajectory */
/* Find out how far it is to the target */
dx = CurrentSmartBomb->destPos.x - CurrentSmartBomb->currentPos.x;
dy = CurrentSmartBomb->destPos.y - CurrentSmartBomb->currentPos.y;
/* And move toward it */
delta_y =.9 * dy * INCOMINGBASEV / sqrt(dy * dy + dx * dx);
delta_x =.9 * dx * INCOMINGBASEV / sqrt(dy * dy + dx * dx);
if (hotDist > 0)
{ /* Any Close calls */
/* Find out how far it is to the target (defensive bomb) */
dx = hotPoint.x - CurrentSmartBomb->currentPos.x;
dy = hotPoint.y - CurrentSmartBomb->currentPos.y;
/* And move *AWAY FROM* it */
delta_y -=.9 *.9 * dy * INCOMINGBASEV / sqrt(dy * dy + dx * dx);
delta_x -=.9 *.9 * dx * INCOMINGBASEV / sqrt(dy * dy + dx * dx);
}
/* OK, move the bomb */
CurrentSmartBomb->currentPos.x += dFactor * delta_x;
CurrentSmartBomb->currentPos.y += dFactor * delta_y;
/* Plot the bomb where the numbers say it is (not lower left of tiff */
plotPoint = CurrentSmartBomb->currentPos;
plotPoint.x -= 3;
plotPoint.y -= 2;
//[smartbombBitmap composite:NX_SOVER toPoint:&plotPoint];
[[smartbombBitmap lastRepresentation] drawAt:&plotPoint];
if (CurrentSmartBomb->currentPos.y < CurrentSmartBomb->destPos.y)
{ /* Did we reach target */
smartBombs.launched--;
if (CurrentSmartBomb->targetLoc == (TARGETS - 1))
{ missiles1 = 0;
[outMissiles setIntValue:missiles1];
displayBase1 = FALSE;
}
else
if (CurrentSmartBomb->targetLoc == (TARGETS - 2))
{ missiles2 = 0;
[toRightMissiles setIntValue:missiles2];
displayBase2 = FALSE;
}
else
cityStatus[CurrentSmartBomb->targetLoc] = FALSE;
[self surfaceExplosionAt:&(CurrentSmartBomb->currentPos)];
if (CurrentSmartBomb == smartBombs.next)
{ /* are we the head bomb */
smartBombs.next = CurrentSmartBomb->next;
free(CurrentSmartBomb);
CurrentSmartBomb = smartBombs.next;
}
else
{
PreviousSmartBomb->next = CurrentSmartBomb->next;
free(CurrentSmartBomb);
CurrentSmartBomb = PreviousSmartBomb;
}
}
}
if (CurrentSmartBomb != NULL)
{ PreviousSmartBomb = CurrentSmartBomb;
CurrentSmartBomb = CurrentSmartBomb->next;
}
}
if(smartBombs.launched > [smartBombSound soundsPlaying])
[smartBombSound play];
}
- (void)surfaceExplosionsDraw
{ /* Not my work -KJW -- Plot and deal with surface explosions */
register EXP_PNTR currentPoint, pp;
pp = currentPoint = surfaceExplosions.next;
while (currentPoint != NULL)
{
if (currentPoint->rad > MAXRADIUS)
{
currentPoint->maxReached = YES;
currentPoint->rad += (currentPoint->maxReached) ? -RADINC : RADINC;
pp = currentPoint;
currentPoint = currentPoint->next;
}
else
if ((currentPoint->rad <= 0.0) && currentPoint->maxReached)
{
if (surfaceExplosions.next == currentPoint)
{
pp = surfaceExplosions.next = currentPoint->next;
PSnewpath();
PSsetgray(NX_WHITE);
PSmoveto(currentPoint->currentPoint.x, currentPoint->currentPoint.y);
PSrlineto(-10, 0);
PSrlineto(0, 20);
PSrlineto(30, 0);
PSrlineto(0, -20);
PSrlineto(-10, 0);
PSfill();
free(currentPoint);
currentPoint = pp;
}
else
{
pp->next = currentPoint->next;
PSnewpath();
PSsetgray(NX_WHITE);
PSmoveto(currentPoint->currentPoint.x, currentPoint->currentPoint.y);
PSrlineto(-10, 0);
PSrlineto(0, 20);
PSrlineto(30, 0);
PSrlineto(0, -20);
PSrlineto(-10, 0);
PSfill();
free(currentPoint);
currentPoint = pp->next;
}
surfaceExplosions.exploding--;
}
else
{ PSWsurfexpl((currentPoint->maxReached) ? NX_WHITE : NX_BLACK,
currentPoint->currentPoint.x, currentPoint->currentPoint.y,
currentPoint->rad);
currentPoint->rad += ((currentPoint->maxReached) ? -RADINC : RADINC);
pp = currentPoint;
currentPoint = currentPoint->next;
}
}
}
- (void)missilePath
{ /* Not my work -KJW -- Plot and deal with defensive missiles */
NXPoint nPos; /* new missile position */
register MSLE_NODE_PNTR currentPoint,dp;
MSLE_NODE_PNTR pp;
pp = currentPoint = defensiveMissiles.next;
while (currentPoint != NULL)
{
if (currentPoint->destPos.y <= currentPoint->currentPos.y)
{ /* missile has reached it destination */
PSWerasetrajectory(currentPoint->startPos.x,currentPoint->startPos.y,
currentPoint->destPos.x ,currentPoint->destPos.y);
if (currentPoint == defensiveMissiles.next)
{ defensiveMissiles.next = currentPoint->next;
dp = currentPoint;
currentPoint = defensiveMissiles.next;
}
else
{ pp->next = currentPoint->next;
dp = currentPoint;
currentPoint = pp->next;
}
[self airExplosionAt:&(dp->destPos)];
free(dp);
defensiveMissiles.launched--;
}
else
{ /* calculate and plot next position */
nPos.x = currentPoint->currentPos.x + currentPoint->delta_x;
if (currentPoint->delta_x < 0)
{ /* make sure that point is not past detonation point */
if (nPos.x < currentPoint->destPos.x)
nPos.x = currentPoint->destPos.x;
}
else
{ if (nPos.x > currentPoint->destPos.x)
nPos.x = currentPoint->destPos.x;
}
nPos.y = currentPoint->currentPos.y + currentPoint->delta_y;
if (currentPoint->destPos.y > currentPoint->currentPos.y)
{ PSWplottrajectory(currentPoint->currentPos.x,
currentPoint->currentPos.y,nPos.x,nPos.y);
currentPoint->currentPos = nPos;
}
pp = currentPoint;
currentPoint = currentPoint->next;
}
}
}
- nextLevel
{ /* initialize the next level */
int cities, i;
cities=0;
for(i=0;i<CITIES;i++)
if(cityStatus[i])
cities++;
if(cities == 0)
{ missiles1 = 0;
missiles2 = 0;
[self newGame:self]; // Abort
return self;
}
else
{ if (phaseOne)
phaseOne = FALSE;
else
{ phaseOne = TRUE;
if (dFactor < MAX_dFactor)
dFactor += FACTORINC;
if(cities)
{ if(gameLevel % 10 == 0)
{ missiles1 = 99;
missiles2 = 99;
}
else
{ missiles1 = 20 + gameLevel / 5;
missiles2 = 20 + gameLevel / 5;
}
}
displayBase1 = TRUE;
displayBase2 = TRUE;
}
/* reset time */
aTimeBefore = currentTimeInMs();
[outMissiles setIntValue:missiles1];
[toRightMissiles setIntValue:missiles2];
[outScore setIntValue:score];
[self display];
/* calculate attack positions */
[self selectTargets];
{ /* Print level number */
char levelString[15];
sprintf(levelString, "Level %02d", gameLevel);
[levelText setStringValue:levelString];
}
/* initialize bomber values for level */
bomber.currentPos.x = 0.0;
bomber.bombPos.y = 100.0 + RAND(100);
bomber.currentPos.y = bomber.bombPos.y;
bomber.delta_x = 3.0;
bomber.launched = (int)((gameLevel + BOMBERSTART) / 10);
if (bomber.launched)
{ bomber.bombs = (int)((gameLevel - BOMBERSTART) / BOMBEREVERY + 1) /
(int)((gameLevel + BOMBERSTART) / 10) + 1;
bomber.bombPos.x = 480 / (bomber.bombs + 1);
}
}
return self;
} /* End 'nextLevel' */
- bonusPoints // cat was here ... and anywhere else
{
NXPoint cityPt;
int cities,i,bmulti = gameLevel/2+1;
int vacantPos[CITIES], j;
char str[100];
static NXRect fr={200,240,200,30};
static id cbon=0,rbon,bonc,font;
if(cbon==0)
{ cbon = [[TextField alloc] initFrame:&fr];
fr.origin.y = 190;
rbon = [[TextField alloc] initFrame:&fr];
fr.origin.x = 190;
fr.origin.y = 140;
bonc = [[TextField alloc] initFrame:&fr];
[cbon setEditable:NO];
[rbon setEditable:NO];
[bonc setEditable:NO];
font = [Font newFont:"Ohlfs" size:16.0];
[cbon setFont:font];
[rbon setFont:font];
[bonc setFont:font];
}
[self addSubview:cbon];
[self addSubview:rbon];
cityPt.x = SCOREXPOS;
cityPt.y = SCOREYPOS;
cities = 0;
for (i = 0; i < CITIES; i++)
if (cityStatus[i])
cities++;
[self drawCityBM:cityPt];
for (i = 0; i <= cities; i++)
{ sprintf(str,"%i x %i = %i",i,CITYBONUS*bmulti,i*CITYBONUS*bmulti);
[outScore setIntValue:score+i*CITYBONUS*bmulti];
[cbon setStringValue:str];
[countCitySound playEvent];
usleep(300000);
}
score += cities * CITYBONUS * bmulti;
[self drawMissile];
for (i = 0; i <= missiles1; i++)
{ sprintf(str,"%i x %i = %i",i,MSLBONUS*bmulti,i*MSLBONUS*bmulti);
[outScore setIntValue:score+i*MSLBONUS*bmulti];
[outMissiles setIntValue:missiles1-i];
[rbon setStringValue:str];
[countRocketSound playEvent];
usleep(170000);
}
for (i = 0; i <= missiles2; i++)
{ sprintf(str,"%i x %i = %i",i+missiles1,MSLBONUS*bmulti,(i+missiles1)*MSLBONUS*bmulti);
[outScore setIntValue:score+missiles1*MSLBONUS*bmulti+i*MSLBONUS*bmulti];
[toRightMissiles setIntValue:missiles2-i];
[rbon setStringValue:str];
[countRocketSound playEvent];
usleep(170000);
}
score += (missiles1 + missiles2) * MSLBONUS * bmulti;
while(score-lastBonusAt > BONUSMARK)
{ j=0;
for(i=0;i<CITIES;i++)
{ if(cityStatus[i]==FALSE)
vacantPos[j++] = i;
}
if(j)
{ cityStatus[vacantPos[PLACECITY(j)]]=TRUE;
lastBonusAt += BONUSMARK;
[self addSubview:bonc];
[bonc setStringValue:"BONUS CITY !"];
PSflushgraphics();
NXPing();
[bonusCitySound play];
}
else
break; // no empty spot !
}
gameLevel++;
sleep(4);
[cbon removeFromSuperview];
[rbon removeFromSuperview];
[bonc removeFromSuperview];
return self;
}
- selectTargets
{ /* Get targets for various things */
float dx, dy;
int i;
MA_NODE_PNTR head, currentPoint, pp;
SMART_NODE_PNTR thisSmart, lastSmart;
pp = head = currentPoint = malloc(sizeof(MA_NODE));
offensiveMissiles.splitters = MIN(MAXATCKMSSLS, (int)gameLevel / 2);
while (++offensiveMissiles.launched <= MAXATCKMSSLS + random()%7-6)
{ /* attack missles init */
currentPoint->startPos.x = RANDLAUNCH;
currentPoint->startPos.y = OLAUNCH_Y;
currentPoint->targetLoc = RANDTARGET;
currentPoint->destPos.x = cityBasearray[currentPoint->targetLoc].coord.x + CDX;
currentPoint->destPos.y = cityBasearray[currentPoint->targetLoc].coord.y;
currentPoint->currentPos = currentPoint->startPos;
dx = currentPoint->destPos.x - currentPoint->startPos.x;
dy = currentPoint->destPos.y - OLAUNCH_Y;
currentPoint->delta_y = 1.0 * dy * INCOMINGBASEV / sqrt(dy * dy + dx * dx);
currentPoint->delta_x = dx * INCOMINGBASEV / sqrt(dy * dy + dx * dx);
if (offensiveMissiles.splitters > 0)
{
currentPoint->destPos.y = OLAUNCH_Y;
currentPoint->destPos.y -= 1.0 / 4.0 * RAND((int)currentPoint->destPos.y);
currentPoint->destPos.y -= gameLevel;
currentPoint->missileType = SPLITTER;
offensiveMissiles.splitters--;
}
else
{
currentPoint->missileType = BOMB;
}
currentPoint->next = malloc(sizeof(MA_NODE));
pp = currentPoint;
currentPoint = currentPoint->next;
}
offensiveMissiles.launched--;
offensiveMissiles.next = head;
pp->next = NULL;
free(currentPoint);
smartBombs.next = thisSmart = lastSmart = malloc(sizeof(SMART_NODE));
while (++smartBombs.launched <= (int)(gameLevel - 4) / 3)
{ /* attack missles init */
thisSmart->currentPos.x = RANDLAUNCH;
thisSmart->currentPos.y = OLAUNCH_Y;
for (i = 0; i < 10; i++) /* Try to get a good city (bwa-ha-ha) */
{ if (cityStatus[thisSmart->targetLoc = RANDTARGET] == TRUE)
break;
}
thisSmart->targetLoc = RANDTARGET;
thisSmart->destPos.x = cityBasearray[thisSmart->targetLoc].coord.x + CDX;
thisSmart->destPos.y = cityBasearray[thisSmart->targetLoc].coord.y;
thisSmart->next = malloc(sizeof(MA_NODE));
lastSmart = thisSmart;
thisSmart = thisSmart->next;
}
smartBombs.launched--;
if (smartBombs.launched == 0)
{
smartBombs.next = NULL;
}
lastSmart->next = NULL;
free(thisSmart);
[redAlertSound play:0.1 pan: 0.0];
return self;
}
- (MA_NODE_PNTR) addToIncoming:(NXPoint)startPoint :(int)number :(MA_NODE_PNTR) next
{ /* attack missles init */
MA_NODE_PNTR newbomb = malloc(sizeof(MA_NODE));
float dx, dy;
offensiveMissiles.launched++; /* missile has been added */
newbomb->startPos = startPoint;
newbomb->targetLoc = RANDTARGET;
newbomb->destPos.x = cityBasearray[newbomb->targetLoc].coord.x + CDX;
newbomb->destPos.y = cityBasearray[newbomb->targetLoc].coord.y;
newbomb->currentPos = newbomb->startPos;
dx = newbomb->destPos.x - newbomb->startPos.x;
dy = newbomb->destPos.y - newbomb->startPos.y;
newbomb->delta_y = dy * INCOMINGBASEV / sqrt(dy * dy + dx * dx);
newbomb->delta_x = dx * INCOMINGBASEV / sqrt(dy * dy + dx * dx);
newbomb->missileType = BOMB;
newbomb->next = next;
return newbomb;
}
- (void) airExplosionAt:(NXPoint*)p
{
register EXP_PNTR newExplosion;
register EXP_PNTR ep;
newExplosion = malloc(sizeof(EXP_NODE));
newExplosion->currentPoint = *p;
newExplosion->rad = 0.0;
newExplosion->maxrad = MAXRADIUS + (random() % 10 - 4); // variety is the spice of life
// this makes small explosions where you need big ones and the other way round !
newExplosion->maxReached = NO;
newExplosion->next = NULL;
if (defensiveExplosions.next == NULL)
defensiveExplosions.next = newExplosion;
else
{ ep = defensiveExplosions.next;
while (ep->next != NULL)
ep = ep->next;
ep->next = newExplosion;
}
defensiveExplosions.exploding++;
[exploSound play];
}
- (void) airExplosionAt:(NXPoint*)p withRadius:(float)r // this is for the bomber
{
register EXP_PNTR newExplosion;
register EXP_PNTR ep;
newExplosion = malloc(sizeof(EXP_NODE));
newExplosion->currentPoint = *p;
newExplosion->rad = 0.0;
newExplosion->maxrad = r;
newExplosion->maxReached = NO;
newExplosion->next = NULL;
if (defensiveExplosions.next == NULL)
defensiveExplosions.next = newExplosion;
else
{ ep = defensiveExplosions.next;
while (ep->next != NULL)
ep = ep->next;
ep->next = newExplosion;
}
defensiveExplosions.exploding++;
[exploSound play];
}
- (void) surfaceExplosionAt:(NXPoint*)p
{
register EXP_PNTR ep, newExplosion;
newExplosion = malloc(sizeof(EXP_NODE));
newExplosion->currentPoint = *p;
newExplosion->rad = 0.0;
newExplosion->maxrad = MAXRADIUS + 10;
newExplosion->maxReached = NO;
newExplosion->next = NULL;
if (surfaceExplosions.next == NULL)
surfaceExplosions.next = newExplosion;
else
{ ep = surfaceExplosions.next;
while (ep->next != NULL)
ep = ep->next;
ep->next = newExplosion;
}
surfaceExplosions.exploding++;
[surfExploSound play];
}
- (void) attackMissilesPath
{
MA_NODE_PNTR head, currentPoint, pp;
NXPoint nextPoint;
head = currentPoint = pp = offensiveMissiles.next;
/*
* set up pointers to point at head of attack missile list
*/
while (currentPoint != NULL)
{
/* calculate next point in trajectory */
nextPoint.x = currentPoint->currentPos.x + dFactor * currentPoint->delta_x;
nextPoint.y = currentPoint->currentPos.y + dFactor * currentPoint->delta_y;
if (nextPoint.y < currentPoint->destPos.y)
{
/* attack missile has arrived at target */
switch (currentPoint->missileType)
{
case SPLITTER:
{ currentPoint->next = [self addToIncoming:currentPoint->currentPos:1
:currentPoint->next];
currentPoint->destPos.y = cityBasearray[currentPoint->targetLoc].coord.y;
currentPoint->missileType = BOMB;
break;
}
case BOMB:
{
offensiveMissiles.launched--;
if (currentPoint->targetLoc == (TARGETS - 1))
{ missiles1 = 0;
[outMissiles setIntValue:missiles1];
displayBase1 = FALSE;
}
else
if (currentPoint->targetLoc == (TARGETS - 2))
{ missiles2 = 0;
[toRightMissiles setIntValue:missiles2];
displayBase2 = FALSE;
}
else
cityStatus[currentPoint->targetLoc] = FALSE;
/* erase missile trajectory */
PSWerasetrajectory(currentPoint->currentPos.x,currentPoint->currentPos.y,
currentPoint->startPos.x,currentPoint->startPos.y);
/* creat an explosion node for surface explosions */
[self surfaceExplosionAt:&(currentPoint->currentPos)];
if (currentPoint == head)
{ offensiveMissiles.next = head = currentPoint->next;
free(currentPoint);
currentPoint = head;
}
else
{ pp->next = currentPoint->next;
free(currentPoint);
currentPoint = pp->next;
}
break;
}
}
}
else /* Test to see if missile is hit */
if (testDefensiveBombs(defensiveExplosions.next, currentPoint->currentPos))
{
offensiveMissiles.launched--; /* missile has been destroyed */
score += MSLDSTRCT;
[outScore setIntValue:score];
/* erase missile trajectory */
PSWerasetrajectory(currentPoint->currentPos.x,currentPoint->currentPos.y,
currentPoint->startPos.x,currentPoint->startPos.y);
[self airExplosionAt:&(currentPoint->currentPos)];
/* remove missile from list */
if (currentPoint == head)
{
offensiveMissiles.next = head = currentPoint->next;
free(currentPoint);
currentPoint = pp = head;
}
else
{
pp->next = currentPoint->next;
free(currentPoint);
currentPoint = pp->next;
}
}
else
{
PSWplottrajectory(currentPoint->currentPos.x,
currentPoint->currentPos.y, nextPoint.x, nextPoint.y);
currentPoint->currentPos = nextPoint;
pp = currentPoint;
currentPoint = currentPoint->next;
}
}
}
- showHighs:sender
{
[highScoreTable displayHighScores: self];
return self;
}
int
pntInRect(pnt) NXPoint *
pnt;
{
if (pnt->x >= X_TRG && pnt->y >= Y_TRG && pnt->x <= W_TRG && pnt->y <= H_TRG)
return (TRUE);
else
return (FALSE);
}
void
stepFun(DPSTimedEntry timedE, double timeN, void *data)
{
[(id)data step];
}
/**************************************************************************************************/
/* all the following code has been copied from the NeXT answers code provided by NeXT */
/* and is not currently used */
/***************************************************************************************************/
/* Returns the color at a certain pixel location in a bitmap. */
/* This version assumes the bitmap is 1, 2, 4, or 8 bps deep */
/* and its either grayscale or RGB. */
NXColor
colorAt(NXBitmapImageRep * image, int x, int y)
{
int sampleCnt, bps, spp, amask;
unsigned char *planes[5], data[5];
NXColorSpace colorSpace;
spp = [image samplesPerPixel];
bps = [image bitsPerSample];
colorSpace = [image colorSpace];
#ifdef CHECK_VALIDITY
if ((bps != 1 && bps != 2 && bps != 4 && bps != 8) ||
(colorSpace != NX_RGBColorSpace ||
colorSpace != NX_OneIsBlackColorSpace ||
colorSpace != NX_OneIsWhiteColorSpace))
{
NXLogError("colorAt() can't deal with provided image.\n");
return NX_COLORCLEAR;
}
#endif
#ifdef CHECK_RANGE
if ((x < 0) || (y < 0) || (x >= [image pixelsWide]) ||
(y >= [image pixelsHigh]))
{
NXLogError("Pixel out of bounds in colorAt().\n");
return NX_COLORCLEAR;
}
#endif
[image getDataPlanes:planes];
amask = (1 << bps) - 1; /* 1, 3, 15, 255 for bps = 1, 2, 4, 8 */
/* Get the samples into the data[] array. */
if ([image isPlanar])
{
int pixel = x * bps;
int byteLoc = [image bytesPerRow] * y + (pixel >> 3);
int bitLoc = pixel & 7;
for (sampleCnt = 0; sampleCnt < spp; sampleCnt++)
{
data[sampleCnt] =
((*(planes[sampleCnt] + byteLoc)) >> (8 - bitLoc - bps)) & amask;
}
}
else
{
unsigned char *byteLoc = planes[0] + [image bytesPerRow] * y;
int bitLoc = x * bps * spp;
for (sampleCnt = 0; sampleCnt < spp; sampleCnt++)
{
data[sampleCnt] =
((byteLoc[bitLoc >> 3]) >> (8 - (bitLoc & 7) - bps)) & amask;
bitLoc += bps;
}
}
/* If no alpha, set it to opaque and increment spp. */
/* Otherwise, compute the true color values (by un-premultipling). */
if (![image hasAlpha])
{
data[spp] = amask;
spp++;
}
else
if (data[spp - 1] && data[spp - 1] != amask)
{
for (sampleCnt = 0; sampleCnt < spp - 1; sampleCnt++)
{
data[sampleCnt] =
(unsigned char)(0.5 + amask *
(((float)(data[sampleCnt])) / (data[spp - 1])));
}
}
/* At this point data[] contains spp samples, right justified */
/* within the range 0..amask. We can either return those, or */
/* return them in a normalized fashion (in the range 0..255, */
/* after shifting them over to the left by multiplying them by */
/* 255/amask), or we can return an NXColor. The latter is less */
/* efficient, probably, but most abstract. */
switch (colorSpace)
{
case NX_RGBColorSpace:
return NXConvertRGBAToColor(((float)data[0]) / amask,
((float)data[1]) / amask,
((float)data[2]) / amask,
[image hasAlpha] ? ((float)data[3]) / amask : NX_NOALPHA);
case NX_OneIsWhiteColorSpace:
return NXConvertGrayAlphaToColor(((float)data[0]) / amask,
[image hasAlpha] ? ((float)data[1]) / amask : NX_NOALPHA);
case NX_OneIsBlackColorSpace:
return NXConvertGrayAlphaToColor(((float)(amask - data[0])) / amask,
[image hasAlpha] ? ((float)(amask - data[1])) / amask :
NX_NOALPHA);
case NX_CustomColorSpace:;
case NX_CMYKColorSpace:;
}
return NXConvertGrayAlphaToColor(((float)(amask - data[0])) / amask,
[image hasAlpha] ? ((float)(amask - data[1])) / amask :
NX_NOALPHA); // just get rid of warning
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.