//============================================================================ //---------------------------------------------------------------------------- // House.c //---------------------------------------------------------------------------- //============================================================================ #include #include #include #include #include "DialogUtils.h" #include "Externs.h" #include "House.h" #include "RectUtils.h" #define kGoToDialogID 1043 void UpdateGoToDialog (DialogPtr); pascal Boolean GoToFilter (DialogPtr, EventRecord *, short *); houseHand thisHouse; linksPtr linksList; Str32 thisHouseName; short srcLocations[kMaxRoomObs]; short destLocations[kMaxRoomObs]; short wasFloor, wasSuite; retroLink retroLinkList[kMaxRoomObs]; Boolean houseUnlocked; extern gameType smallGame; extern short numberRooms, mapLeftRoom, mapTopRoom, numStarsRemaining; extern Boolean houseOpen, noRoomAtAll; extern Boolean twoPlayerGame, wardBitSet, phoneBitSet; //============================================================== Functions //-------------------------------------------------------------- CreateNewHouse // Called to create a new house file. #ifndef COMPILEDEMO Boolean CreateNewHouse (void) { AEKeyword theKeyword; DescType actualType; Size actualSize; NavReplyRecord theReply; NavDialogOptions dialogOptions; FSSpec tempSpec; FSSpec theSpec; OSErr theErr; theErr = NavGetDefaultDialogOptions(&dialogOptions); theErr = NavPutFile(nil, &theReply, &dialogOptions, nil, 'gliH', 'ozm5', nil); if (theErr == userCanceledErr) return false; if (!theReply.validRecord) return (false); theErr = AEGetNthPtr(&(theReply.selection), 1, typeFSS, &theKeyword, &actualType, &theSpec, sizeof(FSSpec), &actualSize); if (theReply.replacing) { theErr = FSMakeFSSpec(theSpec.vRefNum, theSpec.parID, theSpec.name, &tempSpec); if (!CheckFileError(theErr, theSpec.name)) return (false); theErr = FSpDelete(&tempSpec); if (!CheckFileError(theErr, theSpec.name)) return (false); } if (houseOpen) { if (!CloseHouse()) return (false); } theErr = FSpCreate(&theSpec, 'ozm5', 'gliH', theReply.keyScript); if (!CheckFileError(theErr, "\pNew House")) return (false); HCreateResFile(theSpec.vRefNum, theSpec.parID, theSpec.name); if (ResError() != noErr) YellowAlert(kYellowFailedResCreate, ResError()); PasStringCopy(theSpec.name, thisHouseName); AddExtraHouse(&theSpec); BuildHouseList(); InitCursor(); if (!OpenHouse()) return (false); return (true); } #endif //-------------------------------------------------------------- InitializeEmptyHouse // Initializes all the structures for an empty (new) house. #ifndef COMPILEDEMO Boolean InitializeEmptyHouse (void) { houseType *thisHousePtr; Str255 tempStr; if (thisHouse != nil) DisposeHandle((Handle)thisHouse); thisHouse = (houseHand)NewHandle(sizeof(houseType)); if (thisHouse == nil) { YellowAlert(kYellowUnaccounted, 1); return (false); } HLock((Handle)thisHouse); thisHousePtr = *thisHouse; thisHousePtr->version = kHouseVersion; thisHousePtr->firstRoom = -1; thisHousePtr->timeStamp = 0L; thisHousePtr->flags = 0L; thisHousePtr->initial.h = 32; thisHousePtr->initial.v = 32; ZeroHighScores(); GetLocalizedString(11, tempStr); PasStringCopy(tempStr, thisHousePtr->banner); GetLocalizedString(12, tempStr); PasStringCopy(tempStr, thisHousePtr->trailer); thisHousePtr->hasGame = false; thisHousePtr->nRooms = 0; wardBitSet = false; phoneBitSet = false; HUnlock((Handle)thisHouse); numberRooms = 0; mapLeftRoom = 60; mapTopRoom = 50; thisRoomNumber = kRoomIsEmpty; previousRoom = -1; houseUnlocked = true; OpenMapWindow(); UpdateMapWindow(); noRoomAtAll = true; fileDirty = true; UpdateMenus(false); ReflectCurrentRoom(true); return (true); } #endif //-------------------------------------------------------------- RealRoomNumberCount // Returns the real number of rooms in a house (some rooms may stillÉ // be place-holders - they were deleted earlier and are flagged asÉ // deleted but still occupy space in the file). short RealRoomNumberCount (void) { short realRoomCount, i; char wasState; wasState = HGetState((Handle)thisHouse); HLock((Handle)thisHouse); realRoomCount = (*thisHouse)->nRooms; if (realRoomCount != 0) { for (i = 0; i < (*thisHouse)->nRooms; i++) { if ((*thisHouse)->rooms[i].suite == kRoomIsEmpty) realRoomCount--; } } HSetState((Handle)thisHouse, wasState); return (realRoomCount); } //-------------------------------------------------------------- GetFirstRoomNumber // Returns the room number (indicee into house file) of the room whereÉ // the player is to begin. short GetFirstRoomNumber (void) { short firstRoom; char wasState; wasState = HGetState((Handle)thisHouse); HLock((Handle)thisHouse); if ((*thisHouse)->nRooms <= 0) { firstRoom = -1; noRoomAtAll = true; } else { firstRoom = (*thisHouse)->firstRoom; if ((firstRoom >= (*thisHouse)->nRooms) || (firstRoom < 0)) firstRoom = 0; } HSetState((Handle)thisHouse, wasState); return (firstRoom); } //-------------------------------------------------------------- WhereDoesGliderBegin // Returns a rectangle indicating where in the first room the player'sÉ // glider is to appear. void WhereDoesGliderBegin (Rect *theRect, short mode) { Point initialPt; char wasState; wasState = HGetState((Handle)thisHouse); HLock((Handle)thisHouse); if (mode == kResumeGameMode) initialPt = smallGame.where; else if (mode == kNewGameMode) initialPt = (*thisHouse)->initial; HSetState((Handle)thisHouse, wasState); QSetRect(theRect, 0, 0, kGliderWide, kGliderHigh); QOffsetRect(theRect, initialPt.h, initialPt.v); } //-------------------------------------------------------------- HouseHasOriginalPicts // Returns true is the current house has custom artwork imbedded. Boolean HouseHasOriginalPicts (void) { short nPicts; nPicts = Count1Resources('PICT'); return (nPicts > 0); } //-------------------------------------------------------------- CountHouseLinks // Counts up the number of linked objects in a house. short CountHouseLinks (void) { houseType *thisHousePtr; short numRooms, numLinks; short r, i, what; char wasState; numLinks = 0; wasState = HGetState((Handle)thisHouse); HLock((Handle)thisHouse); thisHousePtr = *thisHouse; numRooms = thisHousePtr->nRooms; for (r = 0; r < numRooms; r++) { for (i = 0; i < kMaxRoomObs; i++) { what = thisHousePtr->rooms[r].objects[i].what; switch (what) { case kLightSwitch: case kMachineSwitch: case kThermostat: case kPowerSwitch: case kKnifeSwitch: case kInvisSwitch: case kTrigger: case kLgTrigger: if (thisHousePtr->rooms[r].objects[i].data.e.where != -1) numLinks++; break; case kMailboxLf: case kMailboxRt: case kFloorTrans: case kCeilingTrans: case kInvisTrans: case kDeluxeTrans: if (thisHousePtr->rooms[r].objects[i].data.d.where != -1) numLinks++; break; } } } HSetState((Handle)thisHouse, wasState); return (numLinks); } //-------------------------------------------------------------- GenerateLinksList // Generates a list of all objects that have links and what roomsÉ // and objects they are linked to. It is called in order to preserveÉ // the links if the objects or rooms in a house are to be shuffledÉ // around. #ifndef COMPILEDEMO void GenerateLinksList (void) { houseType *thisHousePtr; objectType thisObject; short numLinks, numRooms, r, i, what; short floor, suite, roomLinked, objectLinked; char wasState; wasState = HGetState((Handle)thisHouse); HLock((Handle)thisHouse); thisHousePtr = *thisHouse; numRooms = thisHousePtr->nRooms; numLinks = 0; for (r = 0; r < numRooms; r++) { for (i = 0; i < kMaxRoomObs; i++) { what = thisHousePtr->rooms[r].objects[i].what; switch (what) { case kLightSwitch: case kMachineSwitch: case kThermostat: case kPowerSwitch: case kKnifeSwitch: case kInvisSwitch: case kTrigger: case kLgTrigger: thisObject = thisHousePtr->rooms[r].objects[i]; if (thisObject.data.e.where != -1) { ExtractFloorSuite(thisObject.data.e.where, &floor, &suite); roomLinked = GetRoomNumber(floor, suite); objectLinked = (short)thisObject.data.e.who; linksList[numLinks].srcRoom = r; linksList[numLinks].srcObj = i; linksList[numLinks].destRoom = roomLinked; linksList[numLinks].destObj = objectLinked; numLinks++; } break; case kMailboxLf: case kMailboxRt: case kFloorTrans: case kCeilingTrans: case kInvisTrans: case kDeluxeTrans: thisObject = thisHousePtr->rooms[r].objects[i]; if (thisObject.data.d.where != -1) { ExtractFloorSuite(thisObject.data.d.where, &floor, &suite); roomLinked = GetRoomNumber(floor, suite); objectLinked = (short)thisObject.data.d.who; linksList[numLinks].srcRoom = r; linksList[numLinks].srcObj = i; linksList[numLinks].destRoom = roomLinked; linksList[numLinks].destObj = objectLinked; numLinks++; } break; } } } HSetState((Handle)thisHouse, wasState); } #endif //-------------------------------------------------------------- SortRoomObjects // I'm a little fuzzy on what this does. #ifndef COMPILEDEMO void SortRoomsObjects (short which) { short probe, probe2, room, obj; Boolean busy, looking; busy = true; probe = 0; do { if ((*thisHouse)->rooms[which].objects[probe].what == kObjectIsEmpty) { looking = true; probe2 = probe + 1; // begin by looking at the next object do { if ((*thisHouse)->rooms[which].objects[probe2].what != kObjectIsEmpty) { (*thisHouse)->rooms[which].objects[probe] = (*thisHouse)->rooms[which].objects[probe2]; (*thisHouse)->rooms[which].objects[probe2].what = kObjectIsEmpty; if (srcLocations[probe2] != -1) linksList[srcLocations[probe2]].srcObj = probe; if (destLocations[probe2] != -1) { linksList[destLocations[probe2]].destObj = probe; room = linksList[destLocations[probe2]].srcRoom; obj = linksList[destLocations[probe2]].srcObj; (*thisHouse)->rooms[room].objects[obj].data.e.who = probe; } fileDirty = true; looking = false; } probe2++; if ((probe2 >= kMaxRoomObs) && (looking)) { looking = false; busy = false; } } while (looking); } probe++; if (probe >= (kMaxRoomObs - 1)) busy = false; } while (busy); } #endif //-------------------------------------------------------------- SortHouseObjects // I'm a little fuzzy on what this does exactly either. #ifndef COMPILEDEMO void SortHouseObjects (void) { houseType *thisHousePtr; short numLinks, numRooms, r, i, l; char wasState; SpinCursor(3); CopyThisRoomToRoom(); numLinks = CountHouseLinks(); if (numLinks == 0) return; linksList = nil; linksList = (linksPtr)NewPtr(sizeof(linksType) * numLinks); if (linksList == nil) RedAlert(kErrNoMemory); GenerateLinksList(); wasState = HGetState((Handle)thisHouse); HLock((Handle)thisHouse); thisHousePtr = *thisHouse; numRooms = thisHousePtr->nRooms; for (r = 0; r < numRooms; r++) { for (i = 0; i < kMaxRoomObs; i++) // initialize arrays { srcLocations[i] = -1; destLocations[i] = -1; } for (i = 0; i < kMaxRoomObs; i++) // walk object list { for (l = 0; l < numLinks; l++) // walk link list { if ((linksList[l].srcRoom == r) && (linksList[l].srcObj == i)) srcLocations[i] = l; if ((linksList[l].destRoom == r) && (linksList[l].destObj == i)) destLocations[i] = l; } } SortRoomsObjects(r); if ((r & 0x0007) == 0x0007) IncrementCursor(); } SpinCursor(3); HSetState((Handle)thisHouse, wasState); if (linksList != nil) DisposePtr((Ptr)linksList); ForceThisRoom(thisRoomNumber); } #endif //-------------------------------------------------------------- CountRoomsVisited // Goes through and counts the number of rooms a player has been to inÉ // the current game. short CountRoomsVisited (void) { houseType *thisHousePtr; short numRooms, r, count; char wasState; wasState = HGetState((Handle)thisHouse); HLock((Handle)thisHouse); thisHousePtr = *thisHouse; numRooms = thisHousePtr->nRooms; count = 0; for (r = 0; r < numRooms; r++) { if (thisHousePtr->rooms[r].visited) count++; } HSetState((Handle)thisHouse, wasState); return (count); } //-------------------------------------------------------------- GenerateRetroLinks // Walk entire house looking for objects which are linked to objectsÉ // in the current room. void GenerateRetroLinks (void) { houseType *thisHousePtr; objectType thisObject; short i, r, numRooms, floor, suite; short what, roomLinked, objectLinked; char wasState; for (i = 0; i < kMaxRoomObs; i++) // Initialize array. retroLinkList[i].room = -1; wasState = HGetState((Handle)thisHouse); HLock((Handle)thisHouse); thisHousePtr = *thisHouse; numRooms = thisHousePtr->nRooms; for (r = 0; r < numRooms; r++) { for (i = 0; i < kMaxRoomObs; i++) { what = thisHousePtr->rooms[r].objects[i].what; switch (what) { case kLightSwitch: case kMachineSwitch: case kThermostat: case kPowerSwitch: case kKnifeSwitch: case kInvisSwitch: case kTrigger: case kLgTrigger: thisObject = thisHousePtr->rooms[r].objects[i]; if (thisObject.data.e.where != -1) { ExtractFloorSuite(thisObject.data.e.where, &floor, &suite); roomLinked = GetRoomNumber(floor, suite); if (roomLinked == thisRoomNumber) { objectLinked = (short)thisObject.data.e.who; if (retroLinkList[objectLinked].room == -1) { retroLinkList[objectLinked].room = r; retroLinkList[objectLinked].object = i; } } } break; case kMailboxLf: case kMailboxRt: case kFloorTrans: case kCeilingTrans: case kInvisTrans: case kDeluxeTrans: thisObject = thisHousePtr->rooms[r].objects[i]; if (thisObject.data.d.where != -1) { ExtractFloorSuite(thisObject.data.d.where, &floor, &suite); roomLinked = GetRoomNumber(floor, suite); if (roomLinked == thisRoomNumber) { objectLinked = (short)thisObject.data.d.who; if (retroLinkList[objectLinked].room == -1) { retroLinkList[objectLinked].room = r; retroLinkList[objectLinked].object = i; } } } break; } } } HSetState((Handle)thisHouse, wasState); } //-------------------------------------------------------------- UpdateGoToDialog // Redraws the "Go To Room..." dialog. void UpdateGoToDialog (DialogPtr theDialog) { DrawDialog(theDialog); DrawDefaultButton(theDialog); FrameDialogItemC(theDialog, 10, kRedOrangeColor8); } //-------------------------------------------------------------- GoToFilter // Dialog filter for the "Go To Room..." dialog. pascal Boolean GoToFilter (DialogPtr dial, EventRecord *event, short *item) { switch (event->what) { case keyDown: switch ((event->message) & charCodeMask) { case kReturnKeyASCII: case kEnterKeyASCII: FlashDialogButton(dial, kOkayButton); *item = kOkayButton; return(true); break; default: return(false); } break; case updateEvt: SetPort((GrafPtr)dial); BeginUpdate(GetDialogWindow(dial)); UpdateGoToDialog(dial); EndUpdate(GetDialogWindow(dial)); event->what = nullEvent; return(false); break; default: return(false); break; } } //-------------------------------------------------------------- DoGoToDialog // "Go To Room..." dialog. void DoGoToDialog (void) { #define kGoToFirstButt 2 #define kGoToPrevButt 3 #define kGoToFSButt 4 #define kFloorEditText 5 #define kSuiteEditText 6 DialogPtr theDialog; long tempLong; short item, roomToGoTo; Boolean leaving, canceled; ModalFilterUPP goToFilterUPP; goToFilterUPP = NewModalFilterUPP(GoToFilter); BringUpDialog(&theDialog, kGoToDialogID); if (GetFirstRoomNumber() == thisRoomNumber) MyDisableControl(theDialog, kGoToFirstButt); if ((!RoomNumExists(previousRoom)) || (previousRoom == thisRoomNumber)) MyDisableControl(theDialog, kGoToPrevButt); SetDialogNumToStr(theDialog, kFloorEditText, (long)wasFloor); SetDialogNumToStr(theDialog, kSuiteEditText, (long)wasSuite); SelectDialogItemText(theDialog, kFloorEditText, 0, 1024); leaving = false; canceled = false; while (!leaving) { ModalDialog(goToFilterUPP, &item); if (item == kOkayButton) { roomToGoTo = -1; canceled = true; leaving = true; } else if (item == kGoToFirstButt) { roomToGoTo = GetFirstRoomNumber(); leaving = true; } else if (item == kGoToPrevButt) { roomToGoTo = previousRoom; leaving = true; } else if (item == kGoToFSButt) { GetDialogNumFromStr(theDialog, kFloorEditText, &tempLong); wasFloor = (short)tempLong; GetDialogNumFromStr(theDialog, kSuiteEditText, &tempLong); wasSuite = (short)tempLong; roomToGoTo = GetRoomNumber(wasFloor, wasSuite); leaving = true; } } DisposeDialog(theDialog); DisposeModalFilterUPP(goToFilterUPP); if (!canceled) { if (RoomNumExists(roomToGoTo)) { DeselectObject(); CopyRoomToThisRoom(roomToGoTo); ReflectCurrentRoom(false); } else SysBeep(1); } } //-------------------------------------------------------------- ConvertHouseVer1To2 // This function goes through an old version 1 house and converts itÉ // to version 2. void ConvertHouseVer1To2 (void) { Str255 roomStr, message; short wasRoom, floor, suite; short i, h, numRooms; char wasState; CopyThisRoomToRoom(); wasRoom = thisRoomNumber; GetLocalizedString(13, message); OpenMessageWindow(message); SpinCursor(3); wasState = HGetState((Handle)thisHouse); HLock((Handle)thisHouse); numRooms = (*thisHouse)->nRooms; for (i = 0; i < numRooms; i++) { if ((*thisHouse)->rooms[i].suite != kRoomIsEmpty) { NumToString((long)i, roomStr); GetLocalizedString(14, message); PasStringConcat(message, roomStr); SetMessageWindowMessage(message); SpinCursor(1); ForceThisRoom(i); for (h = 0; h < kMaxRoomObs; h++) { switch (thisRoom->objects[h].what) { case kMailboxLf: case kMailboxRt: case kFloorTrans: case kCeilingTrans: case kInvisTrans: case kDeluxeTrans: if (thisRoom->objects[h].data.d.where != -1) { ExtractFloorSuite(thisRoom->objects[h].data.d.where, &floor, &suite); floor += kNumUndergroundFloors; thisRoom->objects[h].data.d.where = MergeFloorSuite(floor, suite); } break; case kLightSwitch: case kMachineSwitch: case kThermostat: case kPowerSwitch: case kKnifeSwitch: case kInvisSwitch: case kTrigger: case kLgTrigger: if (thisRoom->objects[h].data.e.where != -1) { ExtractFloorSuite(thisRoom->objects[h].data.e.where, &floor, &suite); floor += kNumUndergroundFloors; thisRoom->objects[h].data.e.where = MergeFloorSuite(floor, suite); } break; } } CopyThisRoomToRoom(); } } (*thisHouse)->version = kHouseVersion; HSetState((Handle)thisHouse, wasState); InitCursor(); CloseMessageWindow(); ForceThisRoom(wasRoom); } //-------------------------------------------------------------- ShiftWholeHouse void ShiftWholeHouse (short howFar) { #pragma unused (howFar) short wasRoom; short i, h, numRooms; char wasState; OpenMessageWindow("\pShifting Whole HouseÉ"); SpinCursor(3); CopyThisRoomToRoom(); wasRoom = thisRoomNumber; wasState = HGetState((Handle)thisHouse); HLock((Handle)thisHouse); numRooms = (*thisHouse)->nRooms; for (i = 0; i < numRooms; i++) { if ((*thisHouse)->rooms[i].suite != kRoomIsEmpty) { SpinCursor(1); ForceThisRoom(i); for (h = 0; h < kMaxRoomObs; h++) { } CopyThisRoomToRoom(); } } HSetState((Handle)thisHouse, wasState); ForceThisRoom(wasRoom); InitCursor(); CloseMessageWindow(); }