3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
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),
46 GroundBorderPartnerTerrain(0),
47 GroundBorderPartnerInfo(0),
48 OverBorderPartnerTerrain(0),
49 OverBorderPartnerInfo(0),
50 SquarePartEmitationTick(0),
51 SquarePartLastSeen(0),
56 TemporaryEmitation(0),
57 SecondarySunLightEmitation(0),
61 Stack
= new stack(this, 0);
65 lsquare::~lsquare () {
75 delete StaticContentCache
.Bitmap
;
76 delete [] GroundBorderPartnerTerrain
;
77 delete [] OverBorderPartnerTerrain
;
85 void lsquare::SignalEmitationIncrease (col24 EmitationUpdate
) {
86 if (game::CompareLights(EmitationUpdate
, Emitation
) > 0 && !game::IsGenerating() && !(Flags
& FREEZED
)) {
87 CalculateEmitation(); // could this be optimized?
93 void lsquare::SignalEmitationDecrease (col24 EmitationUpdate
) {
94 if (game::CompareLights(EmitationUpdate
, Emitation
) >= 0 && Emitation
&& !game::IsGenerating() && !(Flags
& FREEZED
)) {
95 col24 Backup
= Emitation
;
97 if (Backup
!= Emitation
) {
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
,
128 { TILE_SIZE
, TILE_SIZE
},
129 { NORMAL_LUMINANCE
},
132 DrawStaticContents(B
);
133 Memorized
->FastBlit(FowMemorized
);
134 B
.Bitmap
= FowMemorized
;
137 igraph::GetFOWGraphic()->NormalMaskedBlit(B
);
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
,
158 { TILE_SIZE
, TILE_SIZE
},
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
);
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()) {
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();
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
);
226 Character
->Draw(BlitData
);
227 for (const smoke
*S
= Smoke
; S
; S
= S
->Next
) S
->Draw(BlitData
);
229 BlitData
.CustomData
&= ~SQUARE_INDEX_MASK
;
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()) {
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
);
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
];
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
;
293 Square
->Flags
= (SquareFlags
& ~ALLOW_EMITATION_CONTINUE
) | PERFECTLY_QUADRI_HANDLED
;
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
;
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
]));
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
))
348 int Radius
= game::CalculateMinimumEmitationRadius(Emitation
);
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
,
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
);
390 static int GetStartX(int) { return Center
.X
; }
391 static int GetStartY(int) { return Center
.Y
; }
396 feuLong
noxifycontroller::ID
;
397 feuLong
noxifycontroller::Tick
;
399 void lsquare::Noxify(col24 Emitation
, feuLong IDFlags
)
401 if(game::IsDark(Emitation
))
404 int Radius
= game::CalculateMinimumEmitationRadius(Emitation
);
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());
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
)))
445 if(i
->Emitation
!= NewLuminance
)
446 ChangeLuminance(i
->Emitation
, NewLuminance
);
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
;
465 Swap(*i
, SunEmitter
.front());
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
);
483 ADD_MESSAGE("There's something in the way...");
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
);
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
);
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();
528 FowMemorized
= new bitmap(TILE_V2
);
529 FowMemorized
->ActivateFastFlag();
530 Memorized
->FastBlit(FowMemorized
);
531 blitdata B
= { FowMemorized
,
534 { TILE_SIZE
, TILE_SIZE
},
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
);
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
)
574 ABORT("Overgrowth of square population detected!");
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())) {
589 void lsquare::Clean()
594 void lsquare::RemoveCharacter()
598 character
* Backup
= Character
;
600 if(Backup
->IsAnimated())
601 DecAnimatedEntities();
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
);
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());
634 MemorizedDescription
<< "many items";
636 MemorizedDescription
<< " on ";
638 } else if (Anything
) {
639 MemorizedDescription
<< " on ";
642 GLTerrain
->AddName(MemorizedDescription
, INDEFINITE
);
644 GetSideItemDescription(SideItems
, Cheat
);
646 if (!SideItems
.IsEmpty()) MemorizedDescription
<< " and " << SideItems
;
648 if (Anything
) MemorizedDescription
<< " on ";
650 GLTerrain
->AddName(MemorizedDescription
, INDEFINITE
);
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
);
669 MemorizedDescription
= CONST_S("darkness");
672 Flags
&= ~DESCRIPTION_CHANGE
;
676 void lsquare::GetSideItemDescription(festring
& String
, truth Cheat
) const
680 for(int c
= 0; c
< 4; ++c
)
682 stack
* Stack
= GetStackOfAdjacentSquare(c
);
686 ? Stack
->GetSideItems(3 - c
)
687 : Stack
->GetVisibleSideItems(PLAYER
, 3 - c
);
691 String
<< "many items on the wall";
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
)
712 GetCharacter()->BeKicked(Kicker
, Boot
, Leg
, Pos
, KickDamage
, KickToHitValue
, Success
, Direction
, Critical
, ForceHit
);
719 GetLevel()->GetRoom(RoomIndex
)->KickSquare(Kicker
, this);
721 GetStack()->BeKicked(Kicker
, int(KickDamage
), Direction
);
724 GetOLTerrain()->BeKicked(Kicker
, int(KickDamage
* (100 + Success
) / 100), Direction
);
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.");
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();
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();
775 Flags
|= NEW_DRAW_REQUEST
;
776 GetLevel()->SetWalkability(Pos
, GetTheoreticalWalkability());
777 CalculateOverBorderPartners();
778 CalculateIsTransparent();
779 SendMemorizedUpdateRequest();
781 if(WasUsingBorderTiles
|| (NewOver
&& NewOver
->UseBorderTiles()))
782 RequestForOverBorderPartnerUpdates();
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();
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();
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();
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();
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();
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()) {
886 fprintf(stderr
, "forced altar!\n");
888 terra
= altar::Spawn(Room
->GetDivineMaster());
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
);
908 terra
= altar::Spawn(Owner
);
910 fprintf(stderr
, "spawned altar in room w/o divine master, assigning %d\n", terra
->GetConfig());
913 fprintf(stderr
, "spawned altar in room w/o divine master, assigning %d\n", terra
->GetConfig());
915 Room
->SetDivineMaster(terra
->GetConfig());
919 ChangeOLTerrain(terra
);
921 //fprintf(stderr, "WARNING: LTerra spawn error [lsquare] in file %s, line %d\n", OLTerrainScript->GetSrcFile().CStr(), OLTerrainScript->GetSrcLine());
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)) {
938 eyecontroller::Map
= GetLevel()->GetMap();
939 return mapmath
<eyecontroller
>::DoLine(FromPos
.X
, FromPos
.Y
, GetPos().X
, GetPos().Y
, SKIP_FIRST
|LINE_BOTH_DIRS
);
945 void lsquare::StepOn (character
* Stepper
, lsquare
** ComingFrom
) {
947 truth WasInRoom
= false;
950 for (int c
= 0; c
< Stepper
->GetSquaresUnder(); ++c
) {
951 if (ComingFrom
[c
]->GetRoomIndex() == RoomIndex
) {
958 if (!WasInRoom
) GetLevel()->GetRoom(RoomIndex
)->Enter(Stepper
);
961 GLTerrain
->StepOn(Stepper
);
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();
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
);
1032 sLong OldEmit
= OLTerrain
? OLTerrain
->GetEmitation() : 0;
1033 ChangeOLTerrain(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
)
1052 if(!WasTransparent
== !!CalculateIsTransparent())
1055 GetLevel()->ForceEmitterEmitation(EmitterBackup
, SunEmitter
, FORCE_ADD
);
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
)
1076 clock_t StartTime
= clock();
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
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
))
1107 return (GLTerrain
->IsDipDestination() && GLTerrain
->DipInto(Thingy
, Dipper
)) || (OLTerrain
&& OLTerrain
->IsDipDestination() && OLTerrain
->DipInto(Thingy
, Dipper
));
1111 if(Dipper
->IsPlayer())
1112 ADD_MESSAGE("You cannot dip %s on that square!", Thingy
->CHAR_NAME(DEFINITE
));
1118 // return true if key fits someplace
1120 truth
lsquare::TryKey(item
* Key
, character
* Applier
)
1122 if(GetOLTerrain() && GetOLTerrain()->TryKey(Key
, Applier
))
1125 if((!GetOLTerrain() || !GetOLTerrain()->HasKeyHole()) && !GetStack()->TryKey(Key
, Applier
))
1127 ADD_MESSAGE("There's no place here to put the key in!");
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
);
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
;
1158 v2 Dist
= Pos
- PLAYER
->GetPos();
1160 if(abs(Dist
.X
) > 1 || abs(Dist
.Y
) > 1)
1171 UpdateMemorizedDescription();
1174 Character
->CheckIfSeen();
1178 #define DIMMED_LUMINANCE 0x656565
1180 void lsquare::DrawMemorized (blitdata
&BlitData
) const {
1182 Flags
&= ~STRONG_NEW_DRAW_REQUEST
;
1183 BlitData
.Luminance
= ivanconfig::GetContrastLuminance();
1186 blitdata nbd
= BlitData
;
1187 if (nbd
.Luminance
== NORMAL_LUMINANCE
) nbd
.Luminance
= DIMMED_LUMINANCE
;
1188 FowMemorized
->LuminanceBlit(nbd
);
1190 DOUBLE_BUFFER
->Fill(BlitData
.Dest
, BlitData
.Border
, 0);
1193 ccharacter
*C
= Character
;
1195 if (C
&& C
->CanBeSeenByPlayer()) {
1196 BlitData
.CustomData
|= C
->GetSquareIndex(Pos
);
1198 BlitData
.CustomData
&= ~SQUARE_INDEX_MASK
;
1203 void lsquare::DrawMemorizedCharacter (blitdata
&BlitData
) const {
1204 BlitData
.Luminance
= ivanconfig::GetContrastLuminance();
1207 blitdata nbd
= BlitData
;
1208 if (nbd
.Luminance
== NORMAL_LUMINANCE
) nbd
.Luminance
= DIMMED_LUMINANCE
;
1209 FowMemorized
->LuminanceBlit(nbd
);
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
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();
1256 UpdateMemorizedDescription();
1261 void lsquare::KickAnyoneStandingHereAway()
1265 character
* Backup
= Character
;
1267 Backup
->PutNear(Pos
);
1271 outputfile
& operator<<(outputfile
& SaveFile
, const emitter
& Emitter
)
1273 SaveFile
.Write(reinterpret_cast<cchar
*>(&Emitter
), sizeof(Emitter
));
1277 inputfile
& operator>>(inputfile
& SaveFile
, emitter
& Emitter
)
1279 SaveFile
.Read(reinterpret_cast<char*>(&Emitter
), sizeof(Emitter
));
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))
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
));
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
));
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
));
1339 game::DrawEverythingNoBlit();
1341 blitdata B
= { DOUBLE_BUFFER
,
1344 { TILE_SIZE
, TILE_SIZE
},
1349 B
.Dest
= game::CalculateScreenCoordinates(GetPos());
1350 Empty
.NormalMaskedBlit(B
);
1351 Flags
|= STRONG_NEW_DRAW_REQUEST
;
1355 graphics::BlitDBToScreen();
1356 while(clock() - StartTime
< 0.02 * CLOCKS_PER_SEC
);
1362 truth
lsquare::Polymorph(const beamdata
& Beam
)
1364 GetStack()->Polymorph(Beam
.Owner
);
1367 GetOLTerrain()->Polymorph(Beam
.Owner
);
1369 character
* Character
= GetCharacter();
1373 if(Beam
.Owner
&& Character
->GetTeam() != Beam
.Owner
->GetTeam())
1374 Beam
.Owner
->Hostility(Character
);
1376 Character
->PolymorphRandomly(1, 999999, 5000 + RAND() % 5000);
1381 for(int c
= 0; Engraved
[c
] != '\0'; ++c
)
1385 Engraved
[c
] = 32 + RAND_N(95);
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();
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
));
1408 Beam
.Owner
->Hostility(Char
);
1410 Char
->ReceiveDamage(Beam
.Owner
, Damage
, ENERGY
, ALL
);
1411 Char
->CheckDeath(Beam
.DeathMsg
, Beam
.Owner
);
1415 GetOLTerrain()->ReceiveDamage(Beam
.Owner
, Damage
, ENERGY
);
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);
1434 truth
lsquare::Teleport(const beamdata
& Beam
)
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();
1450 GetLevel()->GetRoom(RoomIndex
)->TeleportSquare(Beam
.Owner
, this);
1452 GetStack()->TeleportRandomly();
1456 truth
lsquare::Haste(const beamdata
&)
1458 GetStack()->Haste();
1459 character
* Dude
= GetCharacter();
1467 truth
lsquare::Slow(const beamdata
& Beam
)
1470 character
* Dude
= GetCharacter();
1475 Beam
.Owner
->Hostility(Dude
);
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);
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
);
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);
1517 truth
lsquare::Resurrect(const beamdata
& Beam
)
1520 return GetCharacter()->RaiseTheDead(Beam
.Owner
);
1522 return GetStack()->RaiseTheDead(Beam
.Owner
);
1525 truth
lsquare::Invisibility(const beamdata
&)
1528 GetCharacter()->BeginTemporaryState(INVISIBLE
, 1000 + RAND() % 1001);
1533 truth
lsquare::Duplicate(const beamdata
& Beam
)
1535 truth DuplicatedSomething
= false;
1536 character
* Character
= GetCharacter();
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();
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
));
1563 Beam
.Owner
->Hostility(Char
);
1565 Char
->ReceiveDamage(Beam
.Owner
, Damage
, ELECTRICITY
, ALL
);
1566 Char
->CheckDeath(Beam
.DeathMsg
, Beam
.Owner
);
1570 GetOLTerrain()->ReceiveDamage(Beam
.Owner
, Damage
, ELECTRICITY
);
1575 truth
lsquare::DoorCreation(const beamdata
& Beam
)
1577 if ((!GetOLTerrain() || GetOLTerrain()->IsSafeToCreateDoor()) &&
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
));
1591 ChangeOLTerrainAndUpdateLights(Door
);
1598 truth (lsquare::*BeamEffect
[BEAM_EFFECTS
])(const beamdata
&) =
1600 &lsquare::Polymorph
,
1606 &lsquare::Resurrect
,
1607 &lsquare::Invisibility
,
1608 &lsquare::Duplicate
,
1609 &lsquare::Lightning
,
1610 &lsquare::DoorCreation
,
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
))
1625 if(RoomIndex
&& !GetLevel()->GetRoom(RoomIndex
)->CheckKickSquare(Kicker
, this))
1631 void lsquare::GetHitByExplosion(const explosion
* Explosion
)
1633 if(Explosion
->ID
== LastExplosionID
)
1636 LastExplosionID
= Explosion
->ID
;
1637 int DistanceSquare
= (Pos
- Explosion
->Pos
).GetLengthSquare();
1639 if(DistanceSquare
> Explosion
->RadiusSquare
)
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
);
1656 GetOLTerrain()->ReceiveDamage(Explosion
->Terrorist
, Damage
>> 1, FIRE
);
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
);
1675 if(!AllItems
.empty())
1676 RandomItem
= AllItems
[RAND() % AllItems
.size()];
1682 if(Char
->IsPlayer())
1683 ADD_MESSAGE("%s glows blue for a moment!", RandomItem
->CHAR_NAME(DEFINITE
));
1686 Beam
.Owner
->Hostility(Char
);
1689 if(RandomItem
->GetEnchantment() > -5)
1690 RandomItem
->EditEnchantment(-1);
1695 void lsquare::SortAllItems(const sortdata
& SortData
)
1698 GetCharacter()->SortAllItems(SortData
);
1700 GetStack()->SortAllItems(SortData
);
1704 truth
lsquare::SoftenMaterial (const beamdata
&Beam
) {
1705 character
*Char
= GetCharacter();
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()];
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
);
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;
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());
1739 //may not need this message
1740 if (Char
->IsPlayer()) {
1741 ADD_MESSAGE("Your %s vibrates slightly but remains unchanged.", RandomItem
->CHAR_NAME(UNARTICLED
) );
1743 ADD_MESSAGE("%s's %s vibrates slightly but remains unchanged.", Char
->CHAR_DESCRIPTION(DEFINITE
), RandomItem
->CHAR_NAME(UNARTICLED
) );
1751 void lsquare::RemoveSmoke(smoke
* ToBeRemoved
)
1755 if(S
== ToBeRemoved
)
1760 DecAnimatedEntities();
1771 while(S
!= ToBeRemoved
);
1777 void lsquare::AddSmoke(gas
* ToBeAdded
)
1783 Smoke
= new smoke(ToBeAdded
, this);
1784 IncAnimatedEntities();
1792 if(ToBeAdded
->IsSameAs(S
->GetGas()))
1794 S
->Merge(ToBeAdded
);
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 {
1827 Msg
<< " A cloud of " << Smoke
->GetGas()->GetName(false, false) << " surrounds the square.";
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
);
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()
1855 OLTerrain
->PreProcessForBone();
1859 DecAnimatedEntities();
1861 for(smoke
* S
= Smoke
; S
; S
= S
->Next
)
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
)
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()
1906 OLTerrain
->FinalProcessForBone();
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
))
1933 truth
lsquare::IsScaryToBreathe(ccharacter
* Who
) const
1935 for(const smoke
* S
= Smoke
; S
; S
= S
->Next
)
1936 if(S
->IsScaryToBreathe(Who
))
1943 struct groundborderpartner
{
1944 truth
operator < (const groundborderpartner
&P
) const { return Terrain
->GetBorderTilePriority() < P
.Terrain
->GetBorderTilePriority(); }
1950 void lsquare::CalculateGroundBorderPartners () {
1951 if (GroundBorderPartnerInfo
& BORDER_PARTNER_ANIMATED
) DecStaticAnimatedEntities();
1952 groundborderpartner BorderPartner
[8*2]; //k8: *2 to make g++ shut up (WTF?!)
1954 int Priority
= GLTerrain
->GetBorderTilePriority();
1955 for (int d
= 0; d
< 8; ++d
) {
1956 lsquare
*Square
= NeighbourLSquare
[d
];
1958 glterrain
*Terrain
= Square
->GetGLTerrain();
1959 if (Terrain
&& Terrain
->UseBorderTiles() && Terrain
->GetBorderTilePriority() > Priority
) {
1960 BorderPartner
[Index
].Terrain
= Terrain
;
1961 BorderPartner
[Index
].SquareIndex
= 7-d
;
1966 GroundBorderPartnerInfo
= 0;
1968 delete [] GroundBorderPartnerTerrain
;
1969 GroundBorderPartnerTerrain
= 0;
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;
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(); }
1995 void lsquare::CalculateOverBorderPartners () {
1996 if (OverBorderPartnerInfo
& BORDER_PARTNER_ANIMATED
) DecStaticAnimatedEntities();
1997 overborderpartner BorderPartner
[8*2]; //k8: *2 to make g++ shut up (WTF?!)
1999 int Priority
= OLTerrain
? OLTerrain
->GetBorderTilePriority() : 0;
2000 for (int d
= 0; d
< 8; ++d
) {
2001 lsquare
*Square
= NeighbourLSquare
[d
];
2003 olterrain
*Terrain
= Square
->GetOLTerrain();
2004 if (Terrain
&& Terrain
->UseBorderTiles() && Terrain
->GetBorderTilePriority() > Priority
) {
2005 BorderPartner
[Index
].Terrain
= Terrain
;
2006 BorderPartner
[Index
].SquareIndex
= 7-d
;
2011 OverBorderPartnerInfo
= 0;
2013 delete [] OverBorderPartnerTerrain
;
2014 OverBorderPartnerTerrain
= 0;
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;
2027 OverBorderPartnerInfo
|= BORDER_PARTNER_ANIMATED
;
2028 IncStaticAnimatedEntities();
2030 OverBorderPartnerInfo
|= Index
<<24;
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
];
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
];
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();
2080 return OLTerrain
? OLTerrain
->GetWalkability() & GLTerrain
->GetWalkability() : GLTerrain
->GetWalkability();
2083 void lsquare::RemoveFluid(fluid
* ToRemove
)
2085 fluid
*& F
= ListFind(Fluid
, pointercomparer
<fluid
>(ToRemove
));
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
));
2103 F
->AddLiquidAndVolume(ToBeAdded
->GetVolume());
2108 F
= new fluid(ToBeAdded
, this);
2109 SignalEmitationIncrease(ToBeAdded
->GetEmitation());
2112 SendNewDrawRequest();
2113 SendMemorizedUpdateRequest();
2117 void lsquare::DisplayFluidInfo(festring
& Msg
) const
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())
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
);
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
);
2169 F
->StepOnEffect(GetCharacter());
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
);
2184 Stack
->Draw(PLAYER
, BlitData
, 3 - c
);
2188 void lsquare::RemoveRain(rain
* ToBeRemoved
)
2190 SendNewDrawRequest();
2193 if(ToBeRemoved
->IsEnabled())
2194 DecAnimatedEntities();
2196 if(R
== ToBeRemoved
)
2207 while(R
!= ToBeRemoved
);
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();
2239 void lsquare::RemoveSunLight()
2241 SunLightLuminance
= 0;
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
);
2267 Noxify(OldEmitation
, SECONDARY_SUN_LIGHT
);
2268 Emitate(NewEmitation
, SECONDARY_SUN_LIGHT
|FORCE_ADD
);
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
);
2296 NeighbourLSquare
[d
] = 0;
2300 void lsquare::RemoveLuminance(col24
& Emitation
)
2302 col24 OldLuminance
= Luminance
;
2303 col24 OldEmitation
= Emitation
;
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();
2316 CalculateLuminance();
2318 if(OldLuminance
== Luminance
)
2321 Flags
|= NEW_DRAW_REQUEST
;
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();
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
);
2355 if(EmitationInsignificant
)
2358 CalculateLuminance();
2360 if(OldLuminance
== Luminance
)
2364 Flags
|= NEW_DRAW_REQUEST
;
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())
2381 IncAnimatedEntities();
2385 void lsquare::DisableGlobalRain()
2387 SendNewDrawRequest();
2389 for(rain
* R
= Rain
; R
; R
= R
->Next
)
2390 if(!R
->HasOwnLiquid())
2393 DecAnimatedEntities();
2397 void lsquare::InitLastSeen()
2399 LastSeen
= LastSeen
== game::GetLOSTick() ? 2 : 0;
2400 SquarePartLastSeen
= 0;
2403 truth
lsquare::Engrave(cfestring
& What
)
2410 Engraved
= new char[What
.GetSize() + 1];
2411 strcpy(Engraved
, What
.CStr());
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();
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
;
2465 Flags
&= ~IS_TRANSPARENT
;
2470 void lsquare::CalculateSunLightLuminance(feuLong SeenBitMask
)
2472 sunemittervector::const_iterator i
, SunEnd
= SunEmitter
.end();
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
;
2487 SunLightLuminance
= 0;
2489 SunLightLuminance
= GetLevel()->GetSunLightEmitation();
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
);
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;
2544 void lsquare::Reveal(feuLong Tick
, truth IgnoreDarkness
)
2552 Luminance
= NORMAL_LUMINANCE
;
2555 SquarePartLastSeen
= 0;
2557 for(int c
= 0; c
< 4; ++c
)
2558 SquarePartLastSeen
|= (Tick
<< (c
<< 3));
2560 CalculateLuminance();
2565 MEMORIZED_UPDATE_REQUEST
|
2568 UpdateMemorizedDescription();
2571 void lsquare::DestroyMemorized()
2574 delete FowMemorized
;
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
2598 for(int c
= 0; c
< 8; ++c
)
2600 lsquare
* Square
= NeighbourLSquare
[c
];
2603 OK
[Index
++] = Square
;
2607 return OK
[RAND_N(Index
)];
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
));
2650 SendNewDrawRequest();
2651 SendMemorizedUpdateRequest();
2656 trapcomparer(int Type
) : Type(Type
) { }
2657 truth
operator()(const trap
* T
) const { return T
->GetType() == Type
; }
2661 truth
lsquare::AddTrap(trap
* ToBeAdded
)
2663 trap
*& T
= ListFind(Trap
, trapcomparer(ToBeAdded
->GetType()));
2673 ToBeAdded
->SetLSquareUnder(this);
2674 SendNewDrawRequest();
2675 SendMemorizedUpdateRequest();
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
))
2709 truth
lsquare::HasDangerousFluids(ccharacter
* Who
) const
2711 for(const fluid
* F
= Fluid
; F
; F
= F
->Next
)
2712 if(F
->IsDangerous(Who
))
2718 truth
lsquare::HasNoBorderPartners() const
2720 return !(GroundBorderPartnerInfo
>> 24) && !(OverBorderPartnerInfo
>> 24);
2723 void lsquare::AddLocationDescription(festring
& String
) const
2726 GLTerrain
->AddLocationDescription(String
);
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()
2743 bool lsquare::TeleportAllFluidsAway()
2748 bool lsquare::TeleportAllTrapsAway()
2750 for(trap
* T
= Trap
; T
; T
= Trap
)
2754 v2 V
, Pos
= GetPos();
2755 for(V
= GetLevel()->GetRandomSquare(); V
!= Pos
; V
= GetLevel()->GetRandomSquare());
2756 GetNearLSquare(V
)->AddTrap(T
);
2762 void lsquare::AddSpecialCursors()
2764 if((FowMemorized
|| game::GetSeeWholeMapCheatMode()) && OLTerrain
)
2765 OLTerrain
->AddSpecialCursors();