3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
8 * See LICENSING which should be included
9 * along with this file for more details
13 /* Compiled through levelset.cpp */
16 #define ON_POSSIBLE_ROUTE (2)
17 #define STILL_ON_POSSIBLE_ROUTE (4)
19 #define ICE_TERRAIN (16)
20 #define STONE_TERRAIN (32)
23 feuLong
level::NextExplosionID
= 1;
25 node
*** node::NodeMap
;
26 int node::RequiredWalkability
;
27 ccharacter
* node::SpecialMover
;
29 uChar
** node::WalkabilityMap
;
30 int node::XSize
, node::YSize
;
31 nodequeue
* node::NodeQueue
;
34 // ////////////////////////////////////////////////////////////////////////// //
35 void maze::CreateMaze () {
42 void maze::InitializeMaze () {
43 std::fill(MazeVector
.begin(), MazeVector
.end(), false);
44 for (unsigned x
= 0; x
< MazeXSize
; ++x
) {
46 MazeVector
[(MazeYSize
-1)*MazeXSize
+x
] = true;
48 for (unsigned y
= 0; y
< MazeYSize
; ++y
) {
49 MazeVector
[y
*MazeXSize
] = true;
50 MazeVector
[y
*MazeXSize
+MazeXSize
-1] = true;
55 void maze::CarveMaze (int x
, int y
) {
56 MazeVector
[y
*MazeXSize
+x
] = true;
57 const unsigned d
= RAND();
58 for (unsigned i
= 0; i
< 4; ++i
) {
59 const int dirs
[] = { 1, -1, 0, 0 };
60 const int dx
= dirs
[(i
+d
+0)%4];
61 const int dy
= dirs
[(i
+d
+2)%4];
62 const int x1
= x
+dx
, y1
= y
+dy
;
63 const int x2
= x1
+dx
, y2
= y1
+dy
;
64 if(!MazeVector
[y1
*MazeXSize
+x1
] && !MazeVector
[y2
*MazeXSize
+x2
]) {
65 MazeVector
[y1
*MazeXSize
+x1
] = true;
72 void maze::StripMazeHusk () {
73 for (unsigned y1
= 2; y1
< MazeYSize
-2; ++y1
) {
74 for (unsigned x1
= 2; x1
< MazeXSize
-2; ++x1
) {
75 MazeKernel
.push_back(MazeVector
[y1
*MazeXSize
+x1
]);
81 // ////////////////////////////////////////////////////////////////////////// //
82 level::level() : Room(1, static_cast<room
*>(0)), GlobalRainLiquid(0), SunLightEmitation(0), AmbientLuminance(0), SquareStack(0), NightAmbientLuminance(0) {}
83 void level::SetRoom (int I
, room
*What
) { Room
[I
] = What
; }
84 void level::AddToAttachQueue (v2 Pos
) { AttachQueue
.push_back(Pos
); }
88 for (feuLong c
= 0; c
< XSizeTimesYSize
; ++c
) delete NodeMap
[0][c
];
89 for (feuLong c
= 0; c
< Room
.size(); ++c
) delete Room
[c
];
91 delete [] WalkabilityMap
;
92 delete GlobalRainLiquid
;
93 delete [] SquareStack
;
94 game::SetGlobalRainLiquid(0);
98 #define CHECK(x, y) (!(FlagMap[x][y]&(ON_POSSIBLE_ROUTE|FORBIDDEN)))
99 #define CALL_EXPAND(x, y) do { \
100 ExpandPossibleRoute(x, y, TargetX, TargetY, XMode); \
101 if (FlagMap[TargetX][TargetY]&ON_POSSIBLE_ROUTE) return; \
104 void level::ExpandPossibleRoute (int OrigoX
, int OrigoY
, int TargetX
, int TargetY
, truth XMode
) {
105 FlagMap
[OrigoX
][OrigoY
] |= ON_POSSIBLE_ROUTE
;
107 if (TargetX
< OrigoX
&& CHECK(OrigoX
-1, OrigoY
)) CALL_EXPAND(OrigoX
-1, OrigoY
);
108 if (TargetX
> OrigoX
&& CHECK(OrigoX
+1, OrigoY
)) CALL_EXPAND(OrigoX
+1, OrigoY
);
109 if (TargetY
< OrigoY
&& CHECK(OrigoX
, OrigoY
-1)) CALL_EXPAND(OrigoX
, OrigoY
-1);
110 if (TargetY
> OrigoY
&& CHECK(OrigoX
, OrigoY
+1)) CALL_EXPAND(OrigoX
, OrigoY
+1);
111 if (TargetX
<= OrigoX
&& OrigoX
< XSize
-2 && CHECK(OrigoX
+1, OrigoY
)) CALL_EXPAND(OrigoX
+1, OrigoY
);
112 if (TargetX
>= OrigoX
&& OrigoX
> 1 && CHECK(OrigoX
-1, OrigoY
)) CALL_EXPAND(OrigoX
-1, OrigoY
);
113 if (TargetY
<= OrigoY
&& OrigoY
< YSize
-2 && CHECK(OrigoX
, OrigoY
+1)) CALL_EXPAND(OrigoX
, OrigoY
+1);
114 if (TargetY
>= OrigoY
&& OrigoY
> 1 && CHECK(OrigoX
, OrigoY
-1)) CALL_EXPAND(OrigoX
, OrigoY
-1);
116 if (TargetY
< OrigoY
&& CHECK(OrigoX
, OrigoY
-1)) CALL_EXPAND(OrigoX
, OrigoY
-1);
117 if (TargetY
> OrigoY
&& CHECK(OrigoX
, OrigoY
+1)) CALL_EXPAND(OrigoX
, OrigoY
+1);
118 if (TargetX
< OrigoX
&& CHECK(OrigoX
-1, OrigoY
)) CALL_EXPAND(OrigoX
-1, OrigoY
);
119 if (TargetX
> OrigoX
&& CHECK(OrigoX
+1, OrigoY
)) CALL_EXPAND(OrigoX
+1, OrigoY
);
120 if (TargetY
<= OrigoY
&& OrigoY
< YSize
-2 && CHECK(OrigoX
, OrigoY
+1)) CALL_EXPAND(OrigoX
, OrigoY
+1);
121 if (TargetY
>= OrigoY
&& OrigoY
> 1 && CHECK(OrigoX
, OrigoY
-1)) CALL_EXPAND(OrigoX
, OrigoY
-1);
122 if (TargetX
<= OrigoX
&& OrigoX
< XSize
-2 && CHECK(OrigoX
+1, OrigoY
)) CALL_EXPAND(OrigoX
+1, OrigoY
);
123 if (TargetX
>= OrigoX
&& OrigoX
> 1 && CHECK(OrigoX
-1, OrigoY
)) CALL_EXPAND(OrigoX
-1, OrigoY
);
130 #define CHECK(x, y) ((FlagMap[x][y]&(STILL_ON_POSSIBLE_ROUTE|ON_POSSIBLE_ROUTE)) == ON_POSSIBLE_ROUTE)
131 #define CALL_EXPAND(x, y) do { \
132 ExpandStillPossibleRoute(x, y, TargetX, TargetY, XMode); \
133 if (FlagMap[TargetX][TargetY]&STILL_ON_POSSIBLE_ROUTE) return; \
136 void level::ExpandStillPossibleRoute (int OrigoX
, int OrigoY
, int TargetX
, int TargetY
, truth XMode
) {
137 FlagMap
[OrigoX
][OrigoY
] |= STILL_ON_POSSIBLE_ROUTE
;
139 if (TargetX
< OrigoX
&& CHECK(OrigoX
-1, OrigoY
)) CALL_EXPAND(OrigoX
-1, OrigoY
);
140 if (TargetX
> OrigoX
&& CHECK(OrigoX
+1, OrigoY
)) CALL_EXPAND(OrigoX
+1, OrigoY
);
141 if (TargetY
< OrigoY
&& CHECK(OrigoX
, OrigoY
-1)) CALL_EXPAND(OrigoX
, OrigoY
-1);
142 if (TargetY
> OrigoY
&& CHECK(OrigoX
, OrigoY
+1)) CALL_EXPAND(OrigoX
, OrigoY
+1);
143 if (TargetX
<= OrigoX
&& OrigoX
< XSize
-2 && CHECK(OrigoX
+1, OrigoY
)) CALL_EXPAND(OrigoX
+1, OrigoY
);
144 if (TargetX
>= OrigoX
&& OrigoX
> 1 && CHECK(OrigoX
-1, OrigoY
)) CALL_EXPAND(OrigoX
-1, OrigoY
);
145 if (TargetY
<= OrigoY
&& OrigoY
< YSize
-2 && CHECK(OrigoX
, OrigoY
+1)) CALL_EXPAND(OrigoX
, OrigoY
+1);
146 if (TargetY
>= OrigoY
&& OrigoY
> 1 && CHECK(OrigoX
, OrigoY
-1)) CALL_EXPAND(OrigoX
, OrigoY
-1);
148 if (TargetY
< OrigoY
&& CHECK(OrigoX
, OrigoY
-1)) CALL_EXPAND(OrigoX
, OrigoY
-1);
149 if (TargetY
> OrigoY
&& CHECK(OrigoX
, OrigoY
+1)) CALL_EXPAND(OrigoX
, OrigoY
+1);
150 if (TargetX
< OrigoX
&& CHECK(OrigoX
-1, OrigoY
)) CALL_EXPAND(OrigoX
-1, OrigoY
);
151 if (TargetX
> OrigoX
&& CHECK(OrigoX
+1, OrigoY
)) CALL_EXPAND(OrigoX
+1, OrigoY
);
152 if (TargetY
<= OrigoY
&& OrigoY
< YSize
-2 && CHECK(OrigoX
, OrigoY
+1)) CALL_EXPAND(OrigoX
, OrigoY
+1);
153 if (TargetY
>= OrigoY
&& OrigoY
> 1 && CHECK(OrigoX
, OrigoY
-1)) CALL_EXPAND(OrigoX
, OrigoY
-1);
154 if (TargetX
<= OrigoX
&& OrigoX
< XSize
-2 && CHECK(OrigoX
+1, OrigoY
)) CALL_EXPAND(OrigoX
+1, OrigoY
);
155 if (TargetX
>= OrigoX
&& OrigoX
> 1 && CHECK(OrigoX
-1, OrigoY
)) CALL_EXPAND(OrigoX
-1, OrigoY
);
162 void level::GenerateTunnel (int FromX
, int FromY
, int TargetX
, int TargetY
, truth XMode
) {
163 FlagMap
[FromX
][FromY
] |= ON_POSSIBLE_ROUTE
;
164 ExpandPossibleRoute(FromX
, FromY
, TargetX
, TargetY
, XMode
);
166 const contentscript
<glterrain
> *GTerrain
= LevelScript
->GetTunnelSquare()->GetGTerrain();
167 const contentscript
<olterrain
> *OTerrain
= LevelScript
->GetTunnelSquare()->GetOTerrain();
169 if (FlagMap
[TargetX
][TargetY
]&ON_POSSIBLE_ROUTE
) {
170 for (int x
= 0; x
< XSize
; ++x
) {
171 for (int y
= 0; y
< YSize
; ++y
) {
172 if ((FlagMap
[x
][y
]&(ON_POSSIBLE_ROUTE
|PREFERRED
)) == ON_POSSIBLE_ROUTE
&&
173 !(x
== FromX
&& y
== FromY
) && !(x
== TargetX
&& y
== TargetY
)) {
174 FlagMap
[x
][y
] &= ~ON_POSSIBLE_ROUTE
;
175 FlagMap
[FromX
][FromY
] |= STILL_ON_POSSIBLE_ROUTE
;
176 ExpandStillPossibleRoute(FromX
, FromY
, TargetX
, TargetY
, XMode
);
178 if (!(FlagMap
[TargetX
][TargetY
]&STILL_ON_POSSIBLE_ROUTE
)) {
179 FlagMap
[x
][y
] |= ON_POSSIBLE_ROUTE
|PREFERRED
;
180 Map
[x
][y
]->ChangeGLTerrain(GTerrain
->Instantiate());
181 Map
[x
][y
]->ChangeOLTerrain(OTerrain
->Instantiate());
184 for (int X
= 0; X
< XSize
; ++X
) {
185 for (int Y
= 0; Y
< YSize
; ++Y
) {
186 FlagMap
[X
][Y
] &= ~STILL_ON_POSSIBLE_ROUTE
;
193 for (int x
= 1; x
< XSize
-1; ++x
) {
194 for (int y
= 1; y
< YSize
-1; ++y
) {
195 FlagMap
[x
][y
] &= ~ON_POSSIBLE_ROUTE
;
201 truth
level::Generate (int Index
) {
202 game::BusyAnimation();
203 Initialize(LevelScript
->GetSize()->X
, LevelScript
->GetSize()->Y
);
204 game::SetCurrentArea(this);
205 game::SetCurrentLevel(this);
206 Alloc2D(NodeMap
, XSize
, YSize
);
207 Alloc2D(WalkabilityMap
, XSize
, YSize
);
208 Map
= reinterpret_cast<lsquare
***>(area::Map
);
209 SquareStack
= new lsquare
* [XSizeTimesYSize
];
211 if ((Index
== 0 && GetDungeon()->GetIndex() == NEW_ATTNAM
) ||
212 (Index
== 0 && GetDungeon()->GetIndex() == ATTNAM
) ||
213 (Index
== 0 && GetDungeon()->GetIndex() == MUNTUO
)) {
214 NightAmbientLuminance
= MakeRGB24(95, 95, 95);
217 for (int x
= 0; x
< XSize
; ++x
) {
218 for (int y
= 0; y
< YSize
; ++y
) {
219 Map
[x
][y
] = new lsquare(this, v2(x
, y
));
220 NodeMap
[x
][y
] = new node(x
, y
, Map
[x
][y
]);
224 for (int x
= 0; x
< XSize
; ++x
) {
225 for (int y
= 0; y
< YSize
; ++y
) {
226 Map
[x
][y
]->CalculateNeighbourLSquares();
230 int Type
= LevelScript
->GetType() ? *LevelScript
->GetType() : 0;
232 if (Type
== 0) return GenerateDungeon(Index
);
233 if (Type
== DESERT
) return GenerateDesert();
234 if (Type
== JUNGLE
) return GenerateJungle();
235 if (Type
== STEPPE
) return GenerateSteppe();
236 if (Type
== LEAFY_FOREST
) return GenerateLeafyForest();
237 if (Type
== EVERGREEN_FOREST
) return GenerateEvergreenForest();
238 if (Type
== TUNDRA
) return GenerateTundra();
239 if (Type
== GLACIER
) return GenerateGlacier();
241 ABORT("You are a terrorist. Please stop creating wterrains that are stupid.");
246 void level::ApplyLSquareScript (const squarescript
*Script
) {
247 const interval
*ScriptTimes
= Script
->GetTimes();
248 int Times
= ScriptTimes
? ScriptTimes
->Randomize() : 1;
250 for (int c
= 0; c
< Times
; ++c
) {
253 if (Script
->GetPosition()->GetRandom()) {
254 Pos
= GetRandomSquare(0, Script
->GetPosition()->GetFlags(), Script
->GetPosition()->GetBorders());
256 Pos
= Script
->GetPosition()->GetVector();
258 Map
[Pos
.X
][Pos
.Y
]->ApplyScript(Script
, 0);
263 void level::AttachPos (int WhatX
, int WhatY
) {
264 int PosX
= 1+RAND()%(XSize
-2);
265 int PosY
= 1+RAND()%(YSize
-2);
267 while(!(FlagMap
[PosX
][PosY
]&PREFERRED
)) {
268 PosX
= 1+RAND()%(XSize
-2);
269 PosY
= 1+RAND()%(YSize
-2);
271 FlagMap
[WhatX
][WhatY
] &= ~FORBIDDEN
;
272 FlagMap
[WhatX
][WhatY
] |= PREFERRED
;
273 GenerateTunnel(WhatX
, WhatY
, PosX
, PosY
, RAND()&1);
274 FlagMap
[WhatX
][WhatY
] |= FORBIDDEN
;
275 FlagMap
[WhatX
][WhatY
] &= ~PREFERRED
;
279 void level::CreateItems (int Amount
) {
281 sLong MinPrice
= *LevelScript
->GetItemMinPriceBase() + *LevelScript
->GetItemMinPriceDelta()*Index
;
283 for (int x
= 0; x
< Amount
; ++x
) {
284 v2 Pos
= GetRandomSquare();
285 item
*Item
= protosystem::BalancedCreateItem(this, MinPrice
, MAX_PRICE
, ANY_CATEGORY
, 0, IGNORE_BROKEN_PRICE
);
287 Item
->CalculateEnchantment();
288 Map
[Pos
.X
][Pos
.Y
]->Stack
->AddItem(Item
);
289 Item
->SpecialGenerationHandler();
295 truth
level::MakeRoom (const roomscript
*RoomScript
) {
296 game::BusyAnimation();
298 v2 Pos
= RoomScript
->GetPos()->Randomize();
299 v2 Size
= RoomScript
->GetSize()->Randomize();
301 if (Pos
.X
+Size
.X
> XSize
-2) return false;
302 if (Pos
.Y
+Size
.Y
> YSize
-2) return false;
304 for (int x
= Pos
.X
-1; x
<= Pos
.X
+Size
.X
; ++x
) {
305 for (int y
= Pos
.Y
-1; y
<= Pos
.Y
+Size
.Y
; ++y
) {
306 if (FlagMap
[x
][y
]&FORBIDDEN
|| FlagMap
[x
][y
]&PREFERRED
) return false;
310 auto roomProto
= protocontainer
<room
>::GetProto(*RoomScript
->GetType());
311 if (!roomProto
) ABORT("No prototype #%d for room!", *RoomScript
->GetType());
312 room
*RoomClass
= roomProto
->Spawn();
314 RoomClass
->SetScript(RoomScript
);
315 RoomClass
->SetPos(Pos
);
316 RoomClass
->SetSize(Size
);
317 RoomClass
->SetFlags(*RoomScript
->GetFlags());
319 RoomClass
->SetDivineMaster(*RoomScript
->GetDivineMaster());
320 game::BusyAnimation();
322 std::vector
<v2
> OKForDoor
, Inside
, Border
;
324 GenerateRectangularRoom(OKForDoor
, Inside
, Border
, RoomScript
, RoomClass
, Pos
, Size
);
325 game::BusyAnimation();
327 if (*RoomScript
->GenerateFountains() && !(RAND()%10)) {
328 GetLSquare(Inside
[RAND()%Inside
.size()])->ChangeOLTerrain(fountain::Spawn());
331 // Ward, which gets generated as per fountain activation
332 if (*RoomScript
->GenerateWards() && !(RAND()%5)) {
333 GetLSquare(Inside
[RAND()%Inside
.size()])->ChangeOLTerrain(ward::Spawn());
336 if (*RoomScript
->AltarPossible() && !(RAND()%5)) {
338 const fearray
<int> *am
= RoomScript
->GetAllowedDivineMasters();
340 if (!am
|| am
->Size
== 0) {
341 Owner
= 1+RAND()%GODS
;
343 Owner
= am
->GetRandomElement();
344 if (Owner
< 1 || Owner
> GODS
) ABORT("Your god is a bad god!");
347 GetLSquare(Inside
[RAND()%Inside
.size()])->ChangeOLTerrain(altar::Spawn(Owner
));
348 game::GetGod(Owner
)->SignalRandomAltarGeneration(Inside
);
349 RoomClass
->SetDivineMaster(Owner
);
352 if (*RoomScript
->GenerateTunnel() && !Door
.empty()) {
353 game::BusyAnimation();
354 v2 OutsideDoorPos
= Door
[RAND()%Door
.size()]; // An other room
356 if (OKForDoor
.empty()) ABORT("The Doors - You are strange.");
358 v2 InsideDoorPos
= OKForDoor
[RAND()%OKForDoor
.size()]; // this door
359 olterrain
*Door
= RoomScript
->GetDoorSquare()->GetOTerrain()->Instantiate(); //Bug! Wrong room!
361 if (Door
&& !(RAND()%5) && *RoomScript
->AllowLockedDoors()) {
362 if (*RoomScript
->AllowBoobyTrappedDoors() && !(RAND()%5)) Door
->CreateBoobyTrap();
366 Map
[OutsideDoorPos
.X
][OutsideDoorPos
.Y
]->ChangeLTerrain(RoomScript
->GetDoorSquare()->GetGTerrain()->Instantiate(), Door
);
367 Map
[OutsideDoorPos
.X
][OutsideDoorPos
.Y
]->Clean();
368 FlagMap
[OutsideDoorPos
.X
][OutsideDoorPos
.Y
] &= ~FORBIDDEN
;
369 FlagMap
[OutsideDoorPos
.X
][OutsideDoorPos
.Y
] |= PREFERRED
;
370 FlagMap
[InsideDoorPos
.X
][InsideDoorPos
.Y
] &= ~FORBIDDEN
;
371 FlagMap
[InsideDoorPos
.X
][InsideDoorPos
.Y
] |= PREFERRED
;
372 Door
= RoomScript
->GetDoorSquare()->GetOTerrain()->Instantiate();
374 if (Door
&& !(RAND()%5) && *RoomScript
->AllowLockedDoors()) {
375 if (*RoomScript
->AllowBoobyTrappedDoors() && !(RAND()%5)) Door
->CreateBoobyTrap();
379 Map
[InsideDoorPos
.X
][InsideDoorPos
.Y
]->ChangeLTerrain(RoomScript
->GetDoorSquare()->GetGTerrain()->Instantiate(), Door
);
380 Map
[InsideDoorPos
.X
][InsideDoorPos
.Y
]->Clean();
381 GenerateTunnel(InsideDoorPos
.X
, InsideDoorPos
.Y
, OutsideDoorPos
.X
, OutsideDoorPos
.Y
, RAND()&1);
382 FlagMap
[OutsideDoorPos
.X
][OutsideDoorPos
.Y
] |= FORBIDDEN
;
383 FlagMap
[OutsideDoorPos
.X
][OutsideDoorPos
.Y
] &= ~PREFERRED
;
384 FlagMap
[InsideDoorPos
.X
][InsideDoorPos
.Y
] |= FORBIDDEN
;
385 FlagMap
[InsideDoorPos
.X
][InsideDoorPos
.Y
] &= ~PREFERRED
;
388 if (*RoomScript
->GenerateDoor()) {
389 game::BusyAnimation();
392 if (OKForDoor
.empty()) ABORT("The Doors - This thing has been broken.");
394 DoorPos
= OKForDoor
[RAND()%OKForDoor
.size()];
395 Door
.push_back(DoorPos
);
397 if (!*RoomScript
->GenerateTunnel()) {
398 Map
[DoorPos
.X
][DoorPos
.Y
]->ChangeLTerrain(RoomScript
->GetDoorSquare()->GetGTerrain()->Instantiate(),
399 RoomScript
->GetDoorSquare()->GetOTerrain()->Instantiate());
400 Map
[DoorPos
.X
][DoorPos
.Y
]->Clean();
404 // Make second door for a maze room. If the room has even-numbered dimension, then it will be a rectangular room with two doors...
405 if (*RoomScript
->GenerateDoor() && (*RoomScript
->GetShape() == MAZE_ROOM
)) {
406 game::BusyAnimation();
409 if (!OKForDoor
.empty()) {
410 DoorPos
= OKForDoor
[OKForDoor
.size()-1];
411 Door
.push_back(DoorPos
);
413 if (!*RoomScript
->GenerateTunnel()) {
414 Map
[DoorPos
.X
][DoorPos
.Y
]->ChangeLTerrain(RoomScript
->GetDoorSquare()->GetGTerrain()->Instantiate(),
415 RoomScript
->GetDoorSquare()->GetOTerrain()->Instantiate());
416 Map
[DoorPos
.X
][DoorPos
.Y
]->Clean();
421 const charactercontentmap
*CharacterMap
= RoomScript
->GetCharacterMap();
424 v2
CharPos(Pos
+ *CharacterMap
->GetPos());
425 const contentscript
<character
> *CharacterScript
;
427 for (int x
= 0; x
< CharacterMap
->GetSize()->X
; ++x
) {
428 game::BusyAnimation();
429 for (int y
= 0; y
< CharacterMap
->GetSize()->Y
; ++y
) {
430 if (IsValidScript(CharacterScript
= CharacterMap
->GetContentScript(x
, y
))) {
431 character
*Char
= CharacterScript
->Instantiate();
434 Char
->SetGenerationDanger(Difficulty
);
435 if (!Char
->GetTeam()) Char
->SetTeam(game::GetTeam(*LevelScript
->GetTeamDefault()));
436 if (CharacterScript
->GetFlags()&IS_LEADER
) Char
->GetTeam()->SetLeader(Char
);
437 Char
->PutTo(CharPos
+v2(x
, y
));
438 Char
->CreateHomeData();
439 if (CharacterScript
->GetFlags()&IS_MASTER
) RoomClass
->SetMasterID(Char
->GetID());
446 const itemcontentmap
*ItemMap
= RoomScript
->GetItemMap();
449 v2
ItemPos(Pos
+ *ItemMap
->GetPos());
450 const fearray
<contentscript
<item
> >* ItemScript
;
452 for (int x
= 0; x
< ItemMap
->GetSize()->X
; ++x
) {
453 game::BusyAnimation();
454 for (int y
= 0; y
< ItemMap
->GetSize()->Y
; ++y
) {
455 if (IsValidScript(ItemScript
= ItemMap
->GetContentScript(x
, y
))) {
456 for (uInt c1
= 0; c1
< ItemScript
->Size
; ++c1
) {
457 const interval
* TimesPtr
= ItemScript
->Data
[c1
].GetTimes();
458 int Times
= TimesPtr
? TimesPtr
->Randomize() : 1;
460 for (int c2
= 0; c2
< Times
; ++c2
) {
461 item
*Item
= ItemScript
->Data
[c1
].Instantiate();
464 int SquarePosition
= ItemScript
->Data
[c1
].GetSquarePosition();
466 if (SquarePosition
!= CENTER
) Item
->SignalSquarePositionChange(SquarePosition
);
467 Map
[ItemPos
.X
+x
][ItemPos
.Y
+y
]->GetStack()->AddItem(Item
);
468 Item
->SpecialGenerationHandler();
477 const glterraincontentmap
*GTerrainMap
= RoomScript
->GetGTerrainMap();
480 v2
GTerrainPos(Pos
+ *GTerrainMap
->GetPos());
481 const contentscript
<glterrain
> *GTerrainScript
;
483 for (int x
= 0; x
< GTerrainMap
->GetSize()->X
; ++x
) {
484 game::BusyAnimation();
485 for (int y
= 0; y
< GTerrainMap
->GetSize()->Y
; ++y
) {
486 if (IsValidScript(GTerrainScript
= GTerrainMap
->GetContentScript(x
, y
))) {
487 lsquare
*Square
= Map
[GTerrainPos
.X
+x
][GTerrainPos
.Y
+y
];
489 Square
->ChangeGLTerrain(GTerrainScript
->Instantiate());
490 if (GTerrainScript
->IsInside()) {
491 if (*GTerrainScript
->IsInside()) Square
->Flags
|= INSIDE
; else Square
->Flags
&= ~INSIDE
;
498 const olterraincontentmap
*OTerrainMap
= RoomScript
->GetOTerrainMap();
501 v2
OTerrainPos(Pos
+ *OTerrainMap
->GetPos());
502 const contentscript
<olterrain
> *OTerrainScript
;
504 for (int x
= 0; x
< OTerrainMap
->GetSize()->X
; ++x
) {
505 game::BusyAnimation();
506 for (int y
= 0; y
< OTerrainMap
->GetSize()->Y
; ++y
) {
507 if (IsValidScript(OTerrainScript
= OTerrainMap
->GetContentScript(x
, y
))) {
508 olterrain
*Terrain
= OTerrainScript
->Instantiate();
510 if (Terrain
->AcceptsOffers()) {
511 //FIXME: make IsAltar()? for now only altars can accept offers
512 if (RoomClass
->GetDivineMaster()) {
513 //if (Terrain->GetConfig() != RoomClass->GetDivineMaster()) ABORT("Random altar in room with DivineMaster!");
514 if (Terrain
->GetConfig() != RoomClass
->GetDivineMaster()) {
516 fprintf(stderr
, "forced altar!\n");
518 Terrain
= altar::Spawn(RoomClass
->GetDivineMaster());
521 // no DivineMaster yet, assign it
522 fprintf(stderr
, "spawned altar in room w/o divine master, assigning %d\n", Terrain
->GetConfig());
523 RoomClass
->SetDivineMaster(Terrain
->GetConfig());
526 Map
[OTerrainPos
.X
+x
][OTerrainPos
.Y
+y
]->ChangeOLTerrain(Terrain
);
532 const std::list
<squarescript
> Square
= RoomScript
->GetSquare();
534 for (std::list
<squarescript
>::const_iterator i
= Square
.begin(); i
!= Square
.end(); ++i
) {
535 game::BusyAnimation();
536 const squarescript
*Script
= &*i
;
537 const interval
*ScriptTimes
= Script
->GetTimes();
538 int Times
= ScriptTimes
? ScriptTimes
->Randomize() : 1;
540 for (int t
= 0; t
< Times
; ++t
) {
543 if (Script
->GetPosition()->GetRandom()) {
544 const rect
*ScriptBorders
= Script
->GetPosition()->GetBorders();
545 rect Borders
= ScriptBorders
? *ScriptBorders
+Pos
: rect(Pos
, Pos
+Size
-v2(1, 1));
547 SquarePos
= GetRandomSquare(0, Script
->GetPosition()->GetFlags(), &Borders
);
549 SquarePos
= Pos
+Script
->GetPosition()->GetVector();
551 Map
[SquarePos
.X
][SquarePos
.Y
]->ApplyScript(Script
, RoomClass
);
559 truth
level::GenerateLanterns (int X
, int Y
, int SquarePos
) const {
561 lantern
*Lantern
= lantern::Spawn();
562 Lantern
->SignalSquarePositionChange(SquarePos
);
563 Map
[X
][Y
]->GetStack()->AddItem(Lantern
);
570 void level::CreateRoomSquare (glterrain
*GLTerrain
, olterrain
* OLTerrain
, int X
, int Y
, int Room
, int Flags
) const {
571 Map
[X
][Y
]->ChangeLTerrain(GLTerrain
, OLTerrain
);
572 FlagMap
[X
][Y
] |= FORBIDDEN
;
573 Map
[X
][Y
]->SetRoomIndex(Room
);
574 Map
[X
][Y
]->AddFlags(Flags
);
578 void level::GenerateMonsters () {
579 if (*LevelScript
->GenerateMonsters() &&
580 game::GetTeam(MONSTER_TEAM
)->GetEnabledMembers() < IdealPopulation
&&
581 (MonsterGenerationInterval
<= 1 || !RAND_N(MonsterGenerationInterval
))) {
582 GenerateNewMonsters(1);
583 ++MonsterGenerationInterval
;
588 void level::Save (outputfile
&SaveFile
) const {
589 area::Save(SaveFile
);
590 SaveFile
<< Room
<< GlobalRainLiquid
<< GlobalRainSpeed
;
592 for (int x
= 0; x
< XSize
; ++x
) {
593 for (int y
= 0; y
< YSize
; ++y
) {
594 Map
[x
][y
]->Save(SaveFile
);
598 SaveFile
<< Door
<< LevelMessage
<< IdealPopulation
<< MonsterGenerationInterval
<< Difficulty
;
599 SaveFile
<< SunLightEmitation
<< SunLightDirection
<< AmbientLuminance
<< NightAmbientLuminance
;
603 void level::Load (inputfile
&SaveFile
) {
604 game::SetIsGenerating(true);
605 game::SetIsLoading(true);
606 area::Load(SaveFile
);
607 Map
= reinterpret_cast<lsquare
***>(area::Map
);
609 GlobalRainLiquid
= static_cast<liquid
*>(ReadType(material
*, SaveFile
));
610 SaveFile
>> GlobalRainSpeed
;
612 if (GlobalRainLiquid
) GlobalRainLiquid
->SetVolumeNoSignals(0);
614 game::SetGlobalRainLiquid(GlobalRainLiquid
);
615 game::SetGlobalRainSpeed(GlobalRainSpeed
);
617 for (int x
= 0; x
< XSize
; ++x
) {
618 for (int y
= 0; y
< YSize
; ++y
) {
619 Map
[x
][y
] = new lsquare(this, v2(x
, y
));
623 for (int x
= 0; x
< XSize
; ++x
) {
624 for (int y
= 0; y
< YSize
; ++y
) {
625 game::SetSquareInLoad(Map
[x
][y
]);
626 Map
[x
][y
]->Load(SaveFile
);
627 Map
[x
][y
]->CalculateNeighbourLSquares();
631 SaveFile
>> Door
>> LevelMessage
>> IdealPopulation
>> MonsterGenerationInterval
>> Difficulty
;
632 SaveFile
>> SunLightEmitation
>> SunLightDirection
>> AmbientLuminance
>> NightAmbientLuminance
;
633 Alloc2D(NodeMap
, XSize
, YSize
);
634 Alloc2D(WalkabilityMap
, XSize
, YSize
);
636 for (int x
= 0; x
< XSize
; ++x
) {
637 for (int y
= 0; y
< YSize
; ++y
) {
638 if (!Map
[x
][y
]->IsInside()) Map
[x
][y
]->AmbientLuminance
= AmbientLuminance
;
639 NodeMap
[x
][y
] = new node(x
, y
, Map
[x
][y
]);
640 WalkabilityMap
[x
][y
] = Map
[x
][y
]->GetTheoreticalWalkability();
641 Map
[x
][y
]->CalculateGroundBorderPartners();
642 Map
[x
][y
]->CalculateOverBorderPartners();
646 SquareStack
= new lsquare
* [XSizeTimesYSize
];
647 game::SetIsLoading(false);
648 game::SetIsGenerating(false);
652 void level::FiatLux () {
653 for (int x
= 0; x
< XSize
; ++x
) {
654 for (int y
= 0; y
< YSize
; ++y
) {
655 Map
[x
][y
]->CalculateEmitation();
656 Map
[x
][y
]->Emitate();
657 Map
[x
][y
]->CalculateLuminance();
664 void level::GenerateNewMonsters (int HowMany
, truth ConsiderPlayer
) {
665 for (int c1
= 0; c1
< HowMany
; ++c1
) {
667 character
*Char
= protosystem::BalancedCreateMonster(this);
669 Char
->CalculateEnchantments();
670 for (int c2
= 0; c2
< 30; ++c2
) {
671 Pos
= GetRandomSquare(Char
);
672 if (Pos
== ERROR_V2
) break;
673 lsquare
*Square
= GetLSquare(Pos
);
674 if ((!Square
->GetRoomIndex() || !Square
->GetRoom()->DontGenerateMonsters()) &&
675 (!ConsiderPlayer
|| (Pos
-PLAYER
->GetPos()).GetManhattanLength() > 6)) break;
677 if (Pos
!= ERROR_V2
) {
679 Char
->SetGenerationDanger(Difficulty
);
680 Char
->SignalGeneration();
681 Char
->SignalNaturalGeneration();
684 int Modifier
= Time
.Day
-EDIT_ATTRIBUTE_DAY_MIN
;
685 if (Modifier
> 0) Char
->EditAllAttributes(Modifier
>>EDIT_ATTRIBUTE_DAY_SHIFT
);
689 //Char->SendToHell(); // equipment
695 /* Example of the usage: GetRandomSquare() gives out a random walkable square */
696 v2
level::GetRandomSquare (ccharacter
* Char
, int Flags
, const rect
* Borders
) const {
701 LocalBorder
= *Borders
;
702 Borders
= &LocalBorder
;
703 LimitRef(LocalBorder
.X1
, 0, XSize
-1);
704 LimitRef(LocalBorder
.X2
, 0, XSize
-1);
705 LimitRef(LocalBorder
.Y1
, 0, YSize
-1);
706 LimitRef(LocalBorder
.Y2
, 0, YSize
-1);
708 for (int c
= 0;; ++c
) {
711 if (c
== 50) Char
= 0;
712 if (c
== 500) return ERROR_V2
;
714 Pos
.X
= Borders
->X1
+RAND()%(Borders
->X2
-Borders
->X1
+1);
715 Pos
.Y
= Borders
->Y1
+RAND()%(Borders
->Y2
-Borders
->Y1
+1);
717 Pos
.X
= 1+RAND()%(XSize
-2);
718 Pos
.Y
= 1+RAND()%(YSize
-2);
720 LSquare
= Map
[Pos
.X
][Pos
.Y
];
721 if (((Char
? Char
->CanMoveOn(LSquare
) : (LSquare
->GetWalkability()&WALK
)) != !(Flags
&NOT_WALKABLE
)) ||
722 ((Char
? Char
->IsFreeForMe(LSquare
) : !LSquare
->GetCharacter()) != !(Flags
&HAS_CHARACTER
)) ||
723 (Flags
&ATTACHABLE
&& FlagMap
[Pos
.X
][Pos
.Y
]&FORBIDDEN
) ||
724 (Flags
&HAS_NO_OTERRAIN
&& LSquare
->GetOTerrain()))
728 int RoomFlags
= Flags
&(IN_ROOM
|NOT_IN_ROOM
);
729 if ((RoomFlags
== IN_ROOM
&& !LSquare
->GetRoomIndex()) || (RoomFlags
== NOT_IN_ROOM
&& LSquare
->GetRoomIndex())) continue;
735 void level::ParticleTrail (v2 StartPos
, v2 EndPos
) {
736 if (StartPos
.X
!= EndPos
.X
&& StartPos
.Y
!= EndPos
.Y
) {
737 ABORT("666th rule of thermodynamics - Particles don't move the way you want them to move.");
742 truth
level::IsOnGround () const {
743 return *LevelScript
->IsOnGround();
747 truth
level::EarthquakesAffectTunnels () const {
748 return *LevelScript
->EarthquakesAffectTunnels();
752 int level::GetLOSModifier () const {
753 return *LevelScript
->GetLOSModifier();
757 void level::AddRoom (room
*NewRoom
) {
758 NewRoom
->SetIndex(Room
.size());
759 Room
.push_back(NewRoom
);
763 room
*level::GetRoom (int I
) const {
764 if (!I
) ABORT("Access to room zero denied!");
769 void level::Explosion (character
*Terrorist
, cfestring
&DeathMsg
, v2 Pos
, int Strength
, truth HurtNeutrals
) {
770 static int StrengthLimit
[6] = { 500, 250, 100, 50, 25, 10 };
773 for (int c
= 0; c
< 6; ++c
) if (Strength
>= StrengthLimit
[c
]) { Size
= c
; break; }
774 PlayerHurt
.resize(PlayerHurt
.size()+1);
775 explosion
*Exp
= new explosion
;
776 Exp
->Terrorist
= Terrorist
;
777 Exp
->DeathMsg
= DeathMsg
;
779 Exp
->ID
= NextExplosionID
++;
780 Exp
->Strength
= Strength
;
781 Exp
->RadiusSquare
= (8-Size
)*(8-Size
);
783 Exp
->HurtNeutrals
= HurtNeutrals
;
784 ExplosionQueue
.push_back(Exp
);
786 if (ExplosionQueue
.size() == 1) {
787 unsigned int Explosions
= 0;
789 while (Explosions
!= ExplosionQueue
.size()) {
792 for (c
= Explosions
; c
!= ExplosionQueue
.size(); c
= TriggerExplosions(c
)) ;
793 unsigned int NewExplosions
= c
;
794 for (c
= Explosions
; c
< NewExplosions
; ++c
) {
795 if (PlayerHurt
[c
] && PLAYER
->IsEnabled()) {
796 PLAYER
->GetHitByExplosion(ExplosionQueue
[c
], ExplosionQueue
[c
]->Strength
/((PLAYER
->GetPos()-ExplosionQueue
[c
]->Pos
).GetLengthSquare()+1));
799 Explosions
= NewExplosions
;
801 for (unsigned int c
= 0; c
< ExplosionQueue
.size(); ++c
) delete ExplosionQueue
[c
];
802 ExplosionQueue
.clear();
805 for (int x
= 0; x
< XSize
; ++x
) {
806 for (int y
= 0; y
< YSize
; ++y
) {
807 Map
[x
][y
]->LastExplosionID
= 0;
814 truth
level::DrawExplosion (const explosion
*Explosion
) const {
815 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) };
816 v2 BPos
= game::CalculateScreenCoordinates(Explosion
->Pos
)-v2((6-Explosion
->Size
)<<4, (6-Explosion
->Size
)<<4);
817 v2
SizeVect(16+((6-Explosion
->Size
)<<5), 16+((6-Explosion
->Size
)<<5));
818 v2 OldSizeVect
= SizeVect
;
819 v2 PicPos
= StrengthPicPos
[Explosion
->Size
];
822 if (BPos
.X
+SizeVect
.X
<= 0) return false;
824 SizeVect
.X
+= BPos
.X
;
829 if (BPos
.Y
+SizeVect
.Y
<= 0) return false;
831 SizeVect
.Y
+= BPos
.Y
;
835 if (BPos
.X
>= RES
.X
|| BPos
.Y
>= RES
.Y
) return false;
836 if (BPos
.X
+SizeVect
.X
> RES
.X
) SizeVect
.X
= RES
.X
-BPos
.X
;
837 if (BPos
.Y
+SizeVect
.Y
> RES
.Y
) SizeVect
.Y
= RES
.Y
-BPos
.Y
;
839 int Flags
= RAND()&7;
840 blitdata BlitData
= {
842 { PicPos
.X
, PicPos
.Y
},
844 { SizeVect
.X
, SizeVect
.Y
},
850 if (!Flags
|| SizeVect
!= OldSizeVect
) {
851 BlitData
.Bitmap
= DOUBLE_BUFFER
;
852 BlitData
.Dest
= BPos
;
853 BlitData
.Luminance
= ivanconfig::GetContrastLuminance();
854 igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData
);
857 bitmap
ExplosionPic(SizeVect
);
858 ExplosionPic
.ActivateFastFlag();
859 BlitData
.Bitmap
= &ExplosionPic
;
860 BlitData
.Flags
= Flags
;
861 igraph::GetSymbolGraphic()->NormalBlit(BlitData
);
862 BlitData
.Bitmap
= DOUBLE_BUFFER
;
863 BlitData
.Dest
= BPos
;
864 BlitData
.Src
.X
= BlitData
.Src
.Y
= 0;
865 BlitData
.Luminance
= ivanconfig::GetContrastLuminance();
866 ExplosionPic
.LuminanceMaskedBlit(BlitData
);
873 struct explosioncontroller
{
874 static truth
Handler (int x
, int y
) {
875 lsquare
*Square
= Map
[x
][y
];
876 Square
->GetHitByExplosion(CurrentExplosion
);
877 return Square
->IsFlyable();
879 static lsquare
***Map
;
880 static explosion
*CurrentExplosion
;
884 lsquare
***explosioncontroller::Map
;
885 explosion
*explosioncontroller::CurrentExplosion
;
888 int level::TriggerExplosions (int MinIndex
) {
889 int LastExplosion
= ExplosionQueue
.size();
893 for (c
= MinIndex
; c
< LastExplosion
; ++c
) {
894 int EmitChange
= Min(50+ExplosionQueue
[c
]->Strength
, 255);
895 GetLSquare(ExplosionQueue
[c
]->Pos
)->SetTemporaryEmitation(MakeRGB24(EmitChange
, EmitChange
, EmitChange
));
896 if (!GetSquare(ExplosionQueue
[c
]->Pos
)->CanBeSeenByPlayer(true)) ++NotSeen
;
899 if (NotSeen
== 1) ADD_MESSAGE("You hear an explosion."); else ADD_MESSAGE("You hear explosions.");
901 game::DrawEverythingNoBlit();
903 for (c
= MinIndex
; c
< LastExplosion
; ++c
) if (DrawExplosion(ExplosionQueue
[c
])) Drawn
= true;
905 graphics::BlitDBToScreen();
906 game::GetCurrentArea()->SendNewDrawRequest();
907 clock_t StartTime
= clock();
908 while(clock()-StartTime
< 0.3*CLOCKS_PER_SEC
);
910 for (c
= MinIndex
; c
< LastExplosion
; ++c
) {
911 explosion
*Explosion
= ExplosionQueue
[c
];
912 int Radius
= 8-Explosion
->Size
;
913 game::SetPlayerWasHurtByExplosion(false);
914 explosioncontroller::Map
= Map
;
915 explosioncontroller::CurrentExplosion
= Explosion
;
917 femath::CalculateEnvironmentRectangle(Rect
, GetBorder(), Explosion
->Pos
, Radius
);
918 for (int x
= Rect
.X1
; x
<= Rect
.X2
; ++x
) {
919 mapmath
<explosioncontroller
>::DoLine(Explosion
->Pos
.X
, Explosion
->Pos
.Y
, x
, Rect
.Y1
);
920 mapmath
<explosioncontroller
>::DoLine(Explosion
->Pos
.X
, Explosion
->Pos
.Y
, x
, Rect
.Y2
);
922 for (int y
= Rect
.Y1
+1; y
< Rect
.Y2
; ++y
) {
923 mapmath
<explosioncontroller
>::DoLine(Explosion
->Pos
.X
, Explosion
->Pos
.Y
, Rect
.X1
, y
);
924 mapmath
<explosioncontroller
>::DoLine(Explosion
->Pos
.X
, Explosion
->Pos
.Y
, Rect
.X2
, y
);
926 PlayerHurt
[c
] = game::PlayerWasHurtByExplosion();
927 if (GetLSquare(Explosion
->Pos
)->IsFlyable()) GetLSquare(Explosion
->Pos
)->AddSmoke(gas::Spawn(SMOKE
, 1000));
929 for (c
= MinIndex
; c
< LastExplosion
; ++c
) GetLSquare(ExplosionQueue
[c
]->Pos
)->SetTemporaryEmitation(0);
930 return LastExplosion
;
934 truth
level::CollectCreatures (charactervector
&CharacterArray
, character
* Leader
, truth AllowHostiles
) {
935 if (!AllowHostiles
) {
936 for (int c
= 0; c
< game::GetTeams(); ++c
) {
937 if (Leader
->GetTeam()->GetRelation(game::GetTeam(c
)) == HOSTILE
) {
938 for (std::list
<character
*>::const_iterator i
= game::GetTeam(c
)->GetMember().begin(); i
!= game::GetTeam(c
)->GetMember().end(); ++i
) {
939 if ((*i
)->IsEnabled() && Leader
->CanBeSeenBy(*i
) && Leader
->SquareUnderCanBeSeenBy(*i
, true) && (*i
)->CanFollow()) {
940 ADD_MESSAGE("You can't escape when there are hostile creatures nearby.");
948 truth TakeAll
= true;
950 for (int c
= 0; c
< game::GetTeams(); ++c
) {
951 if (game::GetTeam(c
)->GetEnabledMembers() && Leader
->GetTeam()->GetRelation(game::GetTeam(c
)) == HOSTILE
) {
957 for (int c
= 0; c
< game::GetTeams(); ++c
) {
958 if (game::GetTeam(c
) == Leader
->GetTeam() || Leader
->GetTeam()->GetRelation(game::GetTeam(c
)) == HOSTILE
) {
959 for (std::list
<character
*>::const_iterator i
= game::GetTeam(c
)->GetMember().begin(); i
!= game::GetTeam(c
)->GetMember().end(); ++i
) {
960 if ((*i
)->IsEnabled() && *i
!= Leader
&&
961 (TakeAll
|| (Leader
->CanBeSeenBy(*i
) && Leader
->SquareUnderCanBeSeenBy(*i
, true))) &&
962 (*i
)->CanFollow() && (*i
)->GetCommandFlags()&FOLLOW_LEADER
)
964 if ((*i
)->GetAction() && (*i
)->GetAction()->IsVoluntary()) (*i
)->GetAction()->Terminate(false);
965 if (!(*i
)->GetAction()) {
966 ADD_MESSAGE("%s follows you.", (*i
)->CHAR_NAME(DEFINITE
));
967 CharacterArray
.push_back(*i
);
979 void level::Draw (truth AnimationDraw
) const {
980 cint XMin
= Max(game::GetCamera().X
, 0);
981 cint YMin
= Max(game::GetCamera().Y
, 0);
982 cint XMax
= Min(XSize
, game::GetCamera().X
+game::GetScreenXSize());
983 cint YMax
= Min(YSize
, game::GetCamera().Y
+game::GetScreenYSize());
984 culong LOSTick
= game::GetLOSTick();
985 blitdata BlitData
= {
989 { TILE_SIZE
, TILE_SIZE
},
992 ALLOW_ANIMATE
|ALLOW_ALPHA
995 if (!game::GetSeeWholeMapCheatMode()) {
996 if (!AnimationDraw
) {
997 for (int x
= XMin
; x
< XMax
; ++x
) {
998 BlitData
.Dest
= game::CalculateScreenCoordinates(v2(x
, YMin
));
999 lsquare
**SquarePtr
= &Map
[x
][YMin
];
1000 for (int y
= YMin
; y
< YMax
; ++y
, ++SquarePtr
, BlitData
.Dest
.Y
+= TILE_SIZE
) {
1001 const lsquare
* Square
= *SquarePtr
;
1002 culong LastSeen
= Square
->LastSeen
;
1003 if (LastSeen
== LOSTick
) Square
->Draw(BlitData
);
1004 else if ((Square
->Flags
&STRONG_BIT
) || LastSeen
== LOSTick
-2) Square
->DrawMemorized(BlitData
);
1008 for (int x
= XMin
; x
< XMax
; ++x
) {
1009 BlitData
.Dest
= game::CalculateScreenCoordinates(v2(x
, YMin
));
1010 lsquare
**SquarePtr
= &Map
[x
][YMin
];
1011 for (int y
= YMin
; y
< YMax
; ++y
, ++SquarePtr
, BlitData
.Dest
.Y
+= TILE_SIZE
) {
1012 const lsquare
*Square
= *SquarePtr
;
1013 if (Square
->LastSeen
== LOSTick
) Square
->Draw(BlitData
);
1014 else if (Square
->Flags
&STRONG_BIT
) Square
->DrawMemorized(BlitData
);
1016 ccharacter
*C
= Square
->Character
;
1018 if (C
->CanBeSeenByPlayer()) Square
->DrawMemorizedCharacter(BlitData
); else Square
->DrawMemorized(BlitData
);
1025 for (int x
= XMin
; x
< XMax
; ++x
) {
1026 BlitData
.Dest
= game::CalculateScreenCoordinates(v2(x
, YMin
));
1027 lsquare
**SquarePtr
= &Map
[x
][YMin
];
1028 for (int y
= YMin
; y
< YMax
; ++y
, ++SquarePtr
, BlitData
.Dest
.Y
+= TILE_SIZE
) (*SquarePtr
)->Draw(BlitData
);
1034 v2
level::GetEntryPos (ccharacter
*Char
, int I
) const {
1035 if (I
== FOUNTAIN
) {
1036 std::vector
<v2
> Fountains
;
1037 for (int x
= 0; x
< XSize
; ++x
) {
1038 for (int y
= 0; y
< YSize
; ++y
) {
1039 if (GetLSquare(x
,y
)->GetOLTerrain() && GetLSquare(x
,y
)->GetOLTerrain()->IsFountainWithWater()) Fountains
.push_back(v2(x
,y
));
1042 if (Fountains
.empty()) return GetRandomSquare();
1043 return Fountains
[RAND_N(Fountains
.size())];
1045 std::map
<int, v2
>::const_iterator i
= EntryMap
.find(I
);
1046 return (i
== EntryMap
.end() ? GetRandomSquare(Char
) : i
->second
);
1050 void level::GenerateRectangularRoom (std::vector
<v2
> &OKForDoor
, std::vector
<v2
> &Inside
,
1051 std::vector
<v2
> &Border
, const roomscript
*RoomScript
, room
*RoomClass
, v2 Pos
, v2 Size
)
1053 const contentscript
<glterrain
> *GTerrain
;
1054 const contentscript
<olterrain
> *OTerrain
;
1056 if (*RoomScript
->UseFillSquareWalls()) {
1057 GTerrain
= LevelScript
->GetFillSquare()->GetGTerrain();
1058 OTerrain
= LevelScript
->GetFillSquare()->GetOTerrain();
1060 GTerrain
= RoomScript
->GetWallSquare()->GetGTerrain();
1061 OTerrain
= RoomScript
->GetWallSquare()->GetOTerrain();
1064 int Room
= RoomClass
->GetIndex();
1066 truth AllowLanterns
= *RoomScript
->GenerateLanterns();
1067 truth AllowWindows
= *RoomScript
->GenerateWindows();
1069 int Shape
= *RoomScript
->GetShape();
1070 int Flags
= ((GTerrain
->IsInside() ? *GTerrain
->IsInside() : *RoomScript
->IsInside()) ? INSIDE
: 0);
1073 if ((Shape
== ROUND_CORNERS
|| Shape
== MAZE_ROOM
) && (Size
.X
< 5 || Size
.Y
< 5)) Shape
= RECTANGLE
; /* No weird shapes this way. */
1074 if ((Shape
== MAZE_ROOM
) && (!(Size
.X
% 2) || !(Size
.Y
% 2))) Shape
= RECTANGLE
; /* Alas, no even numbered maze rooms allowed. */
1076 maze
MazeRoom(Size
.X
, Size
.Y
);
1078 if (Shape
== MAZE_ROOM
) {
1079 MazeRoom
.CreateMaze();
1082 for (x
= Pos
.X
; x
< Pos
.X
+Size
.X
; ++x
, Counter
+= 2) {
1083 if (Shape
== ROUND_CORNERS
) {
1085 CreateRoomSquare(GTerrain
->Instantiate(), OTerrain
->Instantiate(), x
+1, Pos
.Y
+1, Room
, Flags
);
1086 CreateRoomSquare(GTerrain
->Instantiate(), OTerrain
->Instantiate(), x
+1, Pos
.Y
+Size
.Y
-2, Room
, Flags
);
1087 Border
.push_back(v2(x
+1, Pos
.Y
+1));
1088 Border
.push_back(v2(x
+1, Pos
.Y
+Size
.Y
-2));
1090 } else if (x
== Pos
.X
+Size
.X
-1) {
1091 CreateRoomSquare(GTerrain
->Instantiate(), OTerrain
->Instantiate(), x
-1, Pos
.Y
+1, Room
, Flags
);
1092 CreateRoomSquare(GTerrain
->Instantiate(), OTerrain
->Instantiate(), x
-1, Pos
.Y
+Size
.Y
-2, Room
, Flags
);
1093 Border
.push_back(v2(x
-1, Pos
.Y
+1));
1094 Border
.push_back(v2(x
-1, Pos
.Y
+Size
.Y
-2));
1098 CreateRoomSquare(GTerrain
->Instantiate(), OTerrain
->Instantiate(), x
, Pos
.Y
, Room
, Flags
);
1099 CreateRoomSquare(GTerrain
->Instantiate(), OTerrain
->Instantiate(), x
, Pos
.Y
+Size
.Y
-1, Room
, Flags
);
1100 if ((Shape
== RECTANGLE
&& x
!= Pos
.X
&& x
!= Pos
.X
+Size
.X
-1) ||
1101 (Shape
== ROUND_CORNERS
&& x
> Pos
.X
+1 && x
< Pos
.X
+Size
.X
-2))
1103 OKForDoor
.push_back(v2(x
, Pos
.Y
));
1104 OKForDoor
.push_back(v2(x
, Pos
.Y
+Size
.Y
-1));
1105 if ((!AllowLanterns
|| !GenerateLanterns(x
, Pos
.Y
, DOWN
)) && AllowWindows
) GenerateWindows(x
, Pos
.Y
);
1106 if ((!AllowLanterns
|| !GenerateLanterns(x
, Pos
.Y
+Size
.Y
-1, UP
)) && AllowWindows
) GenerateWindows(x
, Pos
.Y
+Size
.Y
-1);
1108 Border
.push_back(v2(x
, Pos
.Y
));
1109 Border
.push_back(v2(x
, Pos
.Y
+Size
.Y
-1));
1112 game::BusyAnimation();
1114 for (y
= Pos
.Y
+1; y
< Pos
.Y
+Size
.Y
-1; ++y
, Counter
+= 2) {
1115 CreateRoomSquare(GTerrain
->Instantiate(), OTerrain
->Instantiate(), Pos
.X
, y
, Room
, Flags
);
1116 CreateRoomSquare(GTerrain
->Instantiate(), OTerrain
->Instantiate(), Pos
.X
+Size
.X
-1, y
, Room
, Flags
);
1117 if (Shape
== RECTANGLE
|| (Shape
== ROUND_CORNERS
&& y
!= Pos
.Y
+1 && y
!= Pos
.Y
+Size
.Y
-2)) {
1118 OKForDoor
.push_back(v2(Pos
.X
, y
));
1119 OKForDoor
.push_back(v2(Pos
.X
+Size
.X
-1, y
));
1120 if ((!AllowLanterns
|| !GenerateLanterns(Pos
.X
, y
, RIGHT
)) && AllowWindows
) GenerateWindows(Pos
.X
, y
);
1121 if ((!AllowLanterns
|| !GenerateLanterns(Pos
.X
+Size
.X
-1, y
, LEFT
)) && AllowWindows
) GenerateWindows(Pos
.X
+Size
.X
-1, y
);
1123 Border
.push_back(v2(Pos
.X
, y
));
1124 Border
.push_back(v2(Pos
.X
+Size
.X
-1, y
));
1127 // Maze rooms only: put in the doors, in the corners.
1128 if (Shape
== MAZE_ROOM
) {
1129 int MazeDoors
= RAND() % 4;
1130 switch (MazeDoors
) {
1132 OKForDoor
.push_back(v2(Pos
.X
, Pos
.Y
+ 1));
1133 OKForDoor
.push_back(v2(Pos
.X
+ Size
.X
- 1, Pos
.Y
+ Size
.Y
- 2));
1136 OKForDoor
.push_back(v2(Pos
.X
+ 1, Pos
.Y
));
1137 OKForDoor
.push_back(v2(Pos
.X
+ Size
.X
- 2, Pos
.Y
+ Size
.Y
- 1));
1140 OKForDoor
.push_back(v2(Pos
.X
+ 1, Pos
.Y
+ Size
.Y
- 1));
1141 OKForDoor
.push_back(v2(Pos
.X
+ Size
.X
- 2, Pos
.Y
));
1144 OKForDoor
.push_back(v2(Pos
.X
, Pos
.Y
+ Size
.Y
- 2));
1145 OKForDoor
.push_back(v2(Pos
.X
+ Size
.X
- 1, Pos
.Y
+ 1));
1152 GTerrain
= RoomScript
->GetFloorSquare()->GetGTerrain();
1153 OTerrain
= RoomScript
->GetFloorSquare()->GetOTerrain();
1155 Flags
= ((GTerrain
->IsInside() ? *GTerrain
->IsInside() : *RoomScript
->IsInside()) ? INSIDE
: 0);
1157 for (x
= Pos
.X
+1; x
< Pos
.X
+Size
.X
-1; ++x
) {
1158 for (y
= Pos
.Y
+1; y
< Pos
.Y
+Size
.Y
-1; ++y
, ++Counter
) {
1159 /* if not in the corner */
1160 if (!(Shape
== MAZE_ROOM
) && !(Shape
== ROUND_CORNERS
&& (x
== Pos
.X
+1 || x
== Pos
.X
+Size
.X
-2) && (y
== Pos
.Y
+1 || y
== Pos
.Y
+Size
.Y
-2))) {
1161 CreateRoomSquare(GTerrain
->Instantiate(), OTerrain
->Instantiate(), x
, y
, Room
, Flags
);
1162 Inside
.push_back(v2(x
,y
));
1167 // Maze rooms only: put in the internal walls.
1168 if (Shape
== MAZE_ROOM
) {
1169 for (y
= Pos
.Y
+ 1; y
< Pos
.Y
+ Size
.Y
- 1; ++y
) {
1170 for (x
= Pos
.X
+ 1; x
< Pos
.X
+ Size
.X
- 1; ++x
, ++Counter
) {
1171 // If there is a wall here, then put a wall here. Don't the square "inside" (forbids oterrains like fountains from generating in the wall(?)).
1172 if (!MazeRoom
.MazeKernel
[(y
-Pos
.Y
-1)*(Size
.X
-2)+(x
-Pos
.X
-1)]) {
1173 if (*RoomScript
->UseFillSquareWalls()) {
1174 GTerrain
= LevelScript
->GetFillSquare()->GetGTerrain();
1175 OTerrain
= LevelScript
->GetFillSquare()->GetOTerrain();
1177 GTerrain
= RoomScript
->GetWallSquare()->GetGTerrain();
1178 OTerrain
= RoomScript
->GetWallSquare()->GetOTerrain();
1180 CreateRoomSquare(GTerrain
->Instantiate(), OTerrain
->Instantiate(), x
, y
, Room
, Flags
);
1181 } else if (MazeRoom
.MazeKernel
[(y
-Pos
.Y
-1)*(Size
.X
-2)+(x
-Pos
.X
-1)]) {
1183 GTerrain
= RoomScript
->GetFloorSquare()->GetGTerrain();
1184 OTerrain
= RoomScript
->GetFloorSquare()->GetOTerrain();
1185 CreateRoomSquare(GTerrain
->Instantiate(), OTerrain
->Instantiate(), x
, y
, Room
, Flags
);
1186 Inside
.push_back(v2(x
, y
));
1194 void level::Reveal () {
1195 feuLong Tick
= game::GetLOSTick();
1196 for (int x
= 0; x
< XSize
; ++x
) {
1197 for (int y
= 0; y
< YSize
; ++y
) {
1198 Map
[x
][y
]->Reveal(Tick
);
1204 void level::ParticleBeam (beamdata
&Beam
) {
1205 v2 CurrentPos
= Beam
.StartPos
;
1206 if (Beam
.Direction
!= YOURSELF
) {
1207 for (int Length
= 0; Length
< Beam
.Range
; ++Length
) {
1208 CurrentPos
+= game::GetMoveVector(Beam
.Direction
);
1209 if (!IsValidPos(CurrentPos
)) break;
1210 lsquare
*CurrentSquare
= GetLSquare(CurrentPos
);
1211 if (!CurrentSquare
->IsFlyable()) {
1212 (CurrentSquare
->*lsquare::GetBeamEffect(Beam
.BeamEffect
))(Beam
);
1215 CurrentSquare
->DrawParticles(Beam
.BeamColor
);
1216 if ((CurrentSquare
->*lsquare::GetBeamEffect(Beam
.BeamEffect
))(Beam
)) break;
1220 lsquare
*Where
= GetLSquare(CurrentPos
);
1221 Where
->DrawParticles(Beam
.BeamColor
);
1222 (Where
->*lsquare::GetBeamEffect(Beam
.BeamEffect
))(Beam
);
1227 /* Note: You will most likely need some help from supernatural entities to comprehend this code. Sorry. */
1228 void level::LightningBeam (beamdata
&Beam
) {
1229 v2 CurrentPos
= Beam
.StartPos
;
1231 if (Beam
.Direction
== YOURSELF
) {
1232 lsquare
*Where
= GetLSquare(CurrentPos
);
1233 for (int c
= 0; c
< 4; ++c
) Where
->DrawLightning(v2(8, 8), Beam
.BeamColor
, YOURSELF
);
1234 (Where
->*lsquare::GetBeamEffect(Beam
.BeamEffect
))(Beam
);
1240 switch (Beam
.Direction
) {
1241 case 0: StartPos
= v2(15, 15); break;
1242 case 1: StartPos
= v2(RAND()&15, 15); break;
1243 case 2: StartPos
= v2(0, 15); break;
1244 case 3: StartPos
= v2(15, RAND()&15); break;
1245 case 4: StartPos
= v2(0, RAND()&15); break;
1246 case 5: StartPos
= v2(15, 0); break;
1247 case 6: StartPos
= v2(RAND()&15, 0); break;
1248 case 7: StartPos
= v2(0, 0); break;
1249 default: StartPos
= v2(0, 0); break;
1252 for (int Length
= 0; Length
< Beam
.Range
; ++Length
) {
1253 CurrentPos
+= game::GetMoveVector(Beam
.Direction
);
1254 if (!IsValidPos(CurrentPos
)) break;
1255 lsquare
*CurrentSquare
= GetLSquare(CurrentPos
);
1256 if (!CurrentSquare
->IsFlyable()) {
1257 if ((CurrentSquare
->*lsquare::GetBeamEffect(Beam
.BeamEffect
))(Beam
)) break;
1259 switch (Beam
.Direction
) {
1261 W1
= GetLSquare(CurrentPos
+v2(1, 0))->IsFlyable();
1262 W2
= GetLSquare(CurrentPos
+v2(0, 1))->IsFlyable();
1273 case 1: Beam
.Direction
= 6; StartPos
.Y
= 0; break;
1275 W1
= GetLSquare(CurrentPos
+v2(-1, 0))->IsFlyable();
1276 W2
= GetLSquare(CurrentPos
+v2(0, 1))->IsFlyable();
1287 case 3: Beam
.Direction
= 4; StartPos
.X
= 0; break;
1288 case 4: Beam
.Direction
= 3; StartPos
.X
= 15; break;
1290 W1
= GetLSquare(CurrentPos
+v2(1, 0))->IsFlyable();
1291 W2
= GetLSquare(CurrentPos
+v2(0, -1))->IsFlyable();
1302 case 6: Beam
.Direction
= 1; StartPos
.Y
= 15; break;
1304 W1
= GetLSquare(CurrentPos
+v2(-1, 0))->IsFlyable();
1305 W2
= GetLSquare(CurrentPos
+v2(0, -1))->IsFlyable();
1317 switch (Beam
.Direction
) {
1318 case 0: StartPos
= v2(15, 15); break;
1319 case 2: StartPos
= v2(0, 15); break;
1320 case 5: StartPos
= v2(15, 0); break;
1321 case 7: StartPos
= v2(0, 0); break;
1324 StartPos
= CurrentSquare
->DrawLightning(StartPos
, Beam
.BeamColor
, Beam
.Direction
);
1325 if ((CurrentSquare
->*lsquare::GetBeamEffect(Beam
.BeamEffect
))(Beam
)) break;
1331 void level::ShieldBeam (beamdata
&Beam
) {
1333 switch (Beam
.Direction
) {
1336 Pos
[1] = v2(-1, -1);
1340 Pos
[0] = v2(-1, -1);
1352 Pos
[2] = v2(-1, -1);
1375 GetLSquare(Beam
.StartPos
)->DrawParticles(Beam
.BeamColor
);
1376 (GetLSquare(Beam
.StartPos
)->*lsquare::GetBeamEffect(Beam
.BeamEffect
))(Beam
);
1380 for (int c
= 0; c
< 3; ++c
) {
1381 if (IsValidPos(Beam
.StartPos
+Pos
[c
])) {
1382 GetLSquare(Beam
.StartPos
+Pos
[c
])->DrawParticles(Beam
.BeamColor
);
1383 (GetLSquare(Beam
.StartPos
+Pos
[c
])->*lsquare::GetBeamEffect(Beam
.BeamEffect
))(Beam
);
1389 outputfile
&operator << (outputfile
&SaveFile
, const level
*Level
) {
1390 Level
->Save(SaveFile
);
1395 inputfile
&operator >> (inputfile
&SaveFile
, level
*&Level
) {
1397 Level
->Load(SaveFile
);
1402 void (level::*Beam
[BEAM_STYLES
]) (beamdata
&) = {
1403 &level::ParticleBeam
,
1404 &level::LightningBeam
,
1409 void (level::*level::GetBeam(int I
)) (beamdata
&) {
1414 v2
level::FreeSquareSeeker (ccharacter
*Char
, v2 StartPos
, v2 Prohibited
, int MaxDistance
, truth AllowStartPos
) const {
1417 for (c
= 0; c
< 8; ++c
) {
1418 v2 Pos
= StartPos
+game::GetMoveVector(c
);
1419 if (IsValidPos(Pos
) && Char
->CanMoveOn(GetLSquare(Pos
)) && Char
->IsFreeForMe(GetLSquare(Pos
)) && Pos
!= Prohibited
&& (AllowStartPos
|| !Char
->PlaceIsIllegal(Pos
, Prohibited
))) {
1425 for (c
= 0; c
< 8; ++c
) {
1426 v2 Pos
= StartPos
+game::GetMoveVector(c
);
1427 if (IsValidPos(Pos
)) {
1428 if (Char
->CanMoveOn(GetLSquare(Pos
)) && Pos
!= Prohibited
) {
1429 Pos
= FreeSquareSeeker(Char
, Pos
, Prohibited
, MaxDistance
-1, AllowStartPos
);
1430 if (Pos
!= ERROR_V2
) return Pos
;
1440 /* Returns ERROR_V2 if no free square was found */
1441 v2
level::GetNearestFreeSquare (ccharacter
*Char
, v2 StartPos
, truth AllowStartPos
) const {
1442 if (AllowStartPos
&& Char
->CanMoveOn(GetLSquare(StartPos
)) && Char
->IsFreeForMe(GetLSquare(StartPos
))) return StartPos
;
1446 for (c
= 0; c
< 8; ++c
) {
1447 v2 Pos
= StartPos
+game::GetMoveVector(c
);
1448 if (IsValidPos(Pos
) && Char
->CanMoveOn(GetLSquare(Pos
)) && Char
->IsFreeForMe(GetLSquare(Pos
)) && (AllowStartPos
|| !Char
->PlaceIsIllegal(Pos
, StartPos
))) {
1453 for (int Dist
= 0; Dist
< 5; ++Dist
) {
1454 for (c
= 0; c
< 8; ++c
) {
1455 v2 Pos
= StartPos
+game::GetMoveVector(c
);
1456 if (IsValidPos(Pos
) && Char
->CanMoveOn(GetLSquare(Pos
))) {
1457 Pos
= FreeSquareSeeker(Char
, Pos
, StartPos
, Dist
, AllowStartPos
);
1458 if (Pos
!= ERROR_V2
) return Pos
;
1467 v2
level::GetFreeAdjacentSquare (ccharacter
*Char
, v2 StartPos
, truth AllowCharacter
) const {
1470 lsquare
*Origo
= GetLSquare(StartPos
);
1471 for (int d
= 0; d
< 8; ++d
) {
1472 lsquare
*Square
= Origo
->GetNeighbourLSquare(d
);
1473 if (Square
&& Char
->CanMoveOn(Square
) && (AllowCharacter
|| Char
->IsFreeForMe(Square
))) PossibleDir
[Index
++] = d
;
1475 return (Index
? StartPos
+game::GetMoveVector(PossibleDir
[RAND()%Index
]) : ERROR_V2
);
1479 void (level::*level::GetBeamEffectVisualizer(int I
)) (const fearray
<lsquare
*>&, col16
) const {
1480 static void (level::*Visualizer
[BEAM_STYLES
])(const fearray
<lsquare
*>&, col16
) const = { &level::ParticleVisualizer
, &level::LightningVisualizer
, &level::ParticleVisualizer
};
1481 return Visualizer
[I
];
1485 void level::ParticleVisualizer (const fearray
<lsquare
*> &Stack
, col16 BeamColor
) const {
1486 clock_t StartTime
= clock();
1487 game::DrawEverythingNoBlit();
1488 for (fearray
<lsquare
*>::sizetype c
= 0; c
< Stack
.Size
; ++c
) Stack
[c
]->DrawParticles(BeamColor
, false);
1489 graphics::BlitDBToScreen();
1490 while (clock()-StartTime
< 0.05*CLOCKS_PER_SEC
) {}
1494 void level::LightningVisualizer (const fearray
<lsquare
*> &Stack
, col16 BeamColor
) const {
1495 clock_t StartTime
= clock();
1496 game::DrawEverythingNoBlit();
1497 for (fearray
<lsquare
*>::sizetype c
= 0; c
< Stack
.Size
; ++c
) Stack
[c
]->DrawLightning(v2(8, 8), BeamColor
, YOURSELF
, false);
1498 graphics::BlitDBToScreen();
1499 while (clock()-StartTime
< 0.05*CLOCKS_PER_SEC
) {}
1503 truth
level::PreProcessForBone () {
1504 if (!*LevelScript
->CanGenerateBone()) return false;
1506 game::SetQuestMonstersFound(0);
1507 for (int x
= 0; x
< XSize
; ++x
) {
1508 for (int y
= 0; y
< YSize
; ++y
) {
1509 Map
[x
][y
]->PreProcessForBone();
1512 //int DungeonIndex = GetDungeon()->GetIndex();
1513 /*k8: this logic allows to generate bones on special levels; i don't quite understand it, but... */
1514 /* ok, let's use level tags for special levels, instead of hardcoding the numbers */
1517 !(DungeonIndex == ELPURI_CAVE && Index == IVAN_LEVEL && game::GetQuestMonstersFound() < 5) &&
1518 (game::GetQuestMonstersFound() ||
1519 ((DungeonIndex != UNDER_WATER_TUNNEL || Index != VESANA_LEVEL) &&
1520 (DungeonIndex != ELPURI_CAVE || (Index != ENNER_BEAST_LEVEL && Index != DARK_LEVEL)) &&
1521 (DungeonIndex != ALIEN_VESSEL || Index != ALIENQUEEN_LEVEL)));
1523 // Ivan the Communist level?
1524 if (IsGCIvanLevel()) return (game::GetQuestMonstersFound() >= 5); // why 5?
1525 // other special level conditions are in effect only if no quest mosters are found yet
1526 if (game::GetQuestMonstersFound()) return true; // see above
1527 // other special levels
1528 cfestring
*tag
= GetLevelScript()->GetTag();
1529 if (!tag
) return true;
1530 return (tag
->Find("(!)") == festring::NPos
);
1533 // check for special levels
1534 truth
level::IsGCIvanLevel () const { return (GetLevelScript()->GetTag() ? *(GetLevelScript()->GetTag()) == "GCIvanLevel(!)" : false); }
1536 truth level::IsUTVesanaLevel () const { return (GetTag() ? *GetTag() == "UTVesanaLevel(!)" : false); }
1537 truth level::IsGCEnnerLevel () const { return (GetTag() ? *GetTag() == "GCEnnerBeastLevel(!)" : false); }
1538 truth level::IsGCElpuriLevel () const { return (GetTag() ? *GetTag() == "GCElpuriLevel(!)" : false); }
1539 truth level::IsGCOreeLevel () const { return (GetTag() ? *GetTag() == "GCOreeLair(!)" : false); }
1540 truth level::IsSolicitusLevel () const { return (GetTag() ? *GetTag() == "SolicitusLevel(!)" : false); }
1541 truth level::IsAlienQueenLevel () const { return (GetTag() ? *GetTag() == "AlienQueenLevel(!)" : false); }
1545 truth
level::PostProcessForBone () {
1546 game::SetTooGreatDangerFound(false);
1547 double DangerSum
= 0;
1549 for (int x
= 0; x
< XSize
; ++x
) {
1550 for (int y
= 0; y
< YSize
; ++y
) {
1551 Map
[x
][y
]->PostProcessForBone(DangerSum
, Enemies
);
1554 return !(game::TooGreatDangerFound() || (Enemies
&& DangerSum
/Enemies
> Difficulty
*10));
1558 void level::FinalProcessForBone () {
1559 for (int x
= 0; x
< XSize
; ++x
) {
1560 for (int y
= 0; y
< YSize
; ++y
) {
1561 Map
[x
][y
]->FinalProcessForBone();
1564 for (uInt c
= 1; c
< Room
.size(); ++c
) Room
[c
]->FinalProcessForBone();
1568 truth
level::GenerateDungeon (int Index
) {
1569 cfestring
*Msg
= LevelScript
->GetLevelMessage();
1570 if (Msg
) LevelMessage
= *Msg
;
1572 if (*LevelScript
->GenerateMonsters()) {
1573 MonsterGenerationInterval
= *LevelScript
->GetMonsterGenerationIntervalBase() + *LevelScript
->GetMonsterGenerationIntervalDelta()*Index
;
1574 IdealPopulation
= *LevelScript
->GetMonsterAmountBase() + *LevelScript
->GetMonsterAmountDelta()*Index
;
1577 Difficulty
= 0.001*(*LevelScript
->GetDifficultyBase() + *LevelScript
->GetDifficultyDelta()*Index
);
1578 EnchantmentMinusChance
= *LevelScript
->GetEnchantmentMinusChanceBase() + *LevelScript
->GetEnchantmentMinusChanceDelta()*Index
;
1579 EnchantmentPlusChance
= *LevelScript
->GetEnchantmentPlusChanceBase() + *LevelScript
->GetEnchantmentPlusChanceDelta()*Index
;
1580 const contentscript
<glterrain
> *GTerrain
= LevelScript
->GetFillSquare()->GetGTerrain();
1581 const contentscript
<olterrain
> *OTerrain
= LevelScript
->GetFillSquare()->GetOTerrain();
1585 game::BusyAnimation();
1587 for (x
= 0; x
< XSize
; ++x
) {
1588 for (int y
= 0; y
< YSize
; ++y
, ++Counter
) {
1589 Map
[x
][y
]->SetLTerrain(GTerrain
->Instantiate(), OTerrain
->Instantiate());
1594 uInt Rooms
= LevelScript
->GetRooms()->Randomize();
1595 const std::list
<roomscript
>& RoomList
= LevelScript
->GetRoom();
1596 std::list
<roomscript
>::const_iterator Iterator
= RoomList
.begin();
1598 for (c
= 0; c
< Rooms
; ++c
) {
1599 game::BusyAnimation();
1600 if (c
< RoomList
.size()) {
1602 for (i
= 0; i
< 1000; ++i
) if (MakeRoom(&*Iterator
)) break;
1603 if (i
== 1000) { /*ABORT("Failed to place special room #%d!", c);*/ return false; }
1606 const roomscript
*RoomScript
= LevelScript
->GetRoomDefault();
1607 for (int i
= 0; i
< 50; ++i
) if (MakeRoom(RoomScript
)) break;
1611 game::BusyAnimation();
1613 if (!*LevelScript
->IgnoreDefaultSpecialSquares()) {
1615 const levelscript
*LevelBase
= static_cast<const levelscript
*>(LevelScript
->GetBase());
1617 const std::list
<squarescript
> &Square
= LevelBase
->GetSquare();
1618 for (std::list
<squarescript
>::const_iterator i
= Square
.begin(); i
!= Square
.end(); ++i
) {
1619 game::BusyAnimation();
1620 ApplyLSquareScript(&*i
);
1625 const std::list
<squarescript
> &Square
= LevelScript
->GetSquare();
1627 for (std::list
<squarescript
>::const_iterator i
= Square
.begin(); i
!= Square
.end(); ++i
) {
1628 game::BusyAnimation();
1629 ApplyLSquareScript(&*i
);
1632 for (c
= 0; c
< AttachQueue
.size(); ++c
) AttachPos(AttachQueue
[c
].X
, AttachQueue
[c
].Y
);
1634 for (x
= 0; x
< XSize
; ++x
) {
1635 for (int y
= 0; y
< YSize
; ++y
) {
1636 Map
[x
][y
]->CalculateGroundBorderPartners();
1637 Map
[x
][y
]->CalculateOverBorderPartners();
1641 AttachQueue
.clear();
1642 CreateItems(LevelScript
->GetItems()->Randomize());
1648 truth
level::GenerateJungle () {
1651 for (x
= 0; x
< XSize
; ++x
) {
1652 for (y
= 0; y
< YSize
; ++y
) {
1653 Map
[x
][y
] = new lsquare(this, v2(x
, y
));
1654 Map
[x
][y
]->SetLTerrain(solidterrain::Spawn(GRASS_TERRAIN
), 0);
1659 CreateTunnelNetwork(1, 4, 20, 120, v2(0, YSize
/2));
1660 CreateTunnelNetwork(1, 4, 20, 120, v2(XSize
-1, YSize
/2));
1662 for (int c
= 0; c
< 25; ++c
) {
1666 case 0: StartPos
= v2(RAND_N(XSize
), 0); break;
1667 case 1: StartPos
= v2(RAND_N(XSize
), YSize
-1); break;
1668 case 2: StartPos
= v2(0, RAND_N(YSize
)); break;
1669 case 3: StartPos
= v2(XSize
-1, RAND_N(YSize
)); break;
1670 case 4: StartPos
= v2(RAND_N(XSize
), RAND_N(YSize
)); break;
1671 default: StartPos
= v2(0, 0); break; /* k8: shut up the compiler */
1673 CreateTunnelNetwork(1,4,20, 120, StartPos
);
1676 for (x
= 0; x
< XSize
; ++x
) {
1677 game::BusyAnimation();
1678 for (y
= 0; y
< YSize
; ++y
) {
1679 if(FlagMap
[x
][y
] != PREFERRED
) Map
[x
][y
]->ChangeOLTerrain(wall::Spawn(BRICK_PROPAGANDA
));
1680 else if(RAND_2
) Map
[x
][y
]->ChangeOLTerrain(decoration::Spawn(PALM
));
1689 void level::CreateTunnelNetwork (int MinLength
, int MaxLength
, int MinNodes
, int MaxNodes
, v2 StartPos
) {
1690 v2 Pos
= StartPos
, Direction
;
1692 game::BusyAnimation();
1693 FlagMap
[Pos
.X
][Pos
.Y
] = PREFERRED
;
1694 for (int c1
= 0; c1
< MaxNodes
; ++c1
) {
1695 Direction
= game::GetBasicMoveVector(RAND()%4);
1696 Length
= MinLength
+RAND_N(MaxLength
-MinLength
+1);
1697 for (int c2
= 0; c2
< Length
; ++c2
) {
1698 if (IsValidPos(Direction
+Pos
)) {
1700 FlagMap
[Pos
.X
][Pos
.Y
] = PREFERRED
;
1702 if (c1
>= MinNodes
) return;
1710 truth
level::GenerateDesert () {
1711 for (int x
= 0; x
< XSize
; ++x
) {
1712 for (int y
= 0; y
< YSize
; ++y
) {
1713 Map
[x
][y
] = new lsquare(this, v2(x
, y
));
1714 Map
[x
][y
]->SetLTerrain(solidterrain::Spawn(SAND_TERRAIN
), 0);
1717 game::BusyAnimation();
1718 int AmountOfCactuses
= RAND_N(10);
1720 for (c
= 0; c
< AmountOfCactuses
; ++c
) Map
[RAND_N(XSize
)][RAND_N(YSize
)]->ChangeOLTerrain(decoration::Spawn(CACTUS
));
1721 int AmountOfBoulders
= RAND_N(10);
1722 for (c
= 0; c
< AmountOfBoulders
; ++c
) Map
[RAND_N(XSize
)][RAND_N(YSize
)]->ChangeOLTerrain(boulder::Spawn(1+RAND_2
));
1728 truth
level::GenerateSteppe () {
1729 for (int x
= 0; x
< XSize
; ++x
) {
1730 for (int y
= 0; y
< YSize
; ++y
) {
1731 Map
[x
][y
] = new lsquare(this, v2(x
, y
));
1732 Map
[x
][y
]->SetLTerrain(solidterrain::Spawn(GRASS_TERRAIN
), 0);
1735 game::BusyAnimation();
1737 int AmountOfBoulders
= RAND_N(20)+5;
1738 for (c
= 0; c
< AmountOfBoulders
; ++c
) Map
[RAND_N(XSize
)][RAND_N(YSize
)]->ChangeOLTerrain(boulder::Spawn(1+RAND_2
));
1744 truth
level::GenerateLeafyForest () {
1745 for (int x
= 0; x
< XSize
; ++x
) {
1746 for (int y
= 0; y
< YSize
; ++y
) {
1747 Map
[x
][y
] = new lsquare(this, v2(x
, y
));
1748 olterrain
*OLTerrain
;
1750 case 0: if (RAND_8
) OLTerrain
= decoration::Spawn(OAK
); else OLTerrain
= decoration::Spawn(TEAK
); break;
1751 case 1: OLTerrain
= decoration::Spawn(BIRCH
); break;
1752 case 2: OLTerrain
= 0; if (!RAND_4
) OLTerrain
= boulder::Spawn(1+RAND_2
); if (!RAND_4
) OLTerrain
= boulder::Spawn(3); break;
1753 default: OLTerrain
= 0;
1755 Map
[x
][y
]->SetLTerrain(solidterrain::Spawn(GRASS_TERRAIN
), OLTerrain
);
1763 truth
level::GenerateEvergreenForest () {
1764 for (int x
= 0; x
< XSize
; ++x
) {
1765 for (int y
= 0; y
< YSize
; ++y
) {
1766 Map
[x
][y
] = new lsquare(this, v2(x
, y
));
1767 olterrain
*OLTerrain
= 0;
1769 case 0: if (RAND_2
) OLTerrain
= decoration::Spawn(PINE
); break;
1770 case 1: OLTerrain
= decoration::Spawn(FIR
); break;
1771 case 2: if (!RAND_4
) OLTerrain
= boulder::Spawn(1+RAND_2
); if (!RAND_4
) OLTerrain
= boulder::Spawn(3); break;
1773 Map
[x
][y
]->SetLTerrain(solidterrain::Spawn(GRASS_TERRAIN
), OLTerrain
);
1781 truth
level::GenerateTundra () {
1782 for (int x
= 0; x
< XSize
; ++x
) {
1783 for (int y
= 0; y
< YSize
; ++y
) {
1784 Map
[x
][y
] = new lsquare(this, v2(x
, y
));
1785 Map
[x
][y
]->SetLTerrain(solidterrain::Spawn(SNOW_TERRAIN
), 0);
1788 game::BusyAnimation();
1790 int AmountOfBoulders
= RAND_N(20)+8;
1791 for (c
= 0; c
< AmountOfBoulders
; ++c
) Map
[RAND_N(XSize
)][RAND_N(YSize
)]->ChangeOLTerrain(boulder::Spawn(SNOW_BOULDER
));
1792 int AmountOfDwarfBirches
= RAND_N(10);
1793 for (c
= 0; c
< AmountOfDwarfBirches
; ++c
) Map
[RAND_N(XSize
)][RAND_N(YSize
)]->ChangeOLTerrain(decoration::Spawn(DWARF_BIRCH
));
1799 truth
level::GenerateGlacier () {
1802 for (x
= 0; x
< XSize
; ++x
) {
1803 for (y
= 0; y
< YSize
; ++y
) {
1804 Map
[x
][y
] = new lsquare(this, v2(x
, y
));
1805 Map
[x
][y
]->SetLTerrain(solidterrain::Spawn(SNOW_TERRAIN
), 0);
1809 int AmountOfBoulders
= RAND_N(20)+5;
1811 for (int c
= 0; c
< AmountOfBoulders
; ++c
)
1812 Map
[RAND_N(XSize
)][RAND_N(YSize
)]->ChangeOLTerrain(boulder::Spawn(SNOW_BOULDER
));
1815 CreateTunnelNetwork(1,4,20, 120, v2(0,YSize
/2));
1816 CreateTunnelNetwork(1,4,20, 120, v2(XSize
-1,YSize
/2));
1818 for (int c
= 0; c
< 20; ++c
) {
1821 case 0: StartPos
= v2(RAND_N(XSize
), 0); break;
1822 case 1: StartPos
= v2(RAND_N(XSize
), YSize
-1); break;
1823 case 2: StartPos
= v2(0, RAND_N(YSize
)); break;
1824 case 3: StartPos
= v2(XSize
-1, RAND_N(YSize
)); break;
1825 case 4: StartPos
= v2(RAND_N(XSize
), RAND_N(YSize
)); break;
1826 default: StartPos
= v2(0, 0); break; /* k8: shut up the compiler */
1828 CreateTunnelNetwork(1,4,20, 120, StartPos
);
1831 for (x
= 0; x
< XSize
; ++x
)
1832 for (y
= 0; y
< YSize
; ++y
)
1833 if(FlagMap
[x
][y
] != PREFERRED
) FlagMap
[x
][y
] |= RAND_2
?ICE_TERRAIN
:STONE_TERRAIN
;
1835 for (x
= 0; x
< XSize
; ++x
) {
1836 game::BusyAnimation();
1837 for (y
= 0; y
< YSize
; ++y
) {
1838 if (!(FlagMap
[x
][y
]&PREFERRED
)) {
1839 int SquaresAround
= 0;
1841 for (int d
= 0; d
< 8; ++d
) {
1842 v2 Pos
= v2(x
,y
)+game::GetMoveVector(d
);
1843 if (IsValidPos(Pos
) && !(FlagMap
[Pos
.X
][Pos
.Y
]&PREFERRED
)) {
1845 if(FlagMap
[Pos
.X
][Pos
.Y
]&ICE_TERRAIN
) ++IceAround
;
1848 if (IceAround
> SquaresAround
/2) FlagMap
[x
][y
] = ICE_TERRAIN
;
1849 else FlagMap
[x
][y
] = STONE_TERRAIN
;
1854 for (x
= 0; x
< XSize
; ++x
) {
1855 for (y
= 0; y
< YSize
; ++y
) {
1856 if (!(FlagMap
[x
][y
]&PREFERRED
)) {
1857 if(FlagMap
[x
][y
]&ICE_TERRAIN
) GetLSquare(x
,y
)->ChangeOLTerrain(wall::Spawn(ICE_WALL
));
1858 else GetLSquare(x
,y
)->ChangeOLTerrain(wall::Spawn(STONE_WALL
));
1863 break; // Doesn't yet check path in any way
1870 bool nodepointerstorer::operator < (const nodepointerstorer
&N
) const {
1871 /* In the non-euclidean geometry of IVAN, certain very curved paths are as long as straight ones.
1872 However, they are so ugly that it is best to prefer routes with as few diagonal moves as
1873 possible without lengthening the travel. */
1874 if (Node
->TotalDistanceEstimate
!= N
.Node
->TotalDistanceEstimate
) {
1875 return (Node
->TotalDistanceEstimate
> N
.Node
->TotalDistanceEstimate
);
1877 return (Node
->Diagonals
> N
.Node
->Diagonals
);
1881 void node::CalculateNextNodes () {
1882 static const int TryOrder
[8] = { 1, 3, 4, 6, 0, 2, 5, 7 };
1883 for (int d
= 0; d
< 8; ++d
) {
1884 v2 NodePos
= Pos
+game::GetMoveVector(TryOrder
[d
]);
1885 if (NodePos
.X
>= 0 && NodePos
.Y
>= 0 && NodePos
.X
< XSize
&& NodePos
.Y
< YSize
) {
1886 node
*Node
= NodeMap
[NodePos
.X
][NodePos
.Y
];
1887 if (!Node
->Processed
&& ((!SpecialMover
&& RequiredWalkability
&WalkabilityMap
[NodePos
.X
][NodePos
.Y
]) || (SpecialMover
&& SpecialMover
->CanTheoreticallyMoveOn(Node
->Square
)) || NodePos
== To
)) {
1888 Node
->Processed
= true;
1889 Node
->Distance
= Distance
+1;
1890 Node
->Diagonals
= Diagonals
;
1891 if (d
>= 4) ++Node
->Diagonals
;
1893 /* We use the heuristic max(abs(distance.x), abs(distance.y)) here,
1894 which is exact in the current geometry if the path is open */
1895 sLong Remaining
= To
.X
-NodePos
.X
;
1896 if (Remaining
< NodePos
.X
-To
.X
) Remaining
= NodePos
.X
-To
.X
;
1897 if (Remaining
< NodePos
.Y
-To
.Y
) Remaining
= NodePos
.Y
-To
.Y
;
1898 if (Remaining
< To
.Y
-NodePos
.Y
) Remaining
= To
.Y
-NodePos
.Y
;
1899 Node
->Remaining
= Remaining
;
1900 Node
->TotalDistanceEstimate
= Node
->Distance
+Node
->Remaining
;
1901 NodeQueue
->push(nodepointerstorer(Node
));
1908 /* Finds the shortest (but possibly not the shortest-looking) path between From and To
1909 if such exists. Returns a pointer to the node associated with the last square or zero if
1910 a route can't be found. Calling FindRoute again may invalidate the node, so you must
1911 store the path in another format ASAP. */
1912 node
*level::FindRoute (v2 From
, v2 To
, const std::set
<v2
>& Illegal
, int RequiredWalkability
, ccharacter
* SpecialMover
) {
1913 node::NodeMap
= NodeMap
;
1914 node::RequiredWalkability
= RequiredWalkability
;
1915 node::SpecialMover
= SpecialMover
;
1917 node::WalkabilityMap
= WalkabilityMap
;
1918 node::XSize
= XSize
;
1919 node::YSize
= YSize
;
1921 if (!Illegal
.empty() && Illegal
.find(To
) != Illegal
.end()) return 0;
1923 for (int x
= 0; x
< XSize
; ++x
) {
1924 for (int y
= 0; y
< YSize
; ++y
) {
1925 NodeMap
[x
][y
]->Processed
= false;
1929 node
*Node
= NodeMap
[From
.X
][From
.Y
];
1931 Node
->Processed
= true;
1933 Node
->Diagonals
= 0;
1934 nodequeue NodeQueue
;
1935 NodeQueue
.push(nodepointerstorer(Node
));
1936 node::NodeQueue
= &NodeQueue
;
1938 while (!NodeQueue
.empty()) {
1939 Node
= NodeQueue
.top().Node
;
1941 if (Node
->Pos
== To
) return Node
;
1942 if (Illegal
.empty() || Illegal
.find(Node
->Pos
) == Illegal
.end()) Node
->CalculateNextNodes();
1949 /* All items on ground are moved to the IVector and all characters to CVector */
1950 void level::CollectEverything (itemvector
&IVector
, charactervector
&CVector
) {
1951 for (int x
= 0; x
< XSize
; ++x
) {
1952 for (int y
= 0; y
< YSize
; ++y
) {
1953 lsquare
*LS
= Map
[x
][y
];
1954 LS
->GetStack()->MoveItemsTo(IVector
, CENTER
);
1955 character
*C
= LS
->GetCharacter();
1956 if (C
&& !C
->IsPlayer()) {
1958 CVector
.push_back(C
);
1965 void level::CreateGlobalRain (liquid
*Liquid
, v2 Speed
) {
1966 GlobalRainLiquid
= Liquid
;
1967 GlobalRainSpeed
= Speed
;
1968 for (int x
= 0; x
< XSize
; ++x
) {
1969 for (int y
= 0; y
< YSize
; ++y
) {
1970 if (!Map
[x
][y
]->IsInside()) Map
[x
][y
]->AddRain(Liquid
, Speed
, MONSTER_TEAM
, false);
1976 void level::CheckSunLight () {
1977 if (Index
== 0 && GetDungeon()->GetIndex() == NEW_ATTNAM
) {
1978 double Cos
= cos(FPI
*(game::GetTick()%48000)/24000.0);
1980 int E
= int(100+Cos
*30);
1981 SunLightEmitation
= MakeRGB24(E
, E
, E
);
1982 AmbientLuminance
= MakeRGB24(E
-6, E
-6, E
-6);
1984 SunLightEmitation
= 0;
1985 AmbientLuminance
= NightAmbientLuminance
;
1987 } else if (Index
== 0 && GetDungeon()->GetIndex() == ATTNAM
) {
1988 double Cos
= cos(FPI
*(game::GetTick()%48000)/24000.0);
1990 int E
= int(100+(Cos
-0.40)*40);
1991 SunLightEmitation
= MakeRGB24(E
, E
, E
);
1992 AmbientLuminance
= MakeRGB24(E
-8, E
-8, E
-8);
1994 SunLightEmitation
= 0;
1995 AmbientLuminance
= NightAmbientLuminance
;
1997 } else if (Index
== 0 && GetDungeon()->GetIndex() == MUNTUO
) {
1998 double Cos
= cos(FPI
*(game::GetTick()%48000)/24000.0);
2000 int E
= int(100+(Cos
-0.20)*30);
2001 SunLightEmitation
= MakeRGB24(E
, E
, E
);
2002 AmbientLuminance
= MakeRGB24(E
-4, E
-4, E
-4);
2004 SunLightEmitation
= 0;
2005 AmbientLuminance
= NightAmbientLuminance
;
2010 SunLightDirection
= game::GetSunLightDirectionVector();
2015 void level::ChangeSunLight () {
2016 truth SunSet
= game::IsDark(SunLightEmitation
);
2018 for (c
= 0; c
< XSizeTimesYSize
; ++c
) Map
[0][c
]->RemoveSunLight();
2019 if (!SunSet
) EmitSunBeams();
2020 for (c
= 0; c
< XSizeTimesYSize
; ++c
) {
2021 lsquare
*Square
= Map
[0][c
];
2022 if (Square
->Flags
&IS_TRANSPARENT
) Square
->CalculateSunLightLuminance(EMITTER_SQUARE_PART_BITS
);
2023 if (!Square
->IsInside()) Square
->AmbientLuminance
= AmbientLuminance
;
2024 Square
->SendSunLightSignals();
2026 for (c
= 0; c
< XSizeTimesYSize
; ++c
) Map
[0][c
]->CheckIfIsSecondarySunLightEmitter();
2030 void level::InitSquarePartEmitationTicks () {
2031 for (int x
= 0; x
< XSize
; ++x
) {
2032 for (int y
= 0; y
< YSize
; ++y
) {
2033 Map
[x
][y
]->SquarePartEmitationTick
= 0;
2039 truth
level::GenerateWindows (int X
, int Y
) const {
2040 olterrain
*Terrain
= Map
[X
][Y
]->GetOLTerrain();
2041 if (Terrain
&& Terrain
->CreateWindowConfigurations() && !(RAND()%6)) {
2042 Terrain
->SetConfig(Terrain
->GetConfig()|WINDOW
);
2043 Map
[X
][Y
]->CalculateIsTransparent();
2050 struct sunbeamcontroller
: public stackcontroller
{
2051 static truth
Handler (int, int);
2052 static void ProcessStack ();
2055 static int SunLightBlockHeight
;
2056 static v2 SunLightBlockPos
;
2057 static truth ReSunEmitation
;
2061 feuLong
sunbeamcontroller::ID
;
2062 int sunbeamcontroller::SunLightBlockHeight
;
2063 v2
sunbeamcontroller::SunLightBlockPos
;
2064 truth
sunbeamcontroller::ReSunEmitation
;
2067 void level::ForceEmitterNoxify (const emittervector
& Emitter
) const {
2068 for (emittervector::const_iterator i
= Emitter
.begin(); i
!= Emitter
.end(); ++i
) {
2070 lsquare
*Square
= GetLSquare(ExtractPosFromEmitterID(ID
));
2071 if (ID
&SECONDARY_SUN_LIGHT
) {
2072 Square
->Noxify(Square
->SecondarySunLightEmitation
, SECONDARY_SUN_LIGHT
);
2074 Square
->Noxify(Square
->Emitation
);
2080 void level::ForceEmitterEmitation (const emittervector
&Emitter
, const sunemittervector
&SunEmitter
, feuLong IDFlags
) const {
2081 for (emittervector::const_iterator i
= Emitter
.begin(); i
!= Emitter
.end(); ++i
) {
2083 lsquare
*Square
= GetLSquare(ExtractPosFromEmitterID(ID
));
2084 if (ID
&SECONDARY_SUN_LIGHT
) {
2085 Square
->Emitate(Square
->SecondarySunLightEmitation
, SECONDARY_SUN_LIGHT
|IDFlags
);
2087 Square
->Emitate(Square
->Emitation
, IDFlags
);
2091 stackcontroller::Map
= Map
;
2092 stackcontroller::Stack
= SquareStack
;
2093 stackcontroller::StackIndex
= 0;
2094 stackcontroller::LevelXSize
= XSize
;
2095 stackcontroller::LevelYSize
= YSize
;
2096 sunbeamcontroller::ReSunEmitation
= true;
2097 for (sunemittervector::const_iterator i
= SunEmitter
.begin(); i
!= SunEmitter
.end(); ++i
) {
2098 feuLong ID
= (*i
&~(EMITTER_SHADOW_BITS
|EMITTER_SQUARE_PART_BITS
))|RE_SUN_EMITATED
, SourceFlags
;
2100 if (ID
&ID_X_COORDINATE
) {
2101 X
= (ID
&EMITTER_IDENTIFIER_BITS
)-(XSize
<<3);
2102 Y
= (ID
&ID_BEGIN
? -1 : YSize
);
2103 SourceFlags
= (ID
&ID_BEGIN
? SP_BOTTOM
: SP_TOP
);
2105 X
= (ID
&ID_BEGIN
? -1 : XSize
);
2106 Y
= (ID
&EMITTER_IDENTIFIER_BITS
)-(YSize
<<3);
2107 SourceFlags
= (ID
&ID_BEGIN
? SP_RIGHT
: SP_LEFT
);
2109 EmitSunBeam(v2(X
, Y
), ID
, SourceFlags
);
2111 sunbeamcontroller::ProcessStack();
2116 struct loscontroller
: public tickcontroller
, public stackcontroller
{
2117 static truth
Handler (int x
, int y
) {
2118 lsquare
*Square
= Map
[x
>>1][y
>>1];
2119 culong SquareFlags
= Square
->Flags
;
2120 if (SquareFlags
&PERFECTLY_QUADRI_HANDLED
) return true;
2121 if (!(SquareFlags
&IN_SQUARE_STACK
)) {
2122 Square
->Flags
|= IN_SQUARE_STACK
;
2123 Stack
[StackIndex
++] = Square
;
2125 if (SquareFlags
&IS_TRANSPARENT
) {
2126 Square
->Flags
|= PERFECTLY_QUADRI_HANDLED
;
2129 cint SquarePartIndex
= (x
&1)+((y
&1)<<1);
2130 Square
->SquarePartLastSeen
= (Square
->SquarePartLastSeen
&~SquarePartTickMask
[SquarePartIndex
])|ShiftedTick
[SquarePartIndex
];
2133 static feuLong
&GetTickReference (int X
, int Y
) {
2134 return Map
[X
][Y
]->SquarePartLastSeen
;
2136 static void ProcessStack () {
2137 for (sLong c
= 0; c
< StackIndex
; ++c
) Stack
[c
]->SignalSeen(Tick
);
2142 void level::UpdateLOS () {
2143 game::RemoveLOSUpdateRequest();
2144 stackcontroller::Map
= Map
;
2145 stackcontroller::Stack
= SquareStack
;
2146 stackcontroller::StackIndex
= 0;
2147 tickcontroller::Tick
= game::IncreaseLOSTick();
2148 tickcontroller::PrepareShiftedTick();
2149 int Radius
= PLAYER
->GetLOSRange();
2150 for (int c
= 0; c
< PLAYER
->GetSquaresUnder(); ++c
) {
2151 mapmath
<loscontroller
>::DoQuadriArea(PLAYER
->GetPos(c
).X
, PLAYER
->GetPos(c
).Y
, Radius
*Radius
, XSize
, YSize
);
2153 loscontroller::ProcessStack();
2154 if (PLAYER
->StateIsActivated(INFRA_VISION
)) {
2155 for (int c
= 0; c
< game::GetTeams(); ++c
) {
2156 for (std::list
<character
*>::const_iterator i
= game::GetTeam(c
)->GetMember().begin(); i
!= game::GetTeam(c
)->GetMember().end(); ++i
) {
2157 if ((*i
)->IsEnabled()) (*i
)->SendNewDrawRequest();
2164 void level::EnableGlobalRain () {
2165 for (int x
= 0; x
< XSize
; ++x
) {
2166 for (int y
= 0; y
< YSize
; ++y
) {
2167 Map
[x
][y
]->EnableGlobalRain();
2173 void level::DisableGlobalRain () {
2174 for (int x
= 0; x
< XSize
; ++x
) {
2175 for (int y
= 0; y
< YSize
; ++y
) {
2176 Map
[x
][y
]->DisableGlobalRain();
2182 void level::InitLastSeen () {
2183 for (int x
= 0; x
< XSize
; ++x
) {
2184 for (int y
= 0; y
< YSize
; ++y
) {
2185 Map
[x
][y
]->InitLastSeen();
2191 void level::EmitSunBeams () {
2192 stackcontroller::Map
= Map
;
2193 stackcontroller::LevelXSize
= XSize
;
2194 stackcontroller::LevelYSize
= YSize
;
2195 sunbeamcontroller::ReSunEmitation
= false;
2196 v2 Dir
= SunLightDirection
;
2197 int x
, y
, X
= 0, Y
= 0, SourceFlags
;
2199 /* Do not try to understand the logic behind the starting points of
2200 sunbeams. I determined the formulas by trial and error since all
2201 understandable loops produced strange shapes for the shadows of
2202 either small of large objects probably due to rounding errors
2203 made during line calculations. */
2204 if (!Dir
.X
|| (Dir
.Y
&& abs(Dir
.Y
) < abs(Dir
.X
))) {
2207 SourceFlags
= SP_BOTTOM
;
2208 IDFlags
= ID_X_COORDINATE
|ID_BEGIN
;
2211 SourceFlags
= SP_TOP
;
2212 IDFlags
= ID_X_COORDINATE
;
2217 SourceFlags
= SP_RIGHT
;
2221 SourceFlags
= SP_LEFT
;
2226 int Index
= XSize
<<3;
2227 for (x
= 0; x
< XSize
; ++x
, ++Index
) EmitSunBeam(v2(x
, Y
), Index
|IDFlags
, SourceFlags
);
2228 } else if (!Dir
.Y
) {
2229 int Index
= YSize
<<3;
2230 for (y
= 0; y
< YSize
; ++y
, ++Index
) EmitSunBeam(v2(X
, y
), Index
|IDFlags
, SourceFlags
);
2231 } else if (abs(Dir
.Y
) < abs(Dir
.X
)) {
2232 int Index
= Dir
.X
> 0 ? 0 : XSize
<<3;
2233 int StartX
= Dir
.X
> 0 ? -XSize
<<3 : 0;
2234 int EndX
= Dir
.X
> 0 ? XSize
: (XSize
<<3)+XSize
;
2235 for (x
= StartX
; x
< EndX
; ++x
, ++Index
) EmitSunBeam(v2(x
, Y
), Index
|IDFlags
, SourceFlags
);
2237 int Index
= Dir
.Y
> 0 ? 0 : YSize
<<3;
2238 int StartY
= Dir
.Y
> 0 ? -YSize
<<3 : 0;
2239 int EndY
= Dir
.Y
> 0 ? YSize
: (YSize
<<3)+YSize
;
2240 for (y
= StartY
; y
< EndY
; ++y
, ++Index
) EmitSunBeam(v2(X
, y
), Index
|IDFlags
, SourceFlags
);
2245 void level::EmitSunBeam (v2 S
, feuLong ID
, int SourceFlags
) const {
2247 v2 D
= S
+SunLightDirection
;
2248 sunbeamcontroller::ID
= ID
;
2249 if (SourceFlags
&SP_TOP_LEFT
) {
2250 sunbeamcontroller::SunLightBlockHeight
= 0;
2251 mapmath
<sunbeamcontroller
>::DoLine(S
.X
, S
.Y
, D
.X
, D
.Y
, SKIP_FIRST
);
2253 if (SourceFlags
&SP_TOP_RIGHT
) {
2254 sunbeamcontroller::SunLightBlockHeight
= 0;
2255 mapmath
<sunbeamcontroller
>::DoLine(S
.X
+1, S
.Y
, D
.X
+1, D
.Y
, SKIP_FIRST
);
2257 if (SourceFlags
&SP_BOTTOM_LEFT
) {
2258 sunbeamcontroller::SunLightBlockHeight
= 0;
2259 mapmath
<sunbeamcontroller
>::DoLine(S
.X
, S
.Y
+1, D
.X
, D
.Y
+1, SKIP_FIRST
);
2261 if (SourceFlags
&SP_BOTTOM_RIGHT
) {
2262 sunbeamcontroller::SunLightBlockHeight
= 0;
2263 mapmath
<sunbeamcontroller
>::DoLine(S
.X
+1, S
.Y
+1, D
.X
+1, D
.Y
+1, SKIP_FIRST
);
2268 truth
sunbeamcontroller::Handler (int x
, int y
) {
2269 int X
= x
>>1, Y
= y
>>1;
2271 if (X
< 0 || Y
< 0 || X
>= LevelXSize
|| Y
>= LevelYSize
) {
2272 return (X
>= -1 && X
<= LevelXSize
) || (Y
>= -1 && Y
<= LevelYSize
);
2275 lsquare
*Square
= Map
[X
][Y
];
2276 int SquarePartIndex
= (x
&1)+((y
&1)<<1);
2278 if (SunLightBlockHeight
&& !Square
->IsInside() && HypotSquare(x
-SunLightBlockPos
.X
, y
-SunLightBlockPos
.Y
) > SunLightBlockHeight
) {
2279 SunLightBlockHeight
= 0;
2282 if (!SunLightBlockHeight
) {
2283 feuLong Flag
= 1<<EMITTER_SQUARE_PART_SHIFT
<<SquarePartIndex
;
2284 Square
->AddSunLightEmitter(ID
|Flag
);
2286 feuLong Flags
= ((1<<EMITTER_SQUARE_PART_SHIFT
)|(1<<EMITTER_SHADOW_SHIFT
))<<SquarePartIndex
;
2287 Square
->AddSunLightEmitter(ID
|Flags
);
2290 if (ReSunEmitation
) {
2291 if (!(Square
->Flags
&IN_SQUARE_STACK
)) Stack
[StackIndex
++] = Square
;
2292 Square
->Flags
|= IN_SQUARE_STACK
|CHECK_SUN_LIGHT_NEEDED
;
2293 for (int d
= 0; d
< 8; ++d
) {
2294 lsquare
*Neighbour
= Square
->GetNeighbourLSquare(d
);
2295 if (Neighbour
&& !(Neighbour
->Flags
&IN_SQUARE_STACK
)) {
2296 Neighbour
->Flags
|= IN_SQUARE_STACK
;
2297 Stack
[StackIndex
++] = Neighbour
;
2302 if (!(Square
->Flags
&IS_TRANSPARENT
) || (SunLightBlockHeight
&& Square
->IsInside())) {
2303 /* This should depend on the square */
2304 SunLightBlockHeight
= 81;
2305 SunLightBlockPos
= v2(x
, y
);
2312 void sunbeamcontroller::ProcessStack () {
2314 for (c
= 0; c
< StackIndex
; ++c
) {
2315 lsquare
*Square
= Stack
[c
];
2316 if (Square
->Flags
&CHECK_SUN_LIGHT_NEEDED
) {
2317 if (Square
->Flags
&IS_TRANSPARENT
) Square
->CalculateSunLightLuminance(EMITTER_SQUARE_PART_BITS
);
2318 Square
->SendSunLightSignals();
2319 Square
->ZeroReSunEmitatedFlags();
2321 Square
->Flags
&= ~(IN_SQUARE_STACK
|CHECK_SUN_LIGHT_NEEDED
);
2323 for (c
= 0; c
< StackIndex
; ++c
) Stack
[c
]->CheckIfIsSecondarySunLightEmitter();
2327 int level::DetectMaterial (cmaterial
*Material
) {
2328 feuLong Tick
= game::IncreaseLOSTick();
2330 for (int x
= 0; x
< XSize
; ++x
) {
2331 for (int y
= 0; y
< YSize
; ++y
) {
2332 lsquare
*Square
= Map
[x
][y
];
2333 if (Square
->DetectMaterial(Material
)) {
2334 Square
->Reveal(Tick
, true);
2343 void level::BlurMemory () {
2344 int x
, y
, SquareStackSize
= 0;
2345 for (x
= 0; x
< XSize
; ++x
) {
2346 for (y
= 0; y
< YSize
; ++y
) {
2347 lsquare
*Square
= Map
[x
][y
];
2348 if (Square
->HasNoBorderPartners()) SquareStack
[SquareStackSize
++] = Square
;
2351 for (x
= 0; x
< XSize
; ++x
) {
2352 for (y
= 0; y
< YSize
; ++y
) {
2353 lsquare
*Square
= Map
[x
][y
];
2354 Square
->Flags
|= STRONG_NEW_DRAW_REQUEST
|MEMORIZED_UPDATE_REQUEST
|DESCRIPTION_CHANGE
;
2355 if (Square
->HasNoBorderPartners() && RAND()&1 && SquareStackSize
) {
2356 Square
->SwapMemorized(SquareStack
[RAND()%SquareStackSize
]);
2357 } else if (RAND()&1) {
2358 Square
->DestroyMemorized();
2365 void level::CalculateLuminances () {
2366 for (int x
= 0; x
< XSize
; ++x
) {
2367 for (int y
= 0; y
< YSize
; ++y
) {
2368 lsquare
*Square
= Map
[x
][y
];
2369 Square
->CalculateLuminance();
2370 Square
->Flags
|= MEMORIZED_UPDATE_REQUEST
|DESCRIPTION_CHANGE
;
2376 struct areacontroller
: public stackcontroller
{
2377 static truth
Handler (int x
, int y
) {
2378 if (x
>= 0 && y
>= 0 && x
< LevelXSize
&& y
< LevelYSize
&& HypotSquare(x
-Center
.X
, y
-Center
.Y
) <= RadiusSquare
) {
2379 lsquare
*Square
= Map
[x
][y
];
2380 if (!(Square
->Flags
&IN_SQUARE_STACK
)) {
2381 Stack
[StackIndex
++] = Square
;
2382 Square
->Flags
|= IN_SQUARE_STACK
;
2383 return Square
->IsFlyable();
2388 static int GetStartX (int) { return Center
.X
; }
2389 static int GetStartY (int) { return Center
.Y
; }
2390 static sLong RadiusSquare
;
2394 sLong
areacontroller::RadiusSquare
;
2397 int level::AddRadiusToSquareStack (v2 Center
, sLong RadiusSquare
) const {
2398 stackcontroller::Map
= Map
;
2399 stackcontroller::Stack
= SquareStack
;
2400 SquareStack
[0] = GetLSquare(Center
);
2401 stackcontroller::StackIndex
= 1;
2402 stackcontroller::LevelXSize
= XSize
;
2403 stackcontroller::LevelYSize
= YSize
;
2404 stackcontroller::Center
= Center
;
2405 areacontroller::RadiusSquare
= RadiusSquare
;
2406 mapmath
<areacontroller
>::DoArea();
2407 return stackcontroller::StackIndex
;
2411 /* Any fountain is good that is not dry and is NOT Except */
2412 olterrain
*level::GetRandomFountainWithWater (olterrain
*Except
) const {
2413 std::vector
<olterrain
*> Found
;
2414 olterrain
*OLTerrain
;
2415 for (int x
= 0; x
< XSize
; ++x
) {
2416 for (int y
= 0; y
< YSize
; ++y
) {
2417 OLTerrain
= GetLSquare(x
,y
)->GetOLTerrain();
2418 if (OLTerrain
&& OLTerrain
!= Except
&& OLTerrain
->IsFountainWithWater()) Found
.push_back(OLTerrain
);
2421 if (Found
.empty()) return 0;
2422 return Found
[RAND_N(Found
.size())];
2426 void level::Amnesia (int Percentile
) {
2427 for (int x
= 0; x
< XSize
; ++x
) {
2428 for (int y
= 0; y
< YSize
; ++y
) {
2429 lsquare
*Square
= Map
[x
][y
];
2430 if (Square
->HasNoBorderPartners() && RAND_N(100) < Percentile
) {
2431 Square
->Flags
|= STRONG_NEW_DRAW_REQUEST
|MEMORIZED_UPDATE_REQUEST
|DESCRIPTION_CHANGE
;
2432 Square
->DestroyMemorized();
2439 /* Returns how many of the monsters were seen */
2440 spawnresult
level::SpawnMonsters (characterspawner Spawner
, team
*Team
, v2 Pos
, int Config
, int Amount
, truth IgnoreWalkability
) {
2441 spawnresult SR
= { 0, 0 };
2442 for (int c
= 0; c
< Amount
; ++c
) {
2443 character
*Char
= Spawner(Config
, 0);
2444 if (!c
) SR
.Pioneer
= Char
;
2445 Char
->SetTeam(Team
);
2446 if (IgnoreWalkability
) Char
->ForcePutNear(Pos
); else Char
->PutNear(Pos
);
2447 if (Char
->CanBeSeenByPlayer()) ++SR
.Seen
;
2453 void level::AddSpecialCursors () {
2454 for (int x
= 0; x
< XSize
; ++x
) {
2455 for (int y
= 0; y
< YSize
; ++y
) {
2456 Map
[x
][y
]->AddSpecialCursors();
2462 void level::GasExplosion (gas
* GasMaterial
, lsquare
* Square
, character
* Terrorist
) {
2463 for (int d
= 0; d
< 9; ++d
) {
2464 lsquare
*Neighbour
= Square
->GetNeighbourLSquare(d
);
2466 if (Neighbour
->IsFlyable()) Neighbour
->AddSmoke(static_cast<gas
*>(GasMaterial
->SpawnMore(1000)));
2468 character
*Victim
= Neighbour
->GetCharacter();
2469 if (Victim
) Terrorist
->Hostility(Victim
);