Skip to content

Commit

Permalink
Merge pull request #3245 from ToolMan2k/PixelScrolling
Browse files Browse the repository at this point in the history
Maniacs Feature - Pixel scrolling support for CommandPanScreen
  • Loading branch information
Ghabry authored Nov 22, 2024
2 parents ff4c302 + beb2d96 commit 24930fd
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 5 deletions.
32 changes: 32 additions & 0 deletions src/game_interpreter_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,38 @@ bool Game_Interpreter_Map::CommandPanScreen(lcf::rpg::EventCommand const& com) {
break;
}

if (Player::IsPatchManiac() && com.parameters.size() > 5) {
// Pixel scrolling with h/v offsets
bool centered = false; // absolute from default pan (centered on hero)
bool relative = false; // relative to current camera
int h = ValueOrVariableBitfield(com, 1, 0, 2);
int 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.
_state.wait_time = player.GetPanWait();
Expand Down
99 changes: 94 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 @@ -787,19 +789,83 @@ void Game_Player::StartPan(int direction, int distance, int speed) {
}

data()->pan_speed = 2 << speed;

if (Player::IsPatchManiac()) {
// Maniac uses separate horizontal/vertical pan for everything
data()->maniac_horizontal_pan_speed = data()->pan_speed;
data()->maniac_vertical_pan_speed = data()->pan_speed;
}
}

void Game_Player::StartPixelPan(int h, int v, int speed, bool interpolated, bool centered, bool relative) {
if (!Player::IsPatchManiac()) {
return;
}

h *= TILE_SIZE;
v *= TILE_SIZE;

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

int new_pan_x;
int new_pan_y;

// FIXME: Fails when relative and centered are used in combination
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;
}

double h_speed;
double v_speed;

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) {
data()->pan_finish_x = GetDefaultPanX();
data()->pan_finish_y = GetDefaultPanY();
data()->pan_speed = 2 << speed;

if (Player::IsPatchManiac()) {
// Maniac uses separate horizontal/vertical pan for everything
data()->maniac_horizontal_pan_speed = data()->pan_speed;
data()->maniac_vertical_pan_speed = data()->pan_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 @@ -812,10 +878,33 @@ void Game_Player::UpdatePan() {
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;
int dx;
int dy;

if (Player::IsPatchManiac()) {
const double step_x = data()->maniac_horizontal_pan_speed;
const double step_y = data()->maniac_vertical_pan_speed;

// Maniac uses doubles for smoother screen scrolling
double dx2 = std::min(step_x, std::abs(static_cast<double>(pan_remain_x)));
double dy2 = std::min(step_y, std::abs(static_cast<double>(pan_remain_y)));

dx2 = pan_remain_x >= 0 ? dx2 : -dx2;
dy2 = pan_remain_y >= 0 ? dy2 : -dy2;

maniac_pan_current_x -= dx2;
maniac_pan_current_y -= dy2;

// Depending on the position, floor or ceil the value
dx = Utils::RoundTo<double>(std::abs(maniac_pan_current_x)) == std::ceil(std::abs(maniac_pan_current_x)) ? static_cast<int>(std::floor(dx2)) : static_cast<int>(std::ceil(dx2));
dy = Utils::RoundTo<double>(std::abs(maniac_pan_current_y)) == std::ceil(std::abs(maniac_pan_current_y)) ? static_cast<int>(std::floor(dy2)) : static_cast<int>(std::ceil(dy2));
} 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 24930fd

Please sign in to comment.