'g'o should not miss items in corners anymore
[k8-i-v-a-n.git] / src / game / lsquare.cpp
blob4896de93a40468e4d5934042e19cc371a1c92eb0
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
12 /* Compiled through levelset.cpp */
14 lsquare*** eyecontroller::Map;
16 lsquare*** pathcontroller::Map;
17 ccharacter* pathcontroller::Character;
19 lsquare*** stackcontroller::Map;
20 lsquare** stackcontroller::Stack;
21 sLong stackcontroller::StackIndex;
22 int stackcontroller::LevelXSize, stackcontroller::LevelYSize;
23 v2 stackcontroller::Center;
25 feuLong tickcontroller::Tick;
26 feuLong tickcontroller::ShiftedTick[4];
27 feuLong tickcontroller::ShiftedQuadriTick[4];
29 void tickcontroller::PrepareShiftedTick()
31 for(int c = 0; c < 4; ++c)
33 ShiftedTick[c] = Tick << (c << 3);
34 ShiftedQuadriTick[c] = (Tick + 1) << (c << 3);
38 truth lsquare::IsDipDestination() const { return GLTerrain->IsDipDestination() || (OLTerrain && OLTerrain->IsDipDestination()); }
40 lsquare::lsquare (level *LevelUnder, v2 Pos) :
41 square(LevelUnder, Pos),
42 Fluid(0), Smoke(0), Rain(0), Trap(0),
43 GLTerrain(0), OLTerrain(0),
44 Memorized(0), FowMemorized(0),
45 Engraved(0),
46 GroundBorderPartnerTerrain(0),
47 GroundBorderPartnerInfo(0),
48 OverBorderPartnerTerrain(0),
49 OverBorderPartnerInfo(0),
50 SquarePartEmitationTick(0),
51 SquarePartLastSeen(0),
52 Emitation(0),
53 SmokeAlphaSum(0),
54 AmbientLuminance(0),
55 SunLightLuminance(0),
56 TemporaryEmitation(0),
57 SecondarySunLightEmitation(0),
58 LastExplosionID(0),
59 RoomIndex(0)
61 Stack = new stack(this, 0);
65 lsquare::~lsquare () {
66 delete GLTerrain;
67 delete OLTerrain;
68 delete Stack;
69 delete [] Engraved;
71 deleteList(Fluid);
73 delete Memorized;
74 delete FowMemorized;
75 delete StaticContentCache.Bitmap;
76 delete [] GroundBorderPartnerTerrain;
77 delete [] OverBorderPartnerTerrain;
79 deleteList(Smoke);
80 deleteList(Rain);
81 deleteList(Trap);
85 void lsquare::SignalEmitationIncrease (col24 EmitationUpdate) {
86 if (game::CompareLights(EmitationUpdate, Emitation) > 0 && !game::IsGenerating() && !(Flags & FREEZED)) {
87 CalculateEmitation(); // could this be optimized?
88 Emitate();
93 void lsquare::SignalEmitationDecrease (col24 EmitationUpdate) {
94 if (game::CompareLights(EmitationUpdate, Emitation) >= 0 && Emitation && !game::IsGenerating() && !(Flags & FREEZED)) {
95 col24 Backup = Emitation;
96 CalculateEmitation();
97 if (Backup != Emitation) {
98 Noxify(Backup);
99 Emitate(Emitation, FORCE_ADD);
105 void lsquare::CalculateEmitation () {
106 Emitation = Stack->GetEmitation();
108 for (int c = 0; c < 4; ++c) {
109 stack *Stack = GetStackOfAdjacentSquare(c);
110 if (Stack) game::CombineLights(Emitation, Stack->GetSideEmitation(3 - c));
113 if (Character) game::CombineLights(Emitation, Character->GetEmitation());
114 game::CombineLights(Emitation, GLTerrain->GetEmitation());
115 if (OLTerrain) game::CombineLights(Emitation, OLTerrain->GetEmitation());
116 game::CombineLights(Emitation, TemporaryEmitation);
117 for (const fluid* F = Fluid; F; F = F->Next) game::CombineLights(Emitation, F->GetEmitation());
118 for (const rain* R = Rain; R; R = R->Next) game::CombineLights(Emitation, R->GetEmitation());
122 void lsquare::UpdateMemorized () {
123 if (Flags&MEMORIZED_UPDATE_REQUEST) {
124 if (!IsDark() || CanBeFeltByPlayer()) {
125 blitdata B = { Memorized,
126 { 0, 0 },
127 { 0, 0 },
128 { TILE_SIZE, TILE_SIZE },
129 { NORMAL_LUMINANCE },
130 TRANSPARENT_COLOR,
131 ALLOW_ALPHA };
132 DrawStaticContents(B);
133 Memorized->FastBlit(FowMemorized);
134 B.Bitmap = FowMemorized;
135 B.Flags = 0;
136 B.MaskColor = 0;
137 igraph::GetFOWGraphic()->NormalMaskedBlit(B);
138 } else {
139 Memorized->ClearToColor(0);
140 igraph::GetFOWGraphic()->FastBlit(FowMemorized);
143 if (!StaticContentCache.Bitmap) {
144 StaticContentCache.Bitmap = new bitmap(TILE_V2);
145 StaticContentCache.Bitmap->ActivateFastFlag();
148 UpdateStaticContentCache(Luminance);
149 Flags &= ~MEMORIZED_UPDATE_REQUEST;
154 void lsquare::UpdateStaticContentCache (col24 Luminance) const {
155 blitdata B = { StaticContentCache.Bitmap,
156 { 0, 0 },
157 { 0, 0 },
158 { TILE_SIZE, TILE_SIZE },
159 { Luminance },
161 0 };
162 Memorized->LuminanceBlit(B);
163 StaticContentCache.Luminance = Luminance;
167 void lsquare::DrawStaticContents (blitdata &BlitData) const {
168 if ((BlitData.CustomData&ALLOW_ANIMATE) && !StaticAnimatedEntities && Memorized && !game::GetSeeWholeMapCheatMode()) {
169 if (StaticContentCache.Luminance != BlitData.Luminance) UpdateStaticContentCache(BlitData.Luminance);
170 StaticContentCache.Bitmap->FastBlit(BlitData.Bitmap, BlitData.Dest);
171 return;
174 if (!OLTerrain || OLTerrain->ShowThingsUnder()) GLTerrain->Draw(BlitData);
176 int GroundPartners = (GroundBorderPartnerInfo>>24)&15;
178 for (int c = 0; c < GroundPartners; ++c) {
179 BlitData.CustomData |= 8-((GroundBorderPartnerInfo>>((c<<1)+c))&7);
180 GroundBorderPartnerTerrain[c]->Draw(BlitData);
181 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
184 truth StackDrawn = false;
186 if (OLTerrain && !IsFlyable()) {
187 if (OLTerrain->IsTransparent() && OLTerrain->ShowThingsUnder()) {
188 StackDrawn = true;
189 DrawStacks(BlitData);
191 OLTerrain->Draw(BlitData);
194 for (const fluid* F = Fluid; F; F = F->Next) F->SimpleDraw(BlitData);
195 if (OLTerrain && IsFlyable()) OLTerrain->Draw(BlitData);
196 if (!StackDrawn && Flags & IS_TRANSPARENT) DrawStacks(BlitData);
197 for (const trap* T = Trap; T; T = T->Next) T->Draw(BlitData);
199 int OverPartners = OverBorderPartnerInfo >> 24 & 15;
201 for (int c = 0; c < OverPartners; ++c) {
202 BlitData.CustomData |= 8-((OverBorderPartnerInfo>>((c<<1)+c))&7);
203 OverBorderPartnerTerrain[c]->Draw(BlitData);
204 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
209 void lsquare::Draw (blitdata& BlitData) const {
210 if ((Flags&NEW_DRAW_REQUEST) || AnimatedEntities) {
211 if (!IsDark() || game::GetSeeWholeMapCheatMode()) {
212 if (game::GetSeeWholeMapCheatMode() == SHOW_MAP_IN_UNIFORM_LIGHT || (game::GetSeeWholeMapCheatMode() && !(Flags & IS_TRANSPARENT)))
213 BlitData.Luminance = ivanconfig::GetContrastLuminance();
214 else
215 BlitData.Luminance = ivanconfig::ApplyContrastTo(Luminance);
217 DrawStaticContents(BlitData);
219 if (Character && (Character->CanBeSeenByPlayer() || game::GetSeeWholeMapCheatMode())) {
220 BlitData.CustomData |= Character->GetSquareIndex(Pos);
222 if (Character->IsFlying()) {
223 for (const smoke *S = Smoke; S; S = S->Next) S->Draw(BlitData);
224 Character->Draw(BlitData);
225 } else {
226 Character->Draw(BlitData);
227 for (const smoke *S = Smoke; S; S = S->Next) S->Draw(BlitData);
229 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
230 } else {
231 for(const smoke *S = Smoke; S; S = S->Next) S->Draw(BlitData);
234 for (const rain *R = Rain; R; R = R->Next) if (R->IsEnabled()) R->Draw(BlitData);
235 } else if (CanBeFeltByPlayer()) {
236 col24 L = Luminance;
237 game::CombineLights(L, DIM_LUMINANCE);
238 BlitData.Luminance = ivanconfig::ApplyContrastTo(L);
239 DrawStaticContents(BlitData);
240 for (const rain* R = Rain; R; R = R->Next) if (R->IsEnabled()) R->Draw(BlitData);
241 } else {
242 DOUBLE_BUFFER->Fill(BlitData.Dest, BlitData.Border, 0);
243 if (Character && Character->CanBeSeenByPlayer()) {
244 BlitData.CustomData |= Character->GetSquareIndex(Pos);
245 BlitData.Luminance = ivanconfig::GetContrastLuminance();
246 Character->Draw(BlitData);
247 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
251 Flags &= ~STRONG_NEW_DRAW_REQUEST;
255 struct emitationcontroller : public tickcontroller, public stackcontroller
257 static truth Handler(int x, int y)
259 lsquare* Square = Map[x >> 1][y >> 1];
260 culong SquareFlags = Square->Flags;
262 if(SquareFlags & PERFECTLY_QUADRI_HANDLED)
263 return SquareFlags & ALLOW_EMITATION_CONTINUE;
265 if(SquareFlags & IS_TRANSPARENT)
266 return ProcessSquare(x >> 1, y >> 1, Square);
268 if(!(SquareFlags & IN_SQUARE_STACK))
270 Square->Flags |= IN_SQUARE_STACK;
271 Stack[StackIndex++] = Square;
274 cint SquarePartIndex = (x & 1) + ((y & 1) << 1);
275 Square->SquarePartEmitationTick =
276 (Square->SquarePartEmitationTick & ~SquarePartTickMask[SquarePartIndex]) | ShiftedTick[SquarePartIndex];
278 return false;
280 static int ProcessSquare(int X, int Y, lsquare* Square)
282 Stack[StackIndex++] = Square;
283 culong SquareFlags = Square->Flags;
284 cint MaxE = MaxLuxTable[X - EmitterPosXMinus16][Y - EmitterPosYMinus16];
286 if(MaxE >= LIGHT_BORDER && (SquareFlags & INSIDE || (!(ID & SECONDARY_SUN_LIGHT) && MaxE > MinNightAmbientLuminanceElement)))
288 Square->Flags |= ALLOW_EMITATION_CONTINUE | PERFECTLY_QUADRI_HANDLED;
289 return true;
291 else
293 Square->Flags = (SquareFlags & ~ALLOW_EMITATION_CONTINUE) | PERFECTLY_QUADRI_HANDLED;
294 return false;
297 static feuLong& GetTickReference(int X, int Y)
299 return Map[X][Y]->SquarePartEmitationTick;
301 static void ProcessStack()
303 for(sLong c1 = 0; c1 < StackIndex; ++c1)
305 lsquare* Square = Stack[c1];
306 culong SquareTick = Square->SquarePartEmitationTick;
307 feuLong TempID = ID;
309 for(int c2 = 0; c2 < 4; ++c2) {
310 if ((SquareTick & SquarePartTickMask[c2]) == ShiftedTick[c2]) {
311 TempID |= 1 << EMITTER_SQUARE_PART_SHIFT << c2;
315 Square->Flags &= ~(IN_SQUARE_STACK|PERFECTLY_QUADRI_HANDLED);
316 v2 Pos = Square->Pos;
317 int XVal = Pos.X - EmitterPosXMinus16;
318 int YVal = Pos.Y - EmitterPosYMinus16;
320 if (MaxLuxTable[XVal][YVal] >= LIGHT_BORDER)
321 Square->AlterLuminance(TempID, MakeRGB24(RedLuxTable[XVal][YVal], GreenLuxTable[XVal][YVal], BlueLuxTable[XVal][YVal]));
324 static feuLong ID;
325 static int MinNightAmbientLuminanceElement;
326 static int EmitterPosXMinus16;
327 static int EmitterPosYMinus16;
328 static uChar** MaxLuxTable;
329 static uChar** RedLuxTable;
330 static uChar** GreenLuxTable;
331 static uChar** BlueLuxTable;
334 feuLong emitationcontroller::ID;
335 int emitationcontroller::MinNightAmbientLuminanceElement;
336 int emitationcontroller::EmitterPosXMinus16;
337 int emitationcontroller::EmitterPosYMinus16;
338 uChar** emitationcontroller::MaxLuxTable;
339 uChar** emitationcontroller::RedLuxTable;
340 uChar** emitationcontroller::GreenLuxTable;
341 uChar** emitationcontroller::BlueLuxTable;
343 void lsquare::Emitate(col24 Emitation, feuLong IDFlags)
345 if(game::IsDark(Emitation))
346 return;
348 int Radius = game::CalculateMinimumEmitationRadius(Emitation);
350 if(!Radius)
351 return;
353 stackcontroller::Map = GetLevel()->GetMap();
354 stackcontroller::Stack = GetLevel()->GetSquareStack();
355 stackcontroller::StackIndex = 0;
356 tickcontroller::Tick = game::IncreaseSquarePartEmitationTicks();
357 tickcontroller::PrepareShiftedTick();
358 emitationcontroller::ID = MakeEmitterID(Pos) | IDFlags;
359 emitationcontroller::MinNightAmbientLuminanceElement = GetMinColor24(GetLevel()->GetNightAmbientLuminance());
360 emitationcontroller::EmitterPosXMinus16 = Pos.X - 16;
361 emitationcontroller::EmitterPosYMinus16 = Pos.Y - 16;
362 emitationcontroller::MaxLuxTable = game::GetLuxTable()[GetMaxColor24(Emitation)];
363 emitationcontroller::RedLuxTable = game::GetLuxTable()[GetRed24(Emitation)];
364 emitationcontroller::GreenLuxTable = game::GetLuxTable()[GetGreen24(Emitation)];
365 emitationcontroller::BlueLuxTable = game::GetLuxTable()[GetBlue24(Emitation)];
366 mapmath<emitationcontroller>::DoQuadriArea(Pos.X, Pos.Y,
367 Radius * Radius,
368 GetLevel()->GetXSize(),
369 GetLevel()->GetYSize());
370 emitationcontroller::ProcessStack();
373 struct noxifycontroller : public stackcontroller
375 static truth Handler(int x, int y)
377 if(x >= 0 && y >= 0 && x < LevelXSize && y < LevelYSize)
379 lsquare* Square = Map[x][y];
381 if(Square->SquarePartEmitationTick != Tick)
383 Square->SquarePartEmitationTick = Tick;
384 return Square->NoxifyEmitter(ID);
388 return false;
390 static int GetStartX(int) { return Center.X; }
391 static int GetStartY(int) { return Center.Y; }
392 static feuLong ID;
393 static feuLong Tick;
396 feuLong noxifycontroller::ID;
397 feuLong noxifycontroller::Tick;
399 void lsquare::Noxify(col24 Emitation, feuLong IDFlags)
401 if(game::IsDark(Emitation))
402 return;
404 int Radius = game::CalculateMinimumEmitationRadius(Emitation);
406 if(!Radius)
407 return;
409 stackcontroller::Map = GetLevel()->GetMap();
410 stackcontroller::LevelXSize = GetLevel()->GetXSize();
411 stackcontroller::LevelYSize = GetLevel()->GetYSize();
412 stackcontroller::Center = Pos;
413 noxifycontroller::ID = MakeEmitterID(Pos) | IDFlags;
414 noxifycontroller::Tick = game::IncreaseSquarePartEmitationTicks();
415 NoxifyEmitter(noxifycontroller::ID);
416 mapmath<noxifycontroller>::DoArea();
419 truth lsquare::NoxifyEmitter(feuLong ID)
421 emittervector::iterator i, End = Emitter.end();
423 for(i = Emitter.begin(); i != End; ++i)
424 if(!((i->ID ^ ID) & (EMITTER_IDENTIFIER_BITS|SECONDARY_SUN_LIGHT)))
426 RemoveLuminance(i->Emitation);
427 Swap(*i, Emitter.back());
428 Emitter.pop_back();
429 return true;
432 return false;
435 void lsquare::AlterLuminance(feuLong ID, col24 NewLuminance)
437 emittervector::iterator i, End = Emitter.end();
439 if(!(ID & FORCE_ADD))
440 for(i = Emitter.begin(); i != End; ++i)
441 if(!((i->ID ^ ID) & (EMITTER_IDENTIFIER_BITS|SECONDARY_SUN_LIGHT)))
443 i->ID |= ID;
445 if(i->Emitation != NewLuminance)
446 ChangeLuminance(i->Emitation, NewLuminance);
448 return;
451 Emitter.push_back(emitter(ID, 0));
452 ChangeLuminance(Emitter.back().Emitation, NewLuminance);
455 void lsquare::AddSunLightEmitter(feuLong ID)
457 sunemittervector::iterator i, End = SunEmitter.end();
459 for(i = SunEmitter.begin(); i != End; ++i)
460 if(!((*i ^ ID) & EMITTER_IDENTIFIER_BITS))
462 if(ID & ~*i & RE_SUN_EMITATED) *i &= ~EMITTER_SHADOW_BITS;
464 *i |= ID;
465 Swap(*i, SunEmitter.front());
466 return;
469 SunEmitter.push_back(ID);
472 truth lsquare::Open(character* Opener)
474 return GetStack()->Open(Opener) || (OLTerrain && OLTerrain->Open(Opener));
477 truth lsquare::Close(character* Closer)
479 if(!GetStack()->GetItems() && !Character)
480 return OLTerrain && OLTerrain->Close(Closer);
481 else
483 ADD_MESSAGE("There's something in the way...");
484 return false;
488 void lsquare::Save(outputfile& SaveFile) const
490 Stack->Save(SaveFile); // This must be before square::Save! (Note: This comment is years old. It's probably obsolete)
491 square::Save(SaveFile);
492 SaveFile << mGoSeen;
493 SaveFile << GLTerrain << OLTerrain;
494 SaveFile << Emitter << SunEmitter;
495 SaveFile << Emitation << Engraved << Luminance;
496 SaveFile << SmokeAlphaSum << (uChar)Flags << Memorized;
497 SaveFile << SecondarySunLightEmitation;
498 SaveFile << (uChar)RoomIndex;
499 SaveFile << SunLightLuminance;
500 SaveLinkedList(SaveFile, Fluid);
501 SaveLinkedList(SaveFile, Smoke);
502 SaveLinkedList(SaveFile, Rain);
503 SaveLinkedList(SaveFile, Trap);
506 void lsquare::Load(inputfile& SaveFile)
508 Stack->Load(SaveFile); // This must be before square::Load! (Note: This comment is years old. It's probably obsolete)
509 Stack->SetMotherSquare(this);
510 square::Load(SaveFile);
511 SaveFile >> mGoSeen;
512 SaveFile >> GLTerrain >> OLTerrain;
513 SaveFile >> Emitter >> SunEmitter;
514 SaveFile >> Emitation >> Engraved >> Luminance;
515 SaveFile >> SmokeAlphaSum >> (uChar&)Flags >> Memorized;
516 Flags &= INSIDE|DESCRIPTION_CHANGE; //only these flags are loaded
517 Flags |= MEMORIZED_UPDATE_REQUEST;
518 SecondarySunLightEmitation = ReadType(col24, SaveFile);
519 RoomIndex = ReadType(uChar, SaveFile);
520 SunLightLuminance = ReadType(col24, SaveFile);
521 LoadLinkedList(SaveFile, Fluid);
522 LoadLinkedList(SaveFile, Smoke);
523 LoadLinkedList(SaveFile, Rain);
524 LoadLinkedList(SaveFile, Trap);
525 CalculateIsTransparent();
527 if (Memorized) {
528 FowMemorized = new bitmap(TILE_V2);
529 FowMemorized->ActivateFastFlag();
530 Memorized->FastBlit(FowMemorized);
531 blitdata B = { FowMemorized,
532 { 0, 0 },
533 { 0, 0 },
534 { TILE_SIZE, TILE_SIZE },
535 { 0 },
537 0 };
539 igraph::GetFOWGraphic()->NormalMaskedBlit(B);
543 void lsquare::CalculateLuminance()
545 Luminance = AmbientLuminance;
546 emittervector::const_iterator i, End = Emitter.end();
548 if(Flags & IS_TRANSPARENT)
550 game::CombineLights(Luminance, SunLightLuminance);
552 for(i = Emitter.begin(); i != End; ++i)
553 game::CombineLights(Luminance, i->Emitation);
555 else
557 feuLong BitMask = 0, LOSTick = game::GetLOSTick();
559 for(int c = 0; c < 4; ++c)
560 if((SquarePartLastSeen >> (c << 3) & 0xFF) >= LOSTick)
561 BitMask |= 1 << EMITTER_SQUARE_PART_SHIFT << c;
563 CalculateSunLightLuminance(BitMask);
564 game::CombineLights(Luminance, SunLightLuminance);
566 for(i = Emitter.begin(); i != End; ++i)
567 if(BitMask & i->ID) game::CombineLights(Luminance, i->Emitation);
571 void lsquare::AddCharacter(character* Guy)
573 if(Character)
574 ABORT("Overgrowth of square population detected!");
576 Character = Guy;
577 SignalEmitationIncrease(Guy->GetEmitation());
578 Flags |= STRONG_NEW_DRAW_REQUEST;
580 if (Guy->IsAnimated()) IncAnimatedEntities();
582 SignalPossibleTransparencyChange();
584 if (Guy->IsPlayer() || (Guy->CanBeSeenByPlayer(true) && CanBeSeenByPlayer())) {
585 Guy->SignalSeen();
589 void lsquare::Clean()
591 GetStack()->Clean();
594 void lsquare::RemoveCharacter()
596 if(Character)
598 character* Backup = Character;
600 if(Backup->IsAnimated())
601 DecAnimatedEntities();
603 Character = 0;
604 SignalEmitationDecrease(Backup->GetEmitation());
605 Flags |= STRONG_NEW_DRAW_REQUEST;
606 SignalPossibleTransparencyChange();
611 void lsquare::UpdateMemorizedDescription (truth Cheat) {
612 if ((Flags & DESCRIPTION_CHANGE) || Cheat) {
613 if (!IsDark() || Cheat) {
614 MemorizedDescription.Empty();
616 if (!OLTerrain || (OLTerrain->IsTransparent() && OLTerrain->ShowThingsUnder() && !OLTerrain->IsWall())) {
617 truth Anything = false;
619 if (OLTerrain && OLTerrain->GetNameSingular().GetSize()) {
620 OLTerrain->AddName(MemorizedDescription, INDEFINITE);
621 Anything = true;
624 if (Flags & IS_TRANSPARENT) {
625 itemvectorvector PileVector;
626 GetStack()->Pile(PileVector, PLAYER, CENTER);
628 if (PileVector.size()) {
629 if (Anything) MemorizedDescription << " and ";
631 if (PileVector.size() == 1)
632 PileVector[0][0]->AddName(MemorizedDescription, INDEFINITE, PileVector[0].size());
633 else
634 MemorizedDescription << "many items";
636 MemorizedDescription << " on ";
637 Anything = true;
638 } else if (Anything) {
639 MemorizedDescription << " on ";
642 GLTerrain->AddName(MemorizedDescription, INDEFINITE);
643 festring SideItems;
644 GetSideItemDescription(SideItems, Cheat);
646 if (!SideItems.IsEmpty()) MemorizedDescription << " and " << SideItems;
647 } else {
648 if (Anything) MemorizedDescription << " on ";
650 GLTerrain->AddName(MemorizedDescription, INDEFINITE);
652 } else {
653 OLTerrain->AddName(MemorizedDescription, INDEFINITE);
656 if (FluidIsVisible()) DisplayFluidInfo(MemorizedDescription);
658 DisplayTrapInfo(MemorizedDescription);
660 if (Cheat) MemorizedDescription << " (pos " << Pos.X << ':' << Pos.Y << ")";
661 } else if (CanBeFeltByPlayer()) {
662 MemorizedDescription.Empty();
663 OLTerrain->AddName(MemorizedDescription, INDEFINITE);
665 if (FluidIsVisible()) DisplayFluidInfo(MemorizedDescription);
667 DisplayTrapInfo(MemorizedDescription);
668 } else {
669 MemorizedDescription = CONST_S("darkness");
672 Flags &= ~DESCRIPTION_CHANGE;
676 void lsquare::GetSideItemDescription(festring& String, truth Cheat) const
678 int Items = 0;
680 for(int c = 0; c < 4; ++c)
682 stack* Stack = GetStackOfAdjacentSquare(c);
684 if(Stack)
685 Items += Cheat
686 ? Stack->GetSideItems(3 - c)
687 : Stack->GetVisibleSideItems(PLAYER, 3 - c);
690 if(Items > 1)
691 String << "many items on the wall";
692 else if(Items == 1)
694 for(int c = 0; c < 4; ++c)
696 stack* Stack = GetStackOfAdjacentSquare(c);
698 if (Stack && ((Cheat && Stack->GetSideItems(3 - c)) || (!Cheat && Stack->GetVisibleSideItems(PLAYER, 3 - c))))
699 Stack->GetBottomSideItem(PLAYER, 3 - c, Cheat)->AddName(String, INDEFINITE);
702 String << " on the wall";
706 truth lsquare::BeKicked(character* Kicker, item* Boot, bodypart* Leg, double KickDamage, double KickToHitValue, int Success, int Direction, truth Critical, truth ForceHit)
708 truth Return;
710 if(GetCharacter())
712 GetCharacter()->BeKicked(Kicker, Boot, Leg, Pos, KickDamage, KickToHitValue, Success, Direction, Critical, ForceHit);
713 Return = true;
715 else
716 Return = false;
718 if(RoomIndex)
719 GetLevel()->GetRoom(RoomIndex)->KickSquare(Kicker, this);
721 GetStack()->BeKicked(Kicker, int(KickDamage), Direction);
723 if(GetOLTerrain())
724 GetOLTerrain()->BeKicked(Kicker, int(KickDamage * (100 + Success) / 100), Direction);
726 return Return;
729 truth lsquare::CanBeDug() const
731 if((!GetPos().X || !GetPos().Y || GetPos().X == GetLevel()->GetXSize() - 1 || GetPos().Y == GetLevel()->GetYSize() - 1) && !*GetLevel()->GetLevelScript()->IsOnGround())
733 ADD_MESSAGE("Somehow you feel that by digging this square you would collapse the whole dungeon.");
734 return false;
736 else
737 return true;
740 void lsquare::ChangeLTerrain(glterrain* NewGround, olterrain* NewOver)
742 ChangeGLTerrain(NewGround);
743 ChangeOLTerrain(NewOver);
746 void lsquare::ChangeGLTerrain(glterrain* NewGround)
748 if(GLTerrain->IsAnimated())
749 DecStaticAnimatedEntities();
751 truth WasUsingBorderTiles = GLTerrain->UseBorderTiles();
752 delete GLTerrain;
753 GLTerrain = NewGround;
754 NewGround->SetLSquareUnder(this);
755 Flags |= NEW_DRAW_REQUEST;
756 GetLevel()->SetWalkability(Pos, GetTheoreticalWalkability());
757 CalculateGroundBorderPartners();
758 SendMemorizedUpdateRequest();
760 if(WasUsingBorderTiles || NewGround->UseBorderTiles())
761 RequestForGroundBorderPartnerUpdates();
763 if(NewGround->IsAnimated())
764 IncStaticAnimatedEntities();
767 void lsquare::ChangeOLTerrain(olterrain* NewOver)
769 if(OLTerrain && OLTerrain->IsAnimated())
770 DecStaticAnimatedEntities();
772 truth WasUsingBorderTiles = OLTerrain && OLTerrain->UseBorderTiles();
773 delete OLTerrain;
774 OLTerrain = NewOver;
775 Flags |= NEW_DRAW_REQUEST;
776 GetLevel()->SetWalkability(Pos, GetTheoreticalWalkability());
777 CalculateOverBorderPartners();
778 CalculateIsTransparent();
779 SendMemorizedUpdateRequest();
781 if(WasUsingBorderTiles || (NewOver && NewOver->UseBorderTiles()))
782 RequestForOverBorderPartnerUpdates();
784 if(NewOver)
786 NewOver->SetLSquareUnder(this);
788 if(NewOver->IsAnimated())
789 IncStaticAnimatedEntities();
793 void lsquare::SetLTerrain(glterrain* NewGround, olterrain* NewOver)
795 GLTerrain = NewGround;
796 NewGround->SetLSquareUnder(this);
798 if(NewGround->IsAnimated())
799 IncStaticAnimatedEntities();
801 OLTerrain = NewOver;
803 if(NewOver)
805 NewOver->SetLSquareUnder(this);
807 if(NewOver->IsAnimated())
808 IncStaticAnimatedEntities();
810 if(!NewOver->IsTransparent())
811 Flags &= ~IS_TRANSPARENT;
814 GetLevel()->SetWalkability(Pos, GetTheoreticalWalkability());
818 void lsquare::ApplyScript (const squarescript *SquareScript, room *Room) {
819 if (SquareScript->AttachRequired()) GetLevel()->AddToAttachQueue(Pos);
821 int EntryIndex = SquareScript->GetEntryIndex();
823 if (EntryIndex != NO_ENTRY) GetLevel()->SetEntryPos(EntryIndex, Pos);
825 const contentscript<character> *CharacterScript = SquareScript->GetCharacter();
827 if (CharacterScript) {
828 character *Char = CharacterScript->Instantiate();
830 if (Char) {
831 Char->SetGenerationDanger(GetLevel()->GetDifficulty());
832 if (!Char->GetTeam()) Char->SetTeam(game::GetTeam(*GetLevel()->GetLevelScript()->GetTeamDefault()));
833 if (CharacterScript->GetFlags() & IS_LEADER) Char->GetTeam()->SetLeader(Char);
834 Char->PutToOrNear(Pos);
835 Char->CreateHomeData();
836 if (Room && CharacterScript->GetFlags() & IS_MASTER) Room->SetMasterID(Char->GetID());
840 const fearray<contentscript<item> >* Items = SquareScript->GetItems();
842 if (Items) {
843 for (uInt c1 = 0; c1 < Items->Size; ++c1) {
844 const interval *TimesPtr = Items->Data[c1].GetTimes();
845 int Times = TimesPtr ? TimesPtr->Randomize() : 1;
847 for (int c2 = 0; c2 < Times; ++c2) {
848 item *Item = Items->Data[c1].Instantiate();
850 if (Item) {
851 int SquarePosition = Items->Data[c1].GetSquarePosition();
853 if (SquarePosition != CENTER) Item->SignalSquarePositionChange(SquarePosition);
854 GetStack()->AddItem(Item);
855 Item->SpecialGenerationHandler();
861 const contentscript<glterrain> *GLTerrainScript = SquareScript->GetGTerrain();
863 if (GLTerrainScript) {
864 GetLevel()->AddFlag(Pos, FORBIDDEN);
865 ChangeGLTerrain(GLTerrainScript->Instantiate());
867 if (GLTerrainScript->IsInside()) {
868 if (*GLTerrainScript->IsInside()) Flags |= INSIDE; else Flags &= ~INSIDE;
872 const contentscript<olterrain> *OLTerrainScript = SquareScript->GetOTerrain();
874 if (OLTerrainScript) {
875 olterrain *terra = OLTerrainScript->Instantiate();
877 if (terra) {
878 GetLevel()->AddFlag(Pos, FORBIDDEN);
879 // check for random altars
880 if (terra->AcceptsOffers()) {
881 //FIXME: make IsAltar()? for now only altars can accept offers
882 if (Room->GetDivineMaster()) {
883 //if (Terrain->GetConfig() != RoomClass->GetDivineMaster()) ABORT("Random altar in room with DivineMaster!");
884 if (terra->GetConfig() != Room->GetDivineMaster()) {
885 // force altar type
886 //fprintf(stderr, "forced altar!\n");
887 delete terra;
888 terra = altar::Spawn(Room->GetDivineMaster());
890 } else {
891 // no DivineMaster yet, assign it
892 const fearray<int> *am = Room->GetScript()->GetAllowedDivineMasters();
894 if (am && am->Size > 0) {
895 int Owner = am->GetRandomElement();
898 fprintf(stderr, "AllowedDivineMasters:");
899 for (uInt f = 0; f < am->Size; ++f) fprintf(stderr, " %d", (*am)[f]);
900 fprintf(stderr, "\n");
903 if (Owner < 1 || Owner > GODS) ABORT("Your god is a bad god!");
905 if (terra->GetConfig() != Owner) {
906 //fprintf(stderr, "recreating altar %d --> %d\n", terra->GetConfig(), Owner);
907 delete terra;
908 terra = altar::Spawn(Owner);
909 } else {
910 //fprintf(stderr, "spawned altar in room w/o divine master, assigning %d\n", terra->GetConfig());
912 } else {
913 //fprintf(stderr, "spawned altar in room w/o divine master, assigning %d\n", terra->GetConfig());
915 Room->SetDivineMaster(terra->GetConfig());
919 ChangeOLTerrain(terra);
920 } else {
921 //fprintf(stderr, "WARNING: LTerra spawn error [lsquare] in file %s, line %d\n", OLTerrainScript->GetSrcFile().CStr(), OLTerrainScript->GetSrcLine());
922 ChangeOLTerrain(0);
928 truth lsquare::CanBeSeenByPlayer (truth IgnoreDarkness) const {
929 return ((IgnoreDarkness || !IsDark()) && LastSeen == game::GetLOSTick());
933 truth lsquare::CanBeSeenFrom (v2 FromPos, sLong MaxDistance, truth IgnoreDarkness) const {
934 if ((Pos-FromPos).GetLengthSquare() <= MaxDistance && (IgnoreDarkness || !IsDark())) {
935 if (Character && Character->IsPlayer() && GetNearLSquare(FromPos)->CanBeSeenByPlayer(true)) {
936 return true;
938 eyecontroller::Map = GetLevel()->GetMap();
939 return mapmath<eyecontroller>::DoLine(FromPos.X, FromPos.Y, GetPos().X, GetPos().Y, SKIP_FIRST|LINE_BOTH_DIRS);
941 return false;
945 void lsquare::StepOn (character* Stepper, lsquare** ComingFrom) {
946 if (RoomIndex) {
947 truth WasInRoom = false;
949 if (ComingFrom) {
950 for (int c = 0; c < Stepper->GetSquaresUnder(); ++c) {
951 if (ComingFrom[c]->GetRoomIndex() == RoomIndex) {
952 WasInRoom = true;
953 break;
958 if (!WasInRoom) GetLevel()->GetRoom(RoomIndex)->Enter(Stepper);
961 GLTerrain->StepOn(Stepper);
963 if (OLTerrain) {
964 OLTerrain->StepOn(Stepper);
966 if (Stepper->DestroysWalls() && OLTerrain->WillBeDestroyedBy(Stepper)) {
967 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s destroys %s.", Stepper->CHAR_NAME(DEFINITE), OLTerrain->CHAR_NAME(DEFINITE));
969 Stepper->EditAP(-100);
970 OLTerrain->BeDestroyed();
974 uInt c;
975 std::vector<trap*> TrapVector;
977 for (trap* T = Trap; T; T = T->Next) TrapVector.push_back(T);
979 for (c = 0; c < TrapVector.size(); ++c) {
980 if (TrapVector[c]->Exists()) {
981 TrapVector[c]->StepOnEffect(Stepper);
982 if (!Stepper->IsEnabled()) return;
986 if (!Stepper->IsFlying()) {
987 std::vector<fluid*> FluidVector;
989 for (fluid* F = Fluid; F; F = F->Next) FluidVector.push_back(F);
991 for (c = 0; c < FluidVector.size(); ++c) {
992 if (FluidVector[c]->Exists()) {
993 FluidVector[c]->StepOnEffect(Stepper);
994 if (!Stepper->IsEnabled()) return;
998 GetStack()->CheckForStepOnEffect(Stepper);
1003 void lsquare::ReceiveVomit (character *Who, liquid *Liquid) {
1004 if (!GetOLTerrain() || !GetOLTerrain()->ReceiveVomit(Who, Liquid)) {
1005 SpillFluid(Who, Liquid);
1006 if (RoomIndex) GetRoom()->ReceiveVomit(Who);
1011 void lsquare::SetTemporaryEmitation (col24 What) {
1012 col24 Old = TemporaryEmitation;
1013 TemporaryEmitation = 0;
1014 SignalEmitationDecrease(Old);
1015 TemporaryEmitation = What;
1016 SignalEmitationIncrease(What);
1020 void lsquare::ChangeOLTerrainAndUpdateLights(olterrain* NewTerrain)
1022 truth WasTransparent = Flags & IS_TRANSPARENT, Noxified = false;
1023 emittervector EmitterBackup;
1025 if(WasTransparent && NewTerrain && !NewTerrain->IsTransparent())
1027 EmitterBackup = Emitter;
1028 GetLevel()->ForceEmitterNoxify(EmitterBackup);
1029 Noxified = true;
1032 sLong OldEmit = OLTerrain ? OLTerrain->GetEmitation() : 0;
1033 ChangeOLTerrain(NewTerrain);
1035 if(NewTerrain)
1036 SignalEmitationIncrease(NewTerrain->GetEmitation());
1038 SignalEmitationDecrease(OldEmit);
1039 GetStack()->DropSideItems();
1041 if(!IsFlyable() && Smoke)
1043 DecAnimatedEntities();
1045 for(smoke* S = Smoke; S; S = S->Next)
1046 S->SendToHell();
1048 Smoke = 0;
1049 SmokeAlphaSum = 0;
1052 if(!WasTransparent == !!CalculateIsTransparent())
1054 if(Noxified)
1055 GetLevel()->ForceEmitterEmitation(EmitterBackup, SunEmitter, FORCE_ADD);
1056 else
1057 GetLevel()->ForceEmitterEmitation(Emitter, SunEmitter);
1059 CalculateLuminance();
1061 if(LastSeen == game::GetLOSTick())
1062 game::SendLOSUpdateRequest();
1066 void lsquare::DrawParticles(sLong Color, truth DrawHere)
1068 if(GetPos().X < game::GetCamera().X
1069 || GetPos().Y < game::GetCamera().Y
1070 || GetPos().X >= game::GetCamera().X + game::GetScreenXSize()
1071 || GetPos().Y >= game::GetCamera().Y + game::GetScreenYSize()
1072 || !CanBeSeenByPlayer(true)
1073 || Color == TRANSPARENT_COLOR)
1074 return;
1076 clock_t StartTime = clock();
1078 if(DrawHere)
1079 game::DrawEverythingNoBlit();
1081 if(Color & RANDOM_COLOR)
1082 Color = MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190);
1084 v2 Pos = game::CalculateScreenCoordinates(GetPos());
1086 for(int c = 0; c < 10; ++c)
1087 DOUBLE_BUFFER->PutPixel(Pos + v2(1 + RAND() % 14, 1 + RAND() % 14), Color);
1089 Flags |= STRONG_NEW_DRAW_REQUEST; // Clean the pixels from the screen afterwards
1091 if(DrawHere)
1093 graphics::BlitDBToScreen();
1094 while(clock() - StartTime < 0.02 * CLOCKS_PER_SEC);
1098 truth lsquare::DipInto(item* Thingy, character* Dipper)
1100 if(IsDipDestination())
1102 room* Room = GetRoom();
1104 if(Room && Room->HasDipHandler() && !Room->Dip(Dipper))
1105 return false;
1107 return (GLTerrain->IsDipDestination() && GLTerrain->DipInto(Thingy, Dipper)) || (OLTerrain && OLTerrain->IsDipDestination() && OLTerrain->DipInto(Thingy, Dipper));
1109 else
1111 if(Dipper->IsPlayer())
1112 ADD_MESSAGE("You cannot dip %s on that square!", Thingy->CHAR_NAME(DEFINITE));
1114 return false;
1118 // return true if key fits someplace
1120 truth lsquare::TryKey(item* Key, character* Applier)
1122 if(GetOLTerrain() && GetOLTerrain()->TryKey(Key, Applier))
1123 return true;
1125 if((!GetOLTerrain() || !GetOLTerrain()->HasKeyHole()) && !GetStack()->TryKey(Key, Applier))
1127 ADD_MESSAGE("There's no place here to put the key in!");
1128 return false;
1131 return true;
1134 void lsquare::SignalSeen(feuLong Tick)
1136 if(LastSeen < Tick - 2)
1137 Flags |= STRONG_NEW_DRAW_REQUEST;
1139 Flags &= ~(IN_SQUARE_STACK|PERFECTLY_QUADRI_HANDLED);
1140 LastSeen = Tick;
1142 if(!(Flags & IS_TRANSPARENT))
1144 col24 OldLuminance = Luminance;
1145 CalculateLuminance();
1147 if(OldLuminance != Luminance)
1149 Flags |= NEW_DRAW_REQUEST;
1151 if (IsDark() != game::IsDark(OldLuminance))
1152 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
1156 if(IsDark())
1158 v2 Dist = Pos - PLAYER->GetPos();
1160 if(abs(Dist.X) > 1 || abs(Dist.Y) > 1)
1162 LastSeen -= 2;
1163 return;
1167 if(!Memorized)
1168 CreateMemorized();
1170 UpdateMemorized();
1171 UpdateMemorizedDescription();
1173 if(Character)
1174 Character->CheckIfSeen();
1178 #define DIMMED_LUMINANCE 0x656565
1180 void lsquare::DrawMemorized (blitdata &BlitData) const {
1181 LastSeen = 0;
1182 Flags &= ~STRONG_NEW_DRAW_REQUEST;
1183 BlitData.Luminance = ivanconfig::GetContrastLuminance();
1185 if (FowMemorized) {
1186 blitdata nbd = BlitData;
1187 if (nbd.Luminance == NORMAL_LUMINANCE) nbd.Luminance = DIMMED_LUMINANCE;
1188 FowMemorized->LuminanceBlit(nbd);
1189 } else {
1190 DOUBLE_BUFFER->Fill(BlitData.Dest, BlitData.Border, 0);
1193 ccharacter *C = Character;
1195 if (C && C->CanBeSeenByPlayer()) {
1196 BlitData.CustomData |= C->GetSquareIndex(Pos);
1197 C->Draw(BlitData);
1198 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
1203 void lsquare::DrawMemorizedCharacter (blitdata &BlitData) const {
1204 BlitData.Luminance = ivanconfig::GetContrastLuminance();
1206 if (FowMemorized) {
1207 blitdata nbd = BlitData;
1208 if (nbd.Luminance == NORMAL_LUMINANCE) nbd.Luminance = DIMMED_LUMINANCE;
1209 FowMemorized->LuminanceBlit(nbd);
1210 } else {
1211 DOUBLE_BUFFER->Fill(BlitData.Dest, BlitData.Border, 0);
1214 BlitData.CustomData |= Character->GetSquareIndex(Pos);
1215 Character->Draw(BlitData);
1216 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
1217 Flags |= STRONG_NEW_DRAW_REQUEST;
1221 truth lsquare::IsDangerous(ccharacter* Who) const
1223 return
1224 ((!Who->IsFlying() && (Stack->IsDangerous(Who) || HasDangerousFluids(Who))) ||
1225 IsDangerousToBreathe(Who) || HasDangerousTraps(Who));
1228 truth lsquare::IsScary(ccharacter* Who) const
1230 return IsScaryToBreathe(Who);
1233 stack* lsquare::GetStackOfAdjacentSquare(int I) const
1235 lsquare* Square = 0;
1237 if (I == LEFT) Square = NeighbourLSquare[3];
1238 else if (I == DOWN) Square = NeighbourLSquare[6];
1239 else if (I == UP) Square = NeighbourLSquare[1];
1240 else if (I == RIGHT) Square = NeighbourLSquare[4];
1242 return (Square ? Square->Stack : 0);
1245 void lsquare::SendMemorizedUpdateRequest()
1247 if(!(Flags & FREEZED))
1249 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
1251 if(!game::IsGenerating() && (CanBeSeenByPlayer() || CanBeFeltByPlayer()))
1253 if(!Memorized) CreateMemorized();
1255 UpdateMemorized();
1256 UpdateMemorizedDescription();
1261 truth lsquare::KickAnyoneStandingHereAway (truth allowTeleport) {
1262 if (Character) {
1263 character *Backup = Character;
1264 v2 oldpos = Backup->GetPos(); // largecreatures should be returned to their exact place
1265 Backup->Remove();
1266 if (allowTeleport) {
1267 if (!Backup->PutNear(Pos, true)) {
1268 //k8:???
1269 //Backup->Die(nullptr, CONST_S(""), DISALLOW_MSG|FORBID_REINCARNATION);
1270 // put it back
1271 if (Character) {
1272 if (Character != Backup) ABORT("lsquare::KickAnyoneStandingHereAway: oops no0!");
1273 } else {
1274 Backup->PutTo(oldpos);
1276 // and kill it
1277 Backup->ReceiveDamage(0, 100000000, PHYSICAL_DAMAGE); // kill it
1278 Backup->CheckDeath(CONST_S("Killed by a fatal accident"), nullptr, FORCE_DEATH|FORBID_REINCARNATION/*|DISALLOW_CORPSE*/);
1279 return false;
1281 return true;
1283 if (!Backup->PutNearNoTeleport(Pos)) {
1284 // put it back
1285 if (Character) {
1286 if (Character != Backup) ABORT("lsquare::KickAnyoneStandingHereAway: oops no1!");
1287 } else {
1288 Backup->PutTo(oldpos);
1290 return false;
1293 return true;
1296 outputfile& operator<<(outputfile& SaveFile, const emitter& Emitter)
1298 SaveFile.Write(reinterpret_cast<cchar*>(&Emitter), sizeof(Emitter));
1299 return SaveFile;
1302 inputfile& operator>>(inputfile& SaveFile, emitter& Emitter)
1304 SaveFile.Read(reinterpret_cast<char*>(&Emitter), sizeof(Emitter));
1305 return SaveFile;
1308 void lsquare::AddItem(item* Item)
1310 Stack->AddItem(Item);
1313 v2 lsquare::DrawLightning(v2 StartPos, sLong Color, int Direction, truth DrawHere)
1315 if(GetPos().X < game::GetCamera().X
1316 || GetPos().Y < game::GetCamera().Y
1317 || GetPos().X >= game::GetCamera().X + game::GetScreenXSize()
1318 || GetPos().Y >= game::GetCamera().Y + game::GetScreenYSize()
1319 || !CanBeSeenByPlayer(true))
1320 switch(Direction)
1322 case 1: return v2(RAND() & 15, 15);
1323 case 3: return v2(15, RAND() & 15);
1324 case 4: return v2(0, RAND() & 15);
1325 case 6: return v2(RAND() & 15, 0);
1326 default: return StartPos;
1329 clock_t StartTime = clock();
1330 bitmap Empty(TILE_V2, TRANSPARENT_COLOR);
1331 Empty.ActivateFastFlag();
1333 if(Color & RANDOM_COLOR)
1334 Color = MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190);
1336 if(Direction != YOURSELF)
1338 while(!Empty.CreateLightning(StartPos, game::GetMoveVector(Direction), 16, Color));
1339 v2 EndPos(0, 0);
1341 switch(Direction)
1343 case 0: EndPos = v2(0, 0); break;
1344 case 1: EndPos = v2(RAND() & 15, 0); StartPos = v2(EndPos.X, 15); break;
1345 case 2: EndPos = v2(15, 0); break;
1346 case 3: EndPos = v2(0, RAND() & 15); StartPos = v2(15, EndPos.Y); break;
1347 case 4: EndPos = v2(15, RAND() & 15); StartPos = v2(0, EndPos.Y); break;
1348 case 5: EndPos = v2(0, 15); break;
1349 case 6: EndPos = v2(RAND() & 15, 15); StartPos = v2(EndPos.X, 0); break;
1350 case 7: EndPos = v2(15, 15); break;
1353 while(!Empty.CreateLightning(EndPos, -game::GetMoveVector(Direction), NO_LIMIT, Color));
1355 else
1357 static v2 Dir[4] = { v2(0, -1), v2(-1, 0), v2(1, 0), v2(0, 1) };
1359 for(int d = 0; d < 4; ++d)
1360 while(!Empty.CreateLightning(StartPos + Dir[d], ZERO_V2, 10, Color));
1363 if(DrawHere)
1364 game::DrawEverythingNoBlit();
1366 blitdata B = { DOUBLE_BUFFER,
1367 { 0, 0 },
1368 { 0, 0 },
1369 { TILE_SIZE, TILE_SIZE },
1370 { 0 },
1371 TRANSPARENT_COLOR,
1372 0 };
1374 B.Dest = game::CalculateScreenCoordinates(GetPos());
1375 Empty.NormalMaskedBlit(B);
1376 Flags |= STRONG_NEW_DRAW_REQUEST;
1378 if(DrawHere)
1380 graphics::BlitDBToScreen();
1381 while(clock() - StartTime < 0.02 * CLOCKS_PER_SEC);
1384 return StartPos;
1387 truth lsquare::Polymorph(const beamdata& Beam)
1389 GetStack()->Polymorph(Beam.Owner);
1391 if(GetOLTerrain())
1392 GetOLTerrain()->Polymorph(Beam.Owner);
1394 character* Character = GetCharacter();
1396 if(Character)
1398 if(Beam.Owner && Character->GetTeam() != Beam.Owner->GetTeam())
1399 Beam.Owner->Hostility(Character);
1401 Character->PolymorphRandomly(1, 999999, 5000 + RAND() % 5000);
1404 if(Engraved)
1406 for(int c = 0; Engraved[c] != '\0'; ++c)
1408 if(RAND_2)
1410 Engraved[c] = 32 + RAND_N(95);
1414 return false;
1417 truth lsquare::Strike(const beamdata& Beam)
1419 int Damage = 50 + RAND() % 21 - RAND() % 21;
1420 GetStack()->ReceiveDamage(Beam.Owner, Damage, ENERGY, Beam.Direction);
1421 ReceiveTrapDamage(Beam.Owner, Damage, ENERGY, Beam.Direction);
1423 character* Char = GetCharacter();
1425 if(Char)
1427 if(Char->IsPlayer())
1428 ADD_MESSAGE("You are hit by a burst of energy!");
1429 else if(Char->CanBeSeenByPlayer())
1430 ADD_MESSAGE("%s is hit by a burst of energy!", Char->CHAR_NAME(DEFINITE));
1432 if(Beam.Owner)
1433 Beam.Owner->Hostility(Char);
1435 Char->ReceiveDamage(Beam.Owner, Damage, ENERGY, ALL);
1436 Char->CheckDeath(Beam.DeathMsg, Beam.Owner);
1439 if(GetOLTerrain())
1440 GetOLTerrain()->ReceiveDamage(Beam.Owner, Damage, ENERGY);
1442 return false;
1445 truth lsquare::FireBall(const beamdata& Beam)
1447 if(!IsFlyable() || GetCharacter())
1449 if(CanBeSeenByPlayer(true))
1450 ADD_MESSAGE("A magical explosion is triggered!");
1452 GetLevel()->Explosion(Beam.Owner, Beam.DeathMsg, Pos, 75 + RAND() % 25 - RAND() % 25);
1453 return true;
1456 return false;
1459 truth lsquare::Teleport(const beamdata& Beam)
1461 if(Character)
1463 if(Beam.Owner && Character->GetTeam() != Beam.Owner->GetTeam())
1464 Beam.Owner->Hostility(GetCharacter());
1466 if(Character->IsPlayer())
1467 ADD_MESSAGE("You experience a forced teleportation.");
1468 else if(Character->CanBeSeenByPlayer())
1469 ADD_MESSAGE("%s disappears!", Character->CHAR_NAME(DEFINITE));
1471 Character->TeleportRandomly();
1474 if(RoomIndex)
1475 GetLevel()->GetRoom(RoomIndex)->TeleportSquare(Beam.Owner, this);
1477 GetStack()->TeleportRandomly();
1478 return false;
1481 truth lsquare::Haste(const beamdata&)
1483 GetStack()->Haste();
1484 character* Dude = GetCharacter();
1486 if(Dude)
1487 Dude->Haste();
1489 return false;
1492 truth lsquare::Slow(const beamdata& Beam)
1494 GetStack()->Slow();
1495 character* Dude = GetCharacter();
1497 if(Dude)
1499 if(Beam.Owner)
1500 Beam.Owner->Hostility(Dude);
1502 Dude->Slow();
1505 return false;
1509 truth lsquare::Confuse (const beamdata &Beam) {
1510 character *Dude = GetCharacter();
1512 if (Dude && Dude->CanBeConfused()) {
1513 if (Beam.Owner) Beam.Owner->Hostility(Dude);
1514 Dude->BeginTemporaryState(CONFUSED, 50+RAND()%50);
1516 return false;
1520 truth lsquare::Parasitize (const beamdata &Beam) {
1521 character *Dude = GetCharacter();
1523 if (Dude && Dude->GetTorso()->CanHaveParasite()) {
1524 if (Beam.Owner) Beam.Owner->Hostility(Dude);
1525 Dude->GainIntrinsic(PARASITIZED);
1527 return false;
1531 truth lsquare::InstillPanic (const beamdata &Beam) {
1532 character* Dude = GetCharacter();
1534 if (Dude && Dude->CanPanic()) {
1535 if (Beam.Owner) Beam.Owner->Hostility(Dude);
1536 Dude->BeginTemporaryState(PANIC, 50+RAND()%50);
1538 return false;
1542 truth lsquare::Resurrect(const beamdata& Beam)
1544 if (GetCharacter())
1545 return GetCharacter()->RaiseTheDead(Beam.Owner);
1546 else
1547 return GetStack()->RaiseTheDead(Beam.Owner);
1550 truth lsquare::Invisibility(const beamdata&)
1552 if (GetCharacter())
1553 GetCharacter()->BeginTemporaryState(INVISIBLE, 1000 + RAND() % 1001);
1555 return false;
1558 truth lsquare::Duplicate(const beamdata& Beam)
1560 truth DuplicatedSomething = false;
1561 character* Character = GetCharacter();
1563 if(Character)
1564 DuplicatedSomething = !!(Character->DuplicateToNearestSquare(Beam.Owner, Beam.SpecialParameters));
1566 if(GetStack()->Duplicate(DuplicatedSomething ? 4 : 5, Beam.SpecialParameters))
1567 DuplicatedSomething = true;
1569 return DuplicatedSomething;
1572 truth lsquare::Lightning(const beamdata& Beam)
1574 int Damage = 20 + RAND() % 6 - RAND() % 6;
1575 GetStack()->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY, Beam.Direction);
1576 ReceiveTrapDamage(Beam.Owner, Damage, ELECTRICITY, Beam.Direction);
1578 character* Char = GetCharacter();
1580 if(Char)
1582 if(Char->IsPlayer())
1583 ADD_MESSAGE("A massive burst of electricity runs through your body!");
1584 else if(Char->CanBeSeenByPlayer())
1585 ADD_MESSAGE("A massive burst of electricity runs through %s!", Char->CHAR_NAME(DEFINITE));
1587 if(Beam.Owner)
1588 Beam.Owner->Hostility(Char);
1590 Char->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY, ALL);
1591 Char->CheckDeath(Beam.DeathMsg, Beam.Owner);
1594 if(GetOLTerrain())
1595 GetOLTerrain()->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY);
1597 return false;
1600 truth lsquare::DoorCreation(const beamdata& Beam)
1602 if ((!GetOLTerrain() || GetOLTerrain()->IsSafeToCreateDoor()) &&
1603 !GetCharacter() &&
1604 (GetLevel()->IsOnGround() ||
1605 (Pos.X > 0 && Pos.Y > 0 && Pos.X < GetLevel()->GetXSize()-1 && Pos.Y < GetLevel()->GetYSize()-1)))
1607 if(Beam.Owner && GetRoom())
1608 GetRoom()->HostileAction(Beam.Owner);
1610 door* Door = door::Spawn(0, NO_MATERIALS);
1611 Door->InitMaterials(MAKE_MATERIAL(STEEL));
1613 if(RAND() % 10)
1614 Door->Lock();
1616 ChangeOLTerrainAndUpdateLights(Door);
1617 return true;
1620 return false;
1623 truth (lsquare::*BeamEffect[BEAM_EFFECTS])(const beamdata&) =
1625 &lsquare::Polymorph,
1626 &lsquare::Strike,
1627 &lsquare::FireBall,
1628 &lsquare::Teleport,
1629 &lsquare::Haste,
1630 &lsquare::Slow,
1631 &lsquare::Resurrect,
1632 &lsquare::Invisibility,
1633 &lsquare::Duplicate,
1634 &lsquare::Lightning,
1635 &lsquare::DoorCreation,
1636 &lsquare::AcidRain,
1637 &lsquare::Necromancy
1640 truth (lsquare::*lsquare::GetBeamEffect(int I))(const beamdata&)
1642 return BeamEffect[I];
1645 truth lsquare::CheckKick(ccharacter* Kicker) const
1647 if(Character && Kicker->CheckIfTooScaredToHit(Character))
1648 return false;
1650 if(RoomIndex && !GetLevel()->GetRoom(RoomIndex)->CheckKickSquare(Kicker, this))
1651 return false;
1653 return true;
1656 void lsquare::GetHitByExplosion(const explosion* Explosion)
1658 if(Explosion->ID == LastExplosionID)
1659 return;
1661 LastExplosionID = Explosion->ID;
1662 int DistanceSquare = (Pos - Explosion->Pos).GetLengthSquare();
1664 if(DistanceSquare > Explosion->RadiusSquare)
1665 return;
1667 int Damage = Explosion->Strength / (DistanceSquare + 1);
1669 if (Character && (Explosion->HurtNeutrals || (Explosion->Terrorist && Character->GetRelation(Explosion->Terrorist) == HOSTILE))) {
1670 if (Character->IsPlayer()) game::SetPlayerWasHurtByExplosion(true);
1671 else Character->GetHitByExplosion(Explosion, Damage);
1674 GetStack()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, FIRE);
1675 GetStack()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE);
1677 ReceiveTrapDamage(Explosion->Terrorist, Damage >> 1, FIRE);
1678 ReceiveTrapDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE);
1680 if(GetOLTerrain())
1681 GetOLTerrain()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, FIRE);
1683 if(GetOLTerrain())
1684 GetOLTerrain()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE);
1687 int lsquare::GetSpoiledItems() const
1689 return GetStack()->GetSpoiledItems();
1692 truth lsquare::LowerEnchantment(const beamdata& Beam)
1694 character* Char = GetCharacter();
1695 itemvector AllItems;
1696 sortdata SortData(AllItems, Beam.Owner, true, &item::IsEnchantable);
1697 SortAllItems(SortData);
1698 item* RandomItem;
1700 if(!AllItems.empty())
1701 RandomItem = AllItems[RAND() % AllItems.size()];
1702 else
1703 return false;
1705 if(Char)
1707 if(Char->IsPlayer())
1708 ADD_MESSAGE("%s glows blue for a moment!", RandomItem->CHAR_NAME(DEFINITE));
1710 if(Beam.Owner)
1711 Beam.Owner->Hostility(Char);
1714 if(RandomItem->GetEnchantment() > -5)
1715 RandomItem->EditEnchantment(-1);
1717 return true;
1720 void lsquare::SortAllItems(const sortdata& SortData)
1722 if(GetCharacter())
1723 GetCharacter()->SortAllItems(SortData);
1725 GetStack()->SortAllItems(SortData);
1729 truth lsquare::SoftenMaterial (const beamdata &Beam) {
1730 character *Char = GetCharacter();
1731 item *RandomItem;
1732 itemvector AllItems;
1734 sortdata SortData(AllItems, Beam.Owner, true, &item::IsEnchantable);
1735 SortAllItems(SortData);
1736 //sortdata SortData2(AllItems, Beam.Owner, true, &item::MaterialIsChangeable);
1737 //SortAllItems(SortData2);
1738 if (AllItems.empty()) return false;
1739 RandomItem = AllItems[RAND() % AllItems.size()];
1740 if (Char) {
1741 if (Char->IsPlayer()) ADD_MESSAGE("Your %s glows yellow for a moment!", RandomItem->CHAR_NAME(UNARTICLED));
1742 if (Beam.Owner) Beam.Owner->Hostility(Char);
1745 truth Changed = 0;
1746 festring Desc;
1748 RandomItem->AddName(Desc, UNARTICLED);
1749 material *OldMaterial = RandomItem->GetMainMaterial();
1750 int NewMaterial = RandomItem->GetMainMaterial()->GetSoftenedMaterial(RandomItem);
1752 if (NewMaterial != NONE) {
1753 /* Don't Forget! It is an ugly thing, I know, but removal = seg-fault since cannot have NONE material */
1754 RandomItem->ChangeMainMaterial(MAKE_MATERIAL(NewMaterial)); /*->SpawnMore()*/
1755 if (OldMaterial->GetConfig() != NewMaterial) Changed = 1;
1757 if (Char) {
1758 if (Changed && Char->IsPlayer()) {
1759 ADD_MESSAGE("Your %s softens into %s!", Desc.CStr(), RandomItem->GetMainMaterial()->GetName(false, false).CStr());
1760 } else if (Changed) {
1761 ADD_MESSAGE("%s's %s softens into %s!", Char->CHAR_DESCRIPTION(DEFINITE), Desc.CStr(), RandomItem->GetMainMaterial()->GetName(false, false).CStr());
1763 if (!Changed) {
1764 //may not need this message
1765 if (Char->IsPlayer()) {
1766 ADD_MESSAGE("Your %s vibrates slightly but remains unchanged.", RandomItem->CHAR_NAME(UNARTICLED) );
1767 } else {
1768 ADD_MESSAGE("%s's %s vibrates slightly but remains unchanged.", Char->CHAR_DESCRIPTION(DEFINITE), RandomItem->CHAR_NAME(UNARTICLED) );
1772 return true;
1776 void lsquare::RemoveSmoke(smoke* ToBeRemoved)
1778 smoke* S = Smoke;
1780 if(S == ToBeRemoved)
1782 Smoke = S->Next;
1784 if(!S)
1785 DecAnimatedEntities();
1787 else
1789 smoke* LS;
1793 LS = S;
1794 S = S->Next;
1796 while(S != ToBeRemoved);
1798 LS->Next = S->Next;
1802 void lsquare::AddSmoke(gas* ToBeAdded)
1804 smoke* S = Smoke;
1806 if(!S)
1808 Smoke = new smoke(ToBeAdded, this);
1809 IncAnimatedEntities();
1811 else
1813 smoke* LS;
1817 if(ToBeAdded->IsSameAs(S->GetGas()))
1819 S->Merge(ToBeAdded);
1820 return;
1823 LS = S;
1824 S = S->Next;
1826 while(S);
1828 LS->Next = new smoke(ToBeAdded, this);
1832 void lsquare::ShowSmokeMessage() const
1834 for(const smoke* S = Smoke; S; S = S->Next)
1835 S->AddBreatheMessage();
1838 void lsquare::SignalSmokeAlphaChange(int What)
1840 SmokeAlphaSum += What;
1841 SignalPossibleTransparencyChange();
1844 int lsquare::GetDivineMaster() const
1846 return RoomIndex ? GetLevel()->GetRoom(RoomIndex)->GetDivineMaster() : 0;
1849 void lsquare::DisplaySmokeInfo (festring &Msg) const {
1850 if (Smoke) {
1851 if (!Smoke->Next)
1852 Msg << " A cloud of " << Smoke->GetGas()->GetName(false, false) << " surrounds the square.";
1853 else
1854 Msg << " A lot of gases hover over the square.";
1858 void lsquare::ReceiveEarthQuakeDamage()
1860 GetStack()->ReceiveDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE);
1861 ReceiveTrapDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE);
1862 /* Gum solution */
1864 if(GetOLTerrain() && GetOLTerrain()->IsDoor())
1865 GetOLTerrain()->ReceiveDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE);
1868 truth lsquare::CanBeFeltByPlayer() const
1870 if (!PLAYER) return false;
1871 return OLTerrain && !PLAYER->CanMoveOn(this) && Pos.IsAdjacent(PLAYER->GetPos());
1874 void lsquare::PreProcessForBone()
1876 DestroyMemorized();
1877 LastSeen = 0;
1879 if(OLTerrain)
1880 OLTerrain->PreProcessForBone();
1882 if(Smoke)
1884 DecAnimatedEntities();
1886 for(smoke* S = Smoke; S; S = S->Next)
1887 S->SendToHell();
1889 Smoke = 0;
1890 SmokeAlphaSum = 0;
1893 if(Character && !Character->PreProcessForBone())
1895 Character->SendToHell();
1896 Character->Remove();
1899 for(fluid* F = Fluid; F; F = F->Next)
1900 F->PreProcessForBone();
1902 for(trap* T = Trap; T; T = T->Next)
1903 T->PreProcessForBone();
1905 GetStack()->PreProcessForBone();
1908 void lsquare::PostProcessForBone(double& DangerSum, int& Enemies)
1910 if(OLTerrain)
1911 OLTerrain->PostProcessForBone();
1913 if(Character && !Character->PostProcessForBone(DangerSum, Enemies))
1915 Character->SendToHell();
1916 Character->Remove();
1919 for(fluid* F = Fluid; F; F = F->Next)
1920 F->PostProcessForBone();
1922 for(trap* T = Trap; T; T = T->Next)
1923 T->PostProcessForBone();
1925 GetStack()->PostProcessForBone();
1928 void lsquare::FinalProcessForBone()
1930 if(OLTerrain)
1931 OLTerrain->FinalProcessForBone();
1933 if(Character)
1934 Character->FinalProcessForBone();
1936 GetStack()->FinalProcessForBone();
1939 truth lsquare::EngravingsCanBeReadByPlayer()
1941 return PLAYER->CanRead(); // Might be a good idea to improve sometime in the distant future.
1944 void lsquare::DisplayEngravedInfo(festring& Buffer) const
1946 Buffer << " There is a message engraved here: \"" << Engraved << '\"';
1949 truth lsquare::IsDangerousToBreathe(ccharacter* Who) const
1951 for(const smoke* S = Smoke; S; S = S->Next)
1952 if(S->IsDangerousToBreathe(Who))
1953 return true;
1955 return false;
1958 truth lsquare::IsScaryToBreathe(ccharacter* Who) const
1960 for(const smoke* S = Smoke; S; S = S->Next)
1961 if(S->IsScaryToBreathe(Who))
1962 return true;
1964 return false;
1968 struct groundborderpartner {
1969 truth operator < (const groundborderpartner &P) const { return Terrain->GetBorderTilePriority() < P.Terrain->GetBorderTilePriority(); }
1970 glterrain *Terrain;
1971 int SquareIndex;
1975 void lsquare::CalculateGroundBorderPartners () {
1976 if (GroundBorderPartnerInfo & BORDER_PARTNER_ANIMATED) DecStaticAnimatedEntities();
1977 groundborderpartner BorderPartner[8*2]; //k8: *2 to make g++ shut up (WTF?!)
1978 int Index = 0;
1979 int Priority = GLTerrain->GetBorderTilePriority();
1980 for (int d = 0; d < 8; ++d) {
1981 lsquare *Square = NeighbourLSquare[d];
1982 if (Square) {
1983 glterrain *Terrain = Square->GetGLTerrain();
1984 if (Terrain && Terrain->UseBorderTiles() && Terrain->GetBorderTilePriority() > Priority) {
1985 BorderPartner[Index].Terrain = Terrain;
1986 BorderPartner[Index].SquareIndex = 7-d;
1987 ++Index;
1991 GroundBorderPartnerInfo = 0;
1992 if (!Index) {
1993 delete [] GroundBorderPartnerTerrain;
1994 GroundBorderPartnerTerrain = 0;
1995 return;
1997 if (!GroundBorderPartnerTerrain) GroundBorderPartnerTerrain = new glterrain *[8];
1998 std::sort(BorderPartner, BorderPartner+Index); //k8: why g++ complains here? ah, ignore it for now
1999 truth Animated = false;
2000 for (int c = 0; c < Index; ++c) {
2001 glterrain *T = BorderPartner[c].Terrain;
2002 GroundBorderPartnerTerrain[c] = T;
2003 GroundBorderPartnerInfo |= BorderPartner[c].SquareIndex<<((c<<1)+c);
2004 if (T->IsAnimated()) Animated = true;
2006 if (Animated) {
2007 GroundBorderPartnerInfo |= BORDER_PARTNER_ANIMATED;
2008 IncStaticAnimatedEntities();
2010 GroundBorderPartnerInfo |= Index<<24;
2014 struct overborderpartner {
2015 truth operator < (const overborderpartner &P) const { return Terrain->GetBorderTilePriority() < P.Terrain->GetBorderTilePriority(); }
2016 olterrain *Terrain;
2017 int SquareIndex;
2020 void lsquare::CalculateOverBorderPartners () {
2021 if (OverBorderPartnerInfo & BORDER_PARTNER_ANIMATED) DecStaticAnimatedEntities();
2022 overborderpartner BorderPartner[8*2]; //k8: *2 to make g++ shut up (WTF?!)
2023 int Index = 0;
2024 int Priority = OLTerrain ? OLTerrain->GetBorderTilePriority() : 0;
2025 for (int d = 0; d < 8; ++d) {
2026 lsquare *Square = NeighbourLSquare[d];
2027 if (Square) {
2028 olterrain *Terrain = Square->GetOLTerrain();
2029 if (Terrain && Terrain->UseBorderTiles() && Terrain->GetBorderTilePriority() > Priority) {
2030 BorderPartner[Index].Terrain = Terrain;
2031 BorderPartner[Index].SquareIndex = 7-d;
2032 ++Index;
2036 OverBorderPartnerInfo = 0;
2037 if (!Index) {
2038 delete [] OverBorderPartnerTerrain;
2039 OverBorderPartnerTerrain = 0;
2040 return;
2042 if (!OverBorderPartnerTerrain) OverBorderPartnerTerrain = new olterrain *[8];
2043 std::sort(BorderPartner, BorderPartner+Index); //k8: why g++ complains here? ah, ignore it for now
2044 truth Animated = false;
2045 for (int c = 0; c < Index; ++c) {
2046 olterrain *T = BorderPartner[c].Terrain;
2047 OverBorderPartnerTerrain[c] = T;
2048 OverBorderPartnerInfo |= BorderPartner[c].SquareIndex<<((c<<1)+c);
2049 if (T->IsAnimated()) Animated = true;
2051 if (Animated) {
2052 OverBorderPartnerInfo |= BORDER_PARTNER_ANIMATED;
2053 IncStaticAnimatedEntities();
2055 OverBorderPartnerInfo |= Index<<24;
2056 /*k8
2057 if (OverBorderPartnerInfo & BORDER_PARTNER_ANIMATED)
2058 int esko = esko = 2;
2063 void lsquare::RequestForGroundBorderPartnerUpdates()
2065 if(!game::IsGenerating())
2066 for(int d = 0; d < 8; ++d)
2068 lsquare* Square = NeighbourLSquare[d];
2070 if(Square)
2072 Square->CalculateGroundBorderPartners();
2073 Square->SendNewDrawRequest();
2074 Square->SendMemorizedUpdateRequest();
2079 void lsquare::RequestForOverBorderPartnerUpdates()
2081 if(!game::IsGenerating())
2082 for(int d = 0; d < 8; ++d)
2084 lsquare* Square = NeighbourLSquare[d];
2086 if(Square)
2088 Square->CalculateOverBorderPartners();
2089 Square->SendNewDrawRequest();
2090 Square->SendMemorizedUpdateRequest();
2095 int lsquare::GetWalkability() const
2097 if(!GetLevel()->IsOnGround())
2099 if(Pos.X >= 1 && Pos.Y >= 1 && Pos.X < GetLevel()->GetXSize() - 1 && Pos.Y < GetLevel()->GetYSize() - 1)
2100 return OLTerrain ? OLTerrain->GetWalkability() & GLTerrain->GetWalkability() : GLTerrain->GetWalkability();
2101 else
2102 return 0;
2104 else
2105 return OLTerrain ? OLTerrain->GetWalkability() & GLTerrain->GetWalkability() : GLTerrain->GetWalkability();
2108 void lsquare::RemoveFluid(fluid* ToRemove)
2110 fluid*& F = ListFind(Fluid, pointercomparer<fluid>(ToRemove));
2111 F = F->Next;
2112 SignalEmitationDecrease(ToRemove->GetEmitation());
2115 struct fluidcomparer
2117 fluidcomparer(const liquid* Liquid) : Liquid(Liquid) { }
2118 truth operator()(const fluid* F) const { return Liquid->IsSameAs(F->GetLiquid()); }
2119 const liquid* Liquid;
2122 fluid* lsquare::AddFluid(liquid* ToBeAdded)
2124 fluid*& F = ListFind(Fluid, fluidcomparer(ToBeAdded));
2126 if(F)
2128 F->AddLiquidAndVolume(ToBeAdded->GetVolume());
2129 //delete ToBeAdded; //k8: this is BUG!
2130 ToBeAdded->SendToHell();
2132 else
2134 F = new fluid(ToBeAdded, this);
2135 SignalEmitationIncrease(ToBeAdded->GetEmitation());
2138 SendNewDrawRequest();
2139 SendMemorizedUpdateRequest();
2140 return F;
2143 void lsquare::DisplayFluidInfo(festring& Msg) const
2145 if(Fluid)
2147 Msg << ". There is ";
2148 fluid::AddFluidInfo(Fluid, Msg);
2149 AddLocationDescription(Msg);
2153 void lsquare::SpillFluid(character* Spiller, liquid* Liquid, truth ForceHit, truth ShowMsg)
2155 if(!Liquid->GetVolume())
2157 // delete Liquid; //k8: this is BUG!
2158 //fprintf(stderr, "!!!!!!!!!!! (02)\n");
2159 Liquid->SendToHell();
2160 return;
2163 if(IsFlyable())
2165 if(GetCharacter())
2167 if(Spiller && !GetCharacter()->IsAlly(Spiller))
2168 Spiller->Hostility(GetCharacter());
2170 sLong CharVolume = GetCharacter()->GetVolume();
2171 double ChanceMultiplier = 1. / (300 + sqrt(GetStack()->GetVolume() + CharVolume));
2172 double Root = sqrt(CharVolume);
2174 if(ForceHit || Root > RAND() % 400 || Root > RAND() % 400)
2176 sLong SpillVolume = sLong(Liquid->GetVolume() * Root * ChanceMultiplier);
2178 if(SpillVolume)
2180 if(ShowMsg && (GetCharacter()->IsPlayer() || GetCharacter()->CanBeSeenByPlayer()))
2181 ADD_MESSAGE("%s is spilled all over %s.", Liquid->GetName(false, false).CStr(), GetCharacter()->CHAR_DESCRIPTION(DEFINITE));
2183 Liquid->EditVolume(-SpillVolume);
2184 GetCharacter()->SpillFluid(Spiller, Liquid->SpawnMoreLiquid(SpillVolume), GetCharacter()->GetSquareIndex(GetPos()));
2189 GetStack()->SpillFluid(Spiller, Liquid, Liquid->GetVolume());
2192 if(Liquid->GetVolume() && !Liquid->IsSameAs(GLTerrain->GetMainMaterial()))
2194 fluid* F = AddFluid(Liquid);
2196 if(GetCharacter())
2197 F->StepOnEffect(GetCharacter());
2198 } else {
2199 //fprintf(stderr, "!!!!!!!!!!! (03)\n");
2200 //delete Liquid; //k8: this is BUG!
2201 Liquid->SendToHell();
2205 void lsquare::DrawStacks(blitdata& BlitData) const
2207 Stack->Draw(PLAYER, BlitData, CENTER);
2209 for(int c = 0; c < 4; ++c)
2211 stack* Stack = GetStackOfAdjacentSquare(c);
2213 if(Stack)
2214 Stack->Draw(PLAYER, BlitData, 3 - c);
2218 void lsquare::RemoveRain(rain* ToBeRemoved)
2220 SendNewDrawRequest();
2221 rain* R = Rain;
2223 if(ToBeRemoved->IsEnabled())
2224 DecAnimatedEntities();
2226 if(R == ToBeRemoved)
2227 Rain = R->Next;
2228 else
2230 rain* LR;
2234 LR = R;
2235 R = R->Next;
2237 while(R != ToBeRemoved);
2239 LR->Next = R->Next;
2242 SignalEmitationDecrease(ToBeRemoved->GetEmitation());
2245 void lsquare::AddRain(liquid* RainLiquid, v2 Speed, int Team, truth OwnLiquid)
2247 rain* R = Rain, * NewRain = new rain(RainLiquid, this, Speed, Team, OwnLiquid);
2249 if(NewRain->IsEnabled())
2250 IncAnimatedEntities();
2252 if(!R)
2253 Rain = NewRain;
2254 else
2256 rain* LR;
2260 LR = R;
2261 R = R->Next;
2263 while(R);
2265 LR->Next = NewRain;
2269 void lsquare::RemoveSunLight()
2271 SunLightLuminance = 0;
2272 SunEmitter.clear();
2275 void lsquare::CheckIfIsSecondarySunLightEmitter()
2277 col24 OldEmitation = SecondarySunLightEmitation;
2279 if(Flags & IS_TRANSPARENT && (!(Flags & INSIDE) || SunLightLuminance))
2281 for(int d = 0; d < 8; ++d)
2283 lsquare* Neighbour = NeighbourLSquare[d];
2285 if(Neighbour && Neighbour->Flags & INSIDE)
2287 col24 NewEmitation = GetLevel()->GetAmbientLuminance();
2289 if(OldEmitation != NewEmitation)
2291 SecondarySunLightEmitation = NewEmitation;
2293 if(game::CompareLights(NewEmitation, OldEmitation) >= 0)
2294 Emitate(NewEmitation, SECONDARY_SUN_LIGHT);
2295 else
2297 Noxify(OldEmitation, SECONDARY_SUN_LIGHT);
2298 Emitate(NewEmitation, SECONDARY_SUN_LIGHT|FORCE_ADD);
2302 return;
2307 if(OldEmitation)
2309 Noxify(OldEmitation, SECONDARY_SUN_LIGHT);
2310 SecondarySunLightEmitation = 0;
2314 void lsquare::CalculateNeighbourLSquares()
2316 int XSize = GetLevel()->GetXSize();
2317 int YSize = GetLevel()->GetYSize();
2319 for(int d = 0; d < 8; ++d)
2321 v2 NPos = Pos + game::GetMoveVector(d);
2323 if(NPos.X >= 0 && NPos.Y >= 0 && NPos.X < XSize && NPos.Y < YSize)
2324 NeighbourLSquare[d] = GetLevel()->GetLSquare(NPos);
2325 else
2326 NeighbourLSquare[d] = 0;
2330 void lsquare::RemoveLuminance(col24& Emitation)
2332 col24 OldLuminance = Luminance;
2333 col24 OldEmitation = Emitation;
2334 Emitation = 0;
2336 if(game::CompareLights(OldEmitation, OldLuminance) < 0) return;
2338 if(!(Flags & IS_TRANSPARENT))
2340 Flags |= NEW_DRAW_REQUEST;
2342 if(LastSeen == game::GetLOSTick()) game::SendLOSUpdateRequest();
2344 else
2346 CalculateLuminance();
2348 if(OldLuminance == Luminance)
2349 return;
2351 Flags |= NEW_DRAW_REQUEST;
2353 if(!Luminance)
2355 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
2357 if(LastSeen == game::GetLOSTick()) game::SendLOSUpdateRequest();
2362 void lsquare::ChangeLuminance(col24& Emitation, col24 NewLuminance)
2364 col24 OldLuminance = Luminance;
2366 if(!(Flags & IS_TRANSPARENT))
2368 Emitation = NewLuminance;
2369 Flags |= NEW_DRAW_REQUEST;
2371 if(LastSeen == game::GetLOSTick())
2372 game::SendLOSUpdateRequest();
2374 return;
2377 truth EmitationInsignificant = !Emitation
2378 || game::CompareLights(Emitation, OldLuminance) < 0;
2379 Emitation = NewLuminance;
2381 if(game::CompareLights(NewLuminance, OldLuminance) > 0 && EmitationInsignificant)
2382 game::CombineLights(Luminance, NewLuminance);
2383 else
2385 if(EmitationInsignificant)
2386 return;
2388 CalculateLuminance();
2390 if(OldLuminance == Luminance)
2391 return;
2394 Flags |= NEW_DRAW_REQUEST;
2396 if(!OldLuminance)
2398 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
2400 if(LastSeen == game::GetLOSTick())
2401 game::SendLOSUpdateRequest();
2405 void lsquare::EnableGlobalRain()
2407 for(rain* R = Rain; R; R = R->Next)
2408 if(!R->HasOwnLiquid())
2410 R->Enable();
2411 IncAnimatedEntities();
2415 void lsquare::DisableGlobalRain()
2417 SendNewDrawRequest();
2419 for(rain* R = Rain; R; R = R->Next)
2420 if(!R->HasOwnLiquid())
2422 R->Disable();
2423 DecAnimatedEntities();
2427 void lsquare::InitLastSeen()
2429 LastSeen = LastSeen == game::GetLOSTick() ? 2 : 0;
2430 SquarePartLastSeen = 0;
2433 truth lsquare::Engrave(cfestring& What)
2435 if(Engraved)
2436 delete [] Engraved;
2438 if(!What.IsEmpty())
2440 Engraved = new char[What.GetSize() + 1];
2441 strcpy(Engraved, What.CStr());
2443 else
2444 Engraved = 0;
2446 return true;
2449 void lsquare::SendSunLightSignals()
2451 if(Flags & IS_TRANSPARENT)
2453 col24 OldLuminance = Luminance;
2454 CalculateLuminance();
2456 if(Luminance != OldLuminance)
2458 Flags |= NEW_DRAW_REQUEST;
2460 if(!Luminance != !OldLuminance)
2462 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
2464 if(LastSeen == game::GetLOSTick())
2465 game::SendLOSUpdateRequest();
2469 else
2471 Flags |= NEW_DRAW_REQUEST;
2473 if(LastSeen == game::GetLOSTick())
2474 game::SendLOSUpdateRequest();
2478 void lsquare::ZeroReSunEmitatedFlags()
2480 sunemittervector::iterator i, End = SunEmitter.end();
2482 for(i = SunEmitter.begin(); i != End; ++i) *i &= ~RE_SUN_EMITATED;
2485 truth lsquare::CalculateIsTransparent()
2487 if((!OLTerrain || OLTerrain->IsTransparent()) && SmokeAlphaSum < 175
2488 && (!Character || Character->IsTransparent()))
2490 Flags |= IS_TRANSPARENT;
2491 return true;
2493 else
2495 Flags &= ~IS_TRANSPARENT;
2496 return false;
2500 void lsquare::CalculateSunLightLuminance(feuLong SeenBitMask)
2502 sunemittervector::const_iterator i, SunEnd = SunEmitter.end();
2503 int S = 0, L = 0;
2505 for(i = SunEmitter.begin(); i != SunEnd; ++i)
2507 feuLong ShadowFlag = 1 << EMITTER_SHADOW_SHIFT;
2508 feuLong SquarePartFlag = 1 << EMITTER_SQUARE_PART_SHIFT;
2509 for (int c = 0; c < 4; ++c, ShadowFlag <<= 1, SquarePartFlag <<= 1) {
2510 if (SeenBitMask & *i & SquarePartFlag) {
2511 if (*i & ShadowFlag) ++S; else ++L;
2516 if(!L)
2517 SunLightLuminance = 0;
2518 else if(!S)
2519 SunLightLuminance = GetLevel()->GetSunLightEmitation();
2520 else
2522 col24 ShadowColor = GetLevel()->GetAmbientLuminance();
2523 col24 LightColor = GetLevel()->GetSunLightEmitation();
2524 SunLightLuminance = MakeRGB24(
2525 (GetRed24(LightColor)*L+GetRed24(ShadowColor)*S)/(S+L),
2526 (GetGreen24(LightColor)*L+GetGreen24(ShadowColor)*S)/(S+L),
2527 (GetBlue24(LightColor)*L+GetBlue24(ShadowColor)*S)/(S+L));
2531 void lsquare::CreateMemorized()
2533 Memorized = new bitmap(TILE_V2);
2534 Memorized->ActivateFastFlag();
2535 FowMemorized = new bitmap(TILE_V2);
2536 FowMemorized->ActivateFastFlag();
2539 truth lsquare::AcidRain(const beamdata& Beam)
2541 if(!IsFlyable() || GetCharacter() || Beam.Direction == YOURSELF)
2543 int StackSize = GetLevel()->AddRadiusToSquareStack(Pos, 9);
2544 lsquare** Stack = GetLevel()->GetSquareStack();
2545 v2 Speed = v2(512, 512);
2546 int Team = Beam.Owner ? Beam.Owner->GetTeam()->GetID() : MONSTER_TEAM;
2548 for(int c = 0; c < StackSize; ++c)
2550 Stack[c]->AddRain(liquid::Spawn(SULPHURIC_ACID, 300), Speed, Team, true);
2551 Stack[c]->Flags &= ~IN_SQUARE_STACK;
2554 if(Beam.Owner && Character && Character->GetTeam() != Beam.Owner->GetTeam())
2555 Beam.Owner->Hostility(Character);
2557 return true;
2560 return false;
2563 truth lsquare::DetectMaterial (cmaterial *Material) const {
2564 if (GLTerrain->DetectMaterial(Material) ||
2565 (OLTerrain && OLTerrain->DetectMaterial(Material)) ||
2566 Stack->DetectMaterial(Material) ||
2567 (Character && Character->DetectMaterial(Material))) return true;
2568 for (const fluid *F = Fluid; F; F = F->Next) if (F->GetLiquid()->IsSameAs(Material)) return true;
2569 for (const smoke *S = Smoke; S; S = S->Next) if (S->GetGas()->IsSameAs(Material)) return true;
2570 for (const rain *R = Rain; R; R = R->Next) if (R->GetLiquid()->IsSameAs(Material)) return true;
2571 return false;
2574 void lsquare::Reveal(feuLong Tick, truth IgnoreDarkness)
2576 if(!Memorized)
2577 CreateMemorized();
2579 LastSeen = Tick;
2581 if(IgnoreDarkness)
2582 Luminance = NORMAL_LUMINANCE;
2583 else
2585 SquarePartLastSeen = 0;
2587 for(int c = 0; c < 4; ++c)
2588 SquarePartLastSeen |= (Tick << (c << 3));
2590 CalculateLuminance();
2593 Flags |=
2594 NEW_DRAW_REQUEST|
2595 MEMORIZED_UPDATE_REQUEST|
2596 DESCRIPTION_CHANGE;
2597 UpdateMemorized();
2598 UpdateMemorizedDescription();
2601 void lsquare::DestroyMemorized()
2603 delete Memorized;
2604 delete FowMemorized;
2605 Memorized = 0;
2606 FowMemorized = 0;
2609 void lsquare::SwapMemorized(lsquare* Square)
2611 Swap(Memorized, Square->Memorized);
2612 Swap(FowMemorized, Square->FowMemorized);
2613 MemorizedDescription.SwapData(Square->MemorizedDescription);
2616 truth lsquare::Necromancy(const beamdata& Beam)
2618 return GetStack()->Necromancy(Beam.Owner);
2621 // Returns 0 if fails
2623 lsquare* lsquare::GetRandomAdjacentSquare() const
2625 lsquare* OK[8];
2626 int Index = 0;
2628 for(int c = 0; c < 8; ++c)
2630 lsquare* Square = NeighbourLSquare[c];
2632 if(Square)
2633 OK[Index++] = Square;
2636 if(Index)
2637 return OK[RAND_N(Index)];
2638 else
2639 return 0;
2642 truth pathcontroller::Handler(int x, int y)
2644 return Character->CanMoveOn(Map[x][y]);
2647 void lsquare::SignalPossibleTransparencyChange()
2649 truth WasTransparent = IsTransparent();
2650 CalculateIsTransparent();
2652 if(WasTransparent && !IsTransparent())
2654 Flags |= IS_TRANSPARENT;
2655 emittervector EmitterBackup = Emitter;
2656 GetLevel()->ForceEmitterNoxify(EmitterBackup);
2657 Flags &= ~IS_TRANSPARENT;
2658 GetLevel()->ForceEmitterEmitation(EmitterBackup, SunEmitter, FORCE_ADD);
2659 CalculateLuminance();
2660 Flags |= DESCRIPTION_CHANGE|MEMORIZED_UPDATE_REQUEST;
2662 if(LastSeen == game::GetLOSTick())
2663 game::SendLOSUpdateRequest();
2665 else if(!WasTransparent && IsTransparent())
2667 GetLevel()->ForceEmitterEmitation(Emitter, SunEmitter);
2668 CalculateLuminance();
2669 Flags |= DESCRIPTION_CHANGE|MEMORIZED_UPDATE_REQUEST;
2671 if(LastSeen == game::GetLOSTick())
2672 game::SendLOSUpdateRequest();
2676 void lsquare::RemoveTrap(trap* ToRemove)
2678 trap*& T = ListFind(Trap, pointercomparer<trap>(ToRemove));
2679 T = T->Next;
2680 SendNewDrawRequest();
2681 SendMemorizedUpdateRequest();
2684 struct trapcomparer
2686 trapcomparer(int Type) : Type(Type) { }
2687 truth operator()(const trap* T) const { return T->GetType() == Type; }
2688 int Type;
2691 truth lsquare::AddTrap(trap* ToBeAdded)
2693 trap*& T = ListFind(Trap, trapcomparer(ToBeAdded->GetType()));
2695 if(T)
2697 delete ToBeAdded;
2698 return false;
2700 else
2701 T = ToBeAdded;
2703 ToBeAdded->SetLSquareUnder(this);
2704 SendNewDrawRequest();
2705 SendMemorizedUpdateRequest();
2706 return true;
2709 void lsquare::DisplayTrapInfo(festring& Msg) const
2711 for(const trap* T = Trap; T; T = T->Next)
2712 T->AddDescription(Msg);
2715 void lsquare::FillTrapVector(std::vector<trap*>& TrapVector) const
2717 for(trap* T = Trap; T; T = T->Next)
2718 TrapVector.push_back(T);
2721 void lsquare::ReceiveTrapDamage(character* Damager, int Damage, int Type, int Direction)
2723 std::vector<trap*> TrapVector;
2724 FillTrapVector(TrapVector);
2726 for(uInt c = 0; c < TrapVector.size(); ++c)
2727 TrapVector[c]->ReceiveDamage(Damager, Damage, Type, Direction);
2730 truth lsquare::HasDangerousTraps(ccharacter* Who) const
2732 for(trap* T = Trap; T; T = T->Next)
2733 if(T->IsDangerous(Who))
2734 return true;
2736 return false;
2739 truth lsquare::HasDangerousFluids(ccharacter* Who) const
2741 for(const fluid* F = Fluid; F; F = F->Next)
2742 if(F->IsDangerous(Who))
2743 return true;
2745 return false;
2748 truth lsquare::HasNoBorderPartners() const
2750 return !(GroundBorderPartnerInfo >> 24) && !(OverBorderPartnerInfo >> 24);
2753 void lsquare::AddLocationDescription(festring& String) const
2755 if(IsFlyable())
2756 GLTerrain->AddLocationDescription(String);
2757 else
2758 OLTerrain->AddLocationDescription(String);
2761 truth lsquare::VomitingIsDangerous(ccharacter* Char) const
2763 return ((OLTerrain && OLTerrain->VomitingIsDangerous(Char))
2764 || (Character && Character->GetTeam() != Char->GetTeam()
2765 && Character->GetRelation(Char) != HOSTILE));
2768 bool lsquare::TeleportAllSmokeAway()
2770 return false;
2773 bool lsquare::TeleportAllFluidsAway()
2775 return false;
2778 bool lsquare::TeleportAllTrapsAway()
2780 for(trap* T = Trap; T; T = Trap)
2782 T->Untrap();
2783 RemoveTrap(T);
2784 v2 V, Pos = GetPos();
2785 for (V = GetLevel()->GetRandomSquare(); V != Pos; V = GetLevel()->GetRandomSquare()) {
2786 if (V == ERROR_V2) {
2787 fprintf(stderr, "No country for old trap! possible memleak!\n");
2788 break;
2791 if (V != ERROR_V2) GetNearLSquare(V)->AddTrap(T);
2794 return false;
2797 void lsquare::AddSpecialCursors()
2799 if((FowMemorized || game::GetSeeWholeMapCheatMode()) && OLTerrain)
2800 OLTerrain->AddSpecialCursors();