1 // -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi:tw=80:et:ts=2:sts=2
3 //
4 // -----------------------------------------------------------------------
5 //
6 // This file is part of RLVM, a RealLive virtual machine clone.
7 //
8 // -----------------------------------------------------------------------
9 //
10 // Copyright (C) 2006 Elliot Glaysher
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
25 //
26 // -----------------------------------------------------------------------
27 
28 #include "systems/base/graphics_object_data.h"
29 
30 #include <ostream>
31 
32 #include "systems/base/graphics_object.h"
33 #include "systems/base/graphics_object_of_file.h"
34 #include "systems/base/surface.h"
35 #include "systems/base/rect.h"
36 
37 // -----------------------------------------------------------------------
38 // GraphicsObjectData
39 // -----------------------------------------------------------------------
40 
GraphicsObjectData()41 GraphicsObjectData::GraphicsObjectData()
42     : after_animation_(AFTER_NONE),
43       owned_by_(NULL),
44       currently_playing_(false),
45       animation_finished_(false) {}
46 
GraphicsObjectData(const GraphicsObjectData & obj)47 GraphicsObjectData::GraphicsObjectData(const GraphicsObjectData& obj)
48     : after_animation_(obj.after_animation_),
49       owned_by_(NULL),
50       currently_playing_(obj.currently_playing_),
51       animation_finished_(false) {}
52 
~GraphicsObjectData()53 GraphicsObjectData::~GraphicsObjectData() {}
54 
Render(const GraphicsObject & go,const GraphicsObject * parent,std::ostream * tree)55 void GraphicsObjectData::Render(const GraphicsObject& go,
56                                 const GraphicsObject* parent,
57                                 std::ostream* tree) {
58   std::shared_ptr<const Surface> surface = CurrentSurface(go);
59   if (surface) {
60     Rect src = SrcRect(go);
61     Rect dst = DstRect(go, parent);
62     int alpha = GetRenderingAlpha(go, parent);
63 
64     if (go.GetButtonUsingOverides()) {
65       // Tacked on side channel that lets a ButtonObjectSelectLongOperation
66       // tweak the x/y coordinates of dst. There isn't really a better place to
67       // put this. It can't go in dstRect() because the LongOperation also
68       // consults the data from dstRect().
69       dst = Rect(dst.origin() + Size(go.GetButtonXOffsetOverride(),
70                                      go.GetButtonYOffsetOverride()),
71                  dst.size());
72     }
73 
74     if (tree) {
75       ObjectInfo(*tree);
76       *tree << "  Rendering " << src << " to " << dst << std::endl;
77       if (parent) {
78         *tree << "  Parent Properties: ";
79         PrintGraphicsObjectToTree(*parent, tree);
80         *tree << std::endl;
81       }
82 
83       *tree << "  Properties: ";
84       if (alpha != 255)
85         *tree << "(alpha=" << alpha << ") ";
86       PrintGraphicsObjectToTree(go, tree);
87       *tree << std::endl;
88 
89       if (parent) {
90         *tree << "  Parent Mutators: ";
91         PrintStringVector(parent->GetMutatorNames(), tree);
92         *tree << std::endl;
93       }
94 
95       *tree << "  Mutators: ";
96       PrintStringVector(go.GetMutatorNames(), tree);
97       *tree << std::endl;
98     }
99 
100     if (parent && parent->has_own_clip_rect()) {
101       // In Little Busters, a parent clip rect is used to clip text scrolling
102       // in the battle system. rlvm has the concept of parent objects badly
103       // hacked in, and that means we can't directly apply the own clip
104       // rect. Instead we have to calculate this in terms of the screen
105       // coordinates and then apply that as a global clip rect.
106       Point parent_start(parent->x() + parent->GetXAdjustmentSum(),
107                          parent->y() + parent->GetYAdjustmentSum());
108       Rect full_parent_clip =
109           Rect(parent_start + parent->own_clip_rect().origin(),
110                parent->own_clip_rect().size());
111 
112       Rect clipped_dest = dst.Intersection(full_parent_clip);
113       Rect inset = dst.GetInsetRectangle(clipped_dest);
114       dst = clipped_dest;
115       src = src.ApplyInset(inset);
116 
117       if (tree) {
118         *tree << "  Parent Own Clipping Rect: " << parent->own_clip_rect()
119               << std::endl
120               << "  After clipping: " << src << " to " << dst << std::endl;
121       }
122     }
123 
124     if (go.has_own_clip_rect()) {
125       dst = dst.ApplyInset(go.own_clip_rect());
126       src = src.ApplyInset(go.own_clip_rect());
127 
128       if (tree) {
129         *tree << "  Internal Clipping Rect: " << go.own_clip_rect() << std::endl
130               << "  After internal clipping: " << src << " to " << dst
131               << std::endl;
132       }
133     }
134 
135     // Perform the object clipping.
136     if (go.has_clip_rect()) {
137       Rect clipped_dest = dst.Intersection(go.clip_rect());
138 
139       // Do nothing if object falls wholly outside clip area
140       if (clipped_dest.is_empty())
141         return;
142 
143       // Adjust the source rectangle
144       Rect inset = dst.GetInsetRectangle(clipped_dest);
145 
146       dst = clipped_dest;
147       src = src.ApplyInset(inset);
148 
149       if (tree) {
150         *tree << "  Clipping Rect: " << go.clip_rect() << std::endl
151               << "  After clipping: " << src << " to " << dst << std::endl;
152       }
153     }
154 
155     // TODO(erg): Do we want to skip this if no alpha?
156     surface->RenderToScreenAsObject(go, src, dst, alpha);
157   }
158 }
159 
LoopAnimation()160 void GraphicsObjectData::LoopAnimation() {}
161 
EndAnimation()162 void GraphicsObjectData::EndAnimation() {
163   // Set first, because we may deallocate this by one of our actions
164   currently_playing_ = false;
165 
166   switch (after_animation_) {
167     case AFTER_NONE:
168       animation_finished_ = true;
169       break;
170     case AFTER_CLEAR:
171       if (owned_by_)
172         owned_by_->FreeObjectData();
173       break;
174     case AFTER_LOOP: {
175       // Reset from the beginning
176       currently_playing_ = true;
177       LoopAnimation();
178       break;
179     }
180   }
181 }
182 
PrintGraphicsObjectToTree(const GraphicsObject & go,std::ostream * tree)183 void GraphicsObjectData::PrintGraphicsObjectToTree(const GraphicsObject& go,
184                                                    std::ostream* tree) {
185   if (go.mono())
186     *tree << "(mono) ";
187   if (go.invert())
188     *tree << "(invert) ";
189   if (go.light())
190     *tree << "(light=" << go.light() << ") ";
191   if (go.tint() != RGBColour::Black())
192     *tree << "(tint=" << go.tint() << ") ";
193   if (go.colour() != RGBAColour::Clear())
194     *tree << "(colour=" << go.colour() << ") ";
195   if (go.composite_mode())
196     *tree << "(composite=" << go.composite_mode() << ") ";
197   if (go.origin_x())
198     *tree << "(origin_x=" << go.origin_x() << ") ";
199   if (go.origin_y())
200     *tree << "(origin_y=" << go.origin_y() << ") ";
201 }
202 
PrintStringVector(const std::vector<std::string> & names,std::ostream * tree)203 void GraphicsObjectData::PrintStringVector(
204     const std::vector<std::string>& names,
205     std::ostream* tree) {
206   bool first = true;
207 
208   for (auto const& name : names) {
209     if (!first)
210       *tree << ", ";
211     else
212       first = false;
213 
214     *tree << name;
215   }
216 }
217 
SrcRect(const GraphicsObject & go)218 Rect GraphicsObjectData::SrcRect(const GraphicsObject& go) {
219   return CurrentSurface(go)->GetPattern(go.GetPattNo()).rect;
220 }
221 
DstOrigin(const GraphicsObject & go)222 Point GraphicsObjectData::DstOrigin(const GraphicsObject& go) {
223   if (go.origin_x() || go.origin_y()) {
224     return Point(go.origin_x(), go.origin_y());
225   }
226 
227   std::shared_ptr<const Surface> surface = CurrentSurface(go);
228   if (surface) {
229     return Point(surface->GetPattern(go.GetPattNo()).originX,
230                  surface->GetPattern(go.GetPattNo()).originY);
231   }
232 
233   return Point();
234 }
235 
DstRect(const GraphicsObject & go,const GraphicsObject * parent)236 Rect GraphicsObjectData::DstRect(const GraphicsObject& go,
237                                  const GraphicsObject* parent) {
238   Point origin = DstOrigin(go);
239   Rect src = SrcRect(go);
240 
241   int center_x =
242       go.x() + go.GetXAdjustmentSum() - origin.x() + (src.width() / 2.0f);
243   int center_y =
244       go.y() + go.GetYAdjustmentSum() - origin.y() + (src.height() / 2.0f);
245 
246   float second_factor_x = 1.0f;
247   float second_factor_y = 1.0f;
248   if (parent) {
249     center_x += parent->x() + parent->GetXAdjustmentSum();
250     center_y += parent->y() + parent->GetYAdjustmentSum();
251 
252     second_factor_x = parent->GetWidthScaleFactor();
253     second_factor_y = parent->GetHeightScaleFactor();
254   }
255 
256   int half_real_width =
257       (src.width() * second_factor_x * go.GetWidthScaleFactor()) / 2.0f;
258   int half_real_height =
259       (src.height() * second_factor_y * go.GetHeightScaleFactor()) / 2.0f;
260 
261   int xPos1 = center_x - half_real_width;
262   int yPos1 = center_y - half_real_height;
263   int xPos2 = center_x + half_real_width;
264   int yPos2 = center_y + half_real_height;
265 
266   return Rect::GRP(xPos1, yPos1, xPos2, yPos2);
267 }
268 
GetRenderingAlpha(const GraphicsObject & go,const GraphicsObject * parent)269 int GraphicsObjectData::GetRenderingAlpha(const GraphicsObject& go,
270                                           const GraphicsObject* parent) {
271   if (!parent) {
272     return go.GetComputedAlpha();
273   } else {
274     return int((parent->GetComputedAlpha() / 255.0f) *
275                (go.GetComputedAlpha() / 255.0f) * 255);
276   }
277 }
278 
IsAnimation() const279 bool GraphicsObjectData::IsAnimation() const { return false; }
280 
PlaySet(int set)281 void GraphicsObjectData::PlaySet(int set) {}
282 
IsParentLayer() const283 bool GraphicsObjectData::IsParentLayer() const { return false; }
284