slightly better path and light tracing (try to trace line from both points before...
[k8-i-v-a-n.git] / src / game / lsquare.cpp
blob8ebd376a1551072fc4014b9fdcf4d0381de820ce
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 void lsquare::KickAnyoneStandingHereAway()
1263 if(Character)
1265 character* Backup = Character;
1266 Backup->Remove();
1267 Backup->PutNear(Pos);
1271 outputfile& operator<<(outputfile& SaveFile, const emitter& Emitter)
1273 SaveFile.Write(reinterpret_cast<cchar*>(&Emitter), sizeof(Emitter));
1274 return SaveFile;
1277 inputfile& operator>>(inputfile& SaveFile, emitter& Emitter)
1279 SaveFile.Read(reinterpret_cast<char*>(&Emitter), sizeof(Emitter));
1280 return SaveFile;
1283 void lsquare::AddItem(item* Item)
1285 Stack->AddItem(Item);
1288 v2 lsquare::DrawLightning(v2 StartPos, sLong Color, int Direction, truth DrawHere)
1290 if(GetPos().X < game::GetCamera().X
1291 || GetPos().Y < game::GetCamera().Y
1292 || GetPos().X >= game::GetCamera().X + game::GetScreenXSize()
1293 || GetPos().Y >= game::GetCamera().Y + game::GetScreenYSize()
1294 || !CanBeSeenByPlayer(true))
1295 switch(Direction)
1297 case 1: return v2(RAND() & 15, 15);
1298 case 3: return v2(15, RAND() & 15);
1299 case 4: return v2(0, RAND() & 15);
1300 case 6: return v2(RAND() & 15, 0);
1301 default: return StartPos;
1304 clock_t StartTime = clock();
1305 bitmap Empty(TILE_V2, TRANSPARENT_COLOR);
1306 Empty.ActivateFastFlag();
1308 if(Color & RANDOM_COLOR)
1309 Color = MakeRGB16(60 + RAND() % 190, 60 + RAND() % 190, 60 + RAND() % 190);
1311 if(Direction != YOURSELF)
1313 while(!Empty.CreateLightning(StartPos, game::GetMoveVector(Direction), 16, Color));
1314 v2 EndPos(0, 0);
1316 switch(Direction)
1318 case 0: EndPos = v2(0, 0); break;
1319 case 1: EndPos = v2(RAND() & 15, 0); StartPos = v2(EndPos.X, 15); break;
1320 case 2: EndPos = v2(15, 0); break;
1321 case 3: EndPos = v2(0, RAND() & 15); StartPos = v2(15, EndPos.Y); break;
1322 case 4: EndPos = v2(15, RAND() & 15); StartPos = v2(0, EndPos.Y); break;
1323 case 5: EndPos = v2(0, 15); break;
1324 case 6: EndPos = v2(RAND() & 15, 15); StartPos = v2(EndPos.X, 0); break;
1325 case 7: EndPos = v2(15, 15); break;
1328 while(!Empty.CreateLightning(EndPos, -game::GetMoveVector(Direction), NO_LIMIT, Color));
1330 else
1332 static v2 Dir[4] = { v2(0, -1), v2(-1, 0), v2(1, 0), v2(0, 1) };
1334 for(int d = 0; d < 4; ++d)
1335 while(!Empty.CreateLightning(StartPos + Dir[d], ZERO_V2, 10, Color));
1338 if(DrawHere)
1339 game::DrawEverythingNoBlit();
1341 blitdata B = { DOUBLE_BUFFER,
1342 { 0, 0 },
1343 { 0, 0 },
1344 { TILE_SIZE, TILE_SIZE },
1345 { 0 },
1346 TRANSPARENT_COLOR,
1347 0 };
1349 B.Dest = game::CalculateScreenCoordinates(GetPos());
1350 Empty.NormalMaskedBlit(B);
1351 Flags |= STRONG_NEW_DRAW_REQUEST;
1353 if(DrawHere)
1355 graphics::BlitDBToScreen();
1356 while(clock() - StartTime < 0.02 * CLOCKS_PER_SEC);
1359 return StartPos;
1362 truth lsquare::Polymorph(const beamdata& Beam)
1364 GetStack()->Polymorph(Beam.Owner);
1366 if(GetOLTerrain())
1367 GetOLTerrain()->Polymorph(Beam.Owner);
1369 character* Character = GetCharacter();
1371 if(Character)
1373 if(Beam.Owner && Character->GetTeam() != Beam.Owner->GetTeam())
1374 Beam.Owner->Hostility(Character);
1376 Character->PolymorphRandomly(1, 999999, 5000 + RAND() % 5000);
1379 if(Engraved)
1381 for(int c = 0; Engraved[c] != '\0'; ++c)
1383 if(RAND_2)
1385 Engraved[c] = 32 + RAND_N(95);
1389 return false;
1392 truth lsquare::Strike(const beamdata& Beam)
1394 int Damage = 50 + RAND() % 21 - RAND() % 21;
1395 GetStack()->ReceiveDamage(Beam.Owner, Damage, ENERGY, Beam.Direction);
1396 ReceiveTrapDamage(Beam.Owner, Damage, ENERGY, Beam.Direction);
1398 character* Char = GetCharacter();
1400 if(Char)
1402 if(Char->IsPlayer())
1403 ADD_MESSAGE("You are hit by a burst of energy!");
1404 else if(Char->CanBeSeenByPlayer())
1405 ADD_MESSAGE("%s is hit by a burst of energy!", Char->CHAR_NAME(DEFINITE));
1407 if(Beam.Owner)
1408 Beam.Owner->Hostility(Char);
1410 Char->ReceiveDamage(Beam.Owner, Damage, ENERGY, ALL);
1411 Char->CheckDeath(Beam.DeathMsg, Beam.Owner);
1414 if(GetOLTerrain())
1415 GetOLTerrain()->ReceiveDamage(Beam.Owner, Damage, ENERGY);
1417 return false;
1420 truth lsquare::FireBall(const beamdata& Beam)
1422 if(!IsFlyable() || GetCharacter())
1424 if(CanBeSeenByPlayer(true))
1425 ADD_MESSAGE("A magical explosion is triggered!");
1427 GetLevel()->Explosion(Beam.Owner, Beam.DeathMsg, Pos, 75 + RAND() % 25 - RAND() % 25);
1428 return true;
1431 return false;
1434 truth lsquare::Teleport(const beamdata& Beam)
1436 if(Character)
1438 if(Beam.Owner && Character->GetTeam() != Beam.Owner->GetTeam())
1439 Beam.Owner->Hostility(GetCharacter());
1441 if(Character->IsPlayer())
1442 ADD_MESSAGE("You experience a forced teleportation.");
1443 else if(Character->CanBeSeenByPlayer())
1444 ADD_MESSAGE("%s disappears!", Character->CHAR_NAME(DEFINITE));
1446 Character->TeleportRandomly();
1449 if(RoomIndex)
1450 GetLevel()->GetRoom(RoomIndex)->TeleportSquare(Beam.Owner, this);
1452 GetStack()->TeleportRandomly();
1453 return false;
1456 truth lsquare::Haste(const beamdata&)
1458 GetStack()->Haste();
1459 character* Dude = GetCharacter();
1461 if(Dude)
1462 Dude->Haste();
1464 return false;
1467 truth lsquare::Slow(const beamdata& Beam)
1469 GetStack()->Slow();
1470 character* Dude = GetCharacter();
1472 if(Dude)
1474 if(Beam.Owner)
1475 Beam.Owner->Hostility(Dude);
1477 Dude->Slow();
1480 return false;
1484 truth lsquare::Confuse (const beamdata &Beam) {
1485 character *Dude = GetCharacter();
1487 if (Dude && Dude->CanBeConfused()) {
1488 if (Beam.Owner) Beam.Owner->Hostility(Dude);
1489 Dude->BeginTemporaryState(CONFUSED, 50+RAND()%50);
1491 return false;
1495 truth lsquare::Parasitize (const beamdata &Beam) {
1496 character *Dude = GetCharacter();
1498 if (Dude && Dude->GetTorso()->CanHaveParasite()) {
1499 if (Beam.Owner) Beam.Owner->Hostility(Dude);
1500 Dude->GainIntrinsic(PARASITIZED);
1502 return false;
1506 truth lsquare::InstillPanic (const beamdata &Beam) {
1507 character* Dude = GetCharacter();
1509 if (Dude && Dude->CanPanic()) {
1510 if (Beam.Owner) Beam.Owner->Hostility(Dude);
1511 Dude->BeginTemporaryState(PANIC, 50+RAND()%50);
1513 return false;
1517 truth lsquare::Resurrect(const beamdata& Beam)
1519 if (GetCharacter())
1520 return GetCharacter()->RaiseTheDead(Beam.Owner);
1521 else
1522 return GetStack()->RaiseTheDead(Beam.Owner);
1525 truth lsquare::Invisibility(const beamdata&)
1527 if (GetCharacter())
1528 GetCharacter()->BeginTemporaryState(INVISIBLE, 1000 + RAND() % 1001);
1530 return false;
1533 truth lsquare::Duplicate(const beamdata& Beam)
1535 truth DuplicatedSomething = false;
1536 character* Character = GetCharacter();
1538 if(Character)
1539 DuplicatedSomething = !!(Character->DuplicateToNearestSquare(Beam.Owner, Beam.SpecialParameters));
1541 if(GetStack()->Duplicate(DuplicatedSomething ? 4 : 5, Beam.SpecialParameters))
1542 DuplicatedSomething = true;
1544 return DuplicatedSomething;
1547 truth lsquare::Lightning(const beamdata& Beam)
1549 int Damage = 20 + RAND() % 6 - RAND() % 6;
1550 GetStack()->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY, Beam.Direction);
1551 ReceiveTrapDamage(Beam.Owner, Damage, ELECTRICITY, Beam.Direction);
1553 character* Char = GetCharacter();
1555 if(Char)
1557 if(Char->IsPlayer())
1558 ADD_MESSAGE("A massive burst of electricity runs through your body!");
1559 else if(Char->CanBeSeenByPlayer())
1560 ADD_MESSAGE("A massive burst of electricity runs through %s!", Char->CHAR_NAME(DEFINITE));
1562 if(Beam.Owner)
1563 Beam.Owner->Hostility(Char);
1565 Char->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY, ALL);
1566 Char->CheckDeath(Beam.DeathMsg, Beam.Owner);
1569 if(GetOLTerrain())
1570 GetOLTerrain()->ReceiveDamage(Beam.Owner, Damage, ELECTRICITY);
1572 return false;
1575 truth lsquare::DoorCreation(const beamdata& Beam)
1577 if ((!GetOLTerrain() || GetOLTerrain()->IsSafeToCreateDoor()) &&
1578 !GetCharacter() &&
1579 (GetLevel()->IsOnGround() ||
1580 (Pos.X > 0 && Pos.Y > 0 && Pos.X < GetLevel()->GetXSize()-1 && Pos.Y < GetLevel()->GetYSize()-1)))
1582 if(Beam.Owner && GetRoom())
1583 GetRoom()->HostileAction(Beam.Owner);
1585 door* Door = door::Spawn(0, NO_MATERIALS);
1586 Door->InitMaterials(MAKE_MATERIAL(STEEL));
1588 if(RAND() % 10)
1589 Door->Lock();
1591 ChangeOLTerrainAndUpdateLights(Door);
1592 return true;
1595 return false;
1598 truth (lsquare::*BeamEffect[BEAM_EFFECTS])(const beamdata&) =
1600 &lsquare::Polymorph,
1601 &lsquare::Strike,
1602 &lsquare::FireBall,
1603 &lsquare::Teleport,
1604 &lsquare::Haste,
1605 &lsquare::Slow,
1606 &lsquare::Resurrect,
1607 &lsquare::Invisibility,
1608 &lsquare::Duplicate,
1609 &lsquare::Lightning,
1610 &lsquare::DoorCreation,
1611 &lsquare::AcidRain,
1612 &lsquare::Necromancy
1615 truth (lsquare::*lsquare::GetBeamEffect(int I))(const beamdata&)
1617 return BeamEffect[I];
1620 truth lsquare::CheckKick(ccharacter* Kicker) const
1622 if(Character && Kicker->CheckIfTooScaredToHit(Character))
1623 return false;
1625 if(RoomIndex && !GetLevel()->GetRoom(RoomIndex)->CheckKickSquare(Kicker, this))
1626 return false;
1628 return true;
1631 void lsquare::GetHitByExplosion(const explosion* Explosion)
1633 if(Explosion->ID == LastExplosionID)
1634 return;
1636 LastExplosionID = Explosion->ID;
1637 int DistanceSquare = (Pos - Explosion->Pos).GetLengthSquare();
1639 if(DistanceSquare > Explosion->RadiusSquare)
1640 return;
1642 int Damage = Explosion->Strength / (DistanceSquare + 1);
1644 if (Character && (Explosion->HurtNeutrals || (Explosion->Terrorist && Character->GetRelation(Explosion->Terrorist) == HOSTILE))) {
1645 if (Character->IsPlayer()) game::SetPlayerWasHurtByExplosion(true);
1646 else Character->GetHitByExplosion(Explosion, Damage);
1649 GetStack()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, FIRE);
1650 GetStack()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE);
1652 ReceiveTrapDamage(Explosion->Terrorist, Damage >> 1, FIRE);
1653 ReceiveTrapDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE);
1655 if(GetOLTerrain())
1656 GetOLTerrain()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, FIRE);
1658 if(GetOLTerrain())
1659 GetOLTerrain()->ReceiveDamage(Explosion->Terrorist, Damage >> 1, PHYSICAL_DAMAGE);
1662 int lsquare::GetSpoiledItems() const
1664 return GetStack()->GetSpoiledItems();
1667 truth lsquare::LowerEnchantment(const beamdata& Beam)
1669 character* Char = GetCharacter();
1670 itemvector AllItems;
1671 sortdata SortData(AllItems, Beam.Owner, true, &item::IsEnchantable);
1672 SortAllItems(SortData);
1673 item* RandomItem;
1675 if(!AllItems.empty())
1676 RandomItem = AllItems[RAND() % AllItems.size()];
1677 else
1678 return false;
1680 if(Char)
1682 if(Char->IsPlayer())
1683 ADD_MESSAGE("%s glows blue for a moment!", RandomItem->CHAR_NAME(DEFINITE));
1685 if(Beam.Owner)
1686 Beam.Owner->Hostility(Char);
1689 if(RandomItem->GetEnchantment() > -5)
1690 RandomItem->EditEnchantment(-1);
1692 return true;
1695 void lsquare::SortAllItems(const sortdata& SortData)
1697 if(GetCharacter())
1698 GetCharacter()->SortAllItems(SortData);
1700 GetStack()->SortAllItems(SortData);
1704 truth lsquare::SoftenMaterial (const beamdata &Beam) {
1705 character *Char = GetCharacter();
1706 item *RandomItem;
1707 itemvector AllItems;
1709 sortdata SortData(AllItems, Beam.Owner, true, &item::IsEnchantable);
1710 SortAllItems(SortData);
1711 //sortdata SortData2(AllItems, Beam.Owner, true, &item::MaterialIsChangeable);
1712 //SortAllItems(SortData2);
1713 if (AllItems.empty()) return false;
1714 RandomItem = AllItems[RAND() % AllItems.size()];
1715 if (Char) {
1716 if (Char->IsPlayer()) ADD_MESSAGE("Your %s glows yellow for a moment!", RandomItem->CHAR_NAME(UNARTICLED));
1717 if (Beam.Owner) Beam.Owner->Hostility(Char);
1720 truth Changed = 0;
1721 festring Desc;
1723 RandomItem->AddName(Desc, UNARTICLED);
1724 material *OldMaterial = RandomItem->GetMainMaterial();
1725 int NewMaterial = RandomItem->GetMainMaterial()->GetSoftenedMaterial(RandomItem);
1727 if (NewMaterial != NONE) {
1728 /* Don't Forget! It is an ugly thing, I know, but removal = seg-fault since cannot have NONE material */
1729 RandomItem->ChangeMainMaterial(MAKE_MATERIAL(NewMaterial)); /*->SpawnMore()*/
1730 if (OldMaterial->GetConfig() != NewMaterial) Changed = 1;
1732 if (Char) {
1733 if (Changed && Char->IsPlayer()) {
1734 ADD_MESSAGE("Your %s softens into %s!", Desc.CStr(), RandomItem->GetMainMaterial()->GetName(false, false).CStr());
1735 } else if (Changed) {
1736 ADD_MESSAGE("%s's %s softens into %s!", Char->CHAR_DESCRIPTION(DEFINITE), Desc.CStr(), RandomItem->GetMainMaterial()->GetName(false, false).CStr());
1738 if (!Changed) {
1739 //may not need this message
1740 if (Char->IsPlayer()) {
1741 ADD_MESSAGE("Your %s vibrates slightly but remains unchanged.", RandomItem->CHAR_NAME(UNARTICLED) );
1742 } else {
1743 ADD_MESSAGE("%s's %s vibrates slightly but remains unchanged.", Char->CHAR_DESCRIPTION(DEFINITE), RandomItem->CHAR_NAME(UNARTICLED) );
1747 return true;
1751 void lsquare::RemoveSmoke(smoke* ToBeRemoved)
1753 smoke* S = Smoke;
1755 if(S == ToBeRemoved)
1757 Smoke = S->Next;
1759 if(!S)
1760 DecAnimatedEntities();
1762 else
1764 smoke* LS;
1768 LS = S;
1769 S = S->Next;
1771 while(S != ToBeRemoved);
1773 LS->Next = S->Next;
1777 void lsquare::AddSmoke(gas* ToBeAdded)
1779 smoke* S = Smoke;
1781 if(!S)
1783 Smoke = new smoke(ToBeAdded, this);
1784 IncAnimatedEntities();
1786 else
1788 smoke* LS;
1792 if(ToBeAdded->IsSameAs(S->GetGas()))
1794 S->Merge(ToBeAdded);
1795 return;
1798 LS = S;
1799 S = S->Next;
1801 while(S);
1803 LS->Next = new smoke(ToBeAdded, this);
1807 void lsquare::ShowSmokeMessage() const
1809 for(const smoke* S = Smoke; S; S = S->Next)
1810 S->AddBreatheMessage();
1813 void lsquare::SignalSmokeAlphaChange(int What)
1815 SmokeAlphaSum += What;
1816 SignalPossibleTransparencyChange();
1819 int lsquare::GetDivineMaster() const
1821 return RoomIndex ? GetLevel()->GetRoom(RoomIndex)->GetDivineMaster() : 0;
1824 void lsquare::DisplaySmokeInfo (festring &Msg) const {
1825 if (Smoke) {
1826 if (!Smoke->Next)
1827 Msg << " A cloud of " << Smoke->GetGas()->GetName(false, false) << " surrounds the square.";
1828 else
1829 Msg << " A lot of gases hover over the square.";
1833 void lsquare::ReceiveEarthQuakeDamage()
1835 GetStack()->ReceiveDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE);
1836 ReceiveTrapDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE);
1837 /* Gum solution */
1839 if(GetOLTerrain() && GetOLTerrain()->IsDoor())
1840 GetOLTerrain()->ReceiveDamage(0, 5 + RAND() % 10, PHYSICAL_DAMAGE);
1843 truth lsquare::CanBeFeltByPlayer() const
1845 if (!PLAYER) return false;
1846 return OLTerrain && !PLAYER->CanMoveOn(this) && Pos.IsAdjacent(PLAYER->GetPos());
1849 void lsquare::PreProcessForBone()
1851 DestroyMemorized();
1852 LastSeen = 0;
1854 if(OLTerrain)
1855 OLTerrain->PreProcessForBone();
1857 if(Smoke)
1859 DecAnimatedEntities();
1861 for(smoke* S = Smoke; S; S = S->Next)
1862 S->SendToHell();
1864 Smoke = 0;
1865 SmokeAlphaSum = 0;
1868 if(Character && !Character->PreProcessForBone())
1870 Character->SendToHell();
1871 Character->Remove();
1874 for(fluid* F = Fluid; F; F = F->Next)
1875 F->PreProcessForBone();
1877 for(trap* T = Trap; T; T = T->Next)
1878 T->PreProcessForBone();
1880 GetStack()->PreProcessForBone();
1883 void lsquare::PostProcessForBone(double& DangerSum, int& Enemies)
1885 if(OLTerrain)
1886 OLTerrain->PostProcessForBone();
1888 if(Character && !Character->PostProcessForBone(DangerSum, Enemies))
1890 Character->SendToHell();
1891 Character->Remove();
1894 for(fluid* F = Fluid; F; F = F->Next)
1895 F->PostProcessForBone();
1897 for(trap* T = Trap; T; T = T->Next)
1898 T->PostProcessForBone();
1900 GetStack()->PostProcessForBone();
1903 void lsquare::FinalProcessForBone()
1905 if(OLTerrain)
1906 OLTerrain->FinalProcessForBone();
1908 if(Character)
1909 Character->FinalProcessForBone();
1911 GetStack()->FinalProcessForBone();
1914 truth lsquare::EngravingsCanBeReadByPlayer()
1916 return PLAYER->CanRead(); // Might be a good idea to improve sometime in the distant future.
1919 void lsquare::DisplayEngravedInfo(festring& Buffer) const
1921 Buffer << " There is a message engraved here: \"" << Engraved << '\"';
1924 truth lsquare::IsDangerousToBreathe(ccharacter* Who) const
1926 for(const smoke* S = Smoke; S; S = S->Next)
1927 if(S->IsDangerousToBreathe(Who))
1928 return true;
1930 return false;
1933 truth lsquare::IsScaryToBreathe(ccharacter* Who) const
1935 for(const smoke* S = Smoke; S; S = S->Next)
1936 if(S->IsScaryToBreathe(Who))
1937 return true;
1939 return false;
1943 struct groundborderpartner {
1944 truth operator < (const groundborderpartner &P) const { return Terrain->GetBorderTilePriority() < P.Terrain->GetBorderTilePriority(); }
1945 glterrain *Terrain;
1946 int SquareIndex;
1950 void lsquare::CalculateGroundBorderPartners () {
1951 if (GroundBorderPartnerInfo & BORDER_PARTNER_ANIMATED) DecStaticAnimatedEntities();
1952 groundborderpartner BorderPartner[8*2]; //k8: *2 to make g++ shut up (WTF?!)
1953 int Index = 0;
1954 int Priority = GLTerrain->GetBorderTilePriority();
1955 for (int d = 0; d < 8; ++d) {
1956 lsquare *Square = NeighbourLSquare[d];
1957 if (Square) {
1958 glterrain *Terrain = Square->GetGLTerrain();
1959 if (Terrain && Terrain->UseBorderTiles() && Terrain->GetBorderTilePriority() > Priority) {
1960 BorderPartner[Index].Terrain = Terrain;
1961 BorderPartner[Index].SquareIndex = 7-d;
1962 ++Index;
1966 GroundBorderPartnerInfo = 0;
1967 if (!Index) {
1968 delete [] GroundBorderPartnerTerrain;
1969 GroundBorderPartnerTerrain = 0;
1970 return;
1972 if (!GroundBorderPartnerTerrain) GroundBorderPartnerTerrain = new glterrain *[8];
1973 std::sort(BorderPartner, BorderPartner+Index); //k8: why g++ complains here? ah, ignore it for now
1974 truth Animated = false;
1975 for (int c = 0; c < Index; ++c) {
1976 glterrain *T = BorderPartner[c].Terrain;
1977 GroundBorderPartnerTerrain[c] = T;
1978 GroundBorderPartnerInfo |= BorderPartner[c].SquareIndex<<((c<<1)+c);
1979 if (T->IsAnimated()) Animated = true;
1981 if (Animated) {
1982 GroundBorderPartnerInfo |= BORDER_PARTNER_ANIMATED;
1983 IncStaticAnimatedEntities();
1985 GroundBorderPartnerInfo |= Index<<24;
1989 struct overborderpartner {
1990 truth operator < (const overborderpartner &P) const { return Terrain->GetBorderTilePriority() < P.Terrain->GetBorderTilePriority(); }
1991 olterrain *Terrain;
1992 int SquareIndex;
1995 void lsquare::CalculateOverBorderPartners () {
1996 if (OverBorderPartnerInfo & BORDER_PARTNER_ANIMATED) DecStaticAnimatedEntities();
1997 overborderpartner BorderPartner[8*2]; //k8: *2 to make g++ shut up (WTF?!)
1998 int Index = 0;
1999 int Priority = OLTerrain ? OLTerrain->GetBorderTilePriority() : 0;
2000 for (int d = 0; d < 8; ++d) {
2001 lsquare *Square = NeighbourLSquare[d];
2002 if (Square) {
2003 olterrain *Terrain = Square->GetOLTerrain();
2004 if (Terrain && Terrain->UseBorderTiles() && Terrain->GetBorderTilePriority() > Priority) {
2005 BorderPartner[Index].Terrain = Terrain;
2006 BorderPartner[Index].SquareIndex = 7-d;
2007 ++Index;
2011 OverBorderPartnerInfo = 0;
2012 if (!Index) {
2013 delete [] OverBorderPartnerTerrain;
2014 OverBorderPartnerTerrain = 0;
2015 return;
2017 if (!OverBorderPartnerTerrain) OverBorderPartnerTerrain = new olterrain *[8];
2018 std::sort(BorderPartner, BorderPartner+Index); //k8: why g++ complains here? ah, ignore it for now
2019 truth Animated = false;
2020 for (int c = 0; c < Index; ++c) {
2021 olterrain *T = BorderPartner[c].Terrain;
2022 OverBorderPartnerTerrain[c] = T;
2023 OverBorderPartnerInfo |= BorderPartner[c].SquareIndex<<((c<<1)+c);
2024 if (T->IsAnimated()) Animated = true;
2026 if (Animated) {
2027 OverBorderPartnerInfo |= BORDER_PARTNER_ANIMATED;
2028 IncStaticAnimatedEntities();
2030 OverBorderPartnerInfo |= Index<<24;
2031 /*k8
2032 if (OverBorderPartnerInfo & BORDER_PARTNER_ANIMATED)
2033 int esko = esko = 2;
2038 void lsquare::RequestForGroundBorderPartnerUpdates()
2040 if(!game::IsGenerating())
2041 for(int d = 0; d < 8; ++d)
2043 lsquare* Square = NeighbourLSquare[d];
2045 if(Square)
2047 Square->CalculateGroundBorderPartners();
2048 Square->SendNewDrawRequest();
2049 Square->SendMemorizedUpdateRequest();
2054 void lsquare::RequestForOverBorderPartnerUpdates()
2056 if(!game::IsGenerating())
2057 for(int d = 0; d < 8; ++d)
2059 lsquare* Square = NeighbourLSquare[d];
2061 if(Square)
2063 Square->CalculateOverBorderPartners();
2064 Square->SendNewDrawRequest();
2065 Square->SendMemorizedUpdateRequest();
2070 int lsquare::GetWalkability() const
2072 if(!GetLevel()->IsOnGround())
2074 if(Pos.X >= 1 && Pos.Y >= 1 && Pos.X < GetLevel()->GetXSize() - 1 && Pos.Y < GetLevel()->GetYSize() - 1)
2075 return OLTerrain ? OLTerrain->GetWalkability() & GLTerrain->GetWalkability() : GLTerrain->GetWalkability();
2076 else
2077 return 0;
2079 else
2080 return OLTerrain ? OLTerrain->GetWalkability() & GLTerrain->GetWalkability() : GLTerrain->GetWalkability();
2083 void lsquare::RemoveFluid(fluid* ToRemove)
2085 fluid*& F = ListFind(Fluid, pointercomparer<fluid>(ToRemove));
2086 F = F->Next;
2087 SignalEmitationDecrease(ToRemove->GetEmitation());
2090 struct fluidcomparer
2092 fluidcomparer(const liquid* Liquid) : Liquid(Liquid) { }
2093 truth operator()(const fluid* F) const { return Liquid->IsSameAs(F->GetLiquid()); }
2094 const liquid* Liquid;
2097 fluid* lsquare::AddFluid(liquid* ToBeAdded)
2099 fluid*& F = ListFind(Fluid, fluidcomparer(ToBeAdded));
2101 if(F)
2103 F->AddLiquidAndVolume(ToBeAdded->GetVolume());
2104 delete ToBeAdded;
2106 else
2108 F = new fluid(ToBeAdded, this);
2109 SignalEmitationIncrease(ToBeAdded->GetEmitation());
2112 SendNewDrawRequest();
2113 SendMemorizedUpdateRequest();
2114 return F;
2117 void lsquare::DisplayFluidInfo(festring& Msg) const
2119 if(Fluid)
2121 Msg << ". There is ";
2122 fluid::AddFluidInfo(Fluid, Msg);
2123 AddLocationDescription(Msg);
2127 void lsquare::SpillFluid(character* Spiller, liquid* Liquid, truth ForceHit, truth ShowMsg)
2129 if(!Liquid->GetVolume())
2131 delete Liquid;
2132 return;
2135 if(IsFlyable())
2137 if(GetCharacter())
2139 if(Spiller && !GetCharacter()->IsAlly(Spiller))
2140 Spiller->Hostility(GetCharacter());
2142 sLong CharVolume = GetCharacter()->GetVolume();
2143 double ChanceMultiplier = 1. / (300 + sqrt(GetStack()->GetVolume() + CharVolume));
2144 double Root = sqrt(CharVolume);
2146 if(ForceHit || Root > RAND() % 400 || Root > RAND() % 400)
2148 sLong SpillVolume = sLong(Liquid->GetVolume() * Root * ChanceMultiplier);
2150 if(SpillVolume)
2152 if(ShowMsg && (GetCharacter()->IsPlayer() || GetCharacter()->CanBeSeenByPlayer()))
2153 ADD_MESSAGE("%s is spilled all over %s.", Liquid->GetName(false, false).CStr(), GetCharacter()->CHAR_DESCRIPTION(DEFINITE));
2155 Liquid->EditVolume(-SpillVolume);
2156 GetCharacter()->SpillFluid(Spiller, Liquid->SpawnMoreLiquid(SpillVolume), GetCharacter()->GetSquareIndex(GetPos()));
2161 GetStack()->SpillFluid(Spiller, Liquid, Liquid->GetVolume());
2164 if(Liquid->GetVolume() && !Liquid->IsSameAs(GLTerrain->GetMainMaterial()))
2166 fluid* F = AddFluid(Liquid);
2168 if(GetCharacter())
2169 F->StepOnEffect(GetCharacter());
2171 else
2172 delete Liquid;
2175 void lsquare::DrawStacks(blitdata& BlitData) const
2177 Stack->Draw(PLAYER, BlitData, CENTER);
2179 for(int c = 0; c < 4; ++c)
2181 stack* Stack = GetStackOfAdjacentSquare(c);
2183 if(Stack)
2184 Stack->Draw(PLAYER, BlitData, 3 - c);
2188 void lsquare::RemoveRain(rain* ToBeRemoved)
2190 SendNewDrawRequest();
2191 rain* R = Rain;
2193 if(ToBeRemoved->IsEnabled())
2194 DecAnimatedEntities();
2196 if(R == ToBeRemoved)
2197 Rain = R->Next;
2198 else
2200 rain* LR;
2204 LR = R;
2205 R = R->Next;
2207 while(R != ToBeRemoved);
2209 LR->Next = R->Next;
2212 SignalEmitationDecrease(ToBeRemoved->GetEmitation());
2215 void lsquare::AddRain(liquid* RainLiquid, v2 Speed, int Team, truth OwnLiquid)
2217 rain* R = Rain, * NewRain = new rain(RainLiquid, this, Speed, Team, OwnLiquid);
2219 if(NewRain->IsEnabled())
2220 IncAnimatedEntities();
2222 if(!R)
2223 Rain = NewRain;
2224 else
2226 rain* LR;
2230 LR = R;
2231 R = R->Next;
2233 while(R);
2235 LR->Next = NewRain;
2239 void lsquare::RemoveSunLight()
2241 SunLightLuminance = 0;
2242 SunEmitter.clear();
2245 void lsquare::CheckIfIsSecondarySunLightEmitter()
2247 col24 OldEmitation = SecondarySunLightEmitation;
2249 if(Flags & IS_TRANSPARENT && (!(Flags & INSIDE) || SunLightLuminance))
2251 for(int d = 0; d < 8; ++d)
2253 lsquare* Neighbour = NeighbourLSquare[d];
2255 if(Neighbour && Neighbour->Flags & INSIDE)
2257 col24 NewEmitation = GetLevel()->GetAmbientLuminance();
2259 if(OldEmitation != NewEmitation)
2261 SecondarySunLightEmitation = NewEmitation;
2263 if(game::CompareLights(NewEmitation, OldEmitation) >= 0)
2264 Emitate(NewEmitation, SECONDARY_SUN_LIGHT);
2265 else
2267 Noxify(OldEmitation, SECONDARY_SUN_LIGHT);
2268 Emitate(NewEmitation, SECONDARY_SUN_LIGHT|FORCE_ADD);
2272 return;
2277 if(OldEmitation)
2279 Noxify(OldEmitation, SECONDARY_SUN_LIGHT);
2280 SecondarySunLightEmitation = 0;
2284 void lsquare::CalculateNeighbourLSquares()
2286 int XSize = GetLevel()->GetXSize();
2287 int YSize = GetLevel()->GetYSize();
2289 for(int d = 0; d < 8; ++d)
2291 v2 NPos = Pos + game::GetMoveVector(d);
2293 if(NPos.X >= 0 && NPos.Y >= 0 && NPos.X < XSize && NPos.Y < YSize)
2294 NeighbourLSquare[d] = GetLevel()->GetLSquare(NPos);
2295 else
2296 NeighbourLSquare[d] = 0;
2300 void lsquare::RemoveLuminance(col24& Emitation)
2302 col24 OldLuminance = Luminance;
2303 col24 OldEmitation = Emitation;
2304 Emitation = 0;
2306 if(game::CompareLights(OldEmitation, OldLuminance) < 0) return;
2308 if(!(Flags & IS_TRANSPARENT))
2310 Flags |= NEW_DRAW_REQUEST;
2312 if(LastSeen == game::GetLOSTick()) game::SendLOSUpdateRequest();
2314 else
2316 CalculateLuminance();
2318 if(OldLuminance == Luminance)
2319 return;
2321 Flags |= NEW_DRAW_REQUEST;
2323 if(!Luminance)
2325 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
2327 if(LastSeen == game::GetLOSTick()) game::SendLOSUpdateRequest();
2332 void lsquare::ChangeLuminance(col24& Emitation, col24 NewLuminance)
2334 col24 OldLuminance = Luminance;
2336 if(!(Flags & IS_TRANSPARENT))
2338 Emitation = NewLuminance;
2339 Flags |= NEW_DRAW_REQUEST;
2341 if(LastSeen == game::GetLOSTick())
2342 game::SendLOSUpdateRequest();
2344 return;
2347 truth EmitationInsignificant = !Emitation
2348 || game::CompareLights(Emitation, OldLuminance) < 0;
2349 Emitation = NewLuminance;
2351 if(game::CompareLights(NewLuminance, OldLuminance) > 0 && EmitationInsignificant)
2352 game::CombineLights(Luminance, NewLuminance);
2353 else
2355 if(EmitationInsignificant)
2356 return;
2358 CalculateLuminance();
2360 if(OldLuminance == Luminance)
2361 return;
2364 Flags |= NEW_DRAW_REQUEST;
2366 if(!OldLuminance)
2368 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
2370 if(LastSeen == game::GetLOSTick())
2371 game::SendLOSUpdateRequest();
2375 void lsquare::EnableGlobalRain()
2377 for(rain* R = Rain; R; R = R->Next)
2378 if(!R->HasOwnLiquid())
2380 R->Enable();
2381 IncAnimatedEntities();
2385 void lsquare::DisableGlobalRain()
2387 SendNewDrawRequest();
2389 for(rain* R = Rain; R; R = R->Next)
2390 if(!R->HasOwnLiquid())
2392 R->Disable();
2393 DecAnimatedEntities();
2397 void lsquare::InitLastSeen()
2399 LastSeen = LastSeen == game::GetLOSTick() ? 2 : 0;
2400 SquarePartLastSeen = 0;
2403 truth lsquare::Engrave(cfestring& What)
2405 if(Engraved)
2406 delete [] Engraved;
2408 if(!What.IsEmpty())
2410 Engraved = new char[What.GetSize() + 1];
2411 strcpy(Engraved, What.CStr());
2413 else
2414 Engraved = 0;
2416 return true;
2419 void lsquare::SendSunLightSignals()
2421 if(Flags & IS_TRANSPARENT)
2423 col24 OldLuminance = Luminance;
2424 CalculateLuminance();
2426 if(Luminance != OldLuminance)
2428 Flags |= NEW_DRAW_REQUEST;
2430 if(!Luminance != !OldLuminance)
2432 Flags |= MEMORIZED_UPDATE_REQUEST|DESCRIPTION_CHANGE;
2434 if(LastSeen == game::GetLOSTick())
2435 game::SendLOSUpdateRequest();
2439 else
2441 Flags |= NEW_DRAW_REQUEST;
2443 if(LastSeen == game::GetLOSTick())
2444 game::SendLOSUpdateRequest();
2448 void lsquare::ZeroReSunEmitatedFlags()
2450 sunemittervector::iterator i, End = SunEmitter.end();
2452 for(i = SunEmitter.begin(); i != End; ++i) *i &= ~RE_SUN_EMITATED;
2455 truth lsquare::CalculateIsTransparent()
2457 if((!OLTerrain || OLTerrain->IsTransparent()) && SmokeAlphaSum < 175
2458 && (!Character || Character->IsTransparent()))
2460 Flags |= IS_TRANSPARENT;
2461 return true;
2463 else
2465 Flags &= ~IS_TRANSPARENT;
2466 return false;
2470 void lsquare::CalculateSunLightLuminance(feuLong SeenBitMask)
2472 sunemittervector::const_iterator i, SunEnd = SunEmitter.end();
2473 int S = 0, L = 0;
2475 for(i = SunEmitter.begin(); i != SunEnd; ++i)
2477 feuLong ShadowFlag = 1 << EMITTER_SHADOW_SHIFT;
2478 feuLong SquarePartFlag = 1 << EMITTER_SQUARE_PART_SHIFT;
2479 for (int c = 0; c < 4; ++c, ShadowFlag <<= 1, SquarePartFlag <<= 1) {
2480 if (SeenBitMask & *i & SquarePartFlag) {
2481 if (*i & ShadowFlag) ++S; else ++L;
2486 if(!L)
2487 SunLightLuminance = 0;
2488 else if(!S)
2489 SunLightLuminance = GetLevel()->GetSunLightEmitation();
2490 else
2492 col24 ShadowColor = GetLevel()->GetAmbientLuminance();
2493 col24 LightColor = GetLevel()->GetSunLightEmitation();
2494 SunLightLuminance = MakeRGB24(
2495 (GetRed24(LightColor)*L+GetRed24(ShadowColor)*S)/(S+L),
2496 (GetGreen24(LightColor)*L+GetGreen24(ShadowColor)*S)/(S+L),
2497 (GetBlue24(LightColor)*L+GetBlue24(ShadowColor)*S)/(S+L));
2501 void lsquare::CreateMemorized()
2503 Memorized = new bitmap(TILE_V2);
2504 Memorized->ActivateFastFlag();
2505 FowMemorized = new bitmap(TILE_V2);
2506 FowMemorized->ActivateFastFlag();
2509 truth lsquare::AcidRain(const beamdata& Beam)
2511 if(!IsFlyable() || GetCharacter() || Beam.Direction == YOURSELF)
2513 int StackSize = GetLevel()->AddRadiusToSquareStack(Pos, 9);
2514 lsquare** Stack = GetLevel()->GetSquareStack();
2515 v2 Speed = v2(512, 512);
2516 int Team = Beam.Owner ? Beam.Owner->GetTeam()->GetID() : MONSTER_TEAM;
2518 for(int c = 0; c < StackSize; ++c)
2520 Stack[c]->AddRain(liquid::Spawn(SULPHURIC_ACID, 300), Speed, Team, true);
2521 Stack[c]->Flags &= ~IN_SQUARE_STACK;
2524 if(Beam.Owner && Character && Character->GetTeam() != Beam.Owner->GetTeam())
2525 Beam.Owner->Hostility(Character);
2527 return true;
2530 return false;
2533 truth lsquare::DetectMaterial (cmaterial *Material) const {
2534 if (GLTerrain->DetectMaterial(Material) ||
2535 (OLTerrain && OLTerrain->DetectMaterial(Material)) ||
2536 Stack->DetectMaterial(Material) ||
2537 (Character && Character->DetectMaterial(Material))) return true;
2538 for (const fluid *F = Fluid; F; F = F->Next) if (F->GetLiquid()->IsSameAs(Material)) return true;
2539 for (const smoke *S = Smoke; S; S = S->Next) if (S->GetGas()->IsSameAs(Material)) return true;
2540 for (const rain *R = Rain; R; R = R->Next) if (R->GetLiquid()->IsSameAs(Material)) return true;
2541 return false;
2544 void lsquare::Reveal(feuLong Tick, truth IgnoreDarkness)
2546 if(!Memorized)
2547 CreateMemorized();
2549 LastSeen = Tick;
2551 if(IgnoreDarkness)
2552 Luminance = NORMAL_LUMINANCE;
2553 else
2555 SquarePartLastSeen = 0;
2557 for(int c = 0; c < 4; ++c)
2558 SquarePartLastSeen |= (Tick << (c << 3));
2560 CalculateLuminance();
2563 Flags |=
2564 NEW_DRAW_REQUEST|
2565 MEMORIZED_UPDATE_REQUEST|
2566 DESCRIPTION_CHANGE;
2567 UpdateMemorized();
2568 UpdateMemorizedDescription();
2571 void lsquare::DestroyMemorized()
2573 delete Memorized;
2574 delete FowMemorized;
2575 Memorized = 0;
2576 FowMemorized = 0;
2579 void lsquare::SwapMemorized(lsquare* Square)
2581 Swap(Memorized, Square->Memorized);
2582 Swap(FowMemorized, Square->FowMemorized);
2583 MemorizedDescription.SwapData(Square->MemorizedDescription);
2586 truth lsquare::Necromancy(const beamdata& Beam)
2588 return GetStack()->Necromancy(Beam.Owner);
2591 // Returns 0 if fails
2593 lsquare* lsquare::GetRandomAdjacentSquare() const
2595 lsquare* OK[8];
2596 int Index = 0;
2598 for(int c = 0; c < 8; ++c)
2600 lsquare* Square = NeighbourLSquare[c];
2602 if(Square)
2603 OK[Index++] = Square;
2606 if(Index)
2607 return OK[RAND_N(Index)];
2608 else
2609 return 0;
2612 truth pathcontroller::Handler(int x, int y)
2614 return Character->CanMoveOn(Map[x][y]);
2617 void lsquare::SignalPossibleTransparencyChange()
2619 truth WasTransparent = IsTransparent();
2620 CalculateIsTransparent();
2622 if(WasTransparent && !IsTransparent())
2624 Flags |= IS_TRANSPARENT;
2625 emittervector EmitterBackup = Emitter;
2626 GetLevel()->ForceEmitterNoxify(EmitterBackup);
2627 Flags &= ~IS_TRANSPARENT;
2628 GetLevel()->ForceEmitterEmitation(EmitterBackup, SunEmitter, FORCE_ADD);
2629 CalculateLuminance();
2630 Flags |= DESCRIPTION_CHANGE|MEMORIZED_UPDATE_REQUEST;
2632 if(LastSeen == game::GetLOSTick())
2633 game::SendLOSUpdateRequest();
2635 else if(!WasTransparent && IsTransparent())
2637 GetLevel()->ForceEmitterEmitation(Emitter, SunEmitter);
2638 CalculateLuminance();
2639 Flags |= DESCRIPTION_CHANGE|MEMORIZED_UPDATE_REQUEST;
2641 if(LastSeen == game::GetLOSTick())
2642 game::SendLOSUpdateRequest();
2646 void lsquare::RemoveTrap(trap* ToRemove)
2648 trap*& T = ListFind(Trap, pointercomparer<trap>(ToRemove));
2649 T = T->Next;
2650 SendNewDrawRequest();
2651 SendMemorizedUpdateRequest();
2654 struct trapcomparer
2656 trapcomparer(int Type) : Type(Type) { }
2657 truth operator()(const trap* T) const { return T->GetType() == Type; }
2658 int Type;
2661 truth lsquare::AddTrap(trap* ToBeAdded)
2663 trap*& T = ListFind(Trap, trapcomparer(ToBeAdded->GetType()));
2665 if(T)
2667 delete ToBeAdded;
2668 return false;
2670 else
2671 T = ToBeAdded;
2673 ToBeAdded->SetLSquareUnder(this);
2674 SendNewDrawRequest();
2675 SendMemorizedUpdateRequest();
2676 return true;
2679 void lsquare::DisplayTrapInfo(festring& Msg) const
2681 for(const trap* T = Trap; T; T = T->Next)
2682 T->AddDescription(Msg);
2685 void lsquare::FillTrapVector(std::vector<trap*>& TrapVector) const
2687 for(trap* T = Trap; T; T = T->Next)
2688 TrapVector.push_back(T);
2691 void lsquare::ReceiveTrapDamage(character* Damager, int Damage, int Type, int Direction)
2693 std::vector<trap*> TrapVector;
2694 FillTrapVector(TrapVector);
2696 for(uInt c = 0; c < TrapVector.size(); ++c)
2697 TrapVector[c]->ReceiveDamage(Damager, Damage, Type, Direction);
2700 truth lsquare::HasDangerousTraps(ccharacter* Who) const
2702 for(trap* T = Trap; T; T = T->Next)
2703 if(T->IsDangerous(Who))
2704 return true;
2706 return false;
2709 truth lsquare::HasDangerousFluids(ccharacter* Who) const
2711 for(const fluid* F = Fluid; F; F = F->Next)
2712 if(F->IsDangerous(Who))
2713 return true;
2715 return false;
2718 truth lsquare::HasNoBorderPartners() const
2720 return !(GroundBorderPartnerInfo >> 24) && !(OverBorderPartnerInfo >> 24);
2723 void lsquare::AddLocationDescription(festring& String) const
2725 if(IsFlyable())
2726 GLTerrain->AddLocationDescription(String);
2727 else
2728 OLTerrain->AddLocationDescription(String);
2731 truth lsquare::VomitingIsDangerous(ccharacter* Char) const
2733 return ((OLTerrain && OLTerrain->VomitingIsDangerous(Char))
2734 || (Character && Character->GetTeam() != Char->GetTeam()
2735 && Character->GetRelation(Char) != HOSTILE));
2738 bool lsquare::TeleportAllSmokeAway()
2740 return false;
2743 bool lsquare::TeleportAllFluidsAway()
2745 return false;
2748 bool lsquare::TeleportAllTrapsAway()
2750 for(trap* T = Trap; T; T = Trap)
2752 T->Untrap();
2753 RemoveTrap(T);
2754 v2 V, Pos = GetPos();
2755 for(V = GetLevel()->GetRandomSquare(); V != Pos; V = GetLevel()->GetRandomSquare());
2756 GetNearLSquare(V)->AddTrap(T);
2759 return false;
2762 void lsquare::AddSpecialCursors()
2764 if((FowMemorized || game::GetSeeWholeMapCheatMode()) && OLTerrain)
2765 OLTerrain->AddSpecialCursors();