This is MapView.m in view mode; [Download] [Up]
// // $Id: MapView.m,v 1.8 1997/10/31 04:51:53 nygard Exp $ // // // This file is a part of Empire, a game of exploration and conquest. // Copyright (C) 1996 Steve Nygard // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // // You may contact the author by: // e-mail: nygard@telusplanet.net // #import "Empire.h" RCSID ("$Id: MapView.m,v 1.8 1997/10/31 04:51:53 nygard Exp $"); #import <AppKit/NSEvent.h> #import <AppKit/psopsNeXT.h> #import "EmpireImageVendor.h" #import "Map.h" #import "MapView.h" #import "EmpireProtocols.h" static NSImage *map_images[256]; static NSImage *explosion; static NSImage *default_cursor; static NSImage *direction_cursor; static NSImage **oddballs[4]; //====================================================================== // The Map View is the main interface to the game. It displays the // terrain, cities, and units of a Map, and forwards messages of user // actions to a delegate. // // This should be reasonable fast, as it was developed on a machine // with a rather slow display. //====================================================================== @implementation MapView + (void) initialize { if (self == [MapView class]) { NSImage *city; NSImage **onLand; NSImage **onWater; NSImage **onCity; NSImage **od; EmpireImageVendor *vendor = [EmpireImageVendor instance]; int a, b; int i; id tmp; [vendor player:p_player1 :&city :&onLand :&onWater :&onCity :&od]; // Default all values to invalid for (i = 0; i < 256; i++) map_images[i] = nil; // Set default cursor : player 1,2,3 (icon:15) terrain:0,1,2,3 default_cursor = tmp = [vendor cursor]; for (a = p_player1; a <= p_player3; a++) { for (b = t_unknown; b <= t_city; b++) map_images[EMCreateMapToken (b, a, 15, YES)] = tmp; } // Set direction cursor : player 1,2,3 (icon:14) terrain:0,1,2,3 direction_cursor = tmp = [vendor directionCursor]; for (a = p_neutral; a <= p_player3; a++) { for (b = t_unknown; b <= t_city; b++) map_images[EMCreateMapToken (b, a, 14, YES)] = tmp; } // Player 0 - Neutral. map_images[EMCreateMapToken (t_unknown, p_neutral, i_none, YES)] = [vendor unknown]; map_images[EMCreateMapToken (t_water, p_neutral, i_none, YES)] = [vendor water]; map_images[EMCreateMapToken (t_land, p_neutral, i_none, YES)] = [vendor land]; map_images[EMCreateMapToken (t_city, p_neutral, i_none, YES)] = [vendor neutralCity]; for (a = p_player1; a <= p_player3; a++) { [vendor player:a :&city :&onLand :&onWater :&onCity :&oddballs[a]]; map_images[EMCreateMapToken (t_city, a, i_none, YES)] = city; // on water: for (b = i_fighter; b <= i_hovercraft; b++) { i = EMCreateMapToken (t_water, a, b, YES); map_images[i] = onWater[b - 3]; } // on land: map_images[EMCreateMapToken (t_land, a, i_army, YES)] = onLand[0]; map_images[EMCreateMapToken (t_land, a, i_sentry, YES)] = onLand[1]; map_images[EMCreateMapToken (t_land, a, i_fighter, YES)] = onLand[2]; map_images[EMCreateMapToken (t_land, a, i_hovercraft, YES)] = onLand[3]; // on city for (b = i_army; b <= i_hovercraft; b++) { i = EMCreateMapToken (t_city, a, b, YES); map_images[i] = onCity[b - 1]; } } explosion = [vendor explosion]; } } //---------------------------------------------------------------------- - initWithFrame:(NSRect)frameRect { [super initWithFrame:frameRect]; map = nil; delegate = nil; compressEvents = NO; // Do we need this? cursorEnabled = NO; cursorOn = NO; fastCursor = NO; cursorLocation.row = 5; cursorLocation.column = 10; cursorTimer = nil; cursorImage = default_cursor; cursorIcon = i_none; cursorPlayer = p_neutral; [[EmpireImageVendor instance] attach:self]; [self setCursorEnabled:NO]; return self; } //---------------------------------------------------------------------- - (void) dealloc { if (map != nil) [map detach:self]; // Make sure timer is finished. [self setCursorEnabled:NO]; [super dealloc]; } //---------------------------------------------------------------------- - (BOOL) acceptsFirstResponder { return YES; } //---------------------------------------------------------------------- - (BOOL) acceptsFirstMouse:(NSEvent *)theEvent { return YES; } //---------------------------------------------------------------------- - (BOOL) isOpaque { return YES; } //---------------------------------------------------------------------- - (void) drawRect:(NSRect)rect { EMMapSize mapSize; int row, column; EMMapLocation location1, location2; NSPoint aPoint; MapToken **mapPtrs; MapToken tmp; PSsetgray (NSDarkGray); NSRectFill (rect); if (map != nil) { mapPtrs = [map mapPtrs]; mapSize = [map mapSize]; aPoint.x = NSMinX (rect); aPoint.y = NSMinY (rect); location1 = [self getLocationForPoint:aPoint]; aPoint.x += NSWidth (rect); aPoint.y += NSHeight (rect); location2 = [self getLocationForPoint:aPoint]; //NSLog (@"%d,%d -> %d,%d", location2.row, location1.column, location1.row, location2.column); for (row = location2.row; row <= location1.row; row++) { aPoint.y = 16.0 * (float)(mapSize.height - 1 - row); for (column = location1.column; column <= location2.column; column++) { aPoint.x = 16.0 * (float)column; tmp = mapPtrs[row][column]; if (tmp > 255 && map_images[0] != nil) [map_images[0] compositeToPoint:aPoint operation:NSCompositeSourceOver]; else if (map_images[tmp] != nil) [map_images[tmp] compositeToPoint:aPoint operation:NSCompositeSourceOver]; else { PSsetgray (NSDarkGray); NSRectFill (NSMakeRect(aPoint.x, aPoint.y, 16, 16)); } } } } } //---------------------------------------------------------------------- - (Map *) map { return map; } //---------------------------------------------------------------------- - (void) setMap:(Map *)aMap { EMMapSize mapSize; if (aMap == nil) return; if (map != nil) { [map detach:self]; [map release]; } map = aMap; [map attach:self]; [map retain]; mapSize = [map mapSize]; [self setFrameSize:NSMakeSize (16.0*(float)mapSize.width, 16.0*(float)mapSize.height)]; [self setNeedsDisplay:YES]; } //---------------------------------------------------------------------- - (EMMapLocation) getLocationForPoint:(NSPoint)point { EMMapSize mapSize; EMMapLocation target; NSAssert (map != nil, @"Map was nil."); mapSize = [map mapSize]; target.row = mapSize.height - (int)(point.y / 16.0) - 1; target.column = point.x / 16.0; target.row = (target.row >= mapSize.height) ? mapSize.height - 1 : target.row; target.row = (target.row <= 0) ? 0 : target.row; target.column = (target.column >= mapSize.width) ? mapSize.width - 1 : target.column; target.column = (target.column < 0) ? 0 : target.column; return target; } //---------------------------------------------------------------------- - (NSPoint) getPointForLocation:(EMMapLocation)target { EMMapSize mapSize; NSAssert (map != nil, @"Map was nil."); mapSize = [map mapSize]; return NSMakePoint (16.0 * (float)target.column, (mapSize.height - 1 - target.row) * 16.0); } //---------------------------------------------------------------------- - (NSPoint) getCenterPointForLocation:(EMMapLocation)target { NSRect cellRect = [self getRectForLocation:target]; return NSMakePoint (NSMidX (cellRect), NSMidY (cellRect)); } //---------------------------------------------------------------------- - (NSRect) getRectForLocation:(EMMapLocation)target { EMMapSize mapSize; NSAssert (map != nil, @"Map was nil."); mapSize = [map mapSize]; return NSMakeRect (16.0 * (float)target.column, (mapSize.height - 1 - target.row) * 16.0, 16.0, 16.0); } //---------------------------------------------------------------------- - (NSRect) getRectAround3x3Location:(EMMapLocation)target { EMMapLocation location1, location2; EMMapSize mapSize; NSAssert (map != nil, @"Map was nil."); mapSize = [map mapSize]; location1.row = target.row - 1; location1.row = (location1.row < 0) ? 0 : location1.row; location1.row = (location1.row >= mapSize.height) ? mapSize.height - 1 : location1.row; location2.row = target.row + 1; location2.row = (location2.row < 0) ? 0 : location2.row; location2.row = (location2.row >= mapSize.height) ? mapSize.height - 1 : location2.row; location1.column = target.column - 1; location1.column = (location1.column < 0) ? 0 : location1.column; location1.column = (location1.column >= mapSize.width) ? mapSize.width - 1 : location1.column; location2.column = target.column + 1; location2.column = (location2.column < 0) ? 0 : location2.column; location2.column = (location2.column >= mapSize.width) ? mapSize.width - 1 : location2.column; return NSMakeRect ((float)location1.column * 16.0, (mapSize.height - 1 - location2.row) * 16.0, (float)(location2.column - location1.column + 1) * 16.0, (float)(location2.row - location1.row + 1) * 16.0); } //---------------------------------------------------------------------- // notVisibleFlag == NO: always center screen around the location. // notVisibleFlag == YES: only center the screen if the area around location is not entirely visible //---------------------------------------------------------------------- - (void) centerLocation:(EMMapLocation)target ifNotVisible:(BOOL)notVisibleFlag { NSRect cellRect; NSRect visibleRect; BOOL needToScroll; cellRect = [self getRectAround3x3Location:target]; visibleRect = [self visibleRect]; // Remind me what we use the notVisibleFlag for... needToScroll = (notVisibleFlag == NO) || (NSContainsRect (visibleRect, cellRect) == NO); //needToScroll = (NSContainsRect (visibleRect, cellRect) == NO); #if 0 // What is this doing? cellRect.origin.x = NSMinX (cellRect) + (NSWidth (cellRect) / 2) - (NSWidth (visibleRect) / 2); cellRect.origin.y = NSMinY (cellRect) + (NSHeight (cellRect) / 2) - (NSHeight (visibleRect) / 2); cellRect.size.width = NSWidth (visibleRect); cellRect.size.height = NSHeight (visibleRect); cellRect = NSIntersectionRect ([self bounds] , cellRect); #endif // Redundant... if (needToScroll == YES) { [self scrollRectToVisible:cellRect]; //[self displayRect:cellRect]; } } //---------------------------------------------------------------------- - (void) scrollLocationToVisible:(EMMapLocation)target { NSRect aRect = [self getRectAround3x3Location:target]; [self scrollRectToVisible:aRect]; //[self displayRect:aRect]; } //---------------------------------------------------------------------- - (void) displayLocation:(EMMapLocation)target { EMMapSize mapSize; NSRect aRect; NSAssert (map != nil, @"Map was nil."); mapSize = [map mapSize]; NSAssert (target.row >= 0 && target.column >= 0 && target.row < mapSize.height && target.column < mapSize.width, @"Cell out of range."); aRect = [self getRectForLocation:target]; [self scrollRectToVisible:aRect]; //[self displayRect:aRect]; [self setNeedsDisplayInRect:aRect]; [self displayIfNeeded]; //[self setNeedsDisplayInRect:aRect]; //[self displayRect:aRect]; } //---------------------------------------------------------------------- - (void) displayAround3x3Location:(EMMapLocation)target { EMMapSize mapSize; NSRect aRect; NSAssert (map != nil, @"Map was nil."); mapSize = [map mapSize]; NSAssert (target.row >= 0 && target.column >= 0 && target.row < mapSize.height && target.column < mapSize.width, @"Cell out of range."); aRect = [self getRectAround3x3Location:target]; [self scrollRectToVisible:aRect]; //[self displayRect:aRect]; [self setNeedsDisplayInRect:aRect]; [self displayIfNeeded]; //[self setNeedsDisplayInRect:aRect]; // This also marks it as not needing display, which breaks when doing // diagonal drawing, since only this rect is displayed while a larger rect // actually needs display. //[self displayRect:aRect]; } //---------------------------------------------------------------------- - (void) mouseDown:(NSEvent *)theEvent { NSPoint aPoint; EMMapLocation mapLocation; NSRect visibleRect; BOOL scrolled = NO; NSPoint mouseLocation; EMMapLocation lastLocation; BOOL periodicOn = NO; unsigned int originalModifierFlags = [theEvent modifierFlags]; unsigned int modifierFlags; lastLocation.row = -1; lastLocation.column = -1; if (delegate == nil) { [super mouseDown:theEvent]; return; } mouseLocation = [theEvent locationInWindow]; while ([theEvent type] != NSLeftMouseUp) { visibleRect = [self visibleRect]; switch ([theEvent type]) { case NSLeftMouseUp: break; case NSPeriodic: case NSLeftMouseDown: case NSLeftMouseDragged: if ([theEvent type] == NSPeriodic) { mouseLocation = [[self window] mouseLocationOutsideOfEventStream]; modifierFlags = originalModifierFlags; } else { mouseLocation = [theEvent locationInWindow]; modifierFlags = [theEvent modifierFlags]; } aPoint = [self convertPoint:mouseLocation fromView:nil]; mapLocation = [self getLocationForPoint:aPoint]; if (compressEvents == NO || mapLocation.row != lastLocation.row || mapLocation.column != lastLocation.column || [theEvent type] == NSPeriodic || (originalModifierFlags & NSControlKeyMask != 0)) { lastLocation = mapLocation; [delegate mouseDown:modifierFlags atLocation:mapLocation]; } //if (compressEvents == NO) { if (periodicOn == NO) { //NSLog (@"Starting periodic events. (1)"); [NSEvent startPeriodicEventsAfterDelay:0.1 withPeriod:0.05]; periodicOn = YES; } } if (NSPointInRect(mouseLocation, visibleRect) == NO) { scrolled = YES; if (periodicOn == NO) { //NSLog (@"Starting periodic events. (2)"); [NSEvent startPeriodicEventsAfterDelay:0.1 withPeriod:0.05]; periodicOn = YES; } } #if 0 else if (compressEvents == YES) { //NSLog (@"compress events is YES, stopping periodic events."); [NSEvent stopPeriodicEvents]; periodicOn = NO; } #endif break; default: break; } if (scrolled) scrolled = NO; theEvent = [[self window] nextEventMatchingMask:(NSLeftMouseUpMask | NSLeftMouseDraggedMask | NSPeriodicMask)]; } [NSEvent stopPeriodicEvents]; // Mouse up event has been consumed by this point. [self mouseUp:theEvent]; } //---------------------------------------------------------------------- - (void) mouseUp:(NSEvent *)theEvent { NSPoint point; EMMapLocation mapLocation; if ([theEvent type] == NSLeftMouseUp && delegate != nil) { point = [self convertPoint:[theEvent locationInWindow] fromView:nil]; mapLocation = [self getLocationForPoint:point]; [delegate mouseUp:[theEvent modifierFlags] atLocation:mapLocation]; } } //---------------------------------------------------------------------- - (void) rightMouseDown:(NSEvent *)theEvent { NSPoint point; EMMapLocation mapLocation; if ([theEvent type] == NSRightMouseDown && delegate != nil) { point = [self convertPoint:[theEvent locationInWindow] fromView:nil]; mapLocation = [self getLocationForPoint:point]; [delegate rightMouseDown:[theEvent modifierFlags] atLocation:mapLocation]; } } //---------------------------------------------------------------------- - (void) rightMouseUp:(NSEvent *)theEvent { NSPoint point; EMMapLocation mapLocation; if ([theEvent type] == NSRightMouseUp && delegate != nil) { point = [self convertPoint:[theEvent locationInWindow] fromView:nil]; mapLocation = [self getLocationForPoint:point]; [delegate rightMouseUp:[theEvent modifierFlags] atLocation:mapLocation]; } } //---------------------------------------------------------------------- - (void) keyDown:(NSEvent *)theEvent { if (delegate != nil) { [delegate keyDown:theEvent]; } else { [super keyDown:theEvent]; } } //---------------------------------------------------------------------- - (void) enableCompressedEvents:(BOOL)flag { compressEvents = flag; } //---------------------------------------------------------------------- - (void) vendorImagesUpdated:(BOOL)player1:(BOOL)player2:(BOOL)player3:(BOOL)other { if (other == YES) { [self setNeedsDisplay:YES]; } else { #if 0 if (player1 == YES) [self updateVisiblePlayer:p_player1]; if (player2 == YES) [self updateVisiblePlayer:p_player2]; if (player3 == YES) [self updateVisiblePlayer:p_player3]; #endif [self setNeedsDisplay:YES]; } } //---------------------------------------------------------------------- // This is really just to tweak for speed. //---------------------------------------------------------------------- - (void) updateVisiblePlayer:(Player)number { NSRect visibleRect = [self visibleRect]; EMMapSize mapSize; EMMapLocation current; EMMapLocation location1, location2; NSPoint aPoint; MapToken **mapPtrs; MapToken mapToken; if (map != nil) { mapPtrs = [map mapPtrs]; mapSize = [map mapSize]; aPoint.x = NSMinX (visibleRect); aPoint.y = NSMinY (visibleRect); location1 = [self getLocationForPoint:aPoint]; aPoint.x += NSWidth (visibleRect); aPoint.y += NSHeight (visibleRect); location2 = [self getLocationForPoint:aPoint]; for (current.row = location2.row; current.row <= location1.row; current.row++) { for (current.column = location1.column; current.column <= location2.column; current.column++) { mapToken = mapPtrs[current.row][current.column]; if (EMPlayerComponent (mapToken) == number) [self displayLocation:current]; } } } } //---------------------------------------------------------------------- - (void) refreshMap { [self setNeedsDisplay:YES]; } //---------------------------------------------------------------------- - (void) refreshLocation:(EMMapLocation)target { //NSLog (@"target: (%d,%d) is %@", target.row, target.column, EMFormatComponents ([map tokenAtLocation:target])); [self displayLocation:target]; [self displayIfNeeded];//? //[self updateCursorImage]; } //---------------------------------------------------------------------- - (void) refresh3x3Location:(EMMapLocation)target { //NSLog (@"target: (%d,%d)", target.row, target.column); [self displayAround3x3Location:target]; //[self updateCursorImage]; } //---------------------------------------------------------------------- - (void) showExplosions:(int)count atLocation:(EMMapLocation)target { NSRect aRect; NSPoint aPoint; int l; if ([[self window] isVisible] == YES) { [self lockFocus]; aRect = [self getRectForLocation:target]; aPoint.x = NSMinX (aRect); aPoint.y = NSMinY (aRect); for (l = 0; l < count; l++) { PSsetinstance (YES); [explosion compositeToPoint:aPoint operation:NSCompositeSourceOver]; PSsetinstance (NO); PSWait (); [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:(100000)/1000000.0]]; PSnewinstance (); PSWait (); [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:(100000)/1000000.0]]; } [self unlockFocus]; } } //====================================================================== // Cursor Handling //====================================================================== - (void) centerScreenAroundCursor { [self centerLocation:cursorLocation ifNotVisible:NO]; } //---------------------------------------------------------------------- - (void) centerCursor { } //---------------------------------------------------------------------- - (BOOL) cursorEnabled { return cursorEnabled; } //---------------------------------------------------------------------- - (void) setCursorEnabled:(BOOL)flag { if (cursorEnabled == YES && flag == NO) { if (cursorTimer != nil) { [cursorTimer invalidate]; SNRelease (cursorTimer); } if (cursorOn == YES) [self updateCursor:NO]; } else if (cursorEnabled == NO && flag == YES) { float blink_speed = (fastCursor == YES) ? 0.2 : 0.5; cursorTimer = [[NSTimer scheduledTimerWithTimeInterval:blink_speed target:self selector:@selector (cursorTimer:) userInfo:self repeats:YES] retain]; NSAssert (cursorTimer != nil, @"Cursor timer is nil."); } cursorEnabled = flag; } //---------------------------------------------------------------------- - (void) setFastCursor:(BOOL)flag { if (fastCursor != flag) { fastCursor = flag; if (cursorEnabled == YES) { [self setCursorEnabled:NO]; [self setCursorEnabled:YES]; } } } //---------------------------------------------------------------------- - (EMMapLocation) cursorLocation { return cursorLocation; } //---------------------------------------------------------------------- - (void) positionCursorAtLocation:(EMMapLocation)target { EMMapSize mapSize; EMMapLocation newLocation; NSAssert (map != nil, @"Map was nil."); mapSize = [map mapSize]; //NSLog (@"cursor location: %d,%d", target.row, target.column); newLocation.row = (target.row < 0) ? 0 : (target.row >= mapSize.height) ? mapSize.height - 1 : target.row; newLocation.column = (target.column < 0) ? 0 : (target.column >= mapSize.width) ? mapSize.width - 1 : target.column; //[self scrollLocationToVisible:target]; //[self centerScreenAroundCursor]; [self centerLocation:target ifNotVisible:YES]; if (newLocation.row != cursorLocation.row || newLocation.column != cursorLocation.column) { cursorLocation = newLocation; [self updateCursor:NO]; if (cursorOn == NO) [self updateCursor:NO]; } } //---------------------------------------------------------------------- - (void) setCursor:(Icon)icon player:(Player)aPlayer { NSAssert (icon <= i_hovercraft, @"Icon out of range."); cursorIcon = icon; cursorPlayer = aPlayer; [self updateCursorImage]; } //---------------------------------------------------------------------- - (void) updateCursorImage { if (cursorIcon != i_none) { Terrain terrain; Icon icon; EMMapTokenComponents ([map tokenAtLocation:cursorLocation], &terrain, NULL, &icon, NULL); if (terrain == t_city) { cursorImage = map_images[EMCreateMapToken (t_city, cursorPlayer, cursorIcon, YES)]; } else if (icon == cursorIcon) { cursorImage = map_images[EMCreateMapToken (terrain, p_neutral, i_none, YES)]; } else if (cursorIcon == i_army && icon == i_loaded_transport) { cursorImage = oddballs[cursorPlayer][0]; } else if (cursorIcon == i_fighter && icon == i_loaded_carrier) { cursorImage = oddballs[cursorPlayer][1]; } else { cursorImage = map_images[EMCreateMapToken (terrain, p_neutral, i_none, YES)]; } } // Otherwise, it is default/direction... } //---------------------------------------------------------------------- - (void) cursorTimer:(NSTimer *)aTimer { [self updateCursor:NO]; } //---------------------------------------------------------------------- - (void) updateCursor:(BOOL)scroll { NSPoint aPoint; if (cursorEnabled == NO) return; if (scroll == YES) { //NSLog (@"Scroll."); [self scrollLocationToVisible:cursorLocation]; } if (cursorOn == NO) { cursorOn = YES; aPoint = [self getPointForLocation:cursorLocation]; if ([self canDraw] == YES) { [self lockFocus]; // Caching while instance drawing! Argh!!!!!! //[default_cursor isValid]; PSsetinstance (YES); [cursorImage compositeToPoint:aPoint operation:NSCompositeSourceOver]; PSsetinstance (NO); [self unlockFocus]; } } else { cursorOn = NO; if ([self canDraw] == YES) { [self lockFocus]; PSnewinstance(); [self unlockFocus]; } } //[[self window] flushWindow]; } //---------------------------------------------------------------------- - (void) useDefaultCursor { cursorImage = default_cursor; cursorIcon = i_none; cursorPlayer = p_neutral; } //---------------------------------------------------------------------- - (void) useDirectionCursor { cursorImage = direction_cursor; cursorIcon = i_none; cursorPlayer = p_neutral; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.