moved almost all hardcoded constants to "define.dat"
[k8-i-v-a-n.git] / src / game / materia.cpp
blob61a5dea49a2eec102b8afe1c03269799d1a88cb8
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
13 /* Compiled through materset.cpp */
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); }
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 {
33 if (Articled) {
34 if (GetNameFlags() & USE_AN) Name << "an "; else Name << "a ";
36 Name << (Adjective ? GetAdjectiveStem() : GetNameStem());
39 festring material::GetName(truth Articled, truth Adjective) const
41 festring Name;
42 Name.Empty();
43 AddName(Name, Articled, Adjective);
44 return Name;
47 material* material::TakeDipVolumeAway()
49 if(Volume > 500)
51 EditVolume(-500);
52 return SpawnMore(500);
54 else
55 return MotherEntity->RemoveMaterial(this);
58 material *material::TakeAllVolumeAway () {
59 return MotherEntity->RemoveMaterial(this);
62 void material::Save(outputfile& SaveFile) const
64 SaveFile << (uShort)GetType();
65 SaveFile << Volume;
66 SaveFile << (uShort)GetConfig();
69 void material::Load(inputfile& SaveFile)
71 SaveFile >> Volume;
72 databasecreator<material>::InstallDataBase(this, ReadType(uShort, SaveFile));
76 truth material::Effect (character *Char, int BodyPart, sLong Amount) {
77 /* Receivexxx should return truth! */
79 Amount = Amount*GetEffectStrength()/100;
81 if (!Amount) return false;
83 auto eff = GetEffect();
85 if (eff == EFFECT_POISON) Char->BeginTemporaryState(POISONED, Amount);
86 else if (eff == EFFECT_DARKNESS) Char->ReceiveDarkness(Amount);
87 else if (eff == EFFECT_OMMEL_URINE) Char->ReceiveOmmelUrine(Amount);
88 else if (eff == EFFECT_PEPSI) Char->ReceivePepsi(Amount);
89 else if (eff == EFFECT_KOBOLD_FLESH) Char->ReceiveKoboldFlesh(Amount);
90 else if (eff == EFFECT_HEAL) Char->ReceiveHeal(Amount);
91 else if (eff == EFFECT_LYCANTHROPY) { if (!Char->StateIsActivated(DISEASE_IMMUNITY)) Char->BeginTemporaryState(LYCANTHROPY, Amount); }
92 else if (eff == EFFECT_SCHOOL_FOOD) Char->ReceiveSchoolFood(Amount);
93 else if (eff == EFFECT_ANTIDOTE) Char->ReceiveAntidote(Amount);
94 else if (eff == EFFECT_CONFUSE) Char->BeginTemporaryState(CONFUSED, Amount);
95 else if (eff == EFFECT_POLYMORPH) Char->BeginTemporaryState(POLYMORPH, Amount);
96 else if (eff == EFFECT_ESP) Char->BeginTemporaryState(ESP, Amount);
97 else if (eff == EFFECT_SKUNK_SMELL) Char->BeginTemporaryState(POISONED, Amount);
98 else if (eff == EFFECT_MAGIC_MUSHROOM) {
99 v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos();
100 Char->ActivateRandomState(SRC_MAGIC_MUSHROOM, Amount, Volume%250+Pos.X+Pos.Y+1);
102 else if (eff == EFFECT_TRAIN_PERCEPTION) Char->EditExperience(PERCEPTION, Amount, 1<<14);
103 else if (eff == EFFECT_HOLY_BANANA) Char->ReceiveHolyBanana(Amount);
104 else if (eff == EFFECT_EVIL_WONDER_STAFF_VAPOUR) {
105 v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos();
106 Char->ActivateRandomState(SRC_EVIL, Amount, Volume%250+Pos.X+Pos.Y+1);
108 else if (eff == EFFECT_GOOD_WONDER_STAFF_VAPOUR) {
109 v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos();
110 Char->ActivateRandomState(SRC_GOOD, Amount, Volume%250+Pos.X+Pos.Y+1);
112 else if (eff == EFFECT_PEA_SOUP) Char->ReceivePeaSoup(Amount);
113 else if (eff == EFFECT_BLACK_UNICORN_FLESH) Char->ReceiveBlackUnicorn(Amount);
114 else if (eff == EFFECT_GRAY_UNICORN_FLESH) Char->ReceiveGrayUnicorn(Amount);
115 else if (eff == EFFECT_WHITE_UNICORN_FLESH) Char->ReceiveWhiteUnicorn(Amount);
116 else if (eff == EFFECT_TELEPORT_CONTROL) Char->BeginTemporaryState(TELEPORT_CONTROL, Amount);
117 else if (eff == EFFECT_MUSHROOM) {
118 v2 Pos = GetMotherEntity()->GetSquareUnderEntity()->GetPos();
119 Char->ActivateRandomState(SRC_MUSHROOM, Amount, Volume%250+Pos.X+Pos.Y+1);
121 else if (eff == EFFECT_OMMEL_CERUMEN) Char->ReceiveOmmelCerumen(Amount);
122 else if (eff == EFFECT_OMMEL_SWEAT) Char->ReceiveOmmelSweat(Amount);
123 else if (eff == EFFECT_OMMEL_TEARS) Char->ReceiveOmmelTears(Amount);
124 else if (eff == EFFECT_OMMEL_SNOT) Char->ReceiveOmmelSnot(Amount);
125 else if (eff == EFFECT_OMMEL_BONE) Char->ReceiveOmmelBone(Amount);
126 else if (eff == EFFECT_MUSTARD_GAS) Char->ReceiveMustardGas(BodyPart, Amount);
127 else if (eff == EFFECT_MUSTARD_GAS_LIQUID) Char->ReceiveMustardGasLiquid(BodyPart, Amount);
128 else if (eff == EFFECT_PANIC) { if (!Char->StateIsActivated(FEARLESS)) Char->BeginTemporaryState(PANIC, Amount); }
129 else if (eff == EFFECT_TELEPORT) Char->BeginTemporaryState(TELEPORT, Amount);
130 else if (eff == EFFECT_VAMPIRISM) { if (!Char->StateIsActivated(DISEASE_IMMUNITY)) Char->BeginTemporaryState(VAMPIRISM, Amount); }
131 else if (eff == EFFECT_DETECTING) Char->BeginTemporaryState(DETECTING, Amount);
132 else return false;
134 return true;
137 material* material::EatEffect(character* Eater, sLong Amount)
139 Amount = Volume > Amount ? Amount : Volume;
140 Eater->ReceiveNutrition(GetNutritionValue() * Amount / 50);
141 if(Amount && Volume)
143 if(DisablesPanicWhenConsumed() && Eater->TemporaryStateIsActivated(PANIC))
145 if(Eater->IsPlayer())
147 ADD_MESSAGE("You relax a bit.");
149 else if(Eater->CanBeSeenByPlayer())
151 ADD_MESSAGE("%s relaxes a bit.", Eater->CHAR_NAME(DEFINITE));
153 Eater->DeActivateTemporaryState(PANIC);
157 if(GetInteractionFlags() & AFFECT_INSIDE)
159 head* Head = Eater->GetVirtualHead();
160 sLong NewAmount = Amount;
162 if(Head && Amount >= 8)
164 Head->AddFluid(static_cast<liquid*>(SpawnMore(Amount >> 3)),
165 CONST_S("throat"), 0, true);
166 NewAmount -= Amount >> 3;
169 Eater->GetTorso()->AddFluid(static_cast<liquid*>(SpawnMore(NewAmount)),
170 CONST_S("stomach"), 0, true);
172 else
174 Effect(Eater, TORSO_INDEX, Amount);
176 if(IsLiquid())
177 Eater->EditStamina(int(50. * Amount * Eater->GetMaxStamina()
178 / Eater->GetBodyVolume()),
179 false);
182 if(Volume != Amount)
184 EditVolume(-Amount);
185 return 0;
187 else
188 return MotherEntity->RemoveMaterial(this);
191 truth material::HitEffect(character* Enemy, bodypart* BodyPart)
193 if(!Volume)
194 return false;
196 auto mtp = GetHitMessage();
197 if (mtp == HM_SCHOOL_FOOD) Enemy->AddSchoolFoodHitMessage();
198 else if (mtp == HM_FROG_FLESH) Enemy->AddFrogFleshConsumeEndMessage();
199 else if (mtp == HM_OMMEL) Enemy->AddOmmelConsumeEndMessage();
200 else if (mtp == HM_PEPSI) Enemy->AddPepsiConsumeEndMessage();
201 else if (mtp == HM_KOBOLD_FLESH) Enemy->AddKoboldFleshHitMessage();
202 else if (mtp == HM_HEALING_LIQUID) Enemy->AddHealingLiquidConsumeEndMessage();
203 else if (mtp == HM_ANTIDOTE) Enemy->AddAntidoteConsumeEndMessage();
204 else if (mtp == HM_CONFUSE) Enemy->AddConfuseHitMessage();
205 else if (mtp == HM_HOLY_BANANA) Enemy->AddHolyBananaConsumeEndMessage();
206 else if (mtp == HM_HOLY_MANGO) Enemy->AddHolyMangoConsumeEndMessage();
207 else if (mtp == HM_ALIEN_FLESH) Enemy->AddAlienFleshConsumeEndMessage();
209 sLong Amount = Max<sLong>(GetVolume() >> 1, 1);
210 truth Success;
212 if(GetInteractionFlags() & AFFECT_INSIDE)
214 if(BodyPart)
216 BodyPart->AddFluid(static_cast<liquid*>(SpawnMore(Amount)),
217 CONST_S(""), 0, true);
218 Success = true;
220 else
221 Success = false;
223 else
225 int BPIndex = BodyPart ? BodyPart->GetBodyPartIndex() : NONE_INDEX;
226 Success = Effect(Enemy, BPIndex, Amount);
229 if(Amount != Volume)
230 EditVolume(-Amount);
231 else
232 delete MotherEntity->RemoveMaterial(this);
234 return Success;
238 void material::AddConsumeEndMessage (character *Eater) const {
239 auto mtp = GetConsumeEndMessage();
240 if (mtp == CEM_SCHOOL_FOOD) { Eater->AddSchoolFoodConsumeEndMessage(); return; }
241 if (mtp == CEM_BONE) { Eater->AddBoneConsumeEndMessage(); return; }
242 if (mtp == CEM_FROG_FLESH) { Eater->AddFrogFleshConsumeEndMessage(); return; }
243 if (mtp == CEM_OMMEL) { Eater->AddOmmelConsumeEndMessage(); return; }
244 if (mtp == CEM_PEPSI) { Eater->AddPepsiConsumeEndMessage(); return; }
245 if (mtp == CEM_KOBOLD_FLESH) { Eater->AddKoboldFleshConsumeEndMessage(); return; }
246 if (mtp == CEM_HEALING_LIQUID) { Eater->AddHealingLiquidConsumeEndMessage(); return; }
247 if (mtp == CEM_ANTIDOTE) { Eater->AddAntidoteConsumeEndMessage(); return; }
248 if (mtp == CEM_ESP) { Eater->AddESPConsumeMessage(); return; }
249 if (mtp == CEM_HOLY_BANANA) { Eater->AddHolyBananaConsumeEndMessage(); return; }
250 if (mtp == CEM_PEA_SOUP) { Eater->AddPeaSoupConsumeEndMessage(); return; }
251 if (mtp == CEM_BLACK_UNICORN_FLESH) { Eater->AddBlackUnicornConsumeEndMessage(); return; }
252 if (mtp == CEM_GRAY_UNICORN_FLESH) { Eater->AddGrayUnicornConsumeEndMessage(); return; }
253 if (mtp == CEM_WHITE_UNICORN_FLESH) { Eater->AddWhiteUnicornConsumeEndMessage(); return; }
254 if (mtp == CEM_OMMEL_BONE) { Eater->AddOmmelBoneConsumeEndMessage(); return; }
255 if (mtp == CEM_LIQUID_HORROR) { Eater->AddLiquidHorrorConsumeEndMessage(); return; }
256 if (mtp == CEM_HOLY_MANGO) { Eater->AddHolyMangoConsumeEndMessage(); return; }
257 if (mtp == CEM_ALIEN_FLESH) { Eater->AddAlienFleshConsumeEndMessage(); return; }
261 material *materialprototype::SpawnAndLoad (inputfile &SaveFile) const {
262 material *Material = Spawner(0, 0, true);
263 Material->Load(SaveFile);
264 return Material;
268 material *material::MakeMaterial (int Config, sLong Volume) {
269 if (!Config) return 0;
271 auto cfg = (Config>>12);
272 if (cfg == SOLID_ID >> 12) return solid::Spawn(Config, Volume);
273 if (cfg == ORGANIC_ID >> 12) return organic::Spawn(Config, Volume);
274 if (cfg == GAS_ID >> 12) return gas::Spawn(Config, Volume);
275 if (cfg == LIQUID_ID >> 12) return liquid::Spawn(Config, Volume);
276 if (cfg == FLESH_ID >> 12) return flesh::Spawn(Config, Volume);
277 if (cfg == POWDER_ID >> 12) return powder::Spawn(Config, Volume);
278 if (cfg == IRON_ALLOY_ID >> 12) return ironalloy::Spawn(Config, Volume);
280 ABORT("Odd material configuration number %d of volume %d requested!", Config, Volume);
281 return 0;
284 void material::SetVolume(sLong What)
286 Volume = What;
288 if(MotherEntity)
289 MotherEntity->SignalVolumeAndWeightChange();
292 void material::Initialize(int NewConfig, sLong InitVolume, truth Load)
294 if(!Load)
296 databasecreator<material>::InstallDataBase(this, NewConfig);
297 Volume = InitVolume;
298 PostConstruct();
302 sLong material::GetTotalNutritionValue() const
304 return GetNutritionValue() * GetVolume() / 50;
307 truth material::CanBeEatenByAI(ccharacter* Eater) const
309 return ((Eater->GetAttribute(WISDOM) < GetConsumeWisdomLimit()
310 || (Eater->IsAlcoholic() && (GetCategoryFlags() & IS_BEVERAGE)))
311 && !GetSpoilLevel() && !Eater->CheckCannibalism(this));
314 truth material::BreatheEffect(character* Enemy)
316 return Effect(Enemy, TORSO_INDEX, Max<sLong>(GetVolume() / 10, 50));
320 const materialdatabase* material::GetDataBase (int Config) {
321 const prototype *Proto = 0;
323 auto cfg = (Config >> 12);
324 if (cfg == SOLID_ID >> 12) Proto = &solid::ProtoType;
325 else if (cfg == ORGANIC_ID >> 12) Proto = &organic::ProtoType;
326 else if (cfg == GAS_ID >> 12) Proto = &gas::ProtoType;
327 else if (cfg == LIQUID_ID >> 12) Proto = &liquid::ProtoType;
328 else if (cfg == FLESH_ID >> 12) Proto = &flesh::ProtoType;
329 else if (cfg == POWDER_ID >> 12) Proto = &powder::ProtoType;
330 else if (cfg == IRON_ALLOY_ID >> 12) Proto = &ironalloy::ProtoType;
332 //FIXME: k8: check for nullptr `Proto`?
334 const database *DataBase;
335 databasecreator<material>::FindDataBase(DataBase, Proto, Config);
337 if (DataBase) return DataBase;
339 ABORT("Odd material configuration number %d requested!", Config);
340 return 0;
344 void material::FinishConsuming(character* Consumer)
346 if(!Consumer->IsPlayer() && GetConsumeWisdomLimit() != NO_LIMIT)
347 Consumer->EditExperience(WISDOM, 150, 1 << 13); /** C **/
349 AddConsumeEndMessage(Consumer);
352 void materialdatabase::InitDefaults (const materialprototype *NewProtoType, int NewConfig, cfestring &acfgstrname)
354 ProtoType = NewProtoType;
355 DigProductMaterial = Config = NewConfig;
356 CommonFlags |= IS_ABSTRACT; // dummy value for configcontainer
357 CfgStrName = acfgstrname;
360 item* material::CreateNaturalForm(int Config, sLong Volume)
362 item* Item = GetDataBase(Config)->NaturalForm.Instantiate(NO_MATERIALS
363 |NO_PIC_UPDATE);
364 Item->InitMaterials(MAKE_MATERIAL(Config, Volume));
365 return Item;
368 item* material::CreateNaturalForm(sLong Volume) const
370 item* Item = GetNaturalForm().Instantiate(NO_MATERIALS|NO_PIC_UPDATE);
371 Item->InitMaterials(SpawnMore(Volume));
372 return Item;
376 int material::GetHardenedMaterial (citem *Item) const {
377 const materialdatabase *DB = DataBase;
379 if (!Item->FlexibilityIsEssential()) return DB->HardenedMaterial;
380 while (DB->HardenedMaterial != NONE) {
381 DB = material::GetDataBase(DB->HardenedMaterial);
382 if (DataBase->Flexibility <= DB->Flexibility) return DB->Config;
384 return DB->HardenedMaterial;
388 int material::GetSoftenedMaterial (citem *Item) const {
389 const materialdatabase *DB = DataBase;
391 if (!Item->FlexibilityIsEssential()) return DB->SoftenedMaterial;
392 while (DB->SoftenedMaterial != NONE) {
393 DB = material::GetDataBase(DB->SoftenedMaterial);
394 if (DataBase->Flexibility <= DB->Flexibility) return DB->Config;
396 return DB->SoftenedMaterial;
400 int material::GetHardenModifier(citem* Item) const
402 int M = GetFlexibility() << 2;
404 if(!Item || !Item->FlexibilityIsEssential())
405 M += GetStrengthValue();
407 return M;
410 truth material::IsExplosive() const
412 return DataBase->InteractionFlags & CAN_EXPLODE;
415 truth material::IsSparkling() const
417 return DataBase->CategoryFlags & IS_SPARKLING;
420 truth material::IsStuckTo(ccharacter* Char) const
422 return MotherEntity->IsStuckTo(Char);