'g'o should not miss items in corners anymore
[k8-i-v-a-n.git] / src / game / object.cpp
blob8b2a9ce58b2dd0f82ba72c9eee0f51d610b4f2c6
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 #include "object.h"
13 #include "materia.h"
14 #include "festring.h"
15 #include "whandler.h"
16 #include "rawbit.h"
17 #include "proto.h"
18 #include "game.h"
19 #include "bitmap.h"
20 #include "fesave.h"
21 #include "feparse.h"
24 v2 RightArmSparkleValidityArray[128];
25 v2 LeftArmSparkleValidityArray[128];
26 v2 GroinSparkleValidityArray[169];
27 v2 RightLegSparkleValidityArray[42];
28 v2 LeftLegSparkleValidityArray[45];
29 v2 NormalSparkleValidityArray[256];
30 v2 PossibleSparkleBuffer[256];
33 object::object () : entity(0), MainMaterial(0) {}
34 int object::GetSpecialFlags () const { return ST_NORMAL; }
35 col16 object::GetOutlineColor (int) const { return TRANSPARENT_COLOR; }
36 cbitmap *const *object::GetPicture () const { return GraphicData.Picture; }
39 object::object (const object& Object) : entity(Object), id(Object), VisualEffects(Object.VisualEffects) {
40 CopyMaterial(Object.MainMaterial, MainMaterial);
41 mOnEvents = Object.mOnEvents;
45 object::~object () {
46 delete MainMaterial;
50 void object::CopyMaterial (material *const &Source, material *&Dest) {
51 if (Source) {
52 Dest = Source->Duplicate();
53 Dest->SetMotherEntity(this);
54 } else {
55 Dest = 0;
60 void object::Save (outputfile &SaveFile) const {
61 SaveFile << GraphicData << (int)VisualEffects;
62 SaveFile << MainMaterial;
66 void object::Load (inputfile &SaveFile) {
67 SaveFile >> GraphicData >> (int&)VisualEffects;
68 LoadMaterial(SaveFile, MainMaterial);
72 void object::ObjectInitMaterials (material *&FirstMaterial, material *FirstNewMaterial, sLong FirstDefaultVolume,
73 material *&SecondMaterial, material *SecondNewMaterial, sLong SecondDefaultVolume, truth CallUpdatePictures)
75 InitMaterial(FirstMaterial, FirstNewMaterial, FirstDefaultVolume);
76 InitMaterial(SecondMaterial, SecondNewMaterial, SecondDefaultVolume);
77 SignalVolumeAndWeightChange();
78 if (CallUpdatePictures) UpdatePictures();
82 void object::InitMaterial (material *&Material, material *NewMaterial, sLong DefaultVolume) {
83 Material = NewMaterial;
84 if (Material) {
85 if (Material->HasBe()) Enable();
86 if (DefaultVolume && !Material->GetVolume()) Material->SetVolume(DefaultVolume);
87 Material->SetMotherEntity(this);
88 SignalEmitationIncrease(Material->GetEmitation());
93 void object::ChangeMaterial (material *&Material, material *NewMaterial, sLong DefaultVolume, int SpecialFlags) {
94 delete SetMaterial(Material, NewMaterial, DefaultVolume, SpecialFlags);
98 material *object::SetMaterial (material *&Material, material *NewMaterial, sLong DefaultVolume, int SpecialFlags) {
99 material *OldMaterial = Material;
101 Material = NewMaterial;
102 if ((!OldMaterial || !OldMaterial->HasBe()) && NewMaterial && NewMaterial->HasBe()) {
103 Enable();
104 } else if (OldMaterial && OldMaterial->HasBe() && (!NewMaterial || !NewMaterial->HasBe()) && !CalculateHasBe()) {
105 Disable();
107 if (NewMaterial) {
108 if (!NewMaterial->GetVolume()) {
109 if (OldMaterial) NewMaterial->SetVolume(OldMaterial->GetVolume());
110 else if (DefaultVolume) NewMaterial->SetVolume(DefaultVolume);
111 else ABORT("Singularity spawn detected!");
113 NewMaterial->SetMotherEntity(this);
114 if (!(SpecialFlags&NO_SIGNALS)) SignalEmitationIncrease(NewMaterial->GetEmitation());
116 if (!(SpecialFlags&NO_SIGNALS)) {
117 if (OldMaterial) SignalEmitationDecrease(OldMaterial->GetEmitation());
118 SignalVolumeAndWeightChange();
119 SignalMaterialChange();
121 if (!(SpecialFlags&NO_PIC_UPDATE)) UpdatePictures();
122 return OldMaterial;
126 void object::UpdatePictures () {
127 static cv2 ZeroPos(0, 0);
128 UpdatePictures(GraphicData, ZeroPos, VisualEffects|GetSpecialFlags(), GetMaxAlpha(), GetGraphicsContainerIndex(), &object::GetBitmapPos);
132 truth object::RandomizeSparklePos (v2 &SparklePos, v2 BPos, int &SparkleTime, feuLong SeedBase, int SpecialFlags, int GraphicsContainerIndex) const {
133 static int SeedModifier = 1;
134 v2 *ValidityArray;
135 int ValidityArraySize;
137 femath::SaveSeed();
138 femath::SetSeed(SeedBase+SeedModifier);
139 if (++SeedModifier > 0x10) SeedModifier = 1;
141 if ((SpecialFlags&0x38) == ST_RIGHT_ARM) {
142 ValidityArray = RightArmSparkleValidityArray;
143 ValidityArraySize = 128;
144 } else if ((SpecialFlags&0x38) == ST_LEFT_ARM) {
145 ValidityArray = LeftArmSparkleValidityArray;
146 ValidityArraySize = 128;
147 } else if ((SpecialFlags&0x38) == ST_GROIN) {
148 ValidityArray = GroinSparkleValidityArray;
149 ValidityArraySize = 169;
150 } else if ((SpecialFlags&0x38) == ST_RIGHT_LEG) {
151 ValidityArray = RightLegSparkleValidityArray;
152 ValidityArraySize = 42;
153 } else if ((SpecialFlags&0x38) == ST_LEFT_LEG) {
154 ValidityArray = LeftLegSparkleValidityArray;
155 ValidityArraySize = 45;
156 } else {
157 ValidityArray = NormalSparkleValidityArray;
158 ValidityArraySize = 256;
161 SparklePos = igraph::GetRawGraphic(GraphicsContainerIndex)->RandomizeSparklePos(ValidityArray, PossibleSparkleBuffer, BPos, TILE_V2, ValidityArraySize, GetSparkleFlags());
163 if (SparklePos != ERROR_V2) {
164 SparkleTime = RAND() % 241;
165 femath::LoadSeed();
166 return true;
168 femath::LoadSeed();
169 return false;
173 void object::UpdatePictures (graphicdata &GraphicData, v2 Position, int SpecialFlags, alpha MaxAlpha,
174 int GraphicsContainerIndex, bposretriever BitmapPosRetriever) const
176 int AnimationFrames = GetClassAnimationFrames();
177 v2 SparklePos;
178 int SparkleTime = 0;
179 int Seed = 0;
180 int FlyAmount = GetSpoilLevel();
181 truth Sparkling = false, FrameNeeded = false, SeedNeeded = false;
182 v2 BPos = (this->*BitmapPosRetriever)(0);
183 alpha Alpha;
185 if (!(SpecialFlags&(ST_FLAMES|ST_LIGHTNING))) {
186 if (AllowSparkling()) {
187 int SparkleFlags = GetSparkleFlags();
189 if (SparkleFlags && RandomizeSparklePos(SparklePos, BPos, SparkleTime, BPos.X+BPos.Y+GetMaterialColorA(0), SpecialFlags, GraphicsContainerIndex)) {
190 Sparkling = true;
191 if (AnimationFrames <= 256) AnimationFrames = 256;
194 if (FlyAmount) {
195 SeedNeeded = true;
196 FrameNeeded = true;
197 if (AnimationFrames <= 32) AnimationFrames = 32;
199 } else if (SpecialFlags&ST_FLAMES) {
200 SeedNeeded = true;
201 FrameNeeded = true;
202 if (AnimationFrames <= 16) AnimationFrames = 16;
203 } else if (SpecialFlags&ST_LIGHTNING) {
204 SeedNeeded = true;
205 if (AnimationFrames <= 128) AnimationFrames = 128;
207 if (SeedNeeded) {
208 static int SeedModifier = 1;
209 Seed = BPos.X+BPos.Y+GetMaterialColorA(0)+SeedModifier+0x42;
210 if (++SeedModifier > 0x10) SeedModifier = 1;
213 int WobbleMask = 0, WobbleData = GetWobbleData();
215 if (WobbleData&WOBBLE) {
216 int Speed = (WobbleData&WOBBLE_SPEED_RANGE)>>WOBBLE_SPEED_SHIFT;
217 int Freq = (WobbleData&WOBBLE_FREQ_RANGE)>>WOBBLE_FREQ_SHIFT;
218 int WobbleFrames = 512>>(Freq+Speed);
220 WobbleMask = 7>>Freq<<(6-Speed);
221 if (AnimationFrames <= WobbleFrames) AnimationFrames = WobbleFrames;
224 ModifyAnimationFrames(AnimationFrames);
226 int OldAnimationFrames = GraphicData.AnimationFrames;
228 for (int c = 0; c < OldAnimationFrames; ++c) igraph::RemoveUser(GraphicData.GraphicIterator[c]);
229 if (OldAnimationFrames != AnimationFrames) {
230 if (OldAnimationFrames) {
231 delete [] GraphicData.Picture;
232 delete [] GraphicData.GraphicIterator;
234 GraphicData.Picture = new bitmap *[AnimationFrames];
235 GraphicData.GraphicIterator = new tilemap::iterator[AnimationFrames];
237 GraphicData.AnimationFrames = AnimationFrames;
239 if (!AllowRegularColors()) SpecialFlags |= ST_DISALLOW_R_COLORS;
241 graphicid GI;
242 GI.BaseAlpha = MaxAlpha;
243 GI.FileIndex = GraphicsContainerIndex;
244 GI.SpecialFlags = SpecialFlags;
245 GI.Seed = Seed;
246 GI.FlyAmount = FlyAmount;
247 GI.Position = Position;
248 GI.RustData[0] = GetRustDataA();
249 GI.RustData[1] = GetRustDataB();
250 GI.RustData[2] = GetRustDataC();
251 GI.RustData[3] = GetRustDataD();
252 GI.WobbleData = WobbleData;
254 for (int c = 0; c < AnimationFrames; ++c) {
255 GI.Color[0] = GetMaterialColorA(c);
256 GI.Color[1] = GetMaterialColorB(c);
257 GI.Color[2] = GetMaterialColorC(c);
258 GI.Color[3] = GetMaterialColorD(c);
259 Alpha = GetAlphaA(c);
260 GI.Alpha[0] = Alpha < MaxAlpha ? Alpha : MaxAlpha;
261 Alpha = GetAlphaB(c);
262 GI.Alpha[1] = Alpha < MaxAlpha ? Alpha : MaxAlpha;
263 Alpha = GetAlphaC(c);
264 GI.Alpha[2] = Alpha < MaxAlpha ? Alpha : MaxAlpha;
265 Alpha = GetAlphaD(c);
266 GI.Alpha[3] = Alpha < MaxAlpha ? Alpha : MaxAlpha;
267 v2 BPos = (this->*BitmapPosRetriever)(c);
268 GI.BitmapPosX = BPos.X;
269 GI.BitmapPosY = BPos.Y;
270 if (Sparkling && c > SparkleTime && c < SparkleTime+16) {
271 GI.SparklePosX = SparklePos.X;
272 GI.SparklePosY = SparklePos.Y;
273 GI.SparkleFrame = c-SparkleTime;
274 } else {
275 GI.SparklePosX = SPARKLE_POS_X_ERROR;
276 GI.SparklePosY = 0;
277 GI.SparkleFrame = 0;
279 GI.Frame =
280 !c || FrameNeeded ||
281 ((SpecialFlags&ST_LIGHTNING) && !((c+1)&7)) ||
282 ((WobbleData&WOBBLE) && !(c&WobbleMask)) ? c : 0;
283 GI.OutlineColor = GetOutlineColor(c);
284 GI.OutlineAlpha = GetOutlineAlpha(c);
285 tilemap::iterator Iterator = igraph::AddUser(GI);
286 GraphicData.GraphicIterator[c] = Iterator;
287 GraphicData.Picture[c] = Iterator->second.Bitmap;
292 col16 object::GetMaterialColorA (int) const {
293 return MainMaterial->GetColor();
297 truth object::AddRustLevelDescription (festring &String, truth Articled) const {
298 return MainMaterial->AddRustLevelDescription(String, Articled);
302 truth object::AddMaterialDescription (festring &String, truth Articled) const {
303 //FIXME: gum solution
304 if (IsBoneNameSingular()) {
305 //FIXME: 'bone bone' removing
306 festring s(MainMaterial->GetName(Articled));
307 festring::sizetype pos = s.FindLast("bone");
308 if (pos != festring::NPos && pos == s.GetSize()-4) {
309 while (pos > 0 && s[pos-1] == ' ') --pos;
310 s.Erase(pos, s.GetSize()-pos);
311 if (s.GetSize() == 0) return true; // no name left
313 String << s;
314 } else {
315 MainMaterial->AddName(String, Articled);
317 String << ' ';
318 return true;
322 void object::AddContainerPostFix (festring &String) const {
323 if (GetSecondaryMaterial()) GetSecondaryMaterial()->AddName(String << " full of ", false, false);
327 void object::AddLumpyPostFix (festring &String) const {
328 MainMaterial->AddName(String << " of ", false, false);
332 alpha object::GetAlphaA (int) const {
333 return MainMaterial->GetAlpha();
337 void object::RandomizeVisualEffects () {
338 int AcceptedFlags = GetOKVisualEffects();
340 if (AcceptedFlags) {
341 SetVisualEffects((RAND()&0x7&AcceptedFlags)|GetForcedVisualEffects());
342 } else {
343 SetVisualEffects(GetForcedVisualEffects());
348 void object::LoadMaterial (inputfile &SaveFile, material *&Material) {
349 SaveFile >> Material;
350 if (Material) {
351 if (Material->HasBe()) Enable();
352 Material->SetMotherEntity(this);
353 game::CombineLights(Emitation, Material->GetEmitation());
358 int object::RandomizeMaterialConfiguration () {
359 const fearray<sLong>& MCC = GetMaterialConfigChances();
361 return MCC.Size > 1 ? femath::WeightedRand(MCC.Data, GetMaterialConfigChanceSum()) : 0;
365 truth object::AddEmptyAdjective (festring &String, truth Articled) const {
366 if (GetSecondaryMaterial()) return false;
367 String << (Articled ? "an empty " : "empty ");
368 return true;
372 void object::CalculateEmitation () {
373 Emitation = GetBaseEmitation();
374 if (MainMaterial) game::CombineLights(Emitation, MainMaterial->GetEmitation());
378 truth object::CalculateHasBe () const {
379 return MainMaterial && MainMaterial->HasBe();
383 int object::GetSparkleFlags () const {
384 return MainMaterial->IsSparkling() ? SPARKLING_A : 0;
388 void object::InitSparkleValidityArrays () {
389 int Index = 0;
391 for (int y = 0; y < 16; ++y)
392 for (int x = 0; x < 8; ++x)
393 RightArmSparkleValidityArray[Index++] = v2(x, y);
395 Index = 0;
396 for (int y = 0; y < 16; ++y)
397 for (int x = 8; x < 16; ++x)
398 LeftArmSparkleValidityArray[Index++] = v2(x, y);
400 Index = 0;
401 for (int y = 0; y < 10; ++y)
402 for (int x = 0; x < 16; ++x)
403 GroinSparkleValidityArray[Index++] = v2(x, y);
404 for (int y = 10; y < 13; ++y)
405 for (int x = y-5; x < 20-y; ++x)
406 GroinSparkleValidityArray[Index++] = v2(x, y);
408 Index = 0;
409 for (int y = 10; y < 16; ++y)
410 for (int x = 0; x < 8; ++x)
411 if ((y != 10 || x < 5) && (y != 11 || x < 6) && (y != 12 || x < 7))
412 RightLegSparkleValidityArray[Index++] = v2(x, y);
414 Index = 0;
415 for (int y = 10; y < 16; ++y)
416 for (int x = 8; x < 16; ++x)
417 if ((y != 10 || x > 9) && (y != 11 || x > 8))
418 LeftLegSparkleValidityArray[Index++] = v2(x, y);
420 Index = 0;
421 for (int y = 0; y < 16; ++y)
422 for (int x = 0; x < 16; ++x)
423 NormalSparkleValidityArray[Index++] = v2(x, y);
427 int object::GetRustDataA () const {
428 return MainMaterial->GetRustData();
432 truth object::DetectMaterial (cmaterial *Material) const {
433 for (int c = 0; c < GetMaterials(); ++c) if (GetMaterial(c) && GetMaterial(c)->IsSameAs(Material)) return true;
434 return false;