1 /*
2  *  The ManaPlus Client
3  *  Copyright (C) 2013-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 "resources/beingcommon.h"
23 
24 #include "configuration.h"
25 #include "logger.h"
26 
27 #include "enums/resources/map/blockmask.h"
28 
29 #include "utils/cast.h"
30 
31 #include "resources/beinginfo.h"
32 
33 #include "resources/sprite/spritereference.h"
34 
35 #include "debug.h"
36 
readBasicAttributes(BeingInfo * const info,XmlNodePtrConst node,const std::string & hoverCursor)37 void BeingCommon::readBasicAttributes(BeingInfo *const info,
38                                       XmlNodePtrConst node,
39                                       const std::string &hoverCursor)
40 {
41     info->setTargetCursorSize(XML::getProperty(node,
42         "targetCursor", "medium"));
43 
44     info->setHoverCursor(XML::getProperty(node, "hoverCursor", hoverCursor));
45 
46     info->setTargetOffsetX(XML::getProperty(node, "targetOffsetX", 0));
47     info->setTargetOffsetY(XML::getProperty(node, "targetOffsetY", 0));
48 
49     info->setNameOffsetX(XML::getProperty(node, "nameOffsetX", 0));
50     info->setNameOffsetY(XML::getProperty(node, "nameOffsetY", 0));
51     info->setSortOffsetY(XML::getProperty(node, "sortOffsetY", 0));
52 
53     info->setHpBarOffsetX(XML::getProperty(node, "hpBarOffsetX", 0));
54     info->setHpBarOffsetY(XML::getProperty(node, "hpBarOffsetY", 0));
55 
56     info->setQuickActionEffectId(XML::getProperty(node,
57         "quickActionEffect", -1));
58 }
59 
readWalkingAttributes(BeingInfo * const info,XmlNodePtrConst node,const int moreBlockFlags)60 void BeingCommon::readWalkingAttributes(BeingInfo *const info,
61                                         XmlNodePtrConst node,
62                                         const int moreBlockFlags)
63 {
64     uint8_t block = 0;
65     std::string walkStr = XML::getProperty(
66         node, "walkType", "walk");
67 
68     const uint8_t allFlags = CAST_U8(
69         BlockMask::GROUND |
70         BlockMask::WALL |
71         BlockMask::WATER |
72         BlockMask::AIR |
73         moreBlockFlags);
74     StringVect tokens;
75     splitToStringVector(tokens, walkStr, ',');
76     FOR_EACH (StringVectCIter, it, tokens)
77     {
78         if (walkStr == "walk" || walkStr == "ground")
79             block |= BlockMask::GROUND;
80         else if (walkStr == "fly" || walkStr == "air")
81             block |= BlockMask::GROUND | BlockMask::WATER | BlockMask::AIR;
82         else if (walkStr == "all")
83             block |= allFlags;
84         else if (walkStr == "wall")
85             block |= BlockMask::WALL;
86         else if (walkStr == "monsterwall")
87             block |= BlockMask::MONSTERWALL;
88         else if (walkStr == "swim" || walkStr == "water")
89             block |= BlockMask::WATER;
90         else if (walkStr == "walkswim" || walkStr == "swimwalk")  // legacy
91             block |= BlockMask::GROUND | BlockMask::WATER;
92     }
93     info->setBlockWalkMask(CAST_U8(block ^ allFlags));
94 }
95 
readAiAttributes(BeingInfo * const info,XmlNodePtrConst node)96 void BeingCommon::readAiAttributes(BeingInfo *const info,
97                                    XmlNodePtrConst node)
98 {
99     info->setStartFollowDist(XML::getProperty(node,
100         "startFollowDistance", 3));
101     info->setFollowDist(XML::getProperty(node,
102         "followDistance", 0));
103     info->setWarpDist(XML::getProperty(node,
104         "warpDistance", 11));
105 
106     info->setTargetOffsetX(XML::getProperty(node,
107         "offsetX", 0));
108     info->setTargetOffsetY(XML::getProperty(node,
109         "offsetY", 1));
110     info->setSitOffsetX(XML::getProperty(node,
111         "sitOffsetX", 0));
112     info->setSitOffsetY(XML::getProperty(node,
113         "sitOffsetY", 1));
114     info->setMoveOffsetX(XML::getProperty(node,
115         "moveOffsetX", 0));
116     info->setMoveOffsetY(XML::getProperty(node,
117         "moveOffsetY", 1));
118     info->setDeadOffsetX(XML::getProperty(node,
119         "deadOffsetX", 0));
120     info->setDeadOffsetY(XML::getProperty(node,
121         "deadOffsetY", 1));
122     info->setAttackOffsetX(XML::getProperty(node,
123         "attackOffsetX", info->getTargetOffsetX()));
124     info->setAttackOffsetY(XML::getProperty(node,
125         "attackOffsetY", info->getTargetOffsetY()));
126 
127     info->setThinkTime(XML::getProperty(node,
128         "thinkTime", 500) / 10);
129 
130     info->setDirectionType(XML::getProperty(node,
131         "directionType", 1));
132     info->setSitDirectionType(XML::getProperty(node,
133         "sitDirectionType", 1));
134     info->setDeadDirectionType(XML::getProperty(node,
135         "deadDirectionType", 1));
136     info->setAttackDirectionType(XML::getProperty(node,
137         "attackDirectionType", 4));
138 }
139 
readObjectNodes(XmlNodePtrConst & spriteNode,SpriteDisplay & display,BeingInfo * const currentInfo,const std::string & dbName)140 bool BeingCommon::readObjectNodes(XmlNodePtrConst &spriteNode,
141                                   SpriteDisplay &display,
142                                   BeingInfo *const currentInfo,
143                                   const std::string &dbName)
144 {
145     if (xmlNameEqual(spriteNode, "sprite"))
146     {
147         if (!XmlHaveChildContent(spriteNode))
148             return true;
149 
150         SpriteReference *const currentSprite = new SpriteReference;
151         currentSprite->sprite = XmlChildContent(spriteNode);
152 
153         currentSprite->variant = XML::getProperty(
154             spriteNode, "variant", 0);
155         display.sprites.push_back(currentSprite);
156         return true;
157     }
158     else if (xmlNameEqual(spriteNode, "sound"))
159     {
160         if (!XmlHaveChildContent(spriteNode))
161             return true;
162 
163         const std::string event = XML::getProperty(
164             spriteNode, "event", "");
165         const int delay = XML::getProperty(
166             spriteNode, "delay", 0);
167         const char *const filename = XmlChildContent(spriteNode);
168 
169         if (event == "hit")
170         {
171             currentInfo->addSound(ItemSoundEvent::HIT, filename, delay);
172         }
173         else if (event == "miss")
174         {
175             currentInfo->addSound(ItemSoundEvent::MISS, filename, delay);
176         }
177         else if (event == "hurt")
178         {
179             currentInfo->addSound(ItemSoundEvent::HURT, filename, delay);
180         }
181         else if (event == "die")
182         {
183             currentInfo->addSound(ItemSoundEvent::DIE, filename, delay);
184         }
185         else if (event == "move")
186         {
187             currentInfo->addSound(ItemSoundEvent::MOVE, filename, delay);
188         }
189         else if (event == "sit")
190         {
191             currentInfo->addSound(ItemSoundEvent::SIT, filename, delay);
192         }
193         else if (event == "sittop")
194         {
195             currentInfo->addSound(ItemSoundEvent::SITTOP, filename, delay);
196         }
197         else if (event == "spawn")
198         {
199             currentInfo->addSound(ItemSoundEvent::SPAWN, filename, delay);
200         }
201         else
202         {
203             logger->log((dbName + ": Warning, sound effect %s for "
204                 "unknown event %s of monster %s").c_str(),
205                 filename, event.c_str(),
206                 currentInfo->getName().c_str());
207         }
208         return true;
209     }
210     else if (xmlNameEqual(spriteNode, "attack"))
211     {
212         const int attackId = XML::getProperty(spriteNode, "id", 0);
213         const int effectId = XML::getProperty(spriteNode, "effect-id",
214             paths.getIntValue("effectId"));
215         const int hitEffectId = XML::getProperty(spriteNode, "hit-effect-id",
216             paths.getIntValue("hitEffectId"));
217         const int criticalHitEffectId = XML::getProperty(spriteNode,
218             "critical-hit-effect-id",
219             paths.getIntValue("criticalHitEffectId"));
220         const int missEffectId = XML::getProperty(spriteNode, "miss-effect-id",
221             paths.getIntValue("missEffectId"));
222 
223         const std::string spriteAction = XML::getProperty(spriteNode, "action",
224             "attack");
225         const std::string skySpriteAction = XML::getProperty(spriteNode,
226             "skyaction", "skyattack");
227         const std::string waterSpriteAction = XML::getProperty(spriteNode,
228             "wateraction", "waterattack");
229         const std::string rideSpriteAction = XML::getProperty(spriteNode,
230             "rideaction", "rideattack");
231 
232         const std::string missileParticle = XML::getProperty(spriteNode,
233             "missile-particle", "");
234 
235         const float missileZ = XML::getFloatProperty(
236             spriteNode, "missile-z", 32.0F);
237         const int missileLifeTime = XML::getProperty(
238             spriteNode, "missile-lifetime", 500);
239         const float missileSpeed = XML::getFloatProperty(
240             spriteNode, "missile-speed", 7.0F);
241         const float missileDieDistance = XML::getFloatProperty(
242             spriteNode, "missile-diedistance", 8.0F);
243 
244         currentInfo->addAttack(attackId,
245             spriteAction,
246             skySpriteAction,
247             waterSpriteAction,
248             rideSpriteAction,
249             effectId,
250             hitEffectId,
251             criticalHitEffectId,
252             missEffectId,
253             missileParticle,
254             missileZ,
255             missileSpeed,
256             missileDieDistance,
257             missileLifeTime);
258         return true;
259     }
260     else if (xmlNameEqual(spriteNode, "particlefx"))
261     {
262         if (!XmlHaveChildContent(spriteNode))
263             return true;
264 
265         display.particles.push_back(XmlChildContent(spriteNode));
266         return true;
267     }
268     return false;
269 }
270