moved almost all hardcoded constants to "define.dat"
[k8-i-v-a-n.git] / src / game / object.cpp
blob172b2fa8d71793d4b4bb20162383ad76e911ee74
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);
129 UpdatePictures(GraphicData, ZeroPos, VisualEffects|GetSpecialFlags(), GetMaxAlpha(), GetGraphicsContainerIndex(), &object::GetBitmapPos);
133 truth object::RandomizeSparklePos (v2 &SparklePos, v2 BPos, int &SparkleTime, feuLong SeedBase, int SpecialFlags, int GraphicsContainerIndex) const {
134 static int SeedModifier = 1;
135 v2 *ValidityArray;
136 int ValidityArraySize;
138 femath::SaveSeed();
139 femath::SetSeed(SeedBase+SeedModifier);
140 if (++SeedModifier > 0x10) SeedModifier = 1;
142 if ((SpecialFlags&0x38) == ST_RIGHT_ARM) {
143 ValidityArray = RightArmSparkleValidityArray;
144 ValidityArraySize = 128;
145 } else if ((SpecialFlags&0x38) == ST_LEFT_ARM) {
146 ValidityArray = LeftArmSparkleValidityArray;
147 ValidityArraySize = 128;
148 } else if ((SpecialFlags&0x38) == ST_GROIN) {
149 ValidityArray = GroinSparkleValidityArray;
150 ValidityArraySize = 169;
151 } else if ((SpecialFlags&0x38) == ST_RIGHT_LEG) {
152 ValidityArray = RightLegSparkleValidityArray;
153 ValidityArraySize = 42;
154 } else if ((SpecialFlags&0x38) == ST_LEFT_LEG) {
155 ValidityArray = LeftLegSparkleValidityArray;
156 ValidityArraySize = 45;
157 } else {
158 ValidityArray = NormalSparkleValidityArray;
159 ValidityArraySize = 256;
162 SparklePos = igraph::GetRawGraphic(GraphicsContainerIndex)->RandomizeSparklePos(ValidityArray, PossibleSparkleBuffer, BPos, TILE_V2, ValidityArraySize, GetSparkleFlags());
164 if (SparklePos != ERROR_V2) {
165 SparkleTime = RAND() % 241;
166 femath::LoadSeed();
167 return true;
169 femath::LoadSeed();
170 return false;
174 void object::UpdatePictures (graphicdata &GraphicData, v2 Position, int SpecialFlags, alpha MaxAlpha,
175 int GraphicsContainerIndex, bposretriever BitmapPosRetriever) const
177 int AnimationFrames = GetClassAnimationFrames();
178 v2 SparklePos;
179 int SparkleTime = 0;
180 int Seed = 0;
181 int FlyAmount = GetSpoilLevel();
182 truth Sparkling = false, FrameNeeded = false, SeedNeeded = false;
183 v2 BPos = (this->*BitmapPosRetriever)(0);
184 alpha Alpha;
186 if (!(SpecialFlags&(ST_FLAMES|ST_LIGHTNING))) {
187 if (AllowSparkling()) {
188 int SparkleFlags = GetSparkleFlags();
190 if (SparkleFlags && RandomizeSparklePos(SparklePos, BPos, SparkleTime, BPos.X+BPos.Y+GetMaterialColorA(0), SpecialFlags, GraphicsContainerIndex)) {
191 Sparkling = true;
192 if (AnimationFrames <= 256) AnimationFrames = 256;
195 if (FlyAmount) {
196 SeedNeeded = true;
197 FrameNeeded = true;
198 if (AnimationFrames <= 32) AnimationFrames = 32;
200 } else if (SpecialFlags&ST_FLAMES) {
201 SeedNeeded = true;
202 FrameNeeded = true;
203 if (AnimationFrames <= 16) AnimationFrames = 16;
204 } else if (SpecialFlags&ST_LIGHTNING) {
205 SeedNeeded = true;
206 if (AnimationFrames <= 128) AnimationFrames = 128;
208 if (SeedNeeded) {
209 static int SeedModifier = 1;
210 Seed = BPos.X+BPos.Y+GetMaterialColorA(0)+SeedModifier+0x42;
211 if (++SeedModifier > 0x10) SeedModifier = 1;
214 int WobbleMask = 0, WobbleData = GetWobbleData();
216 if (WobbleData&WOBBLE) {
217 int Speed = (WobbleData&WOBBLE_SPEED_RANGE)>>WOBBLE_SPEED_SHIFT;
218 int Freq = (WobbleData&WOBBLE_FREQ_RANGE)>>WOBBLE_FREQ_SHIFT;
219 int WobbleFrames = 512>>(Freq+Speed);
221 WobbleMask = 7>>Freq<<(6-Speed);
222 if (AnimationFrames <= WobbleFrames) AnimationFrames = WobbleFrames;
225 ModifyAnimationFrames(AnimationFrames);
227 int OldAnimationFrames = GraphicData.AnimationFrames;
229 for (int c = 0; c < OldAnimationFrames; ++c) igraph::RemoveUser(GraphicData.GraphicIterator[c]);
230 if (OldAnimationFrames != AnimationFrames) {
231 if (OldAnimationFrames) {
232 delete [] GraphicData.Picture;
233 delete [] GraphicData.GraphicIterator;
235 GraphicData.Picture = new bitmap * [AnimationFrames];
236 GraphicData.GraphicIterator = new tilemap::iterator[AnimationFrames];
238 GraphicData.AnimationFrames = AnimationFrames;
240 if (!AllowRegularColors()) SpecialFlags |= ST_DISALLOW_R_COLORS;
242 graphicid GI;
243 GI.BaseAlpha = MaxAlpha;
244 GI.FileIndex = GraphicsContainerIndex;
245 GI.SpecialFlags = SpecialFlags;
246 GI.Seed = Seed;
247 GI.FlyAmount = FlyAmount;
248 GI.Position = Position;
249 GI.RustData[0] = GetRustDataA();
250 GI.RustData[1] = GetRustDataB();
251 GI.RustData[2] = GetRustDataC();
252 GI.RustData[3] = GetRustDataD();
253 GI.WobbleData = WobbleData;
255 for (int c = 0; c < AnimationFrames; ++c) {
256 GI.Color[0] = GetMaterialColorA(c);
257 GI.Color[1] = GetMaterialColorB(c);
258 GI.Color[2] = GetMaterialColorC(c);
259 GI.Color[3] = GetMaterialColorD(c);
260 Alpha = GetAlphaA(c);
261 GI.Alpha[0] = Alpha < MaxAlpha ? Alpha : MaxAlpha;
262 Alpha = GetAlphaB(c);
263 GI.Alpha[1] = Alpha < MaxAlpha ? Alpha : MaxAlpha;
264 Alpha = GetAlphaC(c);
265 GI.Alpha[2] = Alpha < MaxAlpha ? Alpha : MaxAlpha;
266 Alpha = GetAlphaD(c);
267 GI.Alpha[3] = Alpha < MaxAlpha ? Alpha : MaxAlpha;
268 v2 BPos = (this->*BitmapPosRetriever)(c);
269 GI.BitmapPosX = BPos.X;
270 GI.BitmapPosY = BPos.Y;
271 if (Sparkling && c > SparkleTime && c < SparkleTime+16) {
272 GI.SparklePosX = SparklePos.X;
273 GI.SparklePosY = SparklePos.Y;
274 GI.SparkleFrame = c-SparkleTime;
275 } else {
276 GI.SparklePosX = SPARKLE_POS_X_ERROR;
277 GI.SparklePosY = 0;
278 GI.SparkleFrame = 0;
280 GI.Frame =
281 !c || FrameNeeded ||
282 ((SpecialFlags&ST_LIGHTNING) && !((c+1)&7)) ||
283 ((WobbleData&WOBBLE) && !(c&WobbleMask)) ? c : 0;
284 GI.OutlineColor = GetOutlineColor(c);
285 GI.OutlineAlpha = GetOutlineAlpha(c);
286 tilemap::iterator Iterator = igraph::AddUser(GI);
287 GraphicData.GraphicIterator[c] = Iterator;
288 GraphicData.Picture[c] = Iterator->second.Bitmap;
293 col16 object::GetMaterialColorA (int) const {
294 return MainMaterial->GetColor();
298 truth object::AddRustLevelDescription (festring &String, truth Articled) const {
299 return MainMaterial->AddRustLevelDescription(String, Articled);
303 truth object::AddMaterialDescription (festring &String, truth Articled) const {
304 //FIXME: gum solution
305 if (IsBoneNameSingular()) {
306 //FIXME: 'bone bone' removing
307 festring s(MainMaterial->GetName(Articled));
308 festring::sizetype pos = s.FindLast("bone");
309 if (pos != festring::NPos && pos == s.GetSize()-4) {
310 while (pos > 0 && s[pos-1] == ' ') --pos;
311 s.Erase(pos, s.GetSize()-pos);
312 if (s.GetSize() == 0) return true; // no name left
314 String << s;
315 } else {
316 MainMaterial->AddName(String, Articled);
318 String << ' ';
319 return true;
323 void object::AddContainerPostFix (festring &String) const {
324 if (GetSecondaryMaterial()) GetSecondaryMaterial()->AddName(String << " full of ", false, false);
328 void object::AddLumpyPostFix (festring &String) const {
329 MainMaterial->AddName(String << " of ", false, false);
333 alpha object::GetAlphaA (int) const {
334 return MainMaterial->GetAlpha();
338 void object::RandomizeVisualEffects () {
339 int AcceptedFlags = GetOKVisualEffects();
341 if (AcceptedFlags) {
342 SetVisualEffects((RAND()&0x7&AcceptedFlags)|GetForcedVisualEffects());
343 } else {
344 SetVisualEffects(GetForcedVisualEffects());
349 void object::LoadMaterial (inputfile &SaveFile, material *&Material) {
350 SaveFile >> Material;
351 if (Material) {
352 if (Material->HasBe()) Enable();
353 Material->SetMotherEntity(this);
354 game::CombineLights(Emitation, Material->GetEmitation());
359 int object::RandomizeMaterialConfiguration () {
360 const fearray<sLong>& MCC = GetMaterialConfigChances();
362 return MCC.Size > 1 ? femath::WeightedRand(MCC.Data, GetMaterialConfigChanceSum()) : 0;
366 truth object::AddEmptyAdjective (festring &String, truth Articled) const {
367 if (GetSecondaryMaterial()) return false;
368 String << (Articled ? "an empty " : "empty ");
369 return true;
373 void object::CalculateEmitation () {
374 Emitation = GetBaseEmitation();
375 if (MainMaterial) game::CombineLights(Emitation, MainMaterial->GetEmitation());
379 truth object::CalculateHasBe () const {
380 return MainMaterial && MainMaterial->HasBe();
384 int object::GetSparkleFlags () const {
385 return MainMaterial->IsSparkling() ? SPARKLING_A : 0;
389 void object::InitSparkleValidityArrays () {
390 int Index = 0;
392 for (int y = 0; y < 16; ++y)
393 for (int x = 0; x < 8; ++x)
394 RightArmSparkleValidityArray[Index++] = v2(x, y);
396 Index = 0;
397 for (int y = 0; y < 16; ++y)
398 for (int x = 8; x < 16; ++x)
399 LeftArmSparkleValidityArray[Index++] = v2(x, y);
401 Index = 0;
402 for (int y = 0; y < 10; ++y)
403 for (int x = 0; x < 16; ++x)
404 GroinSparkleValidityArray[Index++] = v2(x, y);
405 for (int y = 10; y < 13; ++y)
406 for (int x = y-5; x < 20-y; ++x)
407 GroinSparkleValidityArray[Index++] = v2(x, y);
409 Index = 0;
410 for (int y = 10; y < 16; ++y)
411 for (int x = 0; x < 8; ++x)
412 if ((y != 10 || x < 5) && (y != 11 || x < 6) && (y != 12 || x < 7))
413 RightLegSparkleValidityArray[Index++] = v2(x, y);
415 Index = 0;
416 for (int y = 10; y < 16; ++y)
417 for (int x = 8; x < 16; ++x)
418 if ((y != 10 || x > 9) && (y != 11 || x > 8))
419 LeftLegSparkleValidityArray[Index++] = v2(x, y);
421 Index = 0;
422 for (int y = 0; y < 16; ++y)
423 for (int x = 0; x < 16; ++x)
424 NormalSparkleValidityArray[Index++] = v2(x, y);
428 int object::GetRustDataA () const {
429 return MainMaterial->GetRustData();
433 truth object::DetectMaterial (cmaterial *Material) const {
434 for (int c = 0; c < GetMaterials(); ++c) if (GetMaterial(c) && GetMaterial(c)->IsSameAs(Material)) return true;
435 return false;