1 /*
2 *
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
7 *
8 * See LICENSING which should be included
9 * along with this file for more details
10 *
11 */
12
13 /* Compiled through levelset.cpp */
14
15 #include "dbgmsgproj.h"
16
17 lsquare*** eyecontroller::Map;
18
19 lsquare*** pathcontroller::Map;
20 ccharacter* pathcontroller::Character;
21
22 lsquare*** stackcontroller::Map;
23 lsquare** stackcontroller::Stack;
24 long stackcontroller::StackIndex;
25 int stackcontroller::LevelXSize, stackcontroller::LevelYSize;
26 v2 stackcontroller::Center;
27
28 ulong tickcontroller::Tick;
29 ulong tickcontroller::ShiftedTick[4];
30 ulong tickcontroller::ShiftedQuadriTick[4];
31
PrepareShiftedTick()32 void tickcontroller::PrepareShiftedTick()
33 {
34 for(int c = 0; c < 4; ++c)
35 {
36 ShiftedTick[c] = Tick << (c << 3);
37 ShiftedQuadriTick[c] = (Tick + 1) << (c << 3);
38 }
39 }
40
IsDipDestination() const41 truth lsquare::IsDipDestination() const
42 { return GLTerrain->IsDipDestination() || (OLTerrain && OLTerrain->IsDipDestination()); }
43
lsquare(level * LevelUnder,v2 Pos)44 lsquare::lsquare(level* LevelUnder, v2 Pos)
45 : square(LevelUnder, Pos),
46 Fluid(0), Smoke(0), HitEffect(0), bMaterialDetected(false), Rain(0), Trap(0),
47 GLTerrain(0), OLTerrain(0),
48 Memorized(0), FowMemorized(0),
49 Engraved(0),
50 GroundBorderPartnerTerrain(0),
51 GroundBorderPartnerInfo(0),
52 OverBorderPartnerTerrain(0),
53 OverBorderPartnerInfo(0),
54 SquarePartEmitationTick(0),
55 SquarePartLastSeen(0),
56 Emitation(0),
57 SmokeAlphaSum(0),
58 AmbientLuminance(0),
59 SunLightLuminance(0),
60 TemporaryEmitation(0),
61 SecondarySunLightEmitation(0),
62 LastExplosionID(0),
63 RoomIndex(0)
64 {
65 Stack = new stack(this, 0);
66 }
67
~lsquare()68 lsquare::~lsquare()
69 {
70 delete GLTerrain;
71 delete OLTerrain;
72 delete Stack;
73 delete [] Engraved;
74
75 for(fluid* F = Fluid; F;)
76 {
77 fluid* ToDel = F;
78 F = F->Next;
79 delete ToDel;
80 }
81
82 delete Memorized;
83 delete FowMemorized;
84 delete StaticContentCache.Bitmap;
85 delete [] GroundBorderPartnerTerrain;
86 delete [] OverBorderPartnerTerrain;
87
88 for(smoke* S = Smoke; S;)
89 {
90 smoke* ToDel = S;
91 S = S->Next;
92 delete ToDel;
93 }
94
95 for(hiteffect* H = HitEffect; H;)
96 {
97 hiteffect* ToDel = H;
98 H = H->Next;
99 delete ToDel;
100 }
101
102 for(rain* R = Rain; R;)
103 {
104 rain* ToDel = R;
105 R = R->Next;
106 delete ToDel;
107 }
108
109 for(trap* T = Trap; T;)
110 {
111 trap* ToDel = T;
112 T = T->Next;
113 delete ToDel;
114 }
115 }
116
SignalEmitationIncrease(col24 EmitationUpdate)117 void lsquare::SignalEmitationIncrease(col24 EmitationUpdate)
118 {
119 if(game::CompareLights(EmitationUpdate, Emitation) > 0 && !game::IsGenerating() && !(Flags & FREEZED))
120 {
121 CalculateEmitation(); // could this be optimized?
122 Emitate();
123 }
124 }
125
SignalEmitationDecrease(col24 EmitationUpdate)126 void lsquare::SignalEmitationDecrease(col24 EmitationUpdate)
127 {
128 if(game::CompareLights(EmitationUpdate, Emitation) >= 0 && Emitation && !game::IsGenerating() && !(Flags & FREEZED))
129 {
130 col24 Backup = Emitation;
131 CalculateEmitation();
132
133 if(Backup != Emitation)
134 {
135 Noxify(Backup);
136 Emitate(Emitation, FORCE_ADD);
137 }
138 }
139 }
140
CalculateEmitation()141 void lsquare::CalculateEmitation()
142 {
143 Emitation = Stack->GetEmitation();
144 int c;
145
146 for(c = 0; c < 4; ++c)
147 {
148 stack* Stack = GetStackOfAdjacentSquare(c);
149
150 if(Stack)
151 game::CombineLights(Emitation, Stack->GetSideEmitation(3 - c));
152 }
153
154 if(Character)
155 game::CombineLights(Emitation, Character->GetEmitation());
156
157 game::CombineLights(Emitation, GLTerrain->GetEmitation());
158
159 if(OLTerrain)
160 game::CombineLights(Emitation, OLTerrain->GetEmitation());
161
162 game::CombineLights(Emitation, TemporaryEmitation);
163
164 for(const fluid* F = Fluid; F; F = F->Next)
165 game::CombineLights(Emitation, F->GetEmitation());
166
167 for(const rain* R = Rain; R; R = R->Next)
168 game::CombineLights(Emitation, R->GetEmitation());
169 }
170
UpdateMemorized()171 void lsquare::UpdateMemorized()
172 {
173 if(Flags & MEMORIZED_UPDATE_REQUEST)
174 {
175 if(!IsDark() || CanBeFeltByPlayer())
176 {
177 blitdata B = { Memorized,
178 { 0, 0 },
179 { 0, 0 },
180 { TILE_SIZE, TILE_SIZE },
181 { NORMAL_LUMINANCE },
182 TRANSPARENT_COLOR,
183 ALLOW_ALPHA };
184
185 DrawStaticContents(B);
186 Memorized->FastBlit(FowMemorized);
187 B.Bitmap = FowMemorized;
188 B.Flags = 0;
189 B.MaskColor = 0;
190 igraph::GetFOWGraphic()->NormalMaskedBlit(B);
191 }
192 else
193 {
194 Memorized->ClearToColor(0);
195 igraph::GetFOWGraphic()->FastBlit(FowMemorized);
196 }
197
198 if(!StaticContentCache.Bitmap)
199 {
200 StaticContentCache.Bitmap = new bitmap(TILE_V2);
201 StaticContentCache.Bitmap->ActivateFastFlag();
202 }
203
204 UpdateStaticContentCache(Luminance);
205 Flags &= ~MEMORIZED_UPDATE_REQUEST;
206 }
207 }
208
UpdateStaticContentCache(col24 Luminance) const209 void lsquare::UpdateStaticContentCache(col24 Luminance) const
210 {
211 blitdata B = { StaticContentCache.Bitmap,
212 { 0, 0 },
213 { 0, 0 },
214 { TILE_SIZE, TILE_SIZE },
215 { Luminance },
216 0,
217 0 };
218
219 Memorized->LuminanceBlit(B);
220 StaticContentCache.Luminance = Luminance;
221 }
222
DrawStaticContents(blitdata & BlitData) const223 void lsquare::DrawStaticContents(blitdata& BlitData) const
224 {
225 if(BlitData.CustomData & ALLOW_ANIMATE && !StaticAnimatedEntities && Memorized && !game::GetSeeWholeMapCheatMode())
226 {
227 if(StaticContentCache.Luminance != BlitData.Luminance)
228 UpdateStaticContentCache(BlitData.Luminance);
229
230 StaticContentCache.Bitmap->FastBlit(BlitData.Bitmap, BlitData.Dest);
231 return;
232 }
233
234 if(!OLTerrain || OLTerrain->ShowThingsUnder())
235 GLTerrain->Draw(BlitData);
236
237 int c;
238 int GroundPartners = GroundBorderPartnerInfo >> 24 & 15;
239
240 for(c = 0; c < GroundPartners; ++c)
241 {
242 BlitData.CustomData |= 8 - (GroundBorderPartnerInfo >> ((c << 1) + c) & 7);
243 GroundBorderPartnerTerrain[c]->Draw(BlitData);
244 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
245 }
246
247 truth StackDrawn = false;
248
249 if(OLTerrain && !IsFlyable())
250 {
251 if(OLTerrain->IsTransparent() && OLTerrain->ShowThingsUnder())
252 {
253 StackDrawn = true;
254 DrawStacks(BlitData);
255 }
256
257 OLTerrain->Draw(BlitData);
258 }
259
260 for(const fluid* F = Fluid; F; F = F->Next)
261 F->SimpleDraw(BlitData);
262
263 if(OLTerrain && IsFlyable())
264 OLTerrain->Draw(BlitData);
265
266 if(!StackDrawn && Flags & IS_TRANSPARENT)
267 DrawStacks(BlitData);
268
269 for(const trap* T = Trap; T; T = T->Next)
270 T->Draw(BlitData);
271
272 int OverPartners = OverBorderPartnerInfo >> 24 & 15;
273
274 for(c = 0; c < OverPartners; ++c)
275 {
276 BlitData.CustomData |= 8 - (OverBorderPartnerInfo >> ((c << 1) + c) & 7);
277 OverBorderPartnerTerrain[c]->Draw(BlitData);
278 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
279 }
280 }
281
Draw(blitdata & BlitData) const282 void lsquare::Draw(blitdata& BlitData) const
283 {
284 if(Flags & NEW_DRAW_REQUEST || AnimatedEntities)
285 {
286 if(!IsDark() || game::GetSeeWholeMapCheatMode())
287 {
288 if(game::GetSeeWholeMapCheatMode() == SHOW_MAP_IN_UNIFORM_LIGHT
289 || (game::GetSeeWholeMapCheatMode()
290 && !(Flags & IS_TRANSPARENT)))
291 BlitData.Luminance = ivanconfig::GetContrastLuminance();
292 else
293 BlitData.Luminance = ivanconfig::ApplyContrastTo(Luminance);
294
295 DrawStaticContents(BlitData);
296
297 if(Character && (Character->CanBeSeenByPlayer() || game::GetSeeWholeMapCheatMode()))
298 {
299 BlitData.CustomData |= Character->GetSquareIndex(Pos);
300
301 if(Character->IsFlying())
302 {
303 for(const smoke* S = Smoke; S; S = S->Next)
304 S->Draw(BlitData);
305
306 Character->Draw(BlitData);
307 }
308 else
309 {
310 Character->Draw(BlitData);
311
312 for(const smoke* S = Smoke; S; S = S->Next)
313 S->Draw(BlitData);
314 }
315
316 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
317 }
318 else
319 for(const smoke* S = Smoke; S; S = S->Next)
320 S->Draw(BlitData);
321
322 for(const rain* R = Rain; R; R = R->Next)
323 if(R->IsEnabled())
324 R->Draw(BlitData);
325 }
326 else if(CanBeFeltByPlayer())
327 {
328 col24 L = Luminance;
329 game::CombineLights(L, DIM_LUMINANCE);
330 BlitData.Luminance = ivanconfig::ApplyContrastTo(L);
331 DrawStaticContents(BlitData);
332
333 for(const rain* R = Rain; R; R = R->Next)
334 if(R->IsEnabled())
335 R->Draw(BlitData);
336 }
337 else
338 {
339 DOUBLE_BUFFER->Fill(BlitData.Dest, BlitData.Border, 0);
340
341 if(Character && Character->CanBeSeenByPlayer())
342 {
343 BlitData.CustomData |= Character->GetSquareIndex(Pos);
344 BlitData.Luminance = ivanconfig::GetContrastLuminance();
345 Character->Draw(BlitData);
346 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
347 }
348 }
349
350 if(ivanconfig::GetHitIndicator()>0){
351 hiteffect* HE = HitEffect;
352 while(HE){
353 HE->PrepareBlitdata(BlitData);
354 HE = HE->Next;
355 }
356 }
357
358 Flags &= ~STRONG_NEW_DRAW_REQUEST;
359 }
360 }
361
DrawHitEffect()362 void lsquare::DrawHitEffect(){
363 // end this tmp effect as soon as possible
364 for(hiteffect* HE = HitEffect; HE; HE = HE->Next){
365 /**
366 * This is not the normal drawing. The drawing itself determines how long it will last. Therefore not const.
367 * One draw step per frame.
368 */
369 if(HE->DrawStep())break;
370 }
371 }
372
373 struct emitationcontroller : public tickcontroller, public stackcontroller
374 {
Handleremitationcontroller375 static truth Handler(int x, int y)
376 {
377 lsquare* Square = Map[x >> 1][y >> 1];
378 culong SquareFlags = Square->Flags;
379
380 if(SquareFlags & PERFECTLY_QUADRI_HANDLED)
381 return SquareFlags & ALLOW_EMITATION_CONTINUE;
382
383 if(SquareFlags & IS_TRANSPARENT)
384 return ProcessSquare(x >> 1, y >> 1, Square);
385
386 if(!(SquareFlags & IN_SQUARE_STACK))
387 {
388 Square->Flags |= IN_SQUARE_STACK;
389 Stack[StackIndex++] = Square;
390 }
391
392 cint SquarePartIndex = (x & 1) + ((y & 1) << 1);
393 Square->SquarePartEmitationTick = (Square->SquarePartEmitationTick
394 & ~SquarePartTickMask[SquarePartIndex])
395 | ShiftedTick[SquarePartIndex];
396
397 return false;
398 }
ProcessSquareemitationcontroller399 static int ProcessSquare(int X, int Y, lsquare* Square)
400 {
401 Stack[StackIndex++] = Square;
402 culong SquareFlags = Square->Flags;
403 cint MaxE = MaxLuxTable[X - EmitterPosXMinus16][Y - EmitterPosYMinus16];
404
405 if(MaxE >= LIGHT_BORDER
406 && (SquareFlags & INSIDE
407 || (!(ID & SECONDARY_SUN_LIGHT)
408 && MaxE > MinNightAmbientLuminanceElement)))
409 {
410 Square->Flags |= ALLOW_EMITATION_CONTINUE | PERFECTLY_QUADRI_HANDLED;
411 return true;
412 }
413 else
414 {
415 Square->Flags = (SquareFlags & ~ALLOW_EMITATION_CONTINUE) | PERFECTLY_QUADRI_HANDLED;
416 return false;
417 }
418 }
GetTickReferenceemitationcontroller419 static ulong& GetTickReference(int X, int Y)
420 {
421 return Map[X][Y]->SquarePartEmitationTick;
422 }
ProcessStackemitationcontroller423 static void ProcessStack()
424 {
425 for(long c1 = 0; c1 < StackIndex; ++c1)
426 {
427 lsquare* Square = Stack[c1];
428 culong SquareTick = Square->SquarePartEmitationTick;
429 ulong TempID = ID;
430
431 for(int c2 = 0; c2 < 4; ++c2)
432 if((SquareTick & SquarePartTickMask[c2]) == ShiftedTick[c2])
433 TempID |= 1 << EMITTER_SQUARE_PART_SHIFT << c2;
434
435 Square->Flags &= ~(IN_SQUARE_STACK|PERFECTLY_QUADRI_HANDLED);
436 v2 Pos = Square->Pos;
437 int XVal = Pos.X - EmitterPosXMinus16;
438 int YVal = Pos.Y - EmitterPosYMinus16;
439
440 if(MaxLuxTable[XVal][YVal] >= LIGHT_BORDER)
441 Square->AlterLuminance(TempID, MakeRGB24(RedLuxTable[XVal][YVal],
442 GreenLuxTable[XVal][YVal],
443 BlueLuxTable[XVal][YVal]));
444 }
445 }
446 static ulong ID;
447 static int MinNightAmbientLuminanceElement;
448 static int EmitterPosXMinus16;
449 static int EmitterPosYMinus16;
450 static uchar** MaxLuxTable;
451 static uchar** RedLuxTable;
452 static uchar** GreenLuxTable;
453 static uchar** BlueLuxTable;
454 };
455
456 ulong emitationcontroller::ID;
457 int emitationcontroller::MinNightAmbientLuminanceElement;
458 int emitationcontroller::EmitterPosXMinus16;
459 int emitationcontroller::EmitterPosYMinus16;
460 uchar** emitationcontroller::MaxLuxTable;
461 uchar** emitationcontroller::RedLuxTable;
462 uchar** emitationcontroller::GreenLuxTable;
463 uchar** emitationcontroller::BlueLuxTable;
464
Emitate(col24 Emitation,ulong IDFlags)465 void lsquare::Emitate(col24 Emitation, ulong IDFlags)
466 {
467 if(game::IsDark(Emitation))
468 return;
469
470 int Radius = game::CalculateMinimumEmitationRadius(Emitation);
471
472 if(!Radius)
473 return;
474
475 stackcontroller::Map = GetLevel()->GetMap();
476 stackcontroller::Stack = GetLevel()->GetSquareStack();
477 stackcontroller::StackIndex = 0;
478 tickcontroller::Tick = game::IncreaseSquarePartEmitationTicks();
479 tickcontroller::PrepareShiftedTick();
480 emitationcontroller::ID = MakeEmitterID(Pos) | IDFlags;
481 emitationcontroller::MinNightAmbientLuminanceElement = GetMinColor24(GetLevel()->GetNightAmbientLuminance());
482 emitationcontroller::EmitterPosXMinus16 = Pos.X - 16;
483 emitationcontroller::EmitterPosYMinus16 = Pos.Y - 16;
484 emitationcontroller::MaxLuxTable = game::GetLuxTable()[GetMaxColor24(Emitation)];
485 emitationcontroller::RedLuxTable = game::GetLuxTable()[GetRed24(Emitation)];
486 emitationcontroller::GreenLuxTable = game::GetLuxTable()[GetGreen24(Emitation)];
487 emitationcontroller::BlueLuxTable = game::GetLuxTable()[GetBlue24(Emitation)];
488 mapmath<emitationcontroller>::DoQuadriArea(Pos.X, Pos.Y,
489 Radius * Radius,
490 GetLevel()->GetXSize(),
491 GetLevel()->GetYSize());
492 emitationcontroller::ProcessStack();
493 }
494
495 struct noxifycontroller : public stackcontroller
496 {
Handlernoxifycontroller497 static truth Handler(int x, int y)
498 {
499 if(x >= 0 && y >= 0 && x < LevelXSize && y < LevelYSize)
500 {
501 lsquare* Square = Map[x][y];
502
503 if(Square->SquarePartEmitationTick != Tick)
504 {
505 Square->SquarePartEmitationTick = Tick;
506 return Square->NoxifyEmitter(ID);
507 }
508 }
509
510 return false;
511 }
GetStartXnoxifycontroller512 static int GetStartX(int) { return Center.X; }
GetStartYnoxifycontroller513 static int GetStartY(int) { return Center.Y; }
514 static ulong ID;
515 static ulong Tick;
516 };
517
518 ulong noxifycontroller::ID;
519 ulong noxifycontroller::Tick;
520
Noxify(col24 Emitation,ulong IDFlags)521 void lsquare::Noxify(col24 Emitation, ulong IDFlags)
522 {
523 if(game::IsDark(Emitation))
524 return;
525
526 int Radius = game::CalculateMinimumEmitationRadius(Emitation);
527
528 if(!Radius)
529 return;
530
531 stackcontroller::Map = GetLevel()->GetMap();
532 stackcontroller::LevelXSize = GetLevel()->GetXSize();
533 stackcontroller::LevelYSize = GetLevel()->GetYSize();
534 stackcontroller::Center = Pos;
535 noxifycontroller::ID = MakeEmitterID(Pos) | IDFlags;
536 noxifycontroller::Tick = game::IncreaseSquarePartEmitationTicks();
537 NoxifyEmitter(noxifycontroller::ID);
538 mapmath<noxifycontroller>::DoArea();
539 }
540
NoxifyEmitter(ulong ID)541 truth lsquare::NoxifyEmitter(ulong ID)
542 {
543 for(emitter& e : Emitter)
544 if(!((e.ID ^ ID) & (EMITTER_IDENTIFIER_BITS|SECONDARY_SUN_LIGHT)))
545 {
546 RemoveLuminance(e.Emitation);
547 Swap(e, Emitter.back());
548 Emitter.pop_back();
549 return true;
550 }
551
552 return false;
553 }
554
AlterLuminance(ulong ID,col24 NewLuminance)555 void lsquare::AlterLuminance(ulong ID, col24 NewLuminance)
556 {
557 if(!(ID & FORCE_ADD))
558 for(emitter& e : Emitter)
559 if(!((e.ID ^ ID) & (EMITTER_IDENTIFIER_BITS|SECONDARY_SUN_LIGHT)))
560 {
561 e.ID |= ID;
562
563 if(e.Emitation != NewLuminance)
564 ChangeLuminance(e.Emitation, NewLuminance);
565
566 return;
567 }
568
569 Emitter.push_back(emitter(ID, 0));
570 ChangeLuminance(Emitter.back().Emitation, NewLuminance);
571 }
572
AddSunLightEmitter(ulong ID)573 void lsquare::AddSunLightEmitter(ulong ID)
574 {
575 for(sunemittervector::value_type& SE : SunEmitter)
576 if(!((SE ^ ID) & EMITTER_IDENTIFIER_BITS))
577 {
578 if(ID & ~SE & RE_SUN_EMITATED)
579 SE &= ~EMITTER_SHADOW_BITS;
580
581 SE |= ID;
582 Swap(SE, SunEmitter.front());
583 return;
584 }
585
586 SunEmitter.push_back(ID);
587 }
588
Open(character * Opener)589 truth lsquare::Open(character* Opener)
590 {
591 return GetStack()->Open(Opener) || (OLTerrain && OLTerrain->Open(Opener));
592 }
593
Close(character * Closer)594 truth lsquare::Close(character* Closer)
595 {
596 if(!GetStack()->GetItems() && !Character)
597 return OLTerrain && OLTerrain->Close(Closer);
598 else
599 {
600 if(Character == Closer)
601 ADD_MESSAGE("Move out of the way first...");
602 else
603 ADD_MESSAGE("There's something in the way...");
604 return false;
605 }
606 }
607
Save(outputfile & SaveFile) const608 void lsquare::Save(outputfile& SaveFile) const
609 {
610 Stack->Save(SaveFile); // This must be before square::Save! (Note: This comment is years old. It's probably obsolete)
611 square::Save(SaveFile);
612 SaveFile << GLTerrain << OLTerrain;
613 SaveFile << Emitter << SunEmitter;
614 SaveFile << Emitation << Engraved << Luminance;
615 SaveFile << SmokeAlphaSum << static_cast<uchar>(Flags) << Memorized;
616 SaveFile << SecondarySunLightEmitation;
617 SaveFile << RoomIndex;
618 SaveFile << SunLightLuminance;
619 SaveLinkedList(SaveFile, Fluid);
620 SaveLinkedList(SaveFile, Smoke);
621 SaveLinkedList(SaveFile, Rain);
622 SaveLinkedList(SaveFile, Trap);
623 }
624
Load(inputfile & SaveFile)625 void lsquare::Load(inputfile& SaveFile)
626 {
627 Stack->Load(SaveFile); // This must be before square::Load! (Note: This comment is years old. It's probably obsolete)
628 Stack->SetMotherSquare(this);
629 square::Load(SaveFile);
630 SaveFile >> GLTerrain >> OLTerrain;
631 SaveFile >> Emitter >> SunEmitter;
632 SaveFile >> Emitation >> Engraved >> Luminance;
633 SaveFile >> SmokeAlphaSum >> reinterpret_cast<uchar&>(Flags) >> Memorized;
634 Flags &= INSIDE|DESCRIPTION_CHANGE; // only these flags are loaded
635 Flags |= MEMORIZED_UPDATE_REQUEST;
636 SecondarySunLightEmitation = ReadType<col24>(SaveFile);
637 RoomIndex = ReadType<uchar>(SaveFile);
638 SunLightLuminance = ReadType<col24>(SaveFile);
639 LoadLinkedList(SaveFile, Fluid);
640 LoadLinkedList(SaveFile, Smoke);
641 LoadLinkedList(SaveFile, Rain);
642 LoadLinkedList(SaveFile, Trap);
643 CalculateIsTransparent();
644
645 if(Memorized)
646 {
647 FowMemorized = new bitmap(TILE_V2);
648 FowMemorized->ActivateFastFlag();
649 Memorized->FastBlit(FowMemorized);
650 blitdata B = { FowMemorized,
651 { 0, 0 },
652 { 0, 0 },
653 { TILE_SIZE, TILE_SIZE },
654 { 0 },
655 0,
656 0 };
657
658 igraph::GetFOWGraphic()->NormalMaskedBlit(B);
659 }
660 }
661
CalculateLuminance()662 void lsquare::CalculateLuminance()
663 {
664 Luminance = AmbientLuminance;
665
666 if(Flags & IS_TRANSPARENT)
667 {
668 game::CombineLights(Luminance, SunLightLuminance);
669
670 for(const emitter& e : Emitter)
671 game::CombineLights(Luminance, e.Emitation);
672 }
673 else
674 {
675 ulong BitMask = 0, LOSTick = game::GetLOSTick();
676
677 for(int c = 0; c < 4; ++c)
678 if((SquarePartLastSeen >> (c << 3) & 0xFF) >= LOSTick)
679 BitMask |= 1 << EMITTER_SQUARE_PART_SHIFT << c;
680
681 CalculateSunLightLuminance(BitMask);
682 game::CombineLights(Luminance, SunLightLuminance);
683
684 for(const emitter& e : Emitter)
685 if(BitMask & e.ID)
686 game::CombineLights(Luminance, e.Emitation);
687 }
688 }
689
AddCharacter(character * Guy)690 void lsquare::AddCharacter(character* Guy)
691 {
692 if(Character)
693 ABORT("Overgrowth of square population detected!");
694
695 Character = Guy;
696 SignalEmitationIncrease(Guy->GetEmitation());
697 Flags |= STRONG_NEW_DRAW_REQUEST;
698
699 if(Guy->IsAnimated())
700 IncAnimatedEntities();
701
702 SignalPossibleTransparencyChange();
703
704 if(Guy->IsPlayer()
705 || (Guy->CanBeSeenByPlayer(true) && CanBeSeenByPlayer()))
706 Guy->SignalSeen();
707 }
708
Clean()709 void lsquare::Clean()
710 {
711 GetStack()->Clean();
712 }
713
RemoveCharacter()714 void lsquare::RemoveCharacter()
715 {
716 if(Character)
717 {
718 character* Backup = Character;
719
720 if(Backup->IsAnimated())
721 DecAnimatedEntities();
722
723 Character = 0;
724 SignalEmitationDecrease(Backup->GetEmitation());
725 Flags |= STRONG_NEW_DRAW_REQUEST;
726 SignalPossibleTransparencyChange();
727 }
728 }
729
UpdateMemorizedDescription(truth Cheat)730 void lsquare::UpdateMemorizedDescription(truth Cheat)
731 {
732 if(Flags & DESCRIPTION_CHANGE || Cheat)
733 {
734 if(!IsDark() || Cheat)
735 {
736 MemorizedDescription.Empty();
737
738 if(!OLTerrain || (OLTerrain->IsTransparent() && OLTerrain->ShowThingsUnder() && !OLTerrain->IsWall()))
739 {
740 truth Anything = false;
741
742 if(OLTerrain && OLTerrain->GetNameSingular().GetSize())
743 {
744 OLTerrain->AddName(MemorizedDescription, INDEFINITE);
745 Anything = true;
746 }
747
748 if(Flags & IS_TRANSPARENT)
749 {
750 itemvectorvector PileVector;
751 GetStack()->Pile(PileVector, PLAYER, CENTER);
752
753 if(PileVector.size())
754 {
755 if(Anything)
756 MemorizedDescription << " and ";
757
758 if(PileVector.size() == 1)
759 PileVector[0][0]->AddName(MemorizedDescription, INDEFINITE, PileVector[0].size());
760 else
761 MemorizedDescription << "many items";
762
763 MemorizedDescription << " on ";
764 Anything = true;
765 }
766 else if(Anything)
767 MemorizedDescription << " on ";
768
769 GLTerrain->AddName(MemorizedDescription, INDEFINITE);
770 festring SideItems;
771 GetSideItemDescription(SideItems, Cheat);
772
773 if(!SideItems.IsEmpty())
774 MemorizedDescription << " and " << SideItems;
775 }
776 else
777 {
778 if(Anything)
779 MemorizedDescription << " on ";
780
781 GLTerrain->AddName(MemorizedDescription, INDEFINITE);
782 }
783 }
784 else
785 OLTerrain->AddName(MemorizedDescription, INDEFINITE);
786
787 if(FluidIsVisible())
788 DisplayFluidInfo(MemorizedDescription);
789
790 DisplayTrapInfo(MemorizedDescription);
791
792 if(Cheat)
793 MemorizedDescription << " (pos " << Pos.X << ':' << Pos.Y << ")";
794 }
795 else if(CanBeFeltByPlayer())
796 {
797 MemorizedDescription.Empty();
798 OLTerrain->AddName(MemorizedDescription, INDEFINITE);
799
800 if(FluidIsVisible())
801 DisplayFluidInfo(MemorizedDescription);
802
803 DisplayTrapInfo(MemorizedDescription);
804 }
805 else
806 MemorizedDescription = CONST_S("darkness");
807
808 Flags &= ~DESCRIPTION_CHANGE;
809 }
810 }
811
GetSideItemDescription(festring & String,truth Cheat) const812 void lsquare::GetSideItemDescription(festring& String, truth Cheat) const
813 {
814 int Items = 0;
815
816 for(int c = 0; c < 4; ++c)
817 {
818 stack* Stack = GetStackOfAdjacentSquare(c);
819
820 if(Stack)
821 Items += Cheat
822 ? Stack->GetSideItems(3 - c)
823 : Stack->GetVisibleSideItems(PLAYER, 3 - c);
824 }
825
826 if(Items > 1)
827 String << "many items on the wall";
828 else if(Items == 1)
829 {
830 for(int c = 0; c < 4; ++c)
831 {
832 stack* Stack = GetStackOfAdjacentSquare(c);
833
834 if(Stack
835 && ((Cheat && Stack->GetSideItems(3 - c))
836 || (!Cheat && Stack->GetVisibleSideItems(PLAYER, 3 - c))))
837 Stack->GetBottomSideItem(PLAYER, 3 - c, Cheat)->AddName(String, INDEFINITE);
838 }
839
840 String << " on the wall";
841 }
842 }
843
BeKicked(character * Kicker,item * Boot,bodypart * Leg,double KickDamage,double KickToHitValue,int Success,int Direction,truth Critical,truth ForceHit)844 truth lsquare::BeKicked(character* Kicker, item* Boot, bodypart* Leg, double KickDamage, double KickToHitValue,
845 int Success, int Direction, truth Critical, truth ForceHit)
846 {
847 truth Return;
848
849 if(GetCharacter())
850 {
851 GetCharacter()->BeKicked(Kicker, Boot, Leg, Pos, KickDamage, KickToHitValue,
852 Success, Direction, Critical, ForceHit);
853 Return = true;
854 }
855 else
856 Return = false;
857
858 if(RoomIndex)
859 GetLevel()->GetRoom(RoomIndex)->KickSquare(Kicker, this);
860
861 GetStack()->BeKicked(Kicker, int(KickDamage), Direction);
862
863 if(GetOLTerrain())
864 GetOLTerrain()->BeKicked(Kicker, int(KickDamage * (100 + Success) / 100), Direction);
865
866 return Return;
867 }
868
CanBeDug() const869 truth lsquare::CanBeDug() const
870 {
871 if((!GetPos().X || !GetPos().Y
872 || GetPos().X == GetLevel()->GetXSize() - 1
873 || GetPos().Y == GetLevel()->GetYSize() - 1)
874 && !*GetLevel()->GetLevelScript()->IsOnGround())
875 {
876 ADD_MESSAGE("Somehow you feel that by digging this square you would collapse the whole dungeon.");
877 return false;
878 }
879 else
880 return true;
881 }
882
ChangeLTerrain(glterrain * NewGround,olterrain * NewOver)883 void lsquare::ChangeLTerrain(glterrain* NewGround, olterrain* NewOver)
884 {
885 ChangeGLTerrain(NewGround);
886 ChangeOLTerrain(NewOver);
887 }
888
ChangeGLTerrain(glterrain * NewGround)889 void lsquare::ChangeGLTerrain(glterrain* NewGround)
890 {
891 if(GLTerrain->IsAnimated())
892 DecStaticAnimatedEntities();
893
894 truth WasUsingBorderTiles = GLTerrain->UseBorderTiles();
895 delete GLTerrain;
896 GLTerrain = NewGround;
897 NewGround->SetLSquareUnder(this);
898 Flags |= NEW_DRAW_REQUEST;
899 GetLevel()->SetWalkability(Pos, GetTheoreticalWalkability());
900 CalculateGroundBorderPartners();
901 SendMemorizedUpdateRequest();
902
903 if(WasUsingBorderTiles || NewGround->UseBorderTiles())
904 RequestForGroundBorderPartnerUpdates();
905
906 if(NewGround->IsAnimated())
907 IncStaticAnimatedEntities();
908 }
909
ChangeOLTerrain(olterrain * NewOver)910 void lsquare::ChangeOLTerrain(olterrain* NewOver)
911 {
912 if(OLTerrain && OLTerrain->IsAnimated())
913 DecStaticAnimatedEntities();
914
915 truth WasUsingBorderTiles = OLTerrain && OLTerrain->UseBorderTiles();
916 delete OLTerrain;
917 OLTerrain = NewOver;
918 Flags |= NEW_DRAW_REQUEST;
919 GetLevel()->SetWalkability(Pos, GetTheoreticalWalkability());
920 CalculateOverBorderPartners();
921 CalculateIsTransparent();
922 SendMemorizedUpdateRequest();
923
924 if(WasUsingBorderTiles || (NewOver && NewOver->UseBorderTiles()))
925 RequestForOverBorderPartnerUpdates();
926
927 if(NewOver)
928 {
929 NewOver->SetLSquareUnder(this);
930
931 if(NewOver->IsAnimated())
932 IncStaticAnimatedEntities();
933 }
934 }
935
SetLTerrain(glterrain * NewGround,olterrain * NewOver)936 void lsquare::SetLTerrain(glterrain* NewGround, olterrain* NewOver)
937 {
938 GLTerrain = NewGround;
939 NewGround->SetLSquareUnder(this);
940
941 if(NewGround->IsAnimated())
942 IncStaticAnimatedEntities();
943
944 OLTerrain = NewOver;
945
946 if(NewOver)
947 {
948 NewOver->SetLSquareUnder(this);
949
950 if(NewOver->IsAnimated())
951 IncStaticAnimatedEntities();
952
953 if(!NewOver->IsTransparent())
954 Flags &= ~IS_TRANSPARENT;
955 }
956
957 GetLevel()->SetWalkability(Pos, GetTheoreticalWalkability());
958 }
959
ApplyScript(const squarescript * SquareScript,room * Room)960 void lsquare::ApplyScript(const squarescript* SquareScript, room* Room)
961 {
962 if(SquareScript->AttachRequired())
963 GetLevel()->AddToAttachQueue(Pos);
964
965 int EntryIndex = SquareScript->GetEntryIndex();
966
967 if(EntryIndex != NO_ENTRY)
968 GetLevel()->SetEntryPos(EntryIndex, Pos);
969
970 const contentscript<character>* CharacterScript = SquareScript->GetCharacter();
971
972 if(CharacterScript)
973 {
974 character* Char = CharacterScript->Instantiate();
975 Char->SetGenerationDanger(GetLevel()->GetDifficulty());
976
977 if(!Char->GetTeam())
978 Char->SetTeam(game::GetTeam(*GetLevel()->GetLevelScript()->GetTeamDefault()));
979
980 if(CharacterScript->GetFlags() & IS_LEADER)
981 Char->GetTeam()->SetLeader(Char);
982
983 Char->PutToOrNear(Pos);
984 Char->CreateHomeData();
985
986 if(Room && CharacterScript->GetFlags() & IS_MASTER)
987 Room->SetMasterID(Char->GetID());
988 }
989
990 const fearray<contentscript<item>>* Items = SquareScript->GetItems();
991
992 if(Items)
993 for(uint c1 = 0; c1 < Items->Size; ++c1)
994 {
995 const interval* TimesPtr = Items->Data[c1].GetTimes();
996 int Times = TimesPtr ? TimesPtr->Randomize() : 1;
997
998 for(int c2 = 0; c2 < Times; ++c2)
999 {
1000 item* Item = Items->Data[c1].Instantiate();
1001
1002 if(Item)
1003 {
1004 int SquarePosition = Items->Data[c1].GetSquarePosition();
1005
1006 if(SquarePosition != CENTER)
1007 Item->SignalSquarePositionChange(SquarePosition);
1008
1009 GetStack()->AddItem(Item);
1010 Item->SpecialGenerationHandler();
1011 }
1012 }
1013 }
1014
1015 const contentscript<glterrain>* GLTerrainScript = SquareScript->GetGTerrain();
1016
1017 if(GLTerrainScript)
1018 {
1019 GetLevel()->AddFlag(Pos, FORBIDDEN);
1020 ChangeGLTerrain(GLTerrainScript->Instantiate());
1021
1022 if(GLTerrainScript->IsInside())
1023 {
1024 if(*GLTerrainScript->IsInside())
1025 Flags |= INSIDE;
1026 else
1027 Flags &= ~INSIDE;
1028 }
1029 }
1030
1031 const contentscript<olterrain>* OLTerrainScript = SquareScript->GetOTerrain();
1032
1033 if(OLTerrainScript)
1034 {
1035 GetLevel()->AddFlag(Pos, FORBIDDEN);
1036 ChangeOLTerrain(OLTerrainScript->Instantiate());
1037 }
1038 }
1039
CanBeSeenByPlayer(truth IgnoreDarkness) const1040 truth lsquare::CanBeSeenByPlayer(truth IgnoreDarkness) const
1041 {
1042 return (IgnoreDarkness || !IsDark()) && LastSeen == game::GetLOSTick();
1043 }
1044
CanBeSeenFrom(v2 FromPos,long MaxDistance,truth IgnoreDarkness) const1045 truth lsquare::CanBeSeenFrom(v2 FromPos, long MaxDistance, truth IgnoreDarkness) const
1046 {
1047 if((Pos - FromPos).GetLengthSquare() <= MaxDistance
1048 && (IgnoreDarkness || !IsDark()))
1049 {
1050 if(Character && Character->IsPlayer()
1051 && GetNearLSquare(FromPos)->CanBeSeenByPlayer(true))
1052 return true;
1053
1054 eyecontroller::Map = GetLevel()->GetMap();
1055 return mapmath<eyecontroller>::DoLine(FromPos.X, FromPos.Y, GetPos().X, GetPos().Y, SKIP_FIRST);
1056 }
1057
1058 return false;
1059 }
1060
StepOn(character * Stepper,lsquare ** ComingFrom)1061 void lsquare::StepOn(character* Stepper, lsquare** ComingFrom)
1062 {
1063 if(RoomIndex)
1064 {
1065 truth WasInRoom = false;
1066
1067 if(ComingFrom)
1068 for(int c = 0; c < Stepper->GetSquaresUnder(); ++c)
1069 if(ComingFrom[c]->GetRoomIndex() == RoomIndex)
1070 {
1071 WasInRoom = true;
1072 break;
1073 }
1074
1075 if(!WasInRoom)
1076 GetLevel()->GetRoom(RoomIndex)->Enter(Stepper);
1077 }
1078
1079 GLTerrain->StepOn(Stepper);
1080
1081 if(OLTerrain)
1082 {
1083 OLTerrain->StepOn(Stepper);
1084
1085 if(Stepper->DestroysWalls() && OLTerrain->WillBeDestroyedBy(Stepper))
1086 {
1087 if(CanBeSeenByPlayer())
1088 ADD_MESSAGE("%s destroys %s.", Stepper->CHAR_NAME(DEFINITE), OLTerrain->CHAR_NAME(DEFINITE));
1089
1090 Stepper->EditAP(-100);
1091 OLTerrain->BeDestroyed();
1092 }
1093 }
1094
1095 uint c;
1096 std::vector<trap*> TrapVector;
1097
1098 for(trap* T = Trap; T; T = T->Next)
1099 TrapVector.push_back(T);
1100
1101 for(c = 0; c < TrapVector.size(); ++c)
1102 if(TrapVector[c]->Exists())
1103 {
1104 TrapVector[c]->StepOnEffect(Stepper);
1105
1106 if(!Stepper->IsEnabled())
1107 return;
1108 }
1109
1110 if(!Stepper->IsFlying())
1111 {
1112 std::vector<fluid*> FluidVector;
1113
1114 for(fluid* F = Fluid; F; F = F->Next)
1115 FluidVector.push_back(F);
1116
1117 for(c = 0; c < FluidVector.size(); ++c)
1118 if(FluidVector[c]->Exists())
1119 {
1120 FluidVector[c]->StepOnEffect(Stepper);
1121
1122 if(!Stepper->IsEnabled())
1123 return;
1124 }
1125
1126 GetStack()->CheckForStepOnEffect(Stepper);
1127 }
1128 }
1129
ReceiveVomit(character * Who,liquid * Liquid)1130 void lsquare::ReceiveVomit(character* Who, liquid* Liquid)
1131 {
1132 if(!GetOLTerrain() || !GetOLTerrain()->ReceiveVomit(Who, Liquid))
1133 {
1134 SpillFluid(Who, Liquid);
1135
1136 if(RoomIndex)
1137 GetRoom()->ReceiveVomit(Who);
1138 }
1139 }
1140
SetTemporaryEmitation(col24 What)1141 void lsquare::SetTemporaryEmitation(col24 What)
1142 {
1143 col24 Old = TemporaryEmitation;
1144 TemporaryEmitation = 0;
1145 SignalEmitationDecrease(Old);
1146 TemporaryEmitation = What;
1147 SignalEmitationIncrease(What);
1148 }
1149
ChangeOLTerrainAndUpdateLights(olterrain * NewTerrain)1150 void lsquare::ChangeOLTerrainAndUpdateLights(olterrain* NewTerrain)
1151 {
1152 truth WasTransparent = Flags & IS_TRANSPARENT, Noxified = false;
1153 emittervector EmitterBackup;
1154
1155 if(WasTransparent && NewTerrain && !NewTerrain->IsTransparent())
1156 {
1157 EmitterBackup = Emitter;
1158 GetLevel()->ForceEmitterNoxify(EmitterBackup);
1159 Noxified = true;
1160 }
1161
1162 long OldEmit = OLTerrain ? OLTerrain->GetEmitation() : 0;
1163 ChangeOLTerrain(NewTerrain);
1164
1165 if(NewTerrain)
1166 SignalEmitationIncrease(NewTerrain->GetEmitation());
1167
1168 SignalEmitationDecrease(OldEmit);
1169 GetStack()->DropSideItems();
1170
1171 if(!IsFlyable() && Smoke)
1172 {
1173 DecAnimatedEntities();
1174
1175 for(smoke* S = Smoke; S; S = S->Next)
1176 S->SendToHell();
1177
1178 Smoke = 0;
1179 SmokeAlphaSum = 0;
1180 }
1181
1182 if(!WasTransparent == !!CalculateIsTransparent())
1183 {
1184 if(Noxified)
1185 GetLevel()->ForceEmitterEmitation(EmitterBackup, SunEmitter, FORCE_ADD);
1186 else
1187 GetLevel()->ForceEmitterEmitation(Emitter, SunEmitter);
1188
1189 CalculateLuminance();
1190
1191 if(LastSeen == game::GetLOSTick())
1192 game::SendLOSUpdateRequest();
1193 }
1194 }
1195
DrawParticles(long Color,truth DrawHere)1196 void lsquare::DrawParticles(long Color, truth DrawHere)
1197 {
1198 if(GetPos().X < game::GetCamera().X
1199 || GetPos().Y < game::GetCamera().Y
1200 || GetPos().X >= game::GetCamera().X + game::GetScreenXSize()
1201 || GetPos().Y >= game::GetCamera().Y + game::GetScreenYSize()
1202 || !CanBeSeenByPlayer(true)
1203 || Color == TRANSPARENT_COLOR)
1204 return;
1205
1206 clock_t StartTime = clock();
1207
1208 if(DrawHere)
1209 game::DrawEverythingNoBlit();
1210
1211 if(Color & RANDOM_COLOR)
1212 Color = MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190);
1213
1214 v2 Pos = game::CalculateScreenCoordinates(GetPos());
1215
1216 for(int c = 0; c < 10; ++c)
1217 DOUBLE_BUFFER->PutPixel(Pos + v2(1 + RAND() % 14, 1 + RAND() % 14), Color);
1218
1219 Flags |= STRONG_NEW_DRAW_REQUEST; // Clean the pixels from the screen afterwards
1220
1221 if(DrawHere)
1222 {
1223 graphics::BlitDBToScreen();
1224 while(clock() - StartTime < 0.02 * CLOCKS_PER_SEC);
1225 }
1226 }
1227
DipInto(item * Thingy,character * Dipper)1228 truth lsquare::DipInto(item* Thingy, character* Dipper)
1229 {
1230 if(IsDipDestination())
1231 {
1232 room* Room = GetRoom();
1233
1234 if(Room && Room->HasDipHandler() && !Room->Dip(Dipper))
1235 return false;
1236
1237 return (GLTerrain->IsDipDestination() && GLTerrain->DipInto(Thingy, Dipper))
1238 || (OLTerrain && OLTerrain->IsDipDestination() && OLTerrain->DipInto(Thingy, Dipper));
1239 }
1240 else
1241 {
1242 if(Dipper->IsPlayer())
1243 ADD_MESSAGE("You cannot dip %s on that square!", Thingy->CHAR_NAME(DEFINITE));
1244
1245 return false;
1246 }
1247 }
1248
1249 // return true if key fits someplace
1250
TryKey(item * Key,character * Applier)1251 truth lsquare::TryKey(item* Key, character* Applier)
1252 {
1253 if(GetOLTerrain() && GetOLTerrain()->TryKey(Key, Applier))
1254 return true;
1255
1256 if((!GetOLTerrain() || !GetOLTerrain()->HasKeyHole()) && !GetStack()->TryKey(Key, Applier))
1257 {
1258 ADD_MESSAGE("There's no place here to put the key in!");
1259 return false;
1260 }
1261
1262 return true;
1263 }
1264
SignalSeen(ulong Tick)1265 void lsquare::SignalSeen(ulong Tick)
1266 {
1267 if(LastSeen < Tick - 2)
1268 Flags |= STRONG_NEW_DRAW_REQUEST;
1269
1270 Flags &= ~(IN_SQUARE_STACK|PERFECTLY_QUADRI_HANDLED);
1271 LastSeen = Tick;
1272
1273 if(!(Flags & IS_TRANSPARENT))
1274 {
1275 col24 OldLuminance = Luminance;
1276 CalculateLuminance();
1277
1278 if(OldLuminance != Luminance)
1279 {
1280 Flags |= NEW_DRAW_REQUEST;
1281
1282 if(IsDark() != game::IsDark(OldLuminance))
1283 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
1284 }
1285 }
1286
1287 if(IsDark())
1288 {
1289 v2 Dist = Pos - PLAYER->GetPos();
1290
1291 if(abs(Dist.X) > 1 || abs(Dist.Y) > 1)
1292 {
1293 LastSeen -= 2;
1294 return;
1295 }
1296 }
1297
1298 if(!Memorized)
1299 CreateMemorized();
1300
1301 UpdateMemorized();
1302 UpdateMemorizedDescription();
1303
1304 if(Character)
1305 Character->CheckIfSeen();
1306 }
1307
DrawMemorized(blitdata & BlitData) const1308 void lsquare::DrawMemorized(blitdata& BlitData) const
1309 {
1310 LastSeen = 0;
1311 Flags &= ~STRONG_NEW_DRAW_REQUEST;
1312 BlitData.Luminance = ivanconfig::GetContrastLuminance();
1313
1314 if(FowMemorized)
1315 FowMemorized->LuminanceBlit(BlitData);
1316 else
1317 DOUBLE_BUFFER->Fill(BlitData.Dest, BlitData.Border, 0);
1318
1319 ccharacter* C = Character;
1320
1321 if(C && C->CanBeSeenByPlayer())
1322 {
1323 BlitData.CustomData |= C->GetSquareIndex(Pos);
1324 C->Draw(BlitData);
1325 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
1326 }
1327 }
1328
DrawMemorizedCharacter(blitdata & BlitData) const1329 void lsquare::DrawMemorizedCharacter(blitdata& BlitData) const
1330 {
1331 BlitData.Luminance = ivanconfig::GetContrastLuminance();
1332
1333 if(FowMemorized)
1334 FowMemorized->LuminanceBlit(BlitData);
1335 else
1336 DOUBLE_BUFFER->Fill(BlitData.Dest, BlitData.Border, 0);
1337
1338 BlitData.CustomData |= Character->GetSquareIndex(Pos);
1339 Character->Draw(BlitData);
1340 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
1341 Flags |= STRONG_NEW_DRAW_REQUEST;
1342 }
1343
IsDangerous(ccharacter * Who) const1344 truth lsquare::IsDangerous(ccharacter* Who) const
1345 {
1346 return ((!Who->IsFlying()
1347 && (Stack->IsDangerous(Who)
1348 || HasDangerousFluids(Who)))
1349 || IsDangerousToBreathe(Who) || HasDangerousTraps(Who));
1350 }
1351
IsScary(ccharacter * Who) const1352 truth lsquare::IsScary(ccharacter* Who) const
1353 {
1354 return IsScaryToBreathe(Who);
1355 }
1356
GetStackOfAdjacentSquare(int I) const1357 stack* lsquare::GetStackOfAdjacentSquare(int I) const
1358 {
1359 lsquare* Square = 0;
1360
1361 switch(I)
1362 {
1363 case LEFT: Square = NeighbourLSquare[WEST]; break;
1364 case DOWN: Square = NeighbourLSquare[SOUTH]; break;
1365 case UP: Square = NeighbourLSquare[NORTH]; break;
1366 case RIGHT: Square = NeighbourLSquare[EAST]; break;
1367 }
1368
1369 return Square ? Square->Stack : 0;
1370 }
1371
SendMemorizedUpdateRequest()1372 void lsquare::SendMemorizedUpdateRequest()
1373 {
1374 if(!(Flags & FREEZED))
1375 {
1376 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
1377
1378 if(!game::IsGenerating() && (CanBeSeenByPlayer() || CanBeFeltByPlayer()))
1379 {
1380 if(!Memorized)
1381 CreateMemorized();
1382
1383 UpdateMemorized();
1384 UpdateMemorizedDescription();
1385 }
1386 }
1387 }
1388
KickAnyoneStandingHereAway()1389 void lsquare::KickAnyoneStandingHereAway()
1390 {
1391 if(Character)
1392 {
1393 character* Backup = Character;
1394 Backup->Remove();
1395 Backup->PutNear(Pos);
1396 }
1397 }
1398
operator <<(outputfile & SaveFile,const emitter & Emitter)1399 outputfile& operator<<(outputfile& SaveFile, const emitter& Emitter)
1400 {
1401 SaveFile.Write(reinterpret_cast<cchar*>(&Emitter), sizeof(Emitter));
1402 return SaveFile;
1403 }
1404
operator >>(inputfile & SaveFile,emitter & Emitter)1405 inputfile& operator>>(inputfile& SaveFile, emitter& Emitter)
1406 {
1407 SaveFile.Read(reinterpret_cast<char*>(&Emitter), sizeof(Emitter));
1408 return SaveFile;
1409 }
1410
AddItem(item * Item)1411 void lsquare::AddItem(item* Item)
1412 {
1413 Stack->AddItem(Item);
1414 }
1415
DrawLightning(v2 StartPos,long Color,int Direction,truth DrawHere)1416 v2 lsquare::DrawLightning(v2 StartPos, long Color, int Direction, truth DrawHere)
1417 {
1418 if(GetPos().X < game::GetCamera().X
1419 || GetPos().Y < game::GetCamera().Y
1420 || GetPos().X >= game::GetCamera().X + game::GetScreenXSize()
1421 || GetPos().Y >= game::GetCamera().Y + game::GetScreenYSize()
1422 || !CanBeSeenByPlayer(true))
1423 switch(Direction)
1424 {
1425 case NORTH: return v2(RAND() & 15, 15);
1426 case WEST: return v2(15, RAND() & 15);
1427 case EAST: return v2(0, RAND() & 15);
1428 case SOUTH: return v2(RAND() & 15, 0);
1429 default: return StartPos;
1430 }
1431
1432 clock_t StartTime = clock();
1433 bitmap Empty(TILE_V2, TRANSPARENT_COLOR);
1434 Empty.ActivateFastFlag();
1435
1436 if(Color & RANDOM_COLOR)
1437 Color = MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190);
1438
1439 if(Direction != YOURSELF)
1440 {
1441 while(!Empty.CreateLightning(StartPos, game::GetMoveVector(Direction), 16, Color));
1442 v2 EndPos(0, 0);
1443
1444 switch(Direction)
1445 {
1446 case NORTHWEST: EndPos = v2(0, 0); break;
1447 case NORTH: EndPos = v2(RAND() & 15, 0); StartPos = v2(EndPos.X, 15); break;
1448 case NORTHEAST: EndPos = v2(15, 0); break;
1449 case WEST: EndPos = v2(0, RAND() & 15); StartPos = v2(15, EndPos.Y); break;
1450 case EAST: EndPos = v2(15, RAND() & 15); StartPos = v2(0, EndPos.Y); break;
1451 case SOUTHWEST: EndPos = v2(0, 15); break;
1452 case SOUTH: EndPos = v2(RAND() & 15, 15); StartPos = v2(EndPos.X, 0); break;
1453 case SOUTHEAST: EndPos = v2(15, 15); break;
1454 }
1455
1456 while(!Empty.CreateLightning(EndPos, -game::GetMoveVector(Direction), NO_LIMIT, Color));
1457 }
1458 else
1459 {
1460 static v2 Dir[4] = { v2(0, -1), v2(-1, 0), v2(1, 0), v2(0, 1) };
1461
1462 for(int d = 0; d < 4; ++d)
1463 while(!Empty.CreateLightning(StartPos + Dir[d], ZERO_V2, 10, Color));
1464 }
1465
1466 if(DrawHere)
1467 game::DrawEverythingNoBlit();
1468
1469 blitdata B = { DOUBLE_BUFFER,
1470 { 0, 0 },
1471 { 0, 0 },
1472 { TILE_SIZE, TILE_SIZE },
1473 { 0 },
1474 TRANSPARENT_COLOR,
1475 0 };
1476
1477 B.Dest = game::CalculateScreenCoordinates(GetPos());
1478 Empty.NormalMaskedBlit(B);
1479 Flags |= STRONG_NEW_DRAW_REQUEST;
1480
1481 if(DrawHere)
1482 {
1483 graphics::BlitDBToScreen();
1484 while(clock() - StartTime < 0.02 * CLOCKS_PER_SEC);
1485 }
1486
1487 return StartPos;
1488 }
1489
Polymorph(const beamdata & Beam)1490 truth lsquare::Polymorph(const beamdata& Beam)
1491 {
1492 GetStack()->Polymorph(Beam.Owner);
1493
1494 if(GetOLTerrain())
1495 GetOLTerrain()->Polymorph(Beam.Owner);
1496
1497 character* Character = GetCharacter();
1498
1499 if(Character)
1500 {
1501 if(Character->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
1502 {
1503 if(Character->DrinkMagic(Beam))
1504 return false;
1505 }
1506 if(Beam.Owner && Character->GetTeam() != Beam.Owner->GetTeam())
1507 Beam.Owner->Hostility(Character);
1508
1509 Character->PolymorphRandomly(1, 999999, 5000 + RAND() % 5000);
1510 }
1511
1512 if(Engraved)
1513 {
1514 for(int c = 0; Engraved[c] != '\0'; ++c)
1515 {
1516 if(RAND_2)
1517 {
1518 Engraved[c] = 32 + RAND_N(95);
1519 }
1520 }
1521 }
1522 return false;
1523 }
1524
Strike(const beamdata & Beam)1525 truth lsquare::Strike(const beamdata& Beam)
1526 {
1527 int Damage = 50 + RAND() % 21 - RAND() % 21;
1528 GetStack()->ReceiveDamage(Beam.Owner, Damage, ENERGY, Beam.Direction);
1529 ReceiveTrapDamage(Beam.Owner, Damage, ENERGY, Beam.Direction);
1530
1531 character* Char = GetCharacter();
1532
1533 if(Char)
1534 {
1535 if(Char->IsPlayer())
1536 ADD_MESSAGE("You are hit by a burst of energy!");
1537 else if(Char->CanBeSeenByPlayer())
1538 ADD_MESSAGE("%s is hit by a burst of energy!", Char->CHAR_NAME(DEFINITE));
1539
1540 if(Char->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
1541 {
1542 if(Char->DrinkMagic(Beam))
1543 return false;
1544 }
1545
1546 if(Beam.Owner)
1547 Beam.Owner->Hostility(Char);
1548
1549 Char->ReceiveDamage(Beam.Owner, Damage, ENERGY, ALL);
1550 Char->CheckDeath(Beam.DeathMsg, Beam.Owner);
1551 }
1552
1553 if(GetOLTerrain())
1554 GetOLTerrain()->ReceiveDamage(Beam.Owner, Damage, ENERGY);
1555
1556 return false;
1557 }
1558
FireBall(const beamdata & Beam)1559 truth lsquare::FireBall(const beamdata& Beam)
1560 {
1561 character* Char = GetCharacter();
1562
1563 if(!IsFlyable() || Char)
1564 {
1565 if(Char && Char->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
1566 {
1567 if(Char->DrinkMagic(Beam))
1568 return false;
1569 }
1570
1571 if(CanBeSeenByPlayer(true))
1572 ADD_MESSAGE("A magical explosion is triggered!");
1573
1574 GetLevel()->Explosion(Beam.Owner, Beam.DeathMsg, Pos, 75 + RAND() % 25 - RAND() % 25, true, true);
1575 return true;
1576 }
1577
1578 return false;
1579 }
1580
Teleport(const beamdata & Beam)1581 truth lsquare::Teleport(const beamdata& Beam)
1582 {
1583 if(Character)
1584 {
1585 if(Character->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
1586 {
1587 if(Character->DrinkMagic(Beam))
1588 return false;
1589 }
1590
1591 if(Beam.Owner && Character->GetTeam() != Beam.Owner->GetTeam())
1592 Beam.Owner->Hostility(GetCharacter());
1593
1594 if(Character->IsPlayer())
1595 ADD_MESSAGE("You experience a forced teleportation.");
1596 else if(Character->CanBeSeenByPlayer())
1597 ADD_MESSAGE("%s disappears!", Character->CHAR_NAME(DEFINITE));
1598
1599 Character->TeleportRandomly();
1600 }
1601
1602 if(RoomIndex)
1603 GetLevel()->GetRoom(RoomIndex)->TeleportSquare(Beam.Owner, this);
1604
1605 GetStack()->TeleportRandomly();
1606 return false;
1607 }
1608
Haste(const beamdata & Beam)1609 truth lsquare::Haste(const beamdata& Beam)
1610 {
1611 GetStack()->Haste();
1612 character* Dude = GetCharacter();
1613
1614 if(Dude)
1615 {
1616 if(Dude->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
1617 {
1618 if(Dude->DrinkMagic(Beam))
1619 return false;
1620 }
1621
1622 Dude->Haste();
1623 }
1624
1625 return false;
1626 }
1627
Slow(const beamdata & Beam)1628 truth lsquare::Slow(const beamdata& Beam)
1629 {
1630 GetStack()->Slow();
1631 character* Dude = GetCharacter();
1632
1633 if(Dude)
1634 {
1635 if(Dude->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
1636 {
1637 if(Dude->DrinkMagic(Beam))
1638 return false;
1639 }
1640 if(Beam.Owner)
1641 Beam.Owner->Hostility(Dude);
1642
1643 Dude->Slow();
1644 }
1645
1646 return false;
1647 }
1648
Resurrect(const beamdata & Beam)1649 truth lsquare::Resurrect(const beamdata& Beam)
1650 {
1651 character* Char = GetCharacter();
1652
1653 if(Char)
1654 {
1655 if(Char->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
1656 {
1657 if(Char->DrinkMagic(Beam))
1658 return false;
1659 }
1660
1661 return Char->RaiseTheDead(Beam.Owner);
1662 }
1663 else
1664 return GetStack()->RaiseTheDead(Beam.Owner);
1665 }
1666
Invisibility(const beamdata & Beam)1667 truth lsquare::Invisibility(const beamdata& Beam)
1668 {
1669 character* Char = GetCharacter();
1670
1671 if(Char)
1672 {
1673 if(Char->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
1674 {
1675 if(Char->DrinkMagic(Beam))
1676 return false;
1677 }
1678
1679 Char->BeginTemporaryState(INVISIBLE, 1000 + RAND() % 1001);
1680 }
1681
1682 return false;
1683 }
1684
Duplicate(const beamdata & Beam)1685 truth lsquare::Duplicate(const beamdata& Beam)
1686 {
1687 truth DuplicatedSomething = false;
1688 character* Character = GetCharacter();
1689
1690 if(Character)
1691 {
1692 if(Character->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
1693 {
1694 if(Character->DrinkMagic(Beam))
1695 return false;
1696 }
1697
1698 DuplicatedSomething = truth(Character->DuplicateToNearestSquare(Beam.Owner, Beam.SpecialParameters));
1699 }
1700
1701 if(GetStack()->Duplicate(DuplicatedSomething ? 4 : 5, Beam.SpecialParameters))
1702 DuplicatedSomething = true;
1703
1704 return DuplicatedSomething;
1705 }
1706
Lightning(const beamdata & Beam)1707 truth lsquare::Lightning(const beamdata& Beam)
1708 {
1709 int Damage = 20 + RAND() % 6 - RAND() % 6;
1710 GetStack()->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY, Beam.Direction);
1711 ReceiveTrapDamage(Beam.Owner, Damage, ELECTRICITY, Beam.Direction);
1712
1713 character* Char = GetCharacter();
1714
1715 if(Char)
1716 {
1717 if(Char->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
1718 {
1719 if(Char->DrinkMagic(Beam))
1720 return false;
1721 }
1722
1723 if(Char->IsPlayer())
1724 ADD_MESSAGE("A massive burst of electricity runs through your body!");
1725 else if(Char->CanBeSeenByPlayer())
1726 ADD_MESSAGE("A massive burst of electricity runs through %s!", Char->CHAR_NAME(DEFINITE));
1727
1728 if(Beam.Owner)
1729 Beam.Owner->Hostility(Char);
1730
1731 Char->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY, ALL);
1732 Char->CheckDeath(Beam.DeathMsg, Beam.Owner);
1733 }
1734
1735 if(GetOLTerrain())
1736 GetOLTerrain()->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY);
1737
1738 return false;
1739 }
1740
DoorCreation(const beamdata & Beam)1741 truth lsquare::DoorCreation(const beamdata& Beam)
1742 {
1743 character* Char = GetCharacter();
1744 if(Char)
1745 {
1746 if(Char->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
1747 {
1748 if(Char->DrinkMagic(Beam))
1749 return false;
1750 }
1751 }
1752 if((!GetOLTerrain()
1753 || GetOLTerrain()->IsSafeToCreateDoor())
1754 && !Char
1755 && (GetLevel()->IsOnGround()
1756 || (Pos.X > 0 && Pos.Y > 0
1757 && Pos.X < GetLevel()->GetXSize() - 1 && Pos.Y < GetLevel()->GetYSize() - 1)))
1758 {
1759 if(Beam.Owner && GetRoom())
1760 GetRoom()->HostileAction(Beam.Owner);
1761
1762 door* Door = door::Spawn(0, NO_MATERIALS);
1763 Door->InitMaterials(MAKE_MATERIAL(STEEL));
1764
1765 if(RAND() % 10)
1766 Door->Lock();
1767
1768 ChangeOLTerrainAndUpdateLights(Door);
1769 return true;
1770 }
1771
1772 return false;
1773 }
1774
WallCreation(const beamdata & Beam)1775 truth lsquare::WallCreation(const beamdata& Beam)
1776 {
1777 character* Char = GetCharacter();
1778 if(Char)
1779 {
1780 if(Char->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
1781 {
1782 if(Char->DrinkMagic(Beam))
1783 return false;
1784 }
1785 }
1786 if((!GetOLTerrain()
1787 || GetOLTerrain()->IsSafeToCreateDoor())
1788 && !Char
1789 && (GetLevel()->IsOnGround()
1790 || (Pos.X > 0 && Pos.Y > 0
1791 && Pos.X < GetLevel()->GetXSize() - 1 && Pos.Y < GetLevel()->GetYSize() - 1)))
1792 {
1793 if(Beam.Owner && GetRoom())
1794 GetRoom()->HostileAction(Beam.Owner);
1795
1796 earth* Wall = earth::Spawn(0, NO_MATERIALS);
1797 Wall->InitMaterials(MAKE_MATERIAL(GRANITE));
1798
1799 ChangeOLTerrainAndUpdateLights(Wall);
1800 return true;
1801 }
1802
1803 return false;
1804 }
1805
1806 truth (lsquare::*BeamEffect[BEAM_EFFECTS])(const beamdata&) =
1807 {
1808 &lsquare::Polymorph,
1809 &lsquare::Strike,
1810 &lsquare::FireBall,
1811 &lsquare::Teleport,
1812 &lsquare::Haste,
1813 &lsquare::Slow,
1814 &lsquare::Resurrect,
1815 &lsquare::Invisibility,
1816 &lsquare::Duplicate,
1817 &lsquare::Lightning,
1818 &lsquare::DoorCreation,
1819 &lsquare::AcidRain,
1820 &lsquare::Necromancy,
1821 &lsquare::Webbing,
1822 &lsquare::Alchemize,
1823 &lsquare::SoftenMaterial,
1824 &lsquare::WallCreation
1825 };
1826
truth(lsquare::* lsquare::GetBeamEffect (int I))1827 truth (lsquare::*lsquare::GetBeamEffect(int I))(const beamdata&)
1828 {
1829 return BeamEffect[I];
1830 }
1831
CheckKick(ccharacter * Kicker) const1832 truth lsquare::CheckKick(ccharacter* Kicker) const
1833 {
1834 if(Character && Kicker->CheckIfTooScaredToHit(Character))
1835 return false;
1836
1837 if(RoomIndex && !GetLevel()->GetRoom(RoomIndex)->CheckKickSquare(Kicker, this))
1838 return false;
1839
1840 return true;
1841 }
1842
GetHitByExplosion(const explosion * Explosion)1843 void lsquare::GetHitByExplosion(const explosion* Explosion)
1844 {
1845 if(Explosion->ID == LastExplosionID)
1846 return;
1847
1848 LastExplosionID = Explosion->ID;
1849 int DistanceSquare = (Pos - Explosion->Pos).GetLengthSquare();
1850
1851 if(DistanceSquare > Explosion->RadiusSquare)
1852 return;
1853
1854 int Damage = Explosion->Strength / (DistanceSquare + 1);
1855
1856 if(Character && (Explosion->HurtNeutrals
1857 || (Explosion->Terrorist && Character->GetRelation(Explosion->Terrorist) == HOSTILE)))
1858 {
1859 if(Character->IsPlayer())
1860 game::SetPlayerWasHurtByExplosion(true);
1861 else
1862 Character->GetHitByExplosion(Explosion, Damage);
1863 }
1864
1865 GetStack()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, FIRE);
1866 GetStack()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE);
1867
1868 ReceiveTrapDamage(Explosion->Terrorist, Damage >> 1, FIRE);
1869 ReceiveTrapDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE);
1870
1871 if(GetOLTerrain())
1872 GetOLTerrain()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, FIRE);
1873
1874 if(GetOLTerrain())
1875 GetOLTerrain()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE);
1876 }
1877
GetSpoiledItems() const1878 int lsquare::GetSpoiledItems() const
1879 {
1880 return GetStack()->GetSpoiledItems();
1881 }
1882
LowerEnchantment(const beamdata & Beam)1883 truth lsquare::LowerEnchantment(const beamdata& Beam)
1884 {
1885 character* Char = GetCharacter();
1886 itemvector AllItems;
1887 sortdata SortData(AllItems, Beam.Owner, true, &item::IsEnchantable);
1888 SortAllItems(SortData);
1889 item* RandomItem;
1890
1891 if(!AllItems.empty())
1892 RandomItem = AllItems[RAND() % AllItems.size()];
1893 else
1894 return false;
1895
1896 if(Char)
1897 {
1898 if(Beam.Owner && RAND_N(Char->GetAttribute(WILL_POWER)) > RAND_N(Beam.Owner->GetAttribute(MANA)))
1899 {
1900 if(Char->IsPlayer())
1901 ADD_MESSAGE("%s glows dull brown for a second, but then it passes.", RandomItem->CHAR_NAME(DEFINITE));
1902
1903 Char->EditExperience(WILL_POWER, 100, 1 << 12);
1904 return false;
1905 }
1906
1907 if(Char->IsPlayer())
1908 ADD_MESSAGE("%s glows dull brown for a moment!", RandomItem->CHAR_NAME(DEFINITE));
1909
1910 if(Beam.Owner)
1911 Beam.Owner->Hostility(Char);
1912 }
1913
1914 if(RandomItem->GetEnchantment() > -5)
1915 RandomItem->EditEnchantment(-1);
1916
1917 return true;
1918 }
1919
SortAllItems(const sortdata & SortData)1920 void lsquare::SortAllItems(const sortdata& SortData)
1921 {
1922 if(GetCharacter())
1923 GetCharacter()->SortAllItems(SortData);
1924
1925 GetStack()->SortAllItems(SortData);
1926 }
1927
RemoveSmoke(smoke * ToBeRemoved)1928 void lsquare::RemoveSmoke(smoke* ToBeRemoved)
1929 {
1930 smoke* S = Smoke;
1931
1932 if(S == ToBeRemoved)
1933 {
1934 Smoke = S->Next;
1935
1936 if(!S)
1937 DecAnimatedEntities();
1938 }
1939 else
1940 {
1941 smoke* LS;
1942
1943 do
1944 {
1945 LS = S;
1946 S = S->Next;
1947 }
1948 while(S != ToBeRemoved);
1949
1950 LS->Next = S->Next;
1951 }
1952 }
1953
AddSmoke(gas * ToBeAdded)1954 void lsquare::AddSmoke(gas* ToBeAdded)
1955 {
1956 smoke* S = Smoke;
1957
1958 if(!S)
1959 {
1960 Smoke = new smoke(ToBeAdded, this);
1961 IncAnimatedEntities();
1962 }
1963 else
1964 {
1965 smoke* LS;
1966
1967 do
1968 {
1969 if(ToBeAdded->IsSameAs(S->GetGas()))
1970 {
1971 S->Merge(ToBeAdded);
1972 return;
1973 }
1974
1975 LS = S;
1976 S = S->Next;
1977 }
1978 while(S);
1979
1980 LS->Next = new smoke(ToBeAdded, this);
1981 }
1982 }
1983
ShowSmokeMessage() const1984 void lsquare::ShowSmokeMessage() const
1985 {
1986 for(const smoke* S = Smoke; S; S = S->Next)
1987 S->AddBreatheMessage();
1988 }
1989
SignalSmokeAlphaChange(int What)1990 void lsquare::SignalSmokeAlphaChange(int What)
1991 {
1992 SmokeAlphaSum += What;
1993 SignalPossibleTransparencyChange();
1994 }
1995
AddHitEffect(hiteffectSetup s)1996 hiteffect* lsquare::AddHitEffect(hiteffectSetup s)
1997 {DBGLN;
1998 if(ivanconfig::GetHitIndicator()==0)return NULL;
1999 if(s.lItemEffectReferenceID==0)return NULL;
2000
2001 s.iMode=ivanconfig::GetHitIndicator();
2002 s.LSquareUnder=this;
2003 hiteffect* S = HitEffect; //head of the linked list
2004 hiteffect* New = new hiteffect(s);
2005
2006 if(!S)
2007 {
2008 HitEffect = New;
2009 IncAnimatedEntities();
2010 }
2011 else
2012 {
2013 hiteffect* LS;
2014
2015 do
2016 { // The same item can be re-added if it hits again later
2017 LS = S; //finds the tail of linked list
2018 S = S->Next;
2019 }
2020 while(S);
2021
2022 LS->Next = New;
2023 }
2024
2025 return New;
2026 }
RemoveHitEffect(hiteffect * ToBeRemoved)2027 void lsquare::RemoveHitEffect(hiteffect* ToBeRemoved)
2028 {
2029 hiteffect* H = HitEffect;
2030
2031 if(H == ToBeRemoved) // head of linked list
2032 {
2033 HitEffect = H->Next;
2034
2035 if(!H)
2036 DecAnimatedEntities();
2037 }
2038 else
2039 {
2040 hiteffect* LH; // last or previously linked
2041
2042 do
2043 {
2044 LH = H;
2045 H = H->Next;
2046 }
2047 while(H != ToBeRemoved);
2048
2049 LH->Next = H->Next;
2050 }
2051 }
2052
GetDivineMaster() const2053 int lsquare::GetDivineMaster() const
2054 {
2055 return RoomIndex ? GetLevel()->GetRoom(RoomIndex)->GetDivineMaster() : 0;
2056 }
2057
DisplaySmokeInfo(festring & Msg) const2058 void lsquare::DisplaySmokeInfo(festring& Msg) const
2059 {
2060 if(Smoke)
2061 {
2062 if(!Smoke->Next)
2063 Msg << " A cloud of " << Smoke->GetGas()->GetName(false, false) << " surrounds the square.";
2064 else
2065 Msg << " A lot of gases hover over the square.";
2066 }
2067 }
2068
ReceiveEarthQuakeDamage()2069 void lsquare::ReceiveEarthQuakeDamage()
2070 {
2071 GetStack()->ReceiveDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE);
2072 ReceiveTrapDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE);
2073 /* Gum solution */
2074
2075 if(GetOLTerrain() && GetOLTerrain()->IsDoor())
2076 GetOLTerrain()->ReceiveDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE);
2077 }
2078
CanBeFeltByPlayer() const2079 truth lsquare::CanBeFeltByPlayer() const
2080 {
2081 return OLTerrain && !PLAYER->CanMoveOn(this) && Pos.IsAdjacent(PLAYER->GetPos());
2082 }
2083
PreProcessForBone()2084 void lsquare::PreProcessForBone()
2085 {
2086 DestroyMemorized();
2087 LastSeen = 0;
2088
2089 if(OLTerrain)
2090 OLTerrain->PreProcessForBone();
2091
2092 if(Smoke)
2093 {
2094 DecAnimatedEntities();
2095
2096 for(smoke* S = Smoke; S; S = S->Next)
2097 S->SendToHell();
2098
2099 Smoke = 0;
2100 SmokeAlphaSum = 0;
2101 }
2102
2103 if(Character && !Character->PreProcessForBone())
2104 {
2105 Character->SendToHell();
2106 Character->Remove();
2107 }
2108
2109 for(fluid* F = Fluid; F; F = F->Next)
2110 F->PreProcessForBone();
2111
2112 for(trap* T = Trap; T; T = T->Next)
2113 T->PreProcessForBone();
2114
2115 GetStack()->PreProcessForBone();
2116 }
2117
PostProcessForBone(double & DangerSum,int & Enemies)2118 void lsquare::PostProcessForBone(double& DangerSum, int& Enemies)
2119 {
2120 if(OLTerrain)
2121 OLTerrain->PostProcessForBone();
2122
2123 if(Character && !Character->PostProcessForBone(DangerSum, Enemies))
2124 {
2125 Character->SendToHell();
2126 Character->Remove();
2127 }
2128
2129 for(fluid* F = Fluid; F; F = F->Next)
2130 F->PostProcessForBone();
2131
2132 for(trap* T = Trap; T; T = T->Next)
2133 T->PostProcessForBone();
2134
2135 GetStack()->PostProcessForBone();
2136 }
2137
FinalProcessForBone()2138 void lsquare::FinalProcessForBone()
2139 {
2140 if(OLTerrain)
2141 OLTerrain->FinalProcessForBone();
2142
2143 if(Character)
2144 Character->FinalProcessForBone();
2145
2146 GetStack()->FinalProcessForBone();
2147 }
2148
EngravingsCanBeReadByPlayer()2149 truth lsquare::EngravingsCanBeReadByPlayer()
2150 {
2151 return PLAYER->CanRead(); // Might be a good idea to improve sometime in the distant future.
2152 }
2153
DisplayEngravedInfo(festring & Buffer) const2154 void lsquare::DisplayEngravedInfo(festring& Buffer) const
2155 {
2156 Buffer << " There is a message engraved here: \"" << Engraved << '\"';
2157 }
2158
IsDangerousToBreathe(ccharacter * Who) const2159 truth lsquare::IsDangerousToBreathe(ccharacter* Who) const
2160 {
2161 for(const smoke* S = Smoke; S; S = S->Next)
2162 if(S->IsDangerousToBreathe(Who))
2163 return true;
2164
2165 return false;
2166 }
2167
IsScaryToBreathe(ccharacter * Who) const2168 truth lsquare::IsScaryToBreathe(ccharacter* Who) const
2169 {
2170 for(const smoke* S = Smoke; S; S = S->Next)
2171 if(S->IsScaryToBreathe(Who))
2172 return true;
2173
2174 return false;
2175 }
2176
2177 struct groundborderpartner
2178 {
operator <groundborderpartner2179 truth operator<(const groundborderpartner& P) const
2180 {
2181 return Terrain->GetBorderTilePriority() < P.Terrain->GetBorderTilePriority();
2182 }
2183 glterrain* Terrain;
2184 int SquareIndex;
2185 };
2186
CalculateGroundBorderPartners()2187 void lsquare::CalculateGroundBorderPartners()
2188 {
2189 if(GroundBorderPartnerInfo & BORDER_PARTNER_ANIMATED)
2190 DecStaticAnimatedEntities();
2191
2192 groundborderpartner BorderPartner[8];
2193 int Index = 0;
2194 int Priority = GLTerrain->GetBorderTilePriority();
2195
2196 for(int d = 0; d < 8; ++d)
2197 {
2198 lsquare* Square = NeighbourLSquare[d];
2199
2200 if(Square)
2201 {
2202 glterrain* Terrain = Square->GetGLTerrain();
2203
2204 if(Terrain && Terrain->UseBorderTiles()
2205 && Terrain->GetBorderTilePriority() > Priority)
2206 {
2207 BorderPartner[Index].Terrain = Terrain;
2208 BorderPartner[Index].SquareIndex = 7 - d;
2209 ++Index;
2210 }
2211 }
2212 }
2213
2214 GroundBorderPartnerInfo = 0;
2215
2216 if(!Index)
2217 {
2218 delete [] GroundBorderPartnerTerrain;
2219 GroundBorderPartnerTerrain = 0;
2220 return;
2221 }
2222
2223 if(!GroundBorderPartnerTerrain)
2224 GroundBorderPartnerTerrain = new glterrain*[8];
2225
2226 std::sort(BorderPartner, BorderPartner + Index);
2227 truth Animated = false;
2228
2229 for(int c = 0; c < Index; ++c)
2230 {
2231 glterrain* T = BorderPartner[c].Terrain;
2232 GroundBorderPartnerTerrain[c] = T;
2233 GroundBorderPartnerInfo |= BorderPartner[c].SquareIndex << ((c << 1) + c);
2234
2235 if(T->IsAnimated())
2236 Animated = true;
2237 }
2238
2239 if(Animated)
2240 {
2241 GroundBorderPartnerInfo |= BORDER_PARTNER_ANIMATED;
2242 IncStaticAnimatedEntities();
2243 }
2244
2245 GroundBorderPartnerInfo |= Index << 24;
2246 }
2247
2248 struct overborderpartner
2249 {
operator <overborderpartner2250 truth operator<(const overborderpartner& P) const
2251 {
2252 return Terrain->GetBorderTilePriority() < P.Terrain->GetBorderTilePriority();
2253 }
2254 olterrain* Terrain;
2255 int SquareIndex;
2256 };
2257
CalculateOverBorderPartners()2258 void lsquare::CalculateOverBorderPartners()
2259 {
2260 if(OverBorderPartnerInfo & BORDER_PARTNER_ANIMATED)
2261 DecStaticAnimatedEntities();
2262
2263 overborderpartner BorderPartner[8];
2264 int Index = 0;
2265 int Priority = OLTerrain ? OLTerrain->GetBorderTilePriority() : 0;
2266
2267 for(int d = 0; d < 8; ++d)
2268 {
2269 lsquare* Square = NeighbourLSquare[d];
2270
2271 if(Square)
2272 {
2273 olterrain* Terrain = Square->GetOLTerrain();
2274
2275 if(Terrain && Terrain->UseBorderTiles()
2276 && Terrain->GetBorderTilePriority() > Priority)
2277 {
2278 BorderPartner[Index].Terrain = Terrain;
2279 BorderPartner[Index].SquareIndex = 7 - d;
2280 ++Index;
2281 }
2282 }
2283 }
2284
2285 OverBorderPartnerInfo = 0;
2286
2287 if(!Index)
2288 {
2289 delete [] OverBorderPartnerTerrain;
2290 OverBorderPartnerTerrain = 0;
2291 return;
2292 }
2293
2294 if(!OverBorderPartnerTerrain)
2295 OverBorderPartnerTerrain = new olterrain*[8];
2296
2297 std::sort(BorderPartner, BorderPartner + Index);
2298 truth Animated = false;
2299
2300 for(int c = 0; c < Index; ++c)
2301 {
2302 olterrain* T = BorderPartner[c].Terrain;
2303 OverBorderPartnerTerrain[c] = T;
2304 OverBorderPartnerInfo |= BorderPartner[c].SquareIndex << ((c << 1) + c);
2305
2306 if(T->IsAnimated())
2307 Animated = true;
2308 }
2309
2310 if(Animated)
2311 {
2312 OverBorderPartnerInfo |= BORDER_PARTNER_ANIMATED;
2313 IncStaticAnimatedEntities();
2314 }
2315
2316 OverBorderPartnerInfo |= Index << 24;
2317 }
2318
RequestForGroundBorderPartnerUpdates()2319 void lsquare::RequestForGroundBorderPartnerUpdates()
2320 {
2321 if(!game::IsGenerating())
2322 for(int d = 0; d < 8; ++d)
2323 {
2324 lsquare* Square = NeighbourLSquare[d];
2325
2326 if(Square)
2327 {
2328 Square->CalculateGroundBorderPartners();
2329 Square->SendNewDrawRequest();
2330 Square->SendMemorizedUpdateRequest();
2331 }
2332 }
2333 }
2334
RequestForOverBorderPartnerUpdates()2335 void lsquare::RequestForOverBorderPartnerUpdates()
2336 {
2337 if(!game::IsGenerating())
2338 for(int d = 0; d < 8; ++d)
2339 {
2340 lsquare* Square = NeighbourLSquare[d];
2341
2342 if(Square)
2343 {
2344 Square->CalculateOverBorderPartners();
2345 Square->SendNewDrawRequest();
2346 Square->SendMemorizedUpdateRequest();
2347 }
2348 }
2349 }
2350
GetWalkability() const2351 int lsquare::GetWalkability() const
2352 {
2353 if(!GetLevel()->IsOnGround())
2354 {
2355 if(Pos.X >= 1 && Pos.Y >= 1 && Pos.X < GetLevel()->GetXSize() - 1 && Pos.Y < GetLevel()->GetYSize() - 1)
2356 return OLTerrain ? OLTerrain->GetWalkability() & GLTerrain->GetWalkability() : GLTerrain->GetWalkability();
2357 else
2358 return 0;
2359 }
2360 else
2361 return OLTerrain ? OLTerrain->GetWalkability() & GLTerrain->GetWalkability() : GLTerrain->GetWalkability();
2362 }
2363
RemoveFluid(fluid * ToRemove)2364 void lsquare::RemoveFluid(fluid* ToRemove)
2365 {
2366 fluid*& F = ListFind(Fluid, pointercomparer<fluid>(ToRemove));
2367 F = F->Next;
2368 SignalEmitationDecrease(ToRemove->GetEmitation());
2369 }
2370
2371 struct fluidcomparer
2372 {
fluidcomparerfluidcomparer2373 fluidcomparer(const liquid* Liquid) : Liquid(Liquid) { }
operator ()fluidcomparer2374 truth operator()(const fluid* F) const { return Liquid->IsSameAs(F->GetLiquid()); }
2375 const liquid* Liquid;
2376 };
2377
AddFluid(liquid * ToBeAdded)2378 fluid* lsquare::AddFluid(liquid* ToBeAdded)
2379 {
2380 fluid*& F = ListFind(Fluid, fluidcomparer(ToBeAdded));
2381
2382 if(F)
2383 {
2384 F->AddLiquidAndVolume(ToBeAdded->GetVolume());
2385 delete ToBeAdded;
2386 }
2387 else
2388 {
2389 F = new fluid(ToBeAdded, this);
2390 SignalEmitationIncrease(ToBeAdded->GetEmitation());
2391 }
2392
2393 SendNewDrawRequest();
2394 SendMemorizedUpdateRequest();
2395 return F;
2396 }
2397
DisplayFluidInfo(festring & Msg) const2398 void lsquare::DisplayFluidInfo(festring& Msg) const
2399 {
2400 if(Fluid)
2401 {
2402 festring TempMsg;
2403
2404 if(fluid::AddFluidInfo(Fluid, TempMsg)) // AddFluidInfo returns true in case 'are' should be used over 'is'
2405 Msg << ". There are ";
2406 else
2407 Msg << ". There is ";
2408
2409 Msg << TempMsg;
2410 AddLocationDescription(Msg);
2411 }
2412 }
2413
SpillFluid(character * Spiller,liquid * Liquid,truth ForceHit,truth ShowMsg)2414 void lsquare::SpillFluid(character* Spiller, liquid* Liquid, truth ForceHit, truth ShowMsg)
2415 {
2416 if(!Liquid->GetVolume())
2417 {
2418 delete Liquid;
2419 return;
2420 }
2421
2422 if(IsFlyable())
2423 {
2424 if(GetCharacter())
2425 {
2426 if(Spiller && !GetCharacter()->IsAlly(Spiller))
2427 Spiller->Hostility(GetCharacter());
2428
2429 long CharVolume = GetCharacter()->GetVolume();
2430 double ChanceMultiplier = 1. / (300 + sqrt(GetStack()->GetVolume() + CharVolume));
2431 double Root = sqrt(CharVolume);
2432
2433 if(ForceHit || Root > RAND() % 400 || Root > RAND() % 400)
2434 {
2435 long SpillVolume = long(Liquid->GetVolume() * Root * ChanceMultiplier);
2436
2437 if(SpillVolume)
2438 {
2439 if(ShowMsg && (GetCharacter()->IsPlayer() || GetCharacter()->CanBeSeenByPlayer()))
2440 ADD_MESSAGE("%s is spilled all over %s.", Liquid->GetName(false, false).CStr(),
2441 GetCharacter()->CHAR_DESCRIPTION(DEFINITE));
2442
2443 Liquid->EditVolume(-SpillVolume);
2444 GetCharacter()->SpillFluid(Spiller, Liquid->SpawnMoreLiquid(SpillVolume),
2445 GetCharacter()->GetSquareIndex(GetPos()));
2446 }
2447 }
2448 }
2449
2450 GetStack()->SpillFluid(Spiller, Liquid, Liquid->GetVolume());
2451 }
2452
2453 if(Liquid->GetVolume() && !Liquid->IsSameAs(GLTerrain->GetMainMaterial()))
2454 {
2455 fluid* F = AddFluid(Liquid);
2456
2457 if(GetCharacter())
2458 F->StepOnEffect(GetCharacter());
2459 }
2460 else
2461 delete Liquid;
2462 }
2463
DrawStacks(blitdata & BlitData) const2464 void lsquare::DrawStacks(blitdata& BlitData) const
2465 {
2466 Stack->Draw(PLAYER, BlitData, CENTER);
2467
2468 for(int c = 0; c < 4; ++c)
2469 {
2470 stack* Stack = GetStackOfAdjacentSquare(c);
2471
2472 if(Stack)
2473 Stack->Draw(PLAYER, BlitData, 3 - c);
2474 }
2475 }
2476
RemoveRain(rain * ToBeRemoved)2477 void lsquare::RemoveRain(rain* ToBeRemoved)
2478 {
2479 SendNewDrawRequest();
2480 rain* R = Rain;
2481
2482 if(ToBeRemoved->IsEnabled())
2483 DecAnimatedEntities();
2484
2485 if(R == ToBeRemoved)
2486 Rain = R->Next;
2487 else
2488 {
2489 rain* LR;
2490
2491 do
2492 {
2493 LR = R;
2494 R = R->Next;
2495 }
2496 while(R != ToBeRemoved);
2497
2498 LR->Next = R->Next;
2499 }
2500
2501 SignalEmitationDecrease(ToBeRemoved->GetEmitation());
2502 }
2503
AddRain(liquid * RainLiquid,v2 Speed,int Team,truth OwnLiquid)2504 void lsquare::AddRain(liquid* RainLiquid, v2 Speed, int Team, truth OwnLiquid)
2505 {
2506 rain* R = Rain, * NewRain = new rain(RainLiquid, this, Speed, Team, OwnLiquid);
2507
2508 if(NewRain->IsEnabled())
2509 IncAnimatedEntities();
2510
2511 if(!R)
2512 Rain = NewRain;
2513 else
2514 {
2515 rain* LR;
2516
2517 do
2518 {
2519 LR = R;
2520 R = R->Next;
2521 }
2522 while(R);
2523
2524 LR->Next = NewRain;
2525 }
2526 }
2527
RemoveSunLight()2528 void lsquare::RemoveSunLight()
2529 {
2530 SunLightLuminance = 0;
2531 SunEmitter.clear();
2532 }
2533
CheckIfIsSecondarySunLightEmitter()2534 void lsquare::CheckIfIsSecondarySunLightEmitter()
2535 {
2536 col24 OldEmitation = SecondarySunLightEmitation;
2537
2538 if(Flags & IS_TRANSPARENT && (!(Flags & INSIDE) || SunLightLuminance))
2539 for(int d = 0; d < 8; ++d)
2540 {
2541 lsquare* Neighbour = NeighbourLSquare[d];
2542
2543 if(Neighbour && Neighbour->Flags & INSIDE)
2544 {
2545 col24 NewEmitation = GetLevel()->GetAmbientLuminance();
2546
2547 if(OldEmitation != NewEmitation)
2548 {
2549 SecondarySunLightEmitation = NewEmitation;
2550
2551 if(game::CompareLights(NewEmitation, OldEmitation) >= 0)
2552 Emitate(NewEmitation, SECONDARY_SUN_LIGHT);
2553 else
2554 {
2555 Noxify(OldEmitation, SECONDARY_SUN_LIGHT);
2556 Emitate(NewEmitation, SECONDARY_SUN_LIGHT|FORCE_ADD);
2557 }
2558 }
2559
2560 return;
2561 }
2562 }
2563
2564 if(OldEmitation)
2565 {
2566 Noxify(OldEmitation, SECONDARY_SUN_LIGHT);
2567 SecondarySunLightEmitation = 0;
2568 }
2569 }
2570
CalculateNeighbourLSquares()2571 void lsquare::CalculateNeighbourLSquares()
2572 {
2573 int XSize = GetLevel()->GetXSize();
2574 int YSize = GetLevel()->GetYSize();
2575
2576 for(int d = 0; d < 8; ++d)
2577 {
2578 v2 NPos = Pos + game::GetMoveVector(d);
2579
2580 if(NPos.X >= 0 && NPos.Y >= 0 && NPos.X < XSize && NPos.Y < YSize)
2581 NeighbourLSquare[d] = GetLevel()->GetLSquare(NPos);
2582 else
2583 NeighbourLSquare[d] = 0;
2584 }
2585 }
2586
RemoveLuminance(col24 & Emitation)2587 void lsquare::RemoveLuminance(col24& Emitation)
2588 {
2589 col24 OldLuminance = Luminance;
2590 col24 OldEmitation = Emitation;
2591 Emitation = 0;
2592
2593 if(game::CompareLights(OldEmitation, OldLuminance) < 0)
2594 return;
2595
2596 if(!(Flags & IS_TRANSPARENT))
2597 {
2598 Flags |= NEW_DRAW_REQUEST;
2599
2600 if(LastSeen == game::GetLOSTick())
2601 game::SendLOSUpdateRequest();
2602 }
2603 else
2604 {
2605 CalculateLuminance();
2606
2607 if(OldLuminance == Luminance)
2608 return;
2609
2610 Flags |= NEW_DRAW_REQUEST;
2611
2612 if(!Luminance)
2613 {
2614 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
2615
2616 if(LastSeen == game::GetLOSTick())
2617 game::SendLOSUpdateRequest();
2618 }
2619 }
2620 }
2621
ChangeLuminance(col24 & Emitation,col24 NewLuminance)2622 void lsquare::ChangeLuminance(col24& Emitation, col24 NewLuminance)
2623 {
2624 col24 OldLuminance = Luminance;
2625
2626 if(!(Flags & IS_TRANSPARENT))
2627 {
2628 Emitation = NewLuminance;
2629 Flags |= NEW_DRAW_REQUEST;
2630
2631 if(LastSeen == game::GetLOSTick())
2632 game::SendLOSUpdateRequest();
2633
2634 return;
2635 }
2636
2637 truth EmitationInsignificant = !Emitation
2638 || game::CompareLights(Emitation, OldLuminance) < 0;
2639 Emitation = NewLuminance;
2640
2641 if(game::CompareLights(NewLuminance, OldLuminance) > 0
2642 && EmitationInsignificant)
2643 game::CombineLights(Luminance, NewLuminance);
2644 else
2645 {
2646 if(EmitationInsignificant)
2647 return;
2648
2649 CalculateLuminance();
2650
2651 if(OldLuminance == Luminance)
2652 return;
2653 }
2654
2655 Flags |= NEW_DRAW_REQUEST;
2656
2657 if(!OldLuminance)
2658 {
2659 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
2660
2661 if(LastSeen == game::GetLOSTick())
2662 game::SendLOSUpdateRequest();
2663 }
2664 }
2665
EnableGlobalRain()2666 void lsquare::EnableGlobalRain()
2667 {
2668 for(rain* R = Rain; R; R = R->Next)
2669 if(!R->HasOwnLiquid())
2670 {
2671 R->Enable();
2672 IncAnimatedEntities();
2673 }
2674 }
2675
DisableGlobalRain()2676 void lsquare::DisableGlobalRain()
2677 {
2678 SendNewDrawRequest();
2679
2680 for(rain* R = Rain; R; R = R->Next)
2681 if(!R->HasOwnLiquid())
2682 {
2683 R->Disable();
2684 DecAnimatedEntities();
2685 }
2686 }
2687
InitLastSeen()2688 void lsquare::InitLastSeen()
2689 {
2690 LastSeen = LastSeen == game::GetLOSTick() ? 2 : 0;
2691 SquarePartLastSeen = 0;
2692 }
2693
Engrave(cfestring & What)2694 truth lsquare::Engrave(cfestring& What)
2695 {
2696 if(Engraved)
2697 delete [] Engraved;
2698
2699 if(!What.IsEmpty())
2700 {
2701 Engraved = new char[What.GetSize() + 1];
2702 strcpy(Engraved, What.CStr());
2703 }
2704 else
2705 Engraved = 0;
2706
2707 return true;
2708 }
2709
SendSunLightSignals()2710 void lsquare::SendSunLightSignals()
2711 {
2712 if(Flags & IS_TRANSPARENT)
2713 {
2714 col24 OldLuminance = Luminance;
2715 CalculateLuminance();
2716
2717 if(Luminance != OldLuminance)
2718 {
2719 Flags |= NEW_DRAW_REQUEST;
2720
2721 if(!Luminance != !OldLuminance)
2722 {
2723 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
2724
2725 if(LastSeen == game::GetLOSTick())
2726 game::SendLOSUpdateRequest();
2727 }
2728 }
2729 }
2730 else
2731 {
2732 Flags |= NEW_DRAW_REQUEST;
2733
2734 if(LastSeen == game::GetLOSTick())
2735 game::SendLOSUpdateRequest();
2736 }
2737 }
2738
ZeroReSunEmitatedFlags()2739 void lsquare::ZeroReSunEmitatedFlags()
2740 {
2741 for(sunemittervector::value_type& SE : SunEmitter)
2742 SE &= ~RE_SUN_EMITATED;
2743 }
2744
CalculateIsTransparent()2745 truth lsquare::CalculateIsTransparent()
2746 {
2747 if((!OLTerrain || OLTerrain->IsTransparent()) && SmokeAlphaSum < 175
2748 && (!Character || Character->IsTransparent()))
2749 {
2750 Flags |= IS_TRANSPARENT;
2751 return true;
2752 }
2753 else
2754 {
2755 Flags &= ~IS_TRANSPARENT;
2756 return false;
2757 }
2758 }
2759
CalculateSunLightLuminance(ulong SeenBitMask)2760 void lsquare::CalculateSunLightLuminance(ulong SeenBitMask)
2761 {
2762 int S = 0, L = 0;
2763
2764 for(const sunemittervector::value_type& SE : SunEmitter)
2765 {
2766 ulong ShadowFlag = 1 << EMITTER_SHADOW_SHIFT;
2767 ulong SquarePartFlag = 1 << EMITTER_SQUARE_PART_SHIFT;
2768
2769 for(int c = 0; c < 4; ++c, ShadowFlag <<= 1, SquarePartFlag <<= 1)
2770 if(SeenBitMask & SE & SquarePartFlag)
2771 {
2772 if(SE & ShadowFlag)
2773 ++S;
2774 else
2775 ++L;
2776 }
2777 }
2778
2779 if(!L)
2780 SunLightLuminance = 0;
2781 else if(!S)
2782 SunLightLuminance = GetLevel()->GetSunLightEmitation();
2783 else
2784 {
2785 col24 ShadowColor = GetLevel()->GetAmbientLuminance();
2786 col24 LightColor = GetLevel()->GetSunLightEmitation();
2787 SunLightLuminance = MakeRGB24((GetRed24(LightColor) * L
2788 + GetRed24(ShadowColor) * S) / (S + L),
2789 (GetGreen24(LightColor) * L
2790 + GetGreen24(ShadowColor) * S) / (S + L),
2791 (GetBlue24(LightColor) * L
2792 + GetBlue24(ShadowColor) * S) / (S + L));
2793 }
2794 }
2795
CreateMemorized()2796 void lsquare::CreateMemorized()
2797 {
2798 Memorized = new bitmap(TILE_V2);
2799 Memorized->ActivateFastFlag();
2800 FowMemorized = new bitmap(TILE_V2);
2801 FowMemorized->ActivateFastFlag();
2802 }
2803
AcidRain(const beamdata & Beam)2804 truth lsquare::AcidRain(const beamdata& Beam)
2805 {
2806 character* Character = GetCharacter();
2807 if(Character)
2808 {
2809 if(Character->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
2810 {
2811 if(Character->DrinkMagic(Beam))
2812 return false;
2813 }
2814 }
2815 if(!IsFlyable() || Character || Beam.Direction == YOURSELF)
2816 {
2817 int StackSize = GetLevel()->AddRadiusToSquareStack(Pos, 9);
2818 lsquare** Stack = GetLevel()->GetSquareStack();
2819 v2 Speed = v2(512, 512);
2820 int Team = Beam.Owner ? Beam.Owner->GetTeam()->GetID() : MONSTER_TEAM;
2821
2822 for(int c = 0; c < StackSize; ++c)
2823 {
2824 Stack[c]->AddRain(liquid::Spawn(SULPHURIC_ACID, 300), Speed, Team, true);
2825 Character = Stack[c]->GetCharacter();
2826 Stack[c]->Flags &= ~IN_SQUARE_STACK;
2827
2828 if(Beam.Owner && Character && Character->GetTeam() != Beam.Owner->GetTeam())
2829 Beam.Owner->Hostility(Character);
2830 }
2831
2832 return true;
2833 }
2834
2835 return false;
2836 }
2837
LiquidRain(const beamdata & Beam,int Material)2838 truth lsquare::LiquidRain(const beamdata& Beam, int Material)
2839 {
2840 character* Character = GetCharacter();
2841 if(Character)
2842 {
2843 if(Character->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
2844 {
2845 if(Character->DrinkMagic(Beam))
2846 return false;
2847 }
2848 }
2849 if(!IsFlyable() || Character || Beam.Direction == YOURSELF)
2850 {
2851 int StackSize = GetLevel()->AddRadiusToSquareStack(Pos, 9);
2852 lsquare** Stack = GetLevel()->GetSquareStack();
2853 v2 Speed = v2(512, 512);
2854 int Team = Beam.Owner ? Beam.Owner->GetTeam()->GetID() : MONSTER_TEAM;
2855
2856 for(int c = 0; c < StackSize; ++c)
2857 {
2858 Stack[c]->AddRain(liquid::Spawn(Material, 1000), Speed, Team, true);
2859 Stack[c]->Flags &= ~IN_SQUARE_STACK;
2860 }
2861
2862 return true;
2863 }
2864
2865 return false;
2866 }
2867
DetectMaterial(cmaterial * Material) const2868 truth lsquare::DetectMaterial(cmaterial* Material) const
2869 {
2870 if(GLTerrain->DetectMaterial(Material)
2871 || (OLTerrain && OLTerrain->DetectMaterial(Material))
2872 || Stack->DetectMaterial(Material)
2873 || (Character && Character->DetectMaterial(Material)))
2874 return true;
2875
2876 for(const fluid* F = Fluid; F; F = F->Next)
2877 if(F->GetLiquid()->IsSameAs(Material))
2878 return true;
2879
2880 for(const smoke* S = Smoke; S; S = S->Next)
2881 if(S->GetGas()->IsSameAs(Material))
2882 return true;
2883
2884 for(const rain* R = Rain; R; R = R->Next)
2885 if(R->GetLiquid()->IsSameAs(Material))
2886 return true;
2887
2888 return false;
2889 }
2890
Reveal(ulong Tick,truth IgnoreDarkness)2891 void lsquare::Reveal(ulong Tick, truth IgnoreDarkness)
2892 {
2893 if(!Memorized)
2894 CreateMemorized();
2895
2896 LastSeen = Tick;
2897
2898 if(IgnoreDarkness)
2899 Luminance = NORMAL_LUMINANCE;
2900 else
2901 {
2902 SquarePartLastSeen = 0;
2903
2904 for(int c = 0; c < 4; ++c)
2905 SquarePartLastSeen |= (Tick << (c << 3));
2906
2907 CalculateLuminance();
2908 }
2909
2910 Flags |= NEW_DRAW_REQUEST
2911 | MEMORIZED_UPDATE_REQUEST
2912 | DESCRIPTION_CHANGE;
2913 UpdateMemorized();
2914 UpdateMemorizedDescription();
2915 }
2916
DestroyMemorized()2917 void lsquare::DestroyMemorized()
2918 {
2919 delete Memorized;
2920 delete FowMemorized;
2921 Memorized = 0;
2922 FowMemorized = 0;
2923 }
2924
SwapMemorized(lsquare * Square)2925 void lsquare::SwapMemorized(lsquare* Square)
2926 {
2927 Swap(Memorized, Square->Memorized);
2928 Swap(FowMemorized, Square->FowMemorized);
2929 MemorizedDescription.SwapData(Square->MemorizedDescription);
2930 }
2931
Necromancy(const beamdata & Beam)2932 truth lsquare::Necromancy(const beamdata& Beam)
2933 {
2934 character* Character = GetCharacter();
2935 if(Character)
2936 {
2937 if(Character->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
2938 {
2939 if(Character->DrinkMagic(Beam))
2940 return false;
2941 }
2942 else if(Character->IsUndead())
2943 {
2944 if(Character->IsPlayer())
2945 ADD_MESSAGE("Your dead flesh knits itself.");
2946 else if(Character->CanBeSeenByPlayer())
2947 ADD_MESSAGE("%s looks deader than dead.", Character->CHAR_NAME(DEFINITE));
2948
2949 Character->RestoreLivingHP();
2950 }
2951 }
2952
2953 return GetStack()->Necromancy(Beam.Owner);
2954 }
2955
2956 // Returns 0 if fails
2957
GetRandomAdjacentSquare() const2958 lsquare* lsquare::GetRandomAdjacentSquare() const
2959 {
2960 lsquare* OK[8];
2961 int Index = 0;
2962
2963 for(int c = 0; c < 8; ++c)
2964 {
2965 lsquare* Square = NeighbourLSquare[c];
2966
2967 if(Square)
2968 OK[Index++] = Square;
2969 }
2970
2971 if(Index)
2972 return OK[RAND_N(Index)];
2973 else
2974 return 0;
2975 }
2976
Handler(int x,int y)2977 truth pathcontroller::Handler(int x, int y)
2978 {
2979 return Character->CanMoveOn(Map[x][y]);
2980 }
2981
SignalPossibleTransparencyChange()2982 void lsquare::SignalPossibleTransparencyChange()
2983 {
2984 truth WasTransparent = IsTransparent();
2985 CalculateIsTransparent();
2986
2987 if(WasTransparent && !IsTransparent())
2988 {
2989 Flags |= IS_TRANSPARENT;
2990 emittervector EmitterBackup = Emitter;
2991 GetLevel()->ForceEmitterNoxify(EmitterBackup);
2992 Flags &= ~IS_TRANSPARENT;
2993 GetLevel()->ForceEmitterEmitation(EmitterBackup, SunEmitter, FORCE_ADD);
2994 CalculateLuminance();
2995 Flags |= DESCRIPTION_CHANGE|MEMORIZED_UPDATE_REQUEST;
2996
2997 if(LastSeen == game::GetLOSTick())
2998 game::SendLOSUpdateRequest();
2999 }
3000 else if(!WasTransparent && IsTransparent())
3001 {
3002 GetLevel()->ForceEmitterEmitation(Emitter, SunEmitter);
3003 CalculateLuminance();
3004 Flags |= DESCRIPTION_CHANGE|MEMORIZED_UPDATE_REQUEST;
3005
3006 if(LastSeen == game::GetLOSTick())
3007 game::SendLOSUpdateRequest();
3008 }
3009 }
3010
RemoveTrap(trap * ToRemove)3011 void lsquare::RemoveTrap(trap* ToRemove)
3012 {
3013 trap*& T = ListFind(Trap, pointercomparer<trap>(ToRemove));
3014 T = T->Next;
3015 SendNewDrawRequest();
3016 SendMemorizedUpdateRequest();
3017 }
3018
3019 struct trapcomparer
3020 {
trapcomparertrapcomparer3021 trapcomparer(int Type) : Type(Type) { }
operator ()trapcomparer3022 truth operator()(const trap* T) const { return T->GetType() == Type; }
3023 int Type;
3024 };
3025
AddTrap(trap * ToBeAdded)3026 truth lsquare::AddTrap(trap* ToBeAdded)
3027 {
3028 trap*& T = ListFind(Trap, trapcomparer(ToBeAdded->GetType()));
3029
3030 if(T)
3031 {
3032 delete ToBeAdded;
3033 return false;
3034 }
3035 else
3036 T = ToBeAdded;
3037
3038 ToBeAdded->SetLSquareUnder(this);
3039 SendNewDrawRequest();
3040 SendMemorizedUpdateRequest();
3041 return true;
3042 }
3043
DisplayTrapInfo(festring & Msg) const3044 void lsquare::DisplayTrapInfo(festring& Msg) const
3045 {
3046 for(const trap* T = Trap; T; T = T->Next)
3047 T->AddDescription(Msg);
3048 }
3049
FillTrapVector(std::vector<trap * > & TrapVector) const3050 void lsquare::FillTrapVector(std::vector<trap*>& TrapVector) const
3051 {
3052 for(trap* T = Trap; T; T = T->Next)
3053 TrapVector.push_back(T);
3054 }
3055
ReceiveTrapDamage(character * Damager,int Damage,int Type,int Direction)3056 void lsquare::ReceiveTrapDamage(character* Damager, int Damage, int Type, int Direction)
3057 {
3058 std::vector<trap*> TrapVector;
3059 FillTrapVector(TrapVector);
3060
3061 for(uint c = 0; c < TrapVector.size(); ++c)
3062 TrapVector[c]->ReceiveDamage(Damager, Damage, Type, Direction);
3063 }
3064
HasDangerousTraps(ccharacter * Who) const3065 truth lsquare::HasDangerousTraps(ccharacter* Who) const
3066 {
3067 for(trap* T = Trap; T; T = T->Next)
3068 if(T->IsDangerous(Who))
3069 return true;
3070
3071 return false;
3072 }
3073
HasDangerousFluids(ccharacter * Who) const3074 truth lsquare::HasDangerousFluids(ccharacter* Who) const
3075 {
3076 for(const fluid* F = Fluid; F; F = F->Next)
3077 if(F->IsDangerous(Who))
3078 return true;
3079
3080 return false;
3081 }
3082
HasNoBorderPartners() const3083 truth lsquare::HasNoBorderPartners() const
3084 {
3085 return !(GroundBorderPartnerInfo >> 24) && !(OverBorderPartnerInfo >> 24);
3086 }
3087
AddLocationDescription(festring & String) const3088 void lsquare::AddLocationDescription(festring& String) const
3089 {
3090 if(IsFlyable())
3091 GLTerrain->AddLocationDescription(String);
3092 else
3093 OLTerrain->AddLocationDescription(String);
3094 }
3095
VomitingIsDangerous(ccharacter * Char) const3096 truth lsquare::VomitingIsDangerous(ccharacter* Char) const
3097 {
3098 return ((OLTerrain && OLTerrain->VomitingIsDangerous(Char))
3099 || (Character && Character->GetTeam() != Char->GetTeam()
3100 && Character->GetRelation(Char) != HOSTILE));
3101 }
3102
TeleportAllSmokeAway()3103 bool lsquare::TeleportAllSmokeAway()
3104 {
3105 return false;
3106 }
3107
TeleportAllFluidsAway()3108 bool lsquare::TeleportAllFluidsAway()
3109 {
3110 return false;
3111 }
3112
TeleportAllTrapsAway()3113 bool lsquare::TeleportAllTrapsAway()
3114 {
3115 for(trap* T = Trap; T; T = Trap)
3116 {
3117 T->Untrap();
3118 RemoveTrap(T);
3119 v2 V, Pos = GetPos();
3120 for(V = GetLevel()->GetRandomSquare(); V != Pos; V = GetLevel()->GetRandomSquare());
3121 GetNearLSquare(V)->AddTrap(T);
3122 }
3123
3124 return false;
3125 }
3126
AddSpecialCursors()3127 void lsquare::AddSpecialCursors()
3128 {
3129 if((FowMemorized || game::GetSeeWholeMapCheatMode()) && OLTerrain)
3130 OLTerrain->AddSpecialCursors();
3131 }
3132
Webbing(const beamdata & Beam)3133 truth lsquare::Webbing(const beamdata& Beam)
3134 {
3135 character* Character = GetCharacter();
3136 if(Character)
3137 {
3138 if(Character->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
3139 {
3140 if(Character->DrinkMagic(Beam))
3141 return false;
3142 }
3143 if(Beam.Owner && Character->GetTeam() != Beam.Owner->GetTeam())
3144 Beam.Owner->Hostility(Character);
3145 }
3146
3147 if(!IsFlyable())
3148 return false;
3149
3150 if(Beam.Owner && GetRoom())
3151 GetRoom()->HostileAction(Beam.Owner);
3152
3153 web* Web = web::Spawn();
3154 Web->SetStrength(50);
3155
3156 AddTrap(Web);
3157
3158 return false;
3159 }
3160
Alchemize(const beamdata & Beam)3161 truth lsquare::Alchemize(const beamdata& Beam)
3162 {
3163 character* Character = GetCharacter();
3164 if(Character)
3165 {
3166 if(Character->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
3167 {
3168 if(Character->DrinkMagic(Beam))
3169 return false;
3170 }
3171 }
3172 if(!IsFlyable())
3173 return false;
3174
3175 GetStack()->Alchemize(Beam.Owner);
3176 return false;
3177 }
3178
SoftenMaterial(const beamdata & Beam)3179 truth lsquare::SoftenMaterial(const beamdata& Beam)
3180 {
3181 GetStack()->SoftenMaterial(Beam.Owner);
3182
3183 /*if(GetOLTerrain())
3184 GetOLTerrain()->SoftenMaterial(Beam.Owner);*/
3185
3186 character* Character = GetCharacter();
3187
3188 if(Character)
3189 {
3190 if(Character->IsMagicDrinker() && Beam.Wand && Beam.Wand->IsExplosive())
3191 {
3192 if(Character->DrinkMagic(Beam))
3193 return false;
3194 }
3195 if(Beam.Owner && Character->GetTeam() != Beam.Owner->GetTeam())
3196 Beam.Owner->Hostility(Character);
3197
3198 itemvector AllItems;
3199 sortdata SortData(AllItems, Beam.Owner, true, &item::MaterialIsChangeable);
3200 SortAllItems(SortData);
3201 item* RandomItem;
3202
3203 if(!AllItems.empty())
3204 {
3205 RandomItem = AllItems[RAND() % AllItems.size()];
3206 RandomItem->SoftenMaterial();
3207 }
3208
3209 // Kill the golems!!!
3210 for(uint c = 1; c < uint(Character->GetBodyParts()); ++c)
3211 {
3212 bodypart* BodyPart = Character->GetBodyPart(c);
3213
3214 if(BodyPart)
3215 {
3216 BodyPart->SoftenMaterial();
3217 }
3218 }
3219 }
3220
3221 return false;
3222 }
3223