1 ///////////////////////////////////////////////////////////////////////////////
2 //            Copyright (C) 2004-2011 by The Allacrost Project
3 //            Copyright (C) 2012-2016 by Bertram (Valyria Tear)
4 //                         All Rights Reserved
5 //
6 // This code is licensed under the GNU GPL version 2. It is free software
7 // and you may modify it and/or redistribute it under the terms of this license.
8 // See https://www.gnu.org/copyleft/gpl.html for details.
9 ///////////////////////////////////////////////////////////////////////////////
10 
11 #include "modes/map/map_objects/map_object.h"
12 
13 #include "modes/map/map_mode.h"
14 #include "modes/map/map_object_supervisor.h"
15 #include "modes/map/map_sprites/map_virtual_sprite.h"
16 
17 #include "engine/system.h"
18 #include "engine/video/video.h"
19 #include "common/global/global.h"
20 
21 using namespace vt_common;
22 
23 namespace vt_map
24 {
25 
26 namespace private_map
27 {
28 
MapObject(MapObjectDrawLayer layer)29 MapObject::MapObject(MapObjectDrawLayer layer) :
30     _object_id(-1),
31     _img_pixel_half_width(0.0f),
32     _img_pixel_height(0.0f),
33     _img_screen_half_width(0.0f),
34     _img_screen_height(0.0f),
35     _img_grid_half_width(0.0f),
36     _img_grid_height(0.0f),
37     _coll_pixel_half_width(0.0f),
38     _coll_pixel_height(0.0f),
39     _coll_screen_half_width(0.0f),
40     _coll_screen_height(0.0f),
41     _coll_grid_half_width(0.0f),
42     _coll_grid_height(0.0f),
43     _updatable(true),
44     _visible(true),
45     _collision_mask(ALL_COLLISION),
46     _draw_on_second_pass(false),
47     _object_type(OBJECT_TYPE),
48     _emote_animation(nullptr),
49     _interaction_icon(nullptr),
50     _emote_screen_offset(0.0f, 0.0f),
51     _emote_time(0),
52     _draw_layer(layer),
53     _grayscale(false)
54 {
55     // Generate the object Id at creation time.
56     ObjectSupervisor* obj_sup = MapMode::CurrentInstance()->GetObjectSupervisor();
57     _object_id = obj_sup->GenerateObjectID();
58     obj_sup->RegisterObject(this);
59 }
60 
~MapObject()61 MapObject::~MapObject()
62 {
63     if (_interaction_icon)
64         delete _interaction_icon;
65 }
66 
Update()67 void MapObject::Update()
68 {
69     if (_interaction_icon)
70         _interaction_icon->Update();
71 }
72 
ShouldDraw()73 bool MapObject::ShouldDraw()
74 {
75     if(!_visible)
76         return false;
77 
78     MapMode* MM = MapMode::CurrentInstance();
79 
80     // Determine if the sprite is off-screen and if so, don't draw it.
81     if(!GetGridImageRectangle().IntersectsWith(MM->GetMapFrame().screen_edges))
82         return false;
83 
84     // Move the drawing cursor to the appropriate coordinates for this sprite
85     float x_pos = MM->GetScreenXCoordinate(GetXPosition());
86     float y_pos = MM->GetScreenYCoordinate(GetYPosition());
87 
88     vt_video::VideoManager->Move(x_pos, y_pos);
89 
90     return true;
91 }
92 
GetGridCollisionRectangle() const93 Rectangle2D MapObject::GetGridCollisionRectangle() const
94 {
95     Rectangle2D rect;
96     rect.left = _tile_position.x - _coll_grid_half_width;
97     rect.right = _tile_position.x + _coll_grid_half_width;
98     rect.top = _tile_position.y - _coll_grid_height;
99     rect.bottom = _tile_position.y;
100     return rect;
101 }
102 
GetGridCollisionRectangle(float tile_x,float tile_y) const103 Rectangle2D MapObject::GetGridCollisionRectangle(float tile_x, float tile_y) const
104 {
105     Rectangle2D rect;
106     rect.left = tile_x - _coll_grid_half_width;
107     rect.right = tile_x + _coll_grid_half_width;
108     rect.top = tile_y - _coll_grid_height;
109     rect.bottom = tile_y;
110     return rect;
111 }
112 
GetScreenCollisionRectangle(float x,float y) const113 Rectangle2D MapObject::GetScreenCollisionRectangle(float x, float y) const
114 {
115     Rectangle2D rect;
116     rect.left = x - _coll_screen_half_width;
117     rect.right = x + _coll_screen_half_width;
118     rect.top = y - _coll_screen_height;
119     rect.bottom = y;
120     return rect;
121 }
122 
GetScreenCollisionRectangle() const123 Rectangle2D MapObject::GetScreenCollisionRectangle() const
124 {
125     MapMode* mm = MapMode::CurrentInstance();
126     Rectangle2D rect;
127     float x_screen_pos = mm->GetScreenXCoordinate(_tile_position.x);
128     float y_screen_pos = mm->GetScreenYCoordinate(_tile_position.y);
129     rect.left = x_screen_pos - _coll_screen_half_width;
130     rect.right = x_screen_pos + _coll_screen_half_width;
131     rect.top = y_screen_pos - _coll_screen_height;
132     rect.bottom = y_screen_pos;
133     return rect;
134 }
135 
GetScreenImageRectangle() const136 Rectangle2D MapObject::GetScreenImageRectangle() const
137 {
138     MapMode* mm = MapMode::CurrentInstance();
139     Rectangle2D rect;
140     float x_screen_pos = mm->GetScreenXCoordinate(_tile_position.x);
141     float y_screen_pos = mm->GetScreenYCoordinate(_tile_position.y);
142     rect.left = x_screen_pos - _img_screen_half_width;
143     rect.right = x_screen_pos + _img_screen_half_width;
144     rect.top = y_screen_pos - _img_screen_height;
145     rect.bottom = y_screen_pos;
146     return rect;
147 }
148 
GetGridImageRectangle() const149 Rectangle2D MapObject::GetGridImageRectangle() const
150 {
151     Rectangle2D rect;
152     rect.left = _tile_position.x - _img_grid_half_width;
153     rect.right = _tile_position.x + _img_grid_half_width;
154     rect.top = _tile_position.y - _img_grid_height;
155     rect.bottom = _tile_position.y;
156     return rect;
157 }
158 
Emote(const std::string & emote_name,vt_map::private_map::ANIM_DIRECTIONS dir)159 void MapObject::Emote(const std::string &emote_name, vt_map::private_map::ANIM_DIRECTIONS dir)
160 {
161     vt_global::EmoteHandler& emote_handler = vt_global::GlobalManager->GetEmoteHandler();
162     _emote_animation = emote_handler.GetEmoteAnimation(emote_name);
163 
164     if(!_emote_animation) {
165         PRINT_WARNING << "Invalid emote requested: " << emote_name << " for map object: "
166                       << GetObjectID() << std::endl;
167         return;
168     }
169 
170     // Make the offset depend on the sprite direction and emote animation.
171     emote_handler.GetEmoteOffset(_emote_screen_offset.x,
172                                  _emote_screen_offset.y,
173                                  emote_name,
174                                  dir);
175     // Scale the offsets for the map mode
176     _emote_screen_offset.x = _emote_screen_offset.x * MAP_ZOOM_RATIO;
177     _emote_screen_offset.y = _emote_screen_offset.y * MAP_ZOOM_RATIO;
178 
179     _emote_animation->ResetAnimation();
180     _emote_time = _emote_animation->GetAnimationLength();
181 }
182 
_UpdateEmote()183 void MapObject::_UpdateEmote()
184 {
185     if(!_emote_animation)
186         return;
187 
188     _emote_time -= static_cast<int32_t>(vt_system::SystemManager->GetUpdateTime());
189 
190     // Once the animation has reached its end, we dereference it
191     if(_emote_time <= 0) {
192         _emote_animation = nullptr;
193         _emote_time = 0;
194         return;
195     }
196 
197     // Otherwise, just update it
198     _emote_animation->Update();
199 }
200 
_DrawEmote()201 void MapObject::_DrawEmote()
202 {
203     if(!_emote_animation)
204         return;
205 
206     // Move the emote to the sprite head top, where the offset should applied from.
207     vt_video::VideoManager->MoveRelative(_emote_screen_offset.x,
208                                          -_img_screen_height + _emote_screen_offset.y);
209     _emote_animation->Draw();
210 }
211 
SetInteractionIcon(const std::string & animation_filename)212 void MapObject::SetInteractionIcon(const std::string& animation_filename)
213 {
214     if (_interaction_icon)
215         delete _interaction_icon;
216     _interaction_icon = new vt_video::AnimatedImage();
217     if (!_interaction_icon->LoadFromAnimationScript(animation_filename)) {
218         PRINT_WARNING << "Interaction icon animation filename couldn't be loaded: " << animation_filename << std::endl;
219     }
220 }
221 
DrawInteractionIcon()222 void MapObject::DrawInteractionIcon()
223 {
224     if (!_interaction_icon)
225         return;
226 
227     if (!MapObject::ShouldDraw())
228         return;
229 
230     MapMode* map_mode = MapMode::CurrentInstance();
231     vt_video::Color icon_color(1.0f, 1.0f, 1.0f, 0.0f);
232     float icon_alpha = 1.0f - (fabs(GetXPosition() - map_mode->GetCamera()->GetXPosition())
233                             + fabs(GetYPosition() - map_mode->GetCamera()->GetYPosition())) / INTERACTION_ICON_VISIBLE_RANGE;
234     if (icon_alpha < 0.0f)
235         icon_alpha = 0.0f;
236     icon_color.SetAlpha(icon_alpha);
237 
238     vt_video::VideoManager->MoveRelative(0, -GetImgScreenHeight());
239     _interaction_icon->Draw(icon_color);
240 }
241 
IsColliding(float x,float y)242 bool MapObject::IsColliding(float x, float y)
243 {
244     ObjectSupervisor* obj_sup = MapMode::CurrentInstance()->GetObjectSupervisor();
245     return obj_sup->DetectCollision(this, x, y);
246 }
247 
IsCollidingWith(MapObject * other_object)248 bool MapObject::IsCollidingWith(MapObject* other_object)
249 {
250      if (!other_object)
251         return false;
252 
253      if (_collision_mask == NO_COLLISION)
254         return false;
255 
256      if (other_object->GetCollisionMask() == NO_COLLISION)
257         return false;
258 
259     Rectangle2D other_rect = other_object->GetGridCollisionRectangle();
260     if (!GetGridCollisionRectangle().IntersectsWith(other_rect))
261         return false;
262 
263     return _collision_mask & other_object->GetCollisionMask();
264 }
265 
266 } // namespace private_map
267 
268 } // namespace vt_map
269