moved almost all hardcoded constants to "define.dat"
[k8-i-v-a-n.git] / src / game / god.cpp
blob6f09d30da2bf5b7c7008e2c15147422f1a97d489
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 godset.cpp */
15 godprototype::godprototype (godspawner Spawner, cchar* ClassID) : Spawner(Spawner), ClassID(ClassID) { Index = protocontainer<god>::Add(this); }
17 god::god() : Relation(0), LastPray(-1), Timer(0), Known(false) { }
18 int god::GetBasicAlignment() const { return NEUTRAL; }
20 cchar *god::GetPersonalPronoun () const { return (GetSex() == MALE ? "He" : "She"); }
21 cchar *god::GetObjectPronoun () const { return (GetSex() == MALE ? "Him" : "Her"); }
24 void god::Pray () {
25 LastPray = 0;
26 if (!Timer) {
27 if (Relation >= -RAND_N(500)) {
28 ADD_MESSAGE("You feel %s is pleased.", GetName());
30 if (!TryToAttachBodyPart(PLAYER) && !TryToHardenBodyPart(PLAYER)) PrayGoodEffect();
32 AdjustTimer(5000);
33 AdjustRelation(50);
34 game::ApplyDivineAlignmentBonuses(this, 10, true);
35 PLAYER->EditExperience(WISDOM, 200, 1<<10);
37 if (Relation > 250 && !(RAND()%20)) {
38 character *Angel = CreateAngel(PLAYER->GetTeam(), 10000);
39 if (Angel) ADD_MESSAGE("%s seems to be very friendly towards you.", Angel->CHAR_DESCRIPTION(DEFINITE));
40 } else if (Relation > 100 && !(RAND()%20)) {
41 sLong Category = RAND()&ANY_CATEGORY;
42 if (!Category) Category = ANY_CATEGORY;
43 item *Gift = protosystem::BalancedCreateItem(0, Relation/2, Relation*2, Category, 0, 0, GetType());
45 if (Gift) {
46 Gift->CalculateEnchantment();
47 PLAYER->GetStack()->AddItem(Gift);
49 if (Gift->IsBroken()) Gift = Gift->Fix();
50 if (Relation == 1000) Gift->EditEnchantment(3);
51 else if (Relation >= 500) Gift->EditEnchantment(2);
52 else Gift->EditEnchantment(1);
54 ADD_MESSAGE("You notice %s in your inventory which you don't recall picking up anywhere.", Gift->CHAR_NAME(INDEFINITE));
57 } else {
58 ADD_MESSAGE("You feel %s is displeased today.", GetName());
59 PrayBadEffect();
60 AdjustTimer(10000);
61 AdjustRelation(-50);
62 game::ApplyDivineAlignmentBonuses(this, 10, false);
63 PLAYER->EditExperience(WISDOM, -50, 1<<10);
65 } else {
66 if (Relation > RAND_N(500) && Timer < RAND_N(500000)) {
67 ADD_MESSAGE("You feel %s is displeased, but tries to help you anyway.", GetName());
68 if (!TryToAttachBodyPart(PLAYER) && !TryToHardenBodyPart(PLAYER)) PrayGoodEffect();
69 AdjustTimer(25000);
70 AdjustRelation(-75);
71 game::ApplyDivineAlignmentBonuses(this, 15, false);
72 PLAYER->EditExperience(WISDOM, -50, 1<<10);
73 } else {
74 ADD_MESSAGE("You feel %s is angry.", GetName());
75 PrayBadEffect();
76 AdjustTimer(50000);
77 AdjustRelation(-100);
78 game::ApplyDivineAlignmentBonuses(this, 20, false);
79 PLAYER->EditExperience(WISDOM, -100, 1<<11);
80 if (Relation < -250 && !(RAND()%5)) {
81 character* Angel = CreateAngel(game::GetTeam(4), 10000);
82 if (Angel) ADD_MESSAGE("%s seems to be hostile.", Angel->CHAR_DESCRIPTION(DEFINITE));
89 festring god::GetCompleteDescription () const {
90 festring Desc(game::GetAlignment(GetAlignment()));
91 Desc.Resize(4);
92 Desc << GetName();
93 Desc.Resize(20);
95 if (game::WizardModeIsActive()) {
96 Desc << "Timer: " << Timer << " Relation: " << Relation;
97 return Desc;
98 } else {
99 Desc << "You have ";
102 if (LastPray > -1) {
103 int Hour = LastPray/2000;
104 int Day = Hour/24;
105 Hour %= 24;
106 int Min = LastPray%2000*60/2000;
107 Desc << "last prayed ";
108 if (Day >= 7) {
109 Desc << "over a week ago.";
110 } else {
111 if (Day > 1) Desc << Day << " days, ";
112 else if (Day) Desc << "one day, ";
113 if (Hour > 1) Desc << Hour << " hours, ";
114 else if (Hour) Desc << "one hour, ";
115 if (Day || Hour) Desc << "and " << Min << " minutes ago.";
116 else Desc << Min << " minutes ago.";
118 } else {
119 Desc << "never prayed to this god.";
121 return Desc;
125 void god::AdjustRelation (god *Competitor, int Multiplier, truth Good) {
126 int Adjustment = (Multiplier<<1)-abs(GetAlignment()-Competitor->GetAlignment())*Multiplier;
127 if (!Good && Adjustment > 0) Adjustment = -Adjustment;
128 AdjustRelation(Adjustment);
132 void god::AdjustRelation (int Amount) {
133 if (Amount < 0) {
134 Amount = Amount*100/(100+PLAYER->GetAttribute(WISDOM));
135 } else {
136 Amount = Amount*(100+PLAYER->GetAttribute(WISDOM))/100;
139 Relation += Amount;
141 if (Relation < -1000) Relation = -1000;
142 if (Relation > 1000) Relation = 1000;
146 void god::AdjustTimer (sLong Amount) {
147 Timer += Amount;
148 if (Timer < 0) Timer = 0;
149 if (Timer > 1000000000) Timer = 1000000000;
153 truth god::PlayerVomitedOnAltar (liquid *Liquid) {
154 if (PLAYER->GetVirtualHead()) {
155 ADD_MESSAGE("The vomit drops on the altar, but then suddenly gravity changes its direction. The vomit lands on your face.");
156 PLAYER->GetVirtualHead()->SpillFluid(0, Liquid);
157 PLAYER->ReceiveDamage(0, 1+(RAND()&1), ACID, HEAD);
158 } else {
159 ADD_MESSAGE("The vomit drops on the altar, but then suddenly gravity changes its direction. The vomit lands all over you.");
160 PLAYER->SpillFluid(0, Liquid);
161 PLAYER->ReceiveDamage(0, 1+(RAND()&1), ACID);
164 AdjustRelation(-200);
165 PLAYER->CheckDeath(CONST_S("killed by a flying lump of vomit"), 0);
167 if (!(RAND()%5)) {
168 character *Angel = CreateAngel(game::GetTeam(4), 10000);
169 if (Angel) ADD_MESSAGE("%s seems to be hostile.", Angel->CHAR_DESCRIPTION(DEFINITE));
172 return true;
176 character *god::CreateAngel (team *Team, int LifeBase) {
177 v2 TryToCreate;
178 for (int c = 0; c < 100; ++c) {
179 TryToCreate = PLAYER->GetPos()+game::GetMoveVector(RAND()%DIRECTION_COMMAND_KEYS);
180 if (game::GetCurrentArea()->IsValidPos(TryToCreate)) {
181 angel *Angel;
183 if (LifeBase && GetType() != VALPURUS && (!(RAND()%5) || abs(Relation) == 1000)) {
184 Angel = archangel::Spawn(GetType());
185 Angel->SetLifeExpectancy(LifeBase/10, 0);
186 } else {
187 Angel = angel::Spawn(GetType());
188 if (LifeBase) Angel->SetLifeExpectancy(LifeBase, 0);
191 lsquare *Square = game::GetCurrentLevel()->GetLSquare(TryToCreate);
193 if (Angel->CanMoveOn(Square) && Angel->IsFreeForMe(Square)) {
194 Angel->SetTeam(Team);
195 Angel->SetGenerationDanger(ANGEL_GENERATION_DANGER);
196 Angel->PutTo(TryToCreate);
197 ADD_MESSAGE("Suddenly %s appears!", Angel->CHAR_DESCRIPTION(INDEFINITE));
198 return Angel;
199 } else {
200 Angel->SendToHell();
205 return 0;
209 void god::PrintRelation () const {
210 cchar *VerbalRelation;
211 int rel = GetRelation();
213 if (rel >= 1000) VerbalRelation = "greets you as a Champion of the Cause!";
214 else if (rel > 750) VerbalRelation = "is extremely pleased.";
215 else if (rel > 250) VerbalRelation = "is very pleased.";
216 else if (rel > 50) VerbalRelation = "is pleased.";
217 else if (rel > -50) VerbalRelation = "is content.";
218 else if (rel > -250) VerbalRelation = "is angry.";
219 else if (rel > -750) VerbalRelation = "is very angry.";
220 else if (rel >= -1000) VerbalRelation = "is extremely angry.";
221 else VerbalRelation = "hates you more than any other mortal.";
223 ADD_MESSAGE("%s %s", GetName(), VerbalRelation);
227 truth god::ReceiveOffer (item *Sacrifice) {
228 if (Sacrifice->SpecialOfferEffect(GetType())) return true;
230 int OfferValue = Sacrifice->GetOfferValue(GetType());
232 if (OfferValue) {
233 if (!Sacrifice->IsDestroyable(PLAYER)) {
234 ADD_MESSAGE("%s is too important for you to be sacrificed.", Sacrifice->CHAR_NAME(DEFINITE));
235 return false;
238 AdjustRelation(OfferValue);
240 game::ApplyDivineAlignmentBonuses(this, (OfferValue >= 0 ? OfferValue : -OfferValue)/5, true);
242 if (OfferValue > 0) {
243 PLAYER->EditExperience(WISDOM, 150, 1<<7);
244 } else {
245 PLAYER->EditExperience(WISDOM, -100, 1<<7);
248 if (OfferValue > 0) {
249 if (Sacrifice->GetAttachedGod() == GetType()) {
250 ADD_MESSAGE("%s appreciates your generous offer truly.", GetName());
251 } else {
252 ADD_MESSAGE("%s thanks you for your gift.", GetName());
254 } else {
255 ADD_MESSAGE("%s seems not to appreciate your gift at all.", GetName());
258 PrintRelation();
259 int RandModifier = Sacrifice->GetAttachedGod() == GetType() ? 50 : 100;
261 if (OfferValue > 0 && Relation > 250 && !(RAND()%RandModifier)) {
262 character *Angel = CreateAngel(PLAYER->GetTeam());
263 if (Angel) ADD_MESSAGE("%s seems to be very friendly towards you.", Angel->CHAR_DESCRIPTION(DEFINITE));
266 return true;
269 ADD_MESSAGE("Nothing happens.");
270 return false;
274 god *godprototype::SpawnAndLoad (inputfile &SaveFile) const {
275 god *God = Spawner();
276 God->Load(SaveFile);
277 return God;
281 truth god::LikesMaterial (const materialdatabase *MDB, ccharacter *) const {
282 return (MDB->AttachedGod == GetType());
286 struct materialsorter {
287 materialsorter(citem* Item) : Item(Item) {}
288 truth operator () (cmaterial *M1, cmaterial *M2) const { return (M1->GetHardenModifier(Item) > M2->GetHardenModifier(Item)); }
289 citem *Item;
293 truth god::TryToAttachBodyPart (character *Char) {
294 msgsystem::EnterBigMessageMode();
296 if (!Char->HasAllBodyParts()) {
297 bodypart *BodyPart = Char->FindRandomOwnBodyPart(true);
299 if (BodyPart && LikesMaterial(BodyPart->GetMainMaterial()->GetDataBase(), Char)) {
300 BodyPart->RemoveFromSlot();
301 Char->AttachBodyPart(BodyPart);
302 ADD_MESSAGE("%s attaches your old %s back.", GetName(), BodyPart->GetBodyPartName().CStr());
303 } else {
304 BodyPart = 0;
305 materialvector MaterialVector;
306 protosystem::CreateEveryMaterial(MaterialVector, this, Char);
307 std::sort(MaterialVector.begin(), MaterialVector.end(), materialsorter(0));
309 for (uInt c = 0; c < MaterialVector.size(); ++c) {
310 if (ForceGiveBodyPart() ||
311 ((MaterialVector[c]->GetCommonFlags()&CAN_BE_WISHED) &&
312 !RAND_N(6000/(GetRelation()+2000)) &&
313 !RAND_N(Max(MaterialVector[c]->GetIntelligenceRequirement()-10, 1))))
315 BodyPart = Char->GenerateRandomBodyPart();
316 BodyPart->ChangeMainMaterial(MaterialVector[c]->SpawnMore());
317 Char->UpdatePictures();
318 festring MadeOf;
320 if (!MaterialVector[c]->IsSameAs(Char->GetTorso()->GetMainMaterial())) {
321 MadeOf << " made of ";
322 MaterialVector[c]->AddName(MadeOf, false, false);
325 ADD_MESSAGE("%s gives you a new %s%s.", GetName(), BodyPart->GetBodyPartName().CStr(), MadeOf.CStr());
326 break;
330 for (uInt c = 0; c < MaterialVector.size(); ++c) delete MaterialVector[c];
333 if (BodyPart) {
334 if (MutatesBodyParts()) {
335 BodyPart->Mutate();
336 ADD_MESSAGE("It seems somehow different.");
339 truth Heal = !BodyPart->CanRegenerate() || HealRegeneratingBodyParts();
340 BodyPart->SetHP(Heal ? BodyPart->GetMaxHP() : 1);
341 msgsystem::LeaveBigMessageMode();
342 return true;
346 msgsystem::LeaveBigMessageMode();
347 return false;
351 truth god::TryToHardenBodyPart (character *Char) {
352 bodypart *PossibleBodyPart[MAX_BODYPARTS];
353 uInt Index = 0;
355 for (uInt c = 1; c < uInt(Char->GetBodyParts()); ++c) { // annoying :(
356 bodypart *BodyPart = Char->GetBodyPart(c);
357 if (BodyPart && LikesMaterial(BodyPart->GetMainMaterial()->GetDataBase(), Char)) {
358 PossibleBodyPart[Index++] = BodyPart;
362 if (Index == 0) return false;
364 bodypart *BodyPart = PossibleBodyPart[RAND_N(Index)];
365 material *OldMaterial = BodyPart->GetMainMaterial();
366 int OldModifier = OldMaterial->GetHardenModifier(BodyPart);
368 materialvector MaterialVector;
369 protosystem::CreateEveryMaterial(MaterialVector, this, Char);
370 std::sort(MaterialVector.begin(), MaterialVector.end(), materialsorter(BodyPart));
371 truth Changed = false;
373 for (uInt c = 0; c < MaterialVector.size(); ++c) {
374 if (MaterialVector[c]->GetCommonFlags()&CAN_BE_WISHED) {
375 material *Material = MaterialVector[c];
376 if (Material->GetHardenModifier(BodyPart) > OldModifier &&
377 !RAND_N(12000/(GetRelation()+2000)) &&
378 !RAND_N(Max(Material->GetIntelligenceRequirement()-15, 1)))
380 BodyPart->ChangeMainMaterial(Material->SpawnMore());
381 ADD_MESSAGE("%s changes your %s to %s.", GetName(), BodyPart->GetBodyPartName().CStr(), Material->GetName(false, false).CStr());
382 Changed = true;
383 break;
388 for (uInt c = 0; c < MaterialVector.size(); ++c) delete MaterialVector[c];
390 return Changed;
394 void god::SignalRandomAltarGeneration (const std::vector<v2> &RoomSquare) {
395 int Times = 1+femath::LoopRoll(50, 4);
397 for (int c = 0; c < Times; ++c) {
398 sLong Category = RAND()&ANY_CATEGORY;
399 if (!Category) Category = ANY_CATEGORY;
401 sLong MaxPrice = 250+femath::LoopRoll(95, 500)*10;
402 item *Item = protosystem::BalancedCreateItem(0, 0, MaxPrice, Category, 0, 0, GetType());
404 if (Item) {
405 Item->CalculateEnchantment();
406 game::GetCurrentLevel()->GetLSquare(RoomSquare[RAND_N(RoomSquare.size())])->AddItem(Item);
412 void god::Save (outputfile &SaveFile) const {
413 SaveFile << (uShort)GetType();
414 SaveFile << Relation << Timer << Known << LastPray;
418 void god::Load (inputfile &SaveFile) {
419 SaveFile >> Relation >> Timer >> Known >> LastPray;
423 void god::ApplyDivineTick () {
424 if (Timer > 0) --Timer;
425 if (LastPray > -1 && LastPray < 336000) ++LastPray;