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