home *** CD-ROM | disk | FTP | other *** search
- // MazeView by David Bau
- // Copyright 1994 by David Bau. All rights reserved.
- //
- // I feel silly even saying this, but please don't charge for this
- // module or any product containing a portion or modification of it,
- // and when distributing, please give credit where credit is due.
- //
- // If you add anything to this module or derive anything interesting
- // from it, please let me know!
- //
- // I'll probably be bau@cs.cornell.edu for a while.
- //
- // David Bau 01/21/94
- // 777 South Avenue, Weston, MA 02193
- //
- // 01/13/94 Fixed panel maze so that it doesn't change the first
- // time a main-view maze begins drawing; it only changes after the
- // main-view maze finishes. Also, fixed drawSelf to be more efficient.
- // Removed a floaing-point problem with the exponential speed dial.
- // Changed the default colors.
- //
- // 01/14/94 Eliminated lockFocus when [self canDraw]==NO
- // 01/14/94 Modified by Richard Hess (rhess@consilium.com)
- // Fixed startup problems when using MazeView with Engage! Desktop...
- //
- // 02/01/94 Changed maze generation algorithm to make trickier mazes,
- // using a second RandomIntPicker.
-
- #import <appkit/appkit.h>
- #import <defaults/defaults.h>
- #import <libc.h>
- #import <time.h>
- #import <dpsclient/wraps.h>
- #import "MazeView.h"
- #import "RandomIntPicker.h"
- #import "Thinker.h"
-
-
- static void ColorToString(NXColor color, char *str);
- static NXColor ColorFromString(const char *str);
- static NXColor RandomColor();
-
- /* the kind of Maze view that appears in the control panel */
- @implementation StaticMazeView
-
- - initMaze
- {
- /* frame the field 2 cells bigger than the view */
- ncols=MIN((((int)bounds.size.width-wallSize)/cellSize+2),MAXCOLS);
- nrows=MIN((((int)bounds.size.height-wallSize)/cellSize+2),MAXROWS);
- if (ncols<4) ncols=4;
- if (nrows<4) nrows=4;
- xoffset=((int)bounds.size.width-(ncols-2)*cellSize-wallSize)/2-cellSize;
- yoffset=((int)bounds.size.height-(nrows-2)*cellSize-wallSize)/2-cellSize;
-
- /* allocate grid */
- if (Grid) NX_ZONEREALLOC([self zone],Grid,mazecell,nrows*ncols);
- else NX_ZONEMALLOC([self zone],Grid,mazecell,nrows*ncols);
-
- [self computeMaze];
-
- /* choose a random starting place and direction */
- icur=(random()%(ncols-2)+1)+(random()%(nrows-2)+1)*ncols;
- dcur=random()%4;
- Grid[icur].bdir=4;
- ifirst=icur;
- Grid[icur].next=(-1);
-
- /* set the current colors */
- curWallColor=wallColor;
- curPathColor=pathColor;
-
- /* clear drawing buffers */
- wallRectListSize=0;
- pathRectListSize=0;
-
- /* quickly do half-of-a-maze-exploration */
- countDown=((nrows-2)*(ncols-2)*2-2)/2;
- while (countDown--) {
- int inew;
- inew=icur;
- switch (dcur) {
- case 0:
- if (!Grid[icur].eastwall) {dcur=0; inew=icur+1; break;}
- case 1:
- if (!Grid[icur].northwall) {dcur=1; inew=icur+ncols; break;}
- case 2:
- if (!Grid[icur].westwall) {dcur=2; inew=icur-1; break;}
- case 3:
- if (!Grid[icur].southwall) {dcur=3; inew=icur-ncols; break;}
- if (!Grid[icur].eastwall) {dcur=0; inew=icur+1; break;}
- if (!Grid[icur].northwall) {dcur=1; inew=icur+ncols; break;}
- if (!Grid[icur].westwall) {dcur=2; inew=icur-1; break;}
- }
- if (Grid[icur].bdir==dcur) {
- Grid[icur].bdir=(-1);
- } else {
- Grid[inew].next=ifirst;
- ifirst=inew;
- Grid[inew].bdir=(dcur+2)%4;
- }
- icur=inew;
- dcur=(dcur+3)%4;
- }
-
- return self;
- }
-
- - drawSelf:(const NXRect *)rects :(int)rectCount
- {
- PSsetgray(NX_BLACK);
- if (rectCount>1) {
- NXRectFillList(rects+1,rectCount-1);
- } else {
- NXRectFill(rects);
- }
- return [super drawSelf:rects:rectCount];
- }
-
- /* main view can tell panel view what colors to use */
- - setWallColor:(NXColor)wc pathColor:(NXColor)pc
- {
- curWallColor=wallColor=wc;
- curPathColor=pathColor=pc;
- return self;
- }
-
- @end
-
-
-
-
-
-
- @implementation MazeView
-
- - initMaze
- {
- int t;
- /* frame the field 2 cells bigger than the view */
- if (!cellSize) NXLogError("MazeView: cellSize is 0 in initMaze\n");
- ncols=MIN((((int)bounds.size.width-wallSize)/cellSize+2),MAXCOLS);
- nrows=MIN((((int)bounds.size.height-wallSize)/cellSize+2),MAXROWS);
- if (ncols<4) ncols=4;
- if (nrows<4) nrows=4;
- t=(int)bounds.size.width-(ncols-2)*cellSize-wallSize;
- xoffset=(t<0 ? t/2-cellSize : random()%(t+1)-cellSize);
- t=(int)bounds.size.height-(nrows-2)*cellSize-wallSize;
- yoffset=(t<0 ? t/2-cellSize : random()%(t+1)-cellSize);
-
- /* allocate grid */
- if (Grid) NX_ZONEREALLOC([self zone],Grid,mazecell,nrows*ncols);
- else NX_ZONEMALLOC([self zone],Grid,mazecell,nrows*ncols);
- [self computeMaze];
- countDown=(-1);
- ifirst=(-1);
- return self;
- }
-
- - computeMaze
- {
- RandomIntPicker *picker1, *picker2;
-
- /* clear the "last" fields */
- [self clearMaze];
-
- /* compute the maze */
- icur=(random()%(ncols-2)+1)+(random()%(nrows-2)+1)*ncols;
- Grid[icur].bdir=4;
- Grid[icur].eastwall=Grid[icur].northwall=
- Grid[icur].westwall=Grid[icur].southwall=1;
- picker1=[[RandomIntPicker alloc] init];
- picker2=[[RandomIntPicker alloc] init];
- while (1) {
- [picker1 reset];
- [picker2 reset];
- if (Grid[icur+1].bdir<0) {
- [picker1 insert:0];
- if (Grid[icur+2].bdir<0) [picker2 insert:0];
- }
- if (Grid[icur+ncols].bdir<0) {
- [picker1 insert:1];
- if (Grid[icur+2*ncols].bdir<0) [picker2 insert:1];
- }
- if (Grid[icur-1].bdir<0) {
- [picker1 insert:2];
- if (Grid[icur-2].bdir<0) [picker2 insert:2];
- }
- if (Grid[icur-ncols].bdir<0) {
- [picker1 insert:3];
- if (Grid[icur-2*ncols].bdir<0) [picker2 insert:3];
- }
- if ([picker1 count]) {
- int choice;
- choice=[picker2 count] ? [picker2 choice] : [picker1 choice];
- switch(choice) {
- case 0: icur=icur+1; break;
- case 1: icur=icur+ncols; break;
- case 2: icur=icur-1; break;
- case 3: icur=icur-ncols; break;
- }
- Grid[icur].bdir=(choice+2)%4;
- Grid[icur].eastwall=Grid[icur].northwall=
- Grid[icur].westwall=Grid[icur].southwall=1;
- } else {
- switch (Grid[icur].bdir) {
- case 0: Grid[icur].eastwall=0; icur=icur+1;
- Grid[icur].westwall=0; break;
- case 1: Grid[icur].northwall=0; icur=icur+ncols;
- Grid[icur].southwall=0; break;
- case 2: Grid[icur].westwall=0; icur=icur-1;
- Grid[icur].eastwall=0; break;
- case 3: Grid[icur].southwall=0; icur=icur-ncols;
- Grid[icur].northwall=0; break;
- case 4: goto finish;
- }
- }
- }
- finish:
- [picker1 free];
- [picker2 free];
-
- /* walls are set; clear the "last" fields again */
- [self clearMaze];
-
- return self;
- }
-
- - firstStep
- {
- /* choose a random starting place and direction */
- icur=(random()%(ncols-2)+1)+(random()%(nrows-2)+1)*ncols;
- dcur=random()%4;
- Grid[icur].bdir=4;
- ifirst=icur;
- Grid[icur].next=(-1);
-
- /* set the current colors */
- if (randomColor) {
- curWallColor=RandomColor();
- curPathColor=RandomColor();
- } else {
- curWallColor=wallColor;
- curPathColor=pathColor;
- }
-
- if (sharedInspectorPanel) {
- if (panelMazeView) {
- [panelMazeView setWallColor:curWallColor pathColor:curPathColor];
- }
- [sharedInspectorPanel display];
- }
-
- /* clear drawing buffers */
- wallRectListSize=0;
- pathRectListSize=0;
-
- /* clear drawing etc */
- PSsetgray(NX_BLACK);
- NXRectFill(&bounds);
- [self drawForward:icur];
- [self flushDrawing];
-
- /* start countDown */
- countDown=(nrows-2)*(ncols-2)*2-2;
-
- return self;
- }
-
- /* here's where the maze solving and drawing is done */
- - oneStep
- {
- int inew;
- BStimeval curtime;
-
- /* enforce the delay between frames */
- curtime=currentTimeInMs();
- if (curtime-lasttime<delay) {
- if (delay-(curtime-lasttime)>30) {
- usleep(15000);
- return self;
- }
- usleep((delay-(curtime-lasttime))*1000);
- curtime=currentTimeInMs();
- }
- lasttime=curtime;
-
- /* check if finished */
- if (countDown<=0) {
- /* do the following only when recycling, not when running 1st time */
- if (!countDown) {
- [self drawBack:icur];
- [self flushDrawing];
- NXPing();
- if (panelMazeView) {
- [panelMazeView initMaze];
- }
- [self initMaze];
- }
- [self firstStep];
- return self;
- }
- countDown--;
-
- /* follow-the-right-wall algorithm... switch fallthrough intentional */
- inew=icur;
-
- switch (dcur) {
- case 0:
- if (!Grid[icur].eastwall) {dcur=0; inew=icur+1; break;}
- case 1:
- if (!Grid[icur].northwall) {dcur=1; inew=icur+ncols; break;}
- case 2:
- if (!Grid[icur].westwall) {dcur=2; inew=icur-1; break;}
- case 3:
- if (!Grid[icur].southwall) {dcur=3; inew=icur-ncols; break;}
- if (!Grid[icur].eastwall) {dcur=0; inew=icur+1; break;}
- if (!Grid[icur].northwall) {dcur=1; inew=icur+ncols; break;}
- if (!Grid[icur].westwall) {dcur=2; inew=icur-1; break;}
- }
-
- if (Grid[icur].bdir==dcur) {
- [self drawBack:icur];
- Grid[icur].bdir=(-1);
- } else {
- Grid[inew].next=ifirst;
- ifirst=inew;
- Grid[inew].bdir=(dcur+2)%4;
- [self drawForward:inew];
- }
-
- [self flushDrawing];
-
- icur=inew;
- dcur=(dcur+3)%4;
- return self;
- }
-
-
- - drawForward:(int) index
- {
- int x,y;
-
- x=(index%ncols)*cellSize+xoffset;
- y=(index/ncols)*cellSize+yoffset;
- switch (Grid[index].bdir) {
- case 0:
- [self addPathRectOrigin: x+wallSize+gapSize:y+wallSize+gapSize
- size: cellSize:pathSize]; break;
- case 1:
- [self addPathRectOrigin: x+wallSize+gapSize:y+wallSize+gapSize
- size: pathSize:cellSize]; break;
- case 2:
- [self addPathRectOrigin: x-gapSize:y+wallSize+gapSize
- size: cellSize:pathSize]; break;
- case 3:
- [self addPathRectOrigin: x+wallSize+gapSize:y-gapSize
- size: pathSize:cellSize]; break;
- case 4:
- [self addPathRectOrigin:x+wallSize+gapSize:y+wallSize+gapSize
- size: pathSize:pathSize]; break;
- }
-
- if (Grid[index].eastwall && Grid[index+1].next==(-2)) {
- [self addWallRectOrigin:x+cellSize:y size:wallSize:cellSize+wallSize];
- }
- if (Grid[index].northwall && Grid[index+ncols].next==(-2)) {
- [self addWallRectOrigin:x:y+cellSize size:cellSize+wallSize:wallSize];
- }
- if (Grid[index].westwall) {
- [self addWallRectOrigin:x:y size:wallSize:cellSize+wallSize];
- }
- if (Grid[index].southwall) {
- [self addWallRectOrigin:x:y size:cellSize+wallSize:wallSize];
- }
-
- return self;
- }
-
- - drawBack:(int) index
- {
- int x,y;
-
- x=(index%ncols)*cellSize+xoffset;
- y=(index/ncols)*cellSize+yoffset;
-
- [self addEraseRectOrigin:x+wallSize+gapSize:y+wallSize+gapSize
- size:pathSize:pathSize];
-
- switch (Grid[index].bdir) {
- case 0:
- [self addEraseRectOrigin: x+wallSize+gapSize:y+wallSize+gapSize
- size: cellSize:pathSize]; break;
- case 1:
- [self addEraseRectOrigin: x+wallSize+gapSize:y+wallSize+gapSize
- size: pathSize:cellSize]; break;
- case 2:
- [self addEraseRectOrigin: x-gapSize:y+wallSize+gapSize
- size: cellSize:pathSize]; break;
- case 3:
- [self addEraseRectOrigin: x+wallSize+gapSize:y-gapSize
- size: pathSize:cellSize]; break;
- case 4:
- [self addEraseRectOrigin:x+wallSize+gapSize:y+wallSize+gapSize
- size: pathSize:pathSize]; break;
- }
-
- return self;
- }
-
- - addWallRectOrigin:(int) x :(int) y size:(int)w :(int) h
- {
- wallRectList[wallRectListSize].origin.x=x;
- wallRectList[wallRectListSize].origin.y=y;
- wallRectList[wallRectListSize].size.height=h;
- wallRectList[wallRectListSize].size.width=w;
- if (++wallRectListSize>=RECTBUFSIZE) {
- [self flushWallRects];
- }
- return self;
- }
-
- - flushWallRects
- {
- if (wallRectListSize) {
- NXSetColor(curWallColor);
- NXRectFillList(wallRectList, wallRectListSize);
- wallRectListSize=0;
- }
- return self;
- }
-
- - addPathRectOrigin:(int) x :(int) y size:(int)w :(int) h
- {
- pathRectList[pathRectListSize].origin.x=x;
- pathRectList[pathRectListSize].origin.y=y;
- pathRectList[pathRectListSize].size.height=h;
- pathRectList[pathRectListSize].size.width=w;
- if (++pathRectListSize>=RECTBUFSIZE) {
- [self flushPathRects];
- }
- return self;
- }
-
- - flushPathRects
- {
- if (pathRectListSize) {
- NXSetColor(curPathColor);
- NXRectFillList(pathRectList, pathRectListSize);
- pathRectListSize=0;
- }
- return self;
- }
-
- - addEraseRectOrigin:(int) x :(int) y size:(int)w :(int) h
- {
- eraseRectList[eraseRectListSize].origin.x=x;
- eraseRectList[eraseRectListSize].origin.y=y;
- eraseRectList[eraseRectListSize].size.height=h;
- eraseRectList[eraseRectListSize].size.width=w;
- if (++eraseRectListSize>=RECTBUFSIZE) {
- [self flushEraseRects];
- }
- return self;
- }
-
- - flushEraseRects
- {
- if (eraseRectListSize) {
- PSsetgray(NX_BLACK);
- NXRectFillList(eraseRectList, eraseRectListSize);
- eraseRectListSize=0;
- }
- return self;
- }
-
- - flushDrawing
- {
- [self flushWallRects];
- [self flushPathRects];
- [self flushEraseRects];
- return self;
- }
-
- - drawSelf:(const NXRect *)rects :(int)rectCount
- {
- int i;
- for (i=ifirst; i>=0; i=Grid[i].next) {
- int x,y;
- x=i%ncols; y=i/ncols;
- if (x*cellSize-gapSize+xoffset<rects[0].origin.x+rects[0].size.width &&
- x*cellSize+cellSize+wallSize+gapSize+xoffset>rects[0].origin.x &&
- y*cellSize-gapSize+yoffset<rects[0].origin.y+rects[0].size.height &&
- y*cellSize+cellSize+wallSize+gapSize+yoffset>rects[0].origin.y) {
- [self drawForward:i];
- }
- }
- [self flushDrawing];
- return self;
- }
-
- - (const char *) windowTitle
- { return "Maze";
- }
-
- - initFrame:(const NXRect *)frameRect
- {
- Grid=NULL;
- [self getMazeDefaults];
- [super initFrame:frameRect];
- [self initMaze];
- return self;
- }
-
- - free
- {
- if (Grid) NXZoneFree([self zone], Grid);
- Grid=NULL;
- return [super free];
- }
-
- static void ColorToString(NXColor color, char *str)
- {
- sprintf(str,"%f %f %f",NXRedComponent(color),
- NXGreenComponent(color),NXBlueComponent(color));
- }
-
- static NXColor ColorFromString(const char *str)
- {
- NXColor color;
- float r,g,b;
- sscanf(str,"%f %f %f",&r,&g,&b);
- r=(r<0.0 ? 0.0 : (r>1.0 ? 1.0 : r));
- g=(g<0.0 ? 0.0 : (g>1.0 ? 1.0 : g));
- b=(b<0.0 ? 0.0 : (b>1.0 ? 1.0 : b));
- color=NXConvertRGBToColor(r,g,b);
- return color;
- }
-
- static NXColor RandomColor()
- {
- NXColor color;
- float r,g,b;
- r=((random()%65536)/65536.0)/2;
- g=((random()%65536)/65536.0)/2;
- b=((random()%65536)/65536.0)/2;
- color=NXConvertRGBToColor(r,g,b);
- return color;
- }
-
- - getMazeDefaults
- {
- char c;
-
- static NXDefaultsVector MazeDefaults = {
- {"MazeFrameDelay", "16"},
- {"MazeRandomColor", "No"},
- {"MazeWallColor", "0.333338 0.066668 0.066668"},
- {"MazePathColor", "0.200003 0.333338 0.466674"},
- {"MazeWallSize", "2"},
- {"MazePathSize", "4"},
- {"MazeGapSize", "1"},
- {NULL}
- };
-
- NXRegisterDefaults([NXApp appName],MazeDefaults);
- sscanf(NXGetDefaultValue([NXApp appName],"MazeFrameDelay"),"%d",&delay);
- if (delay<0) delay=0;
- if (delay>MAXDELAY) delay=MAXDELAY;
- if ((c=*NXGetDefaultValue([NXApp appName],"MazeRandomColor"))=='y' ||
- c=='Y') {
- randomColor=YES;
- } else {
- randomColor=NO;
- }
- wallColor=
- ColorFromString(NXGetDefaultValue([NXApp appName],"MazeWallColor"));
- pathColor=
- ColorFromString(NXGetDefaultValue([NXApp appName],"MazePathColor"));
- sscanf(NXGetDefaultValue([NXApp appName],"MazeWallSize"),"%d",&wallSize);
- sscanf(NXGetDefaultValue([NXApp appName],"MazePathSize"),"%d",&pathSize);
- sscanf(NXGetDefaultValue([NXApp appName],"MazeGapSize"),"%d",&gapSize);
- cellSize=pathSize+wallSize+2*gapSize;
- if (cellSize<MINCELLSIZE) {
- pathSize+=MINCELLSIZE-cellSize;
- cellSize=MINCELLSIZE;
- }
-
- return self;
- }
-
-
- - sizeTo:(NXCoord)width :(NXCoord)height
- {
- [super sizeTo:width :height];
- [self initMaze];
- return self;
- }
-
- - clearMaze
- {
- int x,y;
-
- icur = -1;
-
- /* clear the field */
- for (y=0; y<nrows; y++) {
- for (x=0; x<ncols; x++) {
- if (x==0 || y==0 || x==ncols-1 || y==nrows-1) {
- Grid[x+y*ncols].bdir=4;
- } else {
- Grid[x+y*ncols].bdir=(-1);
- }
- Grid[x+y*ncols].next=(-2);
- }
- }
-
- return self;
- }
-
-
- /* quick update called when color has been changed */
- - updateViews: sender
- {
- /* update the panel view, if needed */
- if (sharedInspectorPanel) {
- if (panelMazeView) {
- [panelMazeView setWallColor:curWallColor pathColor:curPathColor];
- }
- [sharedInspectorPanel display];
- }
-
- /* update myself */
- if ([self canDraw]) {
- [self lockFocus];
- [self display];
- [self unlockFocus];
- }
-
- return self;
- }
-
- - inspector:sender
- {
- char buf[MAXPATHLEN];
- if (!sharedInspectorPanel) {
- sprintf(buf,"%s/MazeInspector.nib",[sender moduleDirectory:"Maze"]);
- [NXApp loadNibFile:buf owner:self withNames:NO];
- /* initialize some of the panel objects... */
- if (panelWallColorWell) {
- [panelWallColorWell setColor:wallColor];
- [panelWallColorWell setEnabled:!randomColor];
- }
- if (panelPathColorWell) {
- [panelPathColorWell setColor:pathColor];
- [panelPathColorWell setEnabled:!randomColor];
- }
- if (randomColorSwitch) [randomColorSwitch setState:randomColor];
- if (panelSpeedSlider) {
- if (delay<1) {
- [panelSpeedSlider setFloatValue:1.0];
- } else {
- [panelSpeedSlider setFloatValue:
- (float)(-log(delay/99.0)/log(100.0))];
- }
- }
- }
- return sharedInspectorPanel;
- }
-
- - inspectorInstalled
- {
- [self hideCredits:self];
- if (sharedInspectorPanel) {
- [sharedInspectorPanel display];
- }
- return self;
- }
-
- - doRandomColorSwitch:sender
- {
- if ([sender state]) {
- [panelWallColorWell setEnabled:NO];
- [panelPathColorWell setEnabled:NO];
- randomColor=YES;
- curWallColor=RandomColor();
- curPathColor=RandomColor();
- NXWriteDefault([NXApp appName],"MazeRandomColor","Yes");
- } else {
- [panelWallColorWell setEnabled:YES];
- [panelPathColorWell setEnabled:YES];
- randomColor=NO;
- curWallColor=wallColor;
- curPathColor=pathColor;
- NXWriteDefault([NXApp appName],"MazeRandomColor","No");
- }
- [self perform:@selector(updateViews:) with:sender
- afterDelay:0 cancelPrevious:YES];
- return self;
- }
-
- - doSpeedSlider:sender
- {
- char str[80];
- delay=99.0*exp(-[sender floatValue]*log(100.0));
- sprintf(str,"%d",delay);
- NXWriteDefault([NXApp appName],"MazeFrameDelay",str);
- return self;
- }
-
- - takeWallColorFrom:sender
- {
- char str[80];
- curWallColor=wallColor=[(NXColorWell *)sender color];
- [self perform:@selector(updateViews:) with:sender
- afterDelay:0 cancelPrevious:YES];
- ColorToString(wallColor,str);
- NXWriteDefault([NXApp appName],"MazeWallColor",str);
- return self;
- }
-
- - takePathColorFrom:sender
- {
- char str[80];
- curPathColor=pathColor=[(NXColorWell *)sender color];
- [self perform:@selector(updateViews:) with:sender
- afterDelay:0 cancelPrevious:YES];
- ColorToString(pathColor,str);
- NXWriteDefault([NXApp appName],"MazePathColor",str);
- return self;
- }
-
- - showCredits:sender
- {
- if (panelCreditsView && sharedInspectorPanel) {
- if ([panelCreditsView isDescendantOf:sharedInspectorPanel]) {
- [panelCreditsView removeFromSuperview];
- }
- [sharedInspectorPanel addSubview:panelCreditsView];
- [sharedInspectorPanel display];
- }
- return self;
- }
-
- - hideCredits:sender
- {
- if (panelCreditsView && sharedInspectorPanel) {
- if ([panelCreditsView isDescendantOf:sharedInspectorPanel]) {
- [panelCreditsView removeFromSuperview];
- }
- [sharedInspectorPanel display];
- }
- return self;
- }
-
- /* these methods are needed because we should not rearrange the view */
- /* hierarchy while a button is active; we queue the rearranging to be */
- /* done later, when nothing is locked on the view */
-
- - doShowCredits:sender
- {
- [self perform:@selector(showCredits:)
- with:self afterDelay:0 cancelPrevious:YES];
- return self;
- }
-
- - doHideCredits:sender
- {
- [self perform:@selector(hideCredits:)
- with:self afterDelay:0 cancelPrevious:YES];
- return self;
- }
-
- @end
-
-
-