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