1 /*
2  *  The ManaPlus Client
3  *  Copyright (C) 2011-2019  The ManaPlus Developers
4  *  Copyright (C) 2019-2021  Andrei Karas
5  *
6  *  This file is part of The ManaPlus Client.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "net/eathena/mercenaryrecv.h"
23 
24 #include "actormanager.h"
25 #include "notifymanager.h"
26 
27 #include "being/localplayer.h"
28 #include "being/mercenaryinfo.h"
29 #include "being/playerinfo.h"
30 
31 #include "enums/resources/notifytypes.h"
32 
33 #include "gui/windows/skilldialog.h"
34 
35 #include "net/messagein.h"
36 
37 #include "net/eathena/sp.h"
38 
39 #include "utils/checkutils.h"
40 
41 #include "debug.h"
42 
43 namespace EAthena
44 {
45 
46 #define setMercStat(sp, stat) \
47         case sp: \
48             PlayerInfo::setStatBase(stat, \
49                 val, \
50                 Notify_true); \
51             break
52 
processMercenaryUpdate(Net::MessageIn & msg)53 void MercenaryRecv::processMercenaryUpdate(Net::MessageIn &msg)
54 {
55     const int sp = msg.readInt16("type");
56     const int val = msg.readInt32("value");
57     switch (sp)
58     {
59         setMercStat(Sp::ATK1, Attributes::MERC_ATK);
60         setMercStat(Sp::MATK1, Attributes::MERC_MATK);
61         setMercStat(Sp::HIT, Attributes::MERC_HIT);
62         setMercStat(Sp::CRITICAL, Attributes::MERC_CRIT);
63         setMercStat(Sp::DEF1, Attributes::MERC_DEF);
64         setMercStat(Sp::MDEF1, Attributes::MERC_MDEF);
65         setMercStat(Sp::MERCFLEE, Attributes::MERC_FLEE);
66         setMercStat(Sp::ASPD, Attributes::MERC_ATTACK_DELAY);
67         setMercStat(Sp::HP, Attributes::MERC_HP);
68         setMercStat(Sp::MAXHP, Attributes::MERC_MAX_HP);
69         setMercStat(Sp::SP, Attributes::MERC_MP);
70         setMercStat(Sp::MAXSP, Attributes::MERC_MAX_MP);
71         setMercStat(Sp::MERCKILLS, Attributes::MERC_KILLS);
72         setMercStat(Sp::MERCFAITH, Attributes::MERC_FAITH);
73         default:
74             reportAlways("Unknown mercenary stat %d",
75                 sp)
76             break;
77     }
78 }
79 
processMercenaryInfo(Net::MessageIn & msg)80 void MercenaryRecv::processMercenaryInfo(Net::MessageIn &msg)
81 {
82     if (actorManager == nullptr)
83         return;
84     // +++ need create if need mercenary being and update stats
85     Being *const dstBeing = actorManager->findBeing(
86         msg.readBeingId("being id"));
87     PlayerInfo::setStatBase(Attributes::MERC_ATK,
88         msg.readInt16("atk"),
89         Notify_true);
90     PlayerInfo::setStatBase(Attributes::MERC_MATK,
91         msg.readInt16("matk"),
92         Notify_true);
93     PlayerInfo::setStatBase(Attributes::MERC_HIT,
94         msg.readInt16("hit"),
95         Notify_true);
96     PlayerInfo::setStatBase(Attributes::MERC_CRIT,
97         msg.readInt16("crit/10"),
98         Notify_true);
99     PlayerInfo::setStatBase(Attributes::MERC_DEF,
100         msg.readInt16("def"),
101         Notify_true);
102     PlayerInfo::setStatBase(Attributes::MERC_MDEF,
103         msg.readInt16("mdef"),
104         Notify_true);
105     PlayerInfo::setStatBase(Attributes::MERC_FLEE,
106         msg.readInt16("flee"),
107         Notify_true);
108     PlayerInfo::setStatBase(Attributes::MERC_ATTACK_DELAY,
109         msg.readInt16("attack speed"),
110         Notify_true);
111     const std::string name = msg.readString(24, "name");
112     const int level = msg.readInt16("level");
113     PlayerInfo::setStatBase(Attributes::MERC_LEVEL,
114         level,
115         Notify_true);
116     PlayerInfo::setStatBase(Attributes::MERC_HP,
117         msg.readInt32("hp"),
118         Notify_true);
119     PlayerInfo::setStatBase(Attributes::MERC_MAX_HP,
120         msg.readInt32("max hp"),
121         Notify_true);
122     PlayerInfo::setStatBase(Attributes::MERC_MP,
123         msg.readInt32("sp"),
124         Notify_true);
125     PlayerInfo::setStatBase(Attributes::MERC_MAX_MP,
126         msg.readInt32("max sp"),
127         Notify_true);
128     PlayerInfo::setStatBase(Attributes::MERC_EXPIRE,
129         msg.readInt32("expire time"),
130         Notify_true);
131     PlayerInfo::setStatBase(Attributes::MERC_FAITH,
132         msg.readInt16("faith"),
133         Notify_true);
134     PlayerInfo::setStatBase(Attributes::MERC_CALLS,
135         msg.readInt32("calls"),
136         Notify_true);
137     PlayerInfo::setStatBase(Attributes::MERC_KILLS,
138         msg.readInt32("kills"),
139         Notify_true);
140     const int range = msg.readInt16("attack range");
141     PlayerInfo::setStatBase(Attributes::MERC_ATTACK_RANGE,
142         range,
143         Notify_true);
144     PlayerInfo::updateAttrs();
145 
146     if ((dstBeing != nullptr) && (localPlayer != nullptr))
147     {
148         MercenaryInfo *const mercenary = new MercenaryInfo;
149         mercenary->id = dstBeing->getId();
150         mercenary->name = name;
151         mercenary->level = level;
152         mercenary->range = range;
153         PlayerInfo::setMercenary(mercenary);
154         PlayerInfo::setMercenaryBeing(dstBeing);
155     }
156 }
157 
processMercenarySkills(Net::MessageIn & msg)158 void MercenaryRecv::processMercenarySkills(Net::MessageIn &msg)
159 {
160     if (skillDialog != nullptr)
161         skillDialog->hideSkills(SkillOwner::Mercenary);
162     const int count = (msg.readInt16("len") - 4) / 37;
163     for (int f = 0; f < count; f ++)
164     {
165         const int skillId = msg.readInt16("skill id");
166         const SkillType::SkillType inf = static_cast<SkillType::SkillType>(
167             msg.readInt32("inf"));
168         const int level = msg.readInt16("skill level");
169         const int sp = msg.readInt16("sp");
170         const int range = msg.readInt16("range");
171         const std::string name = msg.readString(24, "skill name");
172         const Modifiable up = fromBool(msg.readUInt8("up flag"), Modifiable);
173         PlayerInfo::setSkillLevel(skillId, level);
174         if (skillDialog != nullptr)
175         {
176             if (!skillDialog->updateSkill(skillId, range, up, inf, sp))
177             {
178                 skillDialog->addSkill(SkillOwner::Mercenary,
179                     skillId, name, level, range, up, inf, sp);
180             }
181         }
182     }
183     if (skillDialog != nullptr)
184         skillDialog->updateModels();
185 }
186 
handleMercenaryMessage(const int cmd)187 void MercenaryRecv::handleMercenaryMessage(const int cmd)
188 {
189     PlayerInfo::setMercenary(nullptr);
190     if (skillDialog != nullptr)
191     {
192         skillDialog->hideSkills(SkillOwner::Mercenary);
193         skillDialog->updateModels();
194     }
195 
196     switch (cmd)
197     {
198         case 0:
199             NotifyManager::notify(NotifyTypes::MERCENARY_EXPIRED);
200             break;
201         case 1:
202             NotifyManager::notify(NotifyTypes::MERCENARY_KILLED);
203             break;
204         case 2:
205             NotifyManager::notify(NotifyTypes::MERCENARY_FIRED);
206             break;
207         case 3:
208             NotifyManager::notify(NotifyTypes::MERCENARY_RUN);
209             break;
210         default:
211             NotifyManager::notify(NotifyTypes::MERCENARY_UNKNOWN);
212             break;
213     }
214 }
215 
216 }  // namespace EAthena
217