1 #include <GL/glew.h>
2 #include <SFML/OpenGL.hpp>
3 
4 #include "main.h"
5 #include "wormHole.h"
6 #include "spaceship.h"
7 #include "scriptInterface.h"
8 
9 #include "glObjects.h"
10 #include "shaderRegistry.h"
11 
12 #define FORCE_MULTIPLIER          50.0
13 #define FORCE_MAX                 10000.0
14 #define ALPHA_MULTIPLIER          10.0
15 #define DEFAULT_COLLISION_RADIUS  2500
16 #define AVOIDANCE_MULTIPLIER      1.2
17 #define TARGET_SPREAD             500
18 
19 #if FEATURE_3D_RENDERING
20 struct VertexAndTexCoords
21 {
22     sf::Vector3f vertex;
23     sf::Vector2f texcoords;
24 };
25 #endif
26 
27 /// A wormhole object that drags objects toward it like a black hole, and then
28 /// teleports them to another point when they reach its center.
REGISTER_SCRIPT_SUBCLASS(WormHole,SpaceObject)29 REGISTER_SCRIPT_SUBCLASS(WormHole, SpaceObject)
30 {
31     /// Set the target of this wormhole
32     REGISTER_SCRIPT_CLASS_FUNCTION(WormHole, setTargetPosition);
33     REGISTER_SCRIPT_CLASS_FUNCTION(WormHole, getTargetPosition);
34     /// Set a function that will be called if a SpaceObject is teleported.
35     /// First argument given to the function will be the WormHole, the second the SpaceObject that has been teleported.
36     REGISTER_SCRIPT_CLASS_FUNCTION(WormHole, onTeleportation);
37 }
38 
39 REGISTER_MULTIPLAYER_CLASS(WormHole, "WormHole");
WormHole()40 WormHole::WormHole()
41 : SpaceObject(DEFAULT_COLLISION_RADIUS, "WormHole")
42 {
43     pathPlanner = PathPlannerManager::getInstance();
44     pathPlanner->addAvoidObject(this, (DEFAULT_COLLISION_RADIUS * AVOIDANCE_MULTIPLIER) );
45 
46     setRadarSignatureInfo(0.9, 0.0, 0.0);
47 
48     // Choose a texture to show on radar
49     radar_visual = irandom(1, 3);
50     registerMemberReplication(&radar_visual);
51 
52     // Create some overlaying clouds
53     for(int n=0; n<cloud_count; n++)
54     {
55         clouds[n].size = random(1024, 1024 * 4);
56         clouds[n].texture = irandom(1, 3);
57         clouds[n].offset = sf::Vector2f(0, 0);
58     }
59 }
60 
61 #if FEATURE_3D_RENDERING
draw3DTransparent()62 void WormHole::draw3DTransparent()
63 {
64     ShaderRegistry::ScopedShader shader(ShaderRegistry::Shaders::Billboard);
65     glTranslatef(-getPosition().x, -getPosition().y, 0);
66 
67     std::array<VertexAndTexCoords, 4> quad{
68         sf::Vector3f(), {0.f, 1.f},
69         sf::Vector3f(), {1.f, 1.f},
70         sf::Vector3f(), {1.f, 0.f},
71         sf::Vector3f(), {0.f, 0.f}
72     };
73 
74     gl::ScopedVertexAttribArray positions(shader.get().attribute(ShaderRegistry::Attributes::Position));
75     gl::ScopedVertexAttribArray texcoords(shader.get().attribute(ShaderRegistry::Attributes::Texcoords));
76 
77     for(int n=0; n<cloud_count; n++)
78     {
79         NebulaCloud& cloud = clouds[n];
80 
81         sf::Vector3f position = sf::Vector3f(getPosition().x, getPosition().y, 0) + sf::Vector3f(cloud.offset.x, cloud.offset.y, 0);
82         float size = cloud.size;
83 
84         float distance = sf::length(camera_position - position);
85         float alpha = 1.0 - (distance / 10000.0f);
86         if (alpha < 0.0)
87             continue;
88 
89         glBindTexture(GL_TEXTURE_2D, textureManager.getTexture("wormHole" + string(cloud.texture) + ".png")->getNativeHandle());
90         glUniform4f(shader.get().uniform(ShaderRegistry::Uniforms::Color), alpha * 0.8f, alpha * 0.8f, alpha * 0.8f, size);
91 
92         glVertexAttribPointer(positions.get(), 3, GL_FLOAT, GL_FALSE, sizeof(VertexAndTexCoords), (GLvoid*)quad.data());
93         glVertexAttribPointer(texcoords.get(), 2, GL_FLOAT, GL_FALSE, sizeof(VertexAndTexCoords), (GLvoid*)((char*)quad.data() + sizeof(sf::Vector3f)));
94         std::initializer_list<uint8_t> indices = { 0, 2, 1, 0, 3, 2 };
95         glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, std::begin(indices));
96     }
97 }
98 #endif//FEATURE_3D_RENDERING
99 
100 
drawOnRadar(sf::RenderTarget & window,sf::Vector2f position,float scale,float rotation,bool long_range)101 void WormHole::drawOnRadar(sf::RenderTarget& window, sf::Vector2f position, float scale, float rotation, bool long_range)
102 {
103     sf::Sprite object_sprite;
104     textureManager.setTexture(object_sprite, "wormHole" + string(radar_visual) + ".png");
105     object_sprite.setRotation(getRotation());
106     object_sprite.setPosition(position);
107     float size = getRadius() * scale / object_sprite.getTextureRect().width * 3.0;
108     object_sprite.setScale(size, size);
109     object_sprite.setColor(sf::Color(255, 255, 255));
110     window.draw(object_sprite, sf::RenderStates(sf::BlendAdd));
111 }
112 
113 // Draw a line toward the target position
drawOnGMRadar(sf::RenderTarget & window,sf::Vector2f position,float scale,float rotation,bool long_range)114 void WormHole::drawOnGMRadar(sf::RenderTarget& window, sf::Vector2f position, float scale, float rotation, bool long_range)
115 {
116     sf::VertexArray a(sf::Lines, 2);
117     a[0].position = position;
118     a[1].position = position + (target_position - getPosition()) * scale;
119     a[0].color = sf::Color(255, 255, 255, 32);
120     window.draw(a);
121 
122     sf::CircleShape range_circle(getRadius() * scale);
123     range_circle.setOrigin(getRadius() * scale, getRadius() * scale);
124     range_circle.setPosition(position);
125     range_circle.setFillColor(sf::Color::Transparent);
126     range_circle.setOutlineColor(sf::Color(255, 255, 255, 32));
127     range_circle.setOutlineThickness(2.0);
128     window.draw(range_circle);
129 }
130 
131 
update(float delta)132 void WormHole::update(float delta)
133 {
134     update_delta = delta;
135 }
136 
collide(Collisionable * target,float collision_force)137 void WormHole::collide(Collisionable* target, float collision_force)
138 {
139     if (update_delta == 0.0)
140         return;
141 
142     P<SpaceObject> obj = P<Collisionable>(target);
143     if (!obj) return;
144     if (!obj->hasWeight()) { return; } // the object is not affected by gravitation
145 
146     sf::Vector2f diff = getPosition() - target->getPosition();
147     float distance = sf::length(diff);
148     float force = (getRadius() * getRadius() * FORCE_MULTIPLIER) / (distance * distance);
149 
150     P<SpaceShip> spaceship = P<Collisionable>(target);
151 
152     // Warp postprocessor-alpha is calculated using alpha = (1 - (delay/10))
153     if (spaceship)
154         spaceship->wormhole_alpha = ((distance / getRadius()) * ALPHA_MULTIPLIER);
155 
156     if (force > FORCE_MAX)
157     {
158         force = FORCE_MAX;
159         if (isServer())
160             target->setPosition( (target_position +
161                                   sf::Vector2f(random(-TARGET_SPREAD, TARGET_SPREAD),
162                                                random(-TARGET_SPREAD, TARGET_SPREAD))));
163         if (on_teleportation.isSet())
164         {
165             on_teleportation.call<void>(P<WormHole>(this), obj);
166         }
167         if (spaceship)
168         {
169             spaceship->wormhole_alpha = 0.0;
170         }
171     }
172 
173     // TODO: Escaping is impossible. Change setPosition to something Newtonianish.
174     target->setPosition(target->getPosition() + diff / distance * update_delta * force);
175 }
176 
setTargetPosition(sf::Vector2f v)177 void WormHole::setTargetPosition(sf::Vector2f v)
178 {
179     target_position = v;
180 }
181 
getTargetPosition()182 sf::Vector2f WormHole::getTargetPosition()
183 {
184     return target_position;
185 }
186 
onTeleportation(ScriptSimpleCallback callback)187 void WormHole::onTeleportation(ScriptSimpleCallback callback)
188 {
189     this->on_teleportation = callback;
190 }
191