This is ArcadeApp.m in view mode; [Download] [Up]
// (C) Copyright 1992 Jim Patterson
#import <appkit/appkit.h>
#import <objc/objc.h>
#import <appkit/ClipView.h>
#import <dpsclient/event.h>
#import <soundkit/soundkit.h>
#import "ArcadeApp.h"
#import "Animator.h"
#import "LessonPlan.h"
#import "GameView.h"
#import "PerformanceEvaluator.h"
// Time between timer interrupts to scroll screen vertically
#define TA_CYCLE_TIME 0.1
// Number of points to scroll vertically each cycle (must be even!)
#define TA_DEFAULT_CYCLE_DELTA 5
#define TA_ENDGAME_CYCLE_DELTA 15
@implementation ArcadeApp
+ new
{
self = [super new];
startedWithFile = NO;
// other stuff here when necessary
return self;
}
- appDidInit:sender
{
NXRect clipViewFrame;
int i;
id gameFont;
// initialize Animator instance
timer = [Animator newChronon:TA_CYCLE_TIME
adaptation:0.0
target:self
action:@selector(cycleOnce:)
autoStart:NO
eventMask:NX_KEYDOWNMASK ];
// further initialize LessonPlan instance
[lessonPlan postInitialize];
if (!startedWithFile) {
for (i=0; i < 128; i++) [lessonPlan deselectKey:i];
[lessonPlan selectKeysBetween:'a' :'z']; // select upper & lower case letters
[lessonPlan selectKey:TA_KEY_LEFTSHIFT];
[lessonPlan selectKey:TA_KEY_RIGHTSHIFT];
[[lessonPlan lessonNumMatrix] selectCellAt:2:0];
[lessonPlan setLessonNum: 3];
}
[lessonPlan flushKeyboard];
// setup sound objects
explosionSound = [Sound newFromMachO:"explosion.snd"];
keyHitSound = [Sound newFromMachO:"keyHit.snd"];
// further initialize view hierarchy
NXSetRect(&clipViewFrame,
TA_CLIPVIEW_X,TA_CLIPVIEW_Y,
TA_CLIPVIEW_W,TA_CLIPVIEW_H);
clipView = [ClipView newFrame:&clipViewFrame];
[clipView setBackgroundGray:NX_LTGRAY];
[[gameWindow contentView] addSubview:clipView];
[gameWindow makeFirstResponder:self];
[[gameWindow contentView] lockFocus];
strcpy(personalFileName,"untitled");
return self;
}
- appDidHide:sender
{
[self stopGame:self];
return self;
}
- setGameWindow:anObject { gameWindow = anObject; return self; }
- setGameView:anObject { gameView = anObject; return self; }
- setLessonPlan:anObject { lessonPlan = anObject; return self; }
- setPerformanceEvaluator:anObject {performanceEvaluator = anObject; return self; }
- setSpeedSlider:anObject { speedSlider = anObject; return self; }
- setPauseButton:anObject {pauseButton = anObject; return self; }
- setInstructionsScrollView:anObject {instructionsScrollView = anObject; return self; }
- lessonPlan {return lessonPlan;}
- performanceEvaluator {return performanceEvaluator; }
- gameWindow {return gameWindow; }
- speedChanged:sender
{
int t = [sender tag];
if (t == 0) cycleDelta = [sender intValue]; // sender was the slider
else {
cycleDelta +=t; // else it was a speedup/slowdown key
if (cycleDelta > 20) cycleDelta = 20;
else if (cycleDelta <1) cycleDelta = 1;
[speedSlider setIntValue:cycleDelta];
}
return self;
}
- startNewGame:sender
{
NXRect gameViewFrame;
NXPoint aPoint;
if ([pauseButton state]) { // unpause if we were paused
[timer startEntry];
[pauseButton setState:0];
}
// Don't start a new game if we are already playing
if ([timer isTicking]) return self;
// turn off pause button if it is on
[pauseButton setState:0];
// give user a get-ready message
aPoint.x = 0; aPoint.y = TA_GAMEVIEW_H - TA_CLIPVIEW_H;
[clipView rawScroll:&aPoint];
// create a new GameView
[gameWindow disableDisplay];
[self getReadyPanel];
[gameWindow reenableDisplay];
gameView = [GameView standardNewFrame];
[gameWindow disableDisplay];
[clipView setDocView:gameView];
aPoint.x = 0;
aPoint.y = -TA_CLIPVIEW_H;
[clipView rawScroll:&aPoint]; // orient gameView within clipView
[gameWindow reenableDisplay];
[gameView display];
[clipView setBackgroundGray:NX_LTGRAY];
// reorient from the beginning
gameProgress = 0;
gamePhase = 0;
cycleDelta = [speedSlider intValue];
currentLowestLetter = [gameView lowestLetter]; // figure out which letter we are looking for
[timer startEntry];
return self;
}
- stopGame:sender
{
NXPoint aPoint;
if (([timer isTicking]) || ([pauseButton state])) {
[timer stopEntry];
[pauseButton setState:0];
aPoint.x = 0; aPoint.y = TA_GAMEVIEW_H - TA_CLIPVIEW_H;
[clipView rawScroll:&aPoint];
[performanceEvaluator tellGameCompleted:NO];
[performanceEvaluator summarize];
[performanceEvaluator sortKeyInfo];
[performanceEvaluator flush];
}
return self;
}
- playerLoses:sender
{
NXPoint aPoint;
[explosionSound play];
[timer stopEntry];
aPoint.x = 0; aPoint.y = TA_GAMEVIEW_H - TA_CLIPVIEW_H;
[clipView rawScroll:&aPoint];
[performanceEvaluator tellGameCompleted:NO];
[performanceEvaluator summarize];
[performanceEvaluator sortKeyInfo];
[performanceEvaluator flush];
sleep(3);
return self;
}
- playerWins:sender
{
NXPoint aPoint;
[timer stopEntry];
aPoint.x = 0; aPoint.y = TA_GAMEVIEW_H - TA_CLIPVIEW_H;
[clipView rawScroll:&aPoint];
[performanceEvaluator tellGameCompleted:YES];
[performanceEvaluator summarize];
[performanceEvaluator sortKeyInfo];
[performanceEvaluator flush];
sleep(3);
return self;
}
- cycleOnce:sender
{
NXPoint aPoint;
NXRect aRect;
int dangerLevel;
if (gamePhase == 2)
gameProgress += TA_ENDGAME_CYCLE_DELTA;
else gameProgress += cycleDelta;
aPoint.x = 0; aPoint.y = gameProgress - TA_CLIPVIEW_H;
[clipView rawScroll:&aPoint]; // set's clip view's bounds.origin to aPoint
switch (gamePhase) {
case 0: // starting phase...waiting for signal to change background to black
if (gameProgress >= TA_GAMEVIEW_TOBLACK_H) {
[clipView setBackgroundGray:NX_BLACK];
gamePhase = 1;
}
break;
case 1: // playing phase...waiting for signal to change background to gray
if (gameProgress >= TA_GAMEVIEW_SPEEDUP_H) {
[clipView setBackgroundGray:NX_LTGRAY];
gamePhase = 2;
}
break;
case 2: // winding down phase...waiting for signal to stop scrolling
if (gameProgress >= TA_GAMEVIEW_STOP_H)
[self playerWins:self];
break;
}
dangerLevel = TA_GAMEVIEW_Y + TA_GAMEVIEW_HEADER_H + TA_CLIPVIEW_H +
[gameView lowestNonemptyRow]*TA_LINE_HEIGHT - gameProgress;
if (dangerLevel < 0)
if (gameProgress + dangerLevel < 2750)
[self playerLoses:self];
return self;
}
- pauseGame:sender
{
if ([sender state]) [timer stopEntry];
else [timer startEntry];
return self;
}
- keyDown:(NXEvent *) theEvent
{
char key;
if (![timer isTicking]) return self;
key = (char) theEvent->data.key.charCode;
// correct key was hit
if (key == currentLowestLetter) {
[gameView lowestLetterWasHit:gameProgress]; // notify view of letter hit
currentLowestLetter = [gameView lowestLetter]; // find out new char to look for
}
else if ((key == (int)'\'') && (currentLowestLetter == ',')) { // comma == apostrophe
[gameView lowestLetterWasHit:gameProgress]; // notify view of letter hit
currentLowestLetter = [gameView lowestLetter]; // find out new char to look for
}
else if ((key == (int)',') && (currentLowestLetter == '\'')) { // comma == apostrophe
[gameView lowestLetterWasHit:gameProgress]; // notify view of letter hit
currentLowestLetter = [gameView lowestLetter]; // find out new char to look for
}
else [performanceEvaluator logKeyMiss:currentLowestLetter];
return self;
}
- getReadyPanel
{
[[gameWindow contentView] lockFocus];
[self boxWithStr:"Get Ready !"];
[[gameWindow contentView] unlockFocus];
return self;
}
- boxWithStr:(char *) str
{
// warning: must lockFocus before calling this
float boxX,boxY,boxW,boxH,strX,strY,strW,strH;
NXRect aRect;
boxX = TA_CLIPVIEW_X+TA_GAMEVIEW_TRAILER_XMARGIN;
boxY = TA_CLIPVIEW_Y+(TA_CLIPVIEW_H - TA_GAMEVIEW_TRAILER_BOX_H)/2;
boxW = TA_GAMEVIEW_TRAILER_BOX_W;
boxH = TA_GAMEVIEW_TRAILER_BOX_H;
// clear box area and draw border
PSsetgray(NX_DKGRAY);
NXSetRect(&aRect,boxX,boxY,boxW,boxH);
NXDrawGroove(&aRect,&aRect);
// draw str centered vertically and horizontally
PSstringwidth(str,&strW,&strH);
strX = boxX + (boxW - strW)/2;
strY = boxY + (boxH - strH)/2;
PSsetgray(NX_BLACK);
PSmoveto(strX,strY);
PSscale(1,-1);
PSshow(str);
return self;
}
- saveAs:sender
{
NXTypedStream *tstream;
id sPanel;
sPanel = [SavePanel new];
[sPanel setRequiredFileType:"ta"];
if ([sPanel runModal]) {
tstream = NXOpenTypedStreamForFile([sPanel filename], NX_WRITEONLY);
[performanceEvaluator writeInfo:tstream];
[lessonPlan writeInfo:tstream];
NXCloseTypedStream(tstream);
strcpy(personalFileName,[sPanel filename]);
}
return self;
}
- save:sender
{
NXTypedStream *tstream;
id sPanel;
if (strcmp(personalFileName,"untitled") == 0)
[self saveAs:sender];
else {
tstream = NXOpenTypedStreamForFile(personalFileName,NX_WRITEONLY);
[performanceEvaluator writeInfo:tstream];
[lessonPlan writeInfo:tstream];
NXCloseTypedStream(tstream);
}
return self;
}
- open:sender
{
NXStream *tstream;
id oPanel;
const char *fileTypes[2]={"ta",NULL};
oPanel = [OpenPanel new];
if ([oPanel runModalForTypes:fileTypes]) {
tstream = NXOpenTypedStreamForFile([oPanel filename],NX_READONLY);
[performanceEvaluator readInfo:tstream];
[lessonPlan readInfo:tstream];
NXCloseTypedStream(tstream);
strcpy(personalFileName,[oPanel filename]);
}
return self;
}
- printInstructions:sender
{
[[instructionsScrollView docView] printPSCode:sender];
return self;
}
- (BOOL) appAcceptsAnotherFile:sender { return YES; }
- (int) appOpenFile:(const char *) filename type:(const char *) aType
{
NXStream *tstream;
tstream = NXOpenTypedStreamForFile(filename,NX_READONLY);
[performanceEvaluator readInfo:tstream];
[lessonPlan readInfo:tstream];
NXCloseTypedStream(tstream);
strcpy(personalFileName,filename);
startedWithFile = YES;
return YES;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.