|
@@ -19,6 +19,7 @@
|
|
|
#include <OgreException.h>
|
|
#include <OgreException.h>
|
|
|
#include <OgreViewport.h>
|
|
#include <OgreViewport.h>
|
|
|
#include <OgreTechnique.h>
|
|
#include <OgreTechnique.h>
|
|
|
|
|
+#include <algorithm>
|
|
|
#include "core/Background2D.h"
|
|
#include "core/Background2D.h"
|
|
|
#include "core/CameraManager.h"
|
|
#include "core/CameraManager.h"
|
|
|
#include "core/EntityManager.h"
|
|
#include "core/EntityManager.h"
|
|
@@ -41,6 +42,8 @@ Background2D::Background2D():
|
|
|
scroll_type_(Background2D::NONE),
|
|
scroll_type_(Background2D::NONE),
|
|
|
scroll_seconds_(0),
|
|
scroll_seconds_(0),
|
|
|
scroll_current_seconds_(0),
|
|
scroll_current_seconds_(0),
|
|
|
|
|
+ scroll_target_entity_(nullptr),
|
|
|
|
|
+ scroll_follow_target_(false),
|
|
|
position_(Ogre::Vector2::ZERO),
|
|
position_(Ogre::Vector2::ZERO),
|
|
|
position_real_(Ogre::Vector2::ZERO),
|
|
position_real_(Ogre::Vector2::ZERO),
|
|
|
offset_(Ogre::Vector2::ZERO),
|
|
offset_(Ogre::Vector2::ZERO),
|
|
@@ -119,6 +122,11 @@ void Background2D::Hide(){scene_manager_->removeRenderQueueListener(this);}
|
|
|
void Background2D::Show(){scene_manager_->addRenderQueueListener(this);}
|
|
void Background2D::Show(){scene_manager_->addRenderQueueListener(this);}
|
|
|
|
|
|
|
|
void Background2D::Update(){
|
|
void Background2D::Update(){
|
|
|
|
|
+ if (has_pending_scroll_){
|
|
|
|
|
+ has_pending_scroll_ = false;
|
|
|
|
|
+ SetScroll(pending_scroll_);
|
|
|
|
|
+ }
|
|
|
|
|
+ UpdateShake(Timer::getSingleton().GetGameTimeDelta());
|
|
|
for (unsigned int i = 0; i < animation_played_.size(); ++ i){
|
|
for (unsigned int i = 0; i < animation_played_.size(); ++ i){
|
|
|
for (unsigned int j = 0; j < animations_.size(); ++ j){
|
|
for (unsigned int j = 0; j < animations_.size(); ++ j){
|
|
|
if (animations_[j]->GetName() == animation_played_[i].name){
|
|
if (animations_[j]->GetName() == animation_played_[i].name){
|
|
@@ -187,65 +195,217 @@ void Background2D::calculateScreenScale(){
|
|
|
|
|
|
|
|
void Background2D::OnResize(){
|
|
void Background2D::OnResize(){
|
|
|
calculateScreenScale();
|
|
calculateScreenScale();
|
|
|
- for (unsigned int i = 0; i < tiles_.size(); ++ i){
|
|
|
|
|
- Tile &tile(tiles_[i]);
|
|
|
|
|
- Ogre::Vector2 top_left(static_cast<Ogre::Real>(tile.x), static_cast<Ogre::Real>(- tile.y));
|
|
|
|
|
- Ogre::Vector2 top_right(
|
|
|
|
|
- static_cast<Ogre::Real>(tile.x + tile.width), static_cast<Ogre::Real>(top_left.y)
|
|
|
|
|
- );
|
|
|
|
|
- Ogre::Vector2 bottom_right(
|
|
|
|
|
- static_cast<Ogre::Real>(top_right.x), static_cast<Ogre::Real>(-(tile.y + tile.height))
|
|
|
|
|
- );
|
|
|
|
|
- Ogre::Vector2 bottom_left(
|
|
|
|
|
- static_cast<Ogre::Real>(top_left.x), static_cast<Ogre::Real>(bottom_right.y)
|
|
|
|
|
- );
|
|
|
|
|
- virtualScreenToWorldSpace(top_left);
|
|
|
|
|
- virtualScreenToWorldSpace(top_right);
|
|
|
|
|
- virtualScreenToWorldSpace(bottom_right);
|
|
|
|
|
- virtualScreenToWorldSpace(bottom_left);
|
|
|
|
|
- float new_x1 = top_left.x;
|
|
|
|
|
- float new_y1 = top_left.y;
|
|
|
|
|
- float new_x2 = top_right.x;
|
|
|
|
|
- float new_y2 = top_right.y;
|
|
|
|
|
- float new_x3 = bottom_right.x;
|
|
|
|
|
- float new_y3 = bottom_right.y;
|
|
|
|
|
- float new_x4 = bottom_left.x;
|
|
|
|
|
- float new_y4 = bottom_left.y;
|
|
|
|
|
- Ogre::HardwareVertexBufferSharedPtr vertex_buffer;
|
|
|
|
|
- if (tiles_[i].blending == VGears::B_ALPHA) vertex_buffer = alpha_vertex_buffer_;
|
|
|
|
|
- else if(tiles_[i].blending == VGears::B_ADD) vertex_buffer = add_vertex_buffer_;
|
|
|
|
|
- else if(tiles_[i].blending == VGears::B_SUBTRACT) vertex_buffer = subtract_vertex_buffer_;
|
|
|
|
|
- float* write_iterator
|
|
|
|
|
- = static_cast<float*>(vertex_buffer->lock(Ogre::HardwareBuffer::HBL_NORMAL));
|
|
|
|
|
- write_iterator += tiles_[i].start_vertex_index * TILE_VERTEX_INDEX_SIZE;
|
|
|
|
|
- *write_iterator ++ = new_x1;
|
|
|
|
|
- *write_iterator ++ = new_y1;
|
|
|
|
|
- write_iterator += 7;
|
|
|
|
|
- *write_iterator ++ = new_x2;
|
|
|
|
|
- *write_iterator ++ = new_y2;
|
|
|
|
|
- write_iterator += 7;
|
|
|
|
|
- *write_iterator ++ = new_x3;
|
|
|
|
|
- *write_iterator ++ = new_y3;
|
|
|
|
|
- write_iterator += 7;
|
|
|
|
|
- *write_iterator++ = new_x1;
|
|
|
|
|
- *write_iterator++ = new_y1;
|
|
|
|
|
- write_iterator += 7;
|
|
|
|
|
- *write_iterator ++ = new_x3;
|
|
|
|
|
- *write_iterator ++ = new_y3;
|
|
|
|
|
- write_iterator += 7;
|
|
|
|
|
- *write_iterator ++ = new_x4;
|
|
|
|
|
- *write_iterator ++ = new_y4;
|
|
|
|
|
- vertex_buffer->unlock();
|
|
|
|
|
|
|
+ for (unsigned int i = 0; i < tiles_.size(); ++ i) WriteTilePosition(tiles_[i]);
|
|
|
|
|
+ applyScroll();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void Background2D::WriteTilePosition(const Tile& tile){
|
|
|
|
|
+ Ogre::Vector2 top_left(static_cast<Ogre::Real>(tile.x), static_cast<Ogre::Real>(- tile.y));
|
|
|
|
|
+ Ogre::Vector2 top_right(
|
|
|
|
|
+ static_cast<Ogre::Real>(tile.x + tile.width), static_cast<Ogre::Real>(top_left.y)
|
|
|
|
|
+ );
|
|
|
|
|
+ Ogre::Vector2 bottom_right(
|
|
|
|
|
+ static_cast<Ogre::Real>(top_right.x), static_cast<Ogre::Real>(-(tile.y + tile.height))
|
|
|
|
|
+ );
|
|
|
|
|
+ Ogre::Vector2 bottom_left(
|
|
|
|
|
+ static_cast<Ogre::Real>(top_left.x), static_cast<Ogre::Real>(bottom_right.y)
|
|
|
|
|
+ );
|
|
|
|
|
+ virtualScreenToWorldSpace(top_left);
|
|
|
|
|
+ virtualScreenToWorldSpace(top_right);
|
|
|
|
|
+ virtualScreenToWorldSpace(bottom_right);
|
|
|
|
|
+ virtualScreenToWorldSpace(bottom_left);
|
|
|
|
|
+ // A tile that is not drawn has all its corners in the same point.
|
|
|
|
|
+ if (!tile.visible){
|
|
|
|
|
+ top_right = top_left;
|
|
|
|
|
+ bottom_right = top_left;
|
|
|
|
|
+ bottom_left = top_left;
|
|
|
|
|
+ }
|
|
|
|
|
+ Ogre::HardwareVertexBufferSharedPtr vertex_buffer;
|
|
|
|
|
+ if (tile.blending == VGears::B_ALPHA) vertex_buffer = alpha_vertex_buffer_;
|
|
|
|
|
+ else if (tile.blending == VGears::B_ADD) vertex_buffer = add_vertex_buffer_;
|
|
|
|
|
+ else if (tile.blending == VGears::B_SUBTRACT) vertex_buffer = subtract_vertex_buffer_;
|
|
|
|
|
+ else return;
|
|
|
|
|
+ float* write_iterator
|
|
|
|
|
+ = static_cast<float*>(vertex_buffer->lock(Ogre::HardwareBuffer::HBL_NORMAL));
|
|
|
|
|
+ write_iterator += tile.start_vertex_index * TILE_VERTEX_INDEX_SIZE;
|
|
|
|
|
+ *write_iterator ++ = top_left.x;
|
|
|
|
|
+ *write_iterator ++ = top_left.y;
|
|
|
|
|
+ write_iterator += 7;
|
|
|
|
|
+ *write_iterator ++ = top_right.x;
|
|
|
|
|
+ *write_iterator ++ = top_right.y;
|
|
|
|
|
+ write_iterator += 7;
|
|
|
|
|
+ *write_iterator ++ = bottom_right.x;
|
|
|
|
|
+ *write_iterator ++ = bottom_right.y;
|
|
|
|
|
+ write_iterator += 7;
|
|
|
|
|
+ *write_iterator ++ = top_left.x;
|
|
|
|
|
+ *write_iterator ++ = top_left.y;
|
|
|
|
|
+ write_iterator += 7;
|
|
|
|
|
+ *write_iterator ++ = bottom_right.x;
|
|
|
|
|
+ *write_iterator ++ = bottom_right.y;
|
|
|
|
|
+ write_iterator += 7;
|
|
|
|
|
+ *write_iterator ++ = bottom_left.x;
|
|
|
|
|
+ *write_iterator ++ = bottom_left.y;
|
|
|
|
|
+ vertex_buffer->unlock();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+bool Background2D::IsAreaStateOn(const int area, const int layers) const{
|
|
|
|
|
+ const auto it = area_states_on_.find(area);
|
|
|
|
|
+ return it != area_states_on_.end() && (it->second & static_cast<unsigned int>(layers)) != 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void Background2D::RefreshArea(const int area){
|
|
|
|
|
+ for (Tile& tile : tiles_){
|
|
|
|
|
+ if (tile.area != area) continue;
|
|
|
|
|
+ const bool visible = IsAreaStateOn(tile.area, tile.state);
|
|
|
|
|
+ if (visible == tile.visible) continue;
|
|
|
|
|
+ tile.visible = visible;
|
|
|
|
|
+ WriteTilePosition(tile);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void Background2D::ScriptAreaOn(const int area, const int layer){
|
|
|
|
|
+ LOG_TRIVIAL(
|
|
|
|
|
+ "Background2d area " + std::to_string(area) + " layer " + std::to_string(layer) + " on."
|
|
|
|
|
+ );
|
|
|
|
|
+ if (layer < 0 || layer > 31){
|
|
|
|
|
+ LOG_WARNING("Background2d layer " + std::to_string(layer) + " is out of range.");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ area_states_on_[area] |= 1u << layer;
|
|
|
|
|
+ RefreshArea(area);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void Background2D::ScriptAreaOff(const int area, const int layer){
|
|
|
|
|
+ LOG_TRIVIAL(
|
|
|
|
|
+ "Background2d area " + std::to_string(area) + " layer " + std::to_string(layer) + " off."
|
|
|
|
|
+ );
|
|
|
|
|
+ if (layer < 0 || layer > 31){
|
|
|
|
|
+ LOG_WARNING("Background2d layer " + std::to_string(layer) + " is out of range.");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const auto it = area_states_on_.find(area);
|
|
|
|
|
+ if (it != area_states_on_.end()) it->second &= ~(1u << layer);
|
|
|
|
|
+ RefreshArea(area);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+namespace{
|
|
|
|
|
+ // Random factors the original game applies to each bounce of a shake.
|
|
|
|
|
+ const float SHAKE_FACTORS[] = {
|
|
|
|
|
+ -1.0f, 1.14f, -1.34f, 0.61f, -0.49f, 0.64f, -0.25f, 0.47f, -1.20f, -0.01f, 1.14f, -0.86f,
|
|
|
|
|
+ 0.81f, -0.58f, 0.68f, -1.31f, 0.44f, -0.91f, 0.03f, -0.97f, 1.25f, -0.76f, 0.48f, -0.99f,
|
|
|
|
|
+ 1.34f, -0.85f, 0.55f, -0.65f, 0.07f, -0.83f
|
|
|
|
|
+ };
|
|
|
|
|
+ const size_t SHAKE_FACTOR_COUNT = sizeof(SHAKE_FACTORS) / sizeof(SHAKE_FACTORS[0]);
|
|
|
|
|
+
|
|
|
|
|
+ // Frames per second the shake times are given in.
|
|
|
|
|
+ const float SHAKE_FRAMES_PER_SECOND = 30.0f;
|
|
|
|
|
+
|
|
|
|
|
+ // Pixels of the original screen.
|
|
|
|
|
+ const float ORIGINAL_SCREEN_WIDTH = 320.0f;
|
|
|
|
|
+
|
|
|
|
|
+ // Quadratic in and out easing.
|
|
|
|
|
+ float ShakeEase(const float t){return t < 0.5f ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t);}
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void Background2D::ShakeAxis::Configure(const float new_amplitude, const float new_seconds){
|
|
|
|
|
+ amplitude = new_amplitude;
|
|
|
|
|
+ seconds = std::max(new_seconds, 1.0f / SHAKE_FRAMES_PER_SECOND);
|
|
|
|
|
+ stopping = false;
|
|
|
|
|
+ if (!running){
|
|
|
|
|
+ running = true;
|
|
|
|
|
+ StartBounce();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void Background2D::ShakeAxis::Stop(const float seconds_to_center){
|
|
|
|
|
+ if (!running) return;
|
|
|
|
|
+ stopping = true;
|
|
|
|
|
+ stop_seconds = std::max(seconds_to_center, 1.0f / SHAKE_FRAMES_PER_SECOND);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void Background2D::ShakeAxis::StartBounce(){
|
|
|
|
|
+ from = value;
|
|
|
|
|
+ elapsed = 0;
|
|
|
|
|
+ if (stopping){
|
|
|
|
|
+ to = 0;
|
|
|
|
|
+ duration = stop_seconds;
|
|
|
}
|
|
}
|
|
|
|
|
+ else{
|
|
|
|
|
+ // Half the amplitude, times the random factor, is the distance in pixels.
|
|
|
|
|
+ to = amplitude / 2 * SHAKE_FACTORS[step % SHAKE_FACTOR_COUNT];
|
|
|
|
|
+ ++ step;
|
|
|
|
|
+ duration = seconds;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+bool Background2D::ShakeAxis::Advance(const float delta){
|
|
|
|
|
+ if (!running) return false;
|
|
|
|
|
+ const float old_value = value;
|
|
|
|
|
+ elapsed += delta;
|
|
|
|
|
+ while (elapsed >= duration){
|
|
|
|
|
+ elapsed -= duration;
|
|
|
|
|
+ value = to;
|
|
|
|
|
+ if (stopping && to == 0){
|
|
|
|
|
+ // Back in the center: the shake is over.
|
|
|
|
|
+ *this = ShakeAxis();
|
|
|
|
|
+ return old_value != 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ StartBounce();
|
|
|
|
|
+ }
|
|
|
|
|
+ value = from + (to - from) * ShakeEase(elapsed / duration);
|
|
|
|
|
+ return value != old_value;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void Background2D::UpdateShake(const float delta){
|
|
|
|
|
+ if (!shake_x_.running && !shake_y_.running) return;
|
|
|
|
|
+ const bool x_changed = shake_x_.Advance(delta);
|
|
|
|
|
+ const bool y_changed = shake_y_.Advance(delta);
|
|
|
|
|
+ if (!x_changed && !y_changed) return;
|
|
|
|
|
+ const float scale = virtual_screen_size_.x / ORIGINAL_SCREEN_WIDTH;
|
|
|
|
|
+ shake_offset_ = Ogre::Vector2(shake_x_.value * scale, shake_y_.value * scale);
|
|
|
applyScroll();
|
|
applyScroll();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+void Background2D::ScriptShake(
|
|
|
|
|
+ const int type, const int x_amplitude, const int x_frames, const int y_amplitude,
|
|
|
|
|
+ const int y_frames
|
|
|
|
|
+){
|
|
|
|
|
+ LOG_TRIVIAL("Background2d shake type " + std::to_string(type) + ".");
|
|
|
|
|
+ const float x_seconds = x_frames / SHAKE_FRAMES_PER_SECOND;
|
|
|
|
|
+ const float y_seconds = y_frames / SHAKE_FRAMES_PER_SECOND;
|
|
|
|
|
+ switch (type){
|
|
|
|
|
+ case 0:
|
|
|
|
|
+ {
|
|
|
|
|
+ const float seconds = std::max(x_seconds, y_seconds);
|
|
|
|
|
+ shake_x_.Stop(seconds);
|
|
|
|
|
+ shake_y_.Stop(seconds);
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 1: shake_x_.Configure(static_cast<float>(x_amplitude), x_seconds); break;
|
|
|
|
|
+ case 2: shake_y_.Configure(static_cast<float>(y_amplitude), y_seconds); break;
|
|
|
|
|
+ case 3:
|
|
|
|
|
+ shake_x_.Configure(static_cast<float>(x_amplitude), x_seconds);
|
|
|
|
|
+ shake_y_.Configure(static_cast<float>(y_amplitude), y_seconds);
|
|
|
|
|
+ break;
|
|
|
|
|
+ default: LOG_WARNING("Background2d shake type " + std::to_string(type) + " is unknown.");
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void Background2D::ScriptAreaClear(const int area){
|
|
|
|
|
+ LOG_TRIVIAL("Background2d area " + std::to_string(area) + " cleared.");
|
|
|
|
|
+ area_states_on_.erase(area);
|
|
|
|
|
+ RefreshArea(area);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
void Background2D::Clear(){
|
|
void Background2D::Clear(){
|
|
|
scroll_entity_ = nullptr;
|
|
scroll_entity_ = nullptr;
|
|
|
scroll_position_start_ = Ogre::Vector2::ZERO;
|
|
scroll_position_start_ = Ogre::Vector2::ZERO;
|
|
|
scroll_position_end_ = Ogre::Vector2::ZERO;
|
|
scroll_position_end_ = Ogre::Vector2::ZERO;
|
|
|
scroll_seconds_ = 0;
|
|
scroll_seconds_ = 0;
|
|
|
scroll_current_seconds_ = 0;
|
|
scroll_current_seconds_ = 0;
|
|
|
|
|
+ scroll_target_entity_ = nullptr;
|
|
|
|
|
+ scroll_follow_target_ = false;
|
|
|
|
|
+ has_pending_scroll_ = false;
|
|
|
position_ = Ogre::Vector2::ZERO;
|
|
position_ = Ogre::Vector2::ZERO;
|
|
|
position_real_ = Ogre::Vector2::ZERO;
|
|
position_real_ = Ogre::Vector2::ZERO;
|
|
|
offset_ = Ogre::Vector2::ZERO;
|
|
offset_ = Ogre::Vector2::ZERO;
|
|
@@ -258,6 +418,10 @@ void Background2D::Clear(){
|
|
|
ScriptManager::getSingleton().ContinueScriptExecution(animation_played_[i].sync[j]);
|
|
ScriptManager::getSingleton().ContinueScriptExecution(animation_played_[i].sync[j]);
|
|
|
animation_played_.clear();
|
|
animation_played_.clear();
|
|
|
tiles_.clear();
|
|
tiles_.clear();
|
|
|
|
|
+ area_states_on_.clear();
|
|
|
|
|
+ shake_x_ = ShakeAxis();
|
|
|
|
|
+ shake_y_ = ShakeAxis();
|
|
|
|
|
+ shake_offset_ = Ogre::Vector2::ZERO;
|
|
|
DestroyVertexBuffers();
|
|
DestroyVertexBuffers();
|
|
|
CreateVertexBuffers();
|
|
CreateVertexBuffers();
|
|
|
}
|
|
}
|
|
@@ -266,17 +430,63 @@ void Background2D::ScriptAutoScrollToEntity(Entity* entity){scroll_entity_ = ent
|
|
|
|
|
|
|
|
Entity* Background2D::GetAutoScrollEntity() const{return scroll_entity_;}
|
|
Entity* Background2D::GetAutoScrollEntity() const{return scroll_entity_;}
|
|
|
|
|
|
|
|
-void Background2D::ScriptScrollToPlayer(const SCROLL_TYPE type, const unsigned int seconds){
|
|
|
|
|
|
|
+void Background2D::ScriptScrollToPlayer(const SCROLL_TYPE type, const float seconds){
|
|
|
Entity* player = EntityManager::getSingleton().ScriptGetPlayerEntity();
|
|
Entity* player = EntityManager::getSingleton().ScriptGetPlayerEntity();
|
|
|
if (player == nullptr) return;
|
|
if (player == nullptr) return;
|
|
|
- Ogre::Vector2 position(player->GetPosition().x, player->GetPosition().y);
|
|
|
|
|
|
|
+ scroll_entity_ = nullptr;
|
|
|
|
|
+ StartScroll(GetEntityScroll(player), type, seconds, player, type == Background2D::NONE);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void Background2D::ScriptScrollToEntity(
|
|
|
|
|
+ Entity* entity, const SCROLL_TYPE type, const float seconds
|
|
|
|
|
+){
|
|
|
|
|
+ if (entity == nullptr){
|
|
|
|
|
+ LOG_WARNING("Background2d can't scroll to an entity that doesn't exist.");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ scroll_entity_ = nullptr;
|
|
|
|
|
+ StartScroll(GetEntityScroll(entity), type, seconds, entity, true);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void Background2D::UpdateScrollTarget(){
|
|
|
|
|
+ if (scroll_target_entity_ != nullptr)
|
|
|
|
|
+ scroll_position_end_ = ClampScroll(GetEntityScroll(scroll_target_entity_));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const Ogre::Vector2 Background2D::GetEntityScroll(const Entity* entity) const{
|
|
|
|
|
+ CameraManager &camera_manager(CameraManager::getSingleton());
|
|
|
|
|
+ const Ogre::Vector3 view = camera_manager.ProjectPointToScreen(entity->GetPosition());
|
|
|
|
|
+ const Ogre::Vector2 scroll = GetScreenScroll();
|
|
|
|
|
+ const Ogre::Viewport* viewport(camera_manager.getViewport());
|
|
|
|
|
+ const float width = static_cast<float>(viewport->getActualWidth());
|
|
|
|
|
+ const float height = static_cast<float>(viewport->getActualHeight());
|
|
|
|
|
+ return Ogre::Vector2(
|
|
|
|
|
+ scroll.x + width / 2 - view.x, scroll.y + height / 2 - view.y
|
|
|
|
|
+ ) / screen_scale_;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void Background2D::StartScroll(
|
|
|
|
|
+ const Ogre::Vector2& position, const SCROLL_TYPE type, const float seconds,
|
|
|
|
|
+ Entity* target_entity, const bool follow_target
|
|
|
|
|
+){
|
|
|
|
|
+ // A newer scroll replaces an instant one that has not been applied yet.
|
|
|
|
|
+ has_pending_scroll_ = false;
|
|
|
|
|
+ // Finish the previous scroll first, without following its target.
|
|
|
|
|
+ scroll_target_entity_ = nullptr;
|
|
|
|
|
+ scroll_follow_target_ = false;
|
|
|
|
|
+ UnsetScroll();
|
|
|
|
|
+ scroll_target_entity_ = target_entity;
|
|
|
|
|
+ scroll_follow_target_ = follow_target;
|
|
|
|
|
+ if (type == Background2D::NONE || seconds <= 0){
|
|
|
|
|
+ SetScroll(position);
|
|
|
|
|
+ UnsetScroll();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
scroll_position_start_ = position_;
|
|
scroll_position_start_ = position_;
|
|
|
- scroll_position_end_ = position;
|
|
|
|
|
|
|
+ scroll_position_end_ = ClampScroll(position);
|
|
|
scroll_type_ = type;
|
|
scroll_type_ = type;
|
|
|
scroll_seconds_ = seconds;
|
|
scroll_seconds_ = seconds;
|
|
|
scroll_current_seconds_ = 0;
|
|
scroll_current_seconds_ = 0;
|
|
|
-
|
|
|
|
|
- return;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void Background2D::ScriptScrollToPosition(
|
|
void Background2D::ScriptScrollToPosition(
|
|
@@ -284,22 +494,37 @@ void Background2D::ScriptScrollToPosition(
|
|
|
){
|
|
){
|
|
|
LOG_TRIVIAL(
|
|
LOG_TRIVIAL(
|
|
|
"Background2d set scroll to position '"
|
|
"Background2d set scroll to position '"
|
|
|
- + Ogre::StringConverter::toString(Ogre::Vector2(x, y)) + "'."
|
|
|
|
|
|
|
+ + Ogre::StringConverter::toString(Ogre::Vector2(x, y)) + "', current '"
|
|
|
|
|
+ + Ogre::StringConverter::toString(position_) + "', player would be at '"
|
|
|
|
|
+ + (EntityManager::getSingleton().ScriptGetPlayerEntity() != nullptr
|
|
|
|
|
+ ? Ogre::StringConverter::toString(
|
|
|
|
|
+ GetEntityScroll(EntityManager::getSingleton().ScriptGetPlayerEntity())
|
|
|
|
|
+ )
|
|
|
|
|
+ : Ogre::String("none"))
|
|
|
|
|
+ + "', range " + Ogre::StringConverter::toString(range_.getMinimum()) + " "
|
|
|
|
|
+ + Ogre::StringConverter::toString(range_.getMaximum()) + "."
|
|
|
);
|
|
);
|
|
|
- Ogre::Vector2 position = Ogre::Vector2(x, y);
|
|
|
|
|
scroll_entity_ = nullptr;
|
|
scroll_entity_ = nullptr;
|
|
|
- if (type == Background2D::NONE){
|
|
|
|
|
- SetScroll(position);
|
|
|
|
|
|
|
+ if (type == Background2D::NONE || seconds <= 0){
|
|
|
|
|
+ // The scripts set a position and then scroll from it to another one, in the same frame.
|
|
|
|
|
+ // The camera is only moved at the end of the frame, so if another scroll comes first the
|
|
|
|
|
+ // movement starts from where the camera was, and there is no jump.
|
|
|
|
|
+ // TODO: Some scenes still show a small camera jump (mds7pb_1, when the NPCs go down the
|
|
|
|
|
+ // pinball machine and when Marlene says "Papa"), and the camera stays fixed after them
|
|
|
|
|
+ // until the script scrolls to the player. How the original game handles it is not known.
|
|
|
|
|
+ scroll_target_entity_ = nullptr;
|
|
|
|
|
+ scroll_follow_target_ = false;
|
|
|
|
|
+ UnsetScroll();
|
|
|
|
|
+ pending_scroll_ = Ogre::Vector2(x, y);
|
|
|
|
|
+ has_pending_scroll_ = true;
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- scroll_position_start_ = position_;
|
|
|
|
|
- scroll_position_end_ = position;
|
|
|
|
|
- scroll_type_ = type;
|
|
|
|
|
- scroll_seconds_ = seconds;
|
|
|
|
|
- scroll_current_seconds_ = 0;
|
|
|
|
|
|
|
+ StartScroll(Ogre::Vector2(x, y), type, seconds, nullptr, false);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int Background2D::ScriptScrollSync(){
|
|
int Background2D::ScriptScrollSync(){
|
|
|
|
|
+ // Nothing would ever wake the script up if there is no scroll to wait for.
|
|
|
|
|
+ if (scroll_type_ == Background2D::NONE) return 1;
|
|
|
ScriptId script = ScriptManager::getSingleton().GetCurrentScriptId();
|
|
ScriptId script = ScriptManager::getSingleton().GetCurrentScriptId();
|
|
|
LOG_TRIVIAL(
|
|
LOG_TRIVIAL(
|
|
|
"Wait Background2d scroll for function '"
|
|
"Wait Background2d scroll for function '"
|
|
@@ -322,6 +547,9 @@ void Background2D::ScriptOffset(const float x, const float y){
|
|
|
|
|
|
|
|
void Background2D::UnsetScroll(){
|
|
void Background2D::UnsetScroll(){
|
|
|
scroll_type_ = Background2D::NONE;
|
|
scroll_type_ = Background2D::NONE;
|
|
|
|
|
+ if (scroll_follow_target_) scroll_entity_ = scroll_target_entity_;
|
|
|
|
|
+ scroll_target_entity_ = nullptr;
|
|
|
|
|
+ scroll_follow_target_ = false;
|
|
|
for(unsigned int i = 0; i < scroll_sync_.size(); ++ i)
|
|
for(unsigned int i = 0; i < scroll_sync_.size(); ++ i)
|
|
|
ScriptManager::getSingleton().ContinueScriptExecution(scroll_sync_[i]);
|
|
ScriptManager::getSingleton().ContinueScriptExecution(scroll_sync_[i]);
|
|
|
scroll_sync_.clear();
|
|
scroll_sync_.clear();
|
|
@@ -344,22 +572,27 @@ void Background2D::SetScreenScroll(const Ogre::Vector2& position){
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void Background2D::SetScroll(const Ogre::Vector2& position){
|
|
void Background2D::SetScroll(const Ogre::Vector2& position){
|
|
|
- position_ = position;
|
|
|
|
|
- Ogre::Vector3 pos_3d(position_.x, position_.y, 0);
|
|
|
|
|
|
|
+ position_ = ClampScroll(position);
|
|
|
|
|
+ applyScroll();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const Ogre::Vector2 Background2D::ClampScroll(const Ogre::Vector2& position) const{
|
|
|
|
|
+ Ogre::Vector2 clamped = position;
|
|
|
|
|
+ Ogre::Vector3 pos_3d(position.x, position.y, 0);
|
|
|
if (!range_.contains(pos_3d)){
|
|
if (!range_.contains(pos_3d)){
|
|
|
Ogre::Vector3 max_3d(range_.getMaximum());
|
|
Ogre::Vector3 max_3d(range_.getMaximum());
|
|
|
Ogre::Vector3 min_3d(range_.getMinimum());
|
|
Ogre::Vector3 min_3d(range_.getMinimum());
|
|
|
Ogre::Vector2 max(max_3d.x, max_3d.y);
|
|
Ogre::Vector2 max(max_3d.x, max_3d.y);
|
|
|
Ogre::Vector2 min(min_3d.x, min_3d.y);
|
|
Ogre::Vector2 min(min_3d.x, min_3d.y);
|
|
|
- position_.makeCeil(min);
|
|
|
|
|
- position_.makeFloor(max);
|
|
|
|
|
|
|
+ clamped.makeCeil(min);
|
|
|
|
|
+ clamped.makeFloor(max);
|
|
|
}
|
|
}
|
|
|
- applyScroll();
|
|
|
|
|
|
|
+ return clamped;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void Background2D::applyScroll(){
|
|
void Background2D::applyScroll(){
|
|
|
if(cv_background2d_manual.GetB() != true){
|
|
if(cv_background2d_manual.GetB() != true){
|
|
|
- position_real_ = GetScreenScroll();
|
|
|
|
|
|
|
+ position_real_ = (GetScroll() + shake_offset_) * screen_scale_;
|
|
|
position_real_.x += offset_.x;
|
|
position_real_.x += offset_.x;
|
|
|
position_real_.y += offset_.y;
|
|
position_real_.y += offset_.y;
|
|
|
CameraManager::getSingleton().Set2DScroll(position_real_);
|
|
CameraManager::getSingleton().Set2DScroll(position_real_);
|
|
@@ -415,7 +648,11 @@ void Background2D::AddTile(const VGears::Tile& tile){
|
|
|
}
|
|
}
|
|
|
else depth = 0.9999f;
|
|
else depth = 0.9999f;
|
|
|
}
|
|
}
|
|
|
- AddTile(tile.destination, tile.width, tile.height, depth, tile.uv, tile.blending);
|
|
|
|
|
|
|
+ AddTile(
|
|
|
|
|
+ static_cast<int>(tile.destination.x), static_cast<int>(tile.destination.y),
|
|
|
|
|
+ tile.width, tile.height, depth, tile.uv.x, tile.uv.y, tile.uv.z, tile.uv.w, tile.blending,
|
|
|
|
|
+ tile.area, tile.state
|
|
|
|
|
+ );
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void Background2D::AddTile(
|
|
void Background2D::AddTile(
|
|
@@ -437,7 +674,8 @@ void Background2D::virtualScreenToWorldSpace(Ogre::Vector2& pos) const{
|
|
|
|
|
|
|
|
void Background2D::AddTile(
|
|
void Background2D::AddTile(
|
|
|
const int x, const int y, const int width, const int height, const float depth, const float u1,
|
|
const int x, const int y, const int width, const int height, const float depth, const float u1,
|
|
|
- const float v1, const float u2, const float v2, const Blending blending
|
|
|
|
|
|
|
+ const float v1, const float u2, const float v2, const Blending blending, const int area,
|
|
|
|
|
+ const int state
|
|
|
){
|
|
){
|
|
|
Ogre::RenderOperation render_op;
|
|
Ogre::RenderOperation render_op;
|
|
|
Ogre::HardwareVertexBufferSharedPtr vertex_buffer;
|
|
Ogre::HardwareVertexBufferSharedPtr vertex_buffer;
|
|
@@ -475,6 +713,9 @@ void Background2D::AddTile(
|
|
|
tile.height = height;
|
|
tile.height = height;
|
|
|
tile.start_vertex_index = render_op.vertexData->vertexCount;
|
|
tile.start_vertex_index = render_op.vertexData->vertexCount;
|
|
|
tile.blending = blending;
|
|
tile.blending = blending;
|
|
|
|
|
+ tile.area = area;
|
|
|
|
|
+ tile.state = state;
|
|
|
|
|
+ tile.visible = (area == 0) || IsAreaStateOn(area, state);
|
|
|
//size_t index(tiles_.size());
|
|
//size_t index(tiles_.size());
|
|
|
tiles_.push_back(tile);
|
|
tiles_.push_back(tile);
|
|
|
Ogre::Vector2 top_left(static_cast<Ogre::Real>(x), static_cast<Ogre::Real>(-y));
|
|
Ogre::Vector2 top_left(static_cast<Ogre::Real>(x), static_cast<Ogre::Real>(-y));
|
|
@@ -558,6 +799,8 @@ void Background2D::AddTile(
|
|
|
*write_iterator ++ = v2;
|
|
*write_iterator ++ = v2;
|
|
|
render_op.vertexData->vertexCount += TILE_VERTEX_COUNT;
|
|
render_op.vertexData->vertexCount += TILE_VERTEX_COUNT;
|
|
|
vertex_buffer->unlock();
|
|
vertex_buffer->unlock();
|
|
|
|
|
+ // Tiles of an area that is off are written at their position, and then collapsed.
|
|
|
|
|
+ if (!tiles_.back().visible) WriteTilePosition(tiles_.back());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void Background2D::UpdateTileUV(
|
|
void Background2D::UpdateTileUV(
|
|
@@ -766,11 +1009,17 @@ void Background2D::load(const VGears::Background2DFile::TileList& tiles){
|
|
|
VGears::Background2DFile::TileList::const_iterator it(tiles.begin());
|
|
VGears::Background2DFile::TileList::const_iterator it(tiles.begin());
|
|
|
VGears::Background2DFile::TileList::const_iterator it_end(tiles.end());
|
|
VGears::Background2DFile::TileList::const_iterator it_end(tiles.end());
|
|
|
size_t tile_index(0);
|
|
size_t tile_index(0);
|
|
|
|
|
+ size_t area_tiles(0);
|
|
|
while (it != it_end) {
|
|
while (it != it_end) {
|
|
|
AddTile(*it);
|
|
AddTile(*it);
|
|
|
load(tile_index ++, it->animations);
|
|
load(tile_index ++, it->animations);
|
|
|
|
|
+ if (it->area != 0) ++ area_tiles;
|
|
|
++ it;
|
|
++ it;
|
|
|
}
|
|
}
|
|
|
|
|
+ LOG_TRIVIAL(
|
|
|
|
|
+ "Background2D: " + std::to_string(tile_index) + " tiles loaded, " + std::to_string(area_tiles)
|
|
|
|
|
+ + " of them in scripted areas."
|
|
|
|
|
+ );
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void Background2D::load(const size_t tile_index, const VGears::AnimationMap& animations){
|
|
void Background2D::load(const size_t tile_index, const VGears::AnimationMap& animations){
|