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
13 /* Compiled through materset.cpp */
15 materialprototype::materialprototype(const materialprototype
* Base
,
16 materialspawner Spawner
,
17 materialcloner Cloner
,
19 : Base(Base
), Spawner(Spawner
), Cloner(Cloner
), ClassID(ClassID
)
20 { Index
= protocontainer
<material
>::Add(this); }
22 sLong
material::GetRawPrice() const
23 { return GetPriceModifier() * GetWeight() / 10000; }
24 truth
material::CanBeDug(material
* ShovelMaterial
) const
25 { return ShovelMaterial
->GetStrengthValue() > GetStrengthValue(); }
26 sLong
material::GetTotalExplosivePower() const
27 { return sLong(double(Volume
) * GetExplosivePower() / 1000000); }
28 cchar
* material::GetConsumeVerb() const { return "eating"; }
30 materialpredicate TrueMaterialPredicate
= &material::True
;
32 void material::AddName (festring
&Name
, truth Articled
, truth Adjective
) const {
34 if (GetNameFlags() & USE_AN
) Name
<< "an "; else Name
<< "a ";
36 Name
<< (Adjective
? GetAdjectiveStem() : GetNameStem());
39 festring
material::GetName(truth Articled
, truth Adjective
) const
43 AddName(Name
, Articled
, Adjective
);
47 material
* material::TakeDipVolumeAway()
52 return SpawnMore(500);
55 return MotherEntity
->RemoveMaterial(this);
58 material
*material::TakeAllVolumeAway () {
59 return MotherEntity
->RemoveMaterial(this);
62 void material::Save(outputfile
& SaveFile
) const
65 SaveFile
<< (uShort
)GetConfig();
68 void material::Load(inputfile
& SaveFile
)
71 databasecreator
<material
>::InstallDataBase(this, ReadType(uShort
, SaveFile
));
75 truth
material::Effect (character
*Char
, int BodyPart
, sLong Amount
) {
76 /* Receivexxx should return truth! */
78 Amount
= Amount
*GetEffectStrength()/100;
80 if (!Amount
) return false;
82 auto eff
= GetEffect();
84 if (eff
== EFFECT_POISON
) Char
->BeginTemporaryState(POISONED
, Amount
);
85 else if (eff
== EFFECT_DARKNESS
) Char
->ReceiveDarkness(Amount
);
86 else if (eff
== EFFECT_OMMEL_URINE
) Char
->ReceiveOmmelUrine(Amount
);
87 else if (eff
== EFFECT_PEPSI
) Char
->ReceivePepsi(Amount
);
88 else if (eff
== EFFECT_KOBOLD_FLESH
) Char
->ReceiveKoboldFlesh(Amount
);
89 else if (eff
== EFFECT_HEAL
) Char
->ReceiveHeal(Amount
);
90 else if (eff
== EFFECT_LYCANTHROPY
) { if (!Char
->StateIsActivated(DISEASE_IMMUNITY
)) Char
->BeginTemporaryState(LYCANTHROPY
, Amount
); }
91 else if (eff
== EFFECT_SCHOOL_FOOD
) Char
->ReceiveSchoolFood(Amount
);
92 else if (eff
== EFFECT_ANTIDOTE
) Char
->ReceiveAntidote(Amount
);
93 else if (eff
== EFFECT_CONFUSE
) Char
->BeginTemporaryState(CONFUSED
, Amount
);
94 else if (eff
== EFFECT_POLYMORPH
) Char
->BeginTemporaryState(POLYMORPH
, Amount
);
95 else if (eff
== EFFECT_ESP
) Char
->BeginTemporaryState(ESP
, Amount
);
96 else if (eff
== EFFECT_SKUNK_SMELL
) Char
->BeginTemporaryState(POISONED
, Amount
);
97 else if (eff
== EFFECT_MAGIC_MUSHROOM
) {
98 v2 Pos
= GetMotherEntity()->GetSquareUnderEntity()->GetPos();
99 Char
->ActivateRandomState(SRC_MAGIC_MUSHROOM
, Amount
, Volume
%250+Pos
.X
+Pos
.Y
+1);
101 else if (eff
== EFFECT_TRAIN_PERCEPTION
) Char
->EditExperience(PERCEPTION
, Amount
, 1<<14);
102 else if (eff
== EFFECT_HOLY_BANANA
) Char
->ReceiveHolyBanana(Amount
);
103 else if (eff
== EFFECT_EVIL_WONDER_STAFF_VAPOUR
) {
104 v2 Pos
= GetMotherEntity()->GetSquareUnderEntity()->GetPos();
105 Char
->ActivateRandomState(SRC_EVIL
, Amount
, Volume
%250+Pos
.X
+Pos
.Y
+1);
107 else if (eff
== EFFECT_GOOD_WONDER_STAFF_VAPOUR
) {
108 v2 Pos
= GetMotherEntity()->GetSquareUnderEntity()->GetPos();
109 Char
->ActivateRandomState(SRC_GOOD
, Amount
, Volume
%250+Pos
.X
+Pos
.Y
+1);
111 else if (eff
== EFFECT_PEA_SOUP
) Char
->ReceivePeaSoup(Amount
);
112 else if (eff
== EFFECT_BLACK_UNICORN_FLESH
) Char
->ReceiveBlackUnicorn(Amount
);
113 else if (eff
== EFFECT_GRAY_UNICORN_FLESH
) Char
->ReceiveGrayUnicorn(Amount
);
114 else if (eff
== EFFECT_WHITE_UNICORN_FLESH
) Char
->ReceiveWhiteUnicorn(Amount
);
115 else if (eff
== EFFECT_TELEPORT_CONTROL
) Char
->BeginTemporaryState(TELEPORT_CONTROL
, Amount
);
116 else if (eff
== EFFECT_MUSHROOM
) {
117 v2 Pos
= GetMotherEntity()->GetSquareUnderEntity()->GetPos();
118 Char
->ActivateRandomState(SRC_MUSHROOM
, Amount
, Volume
%250+Pos
.X
+Pos
.Y
+1);
120 else if (eff
== EFFECT_OMMEL_CERUMEN
) Char
->ReceiveOmmelCerumen(Amount
);
121 else if (eff
== EFFECT_OMMEL_SWEAT
) Char
->ReceiveOmmelSweat(Amount
);
122 else if (eff
== EFFECT_OMMEL_TEARS
) Char
->ReceiveOmmelTears(Amount
);
123 else if (eff
== EFFECT_OMMEL_SNOT
) Char
->ReceiveOmmelSnot(Amount
);
124 else if (eff
== EFFECT_OMMEL_BONE
) Char
->ReceiveOmmelBone(Amount
);
125 else if (eff
== EFFECT_MUSTARD_GAS
) Char
->ReceiveMustardGas(BodyPart
, Amount
);
126 else if (eff
== EFFECT_MUSTARD_GAS_LIQUID
) Char
->ReceiveMustardGasLiquid(BodyPart
, Amount
);
127 else if (eff
== EFFECT_PANIC
) { if (!Char
->StateIsActivated(FEARLESS
)) Char
->BeginTemporaryState(PANIC
, Amount
); }
128 else if (eff
== EFFECT_TELEPORT
) Char
->BeginTemporaryState(TELEPORT
, Amount
);
129 else if (eff
== EFFECT_VAMPIRISM
) { if (!Char
->StateIsActivated(DISEASE_IMMUNITY
)) Char
->BeginTemporaryState(VAMPIRISM
, Amount
); }
130 else if (eff
== EFFECT_DETECTING
) Char
->BeginTemporaryState(DETECTING
, Amount
);
136 material
* material::EatEffect(character
* Eater
, sLong Amount
)
138 Amount
= Volume
> Amount
? Amount
: Volume
;
139 Eater
->ReceiveNutrition(GetNutritionValue() * Amount
/ 50);
142 if(DisablesPanicWhenConsumed() && Eater
->TemporaryStateIsActivated(PANIC
))
144 if(Eater
->IsPlayer())
146 ADD_MESSAGE("You relax a bit.");
148 else if(Eater
->CanBeSeenByPlayer())
150 ADD_MESSAGE("%s relaxes a bit.", Eater
->CHAR_NAME(DEFINITE
));
152 Eater
->DeActivateTemporaryState(PANIC
);
156 if(GetInteractionFlags() & AFFECT_INSIDE
)
158 head
* Head
= Eater
->GetVirtualHead();
159 sLong NewAmount
= Amount
;
161 if(Head
&& Amount
>= 8)
163 Head
->AddFluid(static_cast<liquid
*>(SpawnMore(Amount
>> 3)), CONST_S("throat"), 0, true);
164 NewAmount
-= Amount
>> 3;
167 Eater
->GetTorso()->AddFluid(static_cast<liquid
*>(SpawnMore(NewAmount
)), CONST_S("stomach"), 0, true);
171 Effect(Eater
, TORSO_INDEX
, Amount
);
174 Eater
->EditStamina(int(50. * Amount
* Eater
->GetMaxStamina() / Eater
->GetBodyVolume()),
184 return MotherEntity
->RemoveMaterial(this);
187 truth
material::HitEffect(character
* Enemy
, bodypart
* BodyPart
)
192 auto mtp
= GetHitMessage();
193 if (mtp
== HM_SCHOOL_FOOD
) Enemy
->AddSchoolFoodHitMessage();
194 else if (mtp
== HM_FROG_FLESH
) Enemy
->AddFrogFleshConsumeEndMessage();
195 else if (mtp
== HM_OMMEL
) Enemy
->AddOmmelConsumeEndMessage();
196 else if (mtp
== HM_PEPSI
) Enemy
->AddPepsiConsumeEndMessage();
197 else if (mtp
== HM_KOBOLD_FLESH
) Enemy
->AddKoboldFleshHitMessage();
198 else if (mtp
== HM_HEALING_LIQUID
) Enemy
->AddHealingLiquidConsumeEndMessage();
199 else if (mtp
== HM_ANTIDOTE
) Enemy
->AddAntidoteConsumeEndMessage();
200 else if (mtp
== HM_CONFUSE
) Enemy
->AddConfuseHitMessage();
201 else if (mtp
== HM_HOLY_BANANA
) Enemy
->AddHolyBananaConsumeEndMessage();
202 else if (mtp
== HM_HOLY_MANGO
) Enemy
->AddHolyMangoConsumeEndMessage();
203 else if (mtp
== HM_ALIEN_FLESH
) Enemy
->AddAlienFleshConsumeEndMessage();
205 sLong Amount
= Max
<sLong
>(GetVolume() >> 1, 1);
208 if(GetInteractionFlags() & AFFECT_INSIDE
)
212 BodyPart
->AddFluid(static_cast<liquid
*>(SpawnMore(Amount
)),
213 CONST_S(""), 0, true);
221 int BPIndex
= BodyPart
? BodyPart
->GetBodyPartIndex() : NONE_INDEX
;
222 Success
= Effect(Enemy
, BPIndex
, Amount
);
228 delete MotherEntity
->RemoveMaterial(this);
234 void material::AddConsumeEndMessage (character
*Eater
) const {
235 auto mtp
= GetConsumeEndMessage();
236 if (mtp
== CEM_SCHOOL_FOOD
) { Eater
->AddSchoolFoodConsumeEndMessage(); return; }
237 if (mtp
== CEM_BONE
) { Eater
->AddBoneConsumeEndMessage(); return; }
238 if (mtp
== CEM_FROG_FLESH
) { Eater
->AddFrogFleshConsumeEndMessage(); return; }
239 if (mtp
== CEM_OMMEL
) { Eater
->AddOmmelConsumeEndMessage(); return; }
240 if (mtp
== CEM_PEPSI
) { Eater
->AddPepsiConsumeEndMessage(); return; }
241 if (mtp
== CEM_KOBOLD_FLESH
) { Eater
->AddKoboldFleshConsumeEndMessage(); return; }
242 if (mtp
== CEM_HEALING_LIQUID
) { Eater
->AddHealingLiquidConsumeEndMessage(); return; }
243 if (mtp
== CEM_ANTIDOTE
) { Eater
->AddAntidoteConsumeEndMessage(); return; }
244 if (mtp
== CEM_ESP
) { Eater
->AddESPConsumeMessage(); return; }
245 if (mtp
== CEM_HOLY_BANANA
) { Eater
->AddHolyBananaConsumeEndMessage(); return; }
246 if (mtp
== CEM_PEA_SOUP
) { Eater
->AddPeaSoupConsumeEndMessage(); return; }
247 if (mtp
== CEM_BLACK_UNICORN_FLESH
) { Eater
->AddBlackUnicornConsumeEndMessage(); return; }
248 if (mtp
== CEM_GRAY_UNICORN_FLESH
) { Eater
->AddGrayUnicornConsumeEndMessage(); return; }
249 if (mtp
== CEM_WHITE_UNICORN_FLESH
) { Eater
->AddWhiteUnicornConsumeEndMessage(); return; }
250 if (mtp
== CEM_OMMEL_BONE
) { Eater
->AddOmmelBoneConsumeEndMessage(); return; }
251 if (mtp
== CEM_LIQUID_HORROR
) { Eater
->AddLiquidHorrorConsumeEndMessage(); return; }
252 if (mtp
== CEM_HOLY_MANGO
) { Eater
->AddHolyMangoConsumeEndMessage(); return; }
253 if (mtp
== CEM_ALIEN_FLESH
) { Eater
->AddAlienFleshConsumeEndMessage(); return; }
257 material
*materialprototype::SpawnAndLoad (inputfile
&SaveFile
) const {
258 material
*Material
= Spawner(0, 0, true);
259 Material
->Load(SaveFile
);
264 material
*material::MakeMaterial (int Config
, sLong Volume
) {
265 if (!Config
) return 0;
267 auto cfg
= (Config
>>12);
268 if (cfg
== SOLID_ID
>> 12) return solid::Spawn(Config
, Volume
);
269 if (cfg
== ORGANIC_ID
>> 12) return organic::Spawn(Config
, Volume
);
270 if (cfg
== GAS_ID
>> 12) return gas::Spawn(Config
, Volume
);
271 if (cfg
== LIQUID_ID
>> 12) return liquid::Spawn(Config
, Volume
);
272 if (cfg
== FLESH_ID
>> 12) return flesh::Spawn(Config
, Volume
);
273 if (cfg
== POWDER_ID
>> 12) return powder::Spawn(Config
, Volume
);
274 if (cfg
== IRON_ALLOY_ID
>> 12) return ironalloy::Spawn(Config
, Volume
);
276 ABORT("Odd material configuration number %d of volume %d requested!", Config
, Volume
);
280 void material::SetVolume(sLong What
)
285 MotherEntity
->SignalVolumeAndWeightChange();
288 void material::Initialize(int NewConfig
, sLong InitVolume
, truth Load
)
292 databasecreator
<material
>::InstallDataBase(this, NewConfig
);
298 sLong
material::GetTotalNutritionValue() const
300 return GetNutritionValue() * GetVolume() / 50;
303 truth
material::CanBeEatenByAI(ccharacter
* Eater
) const
305 return ((Eater
->GetAttribute(WISDOM
) < GetConsumeWisdomLimit()
306 || (Eater
->IsAlcoholic() && (GetCategoryFlags() & IS_BEVERAGE
)))
307 && !GetSpoilLevel() && !Eater
->CheckCannibalism(this));
310 truth
material::BreatheEffect(character
* Enemy
)
312 return Effect(Enemy
, TORSO_INDEX
, Max
<sLong
>(GetVolume() / 10, 50));
316 const materialdatabase
* material::GetDataBase (int Config
) {
317 const prototype
*Proto
= 0;
319 auto cfg
= (Config
>> 12);
320 if (cfg
== SOLID_ID
>> 12) Proto
= &solid::ProtoType
;
321 else if (cfg
== ORGANIC_ID
>> 12) Proto
= &organic::ProtoType
;
322 else if (cfg
== GAS_ID
>> 12) Proto
= &gas::ProtoType
;
323 else if (cfg
== LIQUID_ID
>> 12) Proto
= &liquid::ProtoType
;
324 else if (cfg
== FLESH_ID
>> 12) Proto
= &flesh::ProtoType
;
325 else if (cfg
== POWDER_ID
>> 12) Proto
= &powder::ProtoType
;
326 else if (cfg
== IRON_ALLOY_ID
>> 12) Proto
= &ironalloy::ProtoType
;
328 //FIXME: k8: check for nullptr `Proto`?
330 const database
*DataBase
;
331 databasecreator
<material
>::FindDataBase(DataBase
, Proto
, Config
);
333 if (DataBase
) return DataBase
;
335 ABORT("Odd material configuration number %d requested!", Config
);
340 void material::FinishConsuming(character
* Consumer
)
342 if(!Consumer
->IsPlayer() && GetConsumeWisdomLimit() != NO_LIMIT
)
343 Consumer
->EditExperience(WISDOM
, 150, 1 << 13); /** C **/
345 AddConsumeEndMessage(Consumer
);
348 void materialdatabase::InitDefaults (const materialprototype
*NewProtoType
, int NewConfig
, cfestring
&acfgstrname
)
350 ProtoType
= NewProtoType
;
351 DigProductMaterial
= Config
= NewConfig
;
352 CommonFlags
|= IS_ABSTRACT
; // dummy value for configcontainer
353 CfgStrName
= acfgstrname
;
356 item
* material::CreateNaturalForm(int Config
, sLong Volume
)
358 item
* Item
= GetDataBase(Config
)->NaturalForm
.Instantiate(NO_MATERIALS
360 Item
->InitMaterials(MAKE_MATERIAL(Config
, Volume
));
364 item
* material::CreateNaturalForm(sLong Volume
) const
366 item
* Item
= GetNaturalForm().Instantiate(NO_MATERIALS
|NO_PIC_UPDATE
);
367 Item
->InitMaterials(SpawnMore(Volume
));
372 int material::GetHardenedMaterial (citem
*Item
) const {
373 const materialdatabase
*DB
= DataBase
;
375 if (!Item
->FlexibilityIsEssential()) return DB
->HardenedMaterial
;
376 while (DB
->HardenedMaterial
!= NONE
) {
377 DB
= material::GetDataBase(DB
->HardenedMaterial
);
378 if (DataBase
->Flexibility
<= DB
->Flexibility
) return DB
->Config
;
380 return DB
->HardenedMaterial
;
384 int material::GetSoftenedMaterial (citem
*Item
) const {
385 const materialdatabase
*DB
= DataBase
;
387 if (!Item
->FlexibilityIsEssential()) return DB
->SoftenedMaterial
;
388 while (DB
->SoftenedMaterial
!= NONE
) {
389 DB
= material::GetDataBase(DB
->SoftenedMaterial
);
390 if (DataBase
->Flexibility
<= DB
->Flexibility
) return DB
->Config
;
392 return DB
->SoftenedMaterial
;
396 int material::GetHardenModifier(citem
* Item
) const
398 int M
= GetFlexibility() << 2;
400 if(!Item
|| !Item
->FlexibilityIsEssential())
401 M
+= GetStrengthValue();
406 truth
material::IsExplosive() const
408 return DataBase
->InteractionFlags
& CAN_EXPLODE
;
411 truth
material::IsSparkling() const
413 return DataBase
->CategoryFlags
& IS_SPARKLING
;
416 truth
material::IsStuckTo(ccharacter
* Char
) const
418 return MotherEntity
->IsStuckTo(Char
);