1/**
2 * The Forgotten Server - a free and open-source MMORPG server emulator
3 * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "otpch.h"
21
22#include "condition.h"
23#include "game.h"
24
25extern Game g_game;
26
27bool Condition::setParam(ConditionParam_t param, int32_t value)
28{
29 switch (param) {
30 case CONDITION_PARAM_TICKS: {
31 ticks = value;
32 return true;
33 }
34
35 case CONDITION_PARAM_BUFF_SPELL: {
36 isBuff = (value != 0);
37 return true;
38 }
39
40 case CONDITION_PARAM_SUBID: {
41 subId = value;
42 return true;
43 }
44
45 case CONDITION_PARAM_AGGRESSIVE: {
46 aggressive = (value != 0);
47 return true;
48 }
49
50 default: {
51 return false;
52 }
53 }
54}
55
56bool Condition::unserialize(PropStream& propStream)
57{
58 uint8_t attr_type;
59 while (propStream.read<uint8_t>(attr_type) && attr_type != CONDITIONATTR_END) {
60 if (!unserializeProp(static_cast<ConditionAttr_t>(attr_type), propStream)) {
61 return false;
62 }
63 }
64 return true;
65}
66
67bool Condition::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
68{
69 switch (attr) {
70 case CONDITIONATTR_TYPE: {
71 int32_t value;
72 if (!propStream.read<int32_t>(value)) {
73 return false;
74 }
75
76 conditionType = static_cast<ConditionType_t>(value);
77 return true;
78 }
79
80 case CONDITIONATTR_ID: {
81 int32_t value;
82 if (!propStream.read<int32_t>(value)) {
83 return false;
84 }
85
86 id = static_cast<ConditionId_t>(value);
87 return true;
88 }
89
90 case CONDITIONATTR_TICKS: {
91 return propStream.read<int32_t>(ticks);
92 }
93
94 case CONDITIONATTR_ISBUFF: {
95 uint8_t value;
96 if (!propStream.read<uint8_t>(value)) {
97 return false;
98 }
99
100 isBuff = (value != 0);
101 return true;
102 }
103
104 case CONDITIONATTR_SUBID: {
105 return propStream.read<uint32_t>(subId);
106 }
107
108 case CONDITIONATTR_ISAGGRESSIVE: {
109 uint8_t value;
110 if (!propStream.read<uint8_t>(value)) {
111 return false;
112 }
113
114 aggressive = (value != 0);
115 return true;
116 }
117
118 case CONDITIONATTR_END:
119 return true;
120
121 default:
122 return false;
123 }
124}
125
126void Condition::serialize(PropWriteStream& propWriteStream)
127{
128 propWriteStream.write<uint8_t>(CONDITIONATTR_TYPE);
129 propWriteStream.write<uint32_t>(conditionType);
130
131 propWriteStream.write<uint8_t>(CONDITIONATTR_ID);
132 propWriteStream.write<uint32_t>(id);
133
134 propWriteStream.write<uint8_t>(CONDITIONATTR_TICKS);
135 propWriteStream.write<uint32_t>(ticks);
136
137 propWriteStream.write<uint8_t>(CONDITIONATTR_ISBUFF);
138 propWriteStream.write<uint8_t>(isBuff);
139
140 propWriteStream.write<uint8_t>(CONDITIONATTR_SUBID);
141 propWriteStream.write<uint32_t>(subId);
142
143 propWriteStream.write<uint8_t>(CONDITIONATTR_ISAGGRESSIVE);
144 propWriteStream.write<uint8_t>(aggressive);
145}
146
147void Condition::setTicks(int32_t newTicks)
148{
149 ticks = newTicks;
150 endTime = ticks + OTSYS_TIME();
151}
152
153bool Condition::executeCondition(Creature*, int32_t interval)
154{
155 if (ticks == -1) {
156 return true;
157 }
158
159 //Not using set ticks here since it would reset endTime
160 ticks = std::max<int32_t>(0, ticks - interval);
161 return getEndTime() >= OTSYS_TIME();
162}
163
164Condition* Condition::createCondition(ConditionId_t id, ConditionType_t type, int32_t ticks, int32_t param/* = 0*/, bool buff/* = false*/, uint32_t subId/* = 0*/, bool aggressive/* = false */)
165{
166 switch (type) {
167 case CONDITION_POISON:
168 case CONDITION_FIRE:
169 case CONDITION_ENERGY:
170 case CONDITION_DROWN:
171 case CONDITION_FREEZING:
172 case CONDITION_DAZZLED:
173 case CONDITION_CURSED:
174 case CONDITION_BLEEDING:
175 return new ConditionDamage(id, type, buff, subId, aggressive);
176
177 case CONDITION_HASTE:
178 case CONDITION_PARALYZE:
179 return new ConditionSpeed(id, type, ticks, buff, subId, param, aggressive);
180
181 case CONDITION_INVISIBLE:
182 return new ConditionInvisible(id, type, ticks, buff, subId, aggressive);
183
184 case CONDITION_OUTFIT:
185 return new ConditionOutfit(id, type, ticks, buff, subId, aggressive);
186
187 case CONDITION_LIGHT:
188 return new ConditionLight(id, type, ticks, buff, subId, param & 0xFF, (param & 0xFF00) >> 8, aggressive);
189
190 case CONDITION_REGENERATION:
191 return new ConditionRegeneration(id, type, ticks, buff, subId, aggressive);
192
193 case CONDITION_SOUL:
194 return new ConditionSoul(id, type, ticks, buff, subId, aggressive);
195
196 case CONDITION_ATTRIBUTES:
197 return new ConditionAttributes(id, type, ticks, buff, subId, aggressive);
198
199 case CONDITION_SPELLCOOLDOWN:
200 return new ConditionSpellCooldown(id, type, ticks, buff, subId, aggressive);
201
202 case CONDITION_SPELLGROUPCOOLDOWN:
203 return new ConditionSpellGroupCooldown(id, type, ticks, buff, subId, aggressive);
204
205 case CONDITION_INFIGHT:
206 case CONDITION_DRUNK:
207 case CONDITION_EXHAUST_WEAPON:
208 case CONDITION_EXHAUST_COMBAT:
209 case CONDITION_EXHAUST_HEAL:
210 case CONDITION_MUTED:
211 case CONDITION_CHANNELMUTEDTICKS:
212 case CONDITION_YELLTICKS:
213 case CONDITION_PACIFIED:
214 case CONDITION_MANASHIELD:
215 return new ConditionGeneric(id, type, ticks, buff, subId, aggressive);
216
217 default:
218 return nullptr;
219 }
220}
221
222Condition* Condition::createCondition(PropStream& propStream)
223{
224 uint8_t attr;
225 if (!propStream.read<uint8_t>(attr) || attr != CONDITIONATTR_TYPE) {
226 return nullptr;
227 }
228
229 uint32_t type;
230 if (!propStream.read<uint32_t>(type)) {
231 return nullptr;
232 }
233
234 if (!propStream.read<uint8_t>(attr) || attr != CONDITIONATTR_ID) {
235 return nullptr;
236 }
237
238 uint32_t id;
239 if (!propStream.read<uint32_t>(id)) {
240 return nullptr;
241 }
242
243 if (!propStream.read<uint8_t>(attr) || attr != CONDITIONATTR_TICKS) {
244 return nullptr;
245 }
246
247 uint32_t ticks;
248 if (!propStream.read<uint32_t>(ticks)) {
249 return nullptr;
250 }
251
252 if (!propStream.read<uint8_t>(attr) || attr != CONDITIONATTR_ISBUFF) {
253 return nullptr;
254 }
255
256 uint8_t buff;
257 if (!propStream.read<uint8_t>(buff)) {
258 return nullptr;
259 }
260
261 if (!propStream.read<uint8_t>(attr) || attr != CONDITIONATTR_SUBID) {
262 return nullptr;
263 }
264
265 uint32_t subId;
266 if (!propStream.read<uint32_t>(subId)) {
267 return nullptr;
268 }
269
270 if (!propStream.read<uint8_t>(attr) || attr != CONDITIONATTR_ISAGGRESSIVE) {
271 return nullptr;
272 }
273
274 uint8_t aggressive;
275 if (!propStream.read<uint8_t>(aggressive)) {
276 return nullptr;
277 }
278
279 return createCondition(static_cast<ConditionId_t>(id), static_cast<ConditionType_t>(type), ticks, 0, buff != 0, subId, aggressive);
280}
281
282bool Condition::startCondition(Creature*)
283{
284 if (ticks > 0) {
285 endTime = ticks + OTSYS_TIME();
286 }
287 return true;
288}
289
290bool Condition::isPersistent() const
291{
292 if (ticks == -1) {
293 return false;
294 }
295
296 if (!(id == CONDITIONID_DEFAULT || id == CONDITIONID_COMBAT || conditionType == CONDITION_MUTED)) {
297 return false;
298 }
299
300 return true;
301}
302
303uint32_t Condition::getIcons() const
304{
305 return isBuff ? ICON_PARTY_BUFF : 0;
306}
307
308bool Condition::updateCondition(const Condition* addCondition)
309{
310 if (conditionType != addCondition->getType()) {
311 return false;
312 }
313
314 if (ticks == -1 && addCondition->getTicks() > 0) {
315 return false;
316 }
317
318 if (addCondition->getTicks() >= 0 && getEndTime() > (OTSYS_TIME() + addCondition->getTicks())) {
319 return false;
320 }
321
322 return true;
323}
324
325bool ConditionGeneric::startCondition(Creature* creature)
326{
327 return Condition::startCondition(creature);
328}
329
330bool ConditionGeneric::executeCondition(Creature* creature, int32_t interval)
331{
332 return Condition::executeCondition(creature, interval);
333}
334
335void ConditionGeneric::endCondition(Creature*)
336{
337 //
338}
339
340void ConditionGeneric::addCondition(Creature*, const Condition* condition)
341{
342 if (updateCondition(condition)) {
343 setTicks(condition->getTicks());
344 }
345}
346
347uint32_t ConditionGeneric::getIcons() const
348{
349 uint32_t icons = Condition::getIcons();
350
351 switch (conditionType) {
352 case CONDITION_MANASHIELD:
353 icons |= ICON_MANASHIELD;
354 break;
355
356 case CONDITION_INFIGHT:
357 icons |= ICON_SWORDS;
358 break;
359
360 case CONDITION_DRUNK:
361 icons |= ICON_DRUNK;
362 break;
363
364 default:
365 break;
366 }
367
368 return icons;
369}
370
371void ConditionAttributes::addCondition(Creature* creature, const Condition* condition)
372{
373 if (updateCondition(condition)) {
374 setTicks(condition->getTicks());
375
376 const ConditionAttributes& conditionAttrs = static_cast<const ConditionAttributes&>(*condition);
377 //Remove the old condition
378 endCondition(creature);
379
380 //Apply the new one
381 memcpy(skills, conditionAttrs.skills, sizeof(skills));
382 memcpy(specialSkills, conditionAttrs.specialSkills, sizeof(specialSkills));
383 memcpy(skillsPercent, conditionAttrs.skillsPercent, sizeof(skillsPercent));
384 memcpy(stats, conditionAttrs.stats, sizeof(stats));
385 memcpy(statsPercent, conditionAttrs.statsPercent, sizeof(statsPercent));
386 disableDefense = conditionAttrs.disableDefense;
387
388 if (Player* player = creature->getPlayer()) {
389 updatePercentSkills(player);
390 updateSkills(player);
391 updatePercentStats(player);
392 updateStats(player);
393 }
394 }
395}
396
397bool ConditionAttributes::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
398{
399 if (attr == CONDITIONATTR_SKILLS) {
400 return propStream.read<int32_t>(skills[currentSkill++]);
401 } else if (attr == CONDITIONATTR_STATS) {
402 return propStream.read<int32_t>(stats[currentStat++]);
403 } else if (attr == CONDITIONATTR_DISABLEDEFENSE) {
404 return propStream.read<bool>(disableDefense);
405 }
406 return Condition::unserializeProp(attr, propStream);
407}
408
409void ConditionAttributes::serialize(PropWriteStream& propWriteStream)
410{
411 Condition::serialize(propWriteStream);
412
413 for (int32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) {
414 propWriteStream.write<uint8_t>(CONDITIONATTR_SKILLS);
415 propWriteStream.write<int32_t>(skills[i]);
416 }
417
418 for (int32_t i = STAT_FIRST; i <= STAT_LAST; ++i) {
419 propWriteStream.write<uint8_t>(CONDITIONATTR_STATS);
420 propWriteStream.write<int32_t>(stats[i]);
421 }
422
423 propWriteStream.write<uint8_t>(CONDITIONATTR_DISABLEDEFENSE);
424 propWriteStream.write<bool>(disableDefense);
425}
426
427bool ConditionAttributes::startCondition(Creature* creature)
428{
429 if (!Condition::startCondition(creature)) {
430 return false;
431 }
432
433 creature->setUseDefense(!disableDefense);
434
435 if (Player* player = creature->getPlayer()) {
436 updatePercentSkills(player);
437 updateSkills(player);
438 updatePercentStats(player);
439 updateStats(player);
440 }
441
442 return true;
443}
444
445void ConditionAttributes::updatePercentStats(Player* player)
446{
447 for (int32_t i = STAT_FIRST; i <= STAT_LAST; ++i) {
448 if (statsPercent[i] == 0) {
449 continue;
450 }
451
452 switch (i) {
453 case STAT_MAXHITPOINTS:
454 stats[i] = static_cast<int32_t>(player->getMaxHealth() * ((statsPercent[i] - 100) / 100.f));
455 break;
456
457 case STAT_MAXMANAPOINTS:
458 stats[i] = static_cast<int32_t>(player->getMaxMana() * ((statsPercent[i] - 100) / 100.f));
459 break;
460
461 case STAT_MAGICPOINTS:
462 stats[i] = static_cast<int32_t>(player->getBaseMagicLevel() * ((statsPercent[i] - 100) / 100.f));
463 break;
464 }
465 }
466}
467
468void ConditionAttributes::updateStats(Player* player)
469{
470 bool needUpdateStats = false;
471
472 for (int32_t i = STAT_FIRST; i <= STAT_LAST; ++i) {
473 if (stats[i]) {
474 needUpdateStats = true;
475 player->setVarStats(static_cast<stats_t>(i), stats[i]);
476 }
477 }
478
479 if (needUpdateStats) {
480 player->sendStats();
481 }
482}
483
484void ConditionAttributes::updatePercentSkills(Player* player)
485{
486 for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) {
487 if (skillsPercent[i] == 0) {
488 continue;
489 }
490
491 int32_t unmodifiedSkill = player->getBaseSkill(i);
492 skills[i] = static_cast<int32_t>(unmodifiedSkill * ((skillsPercent[i] - 100) / 100.f));
493 }
494}
495
496void ConditionAttributes::updateSkills(Player* player)
497{
498 bool needUpdateSkills = false;
499
500 for (int32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) {
501 if (skills[i]) {
502 needUpdateSkills = true;
503 player->setVarSkill(static_cast<skills_t>(i), skills[i]);
504 }
505 }
506
507 for (int32_t i = SPECIALSKILL_FIRST; i <= SPECIALSKILL_LAST; ++i) {
508 if (specialSkills[i]) {
509 needUpdateSkills = true;
510 player->setVarSpecialSkill(static_cast<SpecialSkills_t>(i), specialSkills[i]);
511 }
512 }
513
514 if (needUpdateSkills) {
515 player->sendSkills();
516 }
517}
518
519bool ConditionAttributes::executeCondition(Creature* creature, int32_t interval)
520{
521 return ConditionGeneric::executeCondition(creature, interval);
522}
523
524void ConditionAttributes::endCondition(Creature* creature)
525{
526 Player* player = creature->getPlayer();
527 if (player) {
528 bool needUpdateSkills = false;
529
530 for (int32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) {
531 if (skills[i] || skillsPercent[i]) {
532 needUpdateSkills = true;
533 player->setVarSkill(static_cast<skills_t>(i), -skills[i]);
534 }
535 }
536
537 for (int32_t i = SPECIALSKILL_FIRST; i <= SPECIALSKILL_LAST; ++i) {
538 if (specialSkills[i]) {
539 needUpdateSkills = true;
540 player->setVarSpecialSkill(static_cast<SpecialSkills_t>(i), -specialSkills[i]);
541 }
542 }
543
544 if (needUpdateSkills) {
545 player->sendSkills();
546 }
547
548 bool needUpdateStats = false;
549
550 for (int32_t i = STAT_FIRST; i <= STAT_LAST; ++i) {
551 if (stats[i]) {
552 needUpdateStats = true;
553 player->setVarStats(static_cast<stats_t>(i), -stats[i]);
554 }
555 }
556
557 if (needUpdateStats) {
558 player->sendStats();
559 }
560 }
561
562 if (disableDefense) {
563 creature->setUseDefense(true);
564 }
565}
566
567bool ConditionAttributes::setParam(ConditionParam_t param, int32_t value)
568{
569 bool ret = ConditionGeneric::setParam(param, value);
570
571 switch (param) {
572 case CONDITION_PARAM_SKILL_MELEE: {
573 skills[SKILL_CLUB] = value;
574 skills[SKILL_AXE] = value;
575 skills[SKILL_SWORD] = value;
576 return true;
577 }
578
579 case CONDITION_PARAM_SKILL_MELEEPERCENT: {
580 skillsPercent[SKILL_CLUB] = value;
581 skillsPercent[SKILL_AXE] = value;
582 skillsPercent[SKILL_SWORD] = value;
583 return true;
584 }
585
586 case CONDITION_PARAM_SKILL_FIST: {
587 skills[SKILL_FIST] = value;
588 return true;
589 }
590
591 case CONDITION_PARAM_SKILL_FISTPERCENT: {
592 skillsPercent[SKILL_FIST] = value;
593 return true;
594 }
595
596 case CONDITION_PARAM_SKILL_CLUB: {
597 skills[SKILL_CLUB] = value;
598 return true;
599 }
600
601 case CONDITION_PARAM_SKILL_CLUBPERCENT: {
602 skillsPercent[SKILL_CLUB] = value;
603 return true;
604 }
605
606 case CONDITION_PARAM_SKILL_SWORD: {
607 skills[SKILL_SWORD] = value;
608 return true;
609 }
610
611 case CONDITION_PARAM_SKILL_SWORDPERCENT: {
612 skillsPercent[SKILL_SWORD] = value;
613 return true;
614 }
615
616 case CONDITION_PARAM_SKILL_AXE: {
617 skills[SKILL_AXE] = value;
618 return true;
619 }
620
621 case CONDITION_PARAM_SKILL_AXEPERCENT: {
622 skillsPercent[SKILL_AXE] = value;
623 return true;
624 }
625
626 case CONDITION_PARAM_SKILL_DISTANCE: {
627 skills[SKILL_DISTANCE] = value;
628 return true;
629 }
630
631 case CONDITION_PARAM_SKILL_DISTANCEPERCENT: {
632 skillsPercent[SKILL_DISTANCE] = value;
633 return true;
634 }
635
636 case CONDITION_PARAM_SKILL_SHIELD: {
637 skills[SKILL_SHIELD] = value;
638 return true;
639 }
640
641 case CONDITION_PARAM_SKILL_SHIELDPERCENT: {
642 skillsPercent[SKILL_SHIELD] = value;
643 return true;
644 }
645
646 case CONDITION_PARAM_SKILL_FISHING: {
647 skills[SKILL_FISHING] = value;
648 return true;
649 }
650
651 case CONDITION_PARAM_SKILL_FISHINGPERCENT: {
652 skillsPercent[SKILL_FISHING] = value;
653 return true;
654 }
655
656 case CONDITION_PARAM_STAT_MAXHITPOINTS: {
657 stats[STAT_MAXHITPOINTS] = value;
658 return true;
659 }
660
661 case CONDITION_PARAM_STAT_MAXMANAPOINTS: {
662 stats[STAT_MAXMANAPOINTS] = value;
663 return true;
664 }
665
666 case CONDITION_PARAM_STAT_MAGICPOINTS: {
667 stats[STAT_MAGICPOINTS] = value;
668 return true;
669 }
670
671 case CONDITION_PARAM_STAT_MAXHITPOINTSPERCENT: {
672 statsPercent[STAT_MAXHITPOINTS] = std::max<int32_t>(0, value);
673 return true;
674 }
675
676 case CONDITION_PARAM_STAT_MAXMANAPOINTSPERCENT: {
677 statsPercent[STAT_MAXMANAPOINTS] = std::max<int32_t>(0, value);
678 return true;
679 }
680
681 case CONDITION_PARAM_STAT_MAGICPOINTSPERCENT: {
682 statsPercent[STAT_MAGICPOINTS] = std::max<int32_t>(0, value);
683 return true;
684 }
685
686 case CONDITION_PARAM_DISABLE_DEFENSE: {
687 disableDefense = (value != 0);
688 return true;
689 }
690
691 case CONDITION_PARAM_SPECIALSKILL_CRITICALHITCHANCE: {
692 specialSkills[SPECIALSKILL_CRITICALHITCHANCE] = value;
693 return true;
694 }
695
696 case CONDITION_PARAM_SPECIALSKILL_CRITICALHITAMOUNT: {
697 specialSkills[SPECIALSKILL_CRITICALHITAMOUNT] = value;
698 return true;
699 }
700
701 case CONDITION_PARAM_SPECIALSKILL_LIFELEECHCHANCE: {
702 specialSkills[SPECIALSKILL_LIFELEECHCHANCE] = value;
703 return true;
704 }
705
706 case CONDITION_PARAM_SPECIALSKILL_LIFELEECHAMOUNT: {
707 specialSkills[SPECIALSKILL_LIFELEECHAMOUNT] = value;
708 return true;
709 }
710
711 case CONDITION_PARAM_SPECIALSKILL_MANALEECHCHANCE: {
712 specialSkills[SPECIALSKILL_MANALEECHCHANCE] = value;
713 return true;
714 }
715
716 case CONDITION_PARAM_SPECIALSKILL_MANALEECHAMOUNT: {
717 specialSkills[SPECIALSKILL_MANALEECHAMOUNT] = value;
718 return true;
719 }
720
721 case CONDITION_PARAM_AGGRESSIVE: {
722 aggressive = (value != 0);
723 return true;
724 }
725
726 default:
727 return ret;
728 }
729}
730
731void ConditionRegeneration::addCondition(Creature*, const Condition* condition)
732{
733 if (updateCondition(condition)) {
734 setTicks(condition->getTicks());
735
736 const ConditionRegeneration& conditionRegen = static_cast<const ConditionRegeneration&>(*condition);
737
738 healthTicks = conditionRegen.healthTicks;
739 manaTicks = conditionRegen.manaTicks;
740
741 healthGain = conditionRegen.healthGain;
742 manaGain = conditionRegen.manaGain;
743 }
744}
745
746bool ConditionRegeneration::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
747{
748 if (attr == CONDITIONATTR_HEALTHTICKS) {
749 return propStream.read<uint32_t>(healthTicks);
750 } else if (attr == CONDITIONATTR_HEALTHGAIN) {
751 return propStream.read<uint32_t>(healthGain);
752 } else if (attr == CONDITIONATTR_MANATICKS) {
753 return propStream.read<uint32_t>(manaTicks);
754 } else if (attr == CONDITIONATTR_MANAGAIN) {
755 return propStream.read<uint32_t>(manaGain);
756 }
757 return Condition::unserializeProp(attr, propStream);
758}
759
760void ConditionRegeneration::serialize(PropWriteStream& propWriteStream)
761{
762 Condition::serialize(propWriteStream);
763
764 propWriteStream.write<uint8_t>(CONDITIONATTR_HEALTHTICKS);
765 propWriteStream.write<uint32_t>(healthTicks);
766
767 propWriteStream.write<uint8_t>(CONDITIONATTR_HEALTHGAIN);
768 propWriteStream.write<uint32_t>(healthGain);
769
770 propWriteStream.write<uint8_t>(CONDITIONATTR_MANATICKS);
771 propWriteStream.write<uint32_t>(manaTicks);
772
773 propWriteStream.write<uint8_t>(CONDITIONATTR_MANAGAIN);
774 propWriteStream.write<uint32_t>(manaGain);
775}
776
777bool ConditionRegeneration::executeCondition(Creature* creature, int32_t interval)
778{
779 internalHealthTicks += interval;
780 internalManaTicks += interval;
781
782 if (creature->getZone() == ZONE_PROTECTION) {
783 return ConditionGeneric::executeCondition(creature, interval);
784 }
785
786 if (internalHealthTicks >= healthTicks) {
787 internalHealthTicks = 0;
788
789 int32_t realHealthGain = creature->getHealth();
790 creature->changeHealth(healthGain);
791 realHealthGain = creature->getHealth() - realHealthGain;
792
793 if (isBuff && realHealthGain > 0) {
794 Player* player = creature->getPlayer();
795 if (player) {
796 std::string healString = std::to_string(realHealthGain) + (realHealthGain != 1 ? " hitpoints." : " hitpoint.");
797
798 TextMessage message(MESSAGE_HEALED, "You were healed for " + healString);
799 message.position = player->getPosition();
800 message.primary.value = realHealthGain;
801 message.primary.color = TEXTCOLOR_MAYABLUE;
802 player->sendTextMessage(message);
803
804 SpectatorVec spectators;
805 g_game.map.getSpectators(spectators, player->getPosition(), false, true);
806 spectators.erase(player);
807 if (!spectators.empty()) {
808 message.type = MESSAGE_HEALED_OTHERS;
809 message.text = player->getName() + " was healed for " + healString;
810 for (Creature* spectator : spectators) {
811 spectator->getPlayer()->sendTextMessage(message);
812 }
813 }
814 }
815 }
816 }
817
818 if (internalManaTicks >= manaTicks) {
819 internalManaTicks = 0;
820
821 if (Player* player = creature->getPlayer()) {
822 int32_t realManaGain = player->getMana();
823 player->changeMana(manaGain);
824 realManaGain = player->getMana() - realManaGain;
825
826 if (isBuff && realManaGain > 0) {
827 std::string manaGainString = std::to_string(realManaGain);
828
829 TextMessage message(MESSAGE_HEALED, "You gained " + manaGainString + " mana.");
830 message.position = player->getPosition();
831 message.primary.value = realManaGain;
832 message.primary.color = TEXTCOLOR_MAYABLUE;
833 player->sendTextMessage(message);
834
835 SpectatorVec spectators;
836 g_game.map.getSpectators(spectators, player->getPosition(), false, true);
837 spectators.erase(player);
838 if (!spectators.empty()) {
839 message.type = MESSAGE_HEALED_OTHERS;
840 message.text = player->getName() + " gained " + manaGainString + " mana.";
841 for (Creature* spectator : spectators) {
842 spectator->getPlayer()->sendTextMessage(message);
843 }
844 }
845 }
846 }
847 }
848
849 return ConditionGeneric::executeCondition(creature, interval);
850}
851
852bool ConditionRegeneration::setParam(ConditionParam_t param, int32_t value)
853{
854 bool ret = ConditionGeneric::setParam(param, value);
855
856 switch (param) {
857 case CONDITION_PARAM_HEALTHGAIN:
858 healthGain = value;
859 return true;
860
861 case CONDITION_PARAM_HEALTHTICKS:
862 healthTicks = value;
863 return true;
864
865 case CONDITION_PARAM_MANAGAIN:
866 manaGain = value;
867 return true;
868
869 case CONDITION_PARAM_MANATICKS:
870 manaTicks = value;
871 return true;
872
873 default:
874 return ret;
875 }
876}
877
878void ConditionSoul::addCondition(Creature*, const Condition* condition)
879{
880 if (updateCondition(condition)) {
881 setTicks(condition->getTicks());
882
883 const ConditionSoul& conditionSoul = static_cast<const ConditionSoul&>(*condition);
884
885 soulTicks = conditionSoul.soulTicks;
886 soulGain = conditionSoul.soulGain;
887 }
888}
889
890bool ConditionSoul::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
891{
892 if (attr == CONDITIONATTR_SOULGAIN) {
893 return propStream.read<uint32_t>(soulGain);
894 } else if (attr == CONDITIONATTR_SOULTICKS) {
895 return propStream.read<uint32_t>(soulTicks);
896 }
897 return Condition::unserializeProp(attr, propStream);
898}
899
900void ConditionSoul::serialize(PropWriteStream& propWriteStream)
901{
902 Condition::serialize(propWriteStream);
903
904 propWriteStream.write<uint8_t>(CONDITIONATTR_SOULGAIN);
905 propWriteStream.write<uint32_t>(soulGain);
906
907 propWriteStream.write<uint8_t>(CONDITIONATTR_SOULTICKS);
908 propWriteStream.write<uint32_t>(soulTicks);
909}
910
911bool ConditionSoul::executeCondition(Creature* creature, int32_t interval)
912{
913 internalSoulTicks += interval;
914
915 if (Player* player = creature->getPlayer()) {
916 if (player->getZone() != ZONE_PROTECTION) {
917 if (internalSoulTicks >= soulTicks) {
918 internalSoulTicks = 0;
919 player->changeSoul(soulGain);
920 }
921 }
922 }
923
924 return ConditionGeneric::executeCondition(creature, interval);
925}
926
927bool ConditionSoul::setParam(ConditionParam_t param, int32_t value)
928{
929 bool ret = ConditionGeneric::setParam(param, value);
930 switch (param) {
931 case CONDITION_PARAM_SOULGAIN:
932 soulGain = value;
933 return true;
934
935 case CONDITION_PARAM_SOULTICKS:
936 soulTicks = value;
937 return true;
938
939 default:
940 return ret;
941 }
942}
943
944bool ConditionDamage::setParam(ConditionParam_t param, int32_t value)
945{
946 bool ret = Condition::setParam(param, value);
947
948 switch (param) {
949 case CONDITION_PARAM_OWNER:
950 owner = value;
951 return true;
952
953 case CONDITION_PARAM_FORCEUPDATE:
954 forceUpdate = (value != 0);
955 return true;
956
957 case CONDITION_PARAM_DELAYED:
958 delayed = (value != 0);
959 return true;
960
961 case CONDITION_PARAM_MAXVALUE:
962 maxDamage = std::abs(value);
963 break;
964
965 case CONDITION_PARAM_MINVALUE:
966 minDamage = std::abs(value);
967 break;
968
969 case CONDITION_PARAM_STARTVALUE:
970 startDamage = std::abs(value);
971 break;
972
973 case CONDITION_PARAM_TICKINTERVAL:
974 tickInterval = std::abs(value);
975 break;
976
977 case CONDITION_PARAM_PERIODICDAMAGE:
978 periodDamage = value;
979 break;
980
981 case CONDITION_PARAM_FIELD:
982 field = (value != 0);
983 break;
984
985 default:
986 return false;
987 }
988
989 return ret;
990}
991
992bool ConditionDamage::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
993{
994 if (attr == CONDITIONATTR_DELAYED) {
995 uint8_t value;
996 if (!propStream.read<uint8_t>(value)) {
997 return false;
998 }
999
1000 delayed = (value != 0);
1001 return true;
1002 } else if (attr == CONDITIONATTR_PERIODDAMAGE) {
1003 return propStream.read<int32_t>(periodDamage);
1004 } else if (attr == CONDITIONATTR_OWNER) {
1005 return propStream.skip(4);
1006 } else if (attr == CONDITIONATTR_INTERVALDATA) {
1007 IntervalInfo damageInfo;
1008 if (!propStream.read<IntervalInfo>(damageInfo)) {
1009 return false;
1010 }
1011
1012 damageList.push_back(damageInfo);
1013 if (ticks != -1) {
1014 setTicks(ticks + damageInfo.interval);
1015 }
1016 return true;
1017 }
1018 return Condition::unserializeProp(attr, propStream);
1019}
1020
1021void ConditionDamage::serialize(PropWriteStream& propWriteStream)
1022{
1023 Condition::serialize(propWriteStream);
1024
1025 propWriteStream.write<uint8_t>(CONDITIONATTR_DELAYED);
1026 propWriteStream.write<uint8_t>(delayed);
1027
1028 propWriteStream.write<uint8_t>(CONDITIONATTR_PERIODDAMAGE);
1029 propWriteStream.write<int32_t>(periodDamage);
1030
1031 for (const IntervalInfo& intervalInfo : damageList) {
1032 propWriteStream.write<uint8_t>(CONDITIONATTR_INTERVALDATA);
1033 propWriteStream.write<IntervalInfo>(intervalInfo);
1034 }
1035}
1036
1037bool ConditionDamage::updateCondition(const Condition* addCondition)
1038{
1039 const ConditionDamage& conditionDamage = static_cast<const ConditionDamage&>(*addCondition);
1040 if (conditionDamage.doForceUpdate()) {
1041 return true;
1042 }
1043
1044 if (ticks == -1 && conditionDamage.ticks > 0) {
1045 return false;
1046 }
1047
1048 return conditionDamage.getTotalDamage() > getTotalDamage();
1049}
1050
1051bool ConditionDamage::addDamage(int32_t rounds, int32_t time, int32_t value)
1052{
1053 time = std::max<int32_t>(time, EVENT_CREATURE_THINK_INTERVAL);
1054 if (rounds == -1) {
1055 //periodic damage
1056 periodDamage = value;
1057 setParam(CONDITION_PARAM_TICKINTERVAL, time);
1058 setParam(CONDITION_PARAM_TICKS, -1);
1059 return true;
1060 }
1061
1062 if (periodDamage > 0) {
1063 return false;
1064 }
1065
1066 //rounds, time, damage
1067 for (int32_t i = 0; i < rounds; ++i) {
1068 IntervalInfo damageInfo;
1069 damageInfo.interval = time;
1070 damageInfo.timeLeft = time;
1071 damageInfo.value = value;
1072
1073 damageList.push_back(damageInfo);
1074
1075 if (ticks != -1) {
1076 setTicks(ticks + damageInfo.interval);
1077 }
1078 }
1079
1080 return true;
1081}
1082
1083bool ConditionDamage::init()
1084{
1085 if (periodDamage != 0) {
1086 return true;
1087 }
1088
1089 if (!damageList.empty()) {
1090 return true;
1091 }
1092
1093 setTicks(0);
1094
1095 int32_t amount = uniform_random(minDamage, maxDamage);
1096 if (amount != 0) {
1097 if (startDamage > maxDamage) {
1098 startDamage = maxDamage;
1099 } else if (startDamage == 0) {
1100 startDamage = std::max<int32_t>(1, std::ceil(amount / 20.0));
1101 }
1102
1103 std::list<int32_t> list;
1104 ConditionDamage::generateDamageList(amount, startDamage, list);
1105 for (int32_t value : list) {
1106 addDamage(1, tickInterval, -value);
1107 }
1108 }
1109 return !damageList.empty();
1110}
1111
1112bool ConditionDamage::startCondition(Creature* creature)
1113{
1114 if (!Condition::startCondition(creature)) {
1115 return false;
1116 }
1117
1118 if (!delayed) {
1119 // delayed condition does no initial damage
1120 if (!doDamage(creature, initDamage)) {
1121 return false;
1122 }
1123 }
1124
1125 if (!init()) {
1126 return false;
1127 }
1128 return true;
1129}
1130
1131bool ConditionDamage::executeCondition(Creature* creature, int32_t interval)
1132{
1133 if (periodDamage != 0) {
1134 periodDamageTick += interval;
1135
1136 if (periodDamageTick >= tickInterval) {
1137 periodDamageTick = 0;
1138 doDamage(creature, periodDamage);
1139 }
1140 } else if (!damageList.empty()) {
1141 IntervalInfo& damageInfo = damageList.front();
1142
1143 bool bRemove = (ticks != -1);
1144 creature->onTickCondition(getType(), bRemove);
1145 damageInfo.timeLeft -= interval;
1146
1147 if (damageInfo.timeLeft <= 0) {
1148 int32_t damage = damageInfo.value;
1149
1150 if (bRemove) {
1151 damageList.pop_front();
1152 } else {
1153 damageInfo.timeLeft = damageInfo.interval;
1154 }
1155
1156 doDamage(creature, damage);
1157 }
1158
1159 if (!bRemove) {
1160 if (ticks > 0) {
1161 endTime += interval;
1162 }
1163
1164 interval = 0;
1165 }
1166 }
1167
1168 return Condition::executeCondition(creature, interval);
1169}
1170
1171bool ConditionDamage::getNextDamage(int32_t& damage)
1172{
1173 if (periodDamage != 0) {
1174 damage = periodDamage;
1175 return true;
1176 } else if (!damageList.empty()) {
1177 IntervalInfo& damageInfo = damageList.front();
1178 damage = damageInfo.value;
1179 if (ticks != -1) {
1180 damageList.pop_front();
1181 }
1182 return true;
1183 }
1184 return false;
1185}
1186
1187bool ConditionDamage::doDamage(Creature* creature, int32_t healthChange)
1188{
1189 if (creature->isSuppress(getType()) || creature->isImmune(getType())) {
1190 return false;
1191 }
1192
1193 CombatDamage damage;
1194 damage.origin = ORIGIN_CONDITION;
1195 damage.primary.value = healthChange;
1196 damage.primary.type = Combat::ConditionToDamageType(conditionType);
1197
1198 Creature* attacker = g_game.getCreatureByID(owner);
1199 if (field && creature->getPlayer() && attacker && attacker->getPlayer()) {
1200 damage.primary.value = static_cast<int32_t>(std::round(damage.primary.value / 2.));
1201 }
1202
1203 if (!creature->isAttackable() || Combat::canDoCombat(attacker, creature) != RETURNVALUE_NOERROR) {
1204 if (!creature->isInGhostMode()) {
1205 g_game.addMagicEffect(creature->getPosition(), CONST_ME_POFF);
1206 }
1207 return false;
1208 }
1209
1210 if (g_game.combatBlockHit(damage, attacker, creature, false, false, field)) {
1211 return false;
1212 }
1213
1214 return g_game.combatChangeHealth(attacker, creature, damage);
1215}
1216
1217void ConditionDamage::endCondition(Creature*)
1218{
1219 //
1220}
1221
1222void ConditionDamage::addCondition(Creature* creature, const Condition* condition)
1223{
1224 if (condition->getType() != conditionType) {
1225 return;
1226 }
1227
1228 if (!updateCondition(condition)) {
1229 return;
1230 }
1231
1232 const ConditionDamage& conditionDamage = static_cast<const ConditionDamage&>(*condition);
1233
1234 setTicks(condition->getTicks());
1235 owner = conditionDamage.owner;
1236 maxDamage = conditionDamage.maxDamage;
1237 minDamage = conditionDamage.minDamage;
1238 startDamage = conditionDamage.startDamage;
1239 tickInterval = conditionDamage.tickInterval;
1240 periodDamage = conditionDamage.periodDamage;
1241 int32_t nextTimeLeft = tickInterval;
1242
1243 if (!damageList.empty()) {
1244 //save previous timeLeft
1245 IntervalInfo& damageInfo = damageList.front();
1246 nextTimeLeft = damageInfo.timeLeft;
1247 damageList.clear();
1248 }
1249
1250 damageList = conditionDamage.damageList;
1251
1252 if (init()) {
1253 if (!damageList.empty()) {
1254 //restore last timeLeft
1255 IntervalInfo& damageInfo = damageList.front();
1256 damageInfo.timeLeft = nextTimeLeft;
1257 }
1258
1259 if (!delayed) {
1260 int32_t damage;
1261 if (getNextDamage(damage)) {
1262 doDamage(creature, damage);
1263 }
1264 }
1265 }
1266}
1267
1268int32_t ConditionDamage::getTotalDamage() const
1269{
1270 int32_t result;
1271 if (!damageList.empty()) {
1272 result = 0;
1273 for (const IntervalInfo& intervalInfo : damageList) {
1274 result += intervalInfo.value;
1275 }
1276 } else {
1277 result = minDamage + (maxDamage - minDamage) / 2;
1278 }
1279 return std::abs(result);
1280}
1281
1282uint32_t ConditionDamage::getIcons() const
1283{
1284 uint32_t icons = Condition::getIcons();
1285 switch (conditionType) {
1286 case CONDITION_FIRE:
1287 icons |= ICON_BURN;
1288 break;
1289
1290 case CONDITION_ENERGY:
1291 icons |= ICON_ENERGY;
1292 break;
1293
1294 case CONDITION_DROWN:
1295 icons |= ICON_DROWNING;
1296 break;
1297
1298 case CONDITION_POISON:
1299 icons |= ICON_POISON;
1300 break;
1301
1302 case CONDITION_FREEZING:
1303 icons |= ICON_FREEZING;
1304 break;
1305
1306 case CONDITION_DAZZLED:
1307 icons |= ICON_DAZZLED;
1308 break;
1309
1310 case CONDITION_CURSED:
1311 icons |= ICON_CURSED;
1312 break;
1313
1314 case CONDITION_BLEEDING:
1315 icons |= ICON_BLEEDING;
1316 break;
1317
1318 default:
1319 break;
1320 }
1321 return icons;
1322}
1323
1324void ConditionDamage::generateDamageList(int32_t amount, int32_t start, std::list<int32_t>& list)
1325{
1326 amount = std::abs(amount);
1327 int32_t sum = 0;
1328 double x1, x2;
1329
1330 for (int32_t i = start; i > 0; --i) {
1331 int32_t n = start + 1 - i;
1332 int32_t med = (n * amount) / start;
1333
1334 do {
1335 sum += i;
1336 list.push_back(i);
1337
1338 x1 = std::fabs(1.0 - ((static_cast<float>(sum)) + i) / med);
1339 x2 = std::fabs(1.0 - (static_cast<float>(sum) / med));
1340 } while (x1 < x2);
1341 }
1342}
1343
1344void ConditionSpeed::setFormulaVars(float mina, float minb, float maxa, float maxb)
1345{
1346 this->mina = mina;
1347 this->minb = minb;
1348 this->maxa = maxa;
1349 this->maxb = maxb;
1350}
1351
1352void ConditionSpeed::getFormulaValues(int32_t var, int32_t& min, int32_t& max) const
1353{
1354 min = (var * mina) + minb;
1355 max = (var * maxa) + maxb;
1356}
1357
1358bool ConditionSpeed::setParam(ConditionParam_t param, int32_t value)
1359{
1360 Condition::setParam(param, value);
1361 if (param != CONDITION_PARAM_SPEED) {
1362 return false;
1363 }
1364
1365 speedDelta = value;
1366
1367 if (value > 0) {
1368 conditionType = CONDITION_HASTE;
1369 } else {
1370 conditionType = CONDITION_PARALYZE;
1371 }
1372 return true;
1373}
1374
1375bool ConditionSpeed::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
1376{
1377 if (attr == CONDITIONATTR_SPEEDDELTA) {
1378 return propStream.read<int32_t>(speedDelta);
1379 } else if (attr == CONDITIONATTR_FORMULA_MINA) {
1380 return propStream.read<float>(mina);
1381 } else if (attr == CONDITIONATTR_FORMULA_MINB) {
1382 return propStream.read<float>(minb);
1383 } else if (attr == CONDITIONATTR_FORMULA_MAXA) {
1384 return propStream.read<float>(maxa);
1385 } else if (attr == CONDITIONATTR_FORMULA_MAXB) {
1386 return propStream.read<float>(maxb);
1387 }
1388 return Condition::unserializeProp(attr, propStream);
1389}
1390
1391void ConditionSpeed::serialize(PropWriteStream& propWriteStream)
1392{
1393 Condition::serialize(propWriteStream);
1394
1395 propWriteStream.write<uint8_t>(CONDITIONATTR_SPEEDDELTA);
1396 propWriteStream.write<int32_t>(speedDelta);
1397
1398 propWriteStream.write<uint8_t>(CONDITIONATTR_FORMULA_MINA);
1399 propWriteStream.write<float>(mina);
1400
1401 propWriteStream.write<uint8_t>(CONDITIONATTR_FORMULA_MINB);
1402 propWriteStream.write<float>(minb);
1403
1404 propWriteStream.write<uint8_t>(CONDITIONATTR_FORMULA_MAXA);
1405 propWriteStream.write<float>(maxa);
1406
1407 propWriteStream.write<uint8_t>(CONDITIONATTR_FORMULA_MAXB);
1408 propWriteStream.write<float>(maxb);
1409}
1410
1411bool ConditionSpeed::startCondition(Creature* creature)
1412{
1413 if (!Condition::startCondition(creature)) {
1414 return false;
1415 }
1416
1417 if (speedDelta == 0) {
1418 int32_t min, max;
1419 getFormulaValues(creature->getBaseSpeed(), min, max);
1420 speedDelta = uniform_random(min, max);
1421 }
1422
1423 g_game.changeSpeed(creature, speedDelta);
1424 return true;
1425}
1426
1427bool ConditionSpeed::executeCondition(Creature* creature, int32_t interval)
1428{
1429 return Condition::executeCondition(creature, interval);
1430}
1431
1432void ConditionSpeed::endCondition(Creature* creature)
1433{
1434 g_game.changeSpeed(creature, -speedDelta);
1435}
1436
1437void ConditionSpeed::addCondition(Creature* creature, const Condition* condition)
1438{
1439 if (conditionType != condition->getType()) {
1440 return;
1441 }
1442
1443 if (ticks == -1 && condition->getTicks() > 0) {
1444 return;
1445 }
1446
1447 setTicks(condition->getTicks());
1448
1449 const ConditionSpeed& conditionSpeed = static_cast<const ConditionSpeed&>(*condition);
1450 int32_t oldSpeedDelta = speedDelta;
1451 speedDelta = conditionSpeed.speedDelta;
1452 mina = conditionSpeed.mina;
1453 maxa = conditionSpeed.maxa;
1454 minb = conditionSpeed.minb;
1455 maxb = conditionSpeed.maxb;
1456
1457 if (speedDelta == 0) {
1458 int32_t min;
1459 int32_t max;
1460 getFormulaValues(creature->getBaseSpeed(), min, max);
1461 speedDelta = uniform_random(min, max);
1462 }
1463
1464 int32_t newSpeedChange = (speedDelta - oldSpeedDelta);
1465 if (newSpeedChange != 0) {
1466 g_game.changeSpeed(creature, newSpeedChange);
1467 }
1468}
1469
1470uint32_t ConditionSpeed::getIcons() const
1471{
1472 uint32_t icons = Condition::getIcons();
1473 switch (conditionType) {
1474 case CONDITION_HASTE:
1475 icons |= ICON_HASTE;
1476 break;
1477
1478 case CONDITION_PARALYZE:
1479 icons |= ICON_PARALYZE;
1480 break;
1481
1482 default:
1483 break;
1484 }
1485 return icons;
1486}
1487
1488bool ConditionInvisible::startCondition(Creature* creature)
1489{
1490 if (!Condition::startCondition(creature)) {
1491 return false;
1492 }
1493
1494 g_game.internalCreatureChangeVisible(creature, false);
1495 return true;
1496}
1497
1498void ConditionInvisible::endCondition(Creature* creature)
1499{
1500 if (!creature->isInvisible()) {
1501 g_game.internalCreatureChangeVisible(creature, true);
1502 }
1503}
1504
1505void ConditionOutfit::setOutfit(const Outfit_t& outfit)
1506{
1507 this->outfit = outfit;
1508}
1509
1510bool ConditionOutfit::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
1511{
1512 if (attr == CONDITIONATTR_OUTFIT) {
1513 return propStream.read<Outfit_t>(outfit);
1514 }
1515 return Condition::unserializeProp(attr, propStream);
1516}
1517
1518void ConditionOutfit::serialize(PropWriteStream& propWriteStream)
1519{
1520 Condition::serialize(propWriteStream);
1521
1522 propWriteStream.write<uint8_t>(CONDITIONATTR_OUTFIT);
1523 propWriteStream.write<Outfit_t>(outfit);
1524}
1525
1526bool ConditionOutfit::startCondition(Creature* creature)
1527{
1528 if (!Condition::startCondition(creature)) {
1529 return false;
1530 }
1531
1532 g_game.internalCreatureChangeOutfit(creature, outfit);
1533 return true;
1534}
1535
1536bool ConditionOutfit::executeCondition(Creature* creature, int32_t interval)
1537{
1538 return Condition::executeCondition(creature, interval);
1539}
1540
1541void ConditionOutfit::endCondition(Creature* creature)
1542{
1543 g_game.internalCreatureChangeOutfit(creature, creature->getDefaultOutfit());
1544}
1545
1546void ConditionOutfit::addCondition(Creature* creature, const Condition* condition)
1547{
1548 if (updateCondition(condition)) {
1549 setTicks(condition->getTicks());
1550
1551 const ConditionOutfit& conditionOutfit = static_cast<const ConditionOutfit&>(*condition);
1552 outfit = conditionOutfit.outfit;
1553
1554 g_game.internalCreatureChangeOutfit(creature, outfit);
1555 }
1556}
1557
1558bool ConditionLight::startCondition(Creature* creature)
1559{
1560 if (!Condition::startCondition(creature)) {
1561 return false;
1562 }
1563
1564 internalLightTicks = 0;
1565 lightChangeInterval = ticks / lightInfo.level;
1566 creature->setCreatureLight(lightInfo);
1567 g_game.changeLight(creature);
1568 return true;
1569}
1570
1571bool ConditionLight::executeCondition(Creature* creature, int32_t interval)
1572{
1573 internalLightTicks += interval;
1574
1575 if (internalLightTicks >= lightChangeInterval) {
1576 internalLightTicks = 0;
1577 LightInfo lightInfo = creature->getCreatureLight();
1578
1579 if (lightInfo.level > 0) {
1580 --lightInfo.level;
1581 creature->setCreatureLight(lightInfo);
1582 g_game.changeLight(creature);
1583 }
1584 }
1585
1586 return Condition::executeCondition(creature, interval);
1587}
1588
1589void ConditionLight::endCondition(Creature* creature)
1590{
1591 creature->setNormalCreatureLight();
1592 g_game.changeLight(creature);
1593}
1594
1595void ConditionLight::addCondition(Creature* creature, const Condition* condition)
1596{
1597 if (updateCondition(condition)) {
1598 setTicks(condition->getTicks());
1599
1600 const ConditionLight& conditionLight = static_cast<const ConditionLight&>(*condition);
1601 lightInfo.level = conditionLight.lightInfo.level;
1602 lightInfo.color = conditionLight.lightInfo.color;
1603 lightChangeInterval = ticks / lightInfo.level;
1604 internalLightTicks = 0;
1605 creature->setCreatureLight(lightInfo);
1606 g_game.changeLight(creature);
1607 }
1608}
1609
1610bool ConditionLight::setParam(ConditionParam_t param, int32_t value)
1611{
1612 bool ret = Condition::setParam(param, value);
1613 if (ret) {
1614 return false;
1615 }
1616
1617 switch (param) {
1618 case CONDITION_PARAM_LIGHT_LEVEL:
1619 lightInfo.level = value;
1620 return true;
1621
1622 case CONDITION_PARAM_LIGHT_COLOR:
1623 lightInfo.color = value;
1624 return true;
1625
1626 default:
1627 return false;
1628 }
1629}
1630
1631bool ConditionLight::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
1632{
1633 if (attr == CONDITIONATTR_LIGHTCOLOR) {
1634 uint32_t value;
1635 if (!propStream.read<uint32_t>(value)) {
1636 return false;
1637 }
1638
1639 lightInfo.color = value;
1640 return true;
1641 } else if (attr == CONDITIONATTR_LIGHTLEVEL) {
1642 uint32_t value;
1643 if (!propStream.read<uint32_t>(value)) {
1644 return false;
1645 }
1646
1647 lightInfo.level = value;
1648 return true;
1649 } else if (attr == CONDITIONATTR_LIGHTTICKS) {
1650 return propStream.read<uint32_t>(internalLightTicks);
1651 } else if (attr == CONDITIONATTR_LIGHTINTERVAL) {
1652 return propStream.read<uint32_t>(lightChangeInterval);
1653 }
1654 return Condition::unserializeProp(attr, propStream);
1655}
1656
1657void ConditionLight::serialize(PropWriteStream& propWriteStream)
1658{
1659 Condition::serialize(propWriteStream);
1660
1661 // TODO: color and level could be serialized as 8-bit if we can retain backwards
1662 // compatibility, but perhaps we should keep it like this in case they increase
1663 // in the future...
1664 propWriteStream.write<uint8_t>(CONDITIONATTR_LIGHTCOLOR);
1665 propWriteStream.write<uint32_t>(lightInfo.color);
1666
1667 propWriteStream.write<uint8_t>(CONDITIONATTR_LIGHTLEVEL);
1668 propWriteStream.write<uint32_t>(lightInfo.level);
1669
1670 propWriteStream.write<uint8_t>(CONDITIONATTR_LIGHTTICKS);
1671 propWriteStream.write<uint32_t>(internalLightTicks);
1672
1673 propWriteStream.write<uint8_t>(CONDITIONATTR_LIGHTINTERVAL);
1674 propWriteStream.write<uint32_t>(lightChangeInterval);
1675}
1676
1677void ConditionSpellCooldown::addCondition(Creature* creature, const Condition* condition)
1678{
1679 if (updateCondition(condition)) {
1680 setTicks(condition->getTicks());
1681
1682 if (subId != 0 && ticks > 0) {
1683 Player* player = creature->getPlayer();
1684 if (player) {
1685 player->sendSpellCooldown(subId, ticks);
1686 }
1687 }
1688 }
1689}
1690
1691bool ConditionSpellCooldown::startCondition(Creature* creature)
1692{
1693 if (!Condition::startCondition(creature)) {
1694 return false;
1695 }
1696
1697 if (subId != 0 && ticks > 0) {
1698 Player* player = creature->getPlayer();
1699 if (player) {
1700 player->sendSpellCooldown(subId, ticks);
1701 }
1702 }
1703 return true;
1704}
1705
1706void ConditionSpellGroupCooldown::addCondition(Creature* creature, const Condition* condition)
1707{
1708 if (updateCondition(condition)) {
1709 setTicks(condition->getTicks());
1710
1711 if (subId != 0 && ticks > 0) {
1712 Player* player = creature->getPlayer();
1713 if (player) {
1714 player->sendSpellGroupCooldown(static_cast<SpellGroup_t>(subId), ticks);
1715 }
1716 }
1717 }
1718}
1719
1720bool ConditionSpellGroupCooldown::startCondition(Creature* creature)
1721{
1722 if (!Condition::startCondition(creature)) {
1723 return false;
1724 }
1725
1726 if (subId != 0 && ticks > 0) {
1727 Player* player = creature->getPlayer();
1728 if (player) {
1729 player->sendSpellGroupCooldown(static_cast<SpellGroup_t>(subId), ticks);
1730 }
1731 }
1732 return true;
1733}
1734