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