CLIVAN: copied muntuo (not all critters yet)
[k8-i-v-a-n.git] / src / game / level.cpp
blobdc2c1540ae57b995ca7709b485daf02ffa13a365
1 /*
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
8 * See LICENSING which should be included
9 * along with this file for more details
13 /* Compiled through levelset.cpp */
15 #define FORBIDDEN 1
16 #define ON_POSSIBLE_ROUTE 2
17 #define STILL_ON_POSSIBLE_ROUTE 4
18 #define PREFERRED 8
19 #define ICE_TERRAIN 16
20 #define STONE_TERRAIN 32
22 level::level() : Room(1, static_cast<room*>(0)), GlobalRainLiquid(0), SunLightEmitation(0), AmbientLuminance(0), SquareStack(0), NightAmbientLuminance(0) { }
23 void level::SetRoom(int I, room* What) { Room[I] = What; }
24 void level::AddToAttachQueue(v2 Pos) { AttachQueue.push_back(Pos); }
26 uLong level::NextExplosionID = 1;
28 node*** node::NodeMap;
29 int node::RequiredWalkability;
30 ccharacter* node::SpecialMover;
31 v2 node::To;
32 uChar** node::WalkabilityMap;
33 int node::XSize, node::YSize;
34 nodequeue* node::NodeQueue;
36 level::~level()
38 uLong c;
40 for(c = 0; c < XSizeTimesYSize; ++c)
41 delete NodeMap[0][c];
43 for(c = 0; c < Room.size(); ++c)
44 delete Room[c];
46 delete [] NodeMap;
47 delete [] WalkabilityMap;
48 delete GlobalRainLiquid;
49 delete [] SquareStack;
50 game::SetGlobalRainLiquid(0);
53 void level::ExpandPossibleRoute(int OrigoX, int OrigoY, int TargetX, int TargetY, truth XMode)
55 #define CHECK(x, y) !(FlagMap[x][y] & (ON_POSSIBLE_ROUTE|FORBIDDEN))
57 #define CALL_EXPAND(x, y)\
59 ExpandPossibleRoute(x, y, TargetX, TargetY, XMode);\
61 if(FlagMap[TargetX][TargetY] & ON_POSSIBLE_ROUTE)\
62 return;\
65 FlagMap[OrigoX][OrigoY] |= ON_POSSIBLE_ROUTE;
67 if(XMode)
69 if(TargetX < OrigoX)
70 if(CHECK(OrigoX - 1, OrigoY))
71 CALL_EXPAND(OrigoX - 1, OrigoY);
73 if(TargetX > OrigoX)
74 if(CHECK(OrigoX + 1, OrigoY))
75 CALL_EXPAND(OrigoX + 1, OrigoY);
77 if(TargetY < OrigoY)
78 if(CHECK(OrigoX, OrigoY - 1))
79 CALL_EXPAND(OrigoX, OrigoY - 1);
81 if(TargetY > OrigoY)
82 if(CHECK(OrigoX, OrigoY + 1))
83 CALL_EXPAND(OrigoX, OrigoY + 1);
85 if(TargetX <= OrigoX)
86 if(OrigoX < XSize - 2 && CHECK(OrigoX + 1, OrigoY))
87 CALL_EXPAND(OrigoX + 1, OrigoY);
89 if(TargetX >= OrigoX)
90 if(OrigoX > 1 && CHECK(OrigoX - 1, OrigoY))
91 CALL_EXPAND(OrigoX - 1, OrigoY);
93 if(TargetY <= OrigoY)
94 if(OrigoY < YSize - 2 && CHECK(OrigoX, OrigoY + 1))
95 CALL_EXPAND(OrigoX, OrigoY + 1);
97 if(TargetY >= OrigoY)
98 if(OrigoY > 1 && CHECK(OrigoX, OrigoY - 1))
99 CALL_EXPAND(OrigoX, OrigoY - 1);
101 else
103 if(TargetY < OrigoY)
104 if(CHECK(OrigoX, OrigoY - 1))
105 CALL_EXPAND(OrigoX, OrigoY - 1);
107 if(TargetY > OrigoY)
108 if(CHECK(OrigoX, OrigoY + 1))
109 CALL_EXPAND(OrigoX, OrigoY + 1);
111 if(TargetX < OrigoX)
112 if(CHECK(OrigoX - 1, OrigoY))
113 CALL_EXPAND(OrigoX - 1, OrigoY);
115 if(TargetX > OrigoX)
116 if(CHECK(OrigoX + 1, OrigoY))
117 CALL_EXPAND(OrigoX + 1, OrigoY);
119 if(TargetY <= OrigoY)
120 if(OrigoY < YSize - 2 && CHECK(OrigoX, OrigoY + 1))
121 CALL_EXPAND(OrigoX, OrigoY + 1);
123 if(TargetY >= OrigoY)
124 if(OrigoY > 1 && CHECK(OrigoX, OrigoY - 1))
125 CALL_EXPAND(OrigoX, OrigoY - 1);
127 if(TargetX <= OrigoX)
128 if(OrigoX < XSize - 2 && CHECK(OrigoX + 1, OrigoY))
129 CALL_EXPAND(OrigoX + 1, OrigoY);
131 if(TargetX >= OrigoX)
132 if(OrigoX > 1 && CHECK(OrigoX - 1, OrigoY))
133 CALL_EXPAND(OrigoX - 1, OrigoY);
136 #undef CHECK
138 #undef CALL_EXPAND
141 void level::ExpandStillPossibleRoute(int OrigoX, int OrigoY, int TargetX, int TargetY, truth XMode)
143 #define CHECK(x, y) (FlagMap[x][y] & (STILL_ON_POSSIBLE_ROUTE|ON_POSSIBLE_ROUTE)) == ON_POSSIBLE_ROUTE
145 #define CALL_EXPAND(x, y) \
147 ExpandStillPossibleRoute(x, y, TargetX, TargetY, XMode);\
149 if(FlagMap[TargetX][TargetY] & STILL_ON_POSSIBLE_ROUTE) \
150 return;\
153 FlagMap[OrigoX][OrigoY] |= STILL_ON_POSSIBLE_ROUTE;
155 if(XMode)
157 if(TargetX < OrigoX)
158 if(CHECK(OrigoX - 1, OrigoY))
159 CALL_EXPAND(OrigoX - 1, OrigoY);
161 if(TargetX > OrigoX)
162 if(CHECK(OrigoX + 1, OrigoY))
163 CALL_EXPAND(OrigoX + 1, OrigoY);
165 if(TargetY < OrigoY)
166 if(CHECK(OrigoX, OrigoY - 1))
167 CALL_EXPAND(OrigoX, OrigoY - 1);
169 if(TargetY > OrigoY)
170 if(CHECK(OrigoX, OrigoY + 1))
171 CALL_EXPAND(OrigoX, OrigoY + 1);
173 if(TargetX <= OrigoX)
174 if(OrigoX < XSize - 2 && CHECK(OrigoX + 1, OrigoY))
175 CALL_EXPAND(OrigoX + 1, OrigoY);
177 if(TargetX >= OrigoX)
178 if(OrigoX > 1 && CHECK(OrigoX - 1, OrigoY))
179 CALL_EXPAND(OrigoX - 1, OrigoY);
181 if(TargetY <= OrigoY)
182 if(OrigoY < YSize - 2 && CHECK(OrigoX, OrigoY + 1))
183 CALL_EXPAND(OrigoX, OrigoY + 1);
185 if(TargetY >= OrigoY)
186 if(OrigoY > 1 && CHECK(OrigoX, OrigoY - 1))
187 CALL_EXPAND(OrigoX, OrigoY - 1);
189 else
191 if(TargetY < OrigoY)
192 if(CHECK(OrigoX, OrigoY - 1))
193 CALL_EXPAND(OrigoX, OrigoY - 1);
195 if(TargetY > OrigoY)
196 if(CHECK(OrigoX, OrigoY + 1))
197 CALL_EXPAND(OrigoX, OrigoY + 1);
199 if(TargetX < OrigoX)
200 if(CHECK(OrigoX - 1, OrigoY))
201 CALL_EXPAND(OrigoX - 1, OrigoY);
203 if(TargetX > OrigoX)
204 if(CHECK(OrigoX + 1, OrigoY))
205 CALL_EXPAND(OrigoX + 1, OrigoY);
207 if(TargetY <= OrigoY)
208 if(OrigoY < YSize - 2 && CHECK(OrigoX, OrigoY + 1))
209 CALL_EXPAND(OrigoX, OrigoY + 1);
211 if(TargetY >= OrigoY)
212 if(OrigoY > 1 && CHECK(OrigoX, OrigoY - 1))
213 CALL_EXPAND(OrigoX, OrigoY - 1);
215 if(TargetX <= OrigoX)
216 if(OrigoX < XSize - 2 && CHECK(OrigoX + 1, OrigoY))
217 CALL_EXPAND(OrigoX + 1, OrigoY);
219 if(TargetX >= OrigoX)
220 if(OrigoX > 1 && CHECK(OrigoX - 1, OrigoY))
221 CALL_EXPAND(OrigoX - 1, OrigoY);
224 #undef CHECK
226 #undef CALL_EXPAND
229 void level::GenerateTunnel(int FromX, int FromY, int TargetX, int TargetY, truth XMode)
231 FlagMap[FromX][FromY] |= ON_POSSIBLE_ROUTE;
232 ExpandPossibleRoute(FromX, FromY, TargetX, TargetY, XMode);
233 const contentscript<glterrain>* GTerrain = LevelScript->GetTunnelSquare()->GetGTerrain();
234 const contentscript<olterrain>* OTerrain = LevelScript->GetTunnelSquare()->GetOTerrain();
236 if(FlagMap[TargetX][TargetY] & ON_POSSIBLE_ROUTE)
237 for(int x = 0; x < XSize; ++x)
238 for(int y = 0; y < YSize; ++y)
239 if((FlagMap[x][y] & (ON_POSSIBLE_ROUTE|PREFERRED)) == ON_POSSIBLE_ROUTE
240 && !(x == FromX && y == FromY) && !(x == TargetX && y == TargetY))
242 FlagMap[x][y] &= ~ON_POSSIBLE_ROUTE;
243 FlagMap[FromX][FromY] |= STILL_ON_POSSIBLE_ROUTE;
244 ExpandStillPossibleRoute(FromX, FromY, TargetX, TargetY, XMode);
246 if(!(FlagMap[TargetX][TargetY] & STILL_ON_POSSIBLE_ROUTE))
248 FlagMap[x][y] |= ON_POSSIBLE_ROUTE|PREFERRED;
249 Map[x][y]->ChangeGLTerrain(GTerrain->Instantiate());
250 Map[x][y]->ChangeOLTerrain(OTerrain->Instantiate());
253 for(int X = 0; X < XSize; ++X)
254 for(int Y = 0; Y < YSize; ++Y)
255 FlagMap[X][Y] &= ~STILL_ON_POSSIBLE_ROUTE;
258 for(int x = 1; x < XSize - 1; ++x)
259 for(int y = 1; y < YSize - 1; ++y)
260 FlagMap[x][y] &= ~ON_POSSIBLE_ROUTE;
263 void level::Generate(int Index)
265 game::BusyAnimation();
266 Initialize(LevelScript->GetSize()->X, LevelScript->GetSize()->Y);
267 game::SetCurrentArea(this);
268 game::SetCurrentLevel(this);
269 Alloc2D(NodeMap, XSize, YSize);
270 Alloc2D(WalkabilityMap, XSize, YSize);
271 Map = reinterpret_cast<lsquare***>(area::Map);
272 SquareStack = new lsquare*[XSizeTimesYSize];
274 if ((Index == 0 && GetDungeon()->GetIndex() == NEW_ATTNAM) ||
275 (Index == 0 && GetDungeon()->GetIndex() == ATTNAM) ||
276 (Index == 0 && GetDungeon()->GetIndex() == MUNTUO))
277 NightAmbientLuminance = MakeRGB24(95, 95, 95);
279 int x, y;
281 for(x = 0; x < XSize; ++x)
282 for(y = 0; y < YSize; ++y)
284 Map[x][y] = new lsquare(this, v2(x, y));
285 NodeMap[x][y] = new node(x, y, Map[x][y]);
288 for(x = 0; x < XSize; ++x)
289 for(y = 0; y < YSize; ++y)
290 Map[x][y]->CalculateNeighbourLSquares();
292 int Type = LevelScript->GetType() ? *LevelScript->GetType() : 0;
294 switch(Type)
296 case 0:
297 GenerateDungeon(Index);
298 break;
299 case DESERT:
300 GenerateDesert();
301 break;
302 case JUNGLE:
303 GenerateJungle();
304 break;
305 case STEPPE:
306 GenerateSteppe();
307 break;
308 case LEAFY_FOREST:
309 GenerateLeafyForest();
310 break;
311 case EVERGREEN_FOREST:
312 GenerateEvergreenForest();
313 break;
314 case TUNDRA:
315 GenerateTundra();
316 break;
317 case GLACIER:
318 GenerateGlacier();
319 break;
320 default:
321 ABORT("You are a terrorist. Please stop creating wterrains that are stupid.");
325 void level::ApplyLSquareScript(const squarescript* Script)
327 const interval* ScriptTimes = Script->GetTimes();
328 int Times = ScriptTimes ? ScriptTimes->Randomize() : 1;
330 for(int c = 0; c < Times; ++c)
332 v2 Pos;
334 if(Script->GetPosition()->GetRandom())
335 Pos = GetRandomSquare(0, Script->GetPosition()->GetFlags(), Script->GetPosition()->GetBorders());
336 else
337 Pos = Script->GetPosition()->GetVector();
339 Map[Pos.X][Pos.Y]->ApplyScript(Script, 0);
343 void level::AttachPos(int WhatX, int WhatY)
345 int PosX = 1 + RAND() % (XSize - 2);
346 int PosY = 1 + RAND() % (YSize - 2);
348 while(!(FlagMap[PosX][PosY] & PREFERRED))
350 PosX = 1 + RAND() % (XSize - 2);
351 PosY = 1 + RAND() % (YSize - 2);
354 FlagMap[WhatX][WhatY] &= ~FORBIDDEN;
355 FlagMap[WhatX][WhatY] |= PREFERRED;
356 GenerateTunnel(WhatX, WhatY, PosX, PosY, RAND() & 1);
357 FlagMap[WhatX][WhatY] |= FORBIDDEN;
358 FlagMap[WhatX][WhatY] &= ~PREFERRED;
361 void level::CreateItems(int Amount)
363 if(Amount)
365 sLong MinPrice = *LevelScript->GetItemMinPriceBase() + *LevelScript->GetItemMinPriceDelta() * Index;
367 for(int x = 0; x < Amount; ++x)
369 v2 Pos = GetRandomSquare();
370 item* Item = protosystem::BalancedCreateItem(MinPrice, MAX_PRICE, ANY_CATEGORY, 0, IGNORE_BROKEN_PRICE);
371 Item->CalculateEnchantment();
372 Map[Pos.X][Pos.Y]->Stack->AddItem(Item);
373 Item->SpecialGenerationHandler();
378 truth level::MakeRoom(const roomscript* RoomScript)
380 game::BusyAnimation();
381 v2 Pos = RoomScript->GetPos()->Randomize();
382 v2 Size = RoomScript->GetSize()->Randomize();
383 int x, y;
385 if(Pos.X + Size.X > XSize - 2)
386 return false;
388 if(Pos.Y + Size.Y > YSize - 2)
389 return false;
391 for(x = Pos.X - 1; x <= Pos.X + Size.X; ++x)
392 for(y = Pos.Y - 1; y <= Pos.Y + Size.Y; ++y)
393 if(FlagMap[x][y] & FORBIDDEN || FlagMap[x][y] & PREFERRED)
394 return false;
396 room* RoomClass = protocontainer<room>::GetProto(*RoomScript->GetType())->Spawn();
397 RoomClass->SetPos(Pos);
398 RoomClass->SetSize(Size);
399 RoomClass->SetFlags(*RoomScript->GetFlags());
400 AddRoom(RoomClass);
401 RoomClass->SetDivineMaster(*RoomScript->GetDivineMaster());
402 game::BusyAnimation();
403 std::vector<v2> OKForDoor, Inside, Border;
405 GenerateRectangularRoom(OKForDoor, Inside, Border, RoomScript, RoomClass, Pos, Size);
406 game::BusyAnimation();
408 if(*RoomScript->GenerateFountains() && !(RAND() % 10))
409 GetLSquare(Inside[RAND() % Inside.size()])->ChangeOLTerrain(fountain::Spawn());
411 // Ward, which gets generated as per fountain activation
412 if(*RoomScript->GenerateWards() && !(RAND() % 5))
413 GetLSquare(Inside[RAND() % Inside.size()])->ChangeOLTerrain(ward::Spawn());
415 if(*RoomScript->AltarPossible() && !(RAND() % 5))
417 int Owner = 1 + RAND() % GODS;
418 GetLSquare(Inside[RAND() % Inside.size()])->ChangeOLTerrain(altar::Spawn(Owner));
419 game::GetGod(Owner)->SignalRandomAltarGeneration(Inside);
420 RoomClass->SetDivineMaster(Owner);
423 if(*RoomScript->GenerateTunnel() && !Door.empty())
425 game::BusyAnimation();
426 v2 OutsideDoorPos = Door[RAND() % Door.size()]; // An other room
428 if(OKForDoor.empty())
429 ABORT("The Doors - You are strange.");
431 v2 InsideDoorPos = OKForDoor[RAND() % OKForDoor.size()]; // this door
432 olterrain* Door = RoomScript->GetDoorSquare()->GetOTerrain()->Instantiate(); //Bug! Wrong room!
434 if(Door && !(RAND() % 5) && *RoomScript->AllowLockedDoors())
436 if(*RoomScript->AllowBoobyTrappedDoors() && !(RAND() % 5))
437 Door->CreateBoobyTrap();
439 Door->Lock();
442 Map[OutsideDoorPos.X][OutsideDoorPos.Y]->ChangeLTerrain(RoomScript->GetDoorSquare()->GetGTerrain()->Instantiate(), Door);
443 Map[OutsideDoorPos.X][OutsideDoorPos.Y]->Clean();
444 FlagMap[OutsideDoorPos.X][OutsideDoorPos.Y] &= ~FORBIDDEN;
445 FlagMap[OutsideDoorPos.X][OutsideDoorPos.Y] |= PREFERRED;
446 FlagMap[InsideDoorPos.X][InsideDoorPos.Y] &= ~FORBIDDEN;
447 FlagMap[InsideDoorPos.X][InsideDoorPos.Y] |= PREFERRED;
448 Door = RoomScript->GetDoorSquare()->GetOTerrain()->Instantiate();
450 if(Door && !(RAND() % 5) && *RoomScript->AllowLockedDoors())
452 if(*RoomScript->AllowBoobyTrappedDoors() && !(RAND() % 5))
453 Door->CreateBoobyTrap();
455 Door->Lock();
458 Map[InsideDoorPos.X][InsideDoorPos.Y]->ChangeLTerrain(RoomScript->GetDoorSquare()->GetGTerrain()->Instantiate(), Door);
459 Map[InsideDoorPos.X][InsideDoorPos.Y]->Clean();
460 GenerateTunnel(InsideDoorPos.X, InsideDoorPos.Y, OutsideDoorPos.X, OutsideDoorPos.Y, RAND() & 1);
461 FlagMap[OutsideDoorPos.X][OutsideDoorPos.Y] |= FORBIDDEN;
462 FlagMap[OutsideDoorPos.X][OutsideDoorPos.Y] &= ~PREFERRED;
463 FlagMap[InsideDoorPos.X][InsideDoorPos.Y] |= FORBIDDEN;
464 FlagMap[InsideDoorPos.X][InsideDoorPos.Y] &= ~PREFERRED;
467 if(*RoomScript->GenerateDoor())
469 game::BusyAnimation();
470 v2 DoorPos;
472 if(OKForDoor.empty())
473 ABORT("The Doors - This thing has been broken.");
475 DoorPos = OKForDoor[RAND() % OKForDoor.size()];
476 Door.push_back(DoorPos);
478 if(!*RoomScript->GenerateTunnel())
480 Map[DoorPos.X][DoorPos.Y]->ChangeLTerrain(RoomScript->GetDoorSquare()->GetGTerrain()->Instantiate(), RoomScript->GetDoorSquare()->GetOTerrain()->Instantiate());
481 Map[DoorPos.X][DoorPos.Y]->Clean();
485 const charactercontentmap* CharacterMap = RoomScript->GetCharacterMap();
487 if(CharacterMap)
489 v2 CharPos(Pos + *CharacterMap->GetPos());
490 const contentscript<character>* CharacterScript;
492 for(int x = 0; x < CharacterMap->GetSize()->X; ++x)
494 game::BusyAnimation();
496 for(y = 0; y < CharacterMap->GetSize()->Y; ++y)
497 if(IsValidScript(CharacterScript = CharacterMap->GetContentScript(x, y)))
499 character* Char = CharacterScript->Instantiate();
500 Char->SetGenerationDanger(Difficulty);
502 if(!Char->GetTeam())
503 Char->SetTeam(game::GetTeam(*LevelScript->GetTeamDefault()));
505 if(CharacterScript->GetFlags() & IS_LEADER)
506 Char->GetTeam()->SetLeader(Char);
508 Char->PutTo(CharPos + v2(x, y));
509 Char->CreateHomeData();
511 if(CharacterScript->GetFlags() & IS_MASTER)
512 RoomClass->SetMasterID(Char->GetID());
517 const itemcontentmap* ItemMap = RoomScript->GetItemMap();
519 if(ItemMap)
521 v2 ItemPos(Pos + *ItemMap->GetPos());
522 const fearray<contentscript<item> >* ItemScript;
524 for(int x = 0; x < ItemMap->GetSize()->X; ++x)
526 game::BusyAnimation();
528 for(y = 0; y < ItemMap->GetSize()->Y; ++y)
529 if(IsValidScript(ItemScript = ItemMap->GetContentScript(x, y)))
530 for(uInt c1 = 0; c1 < ItemScript->Size; ++c1)
532 const interval* TimesPtr = ItemScript->Data[c1].GetTimes();
533 int Times = TimesPtr ? TimesPtr->Randomize() : 1;
535 for(int c2 = 0; c2 < Times; ++c2)
537 item* Item = ItemScript->Data[c1].Instantiate();
539 if(Item)
541 int SquarePosition = ItemScript->Data[c1].GetSquarePosition();
543 if(SquarePosition != CENTER)
544 Item->SignalSquarePositionChange(SquarePosition);
546 Map[ItemPos.X + x][ItemPos.Y + y]->GetStack()->AddItem(Item);
547 Item->SpecialGenerationHandler();
554 const glterraincontentmap* GTerrainMap = RoomScript->GetGTerrainMap();
556 if(GTerrainMap)
558 v2 GTerrainPos(Pos + *GTerrainMap->GetPos());
559 const contentscript<glterrain>* GTerrainScript;
561 for(int x = 0; x < GTerrainMap->GetSize()->X; ++x)
563 game::BusyAnimation();
565 for(y = 0; y < GTerrainMap->GetSize()->Y; ++y)
566 if(IsValidScript(GTerrainScript = GTerrainMap->GetContentScript(x, y)))
568 lsquare* Square = Map[GTerrainPos.X + x][GTerrainPos.Y + y];
569 Square->ChangeGLTerrain(GTerrainScript->Instantiate());
571 if (GTerrainScript->IsInside()) {
572 if (*GTerrainScript->IsInside()) Square->Flags |= INSIDE; else Square->Flags &= ~INSIDE;
578 const olterraincontentmap* OTerrainMap = RoomScript->GetOTerrainMap();
580 if(OTerrainMap)
582 v2 OTerrainPos(Pos + *OTerrainMap->GetPos());
583 const contentscript<olterrain>* OTerrainScript;
585 for(int x = 0; x < OTerrainMap->GetSize()->X; ++x)
587 game::BusyAnimation();
589 for(y = 0; y < OTerrainMap->GetSize()->Y; ++y)
590 if(IsValidScript(OTerrainScript = OTerrainMap->GetContentScript(x, y)))
592 olterrain* Terrain = OTerrainScript->Instantiate();
593 Map[OTerrainPos.X + x][OTerrainPos.Y + y]->ChangeOLTerrain(Terrain);
598 const std::list<squarescript> Square = RoomScript->GetSquare();
600 for(std::list<squarescript>::const_iterator i = Square.begin(); i != Square.end(); ++i)
602 game::BusyAnimation();
603 const squarescript* Script = &*i;
604 const interval* ScriptTimes = Script->GetTimes();
605 int Times = ScriptTimes ? ScriptTimes->Randomize() : 1;
607 for(int t = 0; t < Times; ++t)
609 v2 SquarePos;
611 if(Script->GetPosition()->GetRandom())
613 const rect* ScriptBorders = Script->GetPosition()->GetBorders();
614 rect Borders = ScriptBorders ? *ScriptBorders + Pos : rect(Pos, Pos + Size - v2(1, 1));
615 SquarePos = GetRandomSquare(0, Script->GetPosition()->GetFlags(), &Borders);
617 else
618 SquarePos = Pos + Script->GetPosition()->GetVector();
620 Map[SquarePos.X][SquarePos.Y]->ApplyScript(Script, RoomClass);
624 return true;
627 truth level::GenerateLanterns(int X, int Y, int SquarePos) const
629 if(!(RAND() % 7))
631 lantern* Lantern = lantern::Spawn();
632 Lantern->SignalSquarePositionChange(SquarePos);
633 Map[X][Y]->GetStack()->AddItem(Lantern);
634 return true;
637 return false;
640 void level::CreateRoomSquare(glterrain* GLTerrain, olterrain* OLTerrain, int X, int Y, int Room, int Flags) const
642 Map[X][Y]->ChangeLTerrain(GLTerrain, OLTerrain);
643 FlagMap[X][Y] |= FORBIDDEN;
644 Map[X][Y]->SetRoomIndex(Room);
645 Map[X][Y]->AddFlags(Flags);
648 void level::GenerateMonsters()
650 if(*LevelScript->GenerateMonsters()
651 && game::GetTeam(MONSTER_TEAM)->GetEnabledMembers() < IdealPopulation
652 && (MonsterGenerationInterval <= 1 || !RAND_N(MonsterGenerationInterval)))
654 GenerateNewMonsters(1);
655 ++MonsterGenerationInterval;
659 void level::Save(outputfile& SaveFile) const
661 area::Save(SaveFile);
662 SaveFile << Room << GlobalRainLiquid << GlobalRainSpeed;
664 for(int x = 0; x < XSize; ++x)
665 for(int y = 0; y < YSize; ++y)
666 Map[x][y]->Save(SaveFile);
668 SaveFile << Door << LevelMessage << IdealPopulation << MonsterGenerationInterval << Difficulty;
669 SaveFile << SunLightEmitation << SunLightDirection << AmbientLuminance << NightAmbientLuminance;
672 void level::Load(inputfile& SaveFile)
674 game::SetIsGenerating(true);
675 game::SetIsLoading(true);
676 area::Load(SaveFile);
677 Map = reinterpret_cast<lsquare***>(area::Map);
678 SaveFile >> Room;
679 GlobalRainLiquid = static_cast<liquid*>(ReadType<material*>(SaveFile));
680 SaveFile >> GlobalRainSpeed;
682 if(GlobalRainLiquid)
683 GlobalRainLiquid->SetVolumeNoSignals(0);
685 game::SetGlobalRainLiquid(GlobalRainLiquid);
686 game::SetGlobalRainSpeed(GlobalRainSpeed);
687 int x, y;
689 for(x = 0; x < XSize; ++x)
690 for(y = 0; y < YSize; ++y)
691 Map[x][y] = new lsquare(this, v2(x, y));
693 for(x = 0; x < XSize; ++x)
694 for(y = 0; y < YSize; ++y)
696 game::SetSquareInLoad(Map[x][y]);
697 Map[x][y]->Load(SaveFile);
698 Map[x][y]->CalculateNeighbourLSquares();
701 SaveFile >> Door >> LevelMessage >> IdealPopulation >> MonsterGenerationInterval >> Difficulty;
702 SaveFile >> SunLightEmitation >> SunLightDirection >> AmbientLuminance >> NightAmbientLuminance;
703 Alloc2D(NodeMap, XSize, YSize);
704 Alloc2D(WalkabilityMap, XSize, YSize);
706 for(x = 0; x < XSize; ++x)
707 for(y = 0; y < YSize; ++y)
709 if(!Map[x][y]->IsInside())
710 Map[x][y]->AmbientLuminance = AmbientLuminance;
712 NodeMap[x][y] = new node(x, y, Map[x][y]);
713 WalkabilityMap[x][y] = Map[x][y]->GetTheoreticalWalkability();
714 Map[x][y]->CalculateGroundBorderPartners();
715 Map[x][y]->CalculateOverBorderPartners();
718 SquareStack = new lsquare*[XSizeTimesYSize];
719 game::SetIsLoading(false);
720 game::SetIsGenerating(false);
723 void level::FiatLux()
725 for(int x = 0; x < XSize; ++x)
726 for(int y = 0; y < YSize; ++y)
728 Map[x][y]->CalculateEmitation();
729 Map[x][y]->Emitate();
730 Map[x][y]->CalculateLuminance();
733 CheckSunLight();
736 void level::GenerateNewMonsters(int HowMany, truth ConsiderPlayer)
738 v2 Pos;
740 for(int c1 = 0; c1 < HowMany; ++c1)
742 character* Char = protosystem::BalancedCreateMonster();
743 Char->CalculateEnchantments();
745 for(int c2 = 0; c2 < 30; ++c2)
747 Pos = GetRandomSquare(Char);
749 if(Pos == ERROR_V2)
750 break;
752 lsquare* Square = GetLSquare(Pos);
754 if((!Square->GetRoomIndex()
755 || !Square->GetRoom()->DontGenerateMonsters())
756 && (!ConsiderPlayer
757 || (Pos - PLAYER->GetPos()).GetManhattanLength() > 6))
758 break;
761 if(Pos != ERROR_V2)
763 Char->PutTo(Pos);
764 Char->SetGenerationDanger(Difficulty);
765 Char->SignalGeneration();
766 Char->SignalNaturalGeneration();
767 ivantime Time;
768 game::GetTime(Time);
769 int Modifier = Time.Day - EDIT_ATTRIBUTE_DAY_MIN;
771 if(Modifier > 0)
772 Char->EditAllAttributes(Modifier >> EDIT_ATTRIBUTE_DAY_SHIFT);
774 else
775 delete Char;
779 /* Example of the usage: GetRandomSquare() gives out a random walkable square */
781 v2 level::GetRandomSquare(ccharacter* Char, int Flags, const rect* Borders) const
783 rect LocalBorder;
785 if(Borders)
787 LocalBorder = *Borders;
788 Borders = &LocalBorder;
789 LimitRef(LocalBorder.X1, 0, XSize - 1);
790 LimitRef(LocalBorder.X2, 0, XSize - 1);
791 LimitRef(LocalBorder.Y1, 0, YSize - 1);
792 LimitRef(LocalBorder.Y2, 0, YSize - 1);
795 lsquare* LSquare;
797 for(int c = 0;; ++c)
799 if(c == 50)
800 Char = 0;
802 if(c == 500)
803 return ERROR_V2;
805 v2 Pos;
807 if(Borders)
809 Pos.X = Borders->X1 + RAND() % (Borders->X2 - Borders->X1 + 1);
810 Pos.Y = Borders->Y1 + RAND() % (Borders->Y2 - Borders->Y1 + 1);
812 else
814 Pos.X = 1 + RAND() % (XSize - 2);
815 Pos.Y = 1 + RAND() % (YSize - 2);
818 LSquare = Map[Pos.X][Pos.Y];
820 if(((Char ? Char->CanMoveOn(LSquare) : (LSquare->GetWalkability() & WALK)) != !(Flags & NOT_WALKABLE))
821 || ((Char ? Char->IsFreeForMe(LSquare) : !LSquare->GetCharacter()) != !(Flags & HAS_CHARACTER))
822 || (Flags & ATTACHABLE && FlagMap[Pos.X][Pos.Y] & FORBIDDEN)
823 || (Flags & HAS_NO_OTERRAIN && LSquare->GetOTerrain()))
824 continue;
826 int RoomFlags = Flags & (IN_ROOM|NOT_IN_ROOM);
828 if((RoomFlags == IN_ROOM && !LSquare->GetRoomIndex())
829 || (RoomFlags == NOT_IN_ROOM && LSquare->GetRoomIndex()))
830 continue;
832 return Pos;
836 void level::ParticleTrail(v2 StartPos, v2 EndPos)
838 if(StartPos.X != EndPos.X && StartPos.Y != EndPos.Y)
839 ABORT("666th rule of thermodynamics - Particles don't move the way you want them to move.");
842 truth level::IsOnGround() const
844 return *LevelScript->IsOnGround();
847 int level::GetLOSModifier() const
849 return *LevelScript->GetLOSModifier();
852 void level::AddRoom(room* NewRoom)
854 NewRoom->SetIndex(Room.size());
855 Room.push_back(NewRoom);
858 room* level::GetRoom(int I) const
860 if(!I)
861 ABORT("Access to room zero denied!");
863 return Room[I];
866 void level::Explosion(character* Terrorist, cfestring& DeathMsg, v2 Pos, int Strength, truth HurtNeutrals)
868 static int StrengthLimit[6] = { 500, 250, 100, 50, 25, 10 };
869 uInt c;
870 int Size = 6;
872 for(c = 0; c < 6; ++c)
873 if(Strength >= StrengthLimit[c])
875 Size = c;
876 break;
879 PlayerHurt.resize(PlayerHurt.size() + 1);
880 explosion* Exp = new explosion;
881 Exp->Terrorist = Terrorist;
882 Exp->DeathMsg = DeathMsg;
883 Exp->Pos = Pos;
884 Exp->ID = NextExplosionID++;
885 Exp->Strength = Strength;
886 Exp->RadiusSquare = (8 - Size) * (8 - Size);
887 Exp->Size = Size;
888 Exp->HurtNeutrals = HurtNeutrals;
889 ExplosionQueue.push_back(Exp);
891 if(ExplosionQueue.size() == 1)
893 uInt Explosions = 0;
895 while(Explosions != ExplosionQueue.size())
897 for(c = Explosions; c != ExplosionQueue.size(); c = TriggerExplosions(c));
898 uInt NewExplosions = c;
900 for(c = Explosions; c < NewExplosions; ++c)
901 if(PlayerHurt[c] && PLAYER->IsEnabled())
902 PLAYER->GetHitByExplosion(ExplosionQueue[c], ExplosionQueue[c]->Strength / ((PLAYER->GetPos() - ExplosionQueue[c]->Pos).GetLengthSquare() + 1));
904 Explosions = NewExplosions;
907 for(uInt c = 0; c < ExplosionQueue.size(); ++c)
908 delete ExplosionQueue[c];
910 ExplosionQueue.clear();
911 PlayerHurt.clear();
912 NextExplosionID = 1;
914 for(int x = 0; x < XSize; ++x)
915 for(int y = 0; y < YSize; ++y)
916 Map[x][y]->LastExplosionID = 0;
920 truth level::DrawExplosion(const explosion* Explosion) const
922 static v2 StrengthPicPos[7] = { v2(176, 176), v2(0, 144), v2(256, 32), v2(144, 32), v2(64, 32), v2(16, 32),v2(0, 32) };
923 v2 BPos = game::CalculateScreenCoordinates(Explosion->Pos) - v2((6 - Explosion->Size) << 4, (6 - Explosion->Size) << 4);
924 v2 SizeVect(16 + ((6 - Explosion->Size) << 5), 16 + ((6 - Explosion->Size) << 5));
925 v2 OldSizeVect = SizeVect;
926 v2 PicPos = StrengthPicPos[Explosion->Size];
928 if (BPos.X < 0) {
929 if (BPos.X + SizeVect.X <= 0) return false;
930 PicPos.X -= BPos.X;
931 SizeVect.X += BPos.X;
932 BPos.X = 0;
935 if (BPos.Y < 0) {
936 if (BPos.Y + SizeVect.Y <= 0) return false;
937 PicPos.Y -= BPos.Y;
938 SizeVect.Y += BPos.Y;
939 BPos.Y = 0;
942 if(BPos.X >= RES.X || BPos.Y >= RES.Y)
943 return false;
945 if(BPos.X + SizeVect.X > RES.X)
946 SizeVect.X = RES.X - BPos.X;
948 if(BPos.Y + SizeVect.Y > RES.Y)
949 SizeVect.Y = RES.Y - BPos.Y;
951 int Flags = RAND() & 7;
952 blitdata BlitData = { 0,
953 { PicPos.X, PicPos.Y },
954 { 0, 0 },
955 { SizeVect.X, SizeVect.Y },
956 { 0 },
957 TRANSPARENT_COLOR,
958 0 };
960 if(!Flags || SizeVect != OldSizeVect)
962 BlitData.Bitmap = DOUBLE_BUFFER;
963 BlitData.Dest = BPos;
964 BlitData.Luminance = ivanconfig::GetContrastLuminance();
965 igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData);
967 else
969 /* Cache these */
970 bitmap ExplosionPic(SizeVect);
971 ExplosionPic.ActivateFastFlag();
972 BlitData.Bitmap = &ExplosionPic;
973 BlitData.Flags = Flags;
974 igraph::GetSymbolGraphic()->NormalBlit(BlitData);
975 BlitData.Bitmap = DOUBLE_BUFFER;
976 BlitData.Dest = BPos;
977 BlitData.Src.X = BlitData.Src.Y = 0;
978 BlitData.Luminance = ivanconfig::GetContrastLuminance();
979 ExplosionPic.LuminanceMaskedBlit(BlitData);
982 return true;
985 struct explosioncontroller
987 static truth Handler(int x, int y)
989 lsquare* Square = Map[x][y];
990 Square->GetHitByExplosion(CurrentExplosion);
991 return Square->IsFlyable();
993 static lsquare*** Map;
994 static explosion* CurrentExplosion;
997 lsquare*** explosioncontroller::Map;
998 explosion* explosioncontroller::CurrentExplosion;
1000 int level::TriggerExplosions(int MinIndex)
1002 int LastExplosion = ExplosionQueue.size();
1003 int NotSeen = 0;
1004 int c;
1006 for(c = MinIndex; c < LastExplosion; ++c)
1008 int EmitChange = Min(50 + ExplosionQueue[c]->Strength, 255);
1009 GetLSquare(ExplosionQueue[c]->Pos)->SetTemporaryEmitation(MakeRGB24(EmitChange, EmitChange, EmitChange));
1011 if(!GetSquare(ExplosionQueue[c]->Pos)->CanBeSeenByPlayer(true))
1012 ++NotSeen;
1015 if (NotSeen) {
1016 if (NotSeen == 1) ADD_MESSAGE("You hear an explosion."); else ADD_MESSAGE("You hear explosions.");
1019 game::DrawEverythingNoBlit();
1020 truth Drawn = false;
1022 for(c = MinIndex; c < LastExplosion; ++c)
1024 if(DrawExplosion(ExplosionQueue[c]))
1025 Drawn = true;
1028 if(Drawn)
1030 graphics::BlitDBToScreen();
1031 game::GetCurrentArea()->SendNewDrawRequest();
1032 clock_t StartTime = clock();
1033 while(clock() - StartTime < 0.3 * CLOCKS_PER_SEC);
1036 for(c = MinIndex; c < LastExplosion; ++c)
1038 explosion* Explosion = ExplosionQueue[c];
1039 int Radius = 8 - Explosion->Size;
1040 game::SetPlayerWasHurtByExplosion(false);
1041 explosioncontroller::Map = Map;
1042 explosioncontroller::CurrentExplosion = Explosion;
1044 rect Rect;
1045 femath::CalculateEnvironmentRectangle(Rect, GetBorder(), Explosion->Pos, Radius);
1047 for(int x = Rect.X1; x <= Rect.X2; ++x)
1049 mapmath<explosioncontroller>::DoLine(Explosion->Pos.X, Explosion->Pos.Y, x, Rect.Y1);
1050 mapmath<explosioncontroller>::DoLine(Explosion->Pos.X, Explosion->Pos.Y, x, Rect.Y2);
1053 for(int y = Rect.Y1 + 1; y < Rect.Y2; ++y)
1055 mapmath<explosioncontroller>::DoLine(Explosion->Pos.X, Explosion->Pos.Y, Rect.X1, y);
1056 mapmath<explosioncontroller>::DoLine(Explosion->Pos.X, Explosion->Pos.Y, Rect.X2, y);
1059 PlayerHurt[c] = game::PlayerWasHurtByExplosion();
1061 if(GetLSquare(Explosion->Pos)->IsFlyable())
1062 GetLSquare(Explosion->Pos)->AddSmoke(gas::Spawn(SMOKE, 1000));
1065 for(c = MinIndex; c < LastExplosion; ++c)
1066 GetLSquare(ExplosionQueue[c]->Pos)->SetTemporaryEmitation(0);
1068 return LastExplosion;
1071 truth level::CollectCreatures(charactervector& CharacterArray, character* Leader, truth AllowHostiles)
1073 int c;
1075 if(!AllowHostiles)
1076 for(c = 0; c < game::GetTeams(); ++c)
1077 if(Leader->GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE)
1078 for(std::list<character*>::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i)
1079 if((*i)->IsEnabled() && Leader->CanBeSeenBy(*i)
1080 && Leader->SquareUnderCanBeSeenBy(*i, true) && (*i)->CanFollow())
1082 ADD_MESSAGE("You can't escape when there are hostile creatures nearby.");
1083 return false;
1086 truth TakeAll = true;
1088 for(c = 0; c < game::GetTeams(); ++c)
1089 if(game::GetTeam(c)->GetEnabledMembers()
1090 && Leader->GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE)
1092 TakeAll = false;
1093 break;
1096 for(c = 0; c < game::GetTeams(); ++c)
1097 if(game::GetTeam(c) == Leader->GetTeam() || Leader->GetTeam()->GetRelation(game::GetTeam(c)) == HOSTILE)
1098 for(std::list<character*>::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i)
1099 if((*i)->IsEnabled() && *i != Leader
1100 && (TakeAll
1101 || (Leader->CanBeSeenBy(*i)
1102 && Leader->SquareUnderCanBeSeenBy(*i, true)))
1103 && (*i)->CanFollow()
1104 && (*i)->GetCommandFlags() & FOLLOW_LEADER)
1106 if((*i)->GetAction() && (*i)->GetAction()->IsVoluntary())
1107 (*i)->GetAction()->Terminate(false);
1109 if(!(*i)->GetAction())
1111 ADD_MESSAGE("%s follows you.", (*i)->CHAR_NAME(DEFINITE));
1112 CharacterArray.push_back(*i);
1113 (*i)->Remove();
1117 return true;
1120 void level::Draw(truth AnimationDraw) const
1122 cint XMin = Max(game::GetCamera().X, 0);
1123 cint YMin = Max(game::GetCamera().Y, 0);
1124 cint XMax = Min(XSize, game::GetCamera().X + game::GetScreenXSize());
1125 cint YMax = Min(YSize, game::GetCamera().Y + game::GetScreenYSize());
1126 culong LOSTick = game::GetLOSTick();
1127 blitdata BlitData = { DOUBLE_BUFFER,
1128 { 0, 0 },
1129 { 0, 0 },
1130 { TILE_SIZE, TILE_SIZE },
1131 { 0 },
1132 TRANSPARENT_COLOR,
1133 ALLOW_ANIMATE|ALLOW_ALPHA };
1135 if(!game::GetSeeWholeMapCheatMode())
1137 if(!AnimationDraw)
1139 for(int x = XMin; x < XMax; ++x)
1141 BlitData.Dest = game::CalculateScreenCoordinates(v2(x, YMin));
1142 lsquare** SquarePtr = &Map[x][YMin];
1144 for(int y = YMin; y < YMax; ++y, ++SquarePtr, BlitData.Dest.Y += TILE_SIZE)
1146 const lsquare* Square = *SquarePtr;
1147 culong LastSeen = Square->LastSeen;
1149 if(LastSeen == LOSTick)
1150 Square->Draw(BlitData);
1151 else if(Square->Flags & STRONG_BIT || LastSeen == LOSTick - 2)
1152 Square->DrawMemorized(BlitData);
1156 else
1158 for(int x = XMin; x < XMax; ++x)
1160 BlitData.Dest = game::CalculateScreenCoordinates(v2(x, YMin));
1161 lsquare** SquarePtr = &Map[x][YMin];
1163 for(int y = YMin; y < YMax; ++y, ++SquarePtr, BlitData.Dest.Y += TILE_SIZE)
1165 const lsquare* Square = *SquarePtr;
1167 if(Square->LastSeen == LOSTick)
1168 Square->Draw(BlitData);
1169 else if(Square->Flags & STRONG_BIT)
1170 Square->DrawMemorized(BlitData);
1171 else
1173 ccharacter* C = Square->Character;
1175 if(C)
1177 if(C->CanBeSeenByPlayer())
1178 Square->DrawMemorizedCharacter(BlitData);
1179 else
1180 Square->DrawMemorized(BlitData);
1187 else
1189 for(int x = XMin; x < XMax; ++x)
1191 BlitData.Dest = game::CalculateScreenCoordinates(v2(x, YMin));
1192 lsquare** SquarePtr = &Map[x][YMin];
1194 for(int y = YMin; y < YMax; ++y, ++SquarePtr, BlitData.Dest.Y += TILE_SIZE)
1195 (*SquarePtr)->Draw(BlitData);
1200 v2 level::GetEntryPos(ccharacter* Char, int I) const
1202 if(I == FOUNTAIN)
1204 std::vector<v2> Fountains;
1205 for(int x = 0; x < XSize; ++x)
1206 for(int y = 0; y < YSize; ++y)
1208 if(GetLSquare(x,y)->GetOLTerrain() && GetLSquare(x,y)->GetOLTerrain()->IsFountainWithWater())
1209 Fountains.push_back(v2(x,y));
1212 if(Fountains.empty())
1213 return GetRandomSquare();
1215 return Fountains[RAND_N(Fountains.size())];
1217 std::map<int, v2>::const_iterator i = EntryMap.find(I);
1218 return i == EntryMap.end() ? GetRandomSquare(Char) : i->second;
1221 void level::GenerateRectangularRoom(std::vector<v2>& OKForDoor, std::vector<v2>& Inside, std::vector<v2>& Border, const roomscript* RoomScript, room* RoomClass, v2 Pos, v2 Size)
1223 const contentscript<glterrain>* GTerrain;
1224 const contentscript<olterrain>* OTerrain;
1226 if(*RoomScript->UseFillSquareWalls())
1228 GTerrain = LevelScript->GetFillSquare()->GetGTerrain();
1229 OTerrain = LevelScript->GetFillSquare()->GetOTerrain();
1231 else
1233 GTerrain = RoomScript->GetWallSquare()->GetGTerrain();
1234 OTerrain = RoomScript->GetWallSquare()->GetOTerrain();
1237 int Room = RoomClass->GetIndex();
1238 sLong Counter = 0;
1239 truth AllowLanterns = *RoomScript->GenerateLanterns();
1240 truth AllowWindows = *RoomScript->GenerateWindows();
1241 int x, y;
1242 int Shape = *RoomScript->GetShape();
1243 int Flags = (GTerrain->IsInside() ? *GTerrain->IsInside() : *RoomScript->IsInside()) ? INSIDE : 0;
1245 if(Shape == ROUND_CORNERS && (Size.X < 5 || Size.Y < 5)) /* No weird shapes this way. */
1246 Shape = RECTANGLE;
1248 for(x = Pos.X; x < Pos.X + Size.X; ++x, Counter += 2)
1250 if(Shape == ROUND_CORNERS)
1252 if(x == Pos.X)
1254 CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), x + 1, Pos.Y + 1, Room, Flags);
1255 CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), x + 1, Pos.Y + Size.Y - 2, Room, Flags);
1256 Border.push_back(v2(x + 1, Pos.Y + 1));
1257 Border.push_back(v2(x + 1, Pos.Y + Size.Y - 2));
1258 continue;
1260 else if(x == Pos.X + Size.X - 1)
1262 CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), x - 1, Pos.Y + 1, Room, Flags);
1263 CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), x - 1, Pos.Y + Size.Y - 2, Room, Flags);
1264 Border.push_back(v2(x - 1, Pos.Y + 1));
1265 Border.push_back(v2(x - 1, Pos.Y + Size.Y - 2));
1266 continue;
1270 CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), x, Pos.Y, Room, Flags);
1271 CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), x, Pos.Y + Size.Y - 1, Room, Flags);
1273 if((Shape == RECTANGLE && x != Pos.X && x != Pos.X + Size.X - 1)
1274 || (Shape == ROUND_CORNERS && x > Pos.X + 1 && x < Pos.X + Size.X - 2))
1276 OKForDoor.push_back(v2(x, Pos.Y));
1277 OKForDoor.push_back(v2(x, Pos.Y + Size.Y - 1));
1279 if((!AllowLanterns || !GenerateLanterns(x, Pos.Y, DOWN)) && AllowWindows)
1280 GenerateWindows(x, Pos.Y);
1282 if((!AllowLanterns || !GenerateLanterns(x, Pos.Y + Size.Y - 1, UP)) && AllowWindows)
1283 GenerateWindows(x, Pos.Y + Size.Y - 1);
1286 Border.push_back(v2(x, Pos.Y));
1287 Border.push_back(v2(x, Pos.Y + Size.Y - 1));
1290 game::BusyAnimation();
1292 for(y = Pos.Y + 1; y < Pos.Y + Size.Y - 1; ++y, Counter += 2)
1294 CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), Pos.X, y, Room, Flags);
1295 CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), Pos.X + Size.X - 1, y, Room, Flags);
1297 if(Shape == RECTANGLE || (Shape == ROUND_CORNERS && y != Pos.Y + 1 && y != Pos.Y + Size.Y - 2))
1299 OKForDoor.push_back(v2(Pos.X, y));
1300 OKForDoor.push_back(v2(Pos.X + Size.X - 1, y));
1302 if((!AllowLanterns || !GenerateLanterns(Pos.X, y, RIGHT)) && AllowWindows)
1303 GenerateWindows(Pos.X, y);
1305 if((!AllowLanterns || !GenerateLanterns(Pos.X + Size.X - 1, y, LEFT)) && AllowWindows)
1306 GenerateWindows(Pos.X + Size.X - 1, y);
1309 Border.push_back(v2(Pos.X, y));
1310 Border.push_back(v2(Pos.X + Size.X - 1, y));
1313 GTerrain = RoomScript->GetFloorSquare()->GetGTerrain();
1314 OTerrain = RoomScript->GetFloorSquare()->GetOTerrain();
1315 Counter = 0;
1316 Flags = (GTerrain->IsInside() ? *GTerrain->IsInside() : *RoomScript->IsInside()) ? INSIDE : 0;
1318 for(x = Pos.X + 1; x < Pos.X + Size.X - 1; ++x)
1319 for(y = Pos.Y + 1; y < Pos.Y + Size.Y - 1; ++y, ++Counter)
1321 /* if not in the corner */
1323 if(!(Shape == ROUND_CORNERS && (x == Pos.X + 1 || x == Pos.X + Size.X - 2) && (y == Pos.Y + 1 || y == Pos.Y + Size.Y - 2)))
1325 CreateRoomSquare(GTerrain->Instantiate(), OTerrain->Instantiate(), x, y, Room, Flags);
1326 Inside.push_back(v2(x,y));
1331 void level::Reveal()
1333 uLong Tick = game::GetLOSTick();
1335 for(int x = 0; x < XSize; ++x)
1336 for(int y = 0; y < YSize; ++y)
1337 Map[x][y]->Reveal(Tick);
1340 void level::ParticleBeam(beamdata& Beam)
1342 v2 CurrentPos = Beam.StartPos;
1344 if(Beam.Direction != YOURSELF)
1346 for(int Length = 0; Length < Beam.Range; ++Length)
1348 CurrentPos += game::GetMoveVector(Beam.Direction);
1350 if(!IsValidPos(CurrentPos))
1351 break;
1353 lsquare* CurrentSquare = GetLSquare(CurrentPos);
1355 if(!CurrentSquare->IsFlyable())
1357 (CurrentSquare->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam);
1358 break;
1360 else
1362 CurrentSquare->DrawParticles(Beam.BeamColor);
1364 if((CurrentSquare->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam))
1365 break;
1369 else
1371 lsquare* Where = GetLSquare(CurrentPos);
1372 Where->DrawParticles(Beam.BeamColor);
1373 (Where->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam);
1377 /* Note: You will most likely need some help from supernatural entities to comprehend this code. Sorry. */
1379 void level::LightningBeam(beamdata& Beam)
1381 v2 CurrentPos = Beam.StartPos;
1383 if(Beam.Direction == YOURSELF)
1385 lsquare* Where = GetLSquare(CurrentPos);
1387 for(int c = 0; c < 4; ++c)
1388 Where->DrawLightning(v2(8, 8), Beam.BeamColor, YOURSELF);
1390 (Where->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam);
1391 return;
1394 v2 StartPos;
1396 switch(Beam.Direction)
1398 case 0: StartPos = v2(15, 15); break;
1399 case 1: StartPos = v2(RAND() & 15, 15); break;
1400 case 2: StartPos = v2(0, 15); break;
1401 case 3: StartPos = v2(15, RAND() & 15); break;
1402 case 4: StartPos = v2(0, RAND() & 15); break;
1403 case 5: StartPos = v2(15, 0); break;
1404 case 6: StartPos = v2(RAND() & 15, 0); break;
1405 case 7: StartPos = v2(0, 0); break;
1406 default: StartPos = v2(0, 0); break;
1409 for(int Length = 0; Length < Beam.Range; ++Length)
1411 CurrentPos += game::GetMoveVector(Beam.Direction);
1413 if(!IsValidPos(CurrentPos))
1414 break;
1416 lsquare* CurrentSquare = GetLSquare(CurrentPos);
1418 if(!CurrentSquare->IsFlyable())
1420 if((CurrentSquare->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam))
1421 break;
1423 truth W1, W2;
1425 switch(Beam.Direction)
1427 case 0:
1428 W1 = GetLSquare(CurrentPos + v2(1, 0))->IsFlyable();
1429 W2 = GetLSquare(CurrentPos + v2(0, 1))->IsFlyable();
1431 if(W1 == W2)
1432 Beam.Direction = 7;
1433 else if(W1)
1435 ++CurrentPos.Y;
1436 Beam.Direction = 2;
1438 else
1440 ++CurrentPos.X;
1441 Beam.Direction = 5;
1444 break;
1445 case 1: Beam.Direction = 6; StartPos.Y = 0; break;
1446 case 2:
1447 W1 = GetLSquare(CurrentPos + v2(-1, 0))->IsFlyable();
1448 W2 = GetLSquare(CurrentPos + v2(0, 1))->IsFlyable();
1450 if(W1 == W2)
1451 Beam.Direction = 5;
1452 else if(W1)
1454 ++CurrentPos.Y;
1455 Beam.Direction = 0;
1457 else
1459 --CurrentPos.X;
1460 Beam.Direction = 7;
1463 break;
1464 case 3: Beam.Direction = 4; StartPos.X = 0; break;
1465 case 4: Beam.Direction = 3; StartPos.X = 15; break;
1466 case 5:
1467 W1 = GetLSquare(CurrentPos + v2(1, 0))->IsFlyable();
1468 W2 = GetLSquare(CurrentPos + v2(0, -1))->IsFlyable();
1470 if(W1 == W2)
1471 Beam.Direction = 2;
1472 else if(W1)
1474 --CurrentPos.Y;
1475 Beam.Direction = 7;
1477 else
1479 ++CurrentPos.X;
1480 Beam.Direction = 0;
1483 break;
1484 case 6: Beam.Direction = 1; StartPos.Y = 15; break;
1485 case 7:
1486 W1 = GetLSquare(CurrentPos + v2(-1, 0))->IsFlyable();
1487 W2 = GetLSquare(CurrentPos + v2(0, -1))->IsFlyable();
1489 if(W1 == W2)
1490 Beam.Direction = 0;
1491 else if(W1)
1493 --CurrentPos.Y;
1494 Beam.Direction = 5;
1496 else
1498 --CurrentPos.X;
1499 Beam.Direction = 2;
1502 break;
1505 switch(Beam.Direction)
1507 case 0: StartPos = v2(15, 15); break;
1508 case 2: StartPos = v2(0, 15); break;
1509 case 5: StartPos = v2(15, 0); break;
1510 case 7: StartPos = v2(0, 0); break;
1513 else
1515 StartPos = CurrentSquare->DrawLightning(StartPos, Beam.BeamColor, Beam.Direction);
1517 if((CurrentSquare->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam))
1518 break;
1523 void level::ShieldBeam(beamdata& Beam)
1525 v2 Pos[3];
1527 switch(Beam.Direction)
1529 case 0:
1530 Pos[0] = v2(-1, 0);
1531 Pos[1] = v2(-1, -1);
1532 Pos[2] = v2(0, -1);
1533 break;
1534 case 1:
1535 Pos[0] = v2(-1, -1);
1536 Pos[1] = v2(0, -1);
1537 Pos[2] = v2(1, -1);
1538 break;
1539 case 2:
1540 Pos[0] = v2(0, -1);
1541 Pos[1] = v2(1, -1);
1542 Pos[2] = v2(1, 0);
1543 break;
1544 case 3:
1545 Pos[0] = v2(-1, 1);
1546 Pos[1] = v2(-1, 0);
1547 Pos[2] = v2(-1, -1);
1548 break;
1549 case 4:
1550 Pos[0] = v2(1, -1);
1551 Pos[1] = v2(1, 0);
1552 Pos[2] = v2(1, 1);
1553 break;
1554 case 5:
1555 Pos[0] = v2(0, 1);
1556 Pos[1] = v2(-1, 1);
1557 Pos[2] = v2(-1, 0);
1558 break;
1559 case 6:
1560 Pos[0] = v2(1, 1);
1561 Pos[1] = v2(0, 1);
1562 Pos[2] = v2(-1, 1);
1563 break;
1564 case 7:
1565 Pos[0] = v2(1, 0);
1566 Pos[1] = v2(1, 1);
1567 Pos[2] = v2(0, 1);
1568 break;
1569 case 8:
1570 GetLSquare(Beam.StartPos)->DrawParticles(Beam.BeamColor);
1571 (GetLSquare(Beam.StartPos)->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam);
1572 return;
1573 default: return;
1576 for(int c = 0; c < 3; ++c)
1577 if(IsValidPos(Beam.StartPos + Pos[c]))
1579 GetLSquare(Beam.StartPos + Pos[c])->DrawParticles(Beam.BeamColor);
1580 (GetLSquare(Beam.StartPos + Pos[c])->*lsquare::GetBeamEffect(Beam.BeamEffect))(Beam);
1584 outputfile& operator<<(outputfile& SaveFile, const level* Level)
1586 Level->Save(SaveFile);
1587 return SaveFile;
1590 inputfile& operator>>(inputfile& SaveFile, level*& Level)
1592 Level = new level;
1593 Level->Load(SaveFile);
1594 return SaveFile;
1597 void (level::*Beam[BEAM_STYLES])(beamdata&) =
1599 &level::ParticleBeam,
1600 &level::LightningBeam,
1601 &level::ShieldBeam
1604 void (level::*level::GetBeam(int I))(beamdata&)
1606 return Beam[I];
1609 v2 level::FreeSquareSeeker(ccharacter* Char, v2 StartPos, v2 Prohibited, int MaxDistance, truth AllowStartPos) const
1611 int c;
1613 for(c = 0; c < 8; ++c)
1615 v2 Pos = StartPos + game::GetMoveVector(c);
1617 if(IsValidPos(Pos) && Char->CanMoveOn(GetLSquare(Pos)) && Char->IsFreeForMe(GetLSquare(Pos)) && Pos != Prohibited && (AllowStartPos || !Char->PlaceIsIllegal(Pos, Prohibited)))
1618 return Pos;
1621 if(MaxDistance)
1622 for(c = 0; c < 8; ++c)
1624 v2 Pos = StartPos + game::GetMoveVector(c);
1626 if(IsValidPos(Pos))
1628 if(Char->CanMoveOn(GetLSquare(Pos)) && Pos != Prohibited)
1630 Pos = FreeSquareSeeker(Char, Pos, Prohibited, MaxDistance - 1, AllowStartPos);
1632 if(Pos != ERROR_V2)
1633 return Pos;
1638 return ERROR_V2;
1641 /* Returns ERROR_V2 if no free square was found */
1643 v2 level::GetNearestFreeSquare(ccharacter* Char, v2 StartPos, truth AllowStartPos) const
1645 if(AllowStartPos && Char->CanMoveOn(GetLSquare(StartPos)) && Char->IsFreeForMe(GetLSquare(StartPos)))
1646 return StartPos;
1648 int c;
1650 for(c = 0; c < 8; ++c)
1652 v2 Pos = StartPos + game::GetMoveVector(c);
1654 if(IsValidPos(Pos) && Char->CanMoveOn(GetLSquare(Pos)) && Char->IsFreeForMe(GetLSquare(Pos)) && (AllowStartPos || !Char->PlaceIsIllegal(Pos, StartPos)))
1655 return Pos;
1658 for(int Dist = 0; Dist < 5; ++Dist)
1659 for(c = 0; c < 8; ++c)
1661 v2 Pos = StartPos + game::GetMoveVector(c);
1663 if(IsValidPos(Pos) && Char->CanMoveOn(GetLSquare(Pos)))
1665 Pos = FreeSquareSeeker(Char, Pos, StartPos, Dist, AllowStartPos);
1667 if(Pos != ERROR_V2)
1668 return Pos;
1672 return ERROR_V2;
1675 v2 level::GetFreeAdjacentSquare(ccharacter* Char, v2 StartPos, truth AllowCharacter) const
1677 int PossibleDir[8];
1678 int Index = 0;
1679 lsquare* Origo = GetLSquare(StartPos);
1681 for(int d = 0; d < 8; ++d)
1683 lsquare* Square = Origo->GetNeighbourLSquare(d);
1685 if(Square && Char->CanMoveOn(Square) && (AllowCharacter || Char->IsFreeForMe(Square)))
1686 PossibleDir[Index++] = d;
1689 return Index ? StartPos + game::GetMoveVector(PossibleDir[RAND() % Index]) : ERROR_V2;
1692 void (level::*level::GetBeamEffectVisualizer(int I))(const fearray<lsquare*>&, col16) const
1694 static void (level::*Visualizer[BEAM_STYLES])(const fearray<lsquare*>&, col16) const = { &level::ParticleVisualizer, &level::LightningVisualizer, &level::ParticleVisualizer };
1695 return Visualizer[I];
1698 void level::ParticleVisualizer(const fearray<lsquare*>& Stack, col16 BeamColor) const
1700 clock_t StartTime = clock();
1701 game::DrawEverythingNoBlit();
1703 for(fearray<lsquare*>::sizetype c = 0; c < Stack.Size; ++c)
1704 Stack[c]->DrawParticles(BeamColor, false);
1706 graphics::BlitDBToScreen();
1707 while(clock() - StartTime < 0.05 * CLOCKS_PER_SEC);
1710 void level::LightningVisualizer(const fearray<lsquare*>& Stack, col16 BeamColor) const
1712 clock_t StartTime = clock();
1713 game::DrawEverythingNoBlit();
1715 for(fearray<lsquare*>::sizetype c = 0; c < Stack.Size; ++c)
1716 Stack[c]->DrawLightning(v2(8, 8), BeamColor, YOURSELF, false);
1718 graphics::BlitDBToScreen();
1719 while(clock() - StartTime < 0.05 * CLOCKS_PER_SEC);
1722 truth level::PreProcessForBone()
1724 if(!*LevelScript->CanGenerateBone())
1725 return false;
1727 /* Gum solution */
1729 game::SetQuestMonstersFound(0);
1731 for(int x = 0; x < XSize; ++x)
1732 for(int y = 0; y < YSize; ++y)
1733 Map[x][y]->PreProcessForBone();
1735 int DungeonIndex = GetDungeon()->GetIndex();
1737 return !(DungeonIndex == ELPURI_CAVE && Index == IVAN_LEVEL && game::GetQuestMonstersFound() < 5)
1738 && (game::GetQuestMonstersFound()
1739 || ((DungeonIndex != UNDER_WATER_TUNNEL || Index != VESANA_LEVEL)
1740 && (DungeonIndex != ELPURI_CAVE || (Index != ENNER_BEAST_LEVEL && Index != DARK_LEVEL))));
1743 truth level::PostProcessForBone()
1745 game::SetTooGreatDangerFound(false);
1746 double DangerSum = 0;
1747 int Enemies = 0;
1749 for(int x = 0; x < XSize; ++x)
1750 for(int y = 0; y < YSize; ++y)
1751 Map[x][y]->PostProcessForBone(DangerSum, Enemies);
1753 if(game::TooGreatDangerFound() || (Enemies && DangerSum / Enemies > Difficulty * 10))
1754 return false;
1756 return true;
1759 void level::FinalProcessForBone()
1761 for(int x = 0; x < XSize; ++x)
1762 for(int y = 0; y < YSize; ++y)
1763 Map[x][y]->FinalProcessForBone();
1765 for(uInt c = 1; c < Room.size(); ++c)
1766 Room[c]->FinalProcessForBone();
1769 void level::GenerateDungeon(int Index)
1771 cfestring* Msg = LevelScript->GetLevelMessage();
1773 if(Msg)
1774 LevelMessage = *Msg;
1776 if(*LevelScript->GenerateMonsters())
1778 MonsterGenerationInterval = *LevelScript->GetMonsterGenerationIntervalBase() + *LevelScript->GetMonsterGenerationIntervalDelta() * Index;
1779 IdealPopulation = *LevelScript->GetMonsterAmountBase() + *LevelScript->GetMonsterAmountDelta() * Index;
1782 Difficulty = 0.001 * (*LevelScript->GetDifficultyBase() + *LevelScript->GetDifficultyDelta() * Index);
1783 EnchantmentMinusChance = *LevelScript->GetEnchantmentMinusChanceBase() + *LevelScript->GetEnchantmentMinusChanceDelta() * Index;
1784 EnchantmentPlusChance = *LevelScript->GetEnchantmentPlusChanceBase() + *LevelScript->GetEnchantmentPlusChanceDelta() * Index;
1785 const contentscript<glterrain>* GTerrain = LevelScript->GetFillSquare()->GetGTerrain();
1786 const contentscript<olterrain>* OTerrain = LevelScript->GetFillSquare()->GetOTerrain();
1787 sLong Counter = 0;
1788 int x;
1789 game::BusyAnimation();
1791 for(x = 0; x < XSize; ++x)
1792 for(int y = 0; y < YSize; ++y, ++Counter)
1793 Map[x][y]->SetLTerrain(GTerrain->Instantiate(), OTerrain->Instantiate());
1795 uInt c;
1796 uInt Rooms = LevelScript->GetRooms()->Randomize();
1797 const std::list<roomscript>& RoomList = LevelScript->GetRoom();
1798 std::list<roomscript>::const_iterator Iterator = RoomList.begin();
1800 for(c = 0; c < Rooms; ++c)
1802 game::BusyAnimation();
1804 if(c < RoomList.size())
1806 int i;
1808 for(i = 0; i < 1000; ++i)
1809 if(MakeRoom(&*Iterator))
1810 break;
1812 if(i == 1000)
1813 ABORT("Failed to place special room #%d!", c);
1815 ++Iterator;
1817 else
1819 const roomscript* RoomScript = LevelScript->GetRoomDefault();
1821 for(int i = 0; i < 50; ++i)
1822 if(MakeRoom(RoomScript))
1823 break;
1827 game::BusyAnimation();
1829 if(!*LevelScript->IgnoreDefaultSpecialSquares())
1831 /* Gum solution */
1833 const levelscript* LevelBase = static_cast<const levelscript*>(LevelScript->GetBase());
1835 if(LevelBase)
1837 const std::list<squarescript>& Square = LevelBase->GetSquare();
1839 for(std::list<squarescript>::const_iterator i = Square.begin(); i != Square.end(); ++i)
1841 game::BusyAnimation();
1842 ApplyLSquareScript(&*i);
1847 const std::list<squarescript>& Square = LevelScript->GetSquare();
1849 for(std::list<squarescript>::const_iterator i = Square.begin(); i != Square.end(); ++i)
1851 game::BusyAnimation();
1852 ApplyLSquareScript(&*i);
1855 for(c = 0; c < AttachQueue.size(); ++c)
1856 AttachPos(AttachQueue[c].X, AttachQueue[c].Y);
1858 for(x = 0; x < XSize; ++x)
1859 for(int y = 0; y < YSize; ++y)
1861 Map[x][y]->CalculateGroundBorderPartners();
1862 Map[x][y]->CalculateOverBorderPartners();
1865 AttachQueue.clear();
1866 CreateItems(LevelScript->GetItems()->Randomize());
1869 void level::GenerateJungle () {
1870 int x, y;
1872 for (x = 0; x < XSize; ++x) {
1873 for (y = 0; y < YSize; ++y) {
1874 Map[x][y] = new lsquare(this, v2(x, y));
1875 Map[x][y]->SetLTerrain(solidterrain::Spawn(GRASS_TERRAIN), 0);
1879 for (;;) {
1880 CreateTunnelNetwork(1, 4, 20, 120, v2(0, YSize/2));
1881 CreateTunnelNetwork(1, 4, 20, 120, v2(XSize-1, YSize/2));
1883 for (int c = 0; c < 25; ++c) {
1884 v2 StartPos;
1886 switch(RAND_N(5)) {
1887 case 0: StartPos = v2(RAND_N(XSize), 0); break;
1888 case 1: StartPos = v2(RAND_N(XSize), YSize-1); break;
1889 case 2: StartPos = v2(0, RAND_N(YSize)); break;
1890 case 3: StartPos = v2(XSize-1, RAND_N(YSize)); break;
1891 case 4: StartPos = v2(RAND_N(XSize), RAND_N(YSize)); break;
1892 default: StartPos = v2(0, 0); break; /* k8: shut up the compiler */
1894 CreateTunnelNetwork(1,4,20, 120, StartPos);
1897 for (x = 0; x < XSize; ++x) {
1898 game::BusyAnimation();
1899 for (y = 0; y < YSize; ++y) {
1900 if(FlagMap[x][y] != PREFERRED) Map[x][y]->ChangeOLTerrain(wall::Spawn(BRICK_PROPAGANDA));
1901 else if(RAND_2) Map[x][y]->ChangeOLTerrain(decoration::Spawn(PALM));
1908 void level::CreateTunnelNetwork(int MinLength, int MaxLength, int MinNodes, int MaxNodes, v2 StartPos)
1910 v2 Pos = StartPos, Direction;
1911 int Length;
1912 game::BusyAnimation();
1913 FlagMap[Pos.X][Pos.Y] = PREFERRED;
1915 for(int c1 = 0; c1 < MaxNodes; ++c1)
1917 Direction = game::GetBasicMoveVector(RAND() % 4);
1918 Length = MinLength + RAND_N(MaxLength - MinLength + 1);
1920 for(int c2 = 0; c2 < Length; ++c2)
1922 if(IsValidPos(Direction + Pos))
1924 Pos += Direction;
1925 FlagMap[Pos.X][Pos.Y] = PREFERRED;
1927 else
1929 if(c1 >= MinNodes)
1930 return;
1932 break;
1938 void level::GenerateDesert()
1940 for(int x = 0; x < XSize; ++x)
1941 for(int y = 0; y < YSize; ++y)
1943 Map[x][y] = new lsquare(this, v2(x, y));
1944 Map[x][y]->SetLTerrain(solidterrain::Spawn(SAND_TERRAIN), 0);
1947 game::BusyAnimation();
1948 int AmountOfCactuses = RAND_N(10);
1949 int c;
1951 for(c = 0; c < AmountOfCactuses; ++c)
1952 Map[RAND_N(XSize)][RAND_N(YSize)]->ChangeOLTerrain(decoration::Spawn(CACTUS));
1954 int AmountOfBoulders = RAND_N(10);
1956 for(c = 0; c < AmountOfBoulders; ++c)
1957 Map[RAND_N(XSize)][RAND_N(YSize)]->ChangeOLTerrain(boulder::Spawn(1 + RAND_2));
1960 void level::GenerateSteppe()
1962 for(int x = 0; x < XSize; ++x)
1963 for(int y = 0; y < YSize; ++y)
1965 Map[x][y] = new lsquare(this, v2(x, y));
1966 Map[x][y]->SetLTerrain(solidterrain::Spawn(GRASS_TERRAIN), 0);
1969 game::BusyAnimation();
1970 int c;
1972 int AmountOfBoulders = RAND_N(20) + 5;
1974 for(c = 0; c < AmountOfBoulders; ++c)
1975 Map[RAND_N(XSize)][RAND_N(YSize)]->ChangeOLTerrain(boulder::Spawn(1 + RAND_2));
1978 void level::GenerateLeafyForest()
1980 for(int x = 0; x < XSize; ++x)
1981 for(int y = 0; y < YSize; ++y)
1983 Map[x][y] = new lsquare(this, v2(x, y));
1984 olterrain* OLTerrain;
1986 switch(RAND_4)
1988 case 0:
1989 if(RAND_8)
1990 OLTerrain = decoration::Spawn(OAK);
1991 else
1992 OLTerrain = decoration::Spawn(TEAK);
1993 break;
1994 case 1:
1995 OLTerrain = decoration::Spawn(BIRCH);
1996 break;
1997 case 2:
1998 OLTerrain = 0;
1999 if(!RAND_4)
2000 OLTerrain = boulder::Spawn(1 + RAND_2);
2002 if(!RAND_4)
2003 OLTerrain = boulder::Spawn(3);
2004 break;
2005 default:
2006 OLTerrain = 0;
2009 Map[x][y]->SetLTerrain(solidterrain::Spawn(GRASS_TERRAIN), OLTerrain);
2013 void level::GenerateEvergreenForest()
2015 for(int x = 0; x < XSize; ++x)
2016 for(int y = 0; y < YSize; ++y)
2018 Map[x][y] = new lsquare(this, v2(x, y));
2019 olterrain* OLTerrain = 0;
2021 switch(RAND_4)
2023 case 0:
2024 if(RAND_2)
2025 OLTerrain = decoration::Spawn(PINE);
2026 break;
2027 case 1:
2028 OLTerrain = decoration::Spawn(FIR);
2029 break;
2030 case 2:
2031 if(!RAND_4)
2032 OLTerrain = boulder::Spawn(1 + RAND_2);
2034 if(!RAND_4)
2035 OLTerrain = boulder::Spawn(3);
2036 break;
2039 Map[x][y]->SetLTerrain(solidterrain::Spawn(GRASS_TERRAIN), OLTerrain);
2043 void level::GenerateTundra()
2045 for(int x = 0; x < XSize; ++x)
2046 for(int y = 0; y < YSize; ++y)
2048 Map[x][y] = new lsquare(this, v2(x, y));
2049 Map[x][y]->SetLTerrain(solidterrain::Spawn(SNOW_TERRAIN), 0);
2052 game::BusyAnimation();
2053 int c;
2054 int AmountOfBoulders = RAND_N(20) + 8;
2056 for(c = 0; c < AmountOfBoulders; ++c)
2057 Map[RAND_N(XSize)][RAND_N(YSize)]->ChangeOLTerrain(boulder::Spawn(SNOW_BOULDER));
2059 int AmountOfDwarfBirches = RAND_N(10);
2061 for(c = 0; c < AmountOfDwarfBirches; ++c)
2062 Map[RAND_N(XSize)][RAND_N(YSize)]->ChangeOLTerrain(decoration::Spawn(DWARF_BIRCH));
2066 void level::GenerateGlacier () {
2067 int x, y;
2069 for (x = 0; x < XSize; ++x) {
2070 for (y = 0; y < YSize; ++y) {
2071 Map[x][y] = new lsquare(this, v2(x, y));
2072 Map[x][y]->SetLTerrain(solidterrain::Spawn(SNOW_TERRAIN), 0);
2076 int AmountOfBoulders = RAND_N(20)+5;
2078 for (int c = 0; c < AmountOfBoulders; ++c)
2079 Map[RAND_N(XSize)][RAND_N(YSize)]->ChangeOLTerrain(boulder::Spawn(SNOW_BOULDER));
2081 for (;;) {
2082 CreateTunnelNetwork(1,4,20, 120, v2(0,YSize/2));
2083 CreateTunnelNetwork(1,4,20, 120, v2(XSize-1,YSize/2));
2085 for (int c = 0; c < 20; ++c) {
2086 v2 StartPos;
2087 switch(RAND_N(5)) {
2088 case 0: StartPos = v2(RAND_N(XSize), 0); break;
2089 case 1: StartPos = v2(RAND_N(XSize), YSize-1); break;
2090 case 2: StartPos = v2(0, RAND_N(YSize)); break;
2091 case 3: StartPos = v2(XSize-1, RAND_N(YSize)); break;
2092 case 4: StartPos = v2(RAND_N(XSize), RAND_N(YSize)); break;
2093 default: StartPos = v2(0, 0); break; /* k8: shut up the compiler */
2095 CreateTunnelNetwork(1,4,20, 120, StartPos);
2098 for (x = 0; x < XSize; ++x)
2099 for (y = 0; y < YSize; ++y)
2100 if(FlagMap[x][y] != PREFERRED) FlagMap[x][y] |= RAND_2?ICE_TERRAIN:STONE_TERRAIN;
2102 for (x = 0; x < XSize; ++x) {
2103 game::BusyAnimation();
2104 for (y = 0; y < YSize; ++y) {
2105 if (!(FlagMap[x][y] & PREFERRED)) {
2106 int SquaresAround = 0;
2107 int IceAround = 0;
2108 for (int d = 0; d < 8; ++d) {
2109 v2 Pos = v2(x,y) + game::GetMoveVector(d);
2110 if (IsValidPos(Pos) && !(FlagMap[Pos.X][Pos.Y] & PREFERRED)) {
2111 ++SquaresAround;
2112 if(FlagMap[Pos.X][Pos.Y] & ICE_TERRAIN) ++IceAround;
2115 if (IceAround > SquaresAround/2) FlagMap[x][y] = ICE_TERRAIN;
2116 else FlagMap[x][y] = STONE_TERRAIN;
2121 for (x = 0; x < XSize; ++x) {
2122 for (y = 0; y < YSize; ++y) {
2123 if (!(FlagMap[x][y] & PREFERRED)) {
2124 if(FlagMap[x][y] & ICE_TERRAIN) GetLSquare(x,y)->ChangeOLTerrain(wall::Spawn(ICE_WALL));
2125 else GetLSquare(x,y)->ChangeOLTerrain(wall::Spawn(STONE_WALL));
2130 break; // Doesn't yet check path in any way
2135 bool nodepointerstorer::operator<(const nodepointerstorer& N) const
2137 /* In the non-euclidean geometry of IVAN, certain very curved paths are as long as straight ones.
2138 However, they are so ugly that it is best to prefer routes with as few diagonal moves as
2139 possible without lengthening the travel. */
2141 if(Node->TotalDistanceEstimate != N.Node->TotalDistanceEstimate)
2142 return Node->TotalDistanceEstimate > N.Node->TotalDistanceEstimate;
2143 else
2144 return Node->Diagonals > N.Node->Diagonals;
2147 void node::CalculateNextNodes()
2149 static int TryOrder[8] = { 1, 3, 4, 6, 0, 2, 5, 7 };
2151 for(int d = 0; d < 8; ++d)
2153 v2 NodePos = Pos + game::GetMoveVector(TryOrder[d]);
2155 if(NodePos.X >= 0 && NodePos.Y >= 0 && NodePos.X < XSize && NodePos.Y < YSize)
2157 node* Node = NodeMap[NodePos.X][NodePos.Y];
2159 if(!Node->Processed && ((!SpecialMover && RequiredWalkability & WalkabilityMap[NodePos.X][NodePos.Y]) || (SpecialMover && SpecialMover->CanTheoreticallyMoveOn(Node->Square)) || NodePos == To))
2161 Node->Processed = true;
2162 Node->Distance = Distance + 1;
2163 Node->Diagonals = Diagonals;
2165 if(d >= 4)
2166 ++Node->Diagonals;
2168 Node->Last = this;
2170 /* We use the heuristic max(abs(distance.x), abs(distance.y)) here,
2171 which is exact in the current geometry if the path is open */
2173 sLong Remaining = To.X - NodePos.X;
2175 if(Remaining < NodePos.X - To.X)
2176 Remaining = NodePos.X - To.X;
2178 if(Remaining < NodePos.Y - To.Y)
2179 Remaining = NodePos.Y - To.Y;
2181 if(Remaining < To.Y - NodePos.Y)
2182 Remaining = To.Y - NodePos.Y;
2184 Node->Remaining = Remaining;
2185 Node->TotalDistanceEstimate = Node->Distance + Node->Remaining;
2186 NodeQueue->push(nodepointerstorer(Node));
2192 /* Finds the shortest (but possibly not the shortest-looking) path between From and To
2193 if such exists. Returns a pointer to the node associated with the last square or zero if
2194 a route can't be found. Calling FindRoute again may invalidate the node, so you must
2195 store the path in another format ASAP. */
2197 node* level::FindRoute(v2 From, v2 To, const std::set<v2>& Illegal, int RequiredWalkability, ccharacter* SpecialMover)
2199 node::NodeMap = NodeMap;
2200 node::RequiredWalkability = RequiredWalkability;
2201 node::SpecialMover = SpecialMover;
2202 node::To = To;
2203 node::WalkabilityMap = WalkabilityMap;
2204 node::XSize = XSize;
2205 node::YSize = YSize;
2207 if(!Illegal.empty() && Illegal.find(To) != Illegal.end())
2208 return 0;
2210 for(int x = 0; x < XSize; ++x)
2211 for(int y = 0; y < YSize; ++y)
2212 NodeMap[x][y]->Processed = false;
2214 node* Node = NodeMap[From.X][From.Y];
2215 Node->Last = 0;
2216 Node->Processed = true;
2217 Node->Distance = 0;
2218 Node->Diagonals = 0;
2219 nodequeue NodeQueue;
2220 NodeQueue.push(nodepointerstorer(Node));
2221 node::NodeQueue = &NodeQueue;
2223 while(!NodeQueue.empty())
2225 Node = NodeQueue.top().Node;
2226 NodeQueue.pop();
2228 if(Node->Pos == To)
2229 return Node;
2231 if(Illegal.empty() || Illegal.find(Node->Pos) == Illegal.end())
2232 Node->CalculateNextNodes();
2235 return 0;
2238 /* All items on ground are moved to the IVector and all characters to CVector */
2240 void level::CollectEverything(itemvector& IVector, charactervector& CVector)
2242 for(int x = 0; x < XSize; ++x)
2243 for(int y = 0; y < YSize; ++y)
2245 lsquare* LS = Map[x][y];
2246 LS->GetStack()->MoveItemsTo(IVector, CENTER);
2247 character* C = LS->GetCharacter();
2249 if(C && !C->IsPlayer())
2251 C->Remove();
2252 CVector.push_back(C);
2257 void level::CreateGlobalRain(liquid* Liquid, v2 Speed)
2259 GlobalRainLiquid = Liquid;
2260 GlobalRainSpeed = Speed;
2262 for(int x = 0; x < XSize; ++x)
2263 for(int y = 0; y < YSize; ++y)
2264 if(!Map[x][y]->IsInside())
2265 Map[x][y]->AddRain(Liquid, Speed, MONSTER_TEAM, false);
2269 void level::CheckSunLight () {
2270 if (Index == 0 && GetDungeon()->GetIndex() == NEW_ATTNAM) {
2271 double Cos = cos(FPI*(game::GetTick()%48000)/24000.0);
2273 if (Cos > 0.01) {
2274 int E = int(100+Cos*30);
2276 SunLightEmitation = MakeRGB24(E, E, E);
2277 AmbientLuminance = MakeRGB24(E-6, E-6, E-6);
2278 } else {
2279 SunLightEmitation = 0;
2280 AmbientLuminance = NightAmbientLuminance;
2282 } else if (Index == 0 && GetDungeon()->GetIndex() == ATTNAM) {
2283 double Cos = cos(FPI*(game::GetTick()%48000)/24000.0);
2285 if (Cos > 0.41) {
2286 int E = int(100+(Cos-0.40)*40);
2288 SunLightEmitation = MakeRGB24(E, E, E);
2289 AmbientLuminance = MakeRGB24(E-8, E-8, E-8);
2290 } else {
2291 SunLightEmitation = 0;
2292 AmbientLuminance = NightAmbientLuminance;
2294 } else if (Index == 0 && GetDungeon()->GetIndex() == MUNTUO) {
2295 double Cos = cos(FPI*(game::GetTick()%48000)/24000.0);
2297 if (Cos > 0.21) {
2298 int E = int(100+(Cos-0.20)*30);
2300 SunLightEmitation = MakeRGB24(E, E, E);
2301 AmbientLuminance = MakeRGB24(E-4, E-4, E-4);
2302 } else {
2303 SunLightEmitation = 0;
2304 AmbientLuminance = NightAmbientLuminance;
2306 } else {
2307 return;
2310 SunLightDirection = game::GetSunLightDirectionVector();
2311 ChangeSunLight();
2315 void level::ChangeSunLight()
2317 truth SunSet = game::IsDark(SunLightEmitation);
2318 uLong c;
2320 for(c = 0; c < XSizeTimesYSize; ++c)
2321 Map[0][c]->RemoveSunLight();
2323 if(!SunSet)
2324 EmitSunBeams();
2326 for(c = 0; c < XSizeTimesYSize; ++c)
2328 lsquare* Square = Map[0][c];
2330 if(Square->Flags & IS_TRANSPARENT)
2331 Square->CalculateSunLightLuminance(EMITTER_SQUARE_PART_BITS);
2333 if(!Square->IsInside())
2334 Square->AmbientLuminance = AmbientLuminance;
2336 Square->SendSunLightSignals();
2339 for(c = 0; c < XSizeTimesYSize; ++c)
2340 Map[0][c]->CheckIfIsSecondarySunLightEmitter();
2343 void level::InitSquarePartEmitationTicks()
2345 for(int x = 0; x < XSize; ++x)
2346 for(int y = 0; y < YSize; ++y)
2347 Map[x][y]->SquarePartEmitationTick = 0;
2350 truth level::GenerateWindows(int X, int Y) const
2352 olterrain* Terrain = Map[X][Y]->GetOLTerrain();
2354 if(Terrain && Terrain->CreateWindowConfigurations() && !(RAND() % 6))
2356 Terrain->SetConfig(Terrain->GetConfig() | WINDOW);
2357 Map[X][Y]->CalculateIsTransparent();
2358 return true;
2361 return false;
2364 struct sunbeamcontroller : public stackcontroller
2366 static truth Handler(int, int);
2367 static void ProcessStack();
2368 static uLong ID;
2369 static int SunLightBlockHeight;
2370 static v2 SunLightBlockPos;
2371 static truth ReSunEmitation;
2374 uLong sunbeamcontroller::ID;
2375 int sunbeamcontroller::SunLightBlockHeight;
2376 v2 sunbeamcontroller::SunLightBlockPos;
2377 truth sunbeamcontroller::ReSunEmitation;
2379 void level::ForceEmitterNoxify(const emittervector& Emitter) const
2381 for(emittervector::const_iterator i = Emitter.begin(); i != Emitter.end(); ++i)
2383 uLong ID = i->ID;
2384 lsquare* Square = GetLSquare(ExtractPosFromEmitterID(ID));
2386 if(ID & SECONDARY_SUN_LIGHT)
2387 Square->Noxify(Square->SecondarySunLightEmitation, SECONDARY_SUN_LIGHT);
2388 else
2389 Square->Noxify(Square->Emitation);
2393 void level::ForceEmitterEmitation(const emittervector& Emitter, const sunemittervector& SunEmitter, uLong IDFlags) const
2395 for(emittervector::const_iterator i = Emitter.begin(); i != Emitter.end(); ++i)
2397 uLong ID = i->ID;
2398 lsquare* Square = GetLSquare(ExtractPosFromEmitterID(ID));
2400 if(ID & SECONDARY_SUN_LIGHT)
2401 Square->Emitate(Square->SecondarySunLightEmitation, SECONDARY_SUN_LIGHT|IDFlags);
2402 else
2403 Square->Emitate(Square->Emitation, IDFlags);
2407 stackcontroller::Map = Map;
2408 stackcontroller::Stack = SquareStack;
2409 stackcontroller::StackIndex = 0;
2410 stackcontroller::LevelXSize = XSize;
2411 stackcontroller::LevelYSize = YSize;
2412 sunbeamcontroller::ReSunEmitation = true;
2414 for(sunemittervector::const_iterator i = SunEmitter.begin(); i != SunEmitter.end(); ++i)
2416 uLong ID = (*i & ~(EMITTER_SHADOW_BITS|EMITTER_SQUARE_PART_BITS)) | RE_SUN_EMITATED, SourceFlags;
2417 int X, Y;
2419 if(ID & ID_X_COORDINATE)
2421 X = (ID & EMITTER_IDENTIFIER_BITS) - (XSize << 3);
2422 Y = ID & ID_BEGIN ? -1 : YSize;
2423 SourceFlags = ID & ID_BEGIN ? SP_BOTTOM : SP_TOP;
2425 else
2427 X = ID & ID_BEGIN ? -1 : XSize;
2428 Y = (ID & EMITTER_IDENTIFIER_BITS) - (YSize << 3);
2429 SourceFlags = ID & ID_BEGIN ? SP_RIGHT : SP_LEFT;
2432 EmitSunBeam(v2(X, Y), ID, SourceFlags);
2435 sunbeamcontroller::ProcessStack();
2439 struct loscontroller : public tickcontroller, public stackcontroller
2441 static truth Handler(int x, int y)
2443 lsquare* Square = Map[x >> 1][y >> 1];
2444 culong SquareFlags = Square->Flags;
2446 if(SquareFlags & PERFECTLY_QUADRI_HANDLED)
2447 return true;
2449 if(!(SquareFlags & IN_SQUARE_STACK))
2451 Square->Flags |= IN_SQUARE_STACK;
2452 Stack[StackIndex++] = Square;
2455 if(SquareFlags & IS_TRANSPARENT)
2457 Square->Flags |= PERFECTLY_QUADRI_HANDLED;
2458 return true;
2461 cint SquarePartIndex = (x & 1) + ((y & 1) << 1);
2462 Square->SquarePartLastSeen = (Square->SquarePartLastSeen & ~SquarePartTickMask[SquarePartIndex]) | ShiftedTick[SquarePartIndex];
2463 return false;
2465 static uLong& GetTickReference(int X, int Y)
2467 return Map[X][Y]->SquarePartLastSeen;
2469 static void ProcessStack()
2471 for(sLong c = 0; c < StackIndex; ++c)
2472 Stack[c]->SignalSeen(Tick);
2476 void level::UpdateLOS()
2478 game::RemoveLOSUpdateRequest();
2479 stackcontroller::Map = Map;
2480 stackcontroller::Stack = SquareStack;
2481 stackcontroller::StackIndex = 0;
2482 tickcontroller::Tick = game::IncreaseLOSTick();
2483 tickcontroller::PrepareShiftedTick();
2484 int Radius = PLAYER->GetLOSRange();
2486 for(int c = 0; c < PLAYER->GetSquaresUnder(); ++c)
2487 mapmath<loscontroller>::DoQuadriArea(PLAYER->GetPos(c).X, PLAYER->GetPos(c).Y,
2488 Radius * Radius, XSize, YSize);
2490 loscontroller::ProcessStack();
2492 if(PLAYER->StateIsActivated(INFRA_VISION))
2493 for(int c = 0; c < game::GetTeams(); ++c)
2494 for(std::list<character*>::const_iterator i = game::GetTeam(c)->GetMember().begin(); i != game::GetTeam(c)->GetMember().end(); ++i)
2495 if((*i)->IsEnabled())
2496 (*i)->SendNewDrawRequest();
2499 void level::EnableGlobalRain()
2501 for(int x = 0; x < XSize; ++x)
2502 for(int y = 0; y < YSize; ++y)
2503 Map[x][y]->EnableGlobalRain();
2506 void level::DisableGlobalRain()
2508 for(int x = 0; x < XSize; ++x)
2509 for(int y = 0; y < YSize; ++y)
2510 Map[x][y]->DisableGlobalRain();
2513 void level::InitLastSeen()
2515 for(int x = 0; x < XSize; ++x)
2516 for(int y = 0; y < YSize; ++y)
2517 Map[x][y]->InitLastSeen();
2520 void level::EmitSunBeams()
2522 stackcontroller::Map = Map;
2523 stackcontroller::LevelXSize = XSize;
2524 stackcontroller::LevelYSize = YSize;
2525 sunbeamcontroller::ReSunEmitation = false;
2526 v2 Dir = SunLightDirection;
2527 int x, y, X = 0, Y = 0, SourceFlags;
2528 uLong IDFlags;
2530 /* Do not try to understand the logic behind the starting points of
2531 sunbeams. I determined the formulas by trial and error since all
2532 understandable loops produced strange shapes for the shadows of
2533 either small of large objects probably due to rounding errors
2534 made during line calculations. */
2536 if(!Dir.X || (Dir.Y && abs(Dir.Y) < abs(Dir.X)))
2538 if(Dir.Y > 0)
2540 Y = -1;
2541 SourceFlags = SP_BOTTOM;
2542 IDFlags = ID_X_COORDINATE|ID_BEGIN;
2544 else
2546 Y = YSize;
2547 SourceFlags = SP_TOP;
2548 IDFlags = ID_X_COORDINATE;
2551 else
2553 if(Dir.X > 0)
2555 X = -1;
2556 SourceFlags = SP_RIGHT;
2557 IDFlags = ID_BEGIN;
2559 else
2561 X = XSize;
2562 SourceFlags = SP_LEFT;
2563 IDFlags = 0;
2567 if(!Dir.X)
2569 int Index = XSize << 3;
2571 for(x = 0; x < XSize; ++x, ++Index)
2572 EmitSunBeam(v2(x, Y), Index | IDFlags, SourceFlags);
2574 else if(!Dir.Y)
2576 int Index = YSize << 3;
2578 for(y = 0; y < YSize; ++y, ++Index)
2579 EmitSunBeam(v2(X, y), Index | IDFlags, SourceFlags);
2581 else if(abs(Dir.Y) < abs(Dir.X))
2583 int Index = Dir.X > 0 ? 0 : XSize << 3;
2584 int StartX = Dir.X > 0 ? -XSize << 3 : 0;
2585 int EndX = Dir.X > 0 ? XSize : (XSize << 3) + XSize;
2587 for(x = StartX; x < EndX; ++x, ++Index)
2588 EmitSunBeam(v2(x, Y), Index | IDFlags, SourceFlags);
2590 else
2592 int Index = Dir.Y > 0 ? 0 : YSize << 3;
2593 int StartY = Dir.Y > 0 ? -YSize << 3 : 0;
2594 int EndY = Dir.Y > 0 ? YSize : (YSize << 3) + YSize;
2596 for(y = StartY; y < EndY; ++y, ++Index)
2597 EmitSunBeam(v2(X, y), Index | IDFlags, SourceFlags);
2601 void level::EmitSunBeam(v2 S, uLong ID, int SourceFlags) const
2603 S <<= 1;
2604 v2 D = S + SunLightDirection;
2605 sunbeamcontroller::ID = ID;
2607 if(SourceFlags & SP_TOP_LEFT)
2609 sunbeamcontroller::SunLightBlockHeight = 0;
2610 mapmath<sunbeamcontroller>::DoLine(S.X, S.Y, D.X, D.Y, SKIP_FIRST);
2613 if(SourceFlags & SP_TOP_RIGHT)
2615 sunbeamcontroller::SunLightBlockHeight = 0;
2616 mapmath<sunbeamcontroller>::DoLine(S.X + 1, S.Y, D.X + 1, D.Y, SKIP_FIRST);
2619 if(SourceFlags & SP_BOTTOM_LEFT)
2621 sunbeamcontroller::SunLightBlockHeight = 0;
2622 mapmath<sunbeamcontroller>::DoLine(S.X, S.Y + 1, D.X, D.Y + 1, SKIP_FIRST);
2625 if(SourceFlags & SP_BOTTOM_RIGHT)
2627 sunbeamcontroller::SunLightBlockHeight = 0;
2628 mapmath<sunbeamcontroller>::DoLine(S.X + 1, S.Y + 1, D.X + 1, D.Y + 1, SKIP_FIRST);
2632 truth sunbeamcontroller::Handler(int x, int y)
2634 int X = x >> 1, Y = y >> 1;
2636 if(X < 0 || Y < 0 || X >= LevelXSize || Y >= LevelYSize)
2637 return (X >= -1 && X <= LevelXSize) || (Y >= -1 && Y <= LevelYSize);
2639 lsquare* Square = Map[X][Y];
2640 int SquarePartIndex = (x & 1) + ((y & 1) << 1);
2642 if(SunLightBlockHeight && !Square->IsInside()
2643 && HypotSquare(x - SunLightBlockPos.X, y - SunLightBlockPos.Y) > SunLightBlockHeight)
2644 SunLightBlockHeight = 0;
2646 if(!SunLightBlockHeight)
2648 uLong Flag = 1 << EMITTER_SQUARE_PART_SHIFT << SquarePartIndex;
2649 Square->AddSunLightEmitter(ID | Flag);
2651 else
2653 uLong Flags = ((1 << EMITTER_SQUARE_PART_SHIFT)
2654 | (1 << EMITTER_SHADOW_SHIFT))
2655 << SquarePartIndex;
2657 Square->AddSunLightEmitter(ID | Flags);
2660 if(ReSunEmitation)
2662 if(!(Square->Flags & IN_SQUARE_STACK))
2663 Stack[StackIndex++] = Square;
2665 Square->Flags |= IN_SQUARE_STACK|CHECK_SUN_LIGHT_NEEDED;
2667 for(int d = 0; d < 8; ++d)
2669 lsquare* Neighbour = Square->GetNeighbourLSquare(d);
2671 if(Neighbour && !(Neighbour->Flags & IN_SQUARE_STACK))
2673 Neighbour->Flags |= IN_SQUARE_STACK;
2674 Stack[StackIndex++] = Neighbour;
2679 if(!(Square->Flags & IS_TRANSPARENT) || (SunLightBlockHeight && Square->IsInside()))
2681 /* This should depend on the square */
2682 SunLightBlockHeight = 81;
2683 SunLightBlockPos = v2(x, y);
2686 return true;
2689 void sunbeamcontroller::ProcessStack()
2691 sLong c;
2693 for(c = 0; c < StackIndex; ++c)
2695 lsquare* Square = Stack[c];
2697 if(Square->Flags & CHECK_SUN_LIGHT_NEEDED)
2699 if(Square->Flags & IS_TRANSPARENT)
2700 Square->CalculateSunLightLuminance(EMITTER_SQUARE_PART_BITS);
2702 Square->SendSunLightSignals();
2703 Square->ZeroReSunEmitatedFlags();
2706 Square->Flags &= ~(IN_SQUARE_STACK|CHECK_SUN_LIGHT_NEEDED);
2709 for(c = 0; c < StackIndex; ++c)
2710 Stack[c]->CheckIfIsSecondarySunLightEmitter();
2713 int level::DetectMaterial(cmaterial* Material)
2715 uLong Tick = game::IncreaseLOSTick();
2716 int Squares = 0;
2718 for(int x = 0; x < XSize; ++x)
2719 for(int y = 0; y < YSize; ++y)
2721 lsquare* Square = Map[x][y];
2723 if(Square->DetectMaterial(Material))
2725 Square->Reveal(Tick, true);
2726 ++Squares;
2730 return Squares;
2733 void level::BlurMemory()
2735 int x, y, SquareStackSize = 0;
2737 for(x = 0; x < XSize; ++x)
2738 for(y = 0; y < YSize; ++y)
2740 lsquare* Square = Map[x][y];
2742 if(Square->HasNoBorderPartners())
2743 SquareStack[SquareStackSize++] = Square;
2746 for(x = 0; x < XSize; ++x)
2747 for(y = 0; y < YSize; ++y)
2749 lsquare* Square = Map[x][y];
2750 Square->Flags |= STRONG_NEW_DRAW_REQUEST
2751 | MEMORIZED_UPDATE_REQUEST
2752 | DESCRIPTION_CHANGE;
2754 if(Square->HasNoBorderPartners()
2755 && RAND() & 1
2756 && SquareStackSize)
2757 Square->SwapMemorized(SquareStack[RAND() % SquareStackSize]);
2758 else if(RAND() & 1)
2759 Square->DestroyMemorized();
2763 void level::CalculateLuminances()
2765 for(int x = 0; x < XSize; ++x)
2766 for(int y = 0; y < YSize; ++y)
2768 lsquare* Square = Map[x][y];
2769 Square->CalculateLuminance();
2770 Square->Flags |= MEMORIZED_UPDATE_REQUEST
2771 | DESCRIPTION_CHANGE;
2775 struct areacontroller : public stackcontroller
2777 static truth Handler(int x, int y)
2779 if(x >= 0 && y >= 0 && x < LevelXSize && y < LevelYSize
2780 && HypotSquare(x - Center.X, y - Center.Y) <= RadiusSquare)
2782 lsquare* Square = Map[x][y];
2784 if(!(Square->Flags & IN_SQUARE_STACK))
2786 Stack[StackIndex++] = Square;
2787 Square->Flags |= IN_SQUARE_STACK;
2788 return Square->IsFlyable();
2792 return false;
2794 static int GetStartX(int) { return Center.X; }
2795 static int GetStartY(int) { return Center.Y; }
2796 static sLong RadiusSquare;
2799 sLong areacontroller::RadiusSquare;
2801 int level::AddRadiusToSquareStack(v2 Center, sLong RadiusSquare) const
2803 stackcontroller::Map = Map;
2804 stackcontroller::Stack = SquareStack;
2805 SquareStack[0] = GetLSquare(Center);
2806 stackcontroller::StackIndex = 1;
2807 stackcontroller::LevelXSize = XSize;
2808 stackcontroller::LevelYSize = YSize;
2809 stackcontroller::Center = Center;
2810 areacontroller::RadiusSquare = RadiusSquare;
2811 mapmath<areacontroller>::DoArea();
2812 return stackcontroller::StackIndex;
2815 /* Any fountain is good that is not dry and is NOT Except */
2817 olterrain* level::GetRandomFountainWithWater(olterrain* Except) const
2819 std::vector<olterrain*> Found;
2820 olterrain* OLTerrain;
2821 for(int x = 0; x < XSize; ++x)
2822 for(int y = 0; y < YSize; ++y)
2824 OLTerrain = GetLSquare(x,y)->GetOLTerrain();
2825 if(OLTerrain && OLTerrain != Except && OLTerrain->IsFountainWithWater())
2826 Found.push_back(OLTerrain);
2829 if(Found.empty())
2830 return 0;
2832 return Found[RAND_N(Found.size())];
2835 void level::Amnesia(int Percentile)
2837 for(int x = 0; x < XSize; ++x)
2838 for(int y = 0; y < YSize; ++y)
2840 lsquare* Square = Map[x][y];
2842 if(Square->HasNoBorderPartners() && RAND_N(100) < Percentile)
2844 Square->Flags |= STRONG_NEW_DRAW_REQUEST
2845 | MEMORIZED_UPDATE_REQUEST
2846 | DESCRIPTION_CHANGE;
2848 Square->DestroyMemorized();
2853 /* Returns how many of the monsters were seen */
2855 spawnresult level::SpawnMonsters(characterspawner Spawner, team* Team,
2856 v2 Pos, int Config, int Amount,
2857 truth IgnoreWalkability)
2859 spawnresult SR = { 0, 0 };
2861 for(int c = 0; c < Amount; ++c)
2863 character* Char = Spawner(Config, 0);
2865 if(!c)
2866 SR.Pioneer = Char;
2868 Char->SetTeam(Team);
2870 if(IgnoreWalkability)
2871 Char->ForcePutNear(Pos);
2872 else
2873 Char->PutNear(Pos);
2875 if(Char->CanBeSeenByPlayer())
2876 ++SR.Seen;
2879 return SR;
2882 void level::AddSpecialCursors()
2884 for(int x = 0; x < XSize; ++x)
2885 for(int y = 0; y < YSize; ++y)
2886 Map[x][y]->AddSpecialCursors();
2889 void level::GasExplosion(gas* GasMaterial, lsquare* Square)
2891 for(int d = 0; d < 9; ++d)
2893 lsquare* Neighbour = Square->GetNeighbourLSquare(d);
2895 if(Neighbour && Neighbour->IsFlyable())
2896 Neighbour->AddSmoke(static_cast<gas*>(GasMaterial->SpawnMore(1000)));