This is Celestial.m in view mode; [Download] [Up]
#import "Celestial.h"
#import "TiffManager.h"
#import "Thinker.h"
#import <appkit/NXBitmapImageRep.h>
#define PI (3.141592653589)
#define MAXMULTBODY 10 //total multiple objects allowed
@implementation Celestial
- init
{
[super init];
tiffStorageIndex = 0;
animationIndex = 0;
tiffManagerObject = [[TiffManager alloc] init];
tiffStorage = [tiffManagerObject returnTiffStorage];
bodyList = [[List alloc] init];
avoidStorage = [[Storage allocFromZone:[self zone]] initCount:0
elementSize:sizeof(AvoidStruct) description:"{ffff}"];
okToDoAnim = NO;
currentBodyIndex = 0;
cycleStartIndex = 0;
cycles = 0;
currentCycle = 1;
bodyCount = 0;
tiffsNeedBuilding = YES;
okToDoAnim = NO;
multDelay = floor(randBetween(objectSpeed * 0.1,
(objectSpeed * 0.1) + 10));
currentMultTotal = 1;
[starsObject setStarsStopped];
PSsetlinewidth(4.0); //0.0 is more efficient but I liked this better
return self;
}
// Every module should have a first state method
//since class info only gets loaded once - therefore only 1 init called
- setFirstState
{
firstState = YES;
okToDoAnim = NO;
cycleStartIndex = currentBodyIndex;
bodyCount = 0;
tiffsNeedBuilding = YES;
okToDoAnim = NO;
multDelay = floor(randBetween(objectSpeed * 0.1,
(objectSpeed * 0.1) + 10));
currentMultTotal = 1;
starsStopping = NO; //[starsObject isStopping]; //should be NO
starsStopped = NO; //[starsObject isStopped]; //should be NO
bodiesDone = 0;
currentCycle = 1;
return self;
}
- (BOOL)doUntilDone
{
BOOL done;
int ii,count;
done = NO;
if(!starsStopping){
if(!okToDoAnim){ //go build the tiffs
if(tiffsNeedBuilding){
tiffsNeedBuilding = [tiffManagerObject createTiffs];
}
else { //do the animation set up
totalBodies = [tiffStorage count];
animBuilt = (MAXANIMATIONS > totalBodies) ? totalBodies :
MAXANIMATIONS; //pick smaller
[self changeBodyList:currentBodyIndex];
if(soundEnabled)
[pwrUpSnd play];
[starsObject startStars];
starsStopping = NO; //[starsObject isStopping]; //should be NO
starsStopped = NO; //[starsObject isStopped]; //should be NO
okToDoAnim = YES;
}
} //now do the animation
else {
if(nextStartTime < 0){ //time to do animation
count = [bodyList count];
multDelay--;
if(multDelay < 0){ // reset delay timer for next multiple object
currentMultTotal++;
if(currentMultTotal > count)
currentMultTotal = count;
multDelay = floor(randBetween(objectSpeed * 0.1,
(objectSpeed * 0.1) + 10));
}
if(count){ // there are some objects to do
for(ii = 0;ii < currentMultTotal;ii++){
currentBody = [bodyList objectAt:ii];
if([currentBody doUntilDone]){
[bodyList removeObjectAt:ii];
ii--;
currentMultTotal--;
currentBody = [currentBody free];
}
}
[starsObject setAvoidRect:avoidStorage];//set avoidance for all
//bodies
}
else { // object has zipped off screen
//in case of multiple - last one has zipped off screen
[self setNextStartTime];
currentMultTotal = 1;
multDelay = floor(randBetween(objectSpeed * 0.1,
(objectSpeed * 0.1) + 10));
bodyCount++;
if(bodyCount >= animBuilt ){ //complete cycle is done
currentCycle++;
if(currentCycle > cycles){//done with this module
if(!([starsObject isStopping])){
if(soundEnabled)
[pwrDownSnd play];
[starsObject stopStars];
starsStopping = YES;
}
currentBodyIndex++;
if(currentBodyIndex >= totalBodies)
currentBodyIndex = 0;
cycleStartIndex = currentBodyIndex;
}
else {
currentBodyIndex = cycleStartIndex;
bodyCount = 0;
[self changeBodyList:currentBodyIndex]; //create body
}
}
else {
currentBodyIndex++;
if(currentBodyIndex >= totalBodies)
currentBodyIndex = 0;
[self changeBodyList:currentBodyIndex]; //create body
}
}
}
else
nextStartTime--;
}
}
else{ //stars are stopping
if(starsStopped){
PSsetlinewidth(0.0); //reset
done = YES;
}
}
return done;
}
- setStartInterval: (Slider *)sender;
{
startInterval = [sender intValue];
nextStartTime = 0;
return self;
}
- setNextStartTime
{
nextStartTime = startInterval;
return self;
}
- setPwrDownSnd:(Sound *)theSound
{
pwrDownSnd = theSound;
return self;
}
- setPwrUpSnd:(Sound *)theSound;
{
pwrUpSnd = theSound;
return self;
}
- setObjectSpeed: (Slider *)sender
{
objectSpeed =([sender maxValue] + [sender minValue]) - [sender floatValue];
return self;
}
- setBoundsRect:(NXRect *)r
{
bounds.origin.x = r->origin.x;
bounds.origin.y = r->origin.y;
bounds.size.width = r->size.width;
bounds.size.height = r->size.height;
return self;
}
- changeBodyList:(int)index
{
int ii;
int total;
AvoidStruct avoidStruct;
for(ii=0;ii < 4;ii++) //init the lastTheta array
lastTheta[ii] = -1;
total = floor(randBetween(2,MAXMULTBODY));
if([bodyList count]){
[bodyList freeObjects];
}
[avoidStorage empty];
isMult = ((ImageStruct *)[tiffStorage elementAt:
index])->isMult;
avoidStruct.avoid.origin.x = 0.0;
avoidStruct.avoid.origin.y = 0.0;
avoidStruct.avoid.size.width = 0.0;
avoidStruct.avoid.size.height = 0.0;
if(isMult){
for(ii=0;ii < total;ii++){
currentBody = [self createBody:index];
[currentBody setBodyIndex:ii];
[bodyList addObject:currentBody];
[avoidStorage addElement:&avoidStruct];
}
}
else{
currentBody = [self createBody:currentBodyIndex];
[bodyList addObject:currentBody];
[avoidStorage addElement:&avoidStruct];
}
return self;
}
- (Body *)createBody:(int)index
{
Body *body;
float theta; //angle of travel for new body
float multObjectSpeed;
body = [[Body alloc] init];
[body setAvoidStorage:avoidStorage];
[body setImageList:((ImageStruct *)[tiffStorage
elementAt:index])->imageList];
[body setNumberOfFrames];
[body setBoundsRect:(NXRect *)&bounds]; //must be done after setCellSize
if(isMult){
multObjectSpeed = objectSpeed + floor(randBetween(0,10));
[body setObjectSpeed:multObjectSpeed];
theta = [self genUniqueTheta]; //make sure mult objects stay clear
//of each other
}
else{
[body setObjectSpeed:objectSpeed];
theta = randBetween(0,(2*PI));
}
[body setAngle:theta];
[body setStarsOutlet: (id)starsObject];
return(body);
}
- starsStopped
{
starsStopped = YES;
return self;
}
- setStarsOutlet:(id)starsOutlet;
{
starsObject = starsOutlet;
return self;
}// just a prototype - not used in this module
- setStarSpeed:sender
{
return self;
}
//The idea here was that individual modules could decide
//how to interpret the slider value
// if you only want this module to do one pass etc.
- (int)setCycleValue:(int)value;
{
int mutiplier;
// since this module is interesting make it 5 times the slider value
// could be zero
mutiplier = 5;
if(!value)
value = 1;
else
value *= mutiplier;
cycles = value;
currentCycle = 1;
return mutiplier;
}
- windowSizeChanged
{
if(currentBody){ //if one exists
[self setNextStartTime];
//[bodyList freeObjects];
//printf("window size changed bodylist has %d elements\n",
//[bodyList count]);
currentMultTotal = 1;
currentBody = nil;
[self changeBodyList:currentBodyIndex];
}
return self;
}
- freeResources
{
[tiffManagerObject freeTiffs];
if ([bodyList count]){
[bodyList freeObjects];
}
return self;
}
// just a prototype - sound isn't used in this module
- setSoundEnabled:(BOOL)enabled
{
soundEnabled = enabled;
return self;
}
//for each 4 in a row - make sure random theta is
// 40 degrees from any of the others
- (float)genUniqueTheta
{
int ii;
int maxTest = 4;
float theta;
BOOL thetaOK = NO;
theta = randBetween(0,(2*PI));
while(!thetaOK){
thetaOK = YES;
for(ii = 0;ii < maxTest;ii++){
if(lastTheta[ii] < 0)
continue;
//.69 = 40 degrees
if(theta > (lastTheta[ii] - .69) && theta < (lastTheta[ii] + .69)){
thetaOK = NO;
theta = randBetween(0,(2*PI));
}
}
}
for(ii = 1;ii < maxTest;ii++){
lastTheta[ii-1] = lastTheta[ii];
}
lastTheta[ii-1] = theta;
return theta;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.