This is NekoView.m in view mode; [Download] [Up]
/* Generated by Interface Builder */ #import "NekoView.h" // based on xneko by Masayuki Koba // This module is really, truly hideous because I chose to preserve the // original code to a large extent rather than reimplementing the state // machine in a sane fashion. #import <libc.h> #import <math.h> #import <appkit/defaults.h> #import <appkit/graphics.h> #import <appkit/Application.h> #import <appkit/NXImage.h> #import <dpsclient/wraps.h> const NXCoord Even[]={ Mati2, // STATE_STOP Jare2, // STATE_JARE Kaki1, // STATE_KAKI Mati3, // STATE_AKUBI Sleep1, // STATE_SLEEP Awake, // STATE_AWAKE Up1, // STATE_U_MOVE Down1, // STATE_D_MOVE Left1, // STATE_L_MOVE Right1, // STATE_R_MOVE UpLeft1, // STATE_UL_MOVE UpRight1, // STATE_UR_MOVE DownLeft1, // STATE_DL_MOVE DownRight1, // STATE_DR_MOVE UpTogi1, // STATE_U_TOGI DownTogi1, // STATE_D_TOGI LeftTogi1, // STATE_L_TOGI RightTogi1 // STATE_R_TOGI }; const NXCoord Odd[]={ Mati2, // STATE_STOP Mati2, // STATE_JARE Kaki2, // STATE_KAKI Mati3, // STATE_AKUBI Sleep2, // STATE_SLEEP Awake, // STATE_AWAKE Up2, // STATE_U_MOVE Down2, // STATE_D_MOVE Left2, // STATE_L_MOVE Right2, // STATE_R_MOVE UpLeft2, // STATE_UL_MOVE UpRight2, // STATE_UR_MOVE DownLeft2, // STATE_DL_MOVE DownRight2, // STATE_DR_MOVE UpTogi2, // STATE_U_TOGI DownTogi2, // STATE_D_TOGI LeftTogi2, // STATE_L_TOGI RightTogi2 // STATE_R_TOGI }; #define PI_PER8 ((double)M_PI/(double)8) static double SinPiPer8Times3; static double SinPiPer8; @implementation NekoView void inline TickCount(NekoView *self) { if ( ++(self->NekoTickCount) >= MAX_TICK ) { self->NekoTickCount = 0; } if ( self->NekoTickCount % 2 == 0 ) { if ( self->NekoStateCount < MAX_TICK ) { self->NekoStateCount++; } } } void inline SetNekoState(NekoView *self, int SetValue) { self->NekoTickCount=0; self->NekoStateCount=0; self->NekoState=SetValue; } void DrawNeko(NekoView *self, NXCoord DrawIcon) { self->NekoLastIcon.origin.x=DrawIcon; if ([self isAutodisplay]) { [self lockFocus]; if (NX_X(&self->NekoPos)!=self->NekoLastXY.x|| NX_Y(&self->NekoPos)!=self->NekoLastXY.y) { const NXRect zero={ { Space, 0.0 }, { (NXCoord)BITMAP_WIDTH, (NXCoord)BITMAP_HEIGHT } }; self->NekoLastXY.y+=(NXCoord)BITMAP_HEIGHT; // X lossage [self->bitmaps composite:NX_SOVER fromRect:&zero toPoint:&self->NekoLastXY]; } self->NekoPos.origin.y+=(NXCoord)BITMAP_HEIGHT; // X lossage [self->bitmaps composite:NX_SOVER fromRect:&self->NekoLastIcon toPoint:&self->NekoPos.origin]; self->NekoPos.origin.y-=(NXCoord)BITMAP_HEIGHT; PSflushgraphics(); [self unlockFocus]; } else [self setNeedsDisplay:YES]; self->NekoLastXY=self->NekoPos.origin; } void NekoDirection(NekoView *self) { int NewState; double LargeX, LargeY; double Length; double SinTheta; if ( self->NekoMoveDx == 0 && self->NekoMoveDy == 0 ) { NewState = STATE_STOP; } else { LargeX = (double)self->NekoMoveDx; LargeY = (double)(-self->NekoMoveDy); Length = sqrt( LargeX * LargeX + LargeY * LargeY ); SinTheta = LargeY / Length; if ( self->NekoMoveDx > 0 ) { if ( SinTheta > SinPiPer8Times3 ) { NewState = STATE_U_MOVE; } else if ( ( SinTheta <= SinPiPer8Times3 ) && ( SinTheta > SinPiPer8 ) ) { NewState = STATE_UR_MOVE; } else if ( ( SinTheta <= SinPiPer8 ) && ( SinTheta > -( SinPiPer8 ) ) ) { NewState = STATE_R_MOVE; } else if ( ( SinTheta <= -( SinPiPer8 ) ) && ( SinTheta > -( SinPiPer8Times3 ) ) ) { NewState = STATE_DR_MOVE; } else { NewState = STATE_D_MOVE; } } else { if ( SinTheta > SinPiPer8Times3 ) { NewState = STATE_U_MOVE; } else if ( ( SinTheta <= SinPiPer8Times3 ) && ( SinTheta > SinPiPer8 ) ) { NewState = STATE_UL_MOVE; } else if ( ( SinTheta <= SinPiPer8 ) && ( SinTheta > -( SinPiPer8 ) ) ) { NewState = STATE_L_MOVE; } else if ( ( SinTheta <= -( SinPiPer8 ) ) && ( SinTheta > -( SinPiPer8Times3 ) ) ) { NewState = STATE_DL_MOVE; } else { NewState = STATE_D_MOVE; } } } if ( self->NekoState != NewState ) { SetNekoState(self, NewState); } } BOOL IsWindowOver(NekoView *self) { BOOL ReturnValue = NO; if ( NX_Y(&self->NekoPos) <= 0.0 ) { self->NekoPos.origin.y = 0.0; ReturnValue = YES; } else if ( NX_Y(&self->NekoPos) >= NX_HEIGHT(&self->bounds) - BITMAP_HEIGHT ) { self->NekoPos.origin.y = NX_HEIGHT(&self->bounds) - BITMAP_HEIGHT; ReturnValue = YES; } if ( NX_X(&self->NekoPos) <= 0.0 ) { self->NekoPos.origin.x = 0; ReturnValue = YES; } else if ( NX_X(&self->NekoPos) >= NX_WIDTH(&self->bounds) - BITMAP_WIDTH ) { self->NekoPos.origin.x = NX_WIDTH(&self->bounds) - BITMAP_WIDTH; ReturnValue = YES; } return( YES ); } BOOL IsNekoDontMove(NekoView *self) { if ( NX_X(&self->NekoPos) == self->NekoLastXY.x && NX_Y(&self->NekoPos) == self->NekoLastXY.y ) { return( YES ); } else { return( NO ); } } BOOL IsNekoMoveStart(NekoView *self) { if ( ( self->PrevMouseX >= self->MouseX - IDLE_SPACE && self->PrevMouseX <= self->MouseX + IDLE_SPACE ) && ( self->PrevMouseY >= self->MouseY - IDLE_SPACE && self->PrevMouseY <= self->MouseY + IDLE_SPACE ) ) { return( NO ); } else { return( YES ); } } void CalcDxDy(NekoView *self) { NXPoint RelativeXY; double LargeX, LargeY; double DoubleLength, Length; [self->window getMouseLocation:&RelativeXY]; [self convertPoint:&RelativeXY fromView:nil]; self->PrevMouseX = self->MouseX; self->PrevMouseY = self->MouseY; self->MouseX = (int)RelativeXY.x; self->MouseY = (int)RelativeXY.y; LargeX = (double)( self->MouseX - NX_X(&self->NekoPos) - BITMAP_WIDTH/2 ); LargeY = (double)( self->MouseY - NX_Y(&self->NekoPos) - BITMAP_HEIGHT ); DoubleLength = LargeX * LargeX + LargeY * LargeY; if ( DoubleLength != (double)0 ) { Length = sqrt( DoubleLength ); if ( Length <= self->NekoSpeed ) { self->NekoMoveDx = (int)LargeX; self->NekoMoveDy = (int)LargeY; } else { self->NekoMoveDx = (int)( ( self->NekoSpeed * LargeX ) / Length ); self->NekoMoveDy = (int)( ( self->NekoSpeed * LargeY ) / Length ); } } else { self->NekoMoveDx = self->NekoMoveDy = 0; } } void NekoThinkDraw(DPSTimedEntry te, double now, void *userData) { NekoView *self=(NekoView *)userData; CalcDxDy(self); if ( self->NekoState != STATE_SLEEP ) DrawNeko(self, self->NekoTickCount % 2 == 0 ? Even[self->NekoState] : Odd[self->NekoState]); else DrawNeko(self, self->NekoTickCount % 8 <= 3 ? Even[self->NekoState] :Odd[self->NekoState]); TickCount(self); switch ( self->NekoState ) { case STATE_STOP: if ( IsNekoMoveStart(self) ) { SetNekoState(self, STATE_AWAKE ); break; } if ( self->NekoStateCount < TIME_STOP ) { break; } if ( self->NekoMoveDx < 0 && NX_X(&self->NekoPos) <= 0 ) { SetNekoState(self, STATE_L_TOGI ); } else if ( self->NekoMoveDx > 0 && NX_X(&self->NekoPos) >= NX_WIDTH(&self->bounds) - BITMAP_WIDTH ) { SetNekoState(self, STATE_R_TOGI ); } else if ( self->NekoMoveDy < 0 && NX_Y(&self->NekoPos) <= 0 ) { SetNekoState(self, STATE_U_TOGI ); } else if ( self->NekoMoveDy > 0 && NX_Y(&self->NekoPos) >= NX_HEIGHT(&self->bounds) - BITMAP_HEIGHT ) { SetNekoState(self, STATE_D_TOGI ); } else { SetNekoState(self, STATE_JARE ); } break; case STATE_JARE: if ( IsNekoMoveStart(self) ) { SetNekoState(self, STATE_AWAKE ); break; } if ( self->NekoStateCount < TIME_JARE ) { break; } SetNekoState(self, STATE_KAKI ); break; case STATE_KAKI: if ( IsNekoMoveStart(self) ) { SetNekoState(self, STATE_AWAKE ); break; } if ( self->NekoStateCount < TIME_KAKI ) { break; } SetNekoState(self, STATE_AKUBI ); break; case STATE_AKUBI: if ( IsNekoMoveStart(self) ) { SetNekoState(self, STATE_AWAKE ); break; } if ( self->NekoStateCount < TIME_AKUBI ) { break; } SetNekoState(self, STATE_SLEEP ); break; case STATE_SLEEP: if ( IsNekoMoveStart(self) ) { SetNekoState(self, STATE_AWAKE ); break; } break; case STATE_AWAKE: if ( self->NekoStateCount < TIME_AWAKE ) { break; } NekoDirection(self); break; case STATE_U_MOVE: case STATE_D_MOVE: case STATE_L_MOVE: case STATE_R_MOVE: case STATE_UL_MOVE: case STATE_UR_MOVE: case STATE_DL_MOVE: case STATE_DR_MOVE: self->NekoPos.origin.x += self->NekoMoveDx; self->NekoPos.origin.y += self->NekoMoveDy; NekoDirection(self); if ( IsWindowOver(self) ) { if ( IsNekoDontMove(self) ) { SetNekoState(self, STATE_STOP ); } } break; case STATE_U_TOGI: case STATE_D_TOGI: case STATE_L_TOGI: case STATE_R_TOGI: if ( IsNekoMoveStart(self) ) { SetNekoState(self, STATE_AWAKE ); break; } if ( self->NekoStateCount < TIME_TOGI ) { break; } SetNekoState(self, STATE_KAKI ); break; default: /* Internal Error */ SetNekoState(self, STATE_STOP ); break; } } void NekoAdjust(NekoView *self) { if ( NX_X(&self->NekoPos) < 0 ) { self->NekoPos.origin.x = 0; } else if ( NX_X(&self->NekoPos) > NX_WIDTH(&self->bounds) - BITMAP_WIDTH ) { self->NekoPos.origin.x = NX_WIDTH(&self->bounds) - BITMAP_WIDTH; } if ( NX_Y(&self->NekoPos) < 0 ) { self->NekoPos.origin.y = 0; } else if ( NX_Y(&self->NekoPos) > NX_HEIGHT(&self->bounds) - BITMAP_HEIGHT ) { self->NekoPos.origin.y = NX_HEIGHT(&self->bounds) - BITMAP_HEIGHT; } } static id Nlist; + initialize { SinPiPer8Times3=sin(PI_PER8*(double)3); SinPiPer8=sin(PI_PER8); [self setVersion:1]; Nlist=[[List alloc] initCount:1]; return self; } + shouldRun:(BOOL)flag { [Nlist makeObjectsPerform:flag ? @selector(start) : @selector(stop)]; return self; } - setBitmaps:anObject { bitmaps=anObject; return self; } - bitmaps { return bitmaps; } - (int)start { if (!teNum&&[window isVisible]) teNum=DPSAddTimedEntry( atof(NXGetDefaultValue([NXApp appName], "Interval"))/1000000.0, NekoThinkDraw, (void *)self, NX_BASETHRESHOLD); return 0; } - (int)stop { if (teNum) { DPSRemoveTimedEntry(teNum); teNum=0; } return 0; } - initFrame:(const NXRect *)frameRect tora:(BOOL)flag { [super initFrame:frameRect]; [self setFlipped:YES]; // I *hate* X!!! [self setClipping:NO]; [self setOpaque:YES]; NXSetRect(&NekoPos, (NX_WIDTH(frameRect)-(NXCoord)(BITMAP_WIDTH/2))/2.0, (NX_HEIGHT(frameRect)-(NXCoord)(BITMAP_HEIGHT/2))/2.0, (NXCoord)BITMAP_WIDTH, (NXCoord)BITMAP_HEIGHT); NekoLastXY=NekoPos.origin; bitmaps=[NXImage findImageNamed:flag ? "tora.tiff" : "bitmaps.tiff"]; NXSetRect(&NekoLastIcon, Even[STATE_STOP], 0.0, (NXCoord)BITMAP_WIDTH, (NXCoord)BITMAP_HEIGHT); NekoSpeed=atof(NXGetDefaultValue([NXApp appName], "Speed")); SetNekoState(self, STATE_STOP); [Nlist addObject:self]; return [self allocateGState]; } - initFrame:(const NXRect *)frameRect { return [self initFrame:frameRect tora:NO]; } - free { [self stop]; [Nlist removeObject:self]; return [super free]; } - (BOOL)acceptsFirstMouse; { return YES; } - mouseDown:(NXEvent *)theEvent { return [window dragFrom:theEvent->location.x:theEvent->location.y eventNum:theEvent->data.mouse.eventNum]; } - drawSelf:(const NXRect *)rects :(int)rectCount { if (rectCount==3) { NXEraseRect(&rects[1]); NXEraseRect(&rects[2]); if (!NXIntersectsRect(&rects[1], &NekoPos)&& !NXIntersectsRect(&rects[2], &NekoPos)) return self; } else NXEraseRect(rects); NekoPos.origin.y+=BITMAP_HEIGHT; // X lossage [bitmaps composite:NX_SOVER fromRect:&NekoLastIcon toPoint:&NekoPos.origin]; NekoPos.origin.y-=BITMAP_HEIGHT; return self; } - sizeTo:(NXCoord)width :(NXCoord)height { [super sizeTo:width:height]; NekoAdjust(self); return self; } - write:(NXTypedStream *)stream { [super write:stream]; NXWriteTypes(stream, "!iiiiiii", &teNum, &NekoTickCount, &NekoStateCount, &NekoState, &MouseX, &MouseY, &PrevMouseX, &PrevMouseY); NXWriteRect(stream, &NekoPos); NXWriteTypes(stream, "ii", &NekoMoveDx, &NekoMoveDy); NXWritePoint(stream, &NekoLastXY); NXWriteRect(stream, &NekoLastIcon); NXWriteType(stream, "d", &NekoSpeed); NXWriteObjectReference(stream, bitmaps); return self; } - read:(NXTypedStream *)stream { [super read:stream]; NXReadTypes(stream, "!iiiiiii", &teNum, &NekoTickCount, &NekoStateCount, &NekoState, &MouseX, &MouseY, &PrevMouseX, &PrevMouseY); teNum=0; NXReadRect(stream, &NekoPos); NXReadTypes(stream, "ii", &NekoMoveDx, &NekoMoveDy); NXReadPoint(stream, &NekoLastXY); NXReadRect(stream, &NekoLastIcon); NXReadType(stream, "d", &NekoSpeed); bitmaps=NXReadObject(stream); return self; } - awake { [super awake]; if (!bitmaps) bitmaps=[NXImage findImageNamed:"bitmaps.tiff"]; // sigh return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.