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