1 /* Copyright (C) 2013-2014 Michal Brzozowski (rusolis@poczta.fm)
2 
3    This file is part of KeeperRL.
4 
5    KeeperRL is free software; you can redistribute it and/or modify it under the terms of the
6    GNU General Public License as published by the Free Software Foundation; either version 2
7    of the License, or (at your option) any later version.
8 
9    KeeperRL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10    even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License along with this program.
14    If not, see http://www.gnu.org/licenses/ . */
15 
16 #include "stdafx.h"
17 
18 #include "creature_attributes.h"
19 #include "creature.h"
20 #include "sound.h"
21 #include "player_message.h"
22 #include "item.h"
23 #include "item_factory.h"
24 #include "body.h"
25 #include "attack_level.h"
26 #include "attack_type.h"
27 #include "view_object.h"
28 #include "spell_map.h"
29 #include "effect.h"
30 #include "minion_trait.h"
31 
CreatureAttributes(function<void (CreatureAttributes &)> fun)32 CreatureAttributes::CreatureAttributes(function<void(CreatureAttributes&)> fun) {
33   fun(*this);
34   for (LastingEffect effect : ENUM_ALL(LastingEffect))
35     lastingEffects[effect] = -500;
36   for (auto effect : ENUM_ALL(LastingEffect))
37     if (body->isIntrinsicallyAffected(effect))
38       ++permanentEffects[effect];
39 }
40 
~CreatureAttributes()41 CreatureAttributes::~CreatureAttributes() {}
42 
43 template <class Archive>
serialize(Archive & ar,const unsigned int version)44 void CreatureAttributes::serialize(Archive& ar, const unsigned int version) {
45   ar(viewId, retiredViewId, illusionViewObject, spawnType, name, attr, chatReactionFriendly);
46   ar(chatReactionHostile, barehandedAttack, attackEffect, passiveAttack, gender);
47   ar(body, innocent, moraleSpeedIncrease);
48   ar(animal, cantEquip, courage);
49   ar(boulder, noChase, isSpecial, skills, spells);
50   ar(permanentEffects, lastingEffects, lastAffected, minionTasks, expLevel);
51   ar(noAttackSound, maxLevelIncrease, creatureId);
52 }
53 
54 SERIALIZABLE(CreatureAttributes);
55 
56 SERIALIZATION_CONSTRUCTOR_IMPL(CreatureAttributes);
57 
setCreatureId(CreatureId id)58 CreatureAttributes& CreatureAttributes::setCreatureId(CreatureId id) {
59   creatureId = id;
60   return *this;
61 }
62 
getCreatureId() const63 const optional<CreatureId>& CreatureAttributes::getCreatureId() const {
64   return creatureId;
65 }
66 
getName()67 CreatureName& CreatureAttributes::getName() {
68   return *name;
69 }
70 
getName() const71 const CreatureName& CreatureAttributes::getName() const {
72   return *name;
73 }
74 
setBaseAttr(AttrType type,int v)75 void CreatureAttributes::setBaseAttr(AttrType type, int v) {
76   attr[type] = v;
77 }
78 
getCourage() const79 double CreatureAttributes::getCourage() const {
80   if (!body->hasBrain())
81     return 1;
82   return courage;
83 }
84 
setCourage(double c)85 void CreatureAttributes::setCourage(double c) {
86   courage = c;
87 }
88 
getGender() const89 const Gender& CreatureAttributes::getGender() const {
90   return gender;
91 }
92 
getRawAttr(AttrType type) const93 double CreatureAttributes::getRawAttr(AttrType type) const {
94   double ret = attr[type];
95   if (auto expType = getExperienceType(type))
96     ret += expLevel[*expType];
97   return ret;
98 }
99 
getExpLevel(ExperienceType type) const100 double CreatureAttributes::getExpLevel(ExperienceType type) const {
101   return expLevel[type];
102 }
103 
getExpLevel() const104 const EnumMap<ExperienceType, double>& CreatureAttributes::getExpLevel() const {
105   return expLevel;
106 }
107 
getMaxExpLevel() const108 const EnumMap<ExperienceType, int>& CreatureAttributes::getMaxExpLevel() const {
109   return maxLevelIncrease;
110 }
111 
increaseExpLevel(ExperienceType type,double increase)112 void CreatureAttributes::increaseExpLevel(ExperienceType type, double increase) {
113   increase = max(0.0, min(increase, (double) maxLevelIncrease[type] - expLevel[type]));
114   expLevel[type] += increase;
115 }
116 
isTrainingMaxedOut(ExperienceType type) const117 bool CreatureAttributes::isTrainingMaxedOut(ExperienceType type) const {
118   return getExpLevel(type) >= maxLevelIncrease[type];
119 }
120 
increaseBaseExpLevel(ExperienceType type,double increase)121 void CreatureAttributes::increaseBaseExpLevel(ExperienceType type, double increase) {
122   for (auto attrType : getAttrIncreases()[type])
123     attr[attrType] += increase;
124 }
125 
getSpellMap()126 SpellMap& CreatureAttributes::getSpellMap() {
127   return *spells;
128 }
129 
getBody()130 Body& CreatureAttributes::getBody() {
131   return *body;
132 }
133 
getBody() const134 const Body& CreatureAttributes::getBody() const {
135   return *body;
136 }
137 
getSpellMap() const138 const SpellMap& CreatureAttributes::getSpellMap() const {
139   return *spells;
140 }
141 
getAttackSound(AttackType type,bool damage) const142 optional<SoundId> CreatureAttributes::getAttackSound(AttackType type, bool damage) const {
143   if (!noAttackSound)
144     switch (type) {
145       case AttackType::HIT:
146       case AttackType::PUNCH:
147       case AttackType::CRUSH: return damage ? SoundId::BLUNT_DAMAGE : SoundId::BLUNT_NO_DAMAGE;
148       case AttackType::CUT:
149       case AttackType::STAB: return damage ? SoundId::BLADE_DAMAGE : SoundId::BLADE_NO_DAMAGE;
150       default: return none;
151     }
152   else
153     return none;
154 }
155 
getDescription() const156 string CreatureAttributes::getDescription() const {
157   if (!isSpecial)
158     return "";
159   string attack;
160   if (attackEffect)
161     attack = " It has a " + attackEffect->getName() + " attack.";
162   return body->getDescription() + ". " + attack;
163 }
164 
chatReaction(WCreature me,WCreature other)165 void CreatureAttributes::chatReaction(WCreature me, WCreature other) {
166   if (me->isEnemy(other) && chatReactionHostile) {
167     if (chatReactionHostile->front() == '\"')
168       other->privateMessage(*chatReactionHostile);
169     else
170       other->privateMessage(me->getName().the() + " " + *chatReactionHostile);
171   }
172   if (!me->isEnemy(other) && chatReactionFriendly) {
173     if (chatReactionFriendly->front() == '\"')
174       other->privateMessage(*chatReactionFriendly);
175     else
176       other->privateMessage(me->getName().the() + " " + *chatReactionFriendly);
177   }
178 }
179 
isAffected(LastingEffect effect,double time) const180 bool CreatureAttributes::isAffected(LastingEffect effect, double time) const {
181   if (auto suppressor = LastingEffects::getSuppressor(effect))
182     if (isAffected(*suppressor, time))
183       return false;
184   return lastingEffects[effect] >= time || isAffectedPermanently(effect);
185 }
186 
getTimeOut(LastingEffect effect) const187 double CreatureAttributes::getTimeOut(LastingEffect effect) const {
188   return lastingEffects[effect];
189 }
190 
considerTimeout(LastingEffect effect,double globalTime)191 bool CreatureAttributes::considerTimeout(LastingEffect effect, double globalTime) {
192   if (lastingEffects[effect] > 0 && lastingEffects[effect] < globalTime) {
193     clearLastingEffect(effect, globalTime);
194     if (!isAffected(effect, globalTime))
195       return true;
196   }
197   return false;
198 }
199 
addLastingEffect(LastingEffect effect,double endTime)200 void CreatureAttributes::addLastingEffect(LastingEffect effect, double endTime) {
201   if (lastingEffects[effect] < endTime)
202     lastingEffects[effect] = endTime;
203 }
204 
getLastAffected(LastingEffect effect,double currentGlobalTime) const205 optional<double> CreatureAttributes::getLastAffected(LastingEffect effect, double currentGlobalTime) const {
206   if (isAffected(effect, currentGlobalTime))
207     return currentGlobalTime;
208   else
209     return lastAffected[effect];
210 }
211 
consumeProb()212 static bool consumeProb() {
213   return true;
214 }
215 
getAttrNameMore(AttrType attr)216 static string getAttrNameMore(AttrType attr) {
217   switch (attr) {
218     case AttrType::DAMAGE: return "more dangerous";
219     case AttrType::DEFENSE: return "more protected";
220     case AttrType::SPELL_DAMAGE: return "more powerful";
221     case AttrType::RANGED_DAMAGE: return "more accurate";
222     case AttrType::SPEED: return "faster";
223   }
224 }
225 
226 template <typename T>
consumeAttr(T & mine,const T & his,vector<string> & adjectives,const string & adj)227 void consumeAttr(T& mine, const T& his, vector<string>& adjectives, const string& adj) {
228   if (consumeProb() && mine < his) {
229     mine = his;
230     if (!adj.empty())
231       adjectives.push_back(adj);
232   }
233 }
234 
consumeAttr(Gender & mine,const Gender & his,vector<string> & adjectives)235 void consumeAttr(Gender& mine, const Gender& his, vector<string>& adjectives) {
236   if (consumeProb() && mine != his) {
237     mine = his;
238     adjectives.emplace_back(mine == Gender::male ? "more masculine" : "more feminine");
239   }
240 }
241 
242 
243 template <typename T>
consumeAttr(optional<T> & mine,const optional<T> & his,vector<string> & adjectives,const string & adj)244 void consumeAttr(optional<T>& mine, const optional<T>& his, vector<string>& adjectives, const string& adj) {
245   if (consumeProb() && !mine && his) {
246     mine = *his;
247     if (!adj.empty())
248       adjectives.push_back(adj);
249   }
250 }
251 
consumeAttr(Skillset & mine,const Skillset & his,vector<string> & adjectives)252 void consumeAttr(Skillset& mine, const Skillset& his, vector<string>& adjectives) {
253   bool was = false;
254   for (SkillId id : his.getAllDiscrete())
255     if (!mine.hasDiscrete(id) && Skill::get(id)->transferOnConsumption() && consumeProb()) {
256       mine.insert(id);
257       was = true;
258     }
259   for (SkillId id : ENUM_ALL(SkillId)) {
260     if (!Skill::get(id)->isDiscrete() && mine.getValue(id) < his.getValue(id)) {
261       mine.setValue(id, his.getValue(id));
262       was = true;
263     }
264   }
265   if (was)
266     adjectives.push_back("more skillfull");
267 }
268 
consumeEffects(const EnumMap<LastingEffect,int> & permanentEffects)269 void CreatureAttributes::consumeEffects(const EnumMap<LastingEffect, int>& permanentEffects) {
270   for (LastingEffect effect : ENUM_ALL(LastingEffect))
271     if (permanentEffects[effect] > 0 && !isAffectedPermanently(effect) && consumeProb()) {
272       addPermanentEffect(effect, 1);
273     }
274 }
275 
consume(WCreature self,const CreatureAttributes & other)276 void CreatureAttributes::consume(WCreature self, const CreatureAttributes& other) {
277   INFO << name->bare() << " consume " << other.name->bare();
278   self->you(MsgType::CONSUME, other.name->the());
279   self->addPersonalEvent(self->getName().a() + " absorbs " + other.name->a());
280   vector<string> adjectives;
281   body->consumeBodyParts(self, other.getBody(), adjectives);
282   for (auto t : ENUM_ALL(AttrType))
283     consumeAttr(attr[t], other.attr[t], adjectives, getAttrNameMore(t));
284   consumeAttr(barehandedAttack, other.barehandedAttack, adjectives, "");
285   consumeAttr(*attackEffect, *other.attackEffect, adjectives, "");
286   consumeAttr(*passiveAttack, *other.passiveAttack, adjectives, "");
287   consumeAttr(gender, other.gender, adjectives);
288   consumeAttr(skills, other.skills, adjectives);
289   if (!adjectives.empty()) {
290     self->you(MsgType::BECOME, combine(adjectives));
291     self->addPersonalEvent(getName().the() + " becomes " + combine(adjectives));
292   }
293   consumeEffects(other.permanentEffects);
294 }
295 
getAttackType(WConstItem weapon) const296 AttackType CreatureAttributes::getAttackType(WConstItem weapon) const {
297   if (weapon)
298     return weapon->getAttackType();
299   else if (barehandedAttack)
300     return *barehandedAttack;
301   else
302     return body->isHumanoid() ? AttackType::PUNCH : AttackType::BITE;
303 }
304 
getRemainingString(LastingEffect effect,double time) const305 string CreatureAttributes::getRemainingString(LastingEffect effect, double time) const {
306   return "[" + toString<int>(lastingEffects[effect] - time) + "]";
307 }
308 
isBoulder() const309 bool CreatureAttributes::isBoulder() const {
310   return boulder;
311 }
312 
getSkills()313 Skillset& CreatureAttributes::getSkills() {
314   return skills;
315 }
316 
getSkills() const317 const Skillset& CreatureAttributes::getSkills() const {
318   return skills;
319 }
320 
createViewObject() const321 ViewObject CreatureAttributes::createViewObject() const {
322   return ViewObject(*viewId, ViewLayer::CREATURE, name->bare());
323 }
324 
getIllusionViewObject() const325 const optional<ViewObject>& CreatureAttributes::getIllusionViewObject() const {
326   return *illusionViewObject;
327 }
328 
canEquip() const329 bool CreatureAttributes::canEquip() const {
330   return !cantEquip;
331 }
332 
isAffectedPermanently(LastingEffect effect) const333 bool CreatureAttributes::isAffectedPermanently(LastingEffect effect) const {
334   return permanentEffects[effect] > 0;
335 }
336 
shortenEffect(LastingEffect effect,double time)337 void CreatureAttributes::shortenEffect(LastingEffect effect, double time) {
338   CHECK(lastingEffects[effect] >= time);
339   lastingEffects[effect] -= time;
340 }
341 
clearLastingEffect(LastingEffect effect,double globalTime)342 void CreatureAttributes::clearLastingEffect(LastingEffect effect, double globalTime) {
343   lastingEffects[effect] = 0;
344   lastAffected[effect] = globalTime;
345 }
346 
addPermanentEffect(LastingEffect effect,int count)347 void CreatureAttributes::addPermanentEffect(LastingEffect effect, int count) {
348   permanentEffects[effect] += count;
349 }
350 
removePermanentEffect(LastingEffect effect,int count)351 void CreatureAttributes::removePermanentEffect(LastingEffect effect, int count) {
352   permanentEffects[effect] -= count;
353 }
354 
getAttackEffect() const355 optional<Effect> CreatureAttributes::getAttackEffect() const {
356   return *attackEffect;
357 }
358 
isInnocent() const359 bool CreatureAttributes::isInnocent() const {
360   return innocent;
361 }
362 
getSpawnType() const363 optional<SpawnType> CreatureAttributes::getSpawnType() const {
364   return spawnType;
365 }
366 
getMinionTasks() const367 const MinionTaskMap& CreatureAttributes::getMinionTasks() const {
368   return minionTasks;
369 }
370 
getMinionTasks()371 MinionTaskMap& CreatureAttributes::getMinionTasks() {
372   return minionTasks;
373 }
374 
dontChase() const375 bool CreatureAttributes::dontChase() const {
376   return noChase;
377 }
378 
getRetiredViewId()379 optional<ViewId> CreatureAttributes::getRetiredViewId() {
380   return retiredViewId;
381 }
382 
getMoraleSpeedIncrease() const383 optional<double> CreatureAttributes::getMoraleSpeedIncrease() const {
384   return moraleSpeedIncrease;
385 }
386