convert line ends
[canaan.git] / prj / cam / src / dark / drkamap.cpp
blob3ea0479db7342204a2bfc9138805e5429183a77a
1 /*
2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
4 */
6 // $Header: r:/t2repos/thief2/src/dark/drkamap.cpp,v 1.24 2000/03/05 17:50:23 patmac Exp $
8 #include <drkamap.h>
9 #include <autoprop.h>
10 #include <propert_.h>
11 #include <dataops_.h>
12 #include <keydefs.h>
14 #include <sdesbase.h>
15 #include <sdestool.h>
17 #include <room.h>
18 #include <rooms.h>
19 #include <playrobj.h>
21 #include <config.h>
23 #include <command.h>
24 #include <ctype.h>
26 #include <dev2d.h>
27 #include <drkmiss.h>
28 #include <resapi.h>
29 #include <respaths.h>
30 #include <drkuires.h>
31 #include <drkdebrf.h>
32 #include <drkpanl.h>
33 #include <questapi.h>
34 #include <gcompose.h>
35 #include <filevar.h>
36 #include <palmgr.h>
37 #include <mprintf.h>
38 #include <fonrstyp.h>
41 // INCLUDE THESE LAST
43 #include <dbmem.h>
44 #include <initguid.h>
45 #include <amapguid.h>
47 ////////////////////////////////////////////////////////////
49 // Automap Annotation class
51 #define MAX_ANNOT_CHARS 128
53 typedef struct {
54 int x;
55 int y;
56 int page;
57 char text[ MAX_ANNOT_CHARS ];
58 } sAmapAnnotation;
60 typedef cDynArray<sAmapAnnotation> cAmapAnnotationArray;
62 static cAmapAnnotationArray gAnnotations;
64 ////////////////////////////////////////////////////////////
67 static IAutomapProperty* gAutoMapProp = NULL;
69 // We allow up to 8 map pages, and up to 32 decals per page
71 #define MAX_AMAP_PAGES 8
72 // MAX_AMAP_DECALS_PER_PAGE should be a multiple of 32
73 #define MAX_AMAP_DECALS_PER_PAGE 64
74 #define AMAP_LONGS_PER_PAGE (MAX_AMAP_DECALS_PER_PAGE >> 5)
75 #define MAX_MISSION_MAPS 32
77 struct sVisited
79 ulong visited[MAX_MISSION_MAPS][MAX_AMAP_PAGES][AMAP_LONGS_PER_PAGE];
82 // Here's my descriptor, which identifies my stuff to the tag file & editor
83 sFileVarDesc gVisitedDesc =
85 kCampaignVar, // Where do I get saved?
86 "VISITED", // Tag file tag
87 "Rooms Visited", // friendly name
88 FILEVAR_TYPE(sVisited), // Type (for editing)
89 { 1, 0}, // version
90 { 1, 0}, // last valid version
91 "dark", // optional: what game am I in NULL means all
94 // The actual global variable
95 cFileVar<sVisited,&gVisitedDesc> gVisited;
98 // -1 - display all decals as if they have been visited
99 // 0 - normal
100 // 1 - highlight all decals as if they are current player pos
101 static int gImEverywhere = 0;
102 #define AMAP_IM_EVERYWHERE 1
103 #define AMAP_IVE_BEEN_EVERYWHERE -1
105 static BOOL gbAutomapSpew = FALSE;
107 // map revealed source mission
110 // SDESC
113 static sFieldDesc mapsource_fields[] =
115 { "Source Mission", kFieldTypeInt, FieldLocation(sMapSourceData,sourcemiss) },
116 { "Compass Offset", kFieldTypeFloat, FieldLocation(sMapSourceData,compassdiff) },
119 static sStructDesc mapsource_sdesc = StructDescBuild(sMapSourceData,kStructFlagNone,mapsource_fields);
122 // VAR DESCRIPTOR
125 sFileVarDesc gAutomapInfoDesc =
127 kMissionVar, //where do I get saved?
128 "MAPISRC", //tag file tag
129 "Automap Info", //friendly name
130 FILEVAR_TYPE(sMapSourceData), //type (for editing)
131 { 1, 0}, //verion
132 { 1, 0}, // last valid version
133 "dark"
137 // Actually use the defaults when resetting
140 class cMapSourceData : public cFileVar<sMapSourceData,&gAutomapInfoDesc>
145 // the variable itself
148 static cMapSourceData gMapSourceData;
150 int GetMapSourceNum()
152 return gMapSourceData.sourcemiss;
155 float GetCompassOffset()
157 return gMapSourceData.compassdiff;
160 void TransferMapInfo()
162 int prevmiss = gMapSourceData.sourcemiss;
163 const sMissionData* missdata = GetMissionData();
164 Assert_((missdata->num < MAX_MISSION_MAPS) && (missdata->num >= 0));
165 Assert_((prevmiss < MAX_MISSION_MAPS));
166 int curmiss = missdata->num;
168 if ((curmiss==prevmiss) || (prevmiss<=0)) //don't copy in this case.
169 return;
171 //sigh, memcpy goes the opposite way of what I thought... copies to curmiss now.
172 memcpy(&gVisited.visited[curmiss],&gVisited.visited[prevmiss],sizeof(gVisited.visited[curmiss]));
175 void LoadMapSourceInfo(ITagFile* file)
177 gMapSourceData.DatabaseMsg(kDatabaseLoad|kDBMission,file);
180 void SaveMapSourceInfo(ITagFile* file)
182 gMapSourceData.DatabaseMsg(kDatabaseSave|kDBMission,file);
185 void MapSourceInfoInit()
187 AutoAppIPtr_(StructDescTools,pTools);
188 pTools->Register(&mapsource_sdesc);
191 void MapSourceInfoTerm()
195 ////////////////////////////////////////////////////////////
197 // Automap Property
200 typedef cGenericProperty<IAutomapProperty,&IID_IAutomapProperty, sAutomapProperty*> cAutomapPropertyBase;
202 class cAutomapProperty : public cAutomapPropertyBase
204 cClassDataOps< sAutomapProperty> mOps;
206 public:
207 cAutomapProperty(const sPropertyDesc* desc, IPropertyStore* store)
208 : cAutomapPropertyBase(desc,store)
210 SetOps(&mOps);
213 cAutomapProperty(const sPropertyDesc* desc, ePropertyImpl impl)
214 : cAutomapPropertyBase(desc,CreateGenericPropertyStore(impl))
216 SetOps(&mOps);
219 STANDARD_DESCRIBE_TYPE( sAutomapProperty);
224 // Description
225 static sPropertyDesc AutomapProp_desc =
227 PROP_AUTOMAP_NAME, // Name
228 0, // Flags
229 NULL,
230 2, // Version
231 0, // Last ok version (0 means none)
232 { "Room", "Automap" },
235 void SetupAutomapProperty(void);
237 void AutomapPropInit(void)
239 SetupAutomapProperty();
240 gAutoMapProp = new cAutomapProperty(&AutomapProp_desc, kPropertyImplDense);
243 void AutomapPropTerm(void)
245 SafeRelease(gAutoMapProp);
248 // Structure Descriptor
249 static sFieldDesc AutomapFields[] =
251 { "Page", kFieldTypeInt, FieldLocation(sAutomapProperty, page) },
252 { "Location", kFieldTypeInt, FieldLocation(sAutomapProperty, location) },
255 static sStructDesc AutomapDesc = StructDescBuild(sAutomapProperty, kStructFlagNone, AutomapFields);
257 static void SetupAutomapProperty(void)
259 AutoAppIPtr_(StructDescTools,pTools);
260 pTools->Register(&AutomapDesc);
264 typedef cDynClassArray<cStr> cStringArray;
266 ////////////////////////////////////////
268 static sAutomapProperty* map_player_pos()
271 cRoom* player_room = g_pRooms->GetObjRoom(PlayerObject());
272 if (player_room)
274 ObjID room = player_room->GetObjID();
276 sAutomapProperty* prop;
277 if (room != OBJ_NULL && gAutoMapProp->Get(room,&prop))
279 AutoAppIPtr(QuestData);
280 if (prop->page >= pQuestData->Get(MIN_PAGE_QVAR)
281 && prop->page <= pQuestData->Get(MAX_PAGE_QVAR))
282 return prop;
284 static sAutomapProperty oob_pos;
286 oob_pos.page = pQuestData->Get(OOB_PAGE_QVAR);
287 oob_pos.location = pQuestData->Get(OOB_LOC_QVAR);
288 return &oob_pos;
292 return NULL;
297 // tell if the player has visited the specified page & location
299 BOOL
300 DarkAutomapGetLocationVisited( int page, int location )
302 const sMissionData* missdata = GetMissionData();
303 Assert_((missdata->num < MAX_MISSION_MAPS) && (missdata->num >= 0));
304 // page should always be valid, location may be -1
305 assert( (page >= 0) && (page < MAX_AMAP_PAGES) );
306 if ( (location >= 0) && (location < MAX_AMAP_DECALS_PER_PAGE) ) {
307 // tell if the player has visited the room
308 return (gVisited.visited[missdata->num][page][location>>5] & (1 << (location & 31))) != 0;
309 } else {
310 return FALSE;
316 // set the visited flag for the specified page & location
318 void
319 DarkAutomapSetLocationVisited( int page, int location )
321 AutoAppIPtr(QuestData);
322 int locsVisited, p, l;
323 const sMissionData* missdata = GetMissionData();
324 Assert_((missdata->num < MAX_MISSION_MAPS) && (missdata->num >= 0));
326 // page should always be valid, location may be -1
327 assert( (page >= 0) && (page < MAX_AMAP_PAGES) );
328 if ( (location >= 0) && (location < MAX_AMAP_DECALS_PER_PAGE) ) {
329 // tell the automap the player has visited the room
330 gVisited.visited[missdata->num][page][location>>5] |= (1 << (location & 31));
333 // set the quest variable that tracks total number of locations visited
334 locsVisited = 0;
335 for ( p = 0; p < MAX_AMAP_PAGES; p++ ) {
336 for ( l = 0; l < MAX_AMAP_DECALS_PER_PAGE; l++ ) {
337 if ( gVisited.visited[missdata->num][p][l>>5] & (1 << (l & 31)) ) {
338 locsVisited++;
342 if ( pQuestData->Exists( LOCS_VISITED_QVAR ) ) {
343 pQuestData->Set( LOCS_VISITED_QVAR, locsVisited );
344 } else {
345 pQuestData->Create( LOCS_VISITED_QVAR, locsVisited, kQuestDataMission );
347 #ifndef SHIP
348 if ( gbAutomapSpew ) {
349 mprintf( "Automap visited page %d, location %d. Total visited: %d\n",
350 page, location, locsVisited );
352 #endif
355 //------------------------------------------------------------
356 // flag that an area has been visited
358 static BOOL
359 DarkAutomapRoomCB( ObjID roomEntered )
362 // get the decal index & map page properties of the room
363 sAutomapProperty* prop = map_player_pos();
364 if ( prop ) {
365 DarkAutomapSetLocationVisited( prop->page, prop->location );
367 return TRUE;
371 void
372 DarkAutomapFirstFrameInit( void )
374 cRoom* player_room = g_pRooms->GetObjRoom(PlayerObject());
375 if (player_room) {
376 DarkAutomapRoomCB( player_room->GetObjID() );
381 //------------------------------------------------------------
382 // Automap button panel
385 enum mapMode { kMapModeNormal, kMapModeAddText, kMapModeEraseText };
387 class cAutomap: public cDarkPanel
389 static sDarkPanelDesc gDesc;
391 public:
392 cAutomap(): cDarkPanel(&gDesc),mpQuestData(AppGetObj(IQuestData)) {};
393 ~cAutomap() { SafeRelease(mpQuestData); };
395 enum eRects
397 kGoals,
398 kDone,
399 kStringButts,
401 kNext = kStringButts,
402 kPrev,
403 kNumButts,
404 kMap = kNumButts,
406 kNumRects
409 bool MapRegionEventHandler( uiEvent *pEvent, Region *pReg, void *pState );
412 protected:
413 void RedrawDisplay();
414 void OnButtonList(ushort action, int button);
415 void OnLoopMsg(eLoopMessage, tLoopMessageData );
416 void InitUI();
417 void TermUI();
419 void RedrawButtons();
421 static mapMode mMapMode;
422 int mPageNum;
423 enum { kNumArrowImages = 4 };
424 IImageSource* mArrows[kNumArrowImages]; // 4 arrow images
426 IQuestData* mpQuestData;
428 Region mMapRegion;
429 int mMapRegionEventHandlerID;
430 sAmapAnnotation mCurAnnotation;
433 ////////////////////////////////////////
435 mapMode cAutomap::mMapMode;
437 static bool
438 regionCB( uiEvent *pEvent,
439 Region *pReg,
440 void *pState )
442 cAutomap *pAmap = (cAutomap *) pState;
444 return pAmap->MapRegionEventHandler( pEvent, pReg, pState );
448 void cAutomap::InitUI()
450 cDarkPanel::InitUI();
451 int i;
452 Rect& mapr = mRects[(int)kMap];
454 for (i = 0; i < kNumArrowImages; i++)
456 char buf[16];
457 sprintf(buf,"arrow%03d",i);
458 mArrows[i] = FetchUIImage(buf);
461 int n = 0;
462 for (i = kNext; i < kNumButts; i++)
464 DrawElement& elem = mElems[i];
465 memset(&elem,0,sizeof(elem));
466 elem.draw_type = DRAWTYPE_BITMAP;
467 elem.draw_data = mArrows[n++]->Lock();
468 elem.draw_data2 = mArrows[n++]->Lock();
469 elem.draw_flags = INTERNAL(DRAWFLAG_INT_TRANSP);
472 // create region & handler for map window - will be used
473 // to do annotations
474 region_create( LGadBoxRegion(LGadCurrentRoot()),
475 &mMapRegion,
476 &(mRects[ (int)kMap]),
477 0, 0, 0, NULL, NULL, NULL, NULL );
478 uiInstallRegionHandler( &mMapRegion, ALL_EVENTS, regionCB, this,
479 &mMapRegionEventHandlerID );
481 // initialize annotation stuff
482 mMapMode = kMapModeNormal;
483 mCurAnnotation.text[0] = 0;
485 // TBD - init annotations from ???
488 void cAutomap::TermUI()
490 int i;
492 for (i = 0; i < kNumArrowImages; i++)
494 mArrows[i]->Unlock();
495 SafeRelease(mArrows[i]);
497 uiRemoveRegionHandler( &mMapRegion, mMapRegionEventHandlerID );
499 if ( mMapMode == kMapModeAddText ) {
500 // add annotation-in-progress if not 0 length
501 if ( strlen( mCurAnnotation.text ) ) {
502 gAnnotations.Append( mCurAnnotation );
505 mMapMode = kMapModeNormal;
506 // TBD: save annotations to ???
507 for ( i = 0; i < gAnnotations.Size(); i++ ) {
509 // TBD: free any annotation stuff
511 cDarkPanel::TermUI();
514 ////////////////////////////////////////
516 // DrawAnnotation & OverlapsAnnotation support multi-line auto-centered annotations
519 #define EXTRA_VERTICAL_SPACING 0
521 static void
522 DrawAnnotation( sAmapAnnotation *pAnnotation,
523 BOOL bWithCursor )
525 int x, y, endX, endY;
526 short w, h;
527 char txtCopy[ MAX_ANNOT_CHARS ];
528 char *pToken;
529 BOOL bLastLineEmpty;
531 strcpy( txtCopy, pAnnotation->text );
532 bLastLineEmpty = (txtCopy[strlen(txtCopy) - 1] == '\n');
533 pToken = strtok( txtCopy, "\n" );
534 if ( pToken == NULL ) {
535 // if we want cursor for empty string, we at least need to know height of font
536 if ( bWithCursor ) {
537 gr_string_size( "A", &w, &h );
538 endX = pAnnotation->x;
539 endY = pAnnotation->y;
541 } else {
542 y = pAnnotation->y;
543 while ( pToken ) {
544 gr_string_size( pToken, &w, &h );
545 x = pAnnotation->x - (w/2);
546 gr_string( pToken, x, y );
547 endX = x + w;
548 endY = y;
549 y += (h + EXTRA_VERTICAL_SPACING);
550 pToken = strtok( NULL, "\n" );
553 if ( bLastLineEmpty ) {
554 // this is here so that after the user hits return, the cursor
555 // will appear on the new line, not at the end of the last line
556 // this is due to strtok refusing to return an empty token
557 endX = pAnnotation->x;
558 endY += h;
560 if ( bWithCursor ) {
561 gr_int_line( endX, endY, endX, endY + h );
566 static BOOL
567 OverlapsAnnotation( sAmapAnnotation *pAnnotation,
568 int x,
569 int y )
571 short w, h;
572 char txtCopy[ MAX_ANNOT_CHARS ];
573 char *pToken;
574 int xLeft, yTop, xRight, yBottom;
576 strcpy( txtCopy, pAnnotation->text );
577 pToken = strtok( txtCopy, "\n" );
578 yTop = pAnnotation->y;
579 while ( pToken ) {
580 gr_string_size( pToken, &w, &h );
581 xLeft = pAnnotation->x - (w / 2);
582 xRight = xLeft + w;
583 yBottom = yTop + h;
584 if ( (y >= yTop) && (y < yBottom) &&
585 (x >= xLeft) && (x < xRight) ) {
586 // x,y is within string extents
587 return TRUE;
589 yTop += (h + EXTRA_VERTICAL_SPACING);
590 pToken = strtok( NULL, "\n" );
593 return FALSE;
596 ////////////////////////////////////////
598 // enhance contrast
599 // make values less than 128 smaller,
600 // values greater than 128 bigger
602 static float brightenRGB[3] = { 0.7, 0.7, 0.3 };
603 static float darkenRGB[3] = { 0.1, 0.1, 0.1 };
604 static float dimRGB[3] = { 0.2, 0.2, 0.7 };
605 static int splitPoint = 100;
607 static void
608 mungeColor( uchar *pDst,
609 uchar *pSrc,
610 BOOL bYouAreHere )
612 int r, g, b;
614 r = *pSrc++;
615 g = *pSrc++;
616 b = *pSrc++;
618 if ( bYouAreHere ) {
620 // enhance contrast in decal where player is
621 if ( ((r + g + b) / 3) >= splitPoint ) {
622 // make color brighter
623 r = 255 - ((255 - r) * (1.0 - brightenRGB[0]));
624 g = 255 - ((255 - g) * (1.0 - brightenRGB[1]));
625 b = 255 - ((255 - b) * (1.0 - brightenRGB[2]));
626 } else {
627 // make color darker
628 r *= (1.0 - darkenRGB[0]);
629 g *= (1.0 - darkenRGB[1]);
630 b *= (1.0 - darkenRGB[2]);
633 } else {
635 // this decal is where player has visited
636 // reduce the contrast of the red and green components, but
637 // increase the contrast of the blue component. This color
638 // shifts the palette toward blue, to make the visited areas
639 // stand out, without losing so much contrast text is illegible
640 r = (r >= splitPoint) ? splitPoint + ( (r - splitPoint) * (1.0 - dimRGB[0]) )
641 : splitPoint - ( (splitPoint - r) * (1.0 - dimRGB[0]) );
642 g = (g >= splitPoint) ? splitPoint + ( (g - splitPoint) * (1.0 - dimRGB[1]) )
643 : splitPoint - ( (splitPoint - g) * (1.0 - dimRGB[1]) );
644 b = (b >= splitPoint) ? 255 - ((255 - b) * (1.0 - dimRGB[2]))
645 : b * (1.0 - dimRGB[2]);
648 *pDst++ = r;
649 *pDst++ = g;
650 *pDst++ = b;
654 static void
655 DrawDecal( IImageSource *pDecal,
656 Rect *pRect,
657 BOOL bYouAreHere )
659 int i, oldPalSlot;
660 uchar *pNewPal, *pSrc, *pDst;
662 grs_bitmap *pBM = (grs_bitmap *) (pDecal->Lock());
664 // muck with pallette then display
665 oldPalSlot = pBM->align;
666 assert( pBM->w == pBM->row );
668 pNewPal = (uchar *) Malloc( 256 * 3 );
670 pSrc = palmgr_get_pal( oldPalSlot );
671 pDst = pNewPal;
672 for ( i = 0; i < 256; i++ ) {
673 mungeColor( pDst, pSrc, bYouAreHere );
674 pSrc += 3;
675 pDst += 3;
677 pBM->align = palmgr_alloc_pal( pNewPal );
678 gr_bitmap( pBM, pRect->ul.x, pRect->ul.y );
679 palmgr_release_slot( pBM->align );
680 pBM->align = oldPalSlot;
682 Free( pNewPal );
684 pDecal->Unlock();
685 SafeRelease( pDecal );
689 ////////////////////////////////////////
691 void cAutomap::RedrawDisplay()
693 RedrawButtons();
694 const sMissionData* missdata = GetMissionData();
695 char buf[64];
696 sprintf(buf,"%s/page%03d",missdata->path,mPageNum);
697 IImageSource* mapsrc = FetchUIImage(buf);
698 cRectArray tmpRects;
699 int i;
700 BOOL bIAmEverywhere = FALSE;
701 IRes* pFontRes = NULL;
702 AutoAppIPtr(ResMan);
704 //to prevent (no)map crash -- AMSD
705 if (mapsrc == NULL)
706 return;
707 grs_bitmap* map = (grs_bitmap*)mapsrc->Lock();
708 int numDecals, decal;
709 ulong *pVisitedDecals;
711 Rect& mapr = mRects[(int)kMap];
713 GUIcompose c;
714 GUIsetup(&c,&mapr,ComposeFlagRead,GUI_CANV_ANY);
716 gr_bitmap(map,0,0);
718 Assert_((missdata->num < MAX_MISSION_MAPS) && (missdata->num >= 0));
719 sAutomapProperty* prop = map_player_pos();
721 // hack - for some reason, the location the player has just entered is sometimes
722 // not marked as visited - here force mark that location as visited
723 if ( prop ) {
724 DarkAutomapSetLocationVisited( prop->page, prop->location );
727 pVisitedDecals = &(gVisited.visited[missdata->num][mPageNum][0]);
728 sprintf(buf,"%s/p%03dra",missdata->path,mPageNum);
729 FetchUIRects(buf, tmpRects);
730 numDecals = tmpRects.Size();
732 //////////////////////////////////////////////////////////////////////////
734 // draw all the decals for areas the player has visited, EXCEPT for the
735 // decal for the current player position
737 //////////////////////////////////////////////////////////////////////////
738 int youAreHere = -1;
739 IImageSource* img;
740 for ( decal = 0; decal < numDecals; decal++ ) {
741 BOOL bYouAreHere = prop && (prop->page == mPageNum) && (prop->location == decal);
742 // is decal at current player position
743 if ( bYouAreHere && (gImEverywhere == 0) ) {
745 // remember that we are really somewhere
746 youAreHere = decal;
748 } else {
750 if ( (pVisitedDecals[decal>>5] & (1 << (decal & 31))) || (gImEverywhere != 0) ) {
751 // Get the image
752 sprintf(buf,"%s/p%03dr%03d",missdata->path,mPageNum,decal);
754 img = FetchUIImage(buf);
755 if ( img ) {
756 DrawDecal( img, &(tmpRects[decal]), (gImEverywhere == AMAP_IM_EVERYWHERE) );
763 //////////////////////////////////////////////////////////////////////////
765 // lastly, draw the decal where player is, so that it is always on top
767 //////////////////////////////////////////////////////////////////////////
768 if ( youAreHere != -1 ) {
769 // Get the image
770 sprintf( buf, "%s/p%03dr%03d", missdata->path, mPageNum, youAreHere );
772 img = FetchUIImage(buf);
773 if ( img ) {
774 DrawDecal( img, &(tmpRects[youAreHere]), TRUE );
778 GUIdone(&c);
780 mapsrc->Unlock();
781 SafeRelease(mapsrc);
783 //////////////////////////////////////////////////////////////////////////
785 // draw all annotations
787 //////////////////////////////////////////////////////////////////////////
788 grs_clip oldClip;
790 // save old clip window, clip to map window
791 gr_save_cliprect( &oldClip.i );
792 gr_set_cliprect( mapr.ul.x, mapr.ul.y, mapr.lr.x, mapr.lr.y );
794 // set annotation font
795 strcpy( buf, "f_scrp12" );
796 config_get_raw( "automap_note_font", buf, sizeof(buf)-1 );
797 pFontRes = pResMan->Bind( buf, RESTYPE_FONT, NULL, mResPath );
798 if ( pFontRes ) {
799 grs_font* pFont = (grs_font*) pFontRes->Lock();
800 gr_set_font( pFont );
803 gr_set_fcolor( 0 );
804 for ( i = 0; i < gAnnotations.Size(); i++ ) {
805 if ( gAnnotations[i].page == mPageNum ) {
806 DrawAnnotation( &(gAnnotations[i]), FALSE );
810 // draw the current annotation
811 if ( mMapMode == kMapModeAddText ) {
812 DrawAnnotation( &mCurAnnotation, TRUE );
815 if ( pFontRes ) {
816 pFontRes->Unlock();
819 // restore old clip window
820 gr_restore_cliprect( &oldClip.i );
824 ////////////////////////////////////////
826 void cAutomap::RedrawButtons()
828 if (mPageNum == mpQuestData->Get(MIN_PAGE_QVAR))
830 mElems[(int)kPrev].draw_type = DRAWTYPE_NONE;
831 GUIErase(&mRects[(int)kPrev]);
833 else
835 mElems[(int)kPrev].draw_type = DRAWTYPE_BITMAP;
836 region_expose(LGadBoxRegion(LGadCurrentRoot()),&mRects[(int)kPrev]);
839 if (mPageNum == mpQuestData->Get(MAX_PAGE_QVAR))
841 mElems[(int)kNext].draw_type = DRAWTYPE_NONE;
842 GUIErase(&mRects[(int)kNext]);
844 else
846 mElems[(int)kNext].draw_type = DRAWTYPE_BITMAP;
847 region_expose(LGadBoxRegion(LGadCurrentRoot()),&mRects[(int)kNext]);
852 ////////////////////////////////////////
854 void cAutomap::OnButtonList(ushort action , int button)
856 if (action & (BUTTONGADG_LCLICK|BUTTONGADG_RCLICK))
858 switch (button)
860 case kDone:
862 IPanelMode* pMode = GetPanelMode();
863 pMode->Exit();
864 SafeRelease(pMode);
865 break;
867 case kGoals:
868 SwitchToObjectivesMode(FALSE);
869 break;
871 case kNext:
873 int max = mpQuestData->Get(MAX_PAGE_QVAR);
874 if (mPageNum < max)
876 // if an annotation is in progress, complete it
877 if ( strlen( mCurAnnotation.text ) ) {
878 gAnnotations.Append( mCurAnnotation );
880 mCurAnnotation.text[0] = 0;
881 mMapMode = kMapModeNormal;
882 mPageNum++;
883 RedrawDisplay();
886 break;
888 case kPrev:
890 int min = mpQuestData->Get(MIN_PAGE_QVAR);
891 if (mPageNum > min)
893 // if an annotation is in progress, complete it
894 if ( strlen( mCurAnnotation.text ) ) {
895 gAnnotations.Append( mCurAnnotation );
897 mCurAnnotation.text[0] = 0;
898 mMapMode = kMapModeNormal;
899 mPageNum--;
900 RedrawDisplay();
903 break;
908 ////////////////////////////////////////
910 void cAutomap::OnLoopMsg(eLoopMessage msg, tLoopMessageData data)
912 switch (msg)
914 case kMsgEnterMode:
916 sAutomapProperty* pos = map_player_pos();
918 if (pos)
919 mPageNum = pos->page;
920 else // default to first page
921 mPageNum = mpQuestData->Get(MIN_PAGE_QVAR);
924 break;
926 cDarkPanel::OnLoopMsg(msg,data);
930 bool
931 cAutomap::MapRegionEventHandler( uiEvent *pEvent,
932 Region * /*pReg*/,
933 void * /*pState*/ )
935 uiCookedKeyEvent *pKeyEvent;
936 uiMouseEvent *pMouseEvent;
937 char c;
938 bool retVal = FALSE;
939 bool doRedraw = FALSE;
940 int x, y, len, i;
942 switch( pEvent->type ) {
943 case UI_EVENT_KBD_COOKED:
944 if ( (pEvent->subtype & KB_FLAG_DOWN) && (mMapMode == kMapModeAddText) ) {
945 pKeyEvent = (uiCookedKeyEvent *) pEvent;
946 c = kb2ascii( pKeyEvent->code );
948 switch ( c ) {
949 #if 0
950 case KEY_ENTER:
951 // TBD - user hit enter, annotation is complete
952 mMapMode = kMapModeNormal;
953 if ( strlen( mCurAnnotation.text ) ) {
954 gAnnotations.Append( mCurAnnotation );
955 mCurAnnotation.text[0] = 0;
957 break;
958 #endif
959 case KEY_ESC:
960 // TBD - user hit escape, annotation is complete
961 mMapMode = kMapModeNormal;
962 if ( strlen( mCurAnnotation.text ) ) {
963 gAnnotations.Append( mCurAnnotation );
964 mCurAnnotation.text[0] = 0;
966 break;
967 case KEY_BS:
968 case KEY_DEL:
969 // delete a char
970 len = strlen( mCurAnnotation.text );
971 if ( len ) {
972 mCurAnnotation.text[ len - 1 ] = 0;
974 break;
975 case KEY_ENTER:
976 c = '\n';
977 default:
979 // if key is in range, add it to end of string and redisplay
981 len = strlen( mCurAnnotation.text );
982 if ( len < (MAX_ANNOT_CHARS - 1) ) {
983 mCurAnnotation.text[ len ] = c;
984 mCurAnnotation.text[ len + 1 ] = 0;
986 break;
988 doRedraw = TRUE;
989 } //end if KB_DOWN and ADD TEXT
990 else
991 if ( (pEvent->subtype & KB_FLAG_DOWN) && (mMapMode == kMapModeNormal) ){
992 pKeyEvent = (uiCookedKeyEvent *) pEvent;
993 c = kb2ascii( pKeyEvent->code );
994 switch ( c ) {
995 case KEY_ESC:
996 IPanelMode* pMode = GetPanelMode();
997 pMode->Exit();
998 SafeRelease(pMode);
999 break;
1000 } //switch c
1001 } //if KB_DOWN and NORMAL
1002 retVal = TRUE;
1003 break;
1005 case UI_EVENT_MOUSE:
1007 // mouse click -
1009 pMouseEvent = (uiMouseEvent *) pEvent;
1010 x = pMouseEvent->pos.x;
1011 y = pMouseEvent->pos.y;
1013 if ( pMouseEvent->action & MOUSE_LUP ) {
1014 // TBD - change into text entry mode
1015 switch( mMapMode ) {
1017 case kMapModeNormal:
1018 // change into text entry mode
1019 mMapMode = kMapModeAddText;
1020 mCurAnnotation.text[0] = 0;
1021 mCurAnnotation.x = x;
1022 mCurAnnotation.y = y;
1023 mCurAnnotation.page = mPageNum;
1024 break;
1026 case kMapModeAddText:
1027 // end current annotation, start new one
1028 if ( strlen( mCurAnnotation.text ) ) {
1029 gAnnotations.Append( mCurAnnotation );
1030 mCurAnnotation.text[0] = 0;
1032 mCurAnnotation.x = x;
1033 mCurAnnotation.y = y;
1034 mCurAnnotation.page = mPageNum;
1035 break;
1037 case kMapModeEraseText:
1038 // find annotation which was clicked on & delete it
1039 grs_font* pFont = (grs_font*) mpFontRes->Lock();
1040 for ( i = 0; i < gAnnotations.Size(); i++ ) {
1041 // get string extents...
1042 if ( (gAnnotations[i].page == mPageNum) &&
1043 OverlapsAnnotation( &(gAnnotations[i]), x, y ) ) {
1044 gAnnotations.DeleteItem( i );
1045 break;
1048 mpFontRes->Unlock();
1049 mMapMode = kMapModeNormal;
1050 break;
1052 doRedraw = TRUE;
1055 if ( pMouseEvent->action & MOUSE_RUP ) {
1056 // if there was an annotation in progress, finish it
1057 if ( strlen( mCurAnnotation.text ) ) {
1058 gAnnotations.Append( mCurAnnotation );
1059 mCurAnnotation.text[0] = 0;
1061 // find annotation which was clicked on & delete it
1062 grs_font* pFont = (grs_font*) mpFontRes->Lock();
1063 for ( i = 0; i < gAnnotations.Size(); i++ ) {
1064 if ( (gAnnotations[i].page == mPageNum) &&
1065 OverlapsAnnotation( &(gAnnotations[i]), x, y ) ) {
1066 gAnnotations.DeleteItem( i );
1067 break;
1070 mMapMode = kMapModeNormal;
1071 mpFontRes->Unlock();
1072 doRedraw = TRUE;
1074 retVal = TRUE;
1075 break;
1078 if ( doRedraw ) {
1079 uiHideMouse( LGadCurrentRoot()->box.r.r );
1080 RedrawDisplay();
1081 uiShowMouse( LGadCurrentRoot()->box.r.r );
1083 return retVal;
1086 ////////////////////////////////////////
1088 static const char* butt_names[] =
1090 "goals",
1091 "done",
1094 sDarkPanelDesc cAutomap::gDesc =
1096 "map",
1097 cAutomap::kNumButts,
1098 cAutomap::kNumRects,
1099 cAutomap::kStringButts,
1100 butt_names,
1103 ////////////////////////////////////////
1105 static cAutomap* gpAutomap = NULL;
1107 void SwitchToDarkAutomapMode(BOOL push)
1109 if (gpAutomap)
1111 IPanelMode* panel = gpAutomap->GetPanelMode();
1113 panel->Switch((push) ? kLoopModePush : kLoopModeSwitch);
1114 SafeRelease(panel);
1119 static void create_panel_mode()
1121 cAutomap* automap = new cAutomap;
1123 gpAutomap = automap;
1129 void init_commands(void);
1131 void DarkAutomapInit(void)
1133 AutomapPropInit();
1134 create_panel_mode();
1135 init_commands();
1136 cRooms::AddAutomapCallback( DarkAutomapRoomCB );
1140 void DarkAutomapTerm()
1142 AutomapPropTerm();
1143 delete gpAutomap;
1144 cRooms::RemoveAutomapCallback( DarkAutomapRoomCB );
1148 // load/save annotations stuff
1150 static TagFileTag MyTag = { "AMAPANNO" };
1151 static TagVersion MyVersion = { 0, 0 };
1153 static BOOL setup_tagfile(ITagFile* file)
1155 if (file->OpenMode() == kTagOpenRead && file->BlockSize(&MyTag) == 0)
1156 return FALSE;
1158 TagVersion v = MyVersion;
1159 HRESULT result = file->OpenBlock(&MyTag,&v);
1160 if (FAILED(result))
1161 return FALSE;
1163 if (!VersionNumsEqual(&v,&MyVersion))
1165 file->CloseBlock();
1166 return FALSE;
1169 return TRUE;
1173 void DarkAutomapDatabaseNotify( ulong msg,
1174 ITagFile *pFile )
1176 int numAnnotations, i;
1178 switch(DB_MSG(msg))
1180 case kDatabaseReset:
1181 gAnnotations.SetSize( 0 );
1182 break;
1184 case kDatabaseLoad:
1185 if ( setup_tagfile(pFile) ) {
1186 numAnnotations = 0;
1187 pFile->Read( (char *) (&numAnnotations), sizeof(numAnnotations) );
1188 gAnnotations.SetSize( numAnnotations );
1189 for ( i = 0; i < numAnnotations; i++ ) {
1190 pFile->Read( (char *) (&gAnnotations[i]), sizeof(sAmapAnnotation) );
1192 pFile->CloseBlock();
1194 break;
1196 case kDatabaseSave:
1197 if ( setup_tagfile(pFile) ) {
1198 numAnnotations = gAnnotations.Size();
1199 pFile->Write( (char *) (&numAnnotations), sizeof(numAnnotations) );
1200 for ( i = 0; i < numAnnotations; i++ ) {
1201 pFile->Write( (char *) (&gAnnotations[i]), sizeof(sAmapAnnotation) );
1203 pFile->CloseBlock();
1205 break;
1212 // Commands
1216 static void do_automap()
1218 SwitchToDarkAutomapMode(TRUE);
1221 static void
1222 automap_set_visited( int visitedDecal )
1224 sAutomapProperty *pPlayerPos;
1225 pPlayerPos = map_player_pos();
1227 if ( pPlayerPos && (pPlayerPos->page >= 0) && (pPlayerPos->page < MAX_AMAP_PAGES) ) {
1228 if ( visitedDecal == -1 ) {
1229 for ( visitedDecal = 0; visitedDecal < MAX_AMAP_DECALS_PER_PAGE; visitedDecal++ ) {
1230 DarkAutomapSetLocationVisited( pPlayerPos->page, visitedDecal );
1232 } else {
1233 DarkAutomapSetLocationVisited( pPlayerPos->page, visitedDecal );
1239 static void
1240 automap_im_everywhere( int onOff )
1242 gImEverywhere = onOff;
1246 static Command commands [] =
1248 { "automap", FUNC_STRING, do_automap, "Display the automap", HK_ALL },
1249 { "amap_visited", FUNC_INT, automap_set_visited, "mark an automap location on current page visited", HK_ALL },
1250 { "amap_im_everywhere", FUNC_INT, automap_im_everywhere, "make all decals display as current location", HK_ALL },
1251 { "amap_spew", TOGGLE_BOOL, &gbAutomapSpew },
1254 static void init_commands()
1256 COMMANDS(commands,HK_ALL);