unnecessary messing with code
[k8-i-v-a-n.git] / src / game / lsquare.cpp
blob0c1907a9d99040a45d5623fe8311a21881e19fc2
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 << mGoSeen;
586 SaveFile << GLTerrain << OLTerrain;
587 SaveFile << Emitter << SunEmitter;
588 SaveFile << Emitation << Engraved << Luminance;
589 SaveFile << SmokeAlphaSum << (uChar)Flags << Memorized;
590 SaveFile << SecondarySunLightEmitation;
591 SaveFile << (uChar)RoomIndex;
592 SaveFile << SunLightLuminance;
593 SaveLinkedList(SaveFile, Fluid);
594 SaveLinkedList(SaveFile, Smoke);
595 SaveLinkedList(SaveFile, Rain);
596 SaveLinkedList(SaveFile, Trap);
599 void lsquare::Load(inputfile& SaveFile)
601 Stack->Load(SaveFile); // This must be before square::Load! (Note: This comment is years old. It's probably obsolete)
602 Stack->SetMotherSquare(this);
603 square::Load(SaveFile);
604 SaveFile >> mGoSeen;
605 SaveFile >> GLTerrain >> OLTerrain;
606 SaveFile >> Emitter >> SunEmitter;
607 SaveFile >> Emitation >> Engraved >> Luminance;
608 SaveFile >> SmokeAlphaSum >> (uChar&)Flags >> Memorized;
609 Flags &= INSIDE|DESCRIPTION_CHANGE; //only these flags are loaded
610 Flags |= MEMORIZED_UPDATE_REQUEST;
611 SecondarySunLightEmitation = ReadType(col24, SaveFile);
612 RoomIndex = ReadType(uChar, SaveFile);
613 SunLightLuminance = ReadType(col24, SaveFile);
614 LoadLinkedList(SaveFile, Fluid);
615 LoadLinkedList(SaveFile, Smoke);
616 LoadLinkedList(SaveFile, Rain);
617 LoadLinkedList(SaveFile, Trap);
618 CalculateIsTransparent();
620 if(Memorized)
622 FowMemorized = new bitmap(TILE_V2);
623 FowMemorized->ActivateFastFlag();
624 Memorized->FastBlit(FowMemorized);
625 blitdata B = { FowMemorized,
626 { 0, 0 },
627 { 0, 0 },
628 { TILE_SIZE, TILE_SIZE },
629 { 0 },
631 0 };
633 igraph::GetFOWGraphic()->NormalMaskedBlit(B);
637 void lsquare::CalculateLuminance()
639 Luminance = AmbientLuminance;
640 emittervector::const_iterator i, End = Emitter.end();
642 if(Flags & IS_TRANSPARENT)
644 game::CombineLights(Luminance, SunLightLuminance);
646 for(i = Emitter.begin(); i != End; ++i)
647 game::CombineLights(Luminance, i->Emitation);
649 else
651 feuLong BitMask = 0, LOSTick = game::GetLOSTick();
653 for(int c = 0; c < 4; ++c)
654 if((SquarePartLastSeen >> (c << 3) & 0xFF) >= LOSTick)
655 BitMask |= 1 << EMITTER_SQUARE_PART_SHIFT << c;
657 CalculateSunLightLuminance(BitMask);
658 game::CombineLights(Luminance, SunLightLuminance);
660 for(i = Emitter.begin(); i != End; ++i)
661 if(BitMask & i->ID)
662 game::CombineLights(Luminance, i->Emitation);
666 void lsquare::AddCharacter(character* Guy)
668 if(Character)
669 ABORT("Overgrowth of square population detected!");
671 Character = Guy;
672 SignalEmitationIncrease(Guy->GetEmitation());
673 Flags |= STRONG_NEW_DRAW_REQUEST;
675 if(Guy->IsAnimated())
676 IncAnimatedEntities();
678 SignalPossibleTransparencyChange();
680 if(Guy->IsPlayer()
681 || (Guy->CanBeSeenByPlayer(true) && CanBeSeenByPlayer()))
682 Guy->SignalSeen();
685 void lsquare::Clean()
687 GetStack()->Clean();
690 void lsquare::RemoveCharacter()
692 if(Character)
694 character* Backup = Character;
696 if(Backup->IsAnimated())
697 DecAnimatedEntities();
699 Character = 0;
700 SignalEmitationDecrease(Backup->GetEmitation());
701 Flags |= STRONG_NEW_DRAW_REQUEST;
702 SignalPossibleTransparencyChange();
706 void lsquare::UpdateMemorizedDescription(truth Cheat)
708 if(Flags & DESCRIPTION_CHANGE || Cheat)
710 if(!IsDark() || Cheat)
712 MemorizedDescription.Empty();
714 if(!OLTerrain || (OLTerrain->IsTransparent() && OLTerrain->ShowThingsUnder()))
716 truth Anything = false;
718 if(OLTerrain && OLTerrain->GetNameSingular().GetSize())
720 OLTerrain->AddName(MemorizedDescription, INDEFINITE);
721 Anything = true;
724 if(Flags & IS_TRANSPARENT)
726 itemvectorvector PileVector;
727 GetStack()->Pile(PileVector, PLAYER, CENTER);
729 if(PileVector.size())
731 if(Anything)
732 MemorizedDescription << " and ";
734 if(PileVector.size() == 1)
735 PileVector[0][0]->AddName(MemorizedDescription, INDEFINITE, PileVector[0].size());
736 else
737 MemorizedDescription << "many items";
739 MemorizedDescription << " on ";
740 Anything = true;
742 else if(Anything)
743 MemorizedDescription << " on ";
745 GLTerrain->AddName(MemorizedDescription, INDEFINITE);
746 festring SideItems;
747 GetSideItemDescription(SideItems, Cheat);
749 if(!SideItems.IsEmpty())
750 MemorizedDescription << " and " << SideItems;
752 else
754 if(Anything)
755 MemorizedDescription << " on ";
757 GLTerrain->AddName(MemorizedDescription, INDEFINITE);
760 else
761 OLTerrain->AddName(MemorizedDescription, INDEFINITE);
763 if(FluidIsVisible())
764 DisplayFluidInfo(MemorizedDescription);
766 DisplayTrapInfo(MemorizedDescription);
768 if(Cheat)
769 MemorizedDescription << " (pos " << Pos.X << ':' << Pos.Y << ")";
771 else if(CanBeFeltByPlayer())
773 MemorizedDescription.Empty();
774 OLTerrain->AddName(MemorizedDescription, INDEFINITE);
776 if(FluidIsVisible())
777 DisplayFluidInfo(MemorizedDescription);
779 DisplayTrapInfo(MemorizedDescription);
781 else
782 MemorizedDescription = CONST_S("darkness");
784 Flags &= ~DESCRIPTION_CHANGE;
788 void lsquare::GetSideItemDescription(festring& String, truth Cheat) const
790 int Items = 0;
792 for(int c = 0; c < 4; ++c)
794 stack* Stack = GetStackOfAdjacentSquare(c);
796 if(Stack)
797 Items += Cheat
798 ? Stack->GetSideItems(3 - c)
799 : Stack->GetVisibleSideItems(PLAYER, 3 - c);
802 if(Items > 1)
803 String << "many items on the wall";
804 else if(Items == 1)
806 for(int c = 0; c < 4; ++c)
808 stack* Stack = GetStackOfAdjacentSquare(c);
810 if(Stack
811 && ((Cheat && Stack->GetSideItems(3 - c))
812 || (!Cheat && Stack->GetVisibleSideItems(PLAYER, 3 - c))))
813 Stack->GetBottomSideItem(PLAYER, 3 - c, Cheat)->AddName(String, INDEFINITE);
816 String << " on the wall";
820 truth lsquare::BeKicked(character* Kicker, item* Boot, bodypart* Leg, double KickDamage, double KickToHitValue, int Success, int Direction, truth Critical, truth ForceHit)
822 truth Return;
824 if(GetCharacter())
826 GetCharacter()->BeKicked(Kicker, Boot, Leg, Pos, KickDamage, KickToHitValue, Success, Direction, Critical, ForceHit);
827 Return = true;
829 else
830 Return = false;
832 if(RoomIndex)
833 GetLevel()->GetRoom(RoomIndex)->KickSquare(Kicker, this);
835 GetStack()->BeKicked(Kicker, int(KickDamage), Direction);
837 if(GetOLTerrain())
838 GetOLTerrain()->BeKicked(Kicker, int(KickDamage * (100 + Success) / 100), Direction);
840 return Return;
843 truth lsquare::CanBeDug() const
845 if((!GetPos().X || !GetPos().Y || GetPos().X == GetLevel()->GetXSize() - 1 || GetPos().Y == GetLevel()->GetYSize() - 1) && !*GetLevel()->GetLevelScript()->IsOnGround())
847 ADD_MESSAGE("Somehow you feel that by digging this square you would collapse the whole dungeon.");
848 return false;
850 else
851 return true;
854 void lsquare::ChangeLTerrain(glterrain* NewGround, olterrain* NewOver)
856 ChangeGLTerrain(NewGround);
857 ChangeOLTerrain(NewOver);
860 void lsquare::ChangeGLTerrain(glterrain* NewGround)
862 if(GLTerrain->IsAnimated())
863 DecStaticAnimatedEntities();
865 truth WasUsingBorderTiles = GLTerrain->UseBorderTiles();
866 delete GLTerrain;
867 GLTerrain = NewGround;
868 NewGround->SetLSquareUnder(this);
869 Flags |= NEW_DRAW_REQUEST;
870 GetLevel()->SetWalkability(Pos, GetTheoreticalWalkability());
871 CalculateGroundBorderPartners();
872 SendMemorizedUpdateRequest();
874 if(WasUsingBorderTiles || NewGround->UseBorderTiles())
875 RequestForGroundBorderPartnerUpdates();
877 if(NewGround->IsAnimated())
878 IncStaticAnimatedEntities();
881 void lsquare::ChangeOLTerrain(olterrain* NewOver)
883 if(OLTerrain && OLTerrain->IsAnimated())
884 DecStaticAnimatedEntities();
886 truth WasUsingBorderTiles = OLTerrain && OLTerrain->UseBorderTiles();
887 delete OLTerrain;
888 OLTerrain = NewOver;
889 Flags |= NEW_DRAW_REQUEST;
890 GetLevel()->SetWalkability(Pos, GetTheoreticalWalkability());
891 CalculateOverBorderPartners();
892 CalculateIsTransparent();
893 SendMemorizedUpdateRequest();
895 if(WasUsingBorderTiles || (NewOver && NewOver->UseBorderTiles()))
896 RequestForOverBorderPartnerUpdates();
898 if(NewOver)
900 NewOver->SetLSquareUnder(this);
902 if(NewOver->IsAnimated())
903 IncStaticAnimatedEntities();
907 void lsquare::SetLTerrain(glterrain* NewGround, olterrain* NewOver)
909 GLTerrain = NewGround;
910 NewGround->SetLSquareUnder(this);
912 if(NewGround->IsAnimated())
913 IncStaticAnimatedEntities();
915 OLTerrain = NewOver;
917 if(NewOver)
919 NewOver->SetLSquareUnder(this);
921 if(NewOver->IsAnimated())
922 IncStaticAnimatedEntities();
924 if(!NewOver->IsTransparent())
925 Flags &= ~IS_TRANSPARENT;
928 GetLevel()->SetWalkability(Pos, GetTheoreticalWalkability());
932 void lsquare::ApplyScript (const squarescript *SquareScript, room *Room) {
933 if (SquareScript->AttachRequired()) GetLevel()->AddToAttachQueue(Pos);
935 int EntryIndex = SquareScript->GetEntryIndex();
937 if (EntryIndex != NO_ENTRY) 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) {
958 const interval *TimesPtr = Items->Data[c1].GetTimes();
959 int Times = TimesPtr ? TimesPtr->Randomize() : 1;
961 for (int c2 = 0; c2 < Times; ++c2) {
962 item *Item = Items->Data[c1].Instantiate();
964 if (Item) {
965 int SquarePosition = Items->Data[c1].GetSquarePosition();
967 if (SquarePosition != CENTER) Item->SignalSquarePositionChange(SquarePosition);
968 GetStack()->AddItem(Item);
969 Item->SpecialGenerationHandler();
975 const contentscript<glterrain> *GLTerrainScript = SquareScript->GetGTerrain();
977 if (GLTerrainScript) {
978 GetLevel()->AddFlag(Pos, FORBIDDEN);
979 ChangeGLTerrain(GLTerrainScript->Instantiate());
981 if (GLTerrainScript->IsInside()) {
982 if (*GLTerrainScript->IsInside()) Flags |= INSIDE; else Flags &= ~INSIDE;
986 const contentscript<olterrain> *OLTerrainScript = SquareScript->GetOTerrain();
988 if (OLTerrainScript) {
989 olterrain *terra = OLTerrainScript->Instantiate();
991 if (terra) {
992 GetLevel()->AddFlag(Pos, FORBIDDEN);
993 // check for random altars
994 if (terra->AcceptsOffers()) {
995 //FIXME: make IsAltar()? for now only altars can accept offers
996 if (Room->GetDivineMaster()) {
997 //if (Terrain->GetConfig() != RoomClass->GetDivineMaster()) ABORT("Random altar in room with DivineMaster!");
998 if (terra->GetConfig() != Room->GetDivineMaster()) {
999 // force altar type
1000 fprintf(stderr, "forced altar!\n");
1001 delete terra;
1002 terra = altar::Spawn(Room->GetDivineMaster());
1004 } else {
1005 // no DivineMaster yet, assign it
1006 const fearray<int> *am = Room->GetScript()->GetAllowedDivineMasters();
1008 if (am && am->Size > 0) {
1009 int Owner = am->GetRandomElement();
1012 fprintf(stderr, "AllowedDivineMasters:");
1013 for (uInt f = 0; f < am->Size; ++f) fprintf(stderr, " %d", (*am)[f]);
1014 fprintf(stderr, "\n");
1017 if (Owner < 1 || Owner > GODS) ABORT("Your god is a bad god!");
1019 if (terra->GetConfig() != Owner) {
1020 fprintf(stderr, "recreating altar %d --> %d\n", terra->GetConfig(), Owner);
1021 delete terra;
1022 terra = altar::Spawn(Owner);
1023 } else {
1024 fprintf(stderr, "spawned altar in room w/o divine master, assigning %d\n", terra->GetConfig());
1026 } else {
1027 fprintf(stderr, "spawned altar in room w/o divine master, assigning %d\n", terra->GetConfig());
1029 Room->SetDivineMaster(terra->GetConfig());
1033 ChangeOLTerrain(terra);
1034 } else {
1035 //fprintf(stderr, "WARNING: LTerra spawn error [lsquare] in file %s, line %d\n", OLTerrainScript->GetSrcFile().CStr(), OLTerrainScript->GetSrcLine());
1036 ChangeOLTerrain(0);
1042 truth lsquare::CanBeSeenByPlayer(truth IgnoreDarkness) const
1044 return (IgnoreDarkness || !IsDark()) && LastSeen == game::GetLOSTick();
1047 truth lsquare::CanBeSeenFrom(v2 FromPos, sLong MaxDistance, truth IgnoreDarkness) const
1049 if((Pos - FromPos).GetLengthSquare() <= MaxDistance
1050 && (IgnoreDarkness || !IsDark()))
1052 if(Character && Character->IsPlayer()
1053 && GetNearLSquare(FromPos)->CanBeSeenByPlayer(true))
1054 return true;
1056 eyecontroller::Map = GetLevel()->GetMap();
1057 return mapmath<eyecontroller>::DoLine(FromPos.X, FromPos.Y, GetPos().X, GetPos().Y, SKIP_FIRST);
1060 return false;
1063 void lsquare::StepOn(character* Stepper, lsquare** ComingFrom)
1065 if(RoomIndex)
1067 truth WasInRoom = false;
1069 if(ComingFrom)
1070 for(int c = 0; c < Stepper->GetSquaresUnder(); ++c)
1071 if(ComingFrom[c]->GetRoomIndex() == RoomIndex)
1073 WasInRoom = true;
1074 break;
1077 if(!WasInRoom)
1078 GetLevel()->GetRoom(RoomIndex)->Enter(Stepper);
1081 GLTerrain->StepOn(Stepper);
1083 if(OLTerrain)
1085 OLTerrain->StepOn(Stepper);
1087 if(Stepper->DestroysWalls() && OLTerrain->WillBeDestroyedBy(Stepper))
1089 if(CanBeSeenByPlayer())
1090 ADD_MESSAGE("%s destroys %s.", Stepper->CHAR_NAME(DEFINITE), OLTerrain->CHAR_NAME(DEFINITE));
1092 Stepper->EditAP(-100);
1093 OLTerrain->BeDestroyed();
1097 uInt c;
1098 std::vector<trap*> TrapVector;
1100 for(trap* T = Trap; T; T = T->Next)
1101 TrapVector.push_back(T);
1103 for(c = 0; c < TrapVector.size(); ++c)
1104 if(TrapVector[c]->Exists())
1106 TrapVector[c]->StepOnEffect(Stepper);
1108 if(!Stepper->IsEnabled())
1109 return;
1112 if(!Stepper->IsFlying())
1114 std::vector<fluid*> FluidVector;
1116 for(fluid* F = Fluid; F; F = F->Next)
1117 FluidVector.push_back(F);
1119 for(c = 0; c < FluidVector.size(); ++c)
1120 if(FluidVector[c]->Exists())
1122 FluidVector[c]->StepOnEffect(Stepper);
1124 if(!Stepper->IsEnabled())
1125 return;
1128 GetStack()->CheckForStepOnEffect(Stepper);
1132 void lsquare::ReceiveVomit(character* Who, liquid* Liquid)
1134 if(!GetOLTerrain() || !GetOLTerrain()->ReceiveVomit(Who, Liquid))
1136 SpillFluid(Who, Liquid);
1138 if(RoomIndex)
1139 GetRoom()->ReceiveVomit(Who);
1143 void lsquare::SetTemporaryEmitation(col24 What)
1145 col24 Old = TemporaryEmitation;
1146 TemporaryEmitation = 0;
1147 SignalEmitationDecrease(Old);
1148 TemporaryEmitation = What;
1149 SignalEmitationIncrease(What);
1152 void lsquare::ChangeOLTerrainAndUpdateLights(olterrain* NewTerrain)
1154 truth WasTransparent = Flags & IS_TRANSPARENT, Noxified = false;
1155 emittervector EmitterBackup;
1157 if(WasTransparent && NewTerrain && !NewTerrain->IsTransparent())
1159 EmitterBackup = Emitter;
1160 GetLevel()->ForceEmitterNoxify(EmitterBackup);
1161 Noxified = true;
1164 sLong OldEmit = OLTerrain ? OLTerrain->GetEmitation() : 0;
1165 ChangeOLTerrain(NewTerrain);
1167 if(NewTerrain)
1168 SignalEmitationIncrease(NewTerrain->GetEmitation());
1170 SignalEmitationDecrease(OldEmit);
1171 GetStack()->DropSideItems();
1173 if(!IsFlyable() && Smoke)
1175 DecAnimatedEntities();
1177 for(smoke* S = Smoke; S; S = S->Next)
1178 S->SendToHell();
1180 Smoke = 0;
1181 SmokeAlphaSum = 0;
1184 if(!WasTransparent == !!CalculateIsTransparent())
1186 if(Noxified)
1187 GetLevel()->ForceEmitterEmitation(EmitterBackup, SunEmitter, FORCE_ADD);
1188 else
1189 GetLevel()->ForceEmitterEmitation(Emitter, SunEmitter);
1191 CalculateLuminance();
1193 if(LastSeen == game::GetLOSTick())
1194 game::SendLOSUpdateRequest();
1198 void lsquare::DrawParticles(sLong Color, truth DrawHere)
1200 if(GetPos().X < game::GetCamera().X
1201 || GetPos().Y < game::GetCamera().Y
1202 || GetPos().X >= game::GetCamera().X + game::GetScreenXSize()
1203 || GetPos().Y >= game::GetCamera().Y + game::GetScreenYSize()
1204 || !CanBeSeenByPlayer(true)
1205 || Color == TRANSPARENT_COLOR)
1206 return;
1208 clock_t StartTime = clock();
1210 if(DrawHere)
1211 game::DrawEverythingNoBlit();
1213 if(Color & RANDOM_COLOR)
1214 Color = MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190);
1216 v2 Pos = game::CalculateScreenCoordinates(GetPos());
1218 for(int c = 0; c < 10; ++c)
1219 DOUBLE_BUFFER->PutPixel(Pos + v2(1 + RAND() % 14, 1 + RAND() % 14), Color);
1221 Flags |= STRONG_NEW_DRAW_REQUEST; // Clean the pixels from the screen afterwards
1223 if(DrawHere)
1225 graphics::BlitDBToScreen();
1226 while(clock() - StartTime < 0.02 * CLOCKS_PER_SEC);
1230 truth lsquare::DipInto(item* Thingy, character* Dipper)
1232 if(IsDipDestination())
1234 room* Room = GetRoom();
1236 if(Room && Room->HasDipHandler() && !Room->Dip(Dipper))
1237 return false;
1239 return (GLTerrain->IsDipDestination() && GLTerrain->DipInto(Thingy, Dipper)) || (OLTerrain && OLTerrain->IsDipDestination() && OLTerrain->DipInto(Thingy, Dipper));
1241 else
1243 if(Dipper->IsPlayer())
1244 ADD_MESSAGE("You cannot dip %s on that square!", Thingy->CHAR_NAME(DEFINITE));
1246 return false;
1250 // return true if key fits someplace
1252 truth lsquare::TryKey(item* Key, character* Applier)
1254 if(GetOLTerrain() && GetOLTerrain()->TryKey(Key, Applier))
1255 return true;
1257 if((!GetOLTerrain() || !GetOLTerrain()->HasKeyHole()) && !GetStack()->TryKey(Key, Applier))
1259 ADD_MESSAGE("There's no place here to put the key in!");
1260 return false;
1263 return true;
1266 void lsquare::SignalSeen(feuLong Tick)
1268 if(LastSeen < Tick - 2)
1269 Flags |= STRONG_NEW_DRAW_REQUEST;
1271 Flags &= ~(IN_SQUARE_STACK|PERFECTLY_QUADRI_HANDLED);
1272 LastSeen = Tick;
1274 if(!(Flags & IS_TRANSPARENT))
1276 col24 OldLuminance = Luminance;
1277 CalculateLuminance();
1279 if(OldLuminance != Luminance)
1281 Flags |= NEW_DRAW_REQUEST;
1283 if(IsDark() != game::IsDark(OldLuminance))
1284 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
1288 if(IsDark())
1290 v2 Dist = Pos - PLAYER->GetPos();
1292 if(abs(Dist.X) > 1 || abs(Dist.Y) > 1)
1294 LastSeen -= 2;
1295 return;
1299 if(!Memorized)
1300 CreateMemorized();
1302 UpdateMemorized();
1303 UpdateMemorizedDescription();
1305 if(Character)
1306 Character->CheckIfSeen();
1309 void lsquare::DrawMemorized(blitdata& BlitData) const
1311 LastSeen = 0;
1312 Flags &= ~STRONG_NEW_DRAW_REQUEST;
1313 BlitData.Luminance = ivanconfig::GetContrastLuminance();
1315 if(FowMemorized)
1316 FowMemorized->LuminanceBlit(BlitData);
1317 else
1318 DOUBLE_BUFFER->Fill(BlitData.Dest, BlitData.Border, 0);
1320 ccharacter* C = Character;
1322 if(C && C->CanBeSeenByPlayer())
1324 BlitData.CustomData |= C->GetSquareIndex(Pos);
1325 C->Draw(BlitData);
1326 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
1330 void lsquare::DrawMemorizedCharacter(blitdata& BlitData) const
1332 BlitData.Luminance = ivanconfig::GetContrastLuminance();
1334 if(FowMemorized)
1335 FowMemorized->LuminanceBlit(BlitData);
1336 else
1337 DOUBLE_BUFFER->Fill(BlitData.Dest, BlitData.Border, 0);
1339 BlitData.CustomData |= Character->GetSquareIndex(Pos);
1340 Character->Draw(BlitData);
1341 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
1342 Flags |= STRONG_NEW_DRAW_REQUEST;
1345 truth lsquare::IsDangerous(ccharacter* Who) const
1347 return ((!Who->IsFlying()
1348 && (Stack->IsDangerous(Who)
1349 || HasDangerousFluids(Who)))
1350 || IsDangerousToBreathe(Who) || HasDangerousTraps(Who));
1353 truth lsquare::IsScary(ccharacter* Who) const
1355 return IsScaryToBreathe(Who);
1358 stack* lsquare::GetStackOfAdjacentSquare(int I) const
1360 lsquare* Square = 0;
1362 switch(I)
1364 case LEFT: Square = NeighbourLSquare[3]; break;
1365 case DOWN: Square = NeighbourLSquare[6]; break;
1366 case UP: Square = NeighbourLSquare[1]; break;
1367 case RIGHT: Square = NeighbourLSquare[4]; break;
1370 return Square ? Square->Stack : 0;
1373 void lsquare::SendMemorizedUpdateRequest()
1375 if(!(Flags & FREEZED))
1377 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
1379 if(!game::IsGenerating() && (CanBeSeenByPlayer() || CanBeFeltByPlayer()))
1381 if(!Memorized)
1382 CreateMemorized();
1384 UpdateMemorized();
1385 UpdateMemorizedDescription();
1390 void lsquare::KickAnyoneStandingHereAway()
1392 if(Character)
1394 character* Backup = Character;
1395 Backup->Remove();
1396 Backup->PutNear(Pos);
1400 outputfile& operator<<(outputfile& SaveFile, const emitter& Emitter)
1402 SaveFile.Write(reinterpret_cast<cchar*>(&Emitter), sizeof(Emitter));
1403 return SaveFile;
1406 inputfile& operator>>(inputfile& SaveFile, emitter& Emitter)
1408 SaveFile.Read(reinterpret_cast<char*>(&Emitter), sizeof(Emitter));
1409 return SaveFile;
1412 void lsquare::AddItem(item* Item)
1414 Stack->AddItem(Item);
1417 v2 lsquare::DrawLightning(v2 StartPos, sLong Color, int Direction, truth DrawHere)
1419 if(GetPos().X < game::GetCamera().X
1420 || GetPos().Y < game::GetCamera().Y
1421 || GetPos().X >= game::GetCamera().X + game::GetScreenXSize()
1422 || GetPos().Y >= game::GetCamera().Y + game::GetScreenYSize()
1423 || !CanBeSeenByPlayer(true))
1424 switch(Direction)
1426 case 1: return v2(RAND() & 15, 15);
1427 case 3: return v2(15, RAND() & 15);
1428 case 4: return v2(0, RAND() & 15);
1429 case 6: return v2(RAND() & 15, 0);
1430 default: return StartPos;
1433 clock_t StartTime = clock();
1434 bitmap Empty(TILE_V2, TRANSPARENT_COLOR);
1435 Empty.ActivateFastFlag();
1437 if(Color & RANDOM_COLOR)
1438 Color = MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190);
1440 if(Direction != YOURSELF)
1442 while(!Empty.CreateLightning(StartPos, game::GetMoveVector(Direction), 16, Color));
1443 v2 EndPos(0, 0);
1445 switch(Direction)
1447 case 0: EndPos = v2(0, 0); break;
1448 case 1: EndPos = v2(RAND() & 15, 0); StartPos = v2(EndPos.X, 15); break;
1449 case 2: EndPos = v2(15, 0); break;
1450 case 3: EndPos = v2(0, RAND() & 15); StartPos = v2(15, EndPos.Y); break;
1451 case 4: EndPos = v2(15, RAND() & 15); StartPos = v2(0, EndPos.Y); break;
1452 case 5: EndPos = v2(0, 15); break;
1453 case 6: EndPos = v2(RAND() & 15, 15); StartPos = v2(EndPos.X, 0); break;
1454 case 7: EndPos = v2(15, 15); break;
1457 while(!Empty.CreateLightning(EndPos, -game::GetMoveVector(Direction), NO_LIMIT, Color));
1459 else
1461 static v2 Dir[4] = { v2(0, -1), v2(-1, 0), v2(1, 0), v2(0, 1) };
1463 for(int d = 0; d < 4; ++d)
1464 while(!Empty.CreateLightning(StartPos + Dir[d], ZERO_V2, 10, Color));
1467 if(DrawHere)
1468 game::DrawEverythingNoBlit();
1470 blitdata B = { DOUBLE_BUFFER,
1471 { 0, 0 },
1472 { 0, 0 },
1473 { TILE_SIZE, TILE_SIZE },
1474 { 0 },
1475 TRANSPARENT_COLOR,
1476 0 };
1478 B.Dest = game::CalculateScreenCoordinates(GetPos());
1479 Empty.NormalMaskedBlit(B);
1480 Flags |= STRONG_NEW_DRAW_REQUEST;
1482 if(DrawHere)
1484 graphics::BlitDBToScreen();
1485 while(clock() - StartTime < 0.02 * CLOCKS_PER_SEC);
1488 return StartPos;
1491 truth lsquare::Polymorph(const beamdata& Beam)
1493 GetStack()->Polymorph(Beam.Owner);
1495 if(GetOLTerrain())
1496 GetOLTerrain()->Polymorph(Beam.Owner);
1498 character* Character = GetCharacter();
1500 if(Character)
1502 if(Beam.Owner && Character->GetTeam() != Beam.Owner->GetTeam())
1503 Beam.Owner->Hostility(Character);
1505 Character->PolymorphRandomly(1, 999999, 5000 + RAND() % 5000);
1508 if(Engraved)
1510 for(int c = 0; Engraved[c] != '\0'; ++c)
1512 if(RAND_2)
1514 Engraved[c] = 32 + RAND_N(95);
1518 return false;
1521 truth lsquare::Strike(const beamdata& Beam)
1523 int Damage = 50 + RAND() % 21 - RAND() % 21;
1524 GetStack()->ReceiveDamage(Beam.Owner, Damage, ENERGY, Beam.Direction);
1525 ReceiveTrapDamage(Beam.Owner, Damage, ENERGY, Beam.Direction);
1527 character* Char = GetCharacter();
1529 if(Char)
1531 if(Char->IsPlayer())
1532 ADD_MESSAGE("You are hit by a burst of energy!");
1533 else if(Char->CanBeSeenByPlayer())
1534 ADD_MESSAGE("%s is hit by a burst of energy!", Char->CHAR_NAME(DEFINITE));
1536 if(Beam.Owner)
1537 Beam.Owner->Hostility(Char);
1539 Char->ReceiveDamage(Beam.Owner, Damage, ENERGY, ALL);
1540 Char->CheckDeath(Beam.DeathMsg, Beam.Owner);
1543 if(GetOLTerrain())
1544 GetOLTerrain()->ReceiveDamage(Beam.Owner, Damage, ENERGY);
1546 return false;
1549 truth lsquare::FireBall(const beamdata& Beam)
1551 if(!IsFlyable() || GetCharacter())
1553 if(CanBeSeenByPlayer(true))
1554 ADD_MESSAGE("A magical explosion is triggered!");
1556 GetLevel()->Explosion(Beam.Owner, Beam.DeathMsg, Pos, 75 + RAND() % 25 - RAND() % 25);
1557 return true;
1560 return false;
1563 truth lsquare::Teleport(const beamdata& Beam)
1565 if(Character)
1567 if(Beam.Owner && Character->GetTeam() != Beam.Owner->GetTeam())
1568 Beam.Owner->Hostility(GetCharacter());
1570 if(Character->IsPlayer())
1571 ADD_MESSAGE("You experience a forced teleportation.");
1572 else if(Character->CanBeSeenByPlayer())
1573 ADD_MESSAGE("%s disappears!", Character->CHAR_NAME(DEFINITE));
1575 Character->TeleportRandomly();
1578 if(RoomIndex)
1579 GetLevel()->GetRoom(RoomIndex)->TeleportSquare(Beam.Owner, this);
1581 GetStack()->TeleportRandomly();
1582 return false;
1585 truth lsquare::Haste(const beamdata&)
1587 GetStack()->Haste();
1588 character* Dude = GetCharacter();
1590 if(Dude)
1591 Dude->Haste();
1593 return false;
1596 truth lsquare::Slow(const beamdata& Beam)
1598 GetStack()->Slow();
1599 character* Dude = GetCharacter();
1601 if(Dude)
1603 if(Beam.Owner)
1604 Beam.Owner->Hostility(Dude);
1606 Dude->Slow();
1609 return false;
1612 truth lsquare::Confuse (const beamdata &Beam) {
1613 character *Dude = GetCharacter();
1615 if (Dude && Dude->CanBeConfused()) {
1616 if (Beam.Owner) Beam.Owner->Hostility(Dude);
1617 Dude->BeginTemporaryState(CONFUSED, 50+RAND()%50);
1619 return false;
1623 truth lsquare::Parasitize (const beamdata &Beam) {
1624 character *Dude = GetCharacter();
1626 if (Dude && Dude->GetTorso()->CanHaveParasite()) {
1627 if (Beam.Owner) Beam.Owner->Hostility(Dude);
1628 Dude->GainIntrinsic(PARASITIZED);
1630 return false;
1634 truth lsquare::InstillPanic (const beamdata &Beam) {
1635 character* Dude = GetCharacter();
1637 if (Dude && Dude->CanPanic()) {
1638 if (Beam.Owner) Beam.Owner->Hostility(Dude);
1639 Dude->BeginTemporaryState(PANIC, 50+RAND()%50);
1641 return false;
1645 truth lsquare::Resurrect(const beamdata& Beam)
1647 if(GetCharacter())
1648 return GetCharacter()->RaiseTheDead(Beam.Owner);
1649 else
1650 return GetStack()->RaiseTheDead(Beam.Owner);
1653 truth lsquare::Invisibility(const beamdata&)
1655 if(GetCharacter())
1656 GetCharacter()->BeginTemporaryState(INVISIBLE, 1000 + RAND() % 1001);
1658 return false;
1661 truth lsquare::Duplicate(const beamdata& Beam)
1663 truth DuplicatedSomething = false;
1664 character* Character = GetCharacter();
1666 if(Character)
1667 DuplicatedSomething = truth(Character->DuplicateToNearestSquare(Beam.Owner, Beam.SpecialParameters));
1669 if(GetStack()->Duplicate(DuplicatedSomething ? 4 : 5, Beam.SpecialParameters))
1670 DuplicatedSomething = true;
1672 return DuplicatedSomething;
1675 truth lsquare::Lightning(const beamdata& Beam)
1677 int Damage = 20 + RAND() % 6 - RAND() % 6;
1678 GetStack()->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY, Beam.Direction);
1679 ReceiveTrapDamage(Beam.Owner, Damage, ELECTRICITY, Beam.Direction);
1681 character* Char = GetCharacter();
1683 if(Char)
1685 if(Char->IsPlayer())
1686 ADD_MESSAGE("A massive burst of electricity runs through your body!");
1687 else if(Char->CanBeSeenByPlayer())
1688 ADD_MESSAGE("A massive burst of electricity runs through %s!", Char->CHAR_NAME(DEFINITE));
1690 if(Beam.Owner)
1691 Beam.Owner->Hostility(Char);
1693 Char->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY, ALL);
1694 Char->CheckDeath(Beam.DeathMsg, Beam.Owner);
1697 if(GetOLTerrain())
1698 GetOLTerrain()->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY);
1700 return false;
1703 truth lsquare::DoorCreation(const beamdata& Beam)
1705 if((!GetOLTerrain()
1706 || GetOLTerrain()->IsSafeToCreateDoor())
1707 && !GetCharacter()
1708 && (GetLevel()->IsOnGround()
1709 || (Pos.X > 0 && Pos.Y > 0
1710 && Pos.X < GetLevel()->GetXSize() - 1 && Pos.Y < GetLevel()->GetYSize() - 1)))
1712 if(Beam.Owner && GetRoom())
1713 GetRoom()->HostileAction(Beam.Owner);
1715 door* Door = door::Spawn(0, NO_MATERIALS);
1716 Door->InitMaterials(MAKE_MATERIAL(STEEL));
1718 if(RAND() % 10)
1719 Door->Lock();
1721 ChangeOLTerrainAndUpdateLights(Door);
1722 return true;
1725 return false;
1728 truth (lsquare::*BeamEffect[BEAM_EFFECTS])(const beamdata&) =
1730 &lsquare::Polymorph,
1731 &lsquare::Strike,
1732 &lsquare::FireBall,
1733 &lsquare::Teleport,
1734 &lsquare::Haste,
1735 &lsquare::Slow,
1736 &lsquare::Resurrect,
1737 &lsquare::Invisibility,
1738 &lsquare::Duplicate,
1739 &lsquare::Lightning,
1740 &lsquare::DoorCreation,
1741 &lsquare::AcidRain,
1742 &lsquare::Necromancy
1745 truth (lsquare::*lsquare::GetBeamEffect(int I))(const beamdata&)
1747 return BeamEffect[I];
1750 truth lsquare::CheckKick(ccharacter* Kicker) const
1752 if(Character && Kicker->CheckIfTooScaredToHit(Character))
1753 return false;
1755 if(RoomIndex && !GetLevel()->GetRoom(RoomIndex)->CheckKickSquare(Kicker, this))
1756 return false;
1758 return true;
1761 void lsquare::GetHitByExplosion(const explosion* Explosion)
1763 if(Explosion->ID == LastExplosionID)
1764 return;
1766 LastExplosionID = Explosion->ID;
1767 int DistanceSquare = (Pos - Explosion->Pos).GetLengthSquare();
1769 if(DistanceSquare > Explosion->RadiusSquare)
1770 return;
1772 int Damage = Explosion->Strength / (DistanceSquare + 1);
1774 if (Character && (Explosion->HurtNeutrals || (Explosion->Terrorist && Character->GetRelation(Explosion->Terrorist) == HOSTILE))) {
1775 if (Character->IsPlayer()) game::SetPlayerWasHurtByExplosion(true);
1776 else Character->GetHitByExplosion(Explosion, Damage);
1779 GetStack()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, FIRE);
1780 GetStack()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE);
1782 ReceiveTrapDamage(Explosion->Terrorist, Damage >> 1, FIRE);
1783 ReceiveTrapDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE);
1785 if(GetOLTerrain())
1786 GetOLTerrain()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, FIRE);
1788 if(GetOLTerrain())
1789 GetOLTerrain()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE);
1792 int lsquare::GetSpoiledItems() const
1794 return GetStack()->GetSpoiledItems();
1797 truth lsquare::LowerEnchantment(const beamdata& Beam)
1799 character* Char = GetCharacter();
1800 itemvector AllItems;
1801 sortdata SortData(AllItems, Beam.Owner, true, &item::IsEnchantable);
1802 SortAllItems(SortData);
1803 item* RandomItem;
1805 if(!AllItems.empty())
1806 RandomItem = AllItems[RAND() % AllItems.size()];
1807 else
1808 return false;
1810 if(Char)
1812 if(Char->IsPlayer())
1813 ADD_MESSAGE("%s glows blue for a moment!", RandomItem->CHAR_NAME(DEFINITE));
1815 if(Beam.Owner)
1816 Beam.Owner->Hostility(Char);
1819 if(RandomItem->GetEnchantment() > -5)
1820 RandomItem->EditEnchantment(-1);
1822 return true;
1825 void lsquare::SortAllItems(const sortdata& SortData)
1827 if(GetCharacter())
1828 GetCharacter()->SortAllItems(SortData);
1830 GetStack()->SortAllItems(SortData);
1833 truth lsquare::SoftenMaterial (const beamdata &Beam) {
1834 character *Char = GetCharacter();
1835 item *RandomItem;
1836 itemvector AllItems;
1838 sortdata SortData(AllItems, Beam.Owner, true, &item::IsEnchantable);
1839 SortAllItems(SortData);
1840 //sortdata SortData2(AllItems, Beam.Owner, true, &item::MaterialIsChangeable);
1841 //SortAllItems(SortData2);
1842 if (AllItems.empty()) return false;
1843 RandomItem = AllItems[RAND() % AllItems.size()];
1844 if (Char) {
1845 if (Char->IsPlayer()) ADD_MESSAGE("Your %s glows yellow for a moment!", RandomItem->CHAR_NAME(UNARTICLED));
1846 if (Beam.Owner) Beam.Owner->Hostility(Char);
1849 truth Changed = 0;
1850 festring Desc;
1852 RandomItem->AddName(Desc, UNARTICLED);
1853 material *OldMaterial = RandomItem->GetMainMaterial();
1854 int NewMaterial = RandomItem->GetMainMaterial()->GetSoftenedMaterial(RandomItem);
1856 if (NewMaterial != NONE) {
1857 /* Don't Forget! It is an ugly thing, I know, but removal = seg-fault since cannot have NONE material */
1858 RandomItem->ChangeMainMaterial(MAKE_MATERIAL(NewMaterial)); /*->SpawnMore()*/
1859 if (OldMaterial->GetConfig() != NewMaterial) Changed = 1;
1861 if (Char) {
1862 if (Changed && Char->IsPlayer()) {
1863 ADD_MESSAGE("Your %s softens into %s!", Desc.CStr(), RandomItem->GetMainMaterial()->GetName(false, false).CStr());
1864 } else if (Changed) {
1865 ADD_MESSAGE("%s's %s softens into %s!", Char->CHAR_DESCRIPTION(DEFINITE), Desc.CStr(), RandomItem->GetMainMaterial()->GetName(false, false).CStr());
1867 if (!Changed) {
1868 //may not need this message
1869 if (Char->IsPlayer()) {
1870 ADD_MESSAGE("Your %s vibrates slightly but remains unchanged.", RandomItem->CHAR_NAME(UNARTICLED) );
1871 } else {
1872 ADD_MESSAGE("%s's %s vibrates slightly but remains unchanged.", Char->CHAR_DESCRIPTION(DEFINITE), RandomItem->CHAR_NAME(UNARTICLED) );
1876 return true;
1880 void lsquare::RemoveSmoke(smoke* ToBeRemoved)
1882 smoke* S = Smoke;
1884 if(S == ToBeRemoved)
1886 Smoke = S->Next;
1888 if(!S)
1889 DecAnimatedEntities();
1891 else
1893 smoke* LS;
1897 LS = S;
1898 S = S->Next;
1900 while(S != ToBeRemoved);
1902 LS->Next = S->Next;
1906 void lsquare::AddSmoke(gas* ToBeAdded)
1908 smoke* S = Smoke;
1910 if(!S)
1912 Smoke = new smoke(ToBeAdded, this);
1913 IncAnimatedEntities();
1915 else
1917 smoke* LS;
1921 if(ToBeAdded->IsSameAs(S->GetGas()))
1923 S->Merge(ToBeAdded);
1924 return;
1927 LS = S;
1928 S = S->Next;
1930 while(S);
1932 LS->Next = new smoke(ToBeAdded, this);
1936 void lsquare::ShowSmokeMessage() const
1938 for(const smoke* S = Smoke; S; S = S->Next)
1939 S->AddBreatheMessage();
1942 void lsquare::SignalSmokeAlphaChange(int What)
1944 SmokeAlphaSum += What;
1945 SignalPossibleTransparencyChange();
1948 int lsquare::GetDivineMaster() const
1950 return RoomIndex ? GetLevel()->GetRoom(RoomIndex)->GetDivineMaster() : 0;
1953 void lsquare::DisplaySmokeInfo (festring &Msg) const {
1954 if (Smoke) {
1955 if (!Smoke->Next)
1956 Msg << " A cloud of " << Smoke->GetGas()->GetName(false, false) << " surrounds the square.";
1957 else
1958 Msg << " A lot of gases hover over the square.";
1962 void lsquare::ReceiveEarthQuakeDamage()
1964 GetStack()->ReceiveDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE);
1965 ReceiveTrapDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE);
1966 /* Gum solution */
1968 if(GetOLTerrain() && GetOLTerrain()->IsDoor())
1969 GetOLTerrain()->ReceiveDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE);
1972 truth lsquare::CanBeFeltByPlayer() const
1974 if (!PLAYER) return false;
1975 return OLTerrain && !PLAYER->CanMoveOn(this) && Pos.IsAdjacent(PLAYER->GetPos());
1978 void lsquare::PreProcessForBone()
1980 DestroyMemorized();
1981 LastSeen = 0;
1983 if(OLTerrain)
1984 OLTerrain->PreProcessForBone();
1986 if(Smoke)
1988 DecAnimatedEntities();
1990 for(smoke* S = Smoke; S; S = S->Next)
1991 S->SendToHell();
1993 Smoke = 0;
1994 SmokeAlphaSum = 0;
1997 if(Character && !Character->PreProcessForBone())
1999 Character->SendToHell();
2000 Character->Remove();
2003 for(fluid* F = Fluid; F; F = F->Next)
2004 F->PreProcessForBone();
2006 for(trap* T = Trap; T; T = T->Next)
2007 T->PreProcessForBone();
2009 GetStack()->PreProcessForBone();
2012 void lsquare::PostProcessForBone(double& DangerSum, int& Enemies)
2014 if(OLTerrain)
2015 OLTerrain->PostProcessForBone();
2017 if(Character && !Character->PostProcessForBone(DangerSum, Enemies))
2019 Character->SendToHell();
2020 Character->Remove();
2023 for(fluid* F = Fluid; F; F = F->Next)
2024 F->PostProcessForBone();
2026 for(trap* T = Trap; T; T = T->Next)
2027 T->PostProcessForBone();
2029 GetStack()->PostProcessForBone();
2032 void lsquare::FinalProcessForBone()
2034 if(OLTerrain)
2035 OLTerrain->FinalProcessForBone();
2037 if(Character)
2038 Character->FinalProcessForBone();
2040 GetStack()->FinalProcessForBone();
2043 truth lsquare::EngravingsCanBeReadByPlayer()
2045 return PLAYER->CanRead(); // Might be a good idea to improve sometime in the distant future.
2048 void lsquare::DisplayEngravedInfo(festring& Buffer) const
2050 Buffer << " There is a message engraved here: \"" << Engraved << '\"';
2053 truth lsquare::IsDangerousToBreathe(ccharacter* Who) const
2055 for(const smoke* S = Smoke; S; S = S->Next)
2056 if(S->IsDangerousToBreathe(Who))
2057 return true;
2059 return false;
2062 truth lsquare::IsScaryToBreathe(ccharacter* Who) const
2064 for(const smoke* S = Smoke; S; S = S->Next)
2065 if(S->IsScaryToBreathe(Who))
2066 return true;
2068 return false;
2072 struct groundborderpartner {
2073 truth operator < (const groundborderpartner &P) const { return Terrain->GetBorderTilePriority() < P.Terrain->GetBorderTilePriority(); }
2074 glterrain *Terrain;
2075 int SquareIndex;
2078 void lsquare::CalculateGroundBorderPartners () {
2079 if (GroundBorderPartnerInfo & BORDER_PARTNER_ANIMATED) DecStaticAnimatedEntities();
2080 groundborderpartner BorderPartner[8*2]; //k8: *2 to make g++ shut up (WTF?!)
2081 int Index = 0;
2082 int Priority = GLTerrain->GetBorderTilePriority();
2083 for (int d = 0; d < 8; ++d) {
2084 lsquare *Square = NeighbourLSquare[d];
2085 if (Square) {
2086 glterrain *Terrain = Square->GetGLTerrain();
2087 if (Terrain && Terrain->UseBorderTiles() && Terrain->GetBorderTilePriority() > Priority) {
2088 BorderPartner[Index].Terrain = Terrain;
2089 BorderPartner[Index].SquareIndex = 7-d;
2090 ++Index;
2094 GroundBorderPartnerInfo = 0;
2095 if (!Index) {
2096 delete [] GroundBorderPartnerTerrain;
2097 GroundBorderPartnerTerrain = 0;
2098 return;
2100 if (!GroundBorderPartnerTerrain) GroundBorderPartnerTerrain = new glterrain *[8];
2101 std::sort(BorderPartner, BorderPartner+Index); // why g++ complains here? ah, ignore it for now
2102 truth Animated = false;
2103 for (int c = 0; c < Index; ++c) {
2104 glterrain *T = BorderPartner[c].Terrain;
2105 GroundBorderPartnerTerrain[c] = T;
2106 GroundBorderPartnerInfo |= BorderPartner[c].SquareIndex<<((c<<1)+c);
2107 if (T->IsAnimated()) Animated = true;
2109 if (Animated) {
2110 GroundBorderPartnerInfo |= BORDER_PARTNER_ANIMATED;
2111 IncStaticAnimatedEntities();
2113 GroundBorderPartnerInfo |= Index<<24;
2117 struct overborderpartner {
2118 truth operator < (const overborderpartner &P) const { return Terrain->GetBorderTilePriority() < P.Terrain->GetBorderTilePriority(); }
2119 olterrain *Terrain;
2120 int SquareIndex;
2123 void lsquare::CalculateOverBorderPartners () {
2124 if (OverBorderPartnerInfo & BORDER_PARTNER_ANIMATED) DecStaticAnimatedEntities();
2125 overborderpartner BorderPartner[8*2]; //k8: *2 to make g++ shut up (WTF?!)
2126 int Index = 0;
2127 int Priority = OLTerrain ? OLTerrain->GetBorderTilePriority() : 0;
2128 for (int d = 0; d < 8; ++d) {
2129 lsquare *Square = NeighbourLSquare[d];
2130 if (Square) {
2131 olterrain *Terrain = Square->GetOLTerrain();
2132 if (Terrain && Terrain->UseBorderTiles() && Terrain->GetBorderTilePriority() > Priority) {
2133 BorderPartner[Index].Terrain = Terrain;
2134 BorderPartner[Index].SquareIndex = 7-d;
2135 ++Index;
2139 OverBorderPartnerInfo = 0;
2140 if (!Index) {
2141 delete [] OverBorderPartnerTerrain;
2142 OverBorderPartnerTerrain = 0;
2143 return;
2145 if (!OverBorderPartnerTerrain) OverBorderPartnerTerrain = new olterrain *[8];
2146 std::sort(BorderPartner, BorderPartner+Index); // why g++ complains here? ah, ignore it for now
2147 truth Animated = false;
2148 for (int c = 0; c < Index; ++c) {
2149 olterrain *T = BorderPartner[c].Terrain;
2150 OverBorderPartnerTerrain[c] = T;
2151 OverBorderPartnerInfo |= BorderPartner[c].SquareIndex<<((c<<1)+c);
2152 if (T->IsAnimated()) Animated = true;
2154 if (Animated) {
2155 OverBorderPartnerInfo |= BORDER_PARTNER_ANIMATED;
2156 IncStaticAnimatedEntities();
2158 OverBorderPartnerInfo |= Index<<24;
2159 /*k8
2160 if(OverBorderPartnerInfo & BORDER_PARTNER_ANIMATED)
2161 int esko = esko = 2;
2166 void lsquare::RequestForGroundBorderPartnerUpdates()
2168 if(!game::IsGenerating())
2169 for(int d = 0; d < 8; ++d)
2171 lsquare* Square = NeighbourLSquare[d];
2173 if(Square)
2175 Square->CalculateGroundBorderPartners();
2176 Square->SendNewDrawRequest();
2177 Square->SendMemorizedUpdateRequest();
2182 void lsquare::RequestForOverBorderPartnerUpdates()
2184 if(!game::IsGenerating())
2185 for(int d = 0; d < 8; ++d)
2187 lsquare* Square = NeighbourLSquare[d];
2189 if(Square)
2191 Square->CalculateOverBorderPartners();
2192 Square->SendNewDrawRequest();
2193 Square->SendMemorizedUpdateRequest();
2198 int lsquare::GetWalkability() const
2200 if(!GetLevel()->IsOnGround())
2202 if(Pos.X >= 1 && Pos.Y >= 1 && Pos.X < GetLevel()->GetXSize() - 1 && Pos.Y < GetLevel()->GetYSize() - 1)
2203 return OLTerrain ? OLTerrain->GetWalkability() & GLTerrain->GetWalkability() : GLTerrain->GetWalkability();
2204 else
2205 return 0;
2207 else
2208 return OLTerrain ? OLTerrain->GetWalkability() & GLTerrain->GetWalkability() : GLTerrain->GetWalkability();
2211 void lsquare::RemoveFluid(fluid* ToRemove)
2213 fluid*& F = ListFind(Fluid, pointercomparer<fluid>(ToRemove));
2214 F = F->Next;
2215 SignalEmitationDecrease(ToRemove->GetEmitation());
2218 struct fluidcomparer
2220 fluidcomparer(const liquid* Liquid) : Liquid(Liquid) { }
2221 truth operator()(const fluid* F) const { return Liquid->IsSameAs(F->GetLiquid()); }
2222 const liquid* Liquid;
2225 fluid* lsquare::AddFluid(liquid* ToBeAdded)
2227 fluid*& F = ListFind(Fluid, fluidcomparer(ToBeAdded));
2229 if(F)
2231 F->AddLiquidAndVolume(ToBeAdded->GetVolume());
2232 delete ToBeAdded;
2234 else
2236 F = new fluid(ToBeAdded, this);
2237 SignalEmitationIncrease(ToBeAdded->GetEmitation());
2240 SendNewDrawRequest();
2241 SendMemorizedUpdateRequest();
2242 return F;
2245 void lsquare::DisplayFluidInfo(festring& Msg) const
2247 if(Fluid)
2249 Msg << ". There is ";
2250 fluid::AddFluidInfo(Fluid, Msg);
2251 AddLocationDescription(Msg);
2255 void lsquare::SpillFluid(character* Spiller, liquid* Liquid, truth ForceHit, truth ShowMsg)
2257 if(!Liquid->GetVolume())
2259 delete Liquid;
2260 return;
2263 if(IsFlyable())
2265 if(GetCharacter())
2267 if(Spiller && !GetCharacter()->IsAlly(Spiller))
2268 Spiller->Hostility(GetCharacter());
2270 sLong CharVolume = GetCharacter()->GetVolume();
2271 double ChanceMultiplier = 1. / (300 + sqrt(GetStack()->GetVolume() + CharVolume));
2272 double Root = sqrt(CharVolume);
2274 if(ForceHit || Root > RAND() % 400 || Root > RAND() % 400)
2276 sLong SpillVolume = sLong(Liquid->GetVolume() * Root * ChanceMultiplier);
2278 if(SpillVolume)
2280 if(ShowMsg && (GetCharacter()->IsPlayer() || GetCharacter()->CanBeSeenByPlayer()))
2281 ADD_MESSAGE("%s is spilled all over %s.", Liquid->GetName(false, false).CStr(), GetCharacter()->CHAR_DESCRIPTION(DEFINITE));
2283 Liquid->EditVolume(-SpillVolume);
2284 GetCharacter()->SpillFluid(Spiller, Liquid->SpawnMoreLiquid(SpillVolume), GetCharacter()->GetSquareIndex(GetPos()));
2289 GetStack()->SpillFluid(Spiller, Liquid, Liquid->GetVolume());
2292 if(Liquid->GetVolume() && !Liquid->IsSameAs(GLTerrain->GetMainMaterial()))
2294 fluid* F = AddFluid(Liquid);
2296 if(GetCharacter())
2297 F->StepOnEffect(GetCharacter());
2299 else
2300 delete Liquid;
2303 void lsquare::DrawStacks(blitdata& BlitData) const
2305 Stack->Draw(PLAYER, BlitData, CENTER);
2307 for(int c = 0; c < 4; ++c)
2309 stack* Stack = GetStackOfAdjacentSquare(c);
2311 if(Stack)
2312 Stack->Draw(PLAYER, BlitData, 3 - c);
2316 void lsquare::RemoveRain(rain* ToBeRemoved)
2318 SendNewDrawRequest();
2319 rain* R = Rain;
2321 if(ToBeRemoved->IsEnabled())
2322 DecAnimatedEntities();
2324 if(R == ToBeRemoved)
2325 Rain = R->Next;
2326 else
2328 rain* LR;
2332 LR = R;
2333 R = R->Next;
2335 while(R != ToBeRemoved);
2337 LR->Next = R->Next;
2340 SignalEmitationDecrease(ToBeRemoved->GetEmitation());
2343 void lsquare::AddRain(liquid* RainLiquid, v2 Speed, int Team, truth OwnLiquid)
2345 rain* R = Rain, * NewRain = new rain(RainLiquid, this, Speed, Team, OwnLiquid);
2347 if(NewRain->IsEnabled())
2348 IncAnimatedEntities();
2350 if(!R)
2351 Rain = NewRain;
2352 else
2354 rain* LR;
2358 LR = R;
2359 R = R->Next;
2361 while(R);
2363 LR->Next = NewRain;
2367 void lsquare::RemoveSunLight()
2369 SunLightLuminance = 0;
2370 SunEmitter.clear();
2373 void lsquare::CheckIfIsSecondarySunLightEmitter()
2375 col24 OldEmitation = SecondarySunLightEmitation;
2377 if(Flags & IS_TRANSPARENT && (!(Flags & INSIDE) || SunLightLuminance))
2378 for(int d = 0; d < 8; ++d)
2380 lsquare* Neighbour = NeighbourLSquare[d];
2382 if(Neighbour && Neighbour->Flags & INSIDE)
2384 col24 NewEmitation = GetLevel()->GetAmbientLuminance();
2386 if(OldEmitation != NewEmitation)
2388 SecondarySunLightEmitation = NewEmitation;
2390 if(game::CompareLights(NewEmitation, OldEmitation) >= 0)
2391 Emitate(NewEmitation, SECONDARY_SUN_LIGHT);
2392 else
2394 Noxify(OldEmitation, SECONDARY_SUN_LIGHT);
2395 Emitate(NewEmitation, SECONDARY_SUN_LIGHT|FORCE_ADD);
2399 return;
2403 if(OldEmitation)
2405 Noxify(OldEmitation, SECONDARY_SUN_LIGHT);
2406 SecondarySunLightEmitation = 0;
2410 void lsquare::CalculateNeighbourLSquares()
2412 int XSize = GetLevel()->GetXSize();
2413 int YSize = GetLevel()->GetYSize();
2415 for(int d = 0; d < 8; ++d)
2417 v2 NPos = Pos + game::GetMoveVector(d);
2419 if(NPos.X >= 0 && NPos.Y >= 0 && NPos.X < XSize && NPos.Y < YSize)
2420 NeighbourLSquare[d] = GetLevel()->GetLSquare(NPos);
2421 else
2422 NeighbourLSquare[d] = 0;
2426 void lsquare::RemoveLuminance(col24& Emitation)
2428 col24 OldLuminance = Luminance;
2429 col24 OldEmitation = Emitation;
2430 Emitation = 0;
2432 if(game::CompareLights(OldEmitation, OldLuminance) < 0)
2433 return;
2435 if(!(Flags & IS_TRANSPARENT))
2437 Flags |= NEW_DRAW_REQUEST;
2439 if(LastSeen == game::GetLOSTick())
2440 game::SendLOSUpdateRequest();
2442 else
2444 CalculateLuminance();
2446 if(OldLuminance == Luminance)
2447 return;
2449 Flags |= NEW_DRAW_REQUEST;
2451 if(!Luminance)
2453 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
2455 if(LastSeen == game::GetLOSTick())
2456 game::SendLOSUpdateRequest();
2461 void lsquare::ChangeLuminance(col24& Emitation, col24 NewLuminance)
2463 col24 OldLuminance = Luminance;
2465 if(!(Flags & IS_TRANSPARENT))
2467 Emitation = NewLuminance;
2468 Flags |= NEW_DRAW_REQUEST;
2470 if(LastSeen == game::GetLOSTick())
2471 game::SendLOSUpdateRequest();
2473 return;
2476 truth EmitationInsignificant = !Emitation
2477 || game::CompareLights(Emitation, OldLuminance) < 0;
2478 Emitation = NewLuminance;
2480 if(game::CompareLights(NewLuminance, OldLuminance) > 0
2481 && EmitationInsignificant)
2482 game::CombineLights(Luminance, NewLuminance);
2483 else
2485 if(EmitationInsignificant)
2486 return;
2488 CalculateLuminance();
2490 if(OldLuminance == Luminance)
2491 return;
2494 Flags |= NEW_DRAW_REQUEST;
2496 if(!OldLuminance)
2498 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
2500 if(LastSeen == game::GetLOSTick())
2501 game::SendLOSUpdateRequest();
2505 void lsquare::EnableGlobalRain()
2507 for(rain* R = Rain; R; R = R->Next)
2508 if(!R->HasOwnLiquid())
2510 R->Enable();
2511 IncAnimatedEntities();
2515 void lsquare::DisableGlobalRain()
2517 SendNewDrawRequest();
2519 for(rain* R = Rain; R; R = R->Next)
2520 if(!R->HasOwnLiquid())
2522 R->Disable();
2523 DecAnimatedEntities();
2527 void lsquare::InitLastSeen()
2529 LastSeen = LastSeen == game::GetLOSTick() ? 2 : 0;
2530 SquarePartLastSeen = 0;
2533 truth lsquare::Engrave(cfestring& What)
2535 if(Engraved)
2536 delete [] Engraved;
2538 if(!What.IsEmpty())
2540 Engraved = new char[What.GetSize() + 1];
2541 strcpy(Engraved, What.CStr());
2543 else
2544 Engraved = 0;
2546 return true;
2549 void lsquare::SendSunLightSignals()
2551 if(Flags & IS_TRANSPARENT)
2553 col24 OldLuminance = Luminance;
2554 CalculateLuminance();
2556 if(Luminance != OldLuminance)
2558 Flags |= NEW_DRAW_REQUEST;
2560 if(!Luminance != !OldLuminance)
2562 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
2564 if(LastSeen == game::GetLOSTick())
2565 game::SendLOSUpdateRequest();
2569 else
2571 Flags |= NEW_DRAW_REQUEST;
2573 if(LastSeen == game::GetLOSTick())
2574 game::SendLOSUpdateRequest();
2578 void lsquare::ZeroReSunEmitatedFlags()
2580 sunemittervector::iterator i, End = SunEmitter.end();
2582 for(i = SunEmitter.begin(); i != End; ++i)
2583 *i &= ~RE_SUN_EMITATED;
2586 truth lsquare::CalculateIsTransparent()
2588 if((!OLTerrain || OLTerrain->IsTransparent()) && SmokeAlphaSum < 175
2589 && (!Character || Character->IsTransparent()))
2591 Flags |= IS_TRANSPARENT;
2592 return true;
2594 else
2596 Flags &= ~IS_TRANSPARENT;
2597 return false;
2601 void lsquare::CalculateSunLightLuminance(feuLong SeenBitMask)
2603 sunemittervector::const_iterator i, SunEnd = SunEmitter.end();
2604 int S = 0, L = 0;
2606 for(i = SunEmitter.begin(); i != SunEnd; ++i)
2608 feuLong ShadowFlag = 1 << EMITTER_SHADOW_SHIFT;
2609 feuLong SquarePartFlag = 1 << EMITTER_SQUARE_PART_SHIFT;
2610 for (int c = 0; c < 4; ++c, ShadowFlag <<= 1, SquarePartFlag <<= 1) {
2611 if (SeenBitMask & *i & SquarePartFlag) {
2612 if (*i & ShadowFlag) ++S; else ++L;
2617 if(!L)
2618 SunLightLuminance = 0;
2619 else if(!S)
2620 SunLightLuminance = GetLevel()->GetSunLightEmitation();
2621 else
2623 col24 ShadowColor = GetLevel()->GetAmbientLuminance();
2624 col24 LightColor = GetLevel()->GetSunLightEmitation();
2625 SunLightLuminance = MakeRGB24((GetRed24(LightColor) * L
2626 + GetRed24(ShadowColor) * S) / (S + L),
2627 (GetGreen24(LightColor) * L
2628 + GetGreen24(ShadowColor) * S) / (S + L),
2629 (GetBlue24(LightColor) * L
2630 + GetBlue24(ShadowColor) * S) / (S + L));
2634 void lsquare::CreateMemorized()
2636 Memorized = new bitmap(TILE_V2);
2637 Memorized->ActivateFastFlag();
2638 FowMemorized = new bitmap(TILE_V2);
2639 FowMemorized->ActivateFastFlag();
2642 truth lsquare::AcidRain(const beamdata& Beam)
2644 if(!IsFlyable() || GetCharacter() || Beam.Direction == YOURSELF)
2646 int StackSize = GetLevel()->AddRadiusToSquareStack(Pos, 9);
2647 lsquare** Stack = GetLevel()->GetSquareStack();
2648 v2 Speed = v2(512, 512);
2649 int Team = Beam.Owner ? Beam.Owner->GetTeam()->GetID() : MONSTER_TEAM;
2651 for(int c = 0; c < StackSize; ++c)
2653 Stack[c]->AddRain(liquid::Spawn(SULPHURIC_ACID, 300), Speed, Team, true);
2654 Stack[c]->Flags &= ~IN_SQUARE_STACK;
2657 if(Beam.Owner && Character && Character->GetTeam() != Beam.Owner->GetTeam())
2658 Beam.Owner->Hostility(Character);
2660 return true;
2663 return false;
2666 truth lsquare::DetectMaterial (cmaterial *Material) const {
2667 if (GLTerrain->DetectMaterial(Material) ||
2668 (OLTerrain && OLTerrain->DetectMaterial(Material)) ||
2669 Stack->DetectMaterial(Material) ||
2670 (Character && Character->DetectMaterial(Material))) return true;
2671 for (const fluid *F = Fluid; F; F = F->Next) if (F->GetLiquid()->IsSameAs(Material)) return true;
2672 for (const smoke *S = Smoke; S; S = S->Next) if (S->GetGas()->IsSameAs(Material)) return true;
2673 for (const rain *R = Rain; R; R = R->Next) if (R->GetLiquid()->IsSameAs(Material)) return true;
2674 return false;
2677 void lsquare::Reveal(feuLong Tick, truth IgnoreDarkness)
2679 if(!Memorized)
2680 CreateMemorized();
2682 LastSeen = Tick;
2684 if(IgnoreDarkness)
2685 Luminance = NORMAL_LUMINANCE;
2686 else
2688 SquarePartLastSeen = 0;
2690 for(int c = 0; c < 4; ++c)
2691 SquarePartLastSeen |= (Tick << (c << 3));
2693 CalculateLuminance();
2696 Flags |= NEW_DRAW_REQUEST
2697 | MEMORIZED_UPDATE_REQUEST
2698 | DESCRIPTION_CHANGE;
2699 UpdateMemorized();
2700 UpdateMemorizedDescription();
2703 void lsquare::DestroyMemorized()
2705 delete Memorized;
2706 delete FowMemorized;
2707 Memorized = 0;
2708 FowMemorized = 0;
2711 void lsquare::SwapMemorized(lsquare* Square)
2713 Swap(Memorized, Square->Memorized);
2714 Swap(FowMemorized, Square->FowMemorized);
2715 MemorizedDescription.SwapData(Square->MemorizedDescription);
2718 truth lsquare::Necromancy(const beamdata& Beam)
2720 return GetStack()->Necromancy(Beam.Owner);
2723 // Returns 0 if fails
2725 lsquare* lsquare::GetRandomAdjacentSquare() const
2727 lsquare* OK[8];
2728 int Index = 0;
2730 for(int c = 0; c < 8; ++c)
2732 lsquare* Square = NeighbourLSquare[c];
2734 if(Square)
2735 OK[Index++] = Square;
2738 if(Index)
2739 return OK[RAND_N(Index)];
2740 else
2741 return 0;
2744 truth pathcontroller::Handler(int x, int y)
2746 return Character->CanMoveOn(Map[x][y]);
2749 void lsquare::SignalPossibleTransparencyChange()
2751 truth WasTransparent = IsTransparent();
2752 CalculateIsTransparent();
2754 if(WasTransparent && !IsTransparent())
2756 Flags |= IS_TRANSPARENT;
2757 emittervector EmitterBackup = Emitter;
2758 GetLevel()->ForceEmitterNoxify(EmitterBackup);
2759 Flags &= ~IS_TRANSPARENT;
2760 GetLevel()->ForceEmitterEmitation(EmitterBackup, SunEmitter, FORCE_ADD);
2761 CalculateLuminance();
2762 Flags |= DESCRIPTION_CHANGE|MEMORIZED_UPDATE_REQUEST;
2764 if(LastSeen == game::GetLOSTick())
2765 game::SendLOSUpdateRequest();
2767 else if(!WasTransparent && IsTransparent())
2769 GetLevel()->ForceEmitterEmitation(Emitter, SunEmitter);
2770 CalculateLuminance();
2771 Flags |= DESCRIPTION_CHANGE|MEMORIZED_UPDATE_REQUEST;
2773 if(LastSeen == game::GetLOSTick())
2774 game::SendLOSUpdateRequest();
2778 void lsquare::RemoveTrap(trap* ToRemove)
2780 trap*& T = ListFind(Trap, pointercomparer<trap>(ToRemove));
2781 T = T->Next;
2782 SendNewDrawRequest();
2783 SendMemorizedUpdateRequest();
2786 struct trapcomparer
2788 trapcomparer(int Type) : Type(Type) { }
2789 truth operator()(const trap* T) const { return T->GetType() == Type; }
2790 int Type;
2793 truth lsquare::AddTrap(trap* ToBeAdded)
2795 trap*& T = ListFind(Trap, trapcomparer(ToBeAdded->GetType()));
2797 if(T)
2799 delete ToBeAdded;
2800 return false;
2802 else
2803 T = ToBeAdded;
2805 ToBeAdded->SetLSquareUnder(this);
2806 SendNewDrawRequest();
2807 SendMemorizedUpdateRequest();
2808 return true;
2811 void lsquare::DisplayTrapInfo(festring& Msg) const
2813 for(const trap* T = Trap; T; T = T->Next)
2814 T->AddDescription(Msg);
2817 void lsquare::FillTrapVector(std::vector<trap*>& TrapVector) const
2819 for(trap* T = Trap; T; T = T->Next)
2820 TrapVector.push_back(T);
2823 void lsquare::ReceiveTrapDamage(character* Damager, int Damage, int Type, int Direction)
2825 std::vector<trap*> TrapVector;
2826 FillTrapVector(TrapVector);
2828 for(uInt c = 0; c < TrapVector.size(); ++c)
2829 TrapVector[c]->ReceiveDamage(Damager, Damage, Type, Direction);
2832 truth lsquare::HasDangerousTraps(ccharacter* Who) const
2834 for(trap* T = Trap; T; T = T->Next)
2835 if(T->IsDangerous(Who))
2836 return true;
2838 return false;
2841 truth lsquare::HasDangerousFluids(ccharacter* Who) const
2843 for(const fluid* F = Fluid; F; F = F->Next)
2844 if(F->IsDangerous(Who))
2845 return true;
2847 return false;
2850 truth lsquare::HasNoBorderPartners() const
2852 return !(GroundBorderPartnerInfo >> 24) && !(OverBorderPartnerInfo >> 24);
2855 void lsquare::AddLocationDescription(festring& String) const
2857 if(IsFlyable())
2858 GLTerrain->AddLocationDescription(String);
2859 else
2860 OLTerrain->AddLocationDescription(String);
2863 truth lsquare::VomitingIsDangerous(ccharacter* Char) const
2865 return ((OLTerrain && OLTerrain->VomitingIsDangerous(Char))
2866 || (Character && Character->GetTeam() != Char->GetTeam()
2867 && Character->GetRelation(Char) != HOSTILE));
2870 bool lsquare::TeleportAllSmokeAway()
2872 return false;
2875 bool lsquare::TeleportAllFluidsAway()
2877 return false;
2880 bool lsquare::TeleportAllTrapsAway()
2882 for(trap* T = Trap; T; T = Trap)
2884 T->Untrap();
2885 RemoveTrap(T);
2886 v2 V, Pos = GetPos();
2887 for(V = GetLevel()->GetRandomSquare(); V != Pos; V = GetLevel()->GetRandomSquare());
2888 GetNearLSquare(V)->AddTrap(T);
2891 return false;
2894 void lsquare::AddSpecialCursors()
2896 if((FowMemorized || game::GetSeeWholeMapCheatMode()) && OLTerrain)
2897 OLTerrain->AddSpecialCursors();