nonhumans separation
[k8-i-v-a-n.git] / src / game / object.cpp
blob41c427b532b665d094291c97213c74ad369fafdf
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"
23 v2 RightArmSparkleValidityArray[128];
24 v2 LeftArmSparkleValidityArray[128];
25 v2 GroinSparkleValidityArray[169];
26 v2 RightLegSparkleValidityArray[42];
27 v2 LeftLegSparkleValidityArray[45];
28 v2 NormalSparkleValidityArray[256];
29 v2 PossibleSparkleBuffer[256];
32 object::object () : entity(0), MainMaterial(0) {}
33 int object::GetSpecialFlags () const { return ST_NORMAL; }
34 col16 object::GetOutlineColor (int) const { return TRANSPARENT_COLOR; }
35 cbitmap *const *object::GetPicture () const { return GraphicData.Picture; }
38 object::object (const object& Object) : entity(Object), id(Object), VisualEffects(Object.VisualEffects) {
39 CopyMaterial(Object.MainMaterial, MainMaterial);
40 mOnEvents = Object.mOnEvents;
44 object::~object () {
45 delete MainMaterial;
49 void object::CopyMaterial (material *const &Source, material *&Dest) {
50 if (Source) {
51 Dest = Source->Duplicate();
52 Dest->SetMotherEntity(this);
53 } else {
54 Dest = 0;
59 void object::Save (outputfile &SaveFile) const {
60 SaveFile << GraphicData << (int)VisualEffects;
61 SaveFile << MainMaterial;
65 void object::Load (inputfile &SaveFile) {
66 SaveFile >> GraphicData >> (int&)VisualEffects;
67 LoadMaterial(SaveFile, MainMaterial);
71 void object::ObjectInitMaterials (material *&FirstMaterial, material *FirstNewMaterial, sLong FirstDefaultVolume,
72 material *&SecondMaterial, material *SecondNewMaterial, sLong SecondDefaultVolume, truth CallUpdatePictures)
74 InitMaterial(FirstMaterial, FirstNewMaterial, FirstDefaultVolume);
75 InitMaterial(SecondMaterial, SecondNewMaterial, SecondDefaultVolume);
76 SignalVolumeAndWeightChange();
77 if (CallUpdatePictures) UpdatePictures();
81 void object::InitMaterial (material *&Material, material *NewMaterial, sLong DefaultVolume) {
82 Material = NewMaterial;
83 if (Material) {
84 if (Material->HasBe()) Enable();
85 if (DefaultVolume && !Material->GetVolume()) Material->SetVolume(DefaultVolume);
86 Material->SetMotherEntity(this);
87 SignalEmitationIncrease(Material->GetEmitation());
92 void object::ChangeMaterial (material *&Material, material *NewMaterial, sLong DefaultVolume, int SpecialFlags) {
93 delete SetMaterial(Material, NewMaterial, DefaultVolume, SpecialFlags);
97 material *object::SetMaterial (material *&Material, material *NewMaterial, sLong DefaultVolume, int SpecialFlags) {
98 material *OldMaterial = Material;
100 Material = NewMaterial;
101 if ((!OldMaterial || !OldMaterial->HasBe()) && NewMaterial && NewMaterial->HasBe()) {
102 Enable();
103 } else if (OldMaterial && OldMaterial->HasBe() && (!NewMaterial || !NewMaterial->HasBe()) && !CalculateHasBe()) {
104 Disable();
106 if (NewMaterial) {
107 if (!NewMaterial->GetVolume()) {
108 if (OldMaterial) NewMaterial->SetVolume(OldMaterial->GetVolume());
109 else if (DefaultVolume) NewMaterial->SetVolume(DefaultVolume);
110 else ABORT("Singularity spawn detected!");
112 NewMaterial->SetMotherEntity(this);
113 if (!(SpecialFlags&NO_SIGNALS)) SignalEmitationIncrease(NewMaterial->GetEmitation());
115 if (!(SpecialFlags&NO_SIGNALS)) {
116 if (OldMaterial) SignalEmitationDecrease(OldMaterial->GetEmitation());
117 SignalVolumeAndWeightChange();
118 SignalMaterialChange();
120 if (!(SpecialFlags&NO_PIC_UPDATE)) UpdatePictures();
121 return OldMaterial;
125 void object::UpdatePictures () {
126 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 (isBone()) {
305 //FIXME: 'bone bone' removing
306 festring s(MainMaterial->GetName(Articled));
307 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;