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) 2009 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 #include <boost/archive/text_iarchive.hpp>
28 #include <boost/archive/text_oarchive.hpp>
29 #include <boost/serialization/export.hpp>
30 
31 #include "systems/base/drift_graphics_object.h"
32 
33 #include <string>
34 #include <vector>
35 
36 #include "systems/base/event_system.h"
37 #include "systems/base/graphics_object.h"
38 #include "systems/base/graphics_system.h"
39 #include "systems/base/rect.h"
40 #include "systems/base/surface.h"
41 #include "systems/base/system.h"
42 #include "utilities/graphics.h"
43 
44 namespace {
45 
ScaleAmplitude(int amplitude)46 double ScaleAmplitude(int amplitude) {
47   // So the amplitude of the curve in RealLive is weird. Some value close to
48   // 100 means one width of the screen, 1 is a vary large amount that I can't
49   // reliably measure, and values greater than 100 are increasingly smaller.  I
50   // can't reliably measure this because I suspect that RL deliberately
51   // introduces some randomness here. Oh well. There's probably some curve that
52   // fits this, but whatever. I think this might be a valid approximation:
53   int x = amplitude / 100;
54   return 1 / static_cast<double>(x);
55 }
56 
57 }  // namespace
58 
DriftGraphicsObject(System & system)59 DriftGraphicsObject::DriftGraphicsObject(System& system)
60     : system_(system), filename_(), surface_(), last_rendered_time_(0) {}
61 
DriftGraphicsObject(const DriftGraphicsObject & obj)62 DriftGraphicsObject::DriftGraphicsObject(const DriftGraphicsObject& obj)
63     : GraphicsObjectData(obj),
64       system_(obj.system_),
65       filename_(obj.filename_),
66       surface_(obj.surface_),
67       last_rendered_time_(0) {}
68 
DriftGraphicsObject(System & system,const std::string & filename)69 DriftGraphicsObject::DriftGraphicsObject(System& system,
70                                          const std::string& filename)
71     : system_(system), filename_(filename), surface_(), last_rendered_time_(0) {
72   LoadFile();
73 }
74 
~DriftGraphicsObject()75 DriftGraphicsObject::~DriftGraphicsObject() {}
76 
Render(const GraphicsObject & go,const GraphicsObject * parent,std::ostream * tree)77 void DriftGraphicsObject::Render(const GraphicsObject& go,
78                                  const GraphicsObject* parent,
79                                  std::ostream* tree) {
80   std::shared_ptr<const Surface> surface = CurrentSurface(go);
81   if (surface) {
82     int current_time = system_.event().GetTicks();
83     last_rendered_time_ = current_time;
84 
85     size_t count = go.GetDriftParticleCount();
86     bool use_animation = go.GetDriftUseAnimation();
87     int start_pattern = go.GetDriftStartPattern();
88     int end_pattern = go.GetDriftEndPattern();
89     int animation_time = go.GetDriftAnimationTime();
90     int yspeed = go.GetDriftYSpeed();
91     int period = go.GetDriftPeriod();
92     int amplitude = go.GetDriftAmplitude();
93     int use_drift = go.GetDriftUseDrift();
94     int drift_speed = go.GetDriftDriftSpeed();
95 
96     Rect bounding_box = go.GetDriftArea();
97     if (bounding_box.x() == -1) {
98       bounding_box = system_.graphics().screen_rect();
99     }
100 
101     double scaled_amplitude =
102         bounding_box.size().width() * ScaleAmplitude(amplitude);
103 
104     // Grab the drift object
105     if (particles_.size() < count) {
106       Particle p;
107       p.x = rand() % bounding_box.size().width();   // NOLINT
108       p.y = rand() % bounding_box.size().height();  // NOLINT
109       p.alpha = 255;
110       p.start_time = current_time;
111 
112       particles_.push_back(p);
113     }
114 
115     // Now that we have all the particles, update state and render each
116     // particle.
117     for (const Particle& particle : particles_) {
118       int pattern = start_pattern;
119       if (use_animation && end_pattern > start_pattern) {
120         int number_of_patterns = end_pattern - start_pattern + 1;
121         int frame_time = animation_time / number_of_patterns;
122         int frame_number = ((current_time - particle.start_time) / frame_time) %
123                            number_of_patterns;
124         pattern = start_pattern + frame_number;
125       }
126       Rect src = surface->GetPattern(pattern).rect;
127 
128       int dest_x = particle.x;
129       int dest_y = particle.y;
130 
131       // Add the base yspeed.
132       dest_y +=
133           bounding_box.size().height() *
134           (static_cast<double>((current_time - particle.start_time) % yspeed) /
135            static_cast<double>(yspeed));
136 
137       // Add the sine wave that defines how the particle moves back and forth.
138       if (period != 0 && amplitude != 0) {
139         double result = sin(
140             (static_cast<double>(current_time - particle.start_time) / period) *
141             (2 * 3.14));
142         dest_x += scaled_amplitude * result;
143       }
144 
145       // Add the left drift if we have this bit set.
146       if (use_drift) {
147         dest_x -= bounding_box.size().width() *
148                   (static_cast<double>((current_time - particle.start_time) %
149                                        drift_speed) /
150                    static_cast<double>(drift_speed));
151       }
152 
153       if (dest_x < 0)
154         dest_x += bounding_box.size().width();
155       else
156         dest_x %= bounding_box.size().width();
157 
158       if (dest_y < 0)
159         dest_y += bounding_box.size().height();
160       else
161         dest_y %= bounding_box.size().height();
162       Rect dest(bounding_box.origin() + Size(dest_x, dest_y), src.size());
163 
164       if (go.has_clip_rect())
165         ClipDestination(go.clip_rect(), src, dest);
166 
167       surface->RenderToScreen(src, dest, particle.alpha);
168     }
169   }
170 }
171 
PixelWidth(const GraphicsObject & rendering_properties)172 int DriftGraphicsObject::PixelWidth(
173     const GraphicsObject& rendering_properties) {
174   return rendering_properties.GetDriftArea().width();
175 }
176 
PixelHeight(const GraphicsObject & rendering_properties)177 int DriftGraphicsObject::PixelHeight(
178     const GraphicsObject& rendering_properties) {
179   return rendering_properties.GetDriftArea().height();
180 }
181 
Clone() const182 GraphicsObjectData* DriftGraphicsObject::Clone() const {
183   return new DriftGraphicsObject(*this);
184 }
185 
Execute(RLMachine & machine)186 void DriftGraphicsObject::Execute(RLMachine& machine) {
187   // We could theoretically redraw every time around the game loop, so
188   // throttle to once every 100ms.
189   int current_time = system_.event().GetTicks();
190   if (current_time - last_rendered_time_ > 10) {
191     system_.graphics().MarkScreenAsDirty(GUT_DISPLAY_OBJ);
192   }
193 }
194 
CurrentSurface(const GraphicsObject & rp)195 std::shared_ptr<const Surface> DriftGraphicsObject::CurrentSurface(
196     const GraphicsObject& rp) {
197   return surface_;
198 }
199 
ObjectInfo(std::ostream & tree)200 void DriftGraphicsObject::ObjectInfo(std::ostream& tree) {
201   tree << "  Drift image: " << filename_ << std::endl;
202 }
203 
LoadFile()204 void DriftGraphicsObject::LoadFile() {
205   surface_ = system_.graphics().GetSurfaceNamed(filename_);
206   surface_->EnsureUploaded();
207 }
208 
209 template <class Archive>
load(Archive & ar,unsigned int version)210 void DriftGraphicsObject::load(Archive& ar, unsigned int version) {
211   ar& boost::serialization::base_object<GraphicsObjectData>(*this) & filename_;
212 
213   LoadFile();
214 }
215 
216 // -----------------------------------------------------------------------
217 
218 template <class Archive>
save(Archive & ar,unsigned int version) const219 void DriftGraphicsObject::save(Archive& ar, unsigned int version) const {
220   ar& boost::serialization::base_object<GraphicsObjectData>(*this) & filename_;
221 }
222 
223 // -----------------------------------------------------------------------
224 
225 BOOST_CLASS_EXPORT(DriftGraphicsObject);
226 
227 // -----------------------------------------------------------------------
228 
229 // Explicit instantiations for text archives (since we hide the
230 // implementation)
231 
232 template void DriftGraphicsObject::save<boost::archive::text_oarchive>(
233     boost::archive::text_oarchive& ar,
234     unsigned int version) const;
235 
236 template void DriftGraphicsObject::load<boost::archive::text_iarchive>(
237     boost::archive::text_iarchive& ar,
238     unsigned int version);
239