save and bone files now can be compressed with ZLib (wow!)
[k8-i-v-a-n.git] / src / game / lsquare.cpp
blob84a2bc2a9db90fed4ecb82e1a54fb6f33b07321e
1 /*
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
8 * See LICENSING which should be included
9 * along with this file for more details
13 /* Compiled through levelset.cpp */
15 lsquare*** eyecontroller::Map;
17 lsquare*** pathcontroller::Map;
18 ccharacter* pathcontroller::Character;
20 lsquare*** stackcontroller::Map;
21 lsquare** stackcontroller::Stack;
22 sLong stackcontroller::StackIndex;
23 int stackcontroller::LevelXSize, stackcontroller::LevelYSize;
24 v2 stackcontroller::Center;
26 feuLong tickcontroller::Tick;
27 feuLong tickcontroller::ShiftedTick[4];
28 feuLong tickcontroller::ShiftedQuadriTick[4];
30 void tickcontroller::PrepareShiftedTick()
32 for(int c = 0; c < 4; ++c)
34 ShiftedTick[c] = Tick << (c << 3);
35 ShiftedQuadriTick[c] = (Tick + 1) << (c << 3);
39 truth lsquare::IsDipDestination() const { return GLTerrain->IsDipDestination() || (OLTerrain && OLTerrain->IsDipDestination()); }
41 lsquare::lsquare(level* LevelUnder, v2 Pos)
42 : square(LevelUnder, Pos),
43 Fluid(0), Smoke(0), Rain(0), Trap(0),
44 GLTerrain(0), OLTerrain(0),
45 Memorized(0), FowMemorized(0),
46 Engraved(0),
47 GroundBorderPartnerTerrain(0),
48 GroundBorderPartnerInfo(0),
49 OverBorderPartnerTerrain(0),
50 OverBorderPartnerInfo(0),
51 SquarePartEmitationTick(0),
52 SquarePartLastSeen(0),
53 Emitation(0),
54 SmokeAlphaSum(0),
55 AmbientLuminance(0),
56 SunLightLuminance(0),
57 TemporaryEmitation(0),
58 SecondarySunLightEmitation(0),
59 LastExplosionID(0),
60 RoomIndex(0)
62 Stack = new stack(this, 0);
65 lsquare::~lsquare()
67 delete GLTerrain;
68 delete OLTerrain;
69 delete Stack;
70 delete [] Engraved;
72 for(fluid* F = Fluid; F;)
74 fluid* ToDel = F;
75 F = F->Next;
76 delete ToDel;
79 delete Memorized;
80 delete FowMemorized;
81 delete StaticContentCache.Bitmap;
82 delete [] GroundBorderPartnerTerrain;
83 delete [] OverBorderPartnerTerrain;
85 for(smoke* S = Smoke; S;)
87 smoke* ToDel = S;
88 S = S->Next;
89 delete ToDel;
92 for(rain* R = Rain; R;)
94 rain* ToDel = R;
95 R = R->Next;
96 delete ToDel;
99 for(trap* T = Trap; T;)
101 trap* ToDel = T;
102 T = T->Next;
103 delete ToDel;
107 void lsquare::SignalEmitationIncrease(col24 EmitationUpdate)
109 if(game::CompareLights(EmitationUpdate, Emitation) > 0 && !game::IsGenerating() && !(Flags & FREEZED))
111 CalculateEmitation(); // could this be optimized?
112 Emitate();
116 void lsquare::SignalEmitationDecrease(col24 EmitationUpdate)
118 if(game::CompareLights(EmitationUpdate, Emitation) >= 0 && Emitation && !game::IsGenerating() && !(Flags & FREEZED))
120 col24 Backup = Emitation;
121 CalculateEmitation();
123 if(Backup != Emitation)
125 Noxify(Backup);
126 Emitate(Emitation, FORCE_ADD);
131 void lsquare::CalculateEmitation()
133 Emitation = Stack->GetEmitation();
134 int c;
136 for(c = 0; c < 4; ++c)
138 stack* Stack = GetStackOfAdjacentSquare(c);
140 if(Stack)
141 game::CombineLights(Emitation, Stack->GetSideEmitation(3 - c));
144 if(Character)
145 game::CombineLights(Emitation, Character->GetEmitation());
147 game::CombineLights(Emitation, GLTerrain->GetEmitation());
149 if(OLTerrain)
150 game::CombineLights(Emitation, OLTerrain->GetEmitation());
152 game::CombineLights(Emitation, TemporaryEmitation);
154 for(const fluid* F = Fluid; F; F = F->Next)
155 game::CombineLights(Emitation, F->GetEmitation());
157 for(const rain* R = Rain; R; R = R->Next)
158 game::CombineLights(Emitation, R->GetEmitation());
161 void lsquare::UpdateMemorized()
163 if(Flags & MEMORIZED_UPDATE_REQUEST)
165 if(!IsDark() || CanBeFeltByPlayer())
167 blitdata B = { Memorized,
168 { 0, 0 },
169 { 0, 0 },
170 { TILE_SIZE, TILE_SIZE },
171 { NORMAL_LUMINANCE },
172 TRANSPARENT_COLOR,
173 ALLOW_ALPHA };
175 DrawStaticContents(B);
176 Memorized->FastBlit(FowMemorized);
177 B.Bitmap = FowMemorized;
178 B.Flags = 0;
179 B.MaskColor = 0;
180 igraph::GetFOWGraphic()->NormalMaskedBlit(B);
182 else
184 Memorized->ClearToColor(0);
185 igraph::GetFOWGraphic()->FastBlit(FowMemorized);
188 if(!StaticContentCache.Bitmap)
190 StaticContentCache.Bitmap = new bitmap(TILE_V2);
191 StaticContentCache.Bitmap->ActivateFastFlag();
194 UpdateStaticContentCache(Luminance);
195 Flags &= ~MEMORIZED_UPDATE_REQUEST;
199 void lsquare::UpdateStaticContentCache(col24 Luminance) const
201 blitdata B = { StaticContentCache.Bitmap,
202 { 0, 0 },
203 { 0, 0 },
204 { TILE_SIZE, TILE_SIZE },
205 { Luminance },
207 0 };
209 Memorized->LuminanceBlit(B);
210 StaticContentCache.Luminance = Luminance;
213 void lsquare::DrawStaticContents(blitdata& BlitData) const
215 if(BlitData.CustomData & ALLOW_ANIMATE && !StaticAnimatedEntities && Memorized && !game::GetSeeWholeMapCheatMode())
217 if(StaticContentCache.Luminance != BlitData.Luminance)
218 UpdateStaticContentCache(BlitData.Luminance);
220 StaticContentCache.Bitmap->FastBlit(BlitData.Bitmap, BlitData.Dest);
221 return;
224 if(!OLTerrain || OLTerrain->ShowThingsUnder())
225 GLTerrain->Draw(BlitData);
227 int c;
228 int GroundPartners = GroundBorderPartnerInfo >> 24 & 15;
230 for(c = 0; c < GroundPartners; ++c)
232 BlitData.CustomData |= 8 - (GroundBorderPartnerInfo >> ((c << 1) + c) & 7);
233 GroundBorderPartnerTerrain[c]->Draw(BlitData);
234 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
237 truth StackDrawn = false;
239 if(OLTerrain && !IsFlyable())
241 if(OLTerrain->IsTransparent() && OLTerrain->ShowThingsUnder())
243 StackDrawn = true;
244 DrawStacks(BlitData);
247 OLTerrain->Draw(BlitData);
250 for(const fluid* F = Fluid; F; F = F->Next)
251 F->SimpleDraw(BlitData);
253 if(OLTerrain && IsFlyable())
254 OLTerrain->Draw(BlitData);
256 if(!StackDrawn && Flags & IS_TRANSPARENT)
257 DrawStacks(BlitData);
259 for(const trap* T = Trap; T; T = T->Next)
260 T->Draw(BlitData);
262 int OverPartners = OverBorderPartnerInfo >> 24 & 15;
264 for(c = 0; c < OverPartners; ++c)
266 BlitData.CustomData |= 8 - (OverBorderPartnerInfo >> ((c << 1) + c) & 7);
267 OverBorderPartnerTerrain[c]->Draw(BlitData);
268 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
272 void lsquare::Draw(blitdata& BlitData) const
274 if(Flags & NEW_DRAW_REQUEST || AnimatedEntities)
276 if(!IsDark() || game::GetSeeWholeMapCheatMode())
278 if(game::GetSeeWholeMapCheatMode() == SHOW_MAP_IN_UNIFORM_LIGHT
279 || (game::GetSeeWholeMapCheatMode()
280 && !(Flags & IS_TRANSPARENT)))
281 BlitData.Luminance = ivanconfig::GetContrastLuminance();
282 else
283 BlitData.Luminance = ivanconfig::ApplyContrastTo(Luminance);
285 DrawStaticContents(BlitData);
287 if(Character && (Character->CanBeSeenByPlayer() || game::GetSeeWholeMapCheatMode()))
289 BlitData.CustomData |= Character->GetSquareIndex(Pos);
291 if(Character->IsFlying())
293 for(const smoke* S = Smoke; S; S = S->Next)
294 S->Draw(BlitData);
296 Character->Draw(BlitData);
298 else
300 Character->Draw(BlitData);
302 for(const smoke* S = Smoke; S; S = S->Next)
303 S->Draw(BlitData);
306 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
308 else
309 for(const smoke* S = Smoke; S; S = S->Next)
310 S->Draw(BlitData);
312 for(const rain* R = Rain; R; R = R->Next)
313 if(R->IsEnabled())
314 R->Draw(BlitData);
316 else if(CanBeFeltByPlayer())
318 col24 L = Luminance;
319 game::CombineLights(L, DIM_LUMINANCE);
320 BlitData.Luminance = ivanconfig::ApplyContrastTo(L);
321 DrawStaticContents(BlitData);
323 for(const rain* R = Rain; R; R = R->Next)
324 if(R->IsEnabled())
325 R->Draw(BlitData);
327 else
329 DOUBLE_BUFFER->Fill(BlitData.Dest, BlitData.Border, 0);
331 if(Character && Character->CanBeSeenByPlayer())
333 BlitData.CustomData |= Character->GetSquareIndex(Pos);
334 BlitData.Luminance = ivanconfig::GetContrastLuminance();
335 Character->Draw(BlitData);
336 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
340 Flags &= ~STRONG_NEW_DRAW_REQUEST;
344 struct emitationcontroller : public tickcontroller, public stackcontroller
346 static truth Handler(int x, int y)
348 lsquare* Square = Map[x >> 1][y >> 1];
349 culong SquareFlags = Square->Flags;
351 if(SquareFlags & PERFECTLY_QUADRI_HANDLED)
352 return SquareFlags & ALLOW_EMITATION_CONTINUE;
354 if(SquareFlags & IS_TRANSPARENT)
355 return ProcessSquare(x >> 1, y >> 1, Square);
357 if(!(SquareFlags & IN_SQUARE_STACK))
359 Square->Flags |= IN_SQUARE_STACK;
360 Stack[StackIndex++] = Square;
363 cint SquarePartIndex = (x & 1) + ((y & 1) << 1);
364 Square->SquarePartEmitationTick =
365 (Square->SquarePartEmitationTick & ~SquarePartTickMask[SquarePartIndex]) | ShiftedTick[SquarePartIndex];
367 return false;
369 static int ProcessSquare(int X, int Y, lsquare* Square)
371 Stack[StackIndex++] = Square;
372 culong SquareFlags = Square->Flags;
373 cint MaxE = MaxLuxTable[X - EmitterPosXMinus16][Y - EmitterPosYMinus16];
375 if(MaxE >= LIGHT_BORDER
376 && (SquareFlags & INSIDE
377 || (!(ID & SECONDARY_SUN_LIGHT)
378 && MaxE > MinNightAmbientLuminanceElement)))
380 Square->Flags |= ALLOW_EMITATION_CONTINUE | PERFECTLY_QUADRI_HANDLED;
381 return true;
383 else
385 Square->Flags = (SquareFlags & ~ALLOW_EMITATION_CONTINUE) | PERFECTLY_QUADRI_HANDLED;
386 return false;
389 static feuLong& GetTickReference(int X, int Y)
391 return Map[X][Y]->SquarePartEmitationTick;
393 static void ProcessStack()
395 for(sLong c1 = 0; c1 < StackIndex; ++c1)
397 lsquare* Square = Stack[c1];
398 culong SquareTick = Square->SquarePartEmitationTick;
399 feuLong TempID = ID;
401 for(int c2 = 0; c2 < 4; ++c2)
402 if((SquareTick & SquarePartTickMask[c2]) == ShiftedTick[c2])
403 TempID |= 1 << EMITTER_SQUARE_PART_SHIFT << c2;
405 Square->Flags &= ~(IN_SQUARE_STACK|PERFECTLY_QUADRI_HANDLED);
406 v2 Pos = Square->Pos;
407 int XVal = Pos.X - EmitterPosXMinus16;
408 int YVal = Pos.Y - EmitterPosYMinus16;
410 if(MaxLuxTable[XVal][YVal] >= LIGHT_BORDER)
411 Square->AlterLuminance(TempID, MakeRGB24(RedLuxTable[XVal][YVal],
412 GreenLuxTable[XVal][YVal],
413 BlueLuxTable[XVal][YVal]));
416 static feuLong ID;
417 static int MinNightAmbientLuminanceElement;
418 static int EmitterPosXMinus16;
419 static int EmitterPosYMinus16;
420 static uChar** MaxLuxTable;
421 static uChar** RedLuxTable;
422 static uChar** GreenLuxTable;
423 static uChar** BlueLuxTable;
426 feuLong emitationcontroller::ID;
427 int emitationcontroller::MinNightAmbientLuminanceElement;
428 int emitationcontroller::EmitterPosXMinus16;
429 int emitationcontroller::EmitterPosYMinus16;
430 uChar** emitationcontroller::MaxLuxTable;
431 uChar** emitationcontroller::RedLuxTable;
432 uChar** emitationcontroller::GreenLuxTable;
433 uChar** emitationcontroller::BlueLuxTable;
435 void lsquare::Emitate(col24 Emitation, feuLong IDFlags)
437 if(game::IsDark(Emitation))
438 return;
440 int Radius = game::CalculateMinimumEmitationRadius(Emitation);
442 if(!Radius)
443 return;
445 stackcontroller::Map = GetLevel()->GetMap();
446 stackcontroller::Stack = GetLevel()->GetSquareStack();
447 stackcontroller::StackIndex = 0;
448 tickcontroller::Tick = game::IncreaseSquarePartEmitationTicks();
449 tickcontroller::PrepareShiftedTick();
450 emitationcontroller::ID = MakeEmitterID(Pos) | IDFlags;
451 emitationcontroller::MinNightAmbientLuminanceElement = GetMinColor24(GetLevel()->GetNightAmbientLuminance());
452 emitationcontroller::EmitterPosXMinus16 = Pos.X - 16;
453 emitationcontroller::EmitterPosYMinus16 = Pos.Y - 16;
454 emitationcontroller::MaxLuxTable = game::GetLuxTable()[GetMaxColor24(Emitation)];
455 emitationcontroller::RedLuxTable = game::GetLuxTable()[GetRed24(Emitation)];
456 emitationcontroller::GreenLuxTable = game::GetLuxTable()[GetGreen24(Emitation)];
457 emitationcontroller::BlueLuxTable = game::GetLuxTable()[GetBlue24(Emitation)];
458 mapmath<emitationcontroller>::DoQuadriArea(Pos.X, Pos.Y,
459 Radius * Radius,
460 GetLevel()->GetXSize(),
461 GetLevel()->GetYSize());
462 emitationcontroller::ProcessStack();
465 struct noxifycontroller : public stackcontroller
467 static truth Handler(int x, int y)
469 if(x >= 0 && y >= 0 && x < LevelXSize && y < LevelYSize)
471 lsquare* Square = Map[x][y];
473 if(Square->SquarePartEmitationTick != Tick)
475 Square->SquarePartEmitationTick = Tick;
476 return Square->NoxifyEmitter(ID);
480 return false;
482 static int GetStartX(int) { return Center.X; }
483 static int GetStartY(int) { return Center.Y; }
484 static feuLong ID;
485 static feuLong Tick;
488 feuLong noxifycontroller::ID;
489 feuLong noxifycontroller::Tick;
491 void lsquare::Noxify(col24 Emitation, feuLong IDFlags)
493 if(game::IsDark(Emitation))
494 return;
496 int Radius = game::CalculateMinimumEmitationRadius(Emitation);
498 if(!Radius)
499 return;
501 stackcontroller::Map = GetLevel()->GetMap();
502 stackcontroller::LevelXSize = GetLevel()->GetXSize();
503 stackcontroller::LevelYSize = GetLevel()->GetYSize();
504 stackcontroller::Center = Pos;
505 noxifycontroller::ID = MakeEmitterID(Pos) | IDFlags;
506 noxifycontroller::Tick = game::IncreaseSquarePartEmitationTicks();
507 NoxifyEmitter(noxifycontroller::ID);
508 mapmath<noxifycontroller>::DoArea();
511 truth lsquare::NoxifyEmitter(feuLong ID)
513 emittervector::iterator i, End = Emitter.end();
515 for(i = Emitter.begin(); i != End; ++i)
516 if(!((i->ID ^ ID) & (EMITTER_IDENTIFIER_BITS|SECONDARY_SUN_LIGHT)))
518 RemoveLuminance(i->Emitation);
519 Swap(*i, Emitter.back());
520 Emitter.pop_back();
521 return true;
524 return false;
527 void lsquare::AlterLuminance(feuLong ID, col24 NewLuminance)
529 emittervector::iterator i, End = Emitter.end();
531 if(!(ID & FORCE_ADD))
532 for(i = Emitter.begin(); i != End; ++i)
533 if(!((i->ID ^ ID) & (EMITTER_IDENTIFIER_BITS|SECONDARY_SUN_LIGHT)))
535 i->ID |= ID;
537 if(i->Emitation != NewLuminance)
538 ChangeLuminance(i->Emitation, NewLuminance);
540 return;
543 Emitter.push_back(emitter(ID, 0));
544 ChangeLuminance(Emitter.back().Emitation, NewLuminance);
547 void lsquare::AddSunLightEmitter(feuLong ID)
549 sunemittervector::iterator i, End = SunEmitter.end();
551 for(i = SunEmitter.begin(); i != End; ++i)
552 if(!((*i ^ ID) & EMITTER_IDENTIFIER_BITS))
554 if(ID & ~*i & RE_SUN_EMITATED)
555 *i &= ~EMITTER_SHADOW_BITS;
557 *i |= ID;
558 Swap(*i, SunEmitter.front());
559 return;
562 SunEmitter.push_back(ID);
565 truth lsquare::Open(character* Opener)
567 return GetStack()->Open(Opener) || (OLTerrain && OLTerrain->Open(Opener));
570 truth lsquare::Close(character* Closer)
572 if(!GetStack()->GetItems() && !Character)
573 return OLTerrain && OLTerrain->Close(Closer);
574 else
576 ADD_MESSAGE("There's something in the way...");
577 return false;
581 void lsquare::Save(outputfile& SaveFile) const
583 Stack->Save(SaveFile); // This must be before square::Save! (Note: This comment is years old. It's probably obsolete)
584 square::Save(SaveFile);
585 SaveFile << GLTerrain << OLTerrain;
586 SaveFile << Emitter << SunEmitter;
587 SaveFile << Emitation << Engraved << Luminance;
588 SaveFile << SmokeAlphaSum << (uChar)Flags << Memorized;
589 SaveFile << SecondarySunLightEmitation;
590 SaveFile << (uChar)RoomIndex;
591 SaveFile << SunLightLuminance;
592 SaveLinkedList(SaveFile, Fluid);
593 SaveLinkedList(SaveFile, Smoke);
594 SaveLinkedList(SaveFile, Rain);
595 SaveLinkedList(SaveFile, Trap);
598 void lsquare::Load(inputfile& SaveFile)
600 Stack->Load(SaveFile); // This must be before square::Load! (Note: This comment is years old. It's probably obsolete)
601 Stack->SetMotherSquare(this);
602 square::Load(SaveFile);
603 SaveFile >> GLTerrain >> OLTerrain;
604 SaveFile >> Emitter >> SunEmitter;
605 SaveFile >> Emitation >> Engraved >> Luminance;
606 SaveFile >> SmokeAlphaSum >> (uChar&)Flags >> Memorized;
607 Flags &= INSIDE|DESCRIPTION_CHANGE; //only these flags are loaded
608 Flags |= MEMORIZED_UPDATE_REQUEST;
609 SecondarySunLightEmitation = ReadType<col24>(SaveFile);
610 RoomIndex = ReadType<uChar>(SaveFile);
611 SunLightLuminance = ReadType<col24>(SaveFile);
612 LoadLinkedList(SaveFile, Fluid);
613 LoadLinkedList(SaveFile, Smoke);
614 LoadLinkedList(SaveFile, Rain);
615 LoadLinkedList(SaveFile, Trap);
616 CalculateIsTransparent();
618 if(Memorized)
620 FowMemorized = new bitmap(TILE_V2);
621 FowMemorized->ActivateFastFlag();
622 Memorized->FastBlit(FowMemorized);
623 blitdata B = { FowMemorized,
624 { 0, 0 },
625 { 0, 0 },
626 { TILE_SIZE, TILE_SIZE },
627 { 0 },
629 0 };
631 igraph::GetFOWGraphic()->NormalMaskedBlit(B);
635 void lsquare::CalculateLuminance()
637 Luminance = AmbientLuminance;
638 emittervector::const_iterator i, End = Emitter.end();
640 if(Flags & IS_TRANSPARENT)
642 game::CombineLights(Luminance, SunLightLuminance);
644 for(i = Emitter.begin(); i != End; ++i)
645 game::CombineLights(Luminance, i->Emitation);
647 else
649 feuLong BitMask = 0, LOSTick = game::GetLOSTick();
651 for(int c = 0; c < 4; ++c)
652 if((SquarePartLastSeen >> (c << 3) & 0xFF) >= LOSTick)
653 BitMask |= 1 << EMITTER_SQUARE_PART_SHIFT << c;
655 CalculateSunLightLuminance(BitMask);
656 game::CombineLights(Luminance, SunLightLuminance);
658 for(i = Emitter.begin(); i != End; ++i)
659 if(BitMask & i->ID)
660 game::CombineLights(Luminance, i->Emitation);
664 void lsquare::AddCharacter(character* Guy)
666 if(Character)
667 ABORT("Overgrowth of square population detected!");
669 Character = Guy;
670 SignalEmitationIncrease(Guy->GetEmitation());
671 Flags |= STRONG_NEW_DRAW_REQUEST;
673 if(Guy->IsAnimated())
674 IncAnimatedEntities();
676 SignalPossibleTransparencyChange();
678 if(Guy->IsPlayer()
679 || (Guy->CanBeSeenByPlayer(true) && CanBeSeenByPlayer()))
680 Guy->SignalSeen();
683 void lsquare::Clean()
685 GetStack()->Clean();
688 void lsquare::RemoveCharacter()
690 if(Character)
692 character* Backup = Character;
694 if(Backup->IsAnimated())
695 DecAnimatedEntities();
697 Character = 0;
698 SignalEmitationDecrease(Backup->GetEmitation());
699 Flags |= STRONG_NEW_DRAW_REQUEST;
700 SignalPossibleTransparencyChange();
704 void lsquare::UpdateMemorizedDescription(truth Cheat)
706 if(Flags & DESCRIPTION_CHANGE || Cheat)
708 if(!IsDark() || Cheat)
710 MemorizedDescription.Empty();
712 if(!OLTerrain || (OLTerrain->IsTransparent() && OLTerrain->ShowThingsUnder()))
714 truth Anything = false;
716 if(OLTerrain && OLTerrain->GetNameSingular().GetSize())
718 OLTerrain->AddName(MemorizedDescription, INDEFINITE);
719 Anything = true;
722 if(Flags & IS_TRANSPARENT)
724 itemvectorvector PileVector;
725 GetStack()->Pile(PileVector, PLAYER, CENTER);
727 if(PileVector.size())
729 if(Anything)
730 MemorizedDescription << " and ";
732 if(PileVector.size() == 1)
733 PileVector[0][0]->AddName(MemorizedDescription, INDEFINITE, PileVector[0].size());
734 else
735 MemorizedDescription << "many items";
737 MemorizedDescription << " on ";
738 Anything = true;
740 else if(Anything)
741 MemorizedDescription << " on ";
743 GLTerrain->AddName(MemorizedDescription, INDEFINITE);
744 festring SideItems;
745 GetSideItemDescription(SideItems, Cheat);
747 if(!SideItems.IsEmpty())
748 MemorizedDescription << " and " << SideItems;
750 else
752 if(Anything)
753 MemorizedDescription << " on ";
755 GLTerrain->AddName(MemorizedDescription, INDEFINITE);
758 else
759 OLTerrain->AddName(MemorizedDescription, INDEFINITE);
761 if(FluidIsVisible())
762 DisplayFluidInfo(MemorizedDescription);
764 DisplayTrapInfo(MemorizedDescription);
766 if(Cheat)
767 MemorizedDescription << " (pos " << Pos.X << ':' << Pos.Y << ")";
769 else if(CanBeFeltByPlayer())
771 MemorizedDescription.Empty();
772 OLTerrain->AddName(MemorizedDescription, INDEFINITE);
774 if(FluidIsVisible())
775 DisplayFluidInfo(MemorizedDescription);
777 DisplayTrapInfo(MemorizedDescription);
779 else
780 MemorizedDescription = CONST_S("darkness");
782 Flags &= ~DESCRIPTION_CHANGE;
786 void lsquare::GetSideItemDescription(festring& String, truth Cheat) const
788 int Items = 0;
790 for(int c = 0; c < 4; ++c)
792 stack* Stack = GetStackOfAdjacentSquare(c);
794 if(Stack)
795 Items += Cheat
796 ? Stack->GetSideItems(3 - c)
797 : Stack->GetVisibleSideItems(PLAYER, 3 - c);
800 if(Items > 1)
801 String << "many items on the wall";
802 else if(Items == 1)
804 for(int c = 0; c < 4; ++c)
806 stack* Stack = GetStackOfAdjacentSquare(c);
808 if(Stack
809 && ((Cheat && Stack->GetSideItems(3 - c))
810 || (!Cheat && Stack->GetVisibleSideItems(PLAYER, 3 - c))))
811 Stack->GetBottomSideItem(PLAYER, 3 - c, Cheat)->AddName(String, INDEFINITE);
814 String << " on the wall";
818 truth lsquare::BeKicked(character* Kicker, item* Boot, bodypart* Leg, double KickDamage, double KickToHitValue, int Success, int Direction, truth Critical, truth ForceHit)
820 truth Return;
822 if(GetCharacter())
824 GetCharacter()->BeKicked(Kicker, Boot, Leg, Pos, KickDamage, KickToHitValue, Success, Direction, Critical, ForceHit);
825 Return = true;
827 else
828 Return = false;
830 if(RoomIndex)
831 GetLevel()->GetRoom(RoomIndex)->KickSquare(Kicker, this);
833 GetStack()->BeKicked(Kicker, int(KickDamage), Direction);
835 if(GetOLTerrain())
836 GetOLTerrain()->BeKicked(Kicker, int(KickDamage * (100 + Success) / 100), Direction);
838 return Return;
841 truth lsquare::CanBeDug() const
843 if((!GetPos().X || !GetPos().Y || GetPos().X == GetLevel()->GetXSize() - 1 || GetPos().Y == GetLevel()->GetYSize() - 1) && !*GetLevel()->GetLevelScript()->IsOnGround())
845 ADD_MESSAGE("Somehow you feel that by digging this square you would collapse the whole dungeon.");
846 return false;
848 else
849 return true;
852 void lsquare::ChangeLTerrain(glterrain* NewGround, olterrain* NewOver)
854 ChangeGLTerrain(NewGround);
855 ChangeOLTerrain(NewOver);
858 void lsquare::ChangeGLTerrain(glterrain* NewGround)
860 if(GLTerrain->IsAnimated())
861 DecStaticAnimatedEntities();
863 truth WasUsingBorderTiles = GLTerrain->UseBorderTiles();
864 delete GLTerrain;
865 GLTerrain = NewGround;
866 NewGround->SetLSquareUnder(this);
867 Flags |= NEW_DRAW_REQUEST;
868 GetLevel()->SetWalkability(Pos, GetTheoreticalWalkability());
869 CalculateGroundBorderPartners();
870 SendMemorizedUpdateRequest();
872 if(WasUsingBorderTiles || NewGround->UseBorderTiles())
873 RequestForGroundBorderPartnerUpdates();
875 if(NewGround->IsAnimated())
876 IncStaticAnimatedEntities();
879 void lsquare::ChangeOLTerrain(olterrain* NewOver)
881 if(OLTerrain && OLTerrain->IsAnimated())
882 DecStaticAnimatedEntities();
884 truth WasUsingBorderTiles = OLTerrain && OLTerrain->UseBorderTiles();
885 delete OLTerrain;
886 OLTerrain = NewOver;
887 Flags |= NEW_DRAW_REQUEST;
888 GetLevel()->SetWalkability(Pos, GetTheoreticalWalkability());
889 CalculateOverBorderPartners();
890 CalculateIsTransparent();
891 SendMemorizedUpdateRequest();
893 if(WasUsingBorderTiles || (NewOver && NewOver->UseBorderTiles()))
894 RequestForOverBorderPartnerUpdates();
896 if(NewOver)
898 NewOver->SetLSquareUnder(this);
900 if(NewOver->IsAnimated())
901 IncStaticAnimatedEntities();
905 void lsquare::SetLTerrain(glterrain* NewGround, olterrain* NewOver)
907 GLTerrain = NewGround;
908 NewGround->SetLSquareUnder(this);
910 if(NewGround->IsAnimated())
911 IncStaticAnimatedEntities();
913 OLTerrain = NewOver;
915 if(NewOver)
917 NewOver->SetLSquareUnder(this);
919 if(NewOver->IsAnimated())
920 IncStaticAnimatedEntities();
922 if(!NewOver->IsTransparent())
923 Flags &= ~IS_TRANSPARENT;
926 GetLevel()->SetWalkability(Pos, GetTheoreticalWalkability());
929 void lsquare::ApplyScript(const squarescript* SquareScript, room* Room)
931 if(SquareScript->AttachRequired())
932 GetLevel()->AddToAttachQueue(Pos);
934 int EntryIndex = SquareScript->GetEntryIndex();
936 if(EntryIndex != NO_ENTRY)
937 GetLevel()->SetEntryPos(EntryIndex, Pos);
939 const contentscript<character>* CharacterScript = SquareScript->GetCharacter();
941 if (CharacterScript) {
942 character *Char = CharacterScript->Instantiate();
944 if (Char) {
945 Char->SetGenerationDanger(GetLevel()->GetDifficulty());
946 if (!Char->GetTeam()) Char->SetTeam(game::GetTeam(*GetLevel()->GetLevelScript()->GetTeamDefault()));
947 if (CharacterScript->GetFlags() & IS_LEADER) Char->GetTeam()->SetLeader(Char);
948 Char->PutToOrNear(Pos);
949 Char->CreateHomeData();
950 if (Room && CharacterScript->GetFlags() & IS_MASTER) Room->SetMasterID(Char->GetID());
954 const fearray<contentscript<item> >* Items = SquareScript->GetItems();
956 if(Items)
957 for(uInt c1 = 0; c1 < Items->Size; ++c1)
959 const interval* TimesPtr = Items->Data[c1].GetTimes();
960 int Times = TimesPtr ? TimesPtr->Randomize() : 1;
962 for(int c2 = 0; c2 < Times; ++c2)
964 item* Item = Items->Data[c1].Instantiate();
966 if(Item)
968 int SquarePosition = Items->Data[c1].GetSquarePosition();
970 if(SquarePosition != CENTER)
971 Item->SignalSquarePositionChange(SquarePosition);
973 GetStack()->AddItem(Item);
974 Item->SpecialGenerationHandler();
979 const contentscript<glterrain>* GLTerrainScript = SquareScript->GetGTerrain();
981 if(GLTerrainScript)
983 GetLevel()->AddFlag(Pos, FORBIDDEN);
984 ChangeGLTerrain(GLTerrainScript->Instantiate());
986 if (GLTerrainScript->IsInside()) {
987 if (*GLTerrainScript->IsInside()) Flags |= INSIDE; else Flags &= ~INSIDE;
991 const contentscript<olterrain>* OLTerrainScript = SquareScript->GetOTerrain();
993 if(OLTerrainScript)
995 GetLevel()->AddFlag(Pos, FORBIDDEN);
996 ChangeOLTerrain(OLTerrainScript->Instantiate());
1000 truth lsquare::CanBeSeenByPlayer(truth IgnoreDarkness) const
1002 return (IgnoreDarkness || !IsDark()) && LastSeen == game::GetLOSTick();
1005 truth lsquare::CanBeSeenFrom(v2 FromPos, sLong MaxDistance, truth IgnoreDarkness) const
1007 if((Pos - FromPos).GetLengthSquare() <= MaxDistance
1008 && (IgnoreDarkness || !IsDark()))
1010 if(Character && Character->IsPlayer()
1011 && GetNearLSquare(FromPos)->CanBeSeenByPlayer(true))
1012 return true;
1014 eyecontroller::Map = GetLevel()->GetMap();
1015 return mapmath<eyecontroller>::DoLine(FromPos.X, FromPos.Y, GetPos().X, GetPos().Y, SKIP_FIRST);
1018 return false;
1021 void lsquare::StepOn(character* Stepper, lsquare** ComingFrom)
1023 if(RoomIndex)
1025 truth WasInRoom = false;
1027 if(ComingFrom)
1028 for(int c = 0; c < Stepper->GetSquaresUnder(); ++c)
1029 if(ComingFrom[c]->GetRoomIndex() == RoomIndex)
1031 WasInRoom = true;
1032 break;
1035 if(!WasInRoom)
1036 GetLevel()->GetRoom(RoomIndex)->Enter(Stepper);
1039 GLTerrain->StepOn(Stepper);
1041 if(OLTerrain)
1043 OLTerrain->StepOn(Stepper);
1045 if(Stepper->DestroysWalls() && OLTerrain->WillBeDestroyedBy(Stepper))
1047 if(CanBeSeenByPlayer())
1048 ADD_MESSAGE("%s destroys %s.", Stepper->CHAR_NAME(DEFINITE), OLTerrain->CHAR_NAME(DEFINITE));
1050 Stepper->EditAP(-100);
1051 OLTerrain->BeDestroyed();
1055 uInt c;
1056 std::vector<trap*> TrapVector;
1058 for(trap* T = Trap; T; T = T->Next)
1059 TrapVector.push_back(T);
1061 for(c = 0; c < TrapVector.size(); ++c)
1062 if(TrapVector[c]->Exists())
1064 TrapVector[c]->StepOnEffect(Stepper);
1066 if(!Stepper->IsEnabled())
1067 return;
1070 if(!Stepper->IsFlying())
1072 std::vector<fluid*> FluidVector;
1074 for(fluid* F = Fluid; F; F = F->Next)
1075 FluidVector.push_back(F);
1077 for(c = 0; c < FluidVector.size(); ++c)
1078 if(FluidVector[c]->Exists())
1080 FluidVector[c]->StepOnEffect(Stepper);
1082 if(!Stepper->IsEnabled())
1083 return;
1086 GetStack()->CheckForStepOnEffect(Stepper);
1090 void lsquare::ReceiveVomit(character* Who, liquid* Liquid)
1092 if(!GetOLTerrain() || !GetOLTerrain()->ReceiveVomit(Who, Liquid))
1094 SpillFluid(Who, Liquid);
1096 if(RoomIndex)
1097 GetRoom()->ReceiveVomit(Who);
1101 void lsquare::SetTemporaryEmitation(col24 What)
1103 col24 Old = TemporaryEmitation;
1104 TemporaryEmitation = 0;
1105 SignalEmitationDecrease(Old);
1106 TemporaryEmitation = What;
1107 SignalEmitationIncrease(What);
1110 void lsquare::ChangeOLTerrainAndUpdateLights(olterrain* NewTerrain)
1112 truth WasTransparent = Flags & IS_TRANSPARENT, Noxified = false;
1113 emittervector EmitterBackup;
1115 if(WasTransparent && NewTerrain && !NewTerrain->IsTransparent())
1117 EmitterBackup = Emitter;
1118 GetLevel()->ForceEmitterNoxify(EmitterBackup);
1119 Noxified = true;
1122 sLong OldEmit = OLTerrain ? OLTerrain->GetEmitation() : 0;
1123 ChangeOLTerrain(NewTerrain);
1125 if(NewTerrain)
1126 SignalEmitationIncrease(NewTerrain->GetEmitation());
1128 SignalEmitationDecrease(OldEmit);
1129 GetStack()->DropSideItems();
1131 if(!IsFlyable() && Smoke)
1133 DecAnimatedEntities();
1135 for(smoke* S = Smoke; S; S = S->Next)
1136 S->SendToHell();
1138 Smoke = 0;
1139 SmokeAlphaSum = 0;
1142 if(!WasTransparent == !!CalculateIsTransparent())
1144 if(Noxified)
1145 GetLevel()->ForceEmitterEmitation(EmitterBackup, SunEmitter, FORCE_ADD);
1146 else
1147 GetLevel()->ForceEmitterEmitation(Emitter, SunEmitter);
1149 CalculateLuminance();
1151 if(LastSeen == game::GetLOSTick())
1152 game::SendLOSUpdateRequest();
1156 void lsquare::DrawParticles(sLong Color, truth DrawHere)
1158 if(GetPos().X < game::GetCamera().X
1159 || GetPos().Y < game::GetCamera().Y
1160 || GetPos().X >= game::GetCamera().X + game::GetScreenXSize()
1161 || GetPos().Y >= game::GetCamera().Y + game::GetScreenYSize()
1162 || !CanBeSeenByPlayer(true)
1163 || Color == TRANSPARENT_COLOR)
1164 return;
1166 clock_t StartTime = clock();
1168 if(DrawHere)
1169 game::DrawEverythingNoBlit();
1171 if(Color & RANDOM_COLOR)
1172 Color = MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190);
1174 v2 Pos = game::CalculateScreenCoordinates(GetPos());
1176 for(int c = 0; c < 10; ++c)
1177 DOUBLE_BUFFER->PutPixel(Pos + v2(1 + RAND() % 14, 1 + RAND() % 14), Color);
1179 Flags |= STRONG_NEW_DRAW_REQUEST; // Clean the pixels from the screen afterwards
1181 if(DrawHere)
1183 graphics::BlitDBToScreen();
1184 while(clock() - StartTime < 0.02 * CLOCKS_PER_SEC);
1188 truth lsquare::DipInto(item* Thingy, character* Dipper)
1190 if(IsDipDestination())
1192 room* Room = GetRoom();
1194 if(Room && Room->HasDipHandler() && !Room->Dip(Dipper))
1195 return false;
1197 return (GLTerrain->IsDipDestination() && GLTerrain->DipInto(Thingy, Dipper)) || (OLTerrain && OLTerrain->IsDipDestination() && OLTerrain->DipInto(Thingy, Dipper));
1199 else
1201 if(Dipper->IsPlayer())
1202 ADD_MESSAGE("You cannot dip %s on that square!", Thingy->CHAR_NAME(DEFINITE));
1204 return false;
1208 // return true if key fits someplace
1210 truth lsquare::TryKey(item* Key, character* Applier)
1212 if(GetOLTerrain() && GetOLTerrain()->TryKey(Key, Applier))
1213 return true;
1215 if((!GetOLTerrain() || !GetOLTerrain()->HasKeyHole()) && !GetStack()->TryKey(Key, Applier))
1217 ADD_MESSAGE("There's no place here to put the key in!");
1218 return false;
1221 return true;
1224 void lsquare::SignalSeen(feuLong Tick)
1226 if(LastSeen < Tick - 2)
1227 Flags |= STRONG_NEW_DRAW_REQUEST;
1229 Flags &= ~(IN_SQUARE_STACK|PERFECTLY_QUADRI_HANDLED);
1230 LastSeen = Tick;
1232 if(!(Flags & IS_TRANSPARENT))
1234 col24 OldLuminance = Luminance;
1235 CalculateLuminance();
1237 if(OldLuminance != Luminance)
1239 Flags |= NEW_DRAW_REQUEST;
1241 if(IsDark() != game::IsDark(OldLuminance))
1242 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
1246 if(IsDark())
1248 v2 Dist = Pos - PLAYER->GetPos();
1250 if(abs(Dist.X) > 1 || abs(Dist.Y) > 1)
1252 LastSeen -= 2;
1253 return;
1257 if(!Memorized)
1258 CreateMemorized();
1260 UpdateMemorized();
1261 UpdateMemorizedDescription();
1263 if(Character)
1264 Character->CheckIfSeen();
1267 void lsquare::DrawMemorized(blitdata& BlitData) const
1269 LastSeen = 0;
1270 Flags &= ~STRONG_NEW_DRAW_REQUEST;
1271 BlitData.Luminance = ivanconfig::GetContrastLuminance();
1273 if(FowMemorized)
1274 FowMemorized->LuminanceBlit(BlitData);
1275 else
1276 DOUBLE_BUFFER->Fill(BlitData.Dest, BlitData.Border, 0);
1278 ccharacter* C = Character;
1280 if(C && C->CanBeSeenByPlayer())
1282 BlitData.CustomData |= C->GetSquareIndex(Pos);
1283 C->Draw(BlitData);
1284 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
1288 void lsquare::DrawMemorizedCharacter(blitdata& BlitData) const
1290 BlitData.Luminance = ivanconfig::GetContrastLuminance();
1292 if(FowMemorized)
1293 FowMemorized->LuminanceBlit(BlitData);
1294 else
1295 DOUBLE_BUFFER->Fill(BlitData.Dest, BlitData.Border, 0);
1297 BlitData.CustomData |= Character->GetSquareIndex(Pos);
1298 Character->Draw(BlitData);
1299 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
1300 Flags |= STRONG_NEW_DRAW_REQUEST;
1303 truth lsquare::IsDangerous(ccharacter* Who) const
1305 return ((!Who->IsFlying()
1306 && (Stack->IsDangerous(Who)
1307 || HasDangerousFluids(Who)))
1308 || IsDangerousToBreathe(Who) || HasDangerousTraps(Who));
1311 truth lsquare::IsScary(ccharacter* Who) const
1313 return IsScaryToBreathe(Who);
1316 stack* lsquare::GetStackOfAdjacentSquare(int I) const
1318 lsquare* Square = 0;
1320 switch(I)
1322 case LEFT: Square = NeighbourLSquare[3]; break;
1323 case DOWN: Square = NeighbourLSquare[6]; break;
1324 case UP: Square = NeighbourLSquare[1]; break;
1325 case RIGHT: Square = NeighbourLSquare[4]; break;
1328 return Square ? Square->Stack : 0;
1331 void lsquare::SendMemorizedUpdateRequest()
1333 if(!(Flags & FREEZED))
1335 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
1337 if(!game::IsGenerating() && (CanBeSeenByPlayer() || CanBeFeltByPlayer()))
1339 if(!Memorized)
1340 CreateMemorized();
1342 UpdateMemorized();
1343 UpdateMemorizedDescription();
1348 void lsquare::KickAnyoneStandingHereAway()
1350 if(Character)
1352 character* Backup = Character;
1353 Backup->Remove();
1354 Backup->PutNear(Pos);
1358 outputfile& operator<<(outputfile& SaveFile, const emitter& Emitter)
1360 SaveFile.Write(reinterpret_cast<cchar*>(&Emitter), sizeof(Emitter));
1361 return SaveFile;
1364 inputfile& operator>>(inputfile& SaveFile, emitter& Emitter)
1366 SaveFile.Read(reinterpret_cast<char*>(&Emitter), sizeof(Emitter));
1367 return SaveFile;
1370 void lsquare::AddItem(item* Item)
1372 Stack->AddItem(Item);
1375 v2 lsquare::DrawLightning(v2 StartPos, sLong Color, int Direction, truth DrawHere)
1377 if(GetPos().X < game::GetCamera().X
1378 || GetPos().Y < game::GetCamera().Y
1379 || GetPos().X >= game::GetCamera().X + game::GetScreenXSize()
1380 || GetPos().Y >= game::GetCamera().Y + game::GetScreenYSize()
1381 || !CanBeSeenByPlayer(true))
1382 switch(Direction)
1384 case 1: return v2(RAND() & 15, 15);
1385 case 3: return v2(15, RAND() & 15);
1386 case 4: return v2(0, RAND() & 15);
1387 case 6: return v2(RAND() & 15, 0);
1388 default: return StartPos;
1391 clock_t StartTime = clock();
1392 bitmap Empty(TILE_V2, TRANSPARENT_COLOR);
1393 Empty.ActivateFastFlag();
1395 if(Color & RANDOM_COLOR)
1396 Color = MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190);
1398 if(Direction != YOURSELF)
1400 while(!Empty.CreateLightning(StartPos, game::GetMoveVector(Direction), 16, Color));
1401 v2 EndPos(0, 0);
1403 switch(Direction)
1405 case 0: EndPos = v2(0, 0); break;
1406 case 1: EndPos = v2(RAND() & 15, 0); StartPos = v2(EndPos.X, 15); break;
1407 case 2: EndPos = v2(15, 0); break;
1408 case 3: EndPos = v2(0, RAND() & 15); StartPos = v2(15, EndPos.Y); break;
1409 case 4: EndPos = v2(15, RAND() & 15); StartPos = v2(0, EndPos.Y); break;
1410 case 5: EndPos = v2(0, 15); break;
1411 case 6: EndPos = v2(RAND() & 15, 15); StartPos = v2(EndPos.X, 0); break;
1412 case 7: EndPos = v2(15, 15); break;
1415 while(!Empty.CreateLightning(EndPos, -game::GetMoveVector(Direction), NO_LIMIT, Color));
1417 else
1419 static v2 Dir[4] = { v2(0, -1), v2(-1, 0), v2(1, 0), v2(0, 1) };
1421 for(int d = 0; d < 4; ++d)
1422 while(!Empty.CreateLightning(StartPos + Dir[d], ZERO_V2, 10, Color));
1425 if(DrawHere)
1426 game::DrawEverythingNoBlit();
1428 blitdata B = { DOUBLE_BUFFER,
1429 { 0, 0 },
1430 { 0, 0 },
1431 { TILE_SIZE, TILE_SIZE },
1432 { 0 },
1433 TRANSPARENT_COLOR,
1434 0 };
1436 B.Dest = game::CalculateScreenCoordinates(GetPos());
1437 Empty.NormalMaskedBlit(B);
1438 Flags |= STRONG_NEW_DRAW_REQUEST;
1440 if(DrawHere)
1442 graphics::BlitDBToScreen();
1443 while(clock() - StartTime < 0.02 * CLOCKS_PER_SEC);
1446 return StartPos;
1449 truth lsquare::Polymorph(const beamdata& Beam)
1451 GetStack()->Polymorph(Beam.Owner);
1453 if(GetOLTerrain())
1454 GetOLTerrain()->Polymorph(Beam.Owner);
1456 character* Character = GetCharacter();
1458 if(Character)
1460 if(Beam.Owner && Character->GetTeam() != Beam.Owner->GetTeam())
1461 Beam.Owner->Hostility(Character);
1463 Character->PolymorphRandomly(1, 999999, 5000 + RAND() % 5000);
1466 if(Engraved)
1468 for(int c = 0; Engraved[c] != '\0'; ++c)
1470 if(RAND_2)
1472 Engraved[c] = 32 + RAND_N(95);
1476 return false;
1479 truth lsquare::Strike(const beamdata& Beam)
1481 int Damage = 50 + RAND() % 21 - RAND() % 21;
1482 GetStack()->ReceiveDamage(Beam.Owner, Damage, ENERGY, Beam.Direction);
1483 ReceiveTrapDamage(Beam.Owner, Damage, ENERGY, Beam.Direction);
1485 character* Char = GetCharacter();
1487 if(Char)
1489 if(Char->IsPlayer())
1490 ADD_MESSAGE("You are hit by a burst of energy!");
1491 else if(Char->CanBeSeenByPlayer())
1492 ADD_MESSAGE("%s is hit by a burst of energy!", Char->CHAR_NAME(DEFINITE));
1494 if(Beam.Owner)
1495 Beam.Owner->Hostility(Char);
1497 Char->ReceiveDamage(Beam.Owner, Damage, ENERGY, ALL);
1498 Char->CheckDeath(Beam.DeathMsg, Beam.Owner);
1501 if(GetOLTerrain())
1502 GetOLTerrain()->ReceiveDamage(Beam.Owner, Damage, ENERGY);
1504 return false;
1507 truth lsquare::FireBall(const beamdata& Beam)
1509 if(!IsFlyable() || GetCharacter())
1511 if(CanBeSeenByPlayer(true))
1512 ADD_MESSAGE("A magical explosion is triggered!");
1514 GetLevel()->Explosion(Beam.Owner, Beam.DeathMsg, Pos, 75 + RAND() % 25 - RAND() % 25);
1515 return true;
1518 return false;
1521 truth lsquare::Teleport(const beamdata& Beam)
1523 if(Character)
1525 if(Beam.Owner && Character->GetTeam() != Beam.Owner->GetTeam())
1526 Beam.Owner->Hostility(GetCharacter());
1528 if(Character->IsPlayer())
1529 ADD_MESSAGE("You experience a forced teleportation.");
1530 else if(Character->CanBeSeenByPlayer())
1531 ADD_MESSAGE("%s disappears!", Character->CHAR_NAME(DEFINITE));
1533 Character->TeleportRandomly();
1536 if(RoomIndex)
1537 GetLevel()->GetRoom(RoomIndex)->TeleportSquare(Beam.Owner, this);
1539 GetStack()->TeleportRandomly();
1540 return false;
1543 truth lsquare::Haste(const beamdata&)
1545 GetStack()->Haste();
1546 character* Dude = GetCharacter();
1548 if(Dude)
1549 Dude->Haste();
1551 return false;
1554 truth lsquare::Slow(const beamdata& Beam)
1556 GetStack()->Slow();
1557 character* Dude = GetCharacter();
1559 if(Dude)
1561 if(Beam.Owner)
1562 Beam.Owner->Hostility(Dude);
1564 Dude->Slow();
1567 return false;
1570 truth lsquare::Confuse (const beamdata &Beam) {
1571 character *Dude = GetCharacter();
1573 if (Dude && Dude->CanBeConfused()) {
1574 if (Beam.Owner) Beam.Owner->Hostility(Dude);
1575 Dude->BeginTemporaryState(CONFUSED, 50+RAND()%50);
1577 return false;
1581 truth lsquare::Parasitize (const beamdata &Beam) {
1582 character *Dude = GetCharacter();
1584 if (Dude && Dude->GetTorso()->CanHaveParasite()) {
1585 if (Beam.Owner) Beam.Owner->Hostility(Dude);
1586 Dude->GainIntrinsic(PARASITIZED);
1588 return false;
1592 truth lsquare::InstillPanic (const beamdata &Beam) {
1593 character* Dude = GetCharacter();
1595 if (Dude && Dude->CanPanic()) {
1596 if (Beam.Owner) Beam.Owner->Hostility(Dude);
1597 Dude->BeginTemporaryState(PANIC, 50+RAND()%50);
1599 return false;
1603 truth lsquare::Resurrect(const beamdata& Beam)
1605 if(GetCharacter())
1606 return GetCharacter()->RaiseTheDead(Beam.Owner);
1607 else
1608 return GetStack()->RaiseTheDead(Beam.Owner);
1611 truth lsquare::Invisibility(const beamdata&)
1613 if(GetCharacter())
1614 GetCharacter()->BeginTemporaryState(INVISIBLE, 1000 + RAND() % 1001);
1616 return false;
1619 truth lsquare::Duplicate(const beamdata& Beam)
1621 truth DuplicatedSomething = false;
1622 character* Character = GetCharacter();
1624 if(Character)
1625 DuplicatedSomething = truth(Character->DuplicateToNearestSquare(Beam.Owner, Beam.SpecialParameters));
1627 if(GetStack()->Duplicate(DuplicatedSomething ? 4 : 5, Beam.SpecialParameters))
1628 DuplicatedSomething = true;
1630 return DuplicatedSomething;
1633 truth lsquare::Lightning(const beamdata& Beam)
1635 int Damage = 20 + RAND() % 6 - RAND() % 6;
1636 GetStack()->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY, Beam.Direction);
1637 ReceiveTrapDamage(Beam.Owner, Damage, ELECTRICITY, Beam.Direction);
1639 character* Char = GetCharacter();
1641 if(Char)
1643 if(Char->IsPlayer())
1644 ADD_MESSAGE("A massive burst of electricity runs through your body!");
1645 else if(Char->CanBeSeenByPlayer())
1646 ADD_MESSAGE("A massive burst of electricity runs through %s!", Char->CHAR_NAME(DEFINITE));
1648 if(Beam.Owner)
1649 Beam.Owner->Hostility(Char);
1651 Char->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY, ALL);
1652 Char->CheckDeath(Beam.DeathMsg, Beam.Owner);
1655 if(GetOLTerrain())
1656 GetOLTerrain()->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY);
1658 return false;
1661 truth lsquare::DoorCreation(const beamdata& Beam)
1663 if((!GetOLTerrain()
1664 || GetOLTerrain()->IsSafeToCreateDoor())
1665 && !GetCharacter()
1666 && (GetLevel()->IsOnGround()
1667 || (Pos.X > 0 && Pos.Y > 0
1668 && Pos.X < GetLevel()->GetXSize() - 1 && Pos.Y < GetLevel()->GetYSize() - 1)))
1670 if(Beam.Owner && GetRoom())
1671 GetRoom()->HostileAction(Beam.Owner);
1673 door* Door = door::Spawn(0, NO_MATERIALS);
1674 Door->InitMaterials(MAKE_MATERIAL(STEEL));
1676 if(RAND() % 10)
1677 Door->Lock();
1679 ChangeOLTerrainAndUpdateLights(Door);
1680 return true;
1683 return false;
1686 truth (lsquare::*BeamEffect[BEAM_EFFECTS])(const beamdata&) =
1688 &lsquare::Polymorph,
1689 &lsquare::Strike,
1690 &lsquare::FireBall,
1691 &lsquare::Teleport,
1692 &lsquare::Haste,
1693 &lsquare::Slow,
1694 &lsquare::Resurrect,
1695 &lsquare::Invisibility,
1696 &lsquare::Duplicate,
1697 &lsquare::Lightning,
1698 &lsquare::DoorCreation,
1699 &lsquare::AcidRain,
1700 &lsquare::Necromancy
1703 truth (lsquare::*lsquare::GetBeamEffect(int I))(const beamdata&)
1705 return BeamEffect[I];
1708 truth lsquare::CheckKick(ccharacter* Kicker) const
1710 if(Character && Kicker->CheckIfTooScaredToHit(Character))
1711 return false;
1713 if(RoomIndex && !GetLevel()->GetRoom(RoomIndex)->CheckKickSquare(Kicker, this))
1714 return false;
1716 return true;
1719 void lsquare::GetHitByExplosion(const explosion* Explosion)
1721 if(Explosion->ID == LastExplosionID)
1722 return;
1724 LastExplosionID = Explosion->ID;
1725 int DistanceSquare = (Pos - Explosion->Pos).GetLengthSquare();
1727 if(DistanceSquare > Explosion->RadiusSquare)
1728 return;
1730 int Damage = Explosion->Strength / (DistanceSquare + 1);
1732 if (Character && (Explosion->HurtNeutrals || (Explosion->Terrorist && Character->GetRelation(Explosion->Terrorist) == HOSTILE))) {
1733 if (Character->IsPlayer()) game::SetPlayerWasHurtByExplosion(true);
1734 else Character->GetHitByExplosion(Explosion, Damage);
1737 GetStack()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, FIRE);
1738 GetStack()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE);
1740 ReceiveTrapDamage(Explosion->Terrorist, Damage >> 1, FIRE);
1741 ReceiveTrapDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE);
1743 if(GetOLTerrain())
1744 GetOLTerrain()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, FIRE);
1746 if(GetOLTerrain())
1747 GetOLTerrain()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE);
1750 int lsquare::GetSpoiledItems() const
1752 return GetStack()->GetSpoiledItems();
1755 truth lsquare::LowerEnchantment(const beamdata& Beam)
1757 character* Char = GetCharacter();
1758 itemvector AllItems;
1759 sortdata SortData(AllItems, Beam.Owner, true, &item::IsEnchantable);
1760 SortAllItems(SortData);
1761 item* RandomItem;
1763 if(!AllItems.empty())
1764 RandomItem = AllItems[RAND() % AllItems.size()];
1765 else
1766 return false;
1768 if(Char)
1770 if(Char->IsPlayer())
1771 ADD_MESSAGE("%s glows blue for a moment!", RandomItem->CHAR_NAME(DEFINITE));
1773 if(Beam.Owner)
1774 Beam.Owner->Hostility(Char);
1777 if(RandomItem->GetEnchantment() > -5)
1778 RandomItem->EditEnchantment(-1);
1780 return true;
1783 void lsquare::SortAllItems(const sortdata& SortData)
1785 if(GetCharacter())
1786 GetCharacter()->SortAllItems(SortData);
1788 GetStack()->SortAllItems(SortData);
1791 truth lsquare::SoftenMaterial (const beamdata &Beam) {
1792 character *Char = GetCharacter();
1793 item *RandomItem;
1794 itemvector AllItems;
1796 sortdata SortData(AllItems, Beam.Owner, true, &item::IsEnchantable);
1797 SortAllItems(SortData);
1798 //sortdata SortData2(AllItems, Beam.Owner, true, &item::MaterialIsChangeable);
1799 //SortAllItems(SortData2);
1800 if (AllItems.empty()) return false;
1801 RandomItem = AllItems[RAND() % AllItems.size()];
1802 if (Char) {
1803 if (Char->IsPlayer()) ADD_MESSAGE("Your %s glows yellow for a moment!", RandomItem->CHAR_NAME(UNARTICLED));
1804 if (Beam.Owner) Beam.Owner->Hostility(Char);
1807 truth Changed = 0;
1808 festring Desc;
1810 RandomItem->AddName(Desc, UNARTICLED);
1811 material *OldMaterial = RandomItem->GetMainMaterial();
1812 int NewMaterial = RandomItem->GetMainMaterial()->GetSoftenedMaterial(RandomItem);
1814 if (NewMaterial != NONE) {
1815 /* Don't Forget! It is an ugly thing, I know, but removal = seg-fault since cannot have NONE material */
1816 RandomItem->ChangeMainMaterial(MAKE_MATERIAL(NewMaterial)); /*->SpawnMore()*/
1817 if (OldMaterial->GetConfig() != NewMaterial) Changed = 1;
1819 if (Char) {
1820 if (Changed && Char->IsPlayer()) {
1821 ADD_MESSAGE("Your %s softens into %s!", Desc.CStr(), RandomItem->GetMainMaterial()->GetName(false, false).CStr());
1822 } else if (Changed) {
1823 ADD_MESSAGE("%s's %s softens into %s!", Char->CHAR_DESCRIPTION(DEFINITE), Desc.CStr(), RandomItem->GetMainMaterial()->GetName(false, false).CStr());
1825 if (!Changed) {
1826 //may not need this message
1827 if (Char->IsPlayer()) {
1828 ADD_MESSAGE("Your %s vibrates slightly but remains unchanged.", RandomItem->CHAR_NAME(UNARTICLED) );
1829 } else {
1830 ADD_MESSAGE("%s's %s vibrates slightly but remains unchanged.", Char->CHAR_DESCRIPTION(DEFINITE), RandomItem->CHAR_NAME(UNARTICLED) );
1834 return true;
1838 void lsquare::RemoveSmoke(smoke* ToBeRemoved)
1840 smoke* S = Smoke;
1842 if(S == ToBeRemoved)
1844 Smoke = S->Next;
1846 if(!S)
1847 DecAnimatedEntities();
1849 else
1851 smoke* LS;
1855 LS = S;
1856 S = S->Next;
1858 while(S != ToBeRemoved);
1860 LS->Next = S->Next;
1864 void lsquare::AddSmoke(gas* ToBeAdded)
1866 smoke* S = Smoke;
1868 if(!S)
1870 Smoke = new smoke(ToBeAdded, this);
1871 IncAnimatedEntities();
1873 else
1875 smoke* LS;
1879 if(ToBeAdded->IsSameAs(S->GetGas()))
1881 S->Merge(ToBeAdded);
1882 return;
1885 LS = S;
1886 S = S->Next;
1888 while(S);
1890 LS->Next = new smoke(ToBeAdded, this);
1894 void lsquare::ShowSmokeMessage() const
1896 for(const smoke* S = Smoke; S; S = S->Next)
1897 S->AddBreatheMessage();
1900 void lsquare::SignalSmokeAlphaChange(int What)
1902 SmokeAlphaSum += What;
1903 SignalPossibleTransparencyChange();
1906 int lsquare::GetDivineMaster() const
1908 return RoomIndex ? GetLevel()->GetRoom(RoomIndex)->GetDivineMaster() : 0;
1911 void lsquare::DisplaySmokeInfo (festring &Msg) const {
1912 if (Smoke) {
1913 if (!Smoke->Next)
1914 Msg << " A cloud of " << Smoke->GetGas()->GetName(false, false) << " surrounds the square.";
1915 else
1916 Msg << " A lot of gases hover over the square.";
1920 void lsquare::ReceiveEarthQuakeDamage()
1922 GetStack()->ReceiveDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE);
1923 ReceiveTrapDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE);
1924 /* Gum solution */
1926 if(GetOLTerrain() && GetOLTerrain()->IsDoor())
1927 GetOLTerrain()->ReceiveDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE);
1930 truth lsquare::CanBeFeltByPlayer() const
1932 if (!PLAYER) return false;
1933 return OLTerrain && !PLAYER->CanMoveOn(this) && Pos.IsAdjacent(PLAYER->GetPos());
1936 void lsquare::PreProcessForBone()
1938 DestroyMemorized();
1939 LastSeen = 0;
1941 if(OLTerrain)
1942 OLTerrain->PreProcessForBone();
1944 if(Smoke)
1946 DecAnimatedEntities();
1948 for(smoke* S = Smoke; S; S = S->Next)
1949 S->SendToHell();
1951 Smoke = 0;
1952 SmokeAlphaSum = 0;
1955 if(Character && !Character->PreProcessForBone())
1957 Character->SendToHell();
1958 Character->Remove();
1961 for(fluid* F = Fluid; F; F = F->Next)
1962 F->PreProcessForBone();
1964 for(trap* T = Trap; T; T = T->Next)
1965 T->PreProcessForBone();
1967 GetStack()->PreProcessForBone();
1970 void lsquare::PostProcessForBone(double& DangerSum, int& Enemies)
1972 if(OLTerrain)
1973 OLTerrain->PostProcessForBone();
1975 if(Character && !Character->PostProcessForBone(DangerSum, Enemies))
1977 Character->SendToHell();
1978 Character->Remove();
1981 for(fluid* F = Fluid; F; F = F->Next)
1982 F->PostProcessForBone();
1984 for(trap* T = Trap; T; T = T->Next)
1985 T->PostProcessForBone();
1987 GetStack()->PostProcessForBone();
1990 void lsquare::FinalProcessForBone()
1992 if(OLTerrain)
1993 OLTerrain->FinalProcessForBone();
1995 if(Character)
1996 Character->FinalProcessForBone();
1998 GetStack()->FinalProcessForBone();
2001 truth lsquare::EngravingsCanBeReadByPlayer()
2003 return PLAYER->CanRead(); // Might be a good idea to improve sometime in the distant future.
2006 void lsquare::DisplayEngravedInfo(festring& Buffer) const
2008 Buffer << " There is a message engraved here: \"" << Engraved << '\"';
2011 truth lsquare::IsDangerousToBreathe(ccharacter* Who) const
2013 for(const smoke* S = Smoke; S; S = S->Next)
2014 if(S->IsDangerousToBreathe(Who))
2015 return true;
2017 return false;
2020 truth lsquare::IsScaryToBreathe(ccharacter* Who) const
2022 for(const smoke* S = Smoke; S; S = S->Next)
2023 if(S->IsScaryToBreathe(Who))
2024 return true;
2026 return false;
2030 struct groundborderpartner {
2031 truth operator < (const groundborderpartner &P) const { return Terrain->GetBorderTilePriority() < P.Terrain->GetBorderTilePriority(); }
2032 glterrain *Terrain;
2033 int SquareIndex;
2036 void lsquare::CalculateGroundBorderPartners () {
2037 if (GroundBorderPartnerInfo & BORDER_PARTNER_ANIMATED) DecStaticAnimatedEntities();
2038 groundborderpartner BorderPartner[8*2]; //k8: *2 to make g++ shut up (WTF?!)
2039 int Index = 0;
2040 int Priority = GLTerrain->GetBorderTilePriority();
2041 for (int d = 0; d < 8; ++d) {
2042 lsquare *Square = NeighbourLSquare[d];
2043 if (Square) {
2044 glterrain *Terrain = Square->GetGLTerrain();
2045 if (Terrain && Terrain->UseBorderTiles() && Terrain->GetBorderTilePriority() > Priority) {
2046 BorderPartner[Index].Terrain = Terrain;
2047 BorderPartner[Index].SquareIndex = 7-d;
2048 ++Index;
2052 GroundBorderPartnerInfo = 0;
2053 if (!Index) {
2054 delete [] GroundBorderPartnerTerrain;
2055 GroundBorderPartnerTerrain = 0;
2056 return;
2058 if (!GroundBorderPartnerTerrain) GroundBorderPartnerTerrain = new glterrain *[8];
2059 std::sort(BorderPartner, BorderPartner+Index); // why g++ complains here? ah, ignore it for now
2060 truth Animated = false;
2061 for (int c = 0; c < Index; ++c) {
2062 glterrain *T = BorderPartner[c].Terrain;
2063 GroundBorderPartnerTerrain[c] = T;
2064 GroundBorderPartnerInfo |= BorderPartner[c].SquareIndex<<((c<<1)+c);
2065 if (T->IsAnimated()) Animated = true;
2067 if (Animated) {
2068 GroundBorderPartnerInfo |= BORDER_PARTNER_ANIMATED;
2069 IncStaticAnimatedEntities();
2071 GroundBorderPartnerInfo |= Index<<24;
2075 struct overborderpartner {
2076 truth operator < (const overborderpartner &P) const { return Terrain->GetBorderTilePriority() < P.Terrain->GetBorderTilePriority(); }
2077 olterrain *Terrain;
2078 int SquareIndex;
2081 void lsquare::CalculateOverBorderPartners () {
2082 if (OverBorderPartnerInfo & BORDER_PARTNER_ANIMATED) DecStaticAnimatedEntities();
2083 overborderpartner BorderPartner[8*2]; //k8: *2 to make g++ shut up (WTF?!)
2084 int Index = 0;
2085 int Priority = OLTerrain ? OLTerrain->GetBorderTilePriority() : 0;
2086 for (int d = 0; d < 8; ++d) {
2087 lsquare *Square = NeighbourLSquare[d];
2088 if (Square) {
2089 olterrain *Terrain = Square->GetOLTerrain();
2090 if (Terrain && Terrain->UseBorderTiles() && Terrain->GetBorderTilePriority() > Priority) {
2091 BorderPartner[Index].Terrain = Terrain;
2092 BorderPartner[Index].SquareIndex = 7-d;
2093 ++Index;
2097 OverBorderPartnerInfo = 0;
2098 if (!Index) {
2099 delete [] OverBorderPartnerTerrain;
2100 OverBorderPartnerTerrain = 0;
2101 return;
2103 if (!OverBorderPartnerTerrain) OverBorderPartnerTerrain = new olterrain *[8];
2104 std::sort(BorderPartner, BorderPartner+Index); // why g++ complains here? ah, ignore it for now
2105 truth Animated = false;
2106 for (int c = 0; c < Index; ++c) {
2107 olterrain *T = BorderPartner[c].Terrain;
2108 OverBorderPartnerTerrain[c] = T;
2109 OverBorderPartnerInfo |= BorderPartner[c].SquareIndex<<((c<<1)+c);
2110 if (T->IsAnimated()) Animated = true;
2112 if (Animated) {
2113 OverBorderPartnerInfo |= BORDER_PARTNER_ANIMATED;
2114 IncStaticAnimatedEntities();
2116 OverBorderPartnerInfo |= Index<<24;
2117 /*k8
2118 if(OverBorderPartnerInfo & BORDER_PARTNER_ANIMATED)
2119 int esko = esko = 2;
2124 void lsquare::RequestForGroundBorderPartnerUpdates()
2126 if(!game::IsGenerating())
2127 for(int d = 0; d < 8; ++d)
2129 lsquare* Square = NeighbourLSquare[d];
2131 if(Square)
2133 Square->CalculateGroundBorderPartners();
2134 Square->SendNewDrawRequest();
2135 Square->SendMemorizedUpdateRequest();
2140 void lsquare::RequestForOverBorderPartnerUpdates()
2142 if(!game::IsGenerating())
2143 for(int d = 0; d < 8; ++d)
2145 lsquare* Square = NeighbourLSquare[d];
2147 if(Square)
2149 Square->CalculateOverBorderPartners();
2150 Square->SendNewDrawRequest();
2151 Square->SendMemorizedUpdateRequest();
2156 int lsquare::GetWalkability() const
2158 if(!GetLevel()->IsOnGround())
2160 if(Pos.X >= 1 && Pos.Y >= 1 && Pos.X < GetLevel()->GetXSize() - 1 && Pos.Y < GetLevel()->GetYSize() - 1)
2161 return OLTerrain ? OLTerrain->GetWalkability() & GLTerrain->GetWalkability() : GLTerrain->GetWalkability();
2162 else
2163 return 0;
2165 else
2166 return OLTerrain ? OLTerrain->GetWalkability() & GLTerrain->GetWalkability() : GLTerrain->GetWalkability();
2169 void lsquare::RemoveFluid(fluid* ToRemove)
2171 fluid*& F = ListFind(Fluid, pointercomparer<fluid>(ToRemove));
2172 F = F->Next;
2173 SignalEmitationDecrease(ToRemove->GetEmitation());
2176 struct fluidcomparer
2178 fluidcomparer(const liquid* Liquid) : Liquid(Liquid) { }
2179 truth operator()(const fluid* F) const { return Liquid->IsSameAs(F->GetLiquid()); }
2180 const liquid* Liquid;
2183 fluid* lsquare::AddFluid(liquid* ToBeAdded)
2185 fluid*& F = ListFind(Fluid, fluidcomparer(ToBeAdded));
2187 if(F)
2189 F->AddLiquidAndVolume(ToBeAdded->GetVolume());
2190 delete ToBeAdded;
2192 else
2194 F = new fluid(ToBeAdded, this);
2195 SignalEmitationIncrease(ToBeAdded->GetEmitation());
2198 SendNewDrawRequest();
2199 SendMemorizedUpdateRequest();
2200 return F;
2203 void lsquare::DisplayFluidInfo(festring& Msg) const
2205 if(Fluid)
2207 Msg << ". There is ";
2208 fluid::AddFluidInfo(Fluid, Msg);
2209 AddLocationDescription(Msg);
2213 void lsquare::SpillFluid(character* Spiller, liquid* Liquid, truth ForceHit, truth ShowMsg)
2215 if(!Liquid->GetVolume())
2217 delete Liquid;
2218 return;
2221 if(IsFlyable())
2223 if(GetCharacter())
2225 if(Spiller && !GetCharacter()->IsAlly(Spiller))
2226 Spiller->Hostility(GetCharacter());
2228 sLong CharVolume = GetCharacter()->GetVolume();
2229 double ChanceMultiplier = 1. / (300 + sqrt(GetStack()->GetVolume() + CharVolume));
2230 double Root = sqrt(CharVolume);
2232 if(ForceHit || Root > RAND() % 400 || Root > RAND() % 400)
2234 sLong SpillVolume = sLong(Liquid->GetVolume() * Root * ChanceMultiplier);
2236 if(SpillVolume)
2238 if(ShowMsg && (GetCharacter()->IsPlayer() || GetCharacter()->CanBeSeenByPlayer()))
2239 ADD_MESSAGE("%s is spilled all over %s.", Liquid->GetName(false, false).CStr(), GetCharacter()->CHAR_DESCRIPTION(DEFINITE));
2241 Liquid->EditVolume(-SpillVolume);
2242 GetCharacter()->SpillFluid(Spiller, Liquid->SpawnMoreLiquid(SpillVolume), GetCharacter()->GetSquareIndex(GetPos()));
2247 GetStack()->SpillFluid(Spiller, Liquid, Liquid->GetVolume());
2250 if(Liquid->GetVolume() && !Liquid->IsSameAs(GLTerrain->GetMainMaterial()))
2252 fluid* F = AddFluid(Liquid);
2254 if(GetCharacter())
2255 F->StepOnEffect(GetCharacter());
2257 else
2258 delete Liquid;
2261 void lsquare::DrawStacks(blitdata& BlitData) const
2263 Stack->Draw(PLAYER, BlitData, CENTER);
2265 for(int c = 0; c < 4; ++c)
2267 stack* Stack = GetStackOfAdjacentSquare(c);
2269 if(Stack)
2270 Stack->Draw(PLAYER, BlitData, 3 - c);
2274 void lsquare::RemoveRain(rain* ToBeRemoved)
2276 SendNewDrawRequest();
2277 rain* R = Rain;
2279 if(ToBeRemoved->IsEnabled())
2280 DecAnimatedEntities();
2282 if(R == ToBeRemoved)
2283 Rain = R->Next;
2284 else
2286 rain* LR;
2290 LR = R;
2291 R = R->Next;
2293 while(R != ToBeRemoved);
2295 LR->Next = R->Next;
2298 SignalEmitationDecrease(ToBeRemoved->GetEmitation());
2301 void lsquare::AddRain(liquid* RainLiquid, v2 Speed, int Team, truth OwnLiquid)
2303 rain* R = Rain, * NewRain = new rain(RainLiquid, this, Speed, Team, OwnLiquid);
2305 if(NewRain->IsEnabled())
2306 IncAnimatedEntities();
2308 if(!R)
2309 Rain = NewRain;
2310 else
2312 rain* LR;
2316 LR = R;
2317 R = R->Next;
2319 while(R);
2321 LR->Next = NewRain;
2325 void lsquare::RemoveSunLight()
2327 SunLightLuminance = 0;
2328 SunEmitter.clear();
2331 void lsquare::CheckIfIsSecondarySunLightEmitter()
2333 col24 OldEmitation = SecondarySunLightEmitation;
2335 if(Flags & IS_TRANSPARENT && (!(Flags & INSIDE) || SunLightLuminance))
2336 for(int d = 0; d < 8; ++d)
2338 lsquare* Neighbour = NeighbourLSquare[d];
2340 if(Neighbour && Neighbour->Flags & INSIDE)
2342 col24 NewEmitation = GetLevel()->GetAmbientLuminance();
2344 if(OldEmitation != NewEmitation)
2346 SecondarySunLightEmitation = NewEmitation;
2348 if(game::CompareLights(NewEmitation, OldEmitation) >= 0)
2349 Emitate(NewEmitation, SECONDARY_SUN_LIGHT);
2350 else
2352 Noxify(OldEmitation, SECONDARY_SUN_LIGHT);
2353 Emitate(NewEmitation, SECONDARY_SUN_LIGHT|FORCE_ADD);
2357 return;
2361 if(OldEmitation)
2363 Noxify(OldEmitation, SECONDARY_SUN_LIGHT);
2364 SecondarySunLightEmitation = 0;
2368 void lsquare::CalculateNeighbourLSquares()
2370 int XSize = GetLevel()->GetXSize();
2371 int YSize = GetLevel()->GetYSize();
2373 for(int d = 0; d < 8; ++d)
2375 v2 NPos = Pos + game::GetMoveVector(d);
2377 if(NPos.X >= 0 && NPos.Y >= 0 && NPos.X < XSize && NPos.Y < YSize)
2378 NeighbourLSquare[d] = GetLevel()->GetLSquare(NPos);
2379 else
2380 NeighbourLSquare[d] = 0;
2384 void lsquare::RemoveLuminance(col24& Emitation)
2386 col24 OldLuminance = Luminance;
2387 col24 OldEmitation = Emitation;
2388 Emitation = 0;
2390 if(game::CompareLights(OldEmitation, OldLuminance) < 0)
2391 return;
2393 if(!(Flags & IS_TRANSPARENT))
2395 Flags |= NEW_DRAW_REQUEST;
2397 if(LastSeen == game::GetLOSTick())
2398 game::SendLOSUpdateRequest();
2400 else
2402 CalculateLuminance();
2404 if(OldLuminance == Luminance)
2405 return;
2407 Flags |= NEW_DRAW_REQUEST;
2409 if(!Luminance)
2411 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
2413 if(LastSeen == game::GetLOSTick())
2414 game::SendLOSUpdateRequest();
2419 void lsquare::ChangeLuminance(col24& Emitation, col24 NewLuminance)
2421 col24 OldLuminance = Luminance;
2423 if(!(Flags & IS_TRANSPARENT))
2425 Emitation = NewLuminance;
2426 Flags |= NEW_DRAW_REQUEST;
2428 if(LastSeen == game::GetLOSTick())
2429 game::SendLOSUpdateRequest();
2431 return;
2434 truth EmitationInsignificant = !Emitation
2435 || game::CompareLights(Emitation, OldLuminance) < 0;
2436 Emitation = NewLuminance;
2438 if(game::CompareLights(NewLuminance, OldLuminance) > 0
2439 && EmitationInsignificant)
2440 game::CombineLights(Luminance, NewLuminance);
2441 else
2443 if(EmitationInsignificant)
2444 return;
2446 CalculateLuminance();
2448 if(OldLuminance == Luminance)
2449 return;
2452 Flags |= NEW_DRAW_REQUEST;
2454 if(!OldLuminance)
2456 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
2458 if(LastSeen == game::GetLOSTick())
2459 game::SendLOSUpdateRequest();
2463 void lsquare::EnableGlobalRain()
2465 for(rain* R = Rain; R; R = R->Next)
2466 if(!R->HasOwnLiquid())
2468 R->Enable();
2469 IncAnimatedEntities();
2473 void lsquare::DisableGlobalRain()
2475 SendNewDrawRequest();
2477 for(rain* R = Rain; R; R = R->Next)
2478 if(!R->HasOwnLiquid())
2480 R->Disable();
2481 DecAnimatedEntities();
2485 void lsquare::InitLastSeen()
2487 LastSeen = LastSeen == game::GetLOSTick() ? 2 : 0;
2488 SquarePartLastSeen = 0;
2491 truth lsquare::Engrave(cfestring& What)
2493 if(Engraved)
2494 delete [] Engraved;
2496 if(!What.IsEmpty())
2498 Engraved = new char[What.GetSize() + 1];
2499 strcpy(Engraved, What.CStr());
2501 else
2502 Engraved = 0;
2504 return true;
2507 void lsquare::SendSunLightSignals()
2509 if(Flags & IS_TRANSPARENT)
2511 col24 OldLuminance = Luminance;
2512 CalculateLuminance();
2514 if(Luminance != OldLuminance)
2516 Flags |= NEW_DRAW_REQUEST;
2518 if(!Luminance != !OldLuminance)
2520 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
2522 if(LastSeen == game::GetLOSTick())
2523 game::SendLOSUpdateRequest();
2527 else
2529 Flags |= NEW_DRAW_REQUEST;
2531 if(LastSeen == game::GetLOSTick())
2532 game::SendLOSUpdateRequest();
2536 void lsquare::ZeroReSunEmitatedFlags()
2538 sunemittervector::iterator i, End = SunEmitter.end();
2540 for(i = SunEmitter.begin(); i != End; ++i)
2541 *i &= ~RE_SUN_EMITATED;
2544 truth lsquare::CalculateIsTransparent()
2546 if((!OLTerrain || OLTerrain->IsTransparent()) && SmokeAlphaSum < 175
2547 && (!Character || Character->IsTransparent()))
2549 Flags |= IS_TRANSPARENT;
2550 return true;
2552 else
2554 Flags &= ~IS_TRANSPARENT;
2555 return false;
2559 void lsquare::CalculateSunLightLuminance(feuLong SeenBitMask)
2561 sunemittervector::const_iterator i, SunEnd = SunEmitter.end();
2562 int S = 0, L = 0;
2564 for(i = SunEmitter.begin(); i != SunEnd; ++i)
2566 feuLong ShadowFlag = 1 << EMITTER_SHADOW_SHIFT;
2567 feuLong SquarePartFlag = 1 << EMITTER_SQUARE_PART_SHIFT;
2568 for (int c = 0; c < 4; ++c, ShadowFlag <<= 1, SquarePartFlag <<= 1) {
2569 if (SeenBitMask & *i & SquarePartFlag) {
2570 if (*i & ShadowFlag) ++S; else ++L;
2575 if(!L)
2576 SunLightLuminance = 0;
2577 else if(!S)
2578 SunLightLuminance = GetLevel()->GetSunLightEmitation();
2579 else
2581 col24 ShadowColor = GetLevel()->GetAmbientLuminance();
2582 col24 LightColor = GetLevel()->GetSunLightEmitation();
2583 SunLightLuminance = MakeRGB24((GetRed24(LightColor) * L
2584 + GetRed24(ShadowColor) * S) / (S + L),
2585 (GetGreen24(LightColor) * L
2586 + GetGreen24(ShadowColor) * S) / (S + L),
2587 (GetBlue24(LightColor) * L
2588 + GetBlue24(ShadowColor) * S) / (S + L));
2592 void lsquare::CreateMemorized()
2594 Memorized = new bitmap(TILE_V2);
2595 Memorized->ActivateFastFlag();
2596 FowMemorized = new bitmap(TILE_V2);
2597 FowMemorized->ActivateFastFlag();
2600 truth lsquare::AcidRain(const beamdata& Beam)
2602 if(!IsFlyable() || GetCharacter() || Beam.Direction == YOURSELF)
2604 int StackSize = GetLevel()->AddRadiusToSquareStack(Pos, 9);
2605 lsquare** Stack = GetLevel()->GetSquareStack();
2606 v2 Speed = v2(512, 512);
2607 int Team = Beam.Owner ? Beam.Owner->GetTeam()->GetID() : MONSTER_TEAM;
2609 for(int c = 0; c < StackSize; ++c)
2611 Stack[c]->AddRain(liquid::Spawn(SULPHURIC_ACID, 300), Speed, Team, true);
2612 Stack[c]->Flags &= ~IN_SQUARE_STACK;
2615 if(Beam.Owner && Character && Character->GetTeam() != Beam.Owner->GetTeam())
2616 Beam.Owner->Hostility(Character);
2618 return true;
2621 return false;
2624 truth lsquare::DetectMaterial (cmaterial *Material) const {
2625 if (GLTerrain->DetectMaterial(Material) ||
2626 (OLTerrain && OLTerrain->DetectMaterial(Material)) ||
2627 Stack->DetectMaterial(Material) ||
2628 (Character && Character->DetectMaterial(Material))) return true;
2629 for (const fluid *F = Fluid; F; F = F->Next) if (F->GetLiquid()->IsSameAs(Material)) return true;
2630 for (const smoke *S = Smoke; S; S = S->Next) if (S->GetGas()->IsSameAs(Material)) return true;
2631 for (const rain *R = Rain; R; R = R->Next) if (R->GetLiquid()->IsSameAs(Material)) return true;
2632 return false;
2635 void lsquare::Reveal(feuLong Tick, truth IgnoreDarkness)
2637 if(!Memorized)
2638 CreateMemorized();
2640 LastSeen = Tick;
2642 if(IgnoreDarkness)
2643 Luminance = NORMAL_LUMINANCE;
2644 else
2646 SquarePartLastSeen = 0;
2648 for(int c = 0; c < 4; ++c)
2649 SquarePartLastSeen |= (Tick << (c << 3));
2651 CalculateLuminance();
2654 Flags |= NEW_DRAW_REQUEST
2655 | MEMORIZED_UPDATE_REQUEST
2656 | DESCRIPTION_CHANGE;
2657 UpdateMemorized();
2658 UpdateMemorizedDescription();
2661 void lsquare::DestroyMemorized()
2663 delete Memorized;
2664 delete FowMemorized;
2665 Memorized = 0;
2666 FowMemorized = 0;
2669 void lsquare::SwapMemorized(lsquare* Square)
2671 Swap(Memorized, Square->Memorized);
2672 Swap(FowMemorized, Square->FowMemorized);
2673 MemorizedDescription.SwapData(Square->MemorizedDescription);
2676 truth lsquare::Necromancy(const beamdata& Beam)
2678 return GetStack()->Necromancy(Beam.Owner);
2681 // Returns 0 if fails
2683 lsquare* lsquare::GetRandomAdjacentSquare() const
2685 lsquare* OK[8];
2686 int Index = 0;
2688 for(int c = 0; c < 8; ++c)
2690 lsquare* Square = NeighbourLSquare[c];
2692 if(Square)
2693 OK[Index++] = Square;
2696 if(Index)
2697 return OK[RAND_N(Index)];
2698 else
2699 return 0;
2702 truth pathcontroller::Handler(int x, int y)
2704 return Character->CanMoveOn(Map[x][y]);
2707 void lsquare::SignalPossibleTransparencyChange()
2709 truth WasTransparent = IsTransparent();
2710 CalculateIsTransparent();
2712 if(WasTransparent && !IsTransparent())
2714 Flags |= IS_TRANSPARENT;
2715 emittervector EmitterBackup = Emitter;
2716 GetLevel()->ForceEmitterNoxify(EmitterBackup);
2717 Flags &= ~IS_TRANSPARENT;
2718 GetLevel()->ForceEmitterEmitation(EmitterBackup, SunEmitter, FORCE_ADD);
2719 CalculateLuminance();
2720 Flags |= DESCRIPTION_CHANGE|MEMORIZED_UPDATE_REQUEST;
2722 if(LastSeen == game::GetLOSTick())
2723 game::SendLOSUpdateRequest();
2725 else if(!WasTransparent && IsTransparent())
2727 GetLevel()->ForceEmitterEmitation(Emitter, SunEmitter);
2728 CalculateLuminance();
2729 Flags |= DESCRIPTION_CHANGE|MEMORIZED_UPDATE_REQUEST;
2731 if(LastSeen == game::GetLOSTick())
2732 game::SendLOSUpdateRequest();
2736 void lsquare::RemoveTrap(trap* ToRemove)
2738 trap*& T = ListFind(Trap, pointercomparer<trap>(ToRemove));
2739 T = T->Next;
2740 SendNewDrawRequest();
2741 SendMemorizedUpdateRequest();
2744 struct trapcomparer
2746 trapcomparer(int Type) : Type(Type) { }
2747 truth operator()(const trap* T) const { return T->GetType() == Type; }
2748 int Type;
2751 truth lsquare::AddTrap(trap* ToBeAdded)
2753 trap*& T = ListFind(Trap, trapcomparer(ToBeAdded->GetType()));
2755 if(T)
2757 delete ToBeAdded;
2758 return false;
2760 else
2761 T = ToBeAdded;
2763 ToBeAdded->SetLSquareUnder(this);
2764 SendNewDrawRequest();
2765 SendMemorizedUpdateRequest();
2766 return true;
2769 void lsquare::DisplayTrapInfo(festring& Msg) const
2771 for(const trap* T = Trap; T; T = T->Next)
2772 T->AddDescription(Msg);
2775 void lsquare::FillTrapVector(std::vector<trap*>& TrapVector) const
2777 for(trap* T = Trap; T; T = T->Next)
2778 TrapVector.push_back(T);
2781 void lsquare::ReceiveTrapDamage(character* Damager, int Damage, int Type, int Direction)
2783 std::vector<trap*> TrapVector;
2784 FillTrapVector(TrapVector);
2786 for(uInt c = 0; c < TrapVector.size(); ++c)
2787 TrapVector[c]->ReceiveDamage(Damager, Damage, Type, Direction);
2790 truth lsquare::HasDangerousTraps(ccharacter* Who) const
2792 for(trap* T = Trap; T; T = T->Next)
2793 if(T->IsDangerous(Who))
2794 return true;
2796 return false;
2799 truth lsquare::HasDangerousFluids(ccharacter* Who) const
2801 for(const fluid* F = Fluid; F; F = F->Next)
2802 if(F->IsDangerous(Who))
2803 return true;
2805 return false;
2808 truth lsquare::HasNoBorderPartners() const
2810 return !(GroundBorderPartnerInfo >> 24) && !(OverBorderPartnerInfo >> 24);
2813 void lsquare::AddLocationDescription(festring& String) const
2815 if(IsFlyable())
2816 GLTerrain->AddLocationDescription(String);
2817 else
2818 OLTerrain->AddLocationDescription(String);
2821 truth lsquare::VomitingIsDangerous(ccharacter* Char) const
2823 return ((OLTerrain && OLTerrain->VomitingIsDangerous(Char))
2824 || (Character && Character->GetTeam() != Char->GetTeam()
2825 && Character->GetRelation(Char) != HOSTILE));
2828 bool lsquare::TeleportAllSmokeAway()
2830 return false;
2833 bool lsquare::TeleportAllFluidsAway()
2835 return false;
2838 bool lsquare::TeleportAllTrapsAway()
2840 for(trap* T = Trap; T; T = Trap)
2842 T->Untrap();
2843 RemoveTrap(T);
2844 v2 V, Pos = GetPos();
2845 for(V = GetLevel()->GetRandomSquare(); V != Pos; V = GetLevel()->GetRandomSquare());
2846 GetNearLSquare(V)->AddTrap(T);
2849 return false;
2852 void lsquare::AddSpecialCursors()
2854 if((FowMemorized || game::GetSeeWholeMapCheatMode()) && OLTerrain)
2855 OLTerrain->AddSpecialCursors();