Skip to content

Commit

Permalink
Adding pixel scrolling support
Browse files Browse the repository at this point in the history
Removing debug code, polishing code

+ Added support for .relative and .centered options for Absolute Pixel pan. Needed to bitmask parameter[4] in order to check for those.

= Renamed a few commands
= Added comments for some functions
- Removed debug code

Adjusting the speed formula

Now it works very well, even has differnet speeds for both H and V. However since Maniac internally uses doubles for screen panning, we might have to do some more work...

Revert "Adjusting the speed formula"

This reverts commit 52fa842.

Adjusting the speed formula Again

Now everything works as it should.

Time to use doubles for everything

Corrected interpolation, and proper scrolling speed

Correct formula for absolute positioning

Absolute Cam Panning now works

Changed some naming scheme

First things to do for Pixel Scrolling

Adding support for commands

However scrolling is a bit jaggy compared to ManiacPatch
  • Loading branch information
ToolMan2k authored and Ghabry committed Jul 6, 2024
1 parent ff4c302 commit 3579ea4
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 5 deletions.
37 changes: 37 additions & 0 deletions src/game_interpreter_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,15 @@ bool Game_Interpreter_Map::CommandPanScreen(lcf::rpg::EventCommand const& com) {
int speed;
bool waiting_pan_screen = false;

// Maniac has new functions for pixel scrolling, which also have X and Y offsets
bool is_maniac = Player::IsPatchManiac();
int h;
int v;
double h_speed;
double v_speed;
bool centered = false;
bool relative = false;

auto& player = *Main_Data::game_player;

switch (com.parameters[0]) {
Expand Down Expand Up @@ -636,6 +645,34 @@ bool Game_Interpreter_Map::CommandPanScreen(lcf::rpg::EventCommand const& com) {
distance /= SCREEN_TILE_SIZE;
break;
}
if (is_maniac && com.parameters.size() > 5) {
h = ValueOrVariableBitfield(com, 1, 0, 2);
v = ValueOrVariableBitfield(com, 1, 1, 3);
waiting_pan_screen = (com.parameters[4] & 0x01) != 0;
speed = ValueOrVariableBitfield(com, 1, 2, 5);
switch (com.parameters[0]) {
case 4: // Relative Pixel Pan (speed)
centered = false;
relative = true;
player.StartPixelPan(h, v, speed, false, centered, relative);
break;
case 5: // Relative Pixel Pan (interpolated)
centered = false;
relative = true;
player.StartPixelPan(h, v, speed, true, centered, relative);
break;
case 6: // Absolute Pixel Pan (speed)
centered = (com.parameters[4] & 0x02) != 0;
relative = (com.parameters[4] & 0x04) != 0;
player.StartPixelPan(h, v, speed, false, centered, relative);
break;
case 7: // Absolute Pixel Pan (interpolated)
centered = (com.parameters[4] & 0x02) != 0;
relative = (com.parameters[4] & 0x04) != 0;
player.StartPixelPan(h, v, speed, true, centered, relative);
break;
}
}

if (waiting_pan_screen) {
// RPG_RT uses the max wait for all pending pan commands, not just the current one.
Expand Down
106 changes: 101 additions & 5 deletions src/game_player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ void Game_Player::MoveTo(int map_id, int x, int y) {
data()->pan_finish_y = GetDefaultPanY();
data()->pan_current_x = GetDefaultPanX();
data()->pan_current_y = GetDefaultPanY();
maniac_pan_current_x = static_cast<double>(GetDefaultPanX());
maniac_pan_current_y = static_cast<double>(GetDefaultPanY());

ResetAnimation();

Expand Down Expand Up @@ -770,6 +772,8 @@ void Game_Player::UnlockPan() {
}

void Game_Player::StartPan(int direction, int distance, int speed) {
bool is_maniac = Player::IsPatchManiac();

distance *= SCREEN_TILE_SIZE;

if (direction == PanUp) {
Expand All @@ -786,20 +790,88 @@ void Game_Player::StartPan(int direction, int distance, int speed) {
data()->pan_finish_x = new_pan;
}

// Maniac uses separate horizontal/vertical pan doubles for everything
if (is_maniac) {
data()->maniac_horizontal_pan_speed = static_cast<double>(2 << speed);
data()->maniac_vertical_pan_speed = static_cast<double>(2 << speed);
}
data()->pan_speed = 2 << speed;
}

void Game_Player::StartPixelPan(int h, int v, int speed, bool interpolated, bool centered, bool relative) {
const bool is_maniac = Player::IsPatchManiac();

if (!is_maniac) {
return;
}

h *= TILE_SIZE;
v *= TILE_SIZE;

int new_pan_x;
int new_pan_y;

double h_speed;
double v_speed;

int pan_current_x = data()->pan_current_x;
int pan_current_y = data()->pan_current_y;
maniac_pan_current_x = static_cast<double>(pan_current_x);
maniac_pan_current_y = static_cast<double>(pan_current_y);

if (relative) {
new_pan_x = data()->pan_finish_x - h;
new_pan_y = data()->pan_finish_y - v;
} else {
if (centered) {
new_pan_x = GetSpriteX() + GetDefaultPanX() - h;
new_pan_y = GetSpriteY() + GetDefaultPanY() - v;
} else {
new_pan_x = GetSpriteX() - h;
new_pan_y = GetSpriteY() - v;
}
}

if (speed == 0) {
// Instant pan if speed is zero
h_speed = std::abs((static_cast<double>(new_pan_x) - maniac_pan_current_x));
v_speed = std::abs((static_cast<double>(new_pan_y) - maniac_pan_current_y));
} else if (interpolated) {
// Interpolate distance by number of frames
h_speed = std::abs((static_cast<double>(new_pan_x) - maniac_pan_current_x)) / (speed + 1);
v_speed = std::abs((static_cast<double>(new_pan_y) - maniac_pan_current_y)) / (speed + 1);
} else {
// Multiply speed by 0.001
h_speed = std::max(static_cast<double>(speed * TILE_SIZE * 0.001), 1.0);
v_speed = std::max(static_cast<double>(speed * TILE_SIZE * 0.001), 1.0);
}

data()->pan_finish_x = new_pan_x;
data()->pan_finish_y = new_pan_y;
data()->maniac_horizontal_pan_speed = h_speed;
data()->maniac_vertical_pan_speed = v_speed;
}

void Game_Player::ResetPan(int speed) {
bool is_maniac = Player::IsPatchManiac();
data()->pan_finish_x = GetDefaultPanX();
data()->pan_finish_y = GetDefaultPanY();
// Maniac uses separate horizontal/vertical pan doubles for everything
if (is_maniac) {
data()->maniac_horizontal_pan_speed = static_cast<double>(2 << speed);
data()->maniac_vertical_pan_speed = static_cast<double>(2 << speed);
}
data()->pan_speed = 2 << speed;
}

int Game_Player::GetPanWait() {
bool is_maniac = Player::IsPatchManiac();
const auto distance = std::max(
std::abs(data()->pan_current_x - data()->pan_finish_x),
std::abs(data()->pan_current_y - data()->pan_finish_y));
const auto speed = data()->pan_speed;
const auto speed = !is_maniac ? data()->pan_speed : static_cast<int>(std::max(
std::abs(data()->maniac_horizontal_pan_speed),
std::abs(data()->maniac_vertical_pan_speed)));
assert(speed > 0);
return distance / speed + (distance % speed != 0);
}
Expand All @@ -808,14 +880,38 @@ void Game_Player::UpdatePan() {
if (!IsPanActive())
return;

bool is_maniac = Player::IsPatchManiac();
const int step = data()->pan_speed;
const int pan_remain_x = data()->pan_current_x - data()->pan_finish_x;
const int pan_remain_y = data()->pan_current_y - data()->pan_finish_y;

int dx = std::min(step, std::abs(pan_remain_x));
dx = pan_remain_x >= 0 ? dx : -dx;
int dy = std::min(step, std::abs(pan_remain_y));
dy = pan_remain_y >= 0 ? dy : -dy;
// Maniac
const double step_x = data()->maniac_horizontal_pan_speed;
const double step_y = data()->maniac_vertical_pan_speed;

int dx;
int dy;
if (is_maniac) {
// Maniac uses doubles for smoother screen scrolling
double ddx = std::min(step_x, std::abs(static_cast<double>(pan_remain_x)));
double ddy = std::min(step_y, std::abs(static_cast<double>(pan_remain_y)));

ddx = pan_remain_x >= 0 ? ddx : -ddx;
ddy = pan_remain_y >= 0 ? ddy : -ddy;

maniac_pan_current_x -= ddx;
maniac_pan_current_y -= ddy;

// Depending on the position decimal, floor or ceil the value.
dx = std::round(std::abs(maniac_pan_current_x)) == std::ceil(std::abs(maniac_pan_current_x)) ? static_cast<int>(std::floor(ddx)) : static_cast<int>(std::ceil(ddx));
dy = std::round(std::abs(maniac_pan_current_y)) == std::ceil(std::abs(maniac_pan_current_y)) ? static_cast<int>(std::floor(ddy)) : static_cast<int>(std::ceil(ddy));
} else {
dx = std::min(step, std::abs(pan_remain_x));
dy = std::min(step, std::abs(pan_remain_y));

dx = pan_remain_x >= 0 ? dx : -dx;
dy = pan_remain_y >= 0 ? dy : -dy;
}

int screen_x = Game_Map::GetPositionX();
int screen_y = Game_Map::GetPositionY();
Expand Down
5 changes: 5 additions & 0 deletions src/game_player.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,14 @@ class Game_Player : public Game_PlayerBase {
static int GetDefaultPanX();
static int GetDefaultPanY();

// Maniac uses these coordinates for smooth panning
double maniac_pan_current_x;
double maniac_pan_current_y;

void LockPan();
void UnlockPan();
void StartPan(int direction, int distance, int speed);
void StartPixelPan(int h, int v, int speed, bool interpolated, bool centered, bool relative);
void ResetPan(int speed);

/** @return how many frames it'll take to finish the current pan */
Expand Down

0 comments on commit 3579ea4

Please sign in to comment.