1 /*
2 *
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
7 *
8 * See LICENSING which should be included
9 * along with this file for more details
10 *
11 */
12
13 /* Compiled through materset.cpp */
14
materialprototype(const materialprototype * Base,materialspawner Spawner,materialcloner Cloner,cchar * ClassID)15 materialprototype::materialprototype(const materialprototype* Base,
16 materialspawner Spawner,
17 materialcloner Cloner,
18 cchar* ClassID)
19 : Base(Base), Spawner(Spawner), Cloner(Cloner), ClassID(ClassID)
20 { Index = protocontainer<material>::Add(this); }
21
GetRawPrice() const22 long material::GetRawPrice() const
23 { return GetPriceModifier() * GetWeight() / 10000; }
CanBeDug(material * ShovelMaterial) const24 truth material::CanBeDug(material* ShovelMaterial) const
25 { return ShovelMaterial->GetStrengthValue() > GetStrengthValue(); }
GetTotalExplosivePower() const26 long material::GetTotalExplosivePower() const
27 { return long(double(Volume) * GetExplosivePower() / 1000000); }
GetConsumeVerb() const28 cchar* material::GetConsumeVerb() const { return "eating"; }
29
30 materialpredicate TrueMaterialPredicate = &material::True;
31
AddName(festring & Name,truth Articled,truth Adjective) const32 void material::AddName(festring& Name, truth Articled, truth Adjective) const
33 {
34 if(Articled)
35 {
36 if(GetNameFlags() & USE_AN)
37 Name << "an ";
38 else
39 Name << "a ";
40 }
41
42 Name << (Adjective ? GetAdjectiveStem() : GetNameStem());
43 }
44
GetName(truth Articled,truth Adjective) const45 festring material::GetName(truth Articled, truth Adjective) const
46 {
47 static festring Name;
48 Name.Empty();
49 AddName(Name, Articled, Adjective);
50 return Name;
51 }
52
TakeDipVolumeAway(long MaxVolume)53 material* material::TakeDipVolumeAway(long MaxVolume)
54 {
55 if(Volume > MaxVolume)
56 {
57 EditVolume(-MaxVolume);
58 return SpawnMore(MaxVolume);
59 }
60 else
61 return MotherEntity->RemoveMaterial(this);
62 }
63
Save(outputfile & SaveFile) const64 void material::Save(outputfile& SaveFile) const
65 {
66 SaveFile << static_cast<ushort>(GetType());
67 SaveFile << Volume;
68 SaveFile << static_cast<ushort>(GetConfig());
69 }
70
Load(inputfile & SaveFile)71 void material::Load(inputfile& SaveFile)
72 {
73 SaveFile >> Volume;
74 databasecreator<material>::InstallDataBase(this, ReadType<ushort>(SaveFile));
75 }
76
Effect(character * Char,int BodyPart,long Amount)77 truth material::Effect(character* Char, int BodyPart, long Amount)
78 {
79 /* Receivexxx should return truth! */
80
81 Amount = Amount * GetEffectStrength() / 100;
82
83 if(!Amount)
84 return false;
85
86 switch(GetEffect())
87 {
88 case EFFECT_POISON: Char->BeginTemporaryState(POISONED, Amount); break;
89 case EFFECT_DARKNESS: Char->ReceiveDarkness(Amount); break;
90 case EFFECT_OMMEL_URINE: Char->ReceiveOmmelUrine(Amount); break;
91 case EFFECT_PEPSI: Char->ReceivePepsi(Amount); break;
92 case EFFECT_KOBOLD_FLESH: Char->ReceiveKoboldFlesh(Amount); break;
93 case EFFECT_HEAL: Char->ReceiveHeal(Amount); break;
94 case EFFECT_LYCANTHROPY:
95 {
96 if(!Char->StateIsActivated(DISEASE_IMMUNITY))
97 {
98 if(!RAND_N(Char->GetAttribute(ENDURANCE)))
99 Char->GainIntrinsic(LYCANTHROPY);
100 else
101 Char->BeginTemporaryState(LYCANTHROPY, Amount);
102 }
103
104 break;
105 }
106 case EFFECT_SCHOOL_FOOD: Char->ReceiveSchoolFood(Amount); break;
107 case EFFECT_ANTIDOTE: Char->ReceiveAntidote(Amount); break;
108 case EFFECT_CONFUSE: Char->BeginTemporaryState(CONFUSED, Amount); break;
109 case EFFECT_POLYMORPH: Char->BeginTemporaryState(POLYMORPH, Amount); break;
110 case EFFECT_ESP: Char->BeginTemporaryState(ESP, Amount); break;
111 case EFFECT_SKUNK_SMELL: Char->BeginTemporaryState(POISONED, Amount); break;
112 case EFFECT_MAGIC_MUSHROOM:
113 {
114 v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos();
115 Char->ActivateRandomState(SRC_MAGIC_MUSHROOM, Amount,
116 Volume % 250 + Pos.X + Pos.Y + 1);
117 break;
118 }
119 case EFFECT_TRAIN_PERCEPTION:
120 {
121 Char->EditExperience(PERCEPTION, Amount, 1 << 14);
122 break;
123 }
124 case EFFECT_HOLY_BANANA: Char->ReceiveHolyBanana(Amount); break;
125 case EFFECT_EVIL_WONDER_STAFF_VAPOUR:
126 {
127 v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos();
128 Char->ActivateRandomState(SRC_EVIL, Amount,
129 Volume % 250 + Pos.X + Pos.Y + 1);
130 break;
131 }
132 case EFFECT_GOOD_WONDER_STAFF_VAPOUR:
133 {
134 v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos();
135 Char->ActivateRandomState(SRC_GOOD, Amount,
136 Volume % 250 + Pos.X + Pos.Y + 1);
137 break;
138 }
139 case EFFECT_PEA_SOUP: Char->ReceivePeaSoup(Amount); break;
140 case EFFECT_BLACK_UNICORN_FLESH: Char->ReceiveBlackUnicorn(Amount); break;
141 case EFFECT_GRAY_UNICORN_FLESH: Char->ReceiveGrayUnicorn(Amount); break;
142 case EFFECT_WHITE_UNICORN_FLESH: Char->ReceiveWhiteUnicorn(Amount); break;
143 case EFFECT_TELEPORT_CONTROL: Char->BeginTemporaryState(TELEPORT_CONTROL, Amount); break;
144 case EFFECT_MUSHROOM:
145 {
146 v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos();
147 Char->ActivateRandomState(SRC_MUSHROOM, Amount,
148 Volume % 250 + Pos.X + Pos.Y + 1);
149 break;
150 }
151 case EFFECT_OMMEL_CERUMEN: Char->ReceiveOmmelCerumen(Amount); break;
152 case EFFECT_OMMEL_SWEAT: Char->ReceiveOmmelSweat(Amount); break;
153 case EFFECT_OMMEL_TEARS: Char->ReceiveOmmelTears(Amount); break;
154 case EFFECT_OMMEL_SNOT: Char->ReceiveOmmelSnot(Amount); break;
155 case EFFECT_OMMEL_BONE: Char->ReceiveOmmelBone(Amount); break;
156 case EFFECT_MUSTARD_GAS: Char->ReceiveMustardGas(BodyPart, Amount); break;
157 case EFFECT_MUSTARD_GAS_LIQUID: Char->ReceiveMustardGasLiquid(BodyPart, Amount); break;
158 case EFFECT_VAMPIRISM:
159 {
160 if(!Char->StateIsActivated(DISEASE_IMMUNITY))
161 Char->BeginTemporaryState(VAMPIRISM, Amount);
162
163 break;
164 }
165 case EFFECT_PANACEA:
166 {
167 Char->ReceiveHeal(Amount);
168 Char->ReceiveAntidote(Amount);
169 Char->RestoreStamina();
170 break;
171 }
172 case EFFECT_OMMEL_BLOOD: Char->ReceiveOmmelBlood(Amount); break;
173 case EFFECT_PANIC:
174 {
175 if(!Char->StateIsActivated(FEARLESS) && Char->GetPanicLevel() > 0)
176 Char->BeginTemporaryState(PANIC, Amount);
177
178 break;
179 }
180 case EFFECT_TRAIN_WISDOM:
181 {
182 Char->EditExperience(WISDOM, Amount, 1 << 14);
183 break;
184 }
185 case EFFECT_REGENERATION: Char->BeginTemporaryState(REGENERATION, Amount); break;
186 case EFFECT_TELEPORTATION:
187 {
188 Char->BeginTemporaryState(TELEPORT, Amount);
189 Char->TeleportRandomly(false);
190 break;
191 }
192 case EFFECT_LAUGH:
193 {
194 game::CallForAttention(Char->GetPos(), Amount);
195 Char->BeginTemporaryState(HICCUPS, Amount);
196 break;
197 }
198 case EFFECT_POLYJUICE: Char->PolymorphRandomly(Amount, 999999, Amount * 10); break;
199 //case EFFECT_PUKE: Char->VomitAtRandomDirection(Amount); break;
200 case EFFECT_SICKNESS: Char->ReceiveSickness(Amount); break;
201 case EFFECT_PHASE: Char->BeginTemporaryState(ETHEREAL_MOVING, Amount); break;
202 case EFFECT_ACID_GAS: Char->SpillFluid(0, liquid::Spawn(SULPHURIC_ACID, Amount)); break;
203 case EFFECT_FIRE_GAS: Char->ReceiveFlames(Amount); break;
204 default: return false;
205 }
206
207 return true;
208 }
209
EatEffect(character * Eater,long Amount)210 material* material::EatEffect(character* Eater, long Amount)
211 {
212 Amount = Volume > Amount ? Amount : Volume;
213
214 if(Eater->StateIsActivated(VAMPIRISM) && (GetCategoryFlags() & IS_BLOOD))
215 {
216 Amount *= 10; // Vampires are nourished by blood.
217 }
218 Eater->ReceiveNutrition(GetNutritionValue() * Amount / 50);
219
220 if(Amount && Volume)
221 {
222 if(DisablesPanicWhenConsumed() && Eater->TemporaryStateIsActivated(PANIC))
223 {
224 if(Eater->IsPlayer())
225 {
226 ADD_MESSAGE("You relax a bit.");
227 }
228 else if(Eater->CanBeSeenByPlayer())
229 {
230 ADD_MESSAGE("%s relaxes a bit.", Eater->CHAR_NAME(DEFINITE));
231 }
232 Eater->DeActivateTemporaryState(PANIC);
233 }
234 }
235
236 if(GetInteractionFlags() & AFFECT_INSIDE)
237 {
238 head* Head = Eater->GetVirtualHead();
239 long NewAmount = Amount;
240
241 if(Head && Amount >= 8)
242 {
243 Head->AddFluid(static_cast<liquid*>(SpawnMore(Amount >> 3)),
244 CONST_S("throat"), 0, true);
245 NewAmount -= Amount >> 3;
246 }
247
248 Eater->GetTorso()->AddFluid(static_cast<liquid*>(SpawnMore(NewAmount)),
249 CONST_S("stomach"), 0, true);
250 }
251 else
252 {
253 Effect(Eater, TORSO_INDEX, Amount);
254
255 if(IsLiquid())
256 Eater->EditStamina(int(50. * Amount * Eater->GetMaxStamina()
257 / Eater->GetBodyVolume()),
258 false);
259 }
260
261 if(Volume != Amount)
262 {
263 EditVolume(-Amount);
264 return 0;
265 }
266 else
267 return MotherEntity->RemoveMaterial(this);
268 }
269
HitEffect(character * Enemy,bodypart * BodyPart)270 truth material::HitEffect(character* Enemy, bodypart* BodyPart)
271 {
272 if(!Volume)
273 return false;
274
275 switch(GetHitMessage())
276 {
277 case HM_SCHOOL_FOOD: Enemy->AddSchoolFoodHitMessage(); break;
278 case HM_FROG_FLESH: Enemy->AddFrogFleshConsumeEndMessage(); break;
279 case HM_OMMEL: Enemy->AddOmmelConsumeEndMessage(); break;
280 case HM_PEPSI: Enemy->AddPepsiConsumeEndMessage(); break;
281 case HM_KOBOLD_FLESH: Enemy->AddKoboldFleshHitMessage(); break;
282 case HM_HEALING_LIQUID: Enemy->AddHealingLiquidConsumeEndMessage(); break;
283 case HM_ANTIDOTE: Enemy->AddAntidoteConsumeEndMessage(); break;
284 case HM_CONFUSE: Enemy->AddConfuseHitMessage(); break;
285 case HM_HOLY_BANANA: Enemy->AddHolyBananaConsumeEndMessage(); break;
286 }
287
288 long Amount = Max<long>(GetVolume() >> 1, 1);
289 truth Success;
290
291 if(GetInteractionFlags() & AFFECT_INSIDE)
292 {
293 if(BodyPart)
294 {
295 BodyPart->AddFluid(static_cast<liquid*>(SpawnMore(Amount)),
296 CONST_S(""), 0, true);
297 Success = true;
298 }
299 else
300 Success = false;
301 }
302 else
303 {
304 int BPIndex = BodyPart ? BodyPart->GetBodyPartIndex() : NONE_INDEX;
305 Success = Effect(Enemy, BPIndex, Amount);
306 }
307
308 if(Amount != Volume)
309 EditVolume(-Amount);
310 else
311 delete MotherEntity->RemoveMaterial(this);
312
313 return Success;
314 }
315
AddConsumeEndMessage(character * Eater) const316 void material::AddConsumeEndMessage(character* Eater) const
317 {
318 switch(GetConsumeEndMessage())
319 {
320 case CEM_SCHOOL_FOOD: Eater->AddSchoolFoodConsumeEndMessage(); break;
321 case CEM_BONE: Eater->AddBoneConsumeEndMessage(); break;
322 case CEM_FROG_FLESH: Eater->AddFrogFleshConsumeEndMessage(); break;
323 case CEM_OMMEL: Eater->AddOmmelConsumeEndMessage(); break;
324 case CEM_PEPSI: Eater->AddPepsiConsumeEndMessage(); break;
325 case CEM_KOBOLD_FLESH: Eater->AddKoboldFleshConsumeEndMessage(); break;
326 case CEM_HEALING_LIQUID: Eater->AddHealingLiquidConsumeEndMessage(); break;
327 case CEM_ANTIDOTE: Eater->AddAntidoteConsumeEndMessage(); break;
328 case CEM_ESP: Eater->AddESPConsumeMessage(); break;
329 case CEM_HOLY_BANANA: Eater->AddHolyBananaConsumeEndMessage(); break;
330 case CEM_PEA_SOUP: Eater->AddPeaSoupConsumeEndMessage(); break;
331 case CEM_BLACK_UNICORN_FLESH:
332 Eater->AddBlackUnicornConsumeEndMessage();
333 break;
334 case CEM_GRAY_UNICORN_FLESH:
335 Eater->AddGrayUnicornConsumeEndMessage();
336 break;
337 case CEM_WHITE_UNICORN_FLESH:
338 Eater->AddWhiteUnicornConsumeEndMessage();
339 break;
340 case CEM_OMMEL_BONE: Eater->AddOmmelBoneConsumeEndMessage(); break;
341 case CEM_COCA_COLA: Eater->AddCocaColaConsumeEndMessage(); break;
342 case CEM_LIQUID_HORROR: Eater->AddLiquidHorrorConsumeEndMessage(); break;
343 }
344 }
345
SpawnAndLoad(inputfile & SaveFile) const346 material* materialprototype::SpawnAndLoad(inputfile& SaveFile) const
347 {
348 material* Material = Spawner(0, 0, true);
349 Material->Load(SaveFile);
350 return Material;
351 }
352
MakeMaterial(int Config,long Volume)353 material* material::MakeMaterial(int Config, long Volume)
354 {
355 if(!Config)
356 return 0;
357
358 switch(Config >> 12)
359 {
360 case SOLID_ID >> 12: return solid::Spawn(Config, Volume);
361 case ORGANIC_ID >> 12: return organic::Spawn(Config, Volume);
362 case GAS_ID >> 12: return gas::Spawn(Config, Volume);
363 case LIQUID_ID >> 12: return liquid::Spawn(Config, Volume);
364 case FLESH_ID >> 12: return flesh::Spawn(Config, Volume);
365 case POWDER_ID >> 12: return powder::Spawn(Config, Volume);
366 case IRON_ALLOY_ID >> 12: return ironalloy::Spawn(Config, Volume);
367 }
368
369 ABORT("Odd material configuration number %d of volume %ld requested!",
370 Config, Volume);
371 return 0;
372 }
373
SetVolume(long What)374 void material::SetVolume(long What)
375 {
376 Volume = What;
377
378 if(MotherEntity)
379 MotherEntity->SignalVolumeAndWeightChange();
380 }
381
Initialize(int NewConfig,long InitVolume,truth Load)382 void material::Initialize(int NewConfig, long InitVolume, truth Load)
383 {
384 if(!Load)
385 {
386 databasecreator<material>::InstallDataBase(this, NewConfig);
387 Volume = InitVolume;
388 PostConstruct();
389 }
390 }
391
GetTotalNutritionValue() const392 long material::GetTotalNutritionValue() const
393 {
394 return GetNutritionValue() * GetVolume() / 50;
395 }
396
CanBeEatenByAI(ccharacter * Eater) const397 truth material::CanBeEatenByAI(ccharacter* Eater) const
398 {
399 return ((Eater->GetAttribute(WISDOM) < GetConsumeWisdomLimit()
400 || (Eater->IsAlcoholic() && (GetCategoryFlags() & IS_BEVERAGE)))
401 && !GetSpoilLevel() && !Eater->CheckCannibalism(this));
402 }
403
BreatheEffect(character * Enemy)404 truth material::BreatheEffect(character* Enemy)
405 {
406 return Effect(Enemy, TORSO_INDEX, Max<long>(GetVolume() / 10, 50));
407 }
408
CauseExplosion(character * Idiot,long Damage)409 truth material::CauseExplosion(character* Idiot, long Damage)
410 {
411 lsquare* Square = Idiot->GetLSquareUnder();
412
413 if(!Damage)
414 return false;
415
416 if(IsExplosive())
417 {
418 if(Idiot->IsPlayer())
419 ADD_MESSAGE("Suddenly you are engulfed in flames!");
420 else if(Idiot->CanBeSeenByPlayer())
421 ADD_MESSAGE("%s steps in %s.", Idiot->CHAR_NAME(DEFINITE), CHAR_NAME(INDEFINITE));
422 else if(Square->CanBeSeenByPlayer(true))
423 ADD_MESSAGE("Something explodes!");
424
425 Square->GetLevel()->Explosion(0, "killed in a gas explosion", Square->GetPos(), Damage);
426 return true;
427 }
428
429 return false;
430 }
431
ExplosiveEffect(character * Enemy)432 truth material::ExplosiveEffect(character* Enemy)
433 {
434 return CauseExplosion(Enemy, Max<long>(GetVolume() / 6, 10));
435 }
436
GetDataBase(int Config)437 const materialdatabase* material::GetDataBase(int Config)
438 {
439 const prototype* Proto = 0;
440
441 switch(Config >> 12)
442 {
443 case SOLID_ID >> 12: Proto = &solid::ProtoType; break;
444 case ORGANIC_ID >> 12: Proto = &organic::ProtoType; break;
445 case GAS_ID >> 12: Proto = &gas::ProtoType; break;
446 case LIQUID_ID >> 12: Proto = &liquid::ProtoType; break;
447 case FLESH_ID >> 12: Proto = &flesh::ProtoType; break;
448 case POWDER_ID >> 12: Proto = &powder::ProtoType; break;
449 case IRON_ALLOY_ID >> 12: Proto = &ironalloy::ProtoType; break;
450 }
451
452 const database* DataBase;
453 databasecreator<material>::FindDataBase(DataBase, Proto, Config);
454
455 if(DataBase)
456 return DataBase;
457
458 ABORT("Odd material configuration number %d requested!", Config);
459 return 0;
460 }
461
FinishConsuming(character * Consumer)462 void material::FinishConsuming(character* Consumer)
463 {
464 if(!Consumer->IsPlayer() && GetConsumeWisdomLimit() != NO_LIMIT)
465 Consumer->EditExperience(WISDOM, 150, 1 << 13); /** C **/
466
467 AddConsumeEndMessage(Consumer);
468 }
469
InitDefaults(const materialprototype * NewProtoType,int NewConfig)470 void materialdatabase::InitDefaults(const materialprototype* NewProtoType, int NewConfig)
471 {
472 ProtoType = NewProtoType;
473 DigProductMaterial = Config = NewConfig;
474 CommonFlags |= IS_ABSTRACT; // dummy value for configcontainer
475 }
476
CreateNaturalForm(int Config,long Volume)477 item* material::CreateNaturalForm(int Config, long Volume)
478 {
479 item* Item = GetDataBase(Config)->NaturalForm.Instantiate(NO_MATERIALS
480 |NO_PIC_UPDATE);
481 Item->InitMaterials(MAKE_MATERIAL(Config, Volume));
482 return Item;
483 }
484
CreateNaturalForm(long Volume) const485 item* material::CreateNaturalForm(long Volume) const
486 {
487 item* Item = GetNaturalForm().Instantiate(NO_MATERIALS|NO_PIC_UPDATE);
488 Item->InitMaterials(SpawnMore(Volume));
489 return Item;
490 }
491
GetHardenedMaterial(citem * Item) const492 int material::GetHardenedMaterial(citem* Item) const
493 {
494 const materialdatabase* DB = DataBase;
495
496 if(!Item->FlexibilityIsEssential())
497 return DB->HardenedMaterial;
498
499 while(DB->HardenedMaterial != NONE)
500 {
501 DB = material::GetDataBase(DB->HardenedMaterial);
502
503 if(DataBase->Flexibility <= DB->Flexibility)
504 return DB->Config;
505 }
506
507 return DB->HardenedMaterial;
508 }
509
GetSoftenedMaterial(citem * Item) const510 int material::GetSoftenedMaterial(citem* Item) const
511 {
512 const materialdatabase* DB = DataBase;
513 return DB->SoftenedMaterial;
514 }
515
GetHardenModifier(citem * Item) const516 int material::GetHardenModifier(citem* Item) const
517 {
518 int M = GetFlexibility() << 2;
519
520 if(!Item || !Item->FlexibilityIsEssential())
521 M += GetStrengthValue();
522
523 return M;
524 }
525
IsExplosive() const526 truth material::IsExplosive() const
527 {
528 return DataBase->InteractionFlags & CAN_EXPLODE;
529 }
530
IsSparkling() const531 truth material::IsSparkling() const
532 {
533 return DataBase->CategoryFlags & IS_SPARKLING;
534 }
535
IsStuckTo(ccharacter * Char) const536 truth material::IsStuckTo(ccharacter* Char) const
537 {
538 return MotherEntity->IsStuckTo(Char);
539 }
540