scummvm/engines/icb/player_pc.cpp
2020-10-06 09:51:20 +02:00

718 lines
23 KiB
C++

/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* file distributed with this source distribution.
*
* Additional copyright for this file:
* Copyright (C) 1999-2000 Revolution Software Ltd.
* This code is based on source code created by Revolution Software,
* used with permission.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "engines/icb/p4.h" //for machine version
#include "engines/icb/common/px_common.h"
#include "engines/icb/common/px_linkeddatafile.h"
#include "engines/icb/player.h"
#include "engines/icb/direct_input.h"
#include "engines/icb/common/px_route_barriers.h"
#include "engines/icb/global_objects.h"
#include "engines/icb/common/px_scriptengine.h"
#include "engines/icb/mission.h"
#include "engines/icb/global_switches.h"
namespace ICB {
#define FACING_FORWARDS ((70 * 4096) / 360)
#define FACING_BACKWARDS ((135 * 4096) / 360)
#define STOOD_TURN_TOLERANCE ((10 * 4096) / 360)
#define WALK_TURN_TOLERANCE ((20 * 4096) / 360)
#define STAIR_TURN_TOLERANCE ((130 * 4096) / 360)
#define STOOD_HARD_TURN_TOLERANCE ((60 * 4096) / 360)
#define NO_TURN_TOLERANCE (2 * 4096)
#define RUN_TURN_TOLERANCE ((30 * 4096) / 360)
#define NEAREST_INT(X) (X) > 0.0 ? int((X) + 0.5) : int((X)-0.5)
#define FIXED_POINT_ANG(X) NEAREST_INT(((X)*4096.0f) / (2 * M_PI))
#define SECOND_ZONE 80
#define R_LIMIT 50
#define CHECKDXDY(dx, dy) \
{ \
int r2 = (dx) * (dx) + (dy) * (dy); \
if (r2 < R_LIMIT * R_LIMIT) { \
if (r2 < 1) \
r2 = 1; \
int scale = NEAREST_INT(PXsqrt((double)(128 * 128 * R_LIMIT * R_LIMIT) / r2)); \
(dx) = ((dx)*scale) / 128; \
(dy) = ((dy)*scale) / 128; \
} \
}
// Which pan value the joystick is pointing at
uint32 g_targetPan = 0;
int g_playerPan;
int g_joyPan;
uint8 _DRAWCOMPASS_ = 0;
// pc device switch
__pc_input device = __KEYS; // default to keys
uint32 fire_key;
uint32 interact_key;
uint32 inventory_key;
uint32 arm_key;
uint32 remora_key;
uint32 crouch_key;
uint32 sidestep_key;
uint32 run_key;
uint32 up_key;
uint32 down_key;
uint32 left_key;
uint32 right_key;
uint32 pause_key;
void _player::Update_input_state() {
// looks at the input devices in use and returns a single user-is-doing state
// note - analogue controllers return a direction which is different from forward-and-rotate schemes
// this routine requires seperate pc/psx coding
// pc devices
// ----------
// keys
// joysticks
// gamepads
// analogue devices
// Are we trying to find a new pan value
static uint32 hunting = 0;
g_targetPan = 0;
g_playerPan = NEAREST_INT(log->pan * 4096.0f);
int screenPan = 0;
int clastCameraPan = NEAREST_INT(lastCameraPan * 4096.0f);
int cdeltaCameraPan = NEAREST_INT(deltaCameraPan * 4096.0f);
int stairs = 0;
int ladders = 0;
int turn_tolerance = STOOD_TURN_TOLERANCE;
int hard_turn_tolerance = STOOD_HARD_TURN_TOLERANCE;
// Defaults
cur_state.turn = __NO_TURN;
cur_state.bitflag = 0;
// Make sure we have a set to use !
// Use lazy logic evaluation
int haveCamera_ = MS->set.OK();
Poll_direct_input();
// Button controls are universal for all control modes
if ((Read_DI_keys(fire_key)) || (Read_Joystick(fire_button))) // Attack
cur_state.SetButton(__ATTACK);
if ((Read_DI_keys(interact_key)) || (Read_Joystick(interact_button))) // Interact
cur_state.SetButton(__INTERACT);
if ((Read_DI_keys(inventory_key)) || (Read_Joystick(inventory_button))) // Inventory
cur_state.SetButton(__INVENTORY);
if ((Read_DI_keys(arm_key)) || (Read_Joystick(arm_button))) // Arm/unarm
cur_state.SetButton(__ARMUNARM);
if ((Read_DI_keys(remora_key)) || (Read_Joystick(remora_button))) // Remora
cur_state.SetButton(__REMORA);
if ((Read_DI_keys(crouch_key)) || (Read_Joystick(crouch_button))) // Crouch
cur_state.SetButton(__CROUCH);
if ((Read_DI_keys(sidestep_key)) || (Read_Joystick(sidestep_button))) // Sidestep
cur_state.SetButton(__SIDESTEP);
if ((Read_DI_keys(run_key)) || (Read_Joystick(run_button))) // Jog
cur_state.SetButton(__JOG);
// No control whilst sliding down a ladder !
if (player_status == SLIP_SLIDIN_AWAY)
return;
// No control whilst getting onto a ladder
if ((player_status == EASY_LINKING) && (stat_after_link == BEGIN_DOWN_LADDER)) {
cur_state.momentum = __STILL;
return;
}
// Have different turn tolerances & control method for being on or off stairs
if ((stat_after_link == ON_STAIRS) || (player_status == ON_STAIRS) || (stat_after_link == STOOD_ON_STAIRS) || (player_status == STOOD_ON_STAIRS) ||
(stat_after_link == REVERSE_ON_STAIRS) || (player_status == REVERSE_ON_STAIRS) || (stat_after_link == RUNNING_ON_STAIRS) || (player_status == RUNNING_ON_STAIRS)) {
stairs = 1;
}
// Have different bigger tolerances & control method for being on or off ladders
if (((player_status == EASY_LINKING) && ((stat_after_link == ON_LADDER) || (stat_after_link == SLIP_SLIDIN_AWAY) || (stat_after_link == LEAVE_LADDER) ||
(stat_after_link == BEGIN_DOWN_LADDER) || (stat_after_link == LEAVE_LADDER_BOTTOM))) ||
(player_status == ON_LADDER) || (player_status == SLIP_SLIDIN_AWAY) || (player_status == LEAVE_LADDER) || (player_status == BEGIN_DOWN_LADDER) ||
(player_status == LEAVE_LADDER_BOTTOM)) {
ladders = 1;
g_playerPan = NEAREST_INT(4096.0f * MS->stairs[stair_num].pan_ref);
}
if (currentJoystick != NO_JOYSTICK) {
// If we are in inventory or remora mode then we should accept input
// from the keyboard by default (force attached joystick to digital)
if ((player_status == REMORA) || (player_status == INVENTORY)) {
// This mode will accept keyboard and joystick
currentJoystick = DIGITAL_JOYSTICK;
} else {
// Set joystick type to whatever is attached
currentJoystick = attachedJoystick;
// Set control mode dependent on attached device
if (currentJoystick != ANALOGUE_JOYSTICK) {
// Force mode for digital control device
Set_control_mode(ACTOR_RELATIVE);
}
}
// Only allow SCREEN_RELATIVE control if we have an analogue device
if ((Get_control_mode() == SCREEN_RELATIVE) && (haveCamera_ == 1)) {
PXcamera &camera = MSS.GetCamera();
int32 cameraPan = camera.pan;
if ((cameraPan > 2048) || (cameraPan < -2048)) {
// Make it +/- HALF_TURN
if (cameraPan > 0)
cameraPan -= 4096;
else
cameraPan += 4096;
}
screenPan = cameraPan;
// Catch if the screen pan has never been set
if (clastCameraPan > 2 * 4096) {
screenPan = cameraPan;
cdeltaCameraPan = 0;
panCycle = 0;
clastCameraPan = cameraPan;
} else {
// Have we changed camera angles ?
if (clastCameraPan != cameraPan) {
// Compute the new deltaCameraPan
cdeltaCameraPan = cameraPan - screenPan;
// Make -2048 -> 2048
if (cdeltaCameraPan > 2048)
cdeltaCameraPan -= 4096;
else if (cdeltaCameraPan < -2048)
cdeltaCameraPan += 4096;
cdeltaCameraPan = cdeltaCameraPan / CAMERA_SMOOTH_CYCLES;
panCycle = 0;
}
if (panCycle < CAMERA_SMOOTH_CYCLES) {
screenPan += cdeltaCameraPan;
panCycle++;
} else {
screenPan = cameraPan;
}
}
// Read joystick values
int x = Read_Joystick(Y_AXIS);
int y = Read_Joystick(X_AXIS);
int padlr = (x * x) + (y * y);
// Set new direction to hunt for
if (padlr > 0) {
if (x == 0) {
g_joyPan = 1024;
} else {
// This works jes' fine
g_joyPan = NEAREST_INT((atan((float)y / (float)x) / TWO_PI) * 4096.0f);
}
if ((x < 0) && (y > 0))
g_joyPan += 2048;
if ((x <= 0) && (y <= 0))
g_joyPan += 2048;
// Directly down
if (g_joyPan == 0)
g_joyPan = 1;
// Hunted for pan = g_joyPan - screenPan
g_targetPan = g_joyPan - screenPan;
// Make 0-4096
g_targetPan = g_targetPan % 4096;
// Looking for a new pan value
hunting = 1;
} else {
// Within deadzone so reset all
hunting = 0;
g_joyPan = 0;
g_targetPan = g_playerPan;
cur_state.turn = __NO_TURN;
cur_state.momentum = __STILL;
}
// If running then always running
if (cur_state.IsButtonSet(__JOG)) {
if (player_status == STOOD)
cur_state.momentum = __FORWARD_1;
else
cur_state.momentum = __FORWARD_2;
} else
// Sidestep can be used as a brake
if (cur_state.IsButtonSet(__SIDESTEP)) {
if (player_status != STOOD)
cur_state.momentum = __STILL;
}
if (hunting) {
// Correct to get 0-4096
if (g_playerPan < 0)
g_playerPan += 4096;
ASSERT1(((g_playerPan >= 0) && (g_playerPan <= 4096)), "Bad g_playerPan %d", g_playerPan);
// So we should have a g_targetPan the character is trying to turn to
int delta = g_targetPan - g_playerPan;
delta = delta % 4096;
if (delta > 2048)
delta -= 4096;
if (delta < -2048)
delta += 4096;
int absDelta = abs(delta);
// Has the run been pressed
if (cur_state.IsButtonSet(__JOG)) {
// Not hunting for a pan value anymore
hunting = 0;
if (stairs == 1) {
// No turning on stairs whilst running
turn_tolerance = NO_TURN_TOLERANCE;
// Run : go from stand -> walk -> run
if (absDelta < FACING_FORWARDS) {
if (player_status == STOOD_ON_STAIRS)
cur_state.momentum = __FORWARD_1;
else
cur_state.momentum = __FORWARD_2;
} else {
// Turn LEFT or RIGHT you decide whilst running
if (delta > turn_tolerance)
cur_state.turn = __LEFT;
else if (delta < -turn_tolerance)
cur_state.turn = __RIGHT;
}
} else if (ladders == 1) {
// RUN on ladder means slide down it fireman style
cur_state.momentum = __FORWARD_2;
} else {
turn_tolerance = RUN_TURN_TOLERANCE;
// Turn LEFT or RIGHT you decide whilst running
if (delta > turn_tolerance)
cur_state.turn = __LEFT;
else if (delta < -turn_tolerance)
cur_state.turn = __RIGHT;
}
} else if (cur_state.IsButtonSet(__SIDESTEP)) {
hunting = 0;
if (stairs == 1) {
// No turning on stairs whilst running
turn_tolerance = NO_TURN_TOLERANCE;
// Run : go from stand -> walk -> run
if (absDelta < FACING_FORWARDS) {
if (player_status == STOOD_ON_STAIRS)
cur_state.momentum = __FORWARD_1;
else
cur_state.momentum = __FORWARD_2;
} else {
// Turn LEFT or RIGHT you decide whilst running
if (delta > turn_tolerance)
cur_state.turn = __LEFT;
else if (delta < -turn_tolerance)
cur_state.turn = __RIGHT;
}
} else if (ladders == 1) {
// RUN on ladder means slide down it fireman style
cur_state.momentum = __FORWARD_2;
} else {
turn_tolerance = RUN_TURN_TOLERANCE;
// The back facing 90 degrees is step backwards
if (absDelta > FACING_BACKWARDS) {
cur_state.momentum = __BACKWARD_1;
}
// So just left or right based on sign
// This means sidestep left or right
else {
if (delta > 0)
cur_state.turn = __LEFT;
else
cur_state.turn = __RIGHT;
}
}
} else {
// Nope, normal screen relative movement
// Catch if not pressing run but running ! then start walking
if (cur_state.momentum == __FORWARD_2) {
cur_state.momentum = __FORWARD_1;
}
// Have different turn tolerances for standing and walking
if (stairs == 1) {
turn_tolerance = STAIR_TURN_TOLERANCE;
} else if (ladders == 1) {
// No turning on ladders
turn_tolerance = NO_TURN_TOLERANCE;
} else if (player_status == WALKING) {
turn_tolerance = WALK_TURN_TOLERANCE;
} else {
turn_tolerance = STOOD_TURN_TOLERANCE;
hard_turn_tolerance = STOOD_HARD_TURN_TOLERANCE;
}
// Get these checks dealt with first
if (ladders == 1) {
// Just need to test for going down the ladder i.e. BACKWARDS
if (absDelta > FACING_BACKWARDS) {
cur_state.momentum = __BACKWARD_1;
} else if (absDelta < FACING_FORWARDS) {
cur_state.momentum = __FORWARD_1;
}
return;
}
// Turn LEFT or RIGHT you decide
if (delta > hard_turn_tolerance) {
cur_state.turn = __HARD_LEFT;
} else if (delta < -hard_turn_tolerance) {
cur_state.turn = __HARD_RIGHT;
} else if (delta > turn_tolerance) {
cur_state.turn = __LEFT;
} else if (delta < -turn_tolerance) {
cur_state.turn = __RIGHT;
}
// So at the right direction start walking
else {
if (ladders == 1) {
// Just need to test for going down the ladder i.e. BACKWARDS
if (absDelta > FACING_BACKWARDS) {
cur_state.momentum = __BACKWARD_1;
} else if (absDelta < FACING_FORWARDS) {
cur_state.momentum = __FORWARD_1;
}
} else if ((stairs == 0) || (absDelta < FACING_FORWARDS)) {
hunting = 0;
// Are past the dead zone
if (padlr > 0) {
// Walk
cur_state.momentum = __FORWARD_1;
}
}
}
}
} // if(hunting)
else if (ladders == 1) {
// RUN on ladder means slide down it fireman style
if (cur_state.IsButtonSet(__JOG)) {
cur_state.momentum = __FORWARD_2;
}
}
// Can only use HARD_LEFT & HARD_RIGHT when STOOD
if (player_status != STOOD) {
if (cur_state.turn == __HARD_LEFT)
cur_state.turn = __LEFT;
else if (cur_state.turn == __HARD_RIGHT)
cur_state.turn = __RIGHT;
}
} // Get_control_mode() == SCREEN_RELATIVE
if (Get_control_mode() == ACTOR_RELATIVE) {
// Don't need to draw this
_DRAWCOMPASS_ = 0;
// Two modes of actor relative, analogue and digital
if (currentJoystick == ANALOGUE_JOYSTICK) {
int x = Read_Joystick(X_AXIS);
int y = Read_Joystick(Y_AXIS);
int padlr = (x * x) + (y * y);
// If running then always running
if (cur_state.IsButtonSet(__JOG)) {
if (player_status == STOOD)
cur_state.momentum = __FORWARD_1;
else
cur_state.momentum = __FORWARD_2;
// Turn left/right whilst walking/running
if (padlr > SECOND_ZONE) {
if (x < 0)
cur_state.turn = __LEFT;
else
cur_state.turn = __RIGHT;
}
} else
// Are we out of the deadzone
if (padlr > 0) {
// We want to turn right
if (x > 0) {
// We want to turn right fast
if (x > SECOND_ZONE) {
// Only allowed to hard turn when stood still and not sidestepping
if ((cur_state.momentum == __STILL) && (!cur_state.IsButtonSet(__SIDESTEP))) {
// Also consider button restraints
if (cur_state.IsButtonSet(__ARMUNARM) || cur_state.IsButtonSet(__CROUCH))
cur_state.turn = __RIGHT;
else
cur_state.turn = __HARD_RIGHT;
} else
cur_state.turn = __RIGHT;
}
// Turn right
else
cur_state.turn = __RIGHT;
} else
// We want to turn left
if (x < 0) {
// We want to turn left fast
if (x < -SECOND_ZONE) {
// Only allowed to hard turn when stood still and not sidestepping
if ((cur_state.momentum == __STILL) && (!cur_state.IsButtonSet(__SIDESTEP))) {
// Also consider button restraints
if (cur_state.IsButtonSet(__ARMUNARM) || cur_state.IsButtonSet(__CROUCH))
cur_state.turn = __LEFT;
else
cur_state.turn = __HARD_LEFT;
} else
cur_state.turn = __LEFT;
}
// Turn left
else
cur_state.turn = __LEFT;
}
// Don't want to turn at all
else
cur_state.turn = __NO_TURN;
// We want move forward
if (y < 0) {
// Can't move forward with sidestep held (unless on stairs or ladders)
if (cur_state.IsButtonSet(__SIDESTEP) && stairs == 0 && ladders == 0) {
cur_state.momentum = __STILL;
} else
// We want to walk
cur_state.momentum = __FORWARD_1;
} else
// We want to move backwards
if (y > 0) {
cur_state.momentum = __BACKWARD_1;
}
// Don't want to move
else
cur_state.momentum = __STILL;
} else {
// Within deadzone so don't move or turn
cur_state.momentum = __STILL;
cur_state.turn = __NO_TURN;
}
} // End of Analogue actor relative controls
else {
// Digital actor relative controls (same as keys)
// If running then always running
if (cur_state.IsButtonSet(__JOG)) {
if (player_status == STOOD)
cur_state.momentum = __FORWARD_1;
else
cur_state.momentum = __FORWARD_2;
} else
// Forward/backward - one or the other
if ((Read_DI_keys(up_key)) || (Read_Joystick(Y_AXIS) == JOY_UP)) {
// Can't move forward with sidestep held (unless on stairs or ladders)
if (cur_state.IsButtonSet(__SIDESTEP) && stairs == 0 && ladders == 0) {
cur_state.momentum = __STILL;
} else
// We want to walk
cur_state.momentum = __FORWARD_1;
} else if ((Read_DI_keys(down_key)) || (Read_Joystick(Y_AXIS) == JOY_DOWN))
cur_state.momentum = __BACKWARD_1;
else
cur_state.momentum = __STILL;
// Left/right
if ((Read_DI_keys(left_key)) || (Read_Joystick(X_AXIS) == JOY_LEFT)) {
cur_state.turn = __LEFT;
} else if ((Read_DI_keys(right_key)) || (Read_Joystick(X_AXIS) == JOY_RIGHT)) {
cur_state.turn = __RIGHT;
} else
cur_state.turn = __NO_TURN;
} // End of Digital actor relative control
}
}
// No Joystick
else {
// Keyboard support (generic controls)
// If running then always running
if (cur_state.IsButtonSet(__JOG)) {
if (player_status == STOOD)
cur_state.momentum = __FORWARD_1;
else
cur_state.momentum = __FORWARD_2;
} else
// Forward/backward - one or the other
if (Read_DI_keys(up_key)) {
// Can't move forward with sidestep held (unless on stairs or ladders)
if (cur_state.IsButtonSet(__SIDESTEP) && stairs == 0 && ladders == 0) {
cur_state.momentum = __STILL;
} else
// We want to walk
cur_state.momentum = __FORWARD_1;
} else if (Read_DI_keys(down_key))
cur_state.momentum = __BACKWARD_1;
else
cur_state.momentum = __STILL;
// Left/right
if (Read_DI_keys(left_key)) {
cur_state.turn = __LEFT;
} else if (Read_DI_keys(right_key)) {
cur_state.turn = __RIGHT;
} else
cur_state.turn = __NO_TURN;
}
// Set the floating point version of the fixed point variables we've used
lastCameraPan = ((float)clastCameraPan / 4096.0f);
deltaCameraPan = ((float)cdeltaCameraPan / 4096.0f);
}
void _player::DrawCompass() {
// User can disable joystick in which case mode is forced to actor
// relative and only the keys will work until the joystick is enabled
if (currentJoystick != NO_JOYSTICK) {
// Only draw compass in THREED mode and not while player is in inventory or remora mode
if ((px.display_mode == THREED) && (player_status != REMORA) && (player_status != INVENTORY) && _DRAWCOMPASS_) {
uint32 pitch;
uint8 *ad;
#define COMPASS_LENGTH 50.0f
#define COMPASS_OX 60
#define COMPASS_OY 420
_rgb purple = {250, 60, 200, 0};
_rgb turq = {0, 250, 250, 0};
_rgb yellow = {250, 250, 0, 0};
ad = surface_manager->Lock_surface(working_buffer_id);
pitch = surface_manager->Get_pitch(working_buffer_id);
if (g_joyPan == 0) {
// Centered/Within deadzone (display a cross)
General_draw_line_24_32(COMPASS_OX, COMPASS_OY - 5, COMPASS_OX, COMPASS_OY + 5, &yellow, ad, pitch);
General_draw_line_24_32(COMPASS_OX - 5, COMPASS_OY, COMPASS_OX + 5, COMPASS_OY, &yellow, ad, pitch);
} else {
float theta = ((float)g_joyPan / 4096.0f) * TWO_PI;
int x = NEAREST_INT(COMPASS_LENGTH * PXsin(theta));
int y = NEAREST_INT(COMPASS_LENGTH * PXcos(theta));
General_draw_line_24_32(COMPASS_OX, COMPASS_OY, (int16)(COMPASS_OX + x), (int16)(COMPASS_OY + y), &yellow, ad, pitch);
}
// Get the actor's screen position
PXvector origin_world, origin_scrn;
origin_world = log->mega->actor_xyz;
bool8 result;
PXcamera &camera = MSS.GetCamera();
PXWorldToFilm(origin_world, camera, result, origin_scrn);
PXvector point_world, point_scrn;
int dx, dy;
double s_ang, c_ang;
s_ang = PXsin(((float)g_targetPan / 4096.0f) * TWO_PI);
c_ang = PXcos(((float)g_targetPan / 4096.0f) * TWO_PI);
int sang = FIXED_POINT_ANG(s_ang);
int cang = FIXED_POINT_ANG(c_ang);
point_world.x = (float)(origin_world.x + (COMPASS_LENGTH * sang / 4096));
point_world.y = origin_world.y;
point_world.z = (float)(origin_world.z + (COMPASS_LENGTH * cang / 4096));
PXWorldToFilm(point_world, camera, result, point_scrn);
dx = NEAREST_INT(point_scrn.x - origin_scrn.x);
dy = NEAREST_INT(point_scrn.y - origin_scrn.y);
CHECKDXDY(dx, dy)
General_draw_line_24_32(COMPASS_OX, COMPASS_OY, (int16)(COMPASS_OX + dx), (int16)(COMPASS_OY - dy), &turq, ad, pitch);
s_ang = PXsin(((float)g_playerPan / 4096.0f) * TWO_PI);
c_ang = PXcos(((float)g_playerPan / 4096.0f) * TWO_PI);
sang = FIXED_POINT_ANG(s_ang);
cang = FIXED_POINT_ANG(c_ang);
point_world.x = (float)(origin_world.x + (COMPASS_LENGTH * sang / 4096));
point_world.y = origin_world.y;
point_world.z = (float)(origin_world.z + (COMPASS_LENGTH * cang / 4096));
PXWorldToFilm(point_world, camera, result, point_scrn);
dx = NEAREST_INT(point_scrn.x - origin_scrn.x);
dy = NEAREST_INT(point_scrn.y - origin_scrn.y);
CHECKDXDY(dx, dy)
General_draw_line_24_32(COMPASS_OX, COMPASS_OY, (int16)(COMPASS_OX + dx), (int16)(COMPASS_OY - dy), &purple, ad, pitch);
surface_manager->Unlock_surface(working_buffer_id);
}
}
}
} // End of namespace ICB