1# -*-python-*- 2# GemRB - Infinity Engine Emulator 3# Copyright (C) 2003 The GemRB Project 4# 5# This program is free software; you can redistribute it and/or 6# modify it under the terms of the GNU General Public License 7# as published by the Free Software Foundation; either version 2 8# of the License, or (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program; if not, write to the Free Software 17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18# 19 20# Maze.py - script to generate the modron maze in PST 21 22################################################### 23 24import GemRB 25from maze_defs import * 26from GUIDefines import STR_AREANAME 27 28rooms = None 29max = 0 30dims = 0 31entries = None 32offx = (0,0,1,0,-1) 33offy = (0,-1,0,1,0) 34#wallbits = (0, WALL_WEST, WALL_NORTH, WALL_EAST, WALL_SOUTH) 35wallbits = (0, WALL_NORTH, WALL_EAST, WALL_SOUTH, WALL_WEST) 36entrances = ("", "Entry3", "Entry4", "Entry1", "Entry2") 37doors = ("", "northdoor", "eastdoor", "southdoor", "westdoor") 38anims = ("", "a13xxdn", "a13xxde", "a13xxds", "a13xxdw") 39aposx=(0,1012,1005,505,380) 40aposy=(0,524,958,996,562) 41cposx=(686,886,497) 42cposy=(498,722,726) 43 44def Possible(posx, posy): 45 global entries 46 47 pos = posx*MAZE_MAX_DIM+posy 48 49 if entries[pos]: 50 return -1 51 return pos 52 53def GetPossible (pos): 54 posx = pos/MAZE_MAX_DIM 55 posy = pos-posx*MAZE_MAX_DIM 56 possible = [] 57 58 if posx>0: 59 newpos = Possible(posx-1, posy) 60 if newpos!=-1: 61 possible[:0] = [newpos] 62 if posy>0: 63 newpos = Possible(posx, posy-1) 64 if newpos!=-1: 65 possible[:0] = [newpos] 66 if posx<dims-1: 67 newpos = Possible(posx+1, posy) 68 if newpos!=-1: 69 possible[:0] = [newpos] 70 if posy<dims-1: 71 newpos = Possible(posx, posy+1) 72 if newpos!=-1: 73 possible[:0] = [newpos] 74 75 return possible 76 77#loads a 2da and sets it up as maze 78def LoadMazeFrom2da(tablename): 79 MazeTable = GemRB.LoadTable(tablename) 80 if MazeTable == None: 81 return 82 size = MazeTable.GetValue(-1,-1) 83 GemRB.SetupMaze(size, size) 84 traps = 0 85 for i in range(MazeTable.GetRowCount()): 86 Area = MazeTable.GetRowName(i) 87 OVERRIDE = MazeTable.GetValue(Area,"OVERRIDE") 88 TRAPTYPE = MazeTable.GetValue(Area,"TRAPTYPE") 89 WALLS = MazeTable.GetValue(Area,"WALLS") 90 VISITED = MazeTable.GetValue(Area,"VISITED") 91 pos = ConvertPos(int(Area[4:])-1) 92 GemRB.SetMazeEntry(pos, ME_OVERRIDE, OVERRIDE) 93 GemRB.SetMazeEntry(pos, ME_TRAP, TRAPTYPE) 94 GemRB.SetMazeEntry(pos, ME_WALLS, WALLS) 95 GemRB.SetMazeEntry(pos, ME_VISITED, VISITED) 96 if TRAPTYPE>=0: 97 traps = traps+1 98 99 #disabling special rooms 100 GemRB.SetMazeData(MH_POS1X, -1) 101 GemRB.SetMazeData(MH_POS1Y, -1) 102 GemRB.SetMazeData(MH_POS2X, -1) 103 GemRB.SetMazeData(MH_POS2Y, -1) 104 #adding foyer coordinates (middle of bottom) 105 GemRB.SetMazeData(MH_POS3X, size/2) 106 GemRB.SetMazeData(MH_POS3Y, size-1) 107 #adding engine room coordinates (bottom right) 108 GemRB.SetMazeData(MH_POS4X, size-1) 109 GemRB.SetMazeData(MH_POS4Y, size-1) 110 #adding trap 111 GemRB.SetMazeData(MH_TRAPCOUNT, traps) 112 #finish 113 GemRB.SetMazeData(MH_INITED, 1) 114 return 115 116def AddRoom (pos): 117 global rooms 118 global entries 119 120 rooms[len(rooms):]=[pos] 121 entries[pos] = 1 122 return 123 124def MainRoomFits (pos1x, pos1y, pos): 125 global entries 126 127 room = pos1x*MAZE_MAX_DIM+pos1y 128 if room==pos: 129 return False 130 131 south = pos1x*MAZE_MAX_DIM+pos1y+1 132 if south==pos: 133 return False 134 135 north = pos1x*MAZE_MAX_DIM+pos1y-1 136 if north==pos: 137 return False 138 139 GemRB.SetMazeEntry(room, ME_WALLS, WALL_SOUTH) 140 entries[room] = 1 141 entries[north] = 1 142 return True 143 144def zeros (size): 145 return size*[0] 146 147def PrintMaze(): 148 header = GemRB.GetMazeHeader() 149 if header==None or header["Inited"]==0: 150 print("There is no maze or it is not initialized!") 151 return 152 153 MazeX = header["MazeX"] 154 MazeY = header["MazeY"] 155 MainX = header["Pos1X"] 156 MainY = header["Pos1Y"] 157 NordomX = header["Pos2X"] 158 NordomY = header["Pos2Y"] 159 FoyerX = header["Pos3X"] 160 161 print("Maze size is " + str(MazeX) + "X" + str(MazeY)) 162 for y in range (MazeY): 163 line = "" 164 for x in range (MazeX): 165 pos = MAZE_MAX_DIM*x+y 166 entry = GemRB.GetMazeEntry(pos) 167 if entry["Walls"]&WALL_NORTH: 168 line = line + "+ " 169 else: 170 line = line + "+-" 171 print(line + "+") 172 line = "" 173 for x in range (MazeX): 174 pos = MAZE_MAX_DIM*x+y 175 entry = GemRB.GetMazeEntry(pos) 176 if entry["Walls"]&WALL_WEST: 177 line = line + " " 178 else: 179 line = line + "|" 180 if x == NordomX and y == NordomY: 181 line = line + "N" 182 elif x == MainX and y == MainY: 183 line = line + "W" 184 elif entry["Trapped"]>=0: 185 line = line + chr(entry["Trapped"]+65) 186 else: 187 line = line + " " 188 print(line + "|") 189 line = "" 190 for x in range (MazeX): 191 if FoyerX==x: 192 line = line + "+ " 193 else: 194 line = line + "+-" 195 print(line + "+") 196 return 197 198def ConvertPos (pos): 199 return ((pos&7)<<3)|(pos>>3) 200 201################################################### 202def CreateMaze (): 203 global max 204 global dims 205 global entries 206 global rooms 207 208 if GemRB.GetGameVar("EnginInMaze")>0: 209 LoadMazeFrom2da("easymaze") 210 return 211 212 mazedifficulty = GemRB.GetGameVar("MazeDifficulty") 213 214 #make sure there are no more traps than rooms 215 #make sure dimensions don't exceed maximum possible 216 if mazedifficulty==0: 217 dims = 4 218 traps = 5 219 elif mazedifficulty==1: 220 dims = 6 221 traps = 12 222 else: 223 dims = 8 224 traps = 20 225 226 entries = zeros(MAZE_ENTRY_COUNT) 227 rooms = [] 228 229 GemRB.SetupMaze(dims, dims) 230 for x in range(dims, MAZE_MAX_DIM): 231 for y in range(dims, MAZE_MAX_DIM): 232 pos = x*MAZE_MAX_DIM+y 233 entries[pos] = 1 234 235 nordomx = GemRB.Roll(1, dims-1, -1) 236 nordomy = GemRB.Roll(1, dims, -1) 237 pos = nordomx*MAZE_MAX_DIM+nordomy 238 entries[pos] = 1 239 GemRB.SetMazeEntry(pos, ME_WALLS, WALL_EAST) 240 pos = nordomx*MAZE_MAX_DIM+nordomy+MAZE_MAX_DIM 241 AddRoom(pos) 242 if (mazedifficulty>1): 243 GemRB.SetMazeData(MH_POS2X, nordomx) 244 GemRB.SetMazeData(MH_POS2Y, nordomy) 245 pos1x = GemRB.Roll(1, dims, -1) 246 pos1y = GemRB.Roll(1, dims-2, 0) 247 while not MainRoomFits(pos1x, pos1y, pos): 248 pos1x = GemRB.Roll(1, dims, -1) 249 pos1y = GemRB.Roll(1, dims-2, 0) 250 GemRB.SetMazeData(MH_POS1X, pos1x) 251 GemRB.SetMazeData(MH_POS1Y, pos1y) 252 else: 253 GemRB.SetMazeData(MH_POS1X, -1) 254 GemRB.SetMazeData(MH_POS1Y, -1) 255 GemRB.SetMazeData(MH_POS2X, -1) 256 GemRB.SetMazeData(MH_POS2Y, -1) 257 258 oldentries = entries 259 for i in range(traps): 260 posx = GemRB.Roll(1, dims, -1) 261 posy = GemRB.Roll(1, dims, -1) 262 pos = posx*MAZE_MAX_DIM+posy 263 while entries[pos]: 264 pos = pos + 1 265 if pos>=MAZE_ENTRY_COUNT: 266 posx = 0 267 posy = 0 268 pos = 0 269 else: 270 posx = pos/MAZE_MAX_DIM 271 posy = pos-posx*MAZE_MAX_DIM 272 GemRB.SetMazeEntry(pos, ME_TRAP, GemRB.Roll(1, 3, -1) ) 273 274 entries = oldentries 275 while len(rooms)>0: 276 pos = rooms.pop(0) 277 posx = pos/MAZE_MAX_DIM 278 posy = pos-posx*MAZE_MAX_DIM 279 possible = GetPossible(pos) 280 plen = len(possible) 281 if plen>0: 282 if plen==1: 283 newpos = possible[0] 284 else: 285 #adding item back if we got room to grow 286 AddRoom(pos) 287 newpos = possible[GemRB.Roll(1, plen, -1) ] 288 if entries[newpos]==0: 289 if newpos+1 == pos: 290 GemRB.SetMazeEntry(pos, ME_WALLS, WALL_NORTH) 291 elif pos+1 == newpos: 292 GemRB.SetMazeEntry(pos, ME_WALLS, WALL_SOUTH) 293 elif pos+MAZE_MAX_DIM == newpos: 294 GemRB.SetMazeEntry(pos, ME_WALLS, WALL_EAST) 295 elif newpos+MAZE_MAX_DIM == pos: 296 GemRB.SetMazeEntry(pos, ME_WALLS, WALL_WEST) 297 else: 298 print("Something went wrong at pos: ", pos, " newpos: ", newpos) 299 AddRoom(newpos) 300 301 #adding foyer coordinates 302 x = GemRB.Roll(1,dims,-1) 303 while x!=nordomx and dims-1!=nordomy: 304 x=GemRB.Roll(1,dims,-1) 305 306 GemRB.SetMazeData(MH_POS3X, GemRB.Roll(1,dims,-1) ) 307 GemRB.SetMazeData(MH_POS3Y, dims-1) 308 309 #setting engine room coordinates to hidden (accessible from foyer) 310 GemRB.SetMazeData(MH_POS4X, -1) 311 GemRB.SetMazeData(MH_POS4Y, -1) 312 #adding traps 313 GemRB.SetMazeData(MH_TRAPCOUNT, traps) 314 #finish 315 GemRB.SetMazeData(MH_INITED, 1) 316 return 317 318def FormatAreaName(pos): 319 if pos<9: 320 return "AR130"+str(pos+1) 321 return "AR13"+str(pos+1) 322 323def CustomizeMaze(AreaName): 324 325 header = GemRB.GetMazeHeader() 326 327 mainX = header['Pos1X'] 328 mainY = header['Pos1Y'] 329 nordomX = header['Pos2X'] 330 nordomY = header['Pos2Y'] 331 foyerX = header['Pos3X'] 332 foyerY = header['Pos3Y'] 333 engineX = header['Pos4X'] 334 engineY = header['Pos4Y'] 335 #modron foyer 336 if AreaName == "fy": 337 #TODO modron foyer, only one entrance if EnginInMaze = 1 338 tmp = foyerX+foyerY*MAZE_MAX_DIM 339 GemRB.SetMapExit ("exit1", FormatAreaName(tmp), "Entry3" ) 340 341 #disable engine room 342 if GemRB.GetGameVar("EnginInMaze")==1: 343 GemRB.SetMapExit ("exit3" ) 344 GemRB.SetMapDoor (doors[3], 0) 345 GemRB.SetMapAnimation(aposx[3], aposy[3], anims[3]) 346 else: 347 GemRB.SetMapExit ("exit3", "AR13EN") 348 349 GemRB.SetMapExit ("exit4" ) 350 GemRB.SetMapAnimation(aposx[4], aposy[4], anims[4]) 351 return 352 353 if AreaName == "en": 354 if GemRB.GetGameVar("EnginInMaze")==1: 355 tmp = engineX+(engineY-1)*MAZE_MAX_DIM 356 GemRB.SetMapExit ("exit1", FormatAreaName(tmp), "Entry3" ) 357 else: 358 GemRB.SetMapExit ("exit1", "AR13FY" ) 359 return 360 361 if AreaName == "wz": 362 #TODO wizard's lair 363 tmp = mainY+mainX*MAZE_MAX_DIM 364 entry = GemRB.GetMazeEntry(tmp) 365 tmp = mainX+mainY*MAZE_MAX_DIM 366 GemRB.SetMapExit ("exit3", FormatAreaName(tmp), "Entry1" ) 367 GemRB.SetMapDoor (doors[3], 1) 368 return 369 370 if AreaName == "fd": 371 #TODO nordom 372 tmp = nordomY+nordomX*MAZE_MAX_DIM 373 entry = GemRB.GetMazeEntry(tmp) 374 tmp = nordomX+nordomY*MAZE_MAX_DIM 375 GemRB.SetMapExit ("exit2", FormatAreaName(tmp), "Entry4" ) 376 GemRB.SetMapDoor (doors[2], 1) 377 return 378 379 tmp = int(AreaName)-1 380 if tmp<0 or tmp>63: 381 return 382 383 pos = ConvertPos(tmp) 384 entry = GemRB.GetMazeEntry(pos) 385 #TODO: customize maze area based on entry (walls, traps) 386 387 if entry['Visited']: 388 #already customized 389 return 390 391 difficulty = GemRB.GetGameVar("MazeDifficulty") 392 if difficulty == 0: 393 name = "CLOW" 394 elif difficulty == 1: 395 name = "CMOD" 396 else: 397 name = "CHIGH" 398 399 ccount = GemRB.Roll(1,3,0) 400 401 for i in range(ccount): 402 GemRB.CreateCreature(0, name, cposx[i], cposy[i]) 403 404 trapped = entry['Trapped'] 405 if trapped>=0: 406 roll = GemRB.Roll(1,4,0) 407 GemRB.SetMapRegion('Trap'+chr(trapped+65), '1300trp'+str(roll) ) 408 409 GemRB.SetMazeEntry(pos, ME_VISITED, 1) 410 walls = entry['Walls'] 411 y = tmp / MAZE_MAX_DIM 412 x = tmp - y*MAZE_MAX_DIM 413 for i in range(1,5): 414 if wallbits[i]&walls: 415 x2 = x+offx[i] 416 y2 = y+offy[i] 417 set = 0 418 if x2 == nordomX and y2 == nordomY: 419 NewArea = "AR13FD" 420 elif x2 == mainX and y2 == mainY: 421 NewArea = "AR13WZ" 422 elif x2 == foyerX and y2 == foyerY+1: 423 NewArea = "AR13FY" 424 elif x2 == engineX and y2 == engineY: 425 NewArea = "AR13EN" 426 else: 427 if x2>=0 and x2<MAZE_MAX_DIM and y2>=0 and y2<MAZE_MAX_DIM: 428 #reversed coordinates 429 NewArea = FormatAreaName (x2+y2*MAZE_MAX_DIM) 430 else: 431 #maximum dimensions 432 set = 1 433 else: 434 set = 1 435 436 if set: 437 #remove exit 438 GemRB.SetMapExit ("exit"+str(i) ) 439 GemRB.SetMapDoor (doors[i], 0) 440 GemRB.SetMapAnimation(aposx[i], aposy[i], anims[i]) 441 else: 442 #set exit 443 GemRB.SetMapExit ("exit"+str(i), NewArea, entrances[i] ) 444 GemRB.SetMapDoor (doors[i], 1) 445 GemRB.SetMapAnimation(-1, -1, "", 0, 0) 446 return 447 448def CustomizeArea(): 449 Area = GemRB.GetGameString (STR_AREANAME) 450 if Area[0:4] == "ar13": 451 CustomizeMaze(Area[4:]) 452 return 453 454 #TODO insert non maze area customization here (set own area scripts for special areas) 455 return 456