""" UIMap: Display the map, consisting of rooms and paths. """ import os import sys import wx import array import math import types import UIImages import Config import wx.lib.throbber from wx import BitmapFromImage import Global import Keystroke from Global import Log, LogException import GameMap import JoyTracker class RoomWidget: def __init__(self, Room, Image): self.Room = Room self.Image = Image self.X = Room.X self.Y = Room.Y self.ShownFlag = 1 self.ImageFileName = None def HitTest(self, Point): Rectangle = self.GetRect() return Rectangle.InsideXY(Point.x, Point.y) def GetRect(self): if not self.Image: return wx.Rect(0, 0, 0, 0) return wx.Rect(self.X, self.Y, self.Image.GetWidth(), self.Image.GetHeight()) def Draw(self, DeviceContext, op = wx.COPY): if self.ShownFlag and self.Image and self.Image.Ok(): MemoryDC = wx.MemoryDC() MemoryDC.SelectObject(self.Image) DeviceContext.Blit(self.X, self.Y, self.Image.GetWidth(), self.Image.GetHeight(), MemoryDC, 0, 0, op, True) return True else: return False class AnimateStates: """ States to keep track of (simple) map animations. """ # Player walking from room A to room B: Walk = 1 # Room being cleared: ClearRoom = 2 # Door opening (exploding): OpenDoor = 3 # Player warping in and out: Teleport = 4 class UIPath: def __init__(self, RoomWidgetA, RoomWidgetB, Path): self.ShownFlag = 1 self.Path = Path self.WidgetA = RoomWidgetA self.WidgetB = RoomWidgetB # Decide upon our START and END coordinates, # relative to the upper left coords of our START and END room widgets: self.StartX = 0 self.StartY = 0 self.EndX = 0 self.EndY = 0 self.Image = None self.ReviseCoords() def GetImage(self, ClearFlag = 0): Name = self.Path.GetDoorImageName(ClearFlag) if Name == None: self.Image = None return self.Image self.Image = Global.ImageHandler.GetImage("%s.png"%Name) return self.Image def ReviseCoords(self): if (self.WidgetB.X > self.WidgetA.X): XSign = 1 else: XSign = -1 if (self.WidgetB.Y > self.WidgetA.Y): YSign = 1 else: YSign = -1 DeltaX = abs(self.WidgetB.X - self.WidgetA.X) DeltaY = abs(self.WidgetB.Y - self.WidgetA.Y) RoomHeight = self.WidgetA.Image.GetHeight() RoomWidth = self.WidgetA.Image.GetWidth() self.StartX = self.WidgetA.X self.StartY = self.WidgetA.Y self.EndX = self.WidgetB.X self.EndY = self.WidgetB.Y Distance = math.sqrt(DeltaX * DeltaX + DeltaY * DeltaY) Angle = math.cos(DeltaX / Distance) self.StartX = (self.WidgetA.Image.GetWidth() + 1) / 2 self.StartY = (self.WidgetA.Image.GetHeight() + 1) / 2 self.EndX = (self.WidgetB.Image.GetWidth() + 1) / 2 self.EndY = (self.WidgetB.Image.GetHeight() + 1) / 2 return class BaseUIMap(wx.Window): """ Base class for maps: Shared between map editor and UIMap. """ def __init__(self, Parent, App, ID): #wx.ScrolledWindow.__init__(self, Parent, ID, style = wx.WANTS_CHARS) wx.Window.__init__(self, Parent, ID, style = wx.WANTS_CHARS) self.ThrobTick = 0 self.App = App self.SetBackgroundColour("red") # for debugging self.RoomWidgets = [] self.PathWidgets = [] self.Throbbers = [] self.Bind(wx.EVT_TIMER, self.OnTimer) self.InitAnimation() def InitAnimation(self): """ Set up the animation timer and animation tracking """ self.AnimateTimer = 0 self.AnimateState = None self.Timer = wx.Timer(self) self.Timer.Start(5) #self.ThrobTimer = wx.Timer(self) #self.ThrobTimer.Start(50) def TileBackground(self, dc): "Tile the background bitmap" sz = self.GetClientSize() w = self.BackgroundImage.GetWidth() h = self.BackgroundImage.GetHeight() x = 0 while x < sz.width: y = 0 while y < sz.height: dc.DrawBitmap(self.BackgroundImage, x, y) y = y + h x = x + w def FindClickedRoom(self, Point): for Widget in self.RoomWidgets: if Widget.HitTest(Point): return Widget return None def DrawRooms(self, DeviceContext): "Go through our list of rooms and draw them in whatever place they are." for Room in self.RoomWidgets: Room.Draw(DeviceContext) def DrawPaths(self, DeviceContext): GraphicsContext = wx.GCDC(DeviceContext) GraphicsContext.SetPen(wx.Pen(wx.Color(99, 99, 99))) for Path in self.PathWidgets: if Path.ShownFlag: GraphicsContext.DrawLine(Path.WidgetA.X + Path.StartX, Path.WidgetA.Y + Path.StartY, Path.WidgetB.X + Path.EndX, Path.WidgetB.Y + Path.EndY) # If there's a door along the path, then draw the door in the middle: if Path.Image: X = (Path.WidgetA.X + Path.StartX + Path.WidgetB.X + Path.EndX) / 2 X -= Path.Image.GetWidth() / 2 Y = (Path.WidgetA.Y + Path.StartY + Path.WidgetB.Y + Path.EndY) / 2 Y -= Path.Image.GetHeight() / 2 MemoryDC = wx.MemoryDC() MemoryDC.SelectObject(Path.Image) DeviceContext.Blit(X, Y, Path.Image.GetWidth(), Path.Image.GetHeight(), MemoryDC, 0, 0, wx.COPY, True) def DrawThrobbers(self, DeviceContext): for Throbber in self.Throbbers: Throbber.Draw(DeviceContext) def OnEraseBackground(self, Event): """ Clears the background, then redraws it. If the DeviceContext is passed, then we only do so in the area so designated. Otherwise, it's the whole thing. """ DeviceContext = Event.GetDC() if not DeviceContext: DeviceContext = wx.ClientDC(self) Rectangle = self.GetUpdateRegion().GetBox() DeviceContext.SetClippingRect(Rectangle) self.TileBackground(DeviceContext) def ConstructRoomWidgets(self): self.RoomWidgets = [] Log("Add %s room widgets:"%len(self.Map.Rooms)) for Room in self.Map.Rooms: # Is the room cleared? if Global.Player == None: ClearFlag = 0 else: ClearFlag = Global.Player.GetRoomClearFlag(Room.ID) ImageStub = Room.GetImageName(ClearFlag) Image = Global.ImageHandler.GetImage("%s.png"%ImageStub) Widget = RoomWidget(Room, Image) self.RoomWidgets.append(Widget) def ConstructPathWidgets(self): self.PathWidgets = [] for Room in self.Map.Rooms: for Path in Room.Paths: RoomWidgetA = self.RoomWidgets[Path.RoomA.ID] RoomWidgetB = self.RoomWidgets[Path.RoomB.ID] PathWidget = UIPath(RoomWidgetA, RoomWidgetB, Path) if (Global.Player): ClearFlag = Global.Player.GetPathClearFlag(Path.RoomA.ID, Path.RoomB.ID) else: ClearFlag = 0 PathWidget.GetImage(ClearFlag) self.PathWidgets.append(PathWidget) def OnTimer(self): pass class UIMap(BaseUIMap, JoyTracker.JoyTracker): def __init__(self, Parent, App, ID): BaseUIMap.__init__(self, Parent, App, ID) JoyTracker.JoyTracker.__init__(self) Global.UIMap = self self.Map = None self.HasFocus = 0 self.AnimateWalkTicks = 20.0 self.AnimateOpenDoorTicks = 50.0 self.PlayerWidget = None self.CurrentMapID = None Image = Global.ImageHandler.GetImage("MapBack.png") self.BackgroundImage = Image self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftClick) self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDoubleClick) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.Bind(wx.EVT_KEY_UP, self.OnKeyUp) self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightClick) try: self.Joystick = wx.Joystick() except: self.Joystick = None self.KeyState = {} self.ButtonState = [0] * Config.KeyConfig.KeyCount self.OldButtonState = [0] * Config.KeyConfig.KeyCount self.DoorOpenThrobbers = [] def TimerCheckInput(self, *args, **kw): if self.App.QuestRunningFlag: return if not self.HasFocus: return self.SetGamepad() if self.AnimateState != None: return if not self.Map: return # Keystroke handling! CurrentRoom = self.Map.Rooms[Global.Player.CurrentRoom] if (self.ButtonState[Config.KeyConfig.ButtonA]): self.PressActionButton1(CurrentRoom) return if (self.ButtonState[Config.KeyConfig.ButtonB]): self.PressActionButton2(CurrentRoom) return if self.ButtonState[Config.KeyConfig.Left] and self.ButtonState[Config.KeyConfig.Up]: Neighbor = CurrentRoom.FindNeighbor(-1, -1) if Neighbor: self.AttemptToWalk(CurrentRoom, Neighbor) return if self.ButtonState[Config.KeyConfig.Left] and self.ButtonState[Config.KeyConfig.Down]: Neighbor = CurrentRoom.FindNeighbor(-1, 1) if Neighbor: self.AttemptToWalk(CurrentRoom, Neighbor) return if self.ButtonState[Config.KeyConfig.Right] and self.ButtonState[Config.KeyConfig.Up]: Neighbor = CurrentRoom.FindNeighbor(1, -1) if Neighbor: self.AttemptToWalk(CurrentRoom, Neighbor) return if self.ButtonState[Config.KeyConfig.Right] and self.ButtonState[Config.KeyConfig.Down]: Neighbor = CurrentRoom.FindNeighbor(1, 1) if Neighbor: self.AttemptToWalk(CurrentRoom, Neighbor) return if self.ButtonState[Config.KeyConfig.Up]: Neighbor = CurrentRoom.FindNeighbor(0, -1) if Neighbor: self.AttemptToWalk(CurrentRoom, Neighbor) return if self.ButtonState[Config.KeyConfig.Down]: Neighbor = CurrentRoom.FindNeighbor(0, 1) if Neighbor: self.AttemptToWalk(CurrentRoom, Neighbor) return if self.ButtonState[Config.KeyConfig.Left]: Neighbor = CurrentRoom.FindNeighbor(-1, 0) if Neighbor: self.AttemptToWalk(CurrentRoom, Neighbor) return if self.ButtonState[Config.KeyConfig.Right]: Neighbor = CurrentRoom.FindNeighbor(1, 0) if Neighbor: self.AttemptToWalk(CurrentRoom, Neighbor) return def PressActionButton1(self, Room): "Handle button-1, which normally launches quests." self.ResetInputs() if Room.Type == GameMap.RoomTypes.Quest: # If we're in a dungeon, and the quest is already clear, don't go in again: MapInfo = Global.Player.CurrentMap if Room.Map.Type != GameMap.MapType.Overworld and Global.Player.GetRoomClearFlag(Room.ID): self.ShowMessage("This dungeon room has already been cleared.\n\nJourney onward!", "Room cleared already") return Quest = Global.Metagame.Quests[Room.QuestID] Log("Launch room %s quest %s==%s"%(Room.ID, Room.QuestID, Quest.ID)) Global.App.LaunchQuest(Quest, self.Map) if Room.Type == GameMap.RoomTypes.Stairway: NewMap = Global.Metagame.Maps[Room.GotoMapID] if NewMap.Type != GameMap.MapType.Overworld: Global.Player.StartDungeon(NewMap) # Move to the new map: Global.Player.CurrentMap = Room.GotoMapID Global.Player.CurrentRoom = Room.GotoRoomID Global.Player.UpdateSavefilePlayer() Global.Player.UpdateSavefileMap(Room.GotoMapID) self.EnterMap(Global.Player.CurrentMap, Global.Player.CurrentRoom) def PressActionButton2(self, Room): #Log("Keystroke: Action 2 in room %s"%Room) self.ResetInputs() if Room.Type == GameMap.RoomTypes.Quest: Quest = Global.Metagame.Quests[Room.QuestID] self.App.ShowQuestStatsScreen(Quest) def UpdateRoomImages(self): """ The player just cleared a quest, maybe. So, we should perhaps fill in a room, and animate doors closing. """ for RoomWidget in self.RoomWidgets: ClearFlag = Global.Player.GetRoomClearFlag(RoomWidget.Room.ID) ImageStub = RoomWidget.Room.GetImageName(ClearFlag) RoomWidget.Image = Global.ImageHandler.GetImage("%s.png"%ImageStub) self.CheckDoors() def OnTimer(self, Event): self.ThrobTick += 1 if self.ThrobTick > 7: self.ThrobTick = 0 for Throbber in self.Throbbers: Throbber.Update() try: self.TimerCheckInput() except: LogException() #Log("OnTimer: State %s ticks %s"%(self.AnimateState, self.AnimateTimer)) if self.AnimateState == None: return if self.AnimateState == AnimateStates.Walk: self.AnimatePlayerWalk() elif self.AnimateState == AnimateStates.OpenDoor: self.AnimateOpenDoor() def AnimateOpenDoor(self): self.AnimateTimer += 1 if self.AnimateTimer >= self.AnimateOpenDoorTicks: self.AnimateTimer = 0 self.AnimateState = None for DoorOpenThrobber in self.DoorOpenThrobbers: self.RefreshRect(DoorOpenThrobber.GetRect()) self.Throbbers.remove(DoorOpenThrobber) self.DoorOpenThrobbers = [] def AnimatePlayerWalk(self): self.AnimateTimer += 1 if self.AnimateTimer >= self.AnimateWalkTicks: # Set the player to its final position, and stop animating. self.AnimateTimer = 0 self.AnimateState = None Global.Player.PreviousRoom = Global.Player.CurrentRoom Global.Player.CurrentRoom = self.AnimateWalkToRoom Room = self.Map.Rooms[Global.Player.CurrentRoom] Global.UIInfo.BuildRoomWidgets(Room) self.PlayerWidget.MoveTo(Room.X, Room.Y) return # Move the player along the path: X = self.StartX + (self.EndX - self.StartX) * self.AnimateTimer / self.AnimateWalkTicks Y = self.StartY + (self.EndY - self.StartY) * self.AnimateTimer / self.AnimateWalkTicks self.PlayerWidget.MoveTo(X, Y) def OnLeaveWindow(self, Event): pass def EnterMap(self, MapID, RoomID = 0): try: self.ResetInputs() if not self.PlayerWidget: self.PlayerWidget = UIImages.Throbber(self, "Player") self.PlayerWidget.Start() Log("Entered map %s"%MapID) self.Map = Global.Metagame.Maps[MapID] self.PathWidgets = [] self.CurrentMapID = MapID if MapID < 0: # Map -1 is a special widgetless map: return if not self.Map: return self.ConstructRoomWidgets() self.ConstructPathWidgets() # Move the player to the specified room: Room = self.Map.Rooms[RoomID] self.PlayerWidget.MoveTo(Room.X, Room.Y) Global.UIInfo.BuildRoomWidgets(Room) Global.UIInfo.BuildPlayerWidgets() self.Refresh() except: LogException() def OnPaint(self, Event): "Fired whenever a paint event occurs" DeviceContext = wx.PaintDC(self) self.PrepareDC(DeviceContext) self.DrawPaths(DeviceContext) self.DrawRooms(DeviceContext) self.DrawThrobbers(DeviceContext) if self.Map: DeviceContext.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.NORMAL)) TextExtent = DeviceContext.GetTextExtent(self.Map.Name) DeviceContext.SetBrush(wx.LIGHT_GREY_BRUSH) DeviceContext.SetPen(wx.Pen("Black", 2)) DeviceContext.DrawRoundedRectangle(5, 5, 10 + TextExtent[0], 8 + TextExtent[1], 8) DeviceContext.DrawText(self.Map.Name, 10, 9) # If it's a dungeon, indicate whether LIVES or TIME is limited: if (self.Map.Type != GameMap.MapType.Overworld): if self.Map.Type == GameMap.MapType.LifeDungeon: String = "Limited Lives" else: String = "Limited Time" DungeonTextExtent = DeviceContext.GetTextExtent(String) DeviceContext.SetBrush(wx.Brush(wx.Colour(0x80, 0x20, 0x80))) LeftX = 20 + TextExtent[0] DeviceContext.DrawRoundedRectangle(LeftX, 5, 10 + DungeonTextExtent[0], 8 + DungeonTextExtent[1], 8) DeviceContext.DrawText(String, LeftX + 5, 9) #self.DrawMapName(DeviceContext) def ShowMessage(self, Message, Title = None, ProgressLevel = None, ColorName = None): self.ResetInputs() import UIMessage Dialog = UIMessage.MessageDialog(self, Message, Title, ProgressLevel, ColorName) #Dialog = wx.MessageDialog(self, Message, "Message", wx.OK) Dialog.ShowModal() Dialog.Destroy() def OnRightClick(self, Event): """ If they right-clicked a room, then show quest-stats...if it's a quest room and if they've visited the room. """ Log(">>>Right clicked!") if self.App.QuestRunningFlag: return if self.AnimateState: return RoomWidget = self.FindClickedRoom(Event.GetPosition()) Log("Right-clicked %s"%RoomWidget) if not RoomWidget: # They didn't click on a room: return try: Room = RoomWidget.Room # from the SHAPE to the ROOM itself if Room.Type == GameMap.RoomTypes.Quest: Quest = Global.Metagame.Quests[Room.QuestID] QuestInfo = Global.Player.GetQuestInfo(Quest) if (QuestInfo.VisitFlag == 0): self.ShowMessage("You're not sure what's in that room.", "Mystery Cave 2000 Go!") else: self.App.ShowQuestStatsScreen(Quest) except: LogException() def OnLeftDoubleClick(self, Event): """ If they double-clicked their current room, then launch the quest! """ Log("Double-click... %s"%Event.GetPosition()) if self.AnimateState: return RoomWidget = self.FindClickedRoom(Event.GetPosition()) Log("Double-clicked %s"%RoomWidget) if not RoomWidget: # They didn't click on a room: return Room = RoomWidget.Room # from the SHAPE to the ROOM itself CurrentRoom = self.Map.Rooms[Global.Player.CurrentRoom] Log("Clicked room %s vs %s"%(Room, CurrentRoom)) if Room != CurrentRoom: return self.PressActionButton1(Room) def OnLeftClick(self, Event): """ Did they click a room? If so, move there...if it's a legal move! """ if self.App.QuestRunningFlag: return try: self.OnLeftClickInternal(Event) except: LogException() def OnLeftClickInternal(self, Event): RoomWidget = self.FindClickedRoom(Event.GetPosition()) if not RoomWidget: # They didn't click on a room: Event.Skip() return Room = RoomWidget.Room # from the SHAPE to the ROOM itself Log("%s, %s, %s (Animate state %s)"%(Room, Room.ID, type(Room.ID), self.AnimateState)) # Don't start moving to a new room if you're already animating a move: if self.AnimateState: return CurrentRoom = self.Map.Rooms[Global.Player.CurrentRoom] # If they clicked on the room they're standing in, then don't do anything. # (But double-clicking will launch the quest, of course!) if CurrentRoom == Room: Event.Skip() return CurrentRoomWidget = self.RoomWidgets[CurrentRoom.ID] self.AttemptToWalk(CurrentRoom, Room) def AttemptToWalk(self, CurrentRoom, Room): CurrentRoomWidget = self.RoomWidgets[CurrentRoom.ID] RoomWidget = self.RoomWidgets[Room.ID] ConnectPath = None for Path in CurrentRoom.Paths: if (Path.RoomA == CurrentRoom and Path.RoomB == Room) or (Path.RoomB == CurrentRoom and Path.RoomA == Room): ConnectPath = Path if not ConnectPath: return Info = Global.Player.MapInfoDict[self.Map.ID] # Is the current room clear? (If there's no color, the room must be cleared # before the player can advance beyond it) CurrentRoomClearFlag = Global.Player.GetRoomClearFlag(CurrentRoom.ID, self.Map.ID) if CurrentRoom.Type == GameMap.RoomTypes.Quest: if (not CurrentRoom.Color) and (not CurrentRoomClearFlag) and (Room.ID != Global.Player.PreviousRoom): # Forbidden! You can walk into and out of an uncleared room, but # you can't walk through it. self.ShowMessage("You must clear this room to move onward.", "Quest") return if ConnectPath.DoorColor: CompleteByColor = {} RoomCountByColor = {} for CheckRoom in self.Map.Rooms: if Info.RoomClearFlags.get(CheckRoom.ID, 0): CompleteByColor[CheckRoom.Color] = CompleteByColor.get(CheckRoom.Color, 0) + 1 RoomCountByColor[CheckRoom.Color] = RoomCountByColor.get(CheckRoom.Color, 0) + 1 ClearCount = CompleteByColor.get(ConnectPath.DoorColor, 0) if ClearCount >= ConnectPath.DoorClearCount: # Sanity check: The door should ALREADY be considered open! if not Info.DoorOpenFlags.get((CurrentRoom.ID, Room.ID), 0): self.OpenDoor(Info, ConnectPath) return else: # The door isn't open yet. ProgressLevel = 100 * ClearCount / float(max(1, ConnectPath.DoorClearCount)) ColorName = GameMap.RoomColors.ColorNames[ConnectPath.DoorColor] Title = "%s%s door"%(ColorName[0].upper(), ColorName[1:]) Message = "A %s door blocks the way.\n\n"%ColorName Message += "You must clear %s of %s %s rooms to pass.\n"%(ConnectPath.DoorClearCount, RoomCountByColor[ConnectPath.DoorColor], ColorName) if ClearCount == 0: Message += "You haven't cleared any yet. Let's get started!" elif ClearCount == ConnectPath.DoorClearCount - 1: Message += "%s cleared, just one more to go!"%ClearCount else: Message += "%s cleared so far, %s more to go."%(ClearCount, ConnectPath.DoorClearCount - ClearCount) self.ShowMessage(Message, Title, ProgressLevel, ColorName) return # If the room they're walking to is a quest room, the quest is now considered to be "visited": if Room.Type == GameMap.RoomTypes.Quest: QuestInfo = Global.Player.GetQuestInfo(Room.QuestID) if (QuestInfo.VisitFlag == 0): QuestInfo.VisitFlag = 1 Log(">>>Quest %s has now been visited"%Room.QuestID) Global.Player.UpdateSavefileQuest(Room.QuestID) # All clear - let's start moving! self.AnimateState = AnimateStates.Walk self.AnimateTimer = 0 self.StartX = CurrentRoomWidget.X self.StartY = CurrentRoomWidget.Y self.EndX = RoomWidget.X self.EndY = RoomWidget.Y self.AnimateWalkToRoom = Room.ID def OpenDoor(self, Info, Path): try: # Mr. Gorbechev, TEAR DOWN THIS DOOR! Info.DoorOpenFlags[(Path.RoomA.ID, Path.RoomB.ID)] = 1 Info.DoorOpenFlags[(Path.RoomB.ID, Path.RoomA.ID)] = 1 self.AnimateState = AnimateStates.OpenDoor # Create the throbber: DoorOpenThrobber = UIImages.AlphaThrobber(self, "Explode") X = (Path.RoomA.X + Path.RoomB.X) / 2 X -= DoorOpenThrobber.Images[0].GetWidth() / 4 Y = (Path.RoomA.Y + Path.RoomB.Y) / 2 Y -= DoorOpenThrobber.Images[0].GetHeight() / 4 DoorOpenThrobber.MoveTo(X, Y) self.Throbbers.append(DoorOpenThrobber) self.RefreshRect(DoorOpenThrobber.GetRect()) self.DoorOpenThrobbers.append(DoorOpenThrobber) self.AnimateState = AnimateStates.OpenDoor self.AnimateTimer = 0 # Update the path widget: for PathWidget in self.PathWidgets: if PathWidget.Path == Path: PathWidget.GetImage(1) # Save the map: Global.Player.UpdateSavefileMap(self.Map.ID) except: LogException() def CheckDoors(self): """ Check to see whether any new doors are eligible to OPEN: """ if not self.Map: return Info = Global.Player.MapInfoDict[self.Map.ID] CompleteByColor = {} for Room in self.Map.Rooms: if Info.RoomClearFlags.get(Room.ID, 0): CompleteByColor[Room.Color] = CompleteByColor.get(Room.Color, 0) + 1 for Room in self.Map.Rooms: for Path in Room.Paths: if (not Path.DoorColor): continue ClearFlag = Info.DoorOpenFlags.get((Path.RoomA.ID, Path.RoomB.ID), 0) if (not ClearFlag) and (CompleteByColor.get(Path.DoorColor, 0) >= Path.DoorClearCount): self.OpenDoor(Info, Path) if __name__ == "__main__": # Command-line invocation of UIMap: Unit tests pass