[Scummvm-git-logs] scummvm master -> 8d23d0799dcacdbc68a13094174f9c5dadb6b3ca

bluegr noreply at scummvm.org
Mon Sep 16 10:01:53 UTC 2024


This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
8b5ad52d88 SCUMM: HE: Add u32 code for Backyard Basketball
8d23d0799d SCUMM: AKOS: Workaround invalid memory access during scaling


Commit: 8b5ad52d88be4f8a274badff88049a97dd4beffc
    https://github.com/scummvm/scummvm/commit/8b5ad52d88be4f8a274badff88049a97dd4beffc
Author: AndywinXp (andywinxp at gmail.com)
Date: 2024-09-16T13:01:49+03:00

Commit Message:
SCUMM: HE: Add u32 code for Backyard Basketball

Changed paths:
  A engines/scumm/he/basketball/ai.cpp
  A engines/scumm/he/basketball/ai.h
  A engines/scumm/he/basketball/basketball.cpp
  A engines/scumm/he/basketball/basketball.h
  A engines/scumm/he/basketball/collision/bball_collision.cpp
  A engines/scumm/he/basketball/collision/bball_collision.h
  A engines/scumm/he/basketball/collision/bball_collision_basketball.cpp
  A engines/scumm/he/basketball/collision/bball_collision_basketball.h
  A engines/scumm/he/basketball/collision/bball_collision_box.cpp
  A engines/scumm/he/basketball/collision/bball_collision_box.h
  A engines/scumm/he/basketball/collision/bball_collision_cylinder.cpp
  A engines/scumm/he/basketball/collision/bball_collision_cylinder.h
  A engines/scumm/he/basketball/collision/bball_collision_node.cpp
  A engines/scumm/he/basketball/collision/bball_collision_node.h
  A engines/scumm/he/basketball/collision/bball_collision_object.cpp
  A engines/scumm/he/basketball/collision/bball_collision_object.h
  A engines/scumm/he/basketball/collision/bball_collision_player.cpp
  A engines/scumm/he/basketball/collision/bball_collision_player.h
  A engines/scumm/he/basketball/collision/bball_collision_shields.cpp
  A engines/scumm/he/basketball/collision/bball_collision_shields.h
  A engines/scumm/he/basketball/collision/bball_collision_sphere.cpp
  A engines/scumm/he/basketball/collision/bball_collision_sphere.h
  A engines/scumm/he/basketball/collision/bball_collision_stack.cpp
  A engines/scumm/he/basketball/collision/bball_collision_stack.h
  A engines/scumm/he/basketball/collision/bball_collision_support_obj.h
  A engines/scumm/he/basketball/collision/bball_collision_tree.cpp
  A engines/scumm/he/basketball/collision/bball_collision_tree.h
  A engines/scumm/he/basketball/court.cpp
  A engines/scumm/he/basketball/court.h
  A engines/scumm/he/basketball/cursor.cpp
  A engines/scumm/he/basketball/geo_translation.cpp
  A engines/scumm/he/basketball/geo_translations.h
  A engines/scumm/he/basketball/obstacle_avoidance.cpp
  A engines/scumm/he/basketball/obstacle_avoidance.h
  A engines/scumm/he/basketball/passing.cpp
  A engines/scumm/he/basketball/passing.h
  A engines/scumm/he/basketball/shooting.cpp
  A engines/scumm/he/basketball/shooting.h
  A engines/scumm/he/basketball/trajectory.cpp
  A engines/scumm/he/logic/basketball_logic.cpp
  R engines/scumm/he/logic/basketball.cpp
    engines/scumm/he/intern_he.h
    engines/scumm/he/logic_he.cpp
    engines/scumm/he/logic_he.h
    engines/scumm/module.mk
    engines/scumm/saveload.cpp
    engines/scumm/script.cpp
    engines/scumm/script.h
    engines/scumm/scumm.cpp
    engines/scumm/scumm.h
    engines/scumm/vars.cpp


diff --git a/engines/scumm/he/basketball/ai.cpp b/engines/scumm/he/basketball/ai.cpp
new file mode 100644
index 00000000000..9a72650baac
--- /dev/null
+++ b/engines/scumm/he/basketball/ai.cpp
@@ -0,0 +1,249 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+
+#include "scumm/he/basketball/ai.h"
+#include "scumm/he/basketball/basketball.h"
+#include "scumm/he/basketball/court.h"
+#include "scumm/he/basketball/geo_translations.h"
+#include "scumm/he/logic_he.h"
+
+#include "math/utils.h"
+
+namespace Scumm {
+
+int LogicHEBasketball::u32_userGetPlayerClosestToBall(int teamIndex) {
+	assert((TEAM_HOME <= teamIndex) && (teamIndex <= TEAM_AWAY));
+
+	int finalPlayerIndex = NO_PLAYER;
+	int shortestDistance2 = 0x7FFFFFFF;
+
+	// Go through all of the players on the team and calculate the distance between
+	// them and the ball...
+	Common::Array<CCollisionPlayer> *playerList =
+		(teamIndex == TEAM_HOME) ?
+		&_vm->_basketball->_court->_homePlayerList : &_vm->_basketball->_court->_awayPlayerList;
+
+	for (size_t playerIndex = 0; playerIndex < playerList->size(); ++playerIndex) {
+		CCollisionPlayer *currentPlayer = &((*playerList)[playerIndex]);
+
+		if (currentPlayer->_playerIsInGame) {
+
+			int distance2 = (
+				((currentPlayer->center.x - _vm->_basketball->_court->_basketBall.center.x) *
+				 (currentPlayer->center.x - _vm->_basketball->_court->_basketBall.center.x)) +
+				((currentPlayer->center.y - _vm->_basketball->_court->_basketBall.center.y) *
+				 (currentPlayer->center.y - _vm->_basketball->_court->_basketBall.center.y)));
+
+			// Keep track of the player closest to the ball...
+			if (distance2 < shortestDistance2) {
+				finalPlayerIndex = playerIndex;
+				shortestDistance2 = distance2;
+			}
+		}
+	}
+
+	assert(finalPlayerIndex != NO_PLAYER);
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, (*playerList)[finalPlayerIndex]._objectID);
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userGetPlayerClosestToBall() {
+	int finalPlayerIndex = NO_PLAYER;
+	int shortestDistance2 = 0x7FFFFFFF;
+	Common::Array<CCollisionPlayer> *playerList = nullptr;
+
+	// Go through all of the players on both teams and calculate the distance between
+	// them and the ball...
+	for (size_t playerIndex = 0; playerIndex < _vm->_basketball->_court->_homePlayerList.size(); ++playerIndex) {
+		CCollisionPlayer *currentPlayer = &_vm->_basketball->_court->_homePlayerList[playerIndex];
+
+		if (currentPlayer->_playerIsInGame) {
+			int distance2 = (
+				((currentPlayer->center.x - _vm->_basketball->_court->_basketBall.center.x) *
+				 (currentPlayer->center.x - _vm->_basketball->_court->_basketBall.center.x)) +
+				((currentPlayer->center.y - _vm->_basketball->_court->_basketBall.center.y) *
+				 (currentPlayer->center.y - _vm->_basketball->_court->_basketBall.center.y)));
+
+			// Keep track of the player closest to the ball...
+			if (distance2 < shortestDistance2) {
+				finalPlayerIndex = playerIndex;
+				shortestDistance2 = distance2;
+				playerList = &_vm->_basketball->_court->_homePlayerList;
+			}
+		}
+	}
+
+	for (size_t playerIndex = 0; playerIndex < _vm->_basketball->_court->_awayPlayerList.size(); ++playerIndex) {
+		CCollisionPlayer *currentPlayer = &_vm->_basketball->_court->_awayPlayerList[playerIndex];
+
+		if (currentPlayer->_playerIsInGame) {
+			int distance2 = (
+				((currentPlayer->center.x - _vm->_basketball->_court->_basketBall.center.x) *
+				 (currentPlayer->center.x - _vm->_basketball->_court->_basketBall.center.x)) +
+				((currentPlayer->center.y - _vm->_basketball->_court->_basketBall.center.y) *
+				 (currentPlayer->center.y - _vm->_basketball->_court->_basketBall.center.y)));
+
+			// Keep track of the player closest to the ball...
+			if (distance2 < shortestDistance2) {
+				finalPlayerIndex = playerIndex;
+				shortestDistance2 = distance2;
+				playerList = &_vm->_basketball->_court->_awayPlayerList;
+			}
+		}
+	}
+
+	assert(finalPlayerIndex != NO_PLAYER);
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, (*playerList)[finalPlayerIndex]._objectID);
+
+	return 1;
+}
+
+int Basketball::numOpponentsInCone(int team, float widthDistanceRatio, const U32FltVector2D &end, const U32FltVector2D &focus) {
+	// The end out parameter is the end point of the center line of the cone,
+	// and widthDistanceRatio defines the width of the cone = width at dist 1
+	int count = 0;
+
+	Line2D line(focus, end);
+
+	Common::Array<CCollisionPlayer> *playerList = (team == TEAM_HOME) ? &_vm->_basketball->_court->_homePlayerList : &_vm->_basketball->_court->_awayPlayerList;
+
+	for (Common::Array<CCollisionPlayer>::iterator currentPlayer = playerList->begin(); currentPlayer != playerList->end(); ++currentPlayer) {
+		U32FltVector2D point = line.projectPoint(currentPlayer->center);
+
+		if (focus.distance2(currentPlayer->center) < (point.distance2(focus) * widthDistanceRatio * widthDistanceRatio) && line.inBetween(point, focus, end)) {
+			count++;
+		}
+	}
+
+	return count;
+}
+
+static bool isPointInBounds(const U32FltVector2D &point) {
+	return ((point.x > 0) &&
+			(point.x < MAX_WORLD_X) &&
+			(point.y > 0) &&
+			(point.y < MAX_WORLD_Y));
+}
+
+float Basketball::congestion(U32FltVector2D pos, bool ignore, int whichPlayer) {
+	float congestion = 0.0F;
+
+	for (int team = TEAM_HOME; team <= TEAM_AWAY; ++team) {
+		Common::Array<CCollisionPlayer> *playerList = (team == TEAM_HOME) ? &_court->_homePlayerList : &_court->_awayPlayerList;
+
+		for (Common::Array<CCollisionPlayer>::iterator currentPlayer = playerList->begin(); currentPlayer != playerList->end(); ++currentPlayer) {
+			if (currentPlayer->_playerIsInGame && (ignore && !(currentPlayer->_objectID == whichPlayer))) {
+				float distance2 = pos.distance2(currentPlayer->center);
+
+				if (distance2 == 0.0F)
+					return FLT_MAX;
+
+				congestion += (1 / distance2);
+			}
+		}
+	}
+
+	return congestion;
+}
+
+int LogicHEBasketball::u32_userGetOpenSpot(int whichPlayer, U32FltVector2D upperLeft, U32FltVector2D lowerRight, U32FltVector2D passer, bool attract, U32FltVector2D attractPoint) {
+	int xGranularity = 5;
+	int yGranularity = 5;
+
+	int rectWidth = fabs(upperLeft.x - lowerRight.x);
+	int rectHeight = fabs(upperLeft.y - lowerRight.y);
+
+	float xMesh = rectWidth / (xGranularity + 1);
+	float yMesh = rectHeight / (yGranularity + 1);
+
+	float startX = upperLeft.x + xMesh / 2;
+	float startY = upperLeft.y + yMesh / 2;
+
+	float x = startX, y = startY;
+
+	float bestCongestion = FLT_MAX;
+
+	U32FltVector2D bestPoint, point;
+
+	float tmp;
+
+	for (int ii = 0; ii < xGranularity; ii++) {
+		for (int jj = 0; jj < yGranularity; jj++) {
+			tmp = _vm->_basketball->congestion(point = U32FltVector2D(x, y), true, whichPlayer);
+
+			if (attract) {
+				tmp -= (4 * (1 / point.distance2(attractPoint)));
+			}
+
+			if (tmp < bestCongestion &&
+				isPointInBounds(point)) {
+				bestCongestion = tmp;
+				bestPoint = point;
+			}
+
+			y += yMesh;
+		}
+
+		x += xMesh;
+		y = startY;
+	}
+
+	if (bestCongestion == FLT_MAX) {
+		return 0;
+	} else {
+		writeScummVar(_vm1->VAR_U32_USER_VAR_A, bestPoint.x);
+		writeScummVar(_vm1->VAR_U32_USER_VAR_B, bestPoint.y);
+
+		return 1;
+	}
+}
+
+static int isPlayerInBounds(U32Sphere *player) {
+	assert(player);
+
+	return (((player->center.x - player->radius) > -COLLISION_EPSILON) &&
+			((player->center.x + player->radius) < (MAX_WORLD_X + COLLISION_EPSILON)) &&
+			((player->center.y - player->radius) > -COLLISION_EPSILON) &&
+			((player->center.y + player->radius) < (MAX_WORLD_Y + COLLISION_EPSILON)));
+}
+
+int LogicHEBasketball::u32_userIsPlayerInBounds(int playerID) {
+	U32Cylinder *player = _vm->_basketball->_court->getPlayerPtr(playerID);
+
+	int isPlayerInBoundsRes = isPlayerInBounds(player);
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, isPlayerInBoundsRes);
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userIsBallInBounds() {
+	int bIsBallInBounds = isPlayerInBounds((U32Sphere *)&_vm->_basketball->_court->_basketBall);
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, bIsBallInBounds);
+	return 1;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/ai.h b/engines/scumm/he/basketball/ai.h
new file mode 100644
index 00000000000..d6435e689fe
--- /dev/null
+++ b/engines/scumm/he/basketball/ai.h
@@ -0,0 +1,175 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_AI_H
+#define SCUMM_HE_BASKETBALL_AI_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/logic_he.h"
+
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+
+
+namespace Scumm {
+
+#define FLOAT_EPSILON  0.001F
+
+inline int square(int x) { return x * x; }
+
+class Line2D {
+public:
+	Line2D(float x_coef, float y_coef, float constant) {
+		_a = x_coef;
+		_b = y_coef;
+		_c = constant;
+	}
+
+	Line2D(const U32FltVector2D &point1, const U32FltVector2D &point2) {
+		LineFromTwoPoints(point1, point2);
+	}
+
+	void LineFromTwoPoints(U32FltVector2D pt1, U32FltVector2D pt2) {
+		float temp = (pt2.x - pt1.x);
+
+		if (fabs(temp) < FLOAT_EPSILON) {
+			assert(((fabs(pt2.y - pt1.y) >= FLOAT_EPSILON)));
+			_a = 1;
+			_b = 0;
+		} else {
+			float m = (pt2.y - pt1.y) / temp;
+			_a = -m;
+			_b = 1;
+		}
+
+		_c = -(_a * pt2.x + _b * pt2.y);
+	}
+
+	inline float distance(U32FltVector2D point) {
+		return fabs((_a * point.x + _b * point.y + _c) / sqrt(square(_a) + square(_b)));
+	}
+
+	inline float distance2(U32FltVector2D point) {
+		return fabs(square(_a * point.x + _b * point.y + _c) / (square(_a) + square(_b)));
+	}
+
+	inline float angle() {
+		return atan2(-_a, _b);
+	}
+
+	bool inBetween(U32FltVector2D point, U32FltVector2D end1, U32FltVector2D end2) {
+		assert((!onLine(end1) || !onLine(end2)));
+
+		point = projectPoint(point);
+		float distance2 = end1.distance2(end2);
+
+		return (point.distance2(end1) <= distance2 && point.distance2(end2) <= distance2) ? true : false;
+	}
+
+	bool onLine(U32FltVector2D point) {
+		return (distance2(point) < 1.0F) ? true : false;
+	}
+
+	U32FltVector2D projectPoint(U32FltVector2D point) {
+		return intersection(perpendicular(point));
+	}
+
+	float getY(float x) {
+		if (_b != 0.0F)
+			return (-_a * x - _c) / _b;
+
+		return 0.0F;
+	}
+
+	float getX(float y) {
+		if (_a != 0.0F)
+			return (-_b * y - _c) / _a;
+
+		return 0.0F;
+	}
+
+	U32FltVector2D intersection(Line2D line) {
+		U32FltVector2D result(0.0F, 0.0F);
+
+		assert(!sameSlope(line));
+
+		if (_b == 0.0F) {
+			result.x = -_c / _a;
+			result.y = line.getY(result.x);
+			return result;
+		}
+
+		if (line._b == 0.0F) {
+			result.x = -line._c / line._a;
+			result.y = getY(result.y);
+			return result;
+		}
+
+		result.x = (_c * line._b - _b * line._c) / (line._a * _b - _a * line._b);
+		result.y = getY(result.x);
+		return result;
+	}
+
+	Line2D perpendicular(U32FltVector2D point) {
+		return Line2D(_b, -_a, _a * point.y - _b * point.x);
+	}
+
+	Line2D shiftY(float val) {
+		return Line2D(_a, _b, _c - val * _b);
+	}
+
+	Line2D shiftX(float val) {
+		return Line2D(_a, _b, _c - val * _a);
+	}
+
+	// Returns whether the projection of point1 is closer to targPoint than the projection of point2
+	bool isPointCloserToPointOnLine(U32FltVector2D point1, U32FltVector2D point2, U32FltVector2D targPoint) {
+
+		assert(!onLine(targPoint));
+
+		point1 = projectPoint(point1);
+		point2 = projectPoint(point2);
+
+		return (point1.distance(targPoint) < point2.distance(targPoint)) ? true : false;
+	}
+
+	bool halfPlaneTest(U32FltVector2D point) {
+		if (_b == 0)
+			return (point.x < -_c / _a) ? true : false;
+
+		return (point.y > getY(point.x)) ? true : false;
+	}
+
+	bool sameSlope(Line2D line) {
+		return ((_b == 0 && line._b == 0) || ((_a / _b) == (line._a / line._b))) ? true : false;
+	}
+
+private:
+	float _a, _b, _c; // The three coeffs in the line equation:
+					  // Ax + By + C = 0
+};
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_AI_H
diff --git a/engines/scumm/he/basketball/basketball.cpp b/engines/scumm/he/basketball/basketball.cpp
new file mode 100644
index 00000000000..4671dd4e911
--- /dev/null
+++ b/engines/scumm/he/basketball/basketball.cpp
@@ -0,0 +1,235 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/basketball/basketball.h"
+#include "scumm/he/basketball/collision/bball_collision_shields.h"
+
+namespace Scumm {
+
+Basketball::Basketball(ScummEngine_v100he *vm) {
+	_vm = vm;
+	_court = new CBBallCourt();
+	_shields = new CCollisionShieldVector();
+}
+
+Basketball::~Basketball() {}
+
+int Basketball::u32FloatToInt(float input) {
+	int output = 0;
+
+	if (input < 0)
+		output = (int)(input - 0.5F);
+	else if (input > 0)
+		output = (int)(input + 0.5F);
+
+	return output;
+}
+
+int Basketball::u32DoubleToInt(double input) {
+	int output = 0;
+
+	if (input < 0)
+		output = (int)(input - 0.5);
+	else if (input > 0)
+		output = (int)(input + 0.5);
+
+	return output;
+}
+
+void Basketball::fillBallTargetList(const CCollisionSphere *sourceObject, CCollisionObjectVector *targetList) {
+	// Add all of the court objects...
+	_court->_objectTree.selectObjectsInBound(sourceObject->getBigBoundingBox(), targetList);
+
+	// Add the shields...
+	CCollisionShieldVector::const_iterator shieldIt;
+
+	for (shieldIt = _shields->begin(); shieldIt != _shields->end(); ++shieldIt) {
+		if (!shieldIt->_ignore) {
+			targetList->push_back(&(*shieldIt));
+		}
+	}
+
+	// Add all of the home players...
+	Common::Array<CCollisionPlayer>::const_iterator homePlayerIt;
+
+	for (homePlayerIt = _court->_homePlayerList.begin();
+		 homePlayerIt != _court->_homePlayerList.end();
+		 ++homePlayerIt) {
+		if (!homePlayerIt->_ignore) {
+			targetList->push_back(&(*homePlayerIt));
+		}
+	}
+
+	// Add all of the away players...
+	Common::Array<CCollisionPlayer>::const_iterator awayPlayerIt;
+
+	for (awayPlayerIt = _court->_awayPlayerList.begin();
+		 awayPlayerIt != _court->_awayPlayerList.end();
+		 ++awayPlayerIt) {
+		if (!awayPlayerIt->_ignore) {
+			targetList->push_back(&(*awayPlayerIt));
+		}
+	}
+}
+
+void Basketball::fillPlayerTargetList(const CCollisionPlayer *sourceObject, CCollisionObjectVector *targetList) {
+	// Add all of the court objects...
+	_court->_objectTree.selectObjectsInBound(sourceObject->getBigBoundingBox(), targetList);
+
+	// Add the shields if the player has the ball...
+	if (sourceObject->_playerHasBall) {
+		CCollisionShieldVector::const_iterator shieldIt;
+
+		for (shieldIt = _shields->begin(); shieldIt != _shields->end(); ++shieldIt) {
+			if (!shieldIt->_ignore) {
+				targetList->push_back(&(*shieldIt));
+			}
+		}
+	}
+
+	// Add the basketball...
+	if (!_court->_basketBall._ignore) {
+		targetList->push_back((ICollisionObject *)&_court->_basketBall);
+	}
+
+	// Add the virtual basketball...
+	if (!_court->_virtualBall._ignore) {
+		targetList->push_back((ICollisionObject *)&_court->_virtualBall);
+	}
+
+	// Add all of the home players...
+	Common::Array<CCollisionPlayer>::const_iterator homePlayerIt;
+
+	for (homePlayerIt = _court->_homePlayerList.begin();
+		 homePlayerIt != _court->_homePlayerList.end();
+		 ++homePlayerIt) {
+		if ((sourceObject != &(*homePlayerIt)) &&
+			(!homePlayerIt->_ignore)) {
+			targetList->push_back(&(*homePlayerIt));
+		}
+	}
+
+	// Add all of the away players...
+	Common::Array<CCollisionPlayer>::const_iterator awayPlayerIt;
+
+	for (awayPlayerIt = _court->_awayPlayerList.begin();
+		 awayPlayerIt != _court->_awayPlayerList.end();
+		 ++awayPlayerIt) {
+		if ((sourceObject != &(*awayPlayerIt)) &&
+			(!awayPlayerIt->_ignore)) {
+			targetList->push_back(&(*awayPlayerIt));
+		}
+	}
+}
+
+double Basketball::getLaunchAngle(int velocity, int hDist, int vDist, int gravity) {
+	double theta;
+	double bestTheta;
+	double tempTheta;
+	double thetaMin, thetaMax;
+
+	double answer;
+	double bestAnswer;
+	double hiAnswer, loAnswer;
+
+	double time;
+	double bestTime;
+
+	double targetAngle;
+	int counter;
+
+	assert(hDist > 0);
+
+	// Set the search limits, and get a first guess...
+	targetAngle = atan2(vDist, hDist);
+
+	// The minimum angle of the pass is the angle directly between us and the target...
+	thetaMin = targetAngle;
+
+	// The maximum angle is straight up...
+	thetaMax = BBALL_M_PI / 4;
+
+	theta = (thetaMin + thetaMax) / 2;
+	bestTheta = theta;
+	bestTime = hDist / (velocity * cos(theta));
+	if (bestTime < 0)
+		bestTime *= -1;
+
+	// Start binary searching for a close answer...
+	counter = 0;
+	answer = (2 * velocity * velocity * hDist * sin(theta) * cos(theta)) - (2 * velocity * velocity * vDist * cos(theta) * cos(theta)) - (gravity * hDist * hDist);
+	if (answer < 0)
+		answer *= -1;
+	bestAnswer = answer;
+
+	while ((fabs(answer * 1000.0) > 10.0) && (++counter <= 100)) {
+		if (theta < 0)
+			targetAngle += (2 * BBALL_M_PI);
+
+		// Get a theta above and below the current one and see which one gets us closer
+		// to satisfying the equation...
+		tempTheta = (thetaMin + theta) / 2;
+		loAnswer = (2 * velocity * velocity * hDist * sin(tempTheta) * cos(tempTheta)) - (2 * velocity * velocity * vDist * cos(tempTheta) * cos(tempTheta)) - (gravity * hDist * hDist);
+		if (loAnswer < 0)
+			loAnswer = 0 - loAnswer;
+
+		tempTheta = (thetaMax + theta) / 2;
+		hiAnswer = (2 * velocity * velocity * hDist * sin(tempTheta) * cos(tempTheta)) - (2 * velocity * velocity * vDist * cos(tempTheta) * cos(tempTheta)) - (gravity * hDist * hDist);
+		if (hiAnswer < 0)
+			hiAnswer = 0 - hiAnswer;
+
+		if (loAnswer < hiAnswer) {
+			thetaMax = theta;
+			answer = loAnswer;
+		} else {
+			thetaMin = theta;
+			answer = hiAnswer;
+		}
+		theta = (thetaMin + thetaMax) / 2;
+		if (answer < 0)
+			answer *= -1;
+
+		if (answer <= bestAnswer) {
+			if (answer == bestAnswer) {
+				time = hDist / (velocity * cos(theta));
+				if (time < 0)
+					time *= -1;
+				if (time < bestTime) {
+					bestTheta = theta;
+					bestTime = time;
+					bestAnswer = answer;
+				}
+			} else {
+				bestTheta = theta;
+				bestTime = hDist / (velocity * cos(theta));
+				if (bestTime < 0)
+					bestTime *= -1;
+				bestAnswer = answer;
+			}
+		}
+	}
+
+	bestTheta = (bestTheta * 180) / BBALL_M_PI;
+	return bestTheta;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/basketball.h b/engines/scumm/he/basketball/basketball.h
new file mode 100644
index 00000000000..28326a4519d
--- /dev/null
+++ b/engines/scumm/he/basketball/basketball.h
@@ -0,0 +1,87 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_BASKETBALL_H
+#define SCUMM_HE_BASKETBALL_BASKETBALL_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/logic_he.h"
+#include "scumm/he/basketball/court.h"
+#include "scumm/he/basketball/collision/bball_collision_player.h"
+#include "scumm/he/basketball/collision/bball_collision_shields.h"
+
+#include "common/array.h"
+#include "common/stack.h"
+#include "common/queue.h"
+#include "common/std/set.h"
+
+namespace Scumm {
+
+class LogicHEBasketball;
+
+class Basketball {
+
+public:
+	Basketball(ScummEngine_v100he *vm);
+	~Basketball();
+
+	int u32FloatToInt(float input);
+	int u32DoubleToInt(double input);
+
+	int numOpponentsInCone(int team, float widthDistanceRatio, const U32FltVector2D &end, const U32FltVector2D &focus);
+	float congestion(U32FltVector2D pos, bool ignore, int whichPlayer);
+	void fillPlayerTargetList(const CCollisionPlayer *sourceObject, CCollisionObjectVector *targetList);
+	void fillBallTargetList(const CCollisionSphere *sourceObject, CCollisionObjectVector *targetList);
+	double getLaunchAngle(int velocity, int hDist, int vDist, int gravity);
+	float getAvoidanceDistance(const U32Circle &playerMarker, const CCollisionPlayer &obstacle);
+	CCollisionPlayer *detectObstacle(const U32Circle &playerMarker, int playerID, const U32FltPoint2D &targetLocation, bool targetIsObstacle, U32FltPoint2D *intersection, CBBallCourt *court);
+	bool avoidObstacle(const U32Circle &playerMarker, const U32FltPoint2D &targetLocation,  const CCollisionPlayer &obstacle, ERevDirection whichDirection, U32FltPoint2D *newTarget);
+	ERevDirection getAvoidanceDirection(const U32Circle &playerMarker, const U32FltPoint2D &targetLocation, const CCollisionPlayer &obstacle);
+	bool getPathDistance(U32Circle *playerMarker, int playerID, Common::Stack<U32FltPoint2D> *targetStack, ERevDirection lastTurn, float *pathDistance, Common::Queue<U32FltPoint2D> *wayPointQueue, Std::set<int> *obstacleSet, CBBallCourt *court);
+
+	void pushTargetOutOfObstacle(const U32Circle &playerMarker,
+											 const CCollisionPlayer &obstacle,
+											 Common::Stack<U32FltPoint2D> *targetStack);
+
+	ERevDirection getBestPath(const U32Circle &playerMarker,
+										  int playerID,
+										  Common::Stack<U32FltPoint2D> *targetStack,
+										  CCollisionPlayer *obstacle,
+										  ERevDirection lastTurn,
+										  float *distance,
+										  Common::Queue<U32FltPoint2D> *wayPointQueue,
+										  Std::set<int> *obstacleSet,
+										  CBBallCourt *court);
+
+	CBBallCourt *_court;
+	CCollisionShieldVector *_shields;
+
+private:
+	ScummEngine_v100he *_vm;
+};
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_BASKETBALL_H
diff --git a/engines/scumm/he/basketball/collision/bball_collision.cpp b/engines/scumm/he/basketball/collision/bball_collision.cpp
new file mode 100644
index 00000000000..60d6981a959
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision.cpp
@@ -0,0 +1,572 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/basketball/basketball.h"
+#include "scumm/he/basketball/collision/bball_collision.h"
+#include "scumm/he/basketball/geo_translations.h"
+
+namespace Scumm {
+
+int LogicHEBasketball::u32_userInitCourt(int courtID) {
+	static Common::String courtNames[] = {
+		"",
+		"Dobbaguchi", "Jocindas", "SandyFlats", "Queens",
+		"Park", "Scheffler", "Polk", "McMillan",
+		"CrownHill", "Memorial", "TechState", "Garden",
+		"Moon", "Barn"
+	};
+
+
+	// Make sure nothing on the court is currently initialized.
+	_vm->_basketball->_court->_objectTree.~CCollisionObjectTree();
+	_vm->_basketball->_court->_objectList.clear();
+	_vm->_basketball->_court->_homePlayerList.clear();
+	_vm->_basketball->_court->_awayPlayerList.clear();
+
+	// Initialize the shot spots
+	_vm->_basketball->_court->_shotSpot[LEFT_BASKET].center.x = BASKET_X;
+	_vm->_basketball->_court->_shotSpot[LEFT_BASKET].center.y = BASKET_Y;
+	_vm->_basketball->_court->_shotSpot[LEFT_BASKET].center.z = BASKET_Z;
+	_vm->_basketball->_court->_shotSpot[LEFT_BASKET].radius = SHOT_SPOT_RADIUS;
+
+	_vm->_basketball->_court->_shotSpot[RIGHT_BASKET].center.x = MAX_WORLD_X - BASKET_X;
+	_vm->_basketball->_court->_shotSpot[RIGHT_BASKET].center.y = BASKET_Y;
+	_vm->_basketball->_court->_shotSpot[RIGHT_BASKET].center.z = BASKET_Z;
+	_vm->_basketball->_court->_shotSpot[RIGHT_BASKET].radius = SHOT_SPOT_RADIUS;
+
+	// Get the name and object file for this court.
+	_vm->_basketball->_court->_name = courtNames[courtID];
+
+	// Put together to relative path and filename.
+	Common::Path objectFileName = Common::Path(Common::String::format("data/courts/%s.cof", courtNames[courtID].c_str()));
+
+	// Create a file stream to the collision object file
+	Common::File objectFile;
+	if (!objectFile.open(objectFileName))
+		error("LogicHEBasketball::u32_userInitCourt(): Could not open file '%s'", objectFileName.toString(Common::Path::kNativeSeparator).c_str());
+
+	// Read in the object file version
+	char fileVersion[32];
+	int versionStringLength = objectFile.readUint32LE();
+
+	if (versionStringLength <= 0 && versionStringLength > ARRAYSIZE(fileVersion) - 1)
+		error("LogicHEBasketball::u32_userInitCourt(): Read from stream did not read the version string length correctly.");
+	
+	objectFile.read(fileVersion, versionStringLength);
+	fileVersion[versionStringLength] = '\0';
+
+	if (strcmp(fileVersion, "01.05"))
+		error("LogicHEBasketball::u32_userInitCourt(): Invalid court version field: %s", fileVersion);
+
+	// Read in the total number of objects
+	_vm->_basketball->_court->_objectCount = objectFile.readUint32LE();
+	_vm->_basketball->_court->_objectList.resize(_vm->_basketball->_court->_objectCount);
+
+	// Keep a list of pointers to the court objects...
+	CCollisionObjectVector objectPtrList;
+	objectPtrList.resize(_vm->_basketball->_court->_objectCount);
+
+	// Read in each court object...
+	for (int i = 0; i < _vm->_basketball->_court->_objectCount; i++) {
+		CCollisionBox *currentObject = &_vm->_basketball->_court->_objectList[i];
+
+		// Read in this object's description...
+		int descriptionStringLength = objectFile.readUint32LE();
+		char *tmp = (char *)malloc(descriptionStringLength + 1);
+
+		assert(tmp);
+
+		objectFile.read(tmp, descriptionStringLength);
+		tmp[descriptionStringLength] = '\0';
+
+		Common::String tmp2(tmp);
+
+		_vm->_basketball->_court->_objectList[i]._description = tmp2;
+
+		free(tmp);
+
+		// Read in all other object attributes...
+		currentObject->_objectType = (EObjectType)objectFile.readUint32LE();
+		currentObject->_collisionEfficiency = objectFile.readUint32LE();
+		currentObject->_friction  = objectFile.readUint32LE();
+		currentObject->_soundNumber = objectFile.readUint32LE();
+		currentObject->_objectID = objectFile.readUint32LE();
+		currentObject->minPoint.x = objectFile.readUint32LE();
+		currentObject->minPoint.y = objectFile.readUint32LE();
+		currentObject->minPoint.z = objectFile.readUint32LE();
+		currentObject->maxPoint.x = objectFile.readUint32LE();
+		currentObject->maxPoint.y = objectFile.readUint32LE();
+		currentObject->maxPoint.z = objectFile.readUint32LE();
+		objectPtrList[i] = currentObject;
+
+		// Decide if this is a backboard, and keep track of it if it is...
+		if (currentObject->_objectType == kBackboard) {
+			// See which backboard it is...
+			if (((currentObject->minPoint.x + currentObject->maxPoint.x) / 2) < (MAX_WORLD_X / 2)) {
+				_vm->_basketball->_court->_backboardIndex[LEFT_BASKET] = i;
+			} else {
+				_vm->_basketball->_court->_backboardIndex[RIGHT_BASKET] = i;
+			}
+		}
+	}
+
+	_vm->_basketball->_court->_objectTree.initialize(objectPtrList);
+	
+
+	// Lower all the shields...
+	u32_userLowerShields(ALL_SHIELD_ID);
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userDeinitCourt() {
+	// Make sure the collision object tree has been cleared...
+	_vm->_basketball->_court->_objectTree.~CCollisionObjectTree();
+
+	// Lower all the shields...
+	u32_userLowerShields(ALL_SHIELD_ID);
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userInitBall(U32FltPoint3D &ballLocation, U32FltVector3D &bellVelocity, int radius, int ballID) {
+	CCollisionBasketball basketBall = _vm->_basketball->_court->_basketBall;
+
+	basketBall._description = "Basketball";
+	basketBall._objectType = kBall;
+	basketBall._objectID = ballID;
+	basketBall.center = ballLocation;
+	basketBall._velocity = bellVelocity;
+	basketBall.radius = radius;
+	basketBall._collisionEfficiency = 1.0F;
+	basketBall._friction = 0;
+	basketBall._ignore = false;
+
+	basketBall.save();
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userInitVirtualBall(U32FltPoint3D &ballLocation, U32FltVector3D &bellVelocity, int radius, int ballID) {
+	CCollisionBasketball virtualBall = _vm->_basketball->_court->_virtualBall;
+
+	virtualBall._description = "Virtual Basketball";
+	virtualBall._objectType = kBall;
+	virtualBall._objectID = ballID;
+	virtualBall.center = ballLocation;
+	virtualBall._velocity = bellVelocity;
+	virtualBall.radius = radius;
+	virtualBall._collisionEfficiency = 1.0F;
+	virtualBall._friction = 0;
+	virtualBall._ignore = false;
+
+	virtualBall.save();
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userDeinitBall() {
+	_vm->_basketball->_court->_basketBall._ignore = true;
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userDeinitVirtualBall() {
+	_vm->_basketball->_court->_virtualBall._ignore = true;
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userInitPlayer(int playerID, U32FltPoint3D &playerLocation, int height, int radius, bool bPlayerIsInGame) {
+	if (!((FIRST_PLAYER <= playerID) && (playerID <= LAST_PLAYER)))
+		error("LogicHEBasketball::u32_userInitPlayer(): Passed in invalid player ID");
+
+	// Cycle through all of the player slots until an empty one is found...
+	Common::Array<CCollisionPlayer> *playerList = _vm->_basketball->_court->getPlayerListPtr(playerID);
+	if (playerList->size() < MAX_PLAYERS_ON_TEAM) {
+		CCollisionPlayer newPlayer;
+		newPlayer._objectType = kPlayer;
+		newPlayer._objectID = playerID;
+		newPlayer.height = height;
+		newPlayer._catchHeight = PLAYER_CATCH_HEIGHT;
+		newPlayer.radius = radius;
+		newPlayer.center = playerLocation;
+		newPlayer.center.z = playerLocation.z + (height / 2);
+		newPlayer._collisionEfficiency = 0.5F;
+		newPlayer._friction = 0.5F;
+		newPlayer._playerIsInGame = bPlayerIsInGame;
+		newPlayer.save();
+		playerList->push_back(newPlayer);
+		return 1;
+	} else {
+		warning("LogicHEBasketball::u32_userInitPlayer(): There were no empty player slots. You can't initialize a new player until you deinit one.");
+		return 0;
+	}
+}
+
+int LogicHEBasketball::u32_userDeinitPlayer(int playerID) {
+	if (!((FIRST_PLAYER <= playerID) && (playerID <= LAST_PLAYER)))
+		error("LogicHEBasketball::u32_userDeinitPlayer(): Passed in invalid player ID");
+
+	int index = _vm->_basketball->_court->getPlayerIndex(playerID);
+	Common::Array<CCollisionPlayer> *playerList = _vm->_basketball->_court->getPlayerListPtr(playerID);
+	playerList->remove_at(index);
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userPlayerOff(int playerID) {
+	if (!((FIRST_PLAYER <= playerID) && (playerID <= LAST_PLAYER)))
+		error("LogicHEBasketball::u32_userPlayerOff(): Passed in invalid player ID");
+
+	_vm->_basketball->_court->getPlayerPtr(playerID)->_ignore = true;
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userPlayerOn(int playerID) {
+	if (!((FIRST_PLAYER <= playerID) && (playerID <= LAST_PLAYER)))
+		error("LogicHEBasketball::u32_userPlayerOn(): Passed in invalid player ID");
+
+	_vm->_basketball->_court->getPlayerPtr(playerID)->_ignore = false;
+
+	return 1;
+}
+
+static void trackCollisionObject(const ICollisionObject &sourceObject, const ICollisionObject &targetObject, CCollisionObjectVector *objectVector) {
+	float currentDist = sourceObject.getObjectDistance(targetObject);
+
+	// As an object moves backwards along its velocity vector and new collisions
+	// are detected, older collisions may become invalid. Here, we go through prior
+	// collisions, and see which ones are invalidated by this new collision.
+	for (CCollisionObjectVector::const_iterator objectIt = objectVector->begin();
+		 objectIt != objectVector->end();
+		 ++objectIt) {
+		float pastDist = sourceObject.getObjectDistance(**objectIt);
+
+		// If the distance between the source object and the current target object
+		// is less than or equal to the distance between the source object and the last
+		// target object, then the current target object is stored along side the last
+		// target object. Otherwise, the current object replaces the last object.
+		if ((fabs(pastDist - currentDist) < COLLISION_EPSILON) ||
+			(!sourceObject.isCollisionHandled(targetObject)) ||
+			(!sourceObject.isCollisionHandled(**objectIt))) {
+			break;
+		}
+	}
+
+	// Make sure that we aren't keeping track of the same object twice...
+	if (!objectVector->contains(targetObject)) {
+		objectVector->push_back(&targetObject);
+	}
+}
+
+int LogicHEBasketball::u32_userDetectBallCollision(U32FltPoint3D &ballLocation, U32FltVector3D &ballVector, int recordCollision, int ballID) {
+	bool ballIsClear = false; // Flag that indicates if the ball collided with any objects on its current vector
+	bool errorOccurred = false;
+
+	U32Distance3D distance;                 // The distance between the ball and a collision object candidate
+	CCollisionObjectVector targetList;      // All potential collision candidates
+	CCollisionObjectVector collisionVector; // All objects that have been collided with
+	CCollisionObjectVector rollingVector;   // All objects that have been rolled on
+
+	int collisionOccurred = 0;
+	int rollingHappened = 0;
+
+	// Determine which ball we're dealing with...
+	CCollisionBasketball *sourceBall = _vm->_basketball->_court->getBallPtr(ballID);
+
+	// Update the position and vector of the basketball...
+	sourceBall->center = ballLocation;
+	sourceBall->_velocity = ballVector;
+
+	// Clear the ball's collision stack...
+	sourceBall->_objectCollisionHistory.clear();
+	sourceBall->_objectRollingHistory.clear();
+
+	// Find out who our potential collision candidates are...
+	_vm->_basketball->fillBallTargetList((CCollisionSphere *)sourceBall, &targetList);
+
+	// See if there was an error while traversing the object tree,
+	// if there was put the player in the last known safe position...
+	if (_vm->_basketball->_court->_objectTree.checkErrors()) {
+		sourceBall->restore();
+	}
+
+	for (int i = 0; (i < MAX_BALL_COLLISION_PASSES) && (!ballIsClear); i++) {
+		float totalTime = 0; // The time it takes to back out of all objects we have intersected while on the current vector
+		ballIsClear = true;
+
+		// Go through all of the collision candidates....
+		for (size_t j = 0; j < targetList.size(); ++j) {
+			const ICollisionObject *targetObject = targetList[j];
+			assert(targetObject);
+
+			// See if we intersect the current object...
+			bool intersectionResult = sourceBall->ICollisionObject::testObjectIntersection(*targetObject, &distance);
+			if (intersectionResult) {
+				// If we are intersecting a moving object, make sure that we actually
+				// ran into them, and they didn't just run into us...
+				if (sourceBall->ICollisionObject::validateCollision(*targetObject, &distance)) {
+					// If we are intersecting the object, back out of it...
+					if (sourceBall->ICollisionObject::backOutOfObject(*targetObject, &distance, &totalTime)) {
+						// Move in to the exact point of collision...
+						if (sourceBall->ICollisionObject::nudgeObject(*targetObject, &distance, &totalTime)) {
+							// Keep track of this object so we can respond to the collision later...
+							trackCollisionObject(*(ICollisionObject *)sourceBall, *targetObject, &sourceBall->_objectCollisionHistory);
+
+							if (sourceBall->isCollisionHandled(*targetObject)) {
+								trackCollisionObject(*(ICollisionObject *)sourceBall, *targetObject, &collisionVector);
+								ballIsClear = false;
+							}
+
+							collisionOccurred = 1;
+						} else {
+							errorOccurred = true;
+						}
+					} else {
+						errorOccurred = true;
+					}
+				}
+			} else {
+				// See if we are passing over a player...
+				if (sourceBall->testCatch(*targetObject, &distance, _vm->_basketball->_court)) {
+					trackCollisionObject(*sourceBall, *targetObject, &sourceBall->_objectCollisionHistory);
+					collisionOccurred = true;
+				}
+			}
+
+			// See if we are rolling on the current object...
+			if (sourceBall->ICollisionObject::isOnObject(*targetObject, distance)) {
+				rollingHappened = 1;
+
+				if (!intersectionResult) {
+					// This is not really a collision, but the ball is rolling, so we want to slow it down...
+					trackCollisionObject(*(ICollisionObject *)sourceBall, *targetObject, &rollingVector);
+					trackCollisionObject(*(ICollisionObject *)sourceBall, *targetObject, &sourceBall->_objectRollingHistory);
+				}
+			}
+		}
+
+		// Adjust the ball's velocity and position due to any collisions...
+		sourceBall->handleCollisions(&rollingVector, &totalTime, false);
+		sourceBall->handleCollisions(&collisionVector, &totalTime, true);
+	}
+
+	// Keep track of how long we've been rolling...
+	if (rollingHappened) {
+		++sourceBall->m_rollingCount;
+	} else {
+		sourceBall->m_rollingCount = 0;
+	}
+
+	// If there were no errors this frame, save the position...
+	if (!errorOccurred) {
+		sourceBall->save();
+	}
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(sourceBall->center.x));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, _vm->_basketball->u32FloatToInt(sourceBall->center.y));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_C, _vm->_basketball->u32FloatToInt(sourceBall->center.z));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_D, _vm->_basketball->u32FloatToInt(sourceBall->_velocity.x));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_E, _vm->_basketball->u32FloatToInt(sourceBall->_velocity.y));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_F, _vm->_basketball->u32FloatToInt(sourceBall->_velocity.z));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_G, collisionOccurred);
+	writeScummVar(_vm1->VAR_U32_USER_VAR_H, rollingHappened != 0 ? 0 : 1);
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userDetectPlayerCollision(int playerID, U32FltPoint3D &playerLocation, U32FltVector3D &playerVector, bool bPlayerHasBall) {
+	U32Distance3D distance;                 // The distance between the ball and a collision object candidate
+	CCollisionObjectVector collisionVector; // All objects that have been collided with
+
+	int playerIsOnObject = 0;
+	int collisionOccurred = 0;
+
+	bool playerIsClear = false;
+	bool errorOccurred = false;
+
+	float totalTime = 0.0F; // The time it takes to back out of all objects we have intersected on this frame
+
+	if (!((FIRST_PLAYER <= playerID) && (playerID <= LAST_PLAYER)))
+		error("LogicHEBasketball::u32_userDetectPlayerCollision(): Passed in invalid player ID");
+
+	// Get the player who is being tested...
+	CCollisionPlayer *sourcePlayer = _vm->_basketball->_court->getPlayerPtr(playerID);
+
+	// Update the player's status...
+	sourcePlayer->_playerHasBall = bPlayerHasBall;
+
+	// In SCUMM code, the center of a player in the z dimension is at their feet.
+	// In U32 code, it is in the middle of the cylinder, so make the translation...
+	playerLocation.z += (sourcePlayer->height / 2);
+
+	// Update the player's position and velocity...
+	sourcePlayer->center = playerLocation;
+	sourcePlayer->_velocity = playerVector;
+	sourcePlayer->_movementType = kStraight;
+
+	// Clear the player's collision stack...
+	sourcePlayer->_objectCollisionHistory.clear();
+	sourcePlayer->_objectRollingHistory.clear();
+
+	// Find out who our potential collision candidates are...
+	CCollisionObjectVector targetList;
+	_vm->_basketball->fillPlayerTargetList(sourcePlayer, &targetList);
+
+	// See if there was an error while traversing the object tree,
+	// if there was put the player in the last known safe position...
+	if (_vm->_basketball->_court->_objectTree.checkErrors()) {
+		sourcePlayer->restore();
+	}
+
+	for (int i = 0; (i < MAX_PLAYER_COLLISION_PASSES) && (!playerIsClear); i++) {
+		playerIsClear = 1;
+
+		// Check all of the collision candidates...
+		for (size_t j = 0; j < targetList.size(); ++j) {
+			const ICollisionObject *targetObject = targetList[j];
+			assert(targetObject);
+
+			// See if we intersect the current object...
+			bool intersectionResult = sourcePlayer->ICollisionObject::testObjectIntersection(*targetObject, &distance);
+			if (intersectionResult) {
+				// If we are intersecting a moving object, make sure that we actually
+				// ran into them, and they didn't just run into us...
+				if (sourcePlayer->ICollisionObject::validateCollision(*targetObject, &distance)) {
+					// If we are intersecting an object, back out to the exact point of collision...
+					if (sourcePlayer->ICollisionObject::backOutOfObject(*targetObject, &distance, &totalTime)) {
+						// Move in to the exact point of collision...
+						if (sourcePlayer->ICollisionObject::nudgeObject(*targetObject, &distance, &totalTime)) {
+							trackCollisionObject(*sourcePlayer, *targetObject, &sourcePlayer->_objectCollisionHistory);
+							collisionOccurred = true;
+
+							if (sourcePlayer->ICollisionObject::isCollisionHandled(*targetObject)) {
+								trackCollisionObject(*sourcePlayer, *targetObject, &collisionVector);
+								playerIsClear = false;
+							}
+						} else {
+							errorOccurred = true;
+						}
+					} else {
+						errorOccurred = true;
+					}
+				}
+
+			} else {
+
+				// See if the virtual ball is passing over us...
+				if (sourcePlayer->testCatch(*targetObject, &distance, _vm->_basketball->_court)) {
+					trackCollisionObject(*sourcePlayer, *targetObject, &sourcePlayer->_objectCollisionHistory);
+					collisionOccurred = true;
+				}
+			}
+
+			// See if we are standing on the current object...
+			if (sourcePlayer->ICollisionObject::isOnObject(*targetObject, distance)) {
+				playerIsOnObject = true;
+			}
+		}
+
+		sourcePlayer->handleCollisions(&collisionVector, &totalTime, true);
+	}
+
+	// If there were no errors this frame, save the position...
+	if (!errorOccurred) {
+		sourcePlayer->save();
+	}
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(sourcePlayer->center.x));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, _vm->_basketball->u32FloatToInt(sourcePlayer->center.y));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_C, _vm->_basketball->u32FloatToInt(sourcePlayer->center.z - (sourcePlayer->height / 2)));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_D, _vm->_basketball->u32FloatToInt(sourcePlayer->_velocity.x));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_E, _vm->_basketball->u32FloatToInt(sourcePlayer->_velocity.y));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_F, _vm->_basketball->u32FloatToInt(sourcePlayer->_velocity.z));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_G, collisionOccurred);
+	writeScummVar(_vm1->VAR_U32_USER_VAR_H, playerIsOnObject != 0 ? 0 : 1);
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userGetLastBallCollision(int ballID) {
+	EObjectType lastObjectType = kNoObjectType;
+	int objectID = 0;
+
+	// Determine which ball we're dealing with
+	CCollisionSphere *sourceBall;
+	if (ballID == _vm->_basketball->_court->_basketBall._objectID) {
+		sourceBall = (CCollisionSphere *)&_vm->_basketball->_court->_basketBall;
+	} else if (ballID == _vm->_basketball->_court->_virtualBall._objectID) {
+		sourceBall = (CCollisionSphere *)&_vm->_basketball->_court->_virtualBall;
+	} else {
+		warning("LogicHEBasketball::u32_userGetLastBallCollision(): Invalid ball ID %d.", ballID);
+		sourceBall = (CCollisionSphere *)&_vm->_basketball->_court->_basketBall;
+	}
+
+	if (!sourceBall->_objectCollisionHistory.empty()) {
+		lastObjectType = sourceBall->_objectCollisionHistory.back()->_objectType;
+		objectID = sourceBall->_objectCollisionHistory.back()->_objectID;
+		sourceBall->_objectCollisionHistory.pop_back();
+	} else if (!sourceBall->_objectRollingHistory.empty()) {
+		lastObjectType = sourceBall->_objectRollingHistory.back()->_objectType;
+		objectID = sourceBall->_objectRollingHistory.back()->_objectID;
+		sourceBall->_objectRollingHistory.pop_back();
+	}
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, lastObjectType);
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, objectID);
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userGetLastPlayerCollision(int playerID) {
+	EObjectType lastObjectType = kNoObjectType;
+	int objectID = 0;
+	bool playerIsOnObject = false;
+
+	CCollisionPlayer *pPlayer = _vm->_basketball->_court->getPlayerPtr(playerID);
+
+	if (!pPlayer->_objectCollisionHistory.empty()) {
+		const ICollisionObject *targetObject = pPlayer->_objectCollisionHistory.back();
+
+		lastObjectType = targetObject->_objectType;
+		objectID = targetObject->_objectID;
+
+		// See if we are standing on the current object
+		U32Distance3D distance;
+		pPlayer->ICollisionObject::testObjectIntersection(*targetObject, &distance);
+		if (pPlayer->ICollisionObject::isOnObject(*targetObject, distance)) {
+			playerIsOnObject = true;
+		}
+
+		pPlayer->_objectCollisionHistory.pop_back();
+	}
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, lastObjectType);
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, objectID);
+	writeScummVar(_vm1->VAR_U32_USER_VAR_C, playerIsOnObject);
+
+	return 1;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/collision/bball_collision.h b/engines/scumm/he/basketball/collision/bball_collision.h
new file mode 100644
index 00000000000..3d67025b4aa
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision.h
@@ -0,0 +1,48 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_H
+#define SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+
+namespace Scumm {
+
+#define RIM_CE                       0.4F
+#define BACKBOARD_CE                 0.3F
+#define FLOOR_CE                     0.65F
+
+#define LEFT_BASKET                  0
+#define RIGHT_BASKET                 1
+
+#define RIM_WIDTH                    (WORLD_UNIT_MULTIPLIER / 11)
+#define RIM_RADIUS                   ((3 * WORLD_UNIT_MULTIPLIER) / 4)
+
+#define MAX_BALL_COLLISION_PASSES    10
+#define MAX_PLAYER_COLLISION_PASSES  3
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_H
diff --git a/engines/scumm/he/basketball/collision/bball_collision_basketball.cpp b/engines/scumm/he/basketball/collision/bball_collision_basketball.cpp
new file mode 100644
index 00000000000..f1451968155
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_basketball.cpp
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/basketball/court.h"
+#include "scumm/he/basketball/collision/bball_collision_basketball.h"
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+#include "scumm/he/basketball/collision/bball_collision_sphere.h"
+#include "scumm/he/basketball/collision/bball_collision_box.h"
+#include "scumm/he/basketball/collision/bball_collision_cylinder.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+#include "scumm/he/basketball/collision/bball_collision_node.h"
+#include "scumm/he/basketball/collision/bball_collision_tree.h"
+
+namespace Scumm {
+
+bool CCollisionBasketball::testCatch(const ICollisionObject &targetObject, U32Distance3D *distance, CBBallCourt *court) {
+	if (targetObject._objectType == kPlayer) {
+		ICollisionObject *object = const_cast<ICollisionObject *>(&targetObject);
+		CCollisionPlayer *player = static_cast<CCollisionPlayer *>(object);
+		return (player->testCatch(*(ICollisionObject *)this, distance, court));
+	} else {
+		return false;
+	}
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/collision/bball_collision_basketball.h b/engines/scumm/he/basketball/collision/bball_collision_basketball.h
new file mode 100644
index 00000000000..24fbd7aab71
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_basketball.h
@@ -0,0 +1,47 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_BASKETBALL_H
+#define SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_BASKETBALL_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/basketball/court.h"
+#include "scumm/he/basketball/basketball.h"
+#include "scumm/he/basketball/collision/bball_collision_sphere.h"
+
+namespace Scumm {
+
+class CBBallCourt;
+
+class CCollisionBasketball : public CCollisionSphere {
+public:
+	CCollisionBasketball() {}
+	~CCollisionBasketball() {}
+
+	bool testCatch(const ICollisionObject &targetObject, U32Distance3D *distance, CBBallCourt *court);
+};
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_BASKETBALL_H
diff --git a/engines/scumm/he/basketball/collision/bball_collision_box.cpp b/engines/scumm/he/basketball/collision/bball_collision_box.cpp
new file mode 100644
index 00000000000..738e9b1fc54
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_box.cpp
@@ -0,0 +1,59 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+#include "scumm/he/basketball/collision/bball_collision_sphere.h"
+#include "scumm/he/basketball/collision/bball_collision_box.h"
+#include "scumm/he/basketball/collision/bball_collision_cylinder.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+#include "scumm/he/basketball/collision/bball_collision_node.h"
+#include "scumm/he/basketball/collision/bball_collision_tree.h"
+
+namespace Scumm {
+
+U32FltPoint3D CCollisionBox::findNearestPoint(const U32FltPoint3D &testPoint) const {
+	U32FltPoint3D boxPoint;
+
+	for (int i = X_INDEX; i <= Z_INDEX; i++) {
+		EDimension dimension = (EDimension)i;
+
+		if (testPoint[dimension] < minPoint[dimension]) {
+			boxPoint[dimension] = minPoint[dimension];
+		} else if (testPoint[dimension] > maxPoint[dimension]) {
+			boxPoint[dimension] = maxPoint[dimension];
+		} else {
+			boxPoint[dimension] = testPoint[dimension];
+		}
+	}
+
+	return boxPoint;
+}
+
+U32BoundingBox CCollisionBox::getBoundingBox() const {
+	return *this;
+}
+
+U32BoundingBox CCollisionBox::getBigBoundingBox() const {
+	return *this;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/collision/bball_collision_box.h b/engines/scumm/he/basketball/collision/bball_collision_box.h
new file mode 100644
index 00000000000..46dfea4b4d5
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_box.h
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_BOX_H
+#define SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_BOX_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+
+namespace Scumm {
+
+class CCollisionBox : public ICollisionObject, public U32BoundingBox {
+
+public:
+	CCollisionBox() : ICollisionObject(kBox) {}
+	~CCollisionBox() {}
+
+	U32FltPoint3D findNearestPoint(const U32FltPoint3D &testPoint) const override;
+	U32BoundingBox getBoundingBox() const override;
+	U32BoundingBox getBigBoundingBox() const override;
+};
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_BOX_H
diff --git a/engines/scumm/he/basketball/collision/bball_collision_cylinder.cpp b/engines/scumm/he/basketball/collision/bball_collision_cylinder.cpp
new file mode 100644
index 00000000000..6f813c94fd4
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_cylinder.cpp
@@ -0,0 +1,1130 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+
+#include "scumm/he/basketball/collision/bball_collision.h"
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+#include "scumm/he/basketball/collision/bball_collision_sphere.h"
+#include "scumm/he/basketball/collision/bball_collision_box.h"
+#include "scumm/he/basketball/collision/bball_collision_cylinder.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+#include "scumm/he/basketball/collision/bball_collision_node.h"
+#include "scumm/he/basketball/collision/bball_collision_tree.h"
+
+namespace Scumm {
+
+float CCollisionCylinder::getDimensionDistance(const CCollisionBox &targetObject, EDimension dimension) const {
+	if (center[dimension] < targetObject.minPoint[dimension]) {
+		return (center[dimension] - targetObject.minPoint[dimension]);
+	} else if (center[dimension] > targetObject.maxPoint[dimension]) {
+		return (center[dimension] - targetObject.maxPoint[dimension]);
+	} else {
+		return 0;
+	}
+}
+
+float CCollisionCylinder::getDimensionDistance(const CCollisionSphere &targetObject, EDimension dimension) const {
+	float centerDistance = center[dimension] - targetObject.center[dimension];
+
+	if (dimension == Z_INDEX) {
+		if (centerDistance < -(targetObject.radius)) {
+			return (centerDistance + targetObject.radius);
+		} else if (centerDistance > (targetObject.radius)) {
+			return (centerDistance - targetObject.radius);
+		} else {
+			return 0;
+		}
+	} else {
+		return centerDistance;
+	}
+}
+
+float CCollisionCylinder::getDimensionDistance(const CCollisionCylinder &targetObject, EDimension dimension) const {
+	float centerDistance = center[dimension] - targetObject.center[dimension];
+
+	if (dimension == Z_INDEX) {
+		if (centerDistance < -(targetObject.height / 2)) {
+			return (centerDistance + (targetObject.height / 2));
+		} else if (centerDistance > (targetObject.height / 2)) {
+			return (centerDistance - (targetObject.height / 2));
+		} else {
+			return 0;
+		}
+	} else {
+		return centerDistance;
+	}
+}
+
+float CCollisionCylinder::getObjectDistance(const CCollisionSphere &targetObject) const {
+	U32Distance3D distance;
+
+	distance.x = getDimensionDistance(targetObject, X_INDEX);
+	distance.y = getDimensionDistance(targetObject, Y_INDEX);
+	distance.z = getDimensionDistance(targetObject, Z_INDEX);
+
+	float xyDistance = distance.xyMagnitude() - radius - targetObject.radius;
+	if (xyDistance < 0)
+		xyDistance = 0;
+
+	float zDistance = fabs(distance.z) - (height / 2) - targetObject.radius;
+	if (zDistance < 0)
+		zDistance = 0;
+
+	float totalDistance = sqrt((xyDistance * xyDistance) + (zDistance * zDistance));
+	return (totalDistance);
+}
+
+float CCollisionCylinder::getObjectDistance(const CCollisionBox &targetObject) const {
+	U32Distance3D distance;
+
+	distance.x = getDimensionDistance(targetObject, X_INDEX);
+	distance.y = getDimensionDistance(targetObject, Y_INDEX);
+	distance.z = getDimensionDistance(targetObject, Z_INDEX);
+
+	float xyDistance = distance.xyMagnitude() - radius;
+	if (xyDistance < 0)
+		xyDistance = 0;
+
+	float zDistance = fabs(distance.z) - (height / 2);
+	if (zDistance < 0)
+		zDistance = 0;
+
+	float totalDistance = sqrt((xyDistance * xyDistance) + (zDistance * zDistance));
+	return (totalDistance);
+}
+
+float CCollisionCylinder::getObjectDistance(const CCollisionCylinder &targetObject) const {
+	U32Distance3D distance;
+
+	distance.x = getDimensionDistance(targetObject, X_INDEX);
+	distance.y = getDimensionDistance(targetObject, Y_INDEX);
+	distance.z = getDimensionDistance(targetObject, Z_INDEX);
+
+	float xyDistance = distance.xyMagnitude() - radius - targetObject.radius;
+	if (xyDistance < 0)
+		xyDistance = 0;
+
+	float zDistance = fabs(distance.z) - (height / 2) - (targetObject.height / 2);
+	if (zDistance < 0)
+		zDistance = 0;
+
+	float totalDistance = sqrt((xyDistance * xyDistance) + (zDistance * zDistance));
+	return (totalDistance);
+}
+
+bool CCollisionCylinder::testObjectIntersection(const CCollisionSphere &targetObject, U32Distance3D *distance) const {
+	// Get the distance between the ball and the cylinder...
+	distance->x = getDimensionDistance(targetObject, X_INDEX);
+	distance->y = getDimensionDistance(targetObject, Y_INDEX);
+	distance->z = getDimensionDistance(targetObject, Z_INDEX);
+
+	if (distance->xyMagnitude() < (radius + targetObject.radius)) {
+		return (fabs(distance->z) < (height / 2));
+	} else {
+		return false;
+	}
+}
+
+bool CCollisionCylinder::testObjectIntersection(const CCollisionBox &targetObject, U32Distance3D *distance) const {
+	// Get the distance between the ball and the cylinder...
+	distance->x = getDimensionDistance(targetObject, X_INDEX);
+	distance->y = getDimensionDistance(targetObject, Y_INDEX);
+	distance->z = getDimensionDistance(targetObject, Z_INDEX);
+	;
+
+	if (distance->xyMagnitude() < radius) {
+		return (fabs(distance->z) < (height / 2));
+	} else {
+		return false;
+	}
+}
+
+bool CCollisionCylinder::testObjectIntersection(const CCollisionCylinder &targetObject, U32Distance3D *distance) const {
+	// Get the distance between the ball and the cylinder...
+	distance->x = getDimensionDistance(targetObject, X_INDEX);
+	distance->y = getDimensionDistance(targetObject, Y_INDEX);
+	distance->z = getDimensionDistance(targetObject, Z_INDEX);
+
+	if (distance->xyMagnitude() < (radius + targetObject.radius)) {
+		return (fabs(distance->z) < (height / 2));
+	} else {
+		return false;
+	}
+}
+
+float CCollisionCylinder::getPenetrationTime(const CCollisionBox &targetObject, const U32Distance3D &distance, EDimension dimension) const {
+	float collisionDepth;
+
+	if (dimension == Z_INDEX) {
+		if (distance[dimension] > 0) {
+			collisionDepth = (height / 2) - distance[dimension];
+		} else if (distance[dimension] < 0) {
+			collisionDepth = -(height / 2) - distance[dimension];
+		} else {
+			collisionDepth = 0;
+		}
+	} else {
+		if (distance[dimension] > 0) {
+			collisionDepth = radius - distance[dimension];
+		} else if (distance[dimension] < 0) {
+			collisionDepth = -radius - distance[dimension];
+		} else {
+			return 0;
+		}
+	}
+
+	float tFinal = (_velocity[dimension] == 0) ? 0 : (collisionDepth / -_velocity[dimension]);
+
+	return tFinal;
+}
+
+float CCollisionCylinder::getPenetrationTime(const CCollisionCylinder &targetObject, const U32Distance3D &distance, EDimension dimension) const {
+	float collisionDepth;
+
+	if (dimension == Z_INDEX) {
+		if (distance[dimension] > 0) {
+			collisionDepth = (height / 2) - distance[dimension];
+		} else if (distance[dimension] < 0) {
+			collisionDepth = (height / 2) + distance[dimension];
+		} else {
+			collisionDepth = 0;
+		}
+	} else {
+		if (distance[dimension] > 0) {
+			collisionDepth = radius + targetObject.radius - distance[dimension];
+		} else if (distance[dimension] < 0) {
+			collisionDepth = -radius - targetObject.radius - distance[dimension];
+		} else {
+			collisionDepth = 0;
+		}
+	}
+
+	float tFinal = (_velocity[dimension] == 0) ? 0 : (collisionDepth / -_velocity[dimension]);
+
+	return tFinal;
+}
+
+bool CCollisionCylinder::validateCollision(const CCollisionCylinder &targetObject, U32Distance3D *distance) {
+	float zCollisionTime = getPenetrationTime(targetObject, *distance, Z_INDEX);
+
+	if (((zCollisionTime > 1) || (zCollisionTime == 0)) &&
+		(_velocity.xyMagnitude() == 0) &&
+		(_velocity.z != 0)) {
+		forceOutOfObject(targetObject, distance);
+		return _ignore;
+	} else {
+		// If a player raises their shields and another player is standing inside
+		// the shields, that player should be pushed out...
+		if ((_velocity.magnitude() == 0) &&
+			(_shieldRadius == 0) &&
+			(targetObject._shieldRadius != 0)) {
+			return true;
+		} else {
+			// See if we're hitting the top or bottom of this...
+			if (((distance->z > 0) && (_velocity.z <= 0)) ||
+				((distance->z < 0) && (_velocity.z >= 0))) {
+				return true;
+			} else {
+				// Create a vector from the center of the target cylinder to the center of
+				// this cylinder...
+				U32FltVector2D centerVector = targetObject.center - center;
+
+				float aMag = _velocity.magnitude();
+				float bMag = centerVector.magnitude();
+				float aDotb = centerVector * _velocity;
+
+				if (aMag == 0) {
+					// If this object isn't moving, this can't be a valid collision...
+					return false;
+				}
+
+				if (bMag == 0) {
+					// bMag is 0, it is possible that this sphere penetrated too far into the target
+					// object. If this is the case, we'll go ahead and validate the collision...
+					return true;
+				}
+
+				double angleCosine = aDotb / (aMag * bMag);
+
+				if (angleCosine > 0) {
+					return true;
+				} else {
+					return false;
+				}
+			}
+		}
+	}
+}
+
+bool CCollisionCylinder::backOutOfObject(const CCollisionBox &targetObject, U32Distance3D *distance, float *timeUsed) {
+	// See if this is an object we can simply step up onto...
+	if (((height / 2) - distance->z) <= MAX_STEP_HEIGHT) {
+		center.z += ((height / 2) - distance->z);
+		return true;
+	} else if (_movementType == kCircular) {
+		// Since we are moving with a circular motion, try circling out of the object...
+		if (circleOutOfObject(targetObject, distance, timeUsed)) {
+			return true;
+		} else {
+			// If we aren't able to circle out of the object, back out in a straight line...
+			_movementType = kStraight;
+			return backStraightOutOfObject(targetObject, distance, timeUsed);
+		}
+	} else {
+		_movementType = kStraight;
+		return backStraightOutOfObject(targetObject, distance, timeUsed);
+	}
+}
+
+bool CCollisionCylinder::backOutOfObject(const CCollisionCylinder &targetObject, U32Distance3D *distance, float *timeUsed) {
+	if (_velocity.magnitude() == 0) {
+		forceOutOfObject(targetObject, distance);
+		return true;
+	} else if ((_movementType == kCircular) && (&targetObject != _revCenter)) {
+		// Since we are moving with a circular motion, try circling out of the object...
+		if (circleOutOfObject(targetObject, distance, timeUsed)) {
+			return true;
+		} else {
+			// If we aren't able to circle out of the object, back out in a straight line...
+			_movementType = kStraight;
+			return backStraightOutOfObject(targetObject, distance, timeUsed);
+		}
+	} else {
+		_movementType = kStraight;
+		return backStraightOutOfObject(targetObject, distance, timeUsed);
+	}
+}
+
+bool CCollisionCylinder::backStraightOutOfObject(const ICollisionObject &targetObject, U32Distance3D *distance, float *timeUsed) {
+	if (_velocity.magnitude() == 0)
+		return true;
+
+	U32FltPoint3D startPosition = center;
+
+	int loopCounter = 0;
+
+	while (ICollisionObject::testObjectIntersection(targetObject, distance)) {
+		float collisionTimes[3];
+
+		collisionTimes[X_INDEX] = ICollisionObject::getPenetrationTime(targetObject, *distance, X_INDEX);
+		collisionTimes[Y_INDEX] = ICollisionObject::getPenetrationTime(targetObject, *distance, Y_INDEX);
+		collisionTimes[Z_INDEX] = ICollisionObject::getPenetrationTime(targetObject, *distance, Z_INDEX);
+
+		Std::sort(collisionTimes, collisionTimes + Z_INDEX + 1);
+
+		float collisionTime = COLLISION_SMALL_TIME_INCREMENT;
+		if (collisionTimes[2] > 0)
+			collisionTime = collisionTimes[2];
+		if (collisionTimes[1] > 0)
+			collisionTime = collisionTimes[1];
+		if (collisionTimes[0] > 0)
+			collisionTime = collisionTimes[0];
+
+		*timeUsed += collisionTime;
+
+		// If we take too long to back out, something is wrong,
+		// so restore the object to an ok state...
+		if ((*timeUsed > COLLISION_BACK_OUT_TIME_LIMIT) && (*timeUsed != collisionTime)) {
+			warning("CCollisionCylinder::backStraightOutOfObject(): It took too long for one object to back out of another.  Ignore and U32 will attempt to correct.");
+			center = startPosition;
+			restore();
+			return false;
+		}
+
+		center.x -= (collisionTime * _velocity.x);
+		center.y -= (collisionTime * _velocity.y);
+		center.z -= (collisionTime * _velocity.z);
+
+		// Make doubly sure we don't loop forever...
+		if (++loopCounter > 500)
+			return false;
+	}
+
+	return true;
+}
+
+bool CCollisionCylinder::getCornerIntersection(const CCollisionBox &targetObject, const U32Distance3D &distance, U32FltPoint2D *intersection) {
+	float centerDistance = (center - _revCenterPt).xyMagnitude();
+	U32BoundingBox testBox = targetObject;
+	U32FltPoint2D boxCorner;
+
+	// Get the corner that we think we've collided with...
+	if (distance.x < 0) {
+		boxCorner.x = targetObject.minPoint.x;
+		testBox.maxPoint.x += centerDistance;
+	} else if (distance.x > 0) {
+		boxCorner.x = targetObject.maxPoint.x;
+		testBox.minPoint.x -= centerDistance;
+	} else {
+		return false;
+	}
+
+	if (distance.y < 0) {
+		boxCorner.y = targetObject.minPoint.y;
+		testBox.maxPoint.y += centerDistance;
+	} else if (distance.y > 0) {
+		boxCorner.y = targetObject.maxPoint.y;
+		testBox.maxPoint.y -= centerDistance;
+	} else {
+		return false;
+	}
+
+	U32FltPoint2D point1;
+	U32FltPoint2D point2;
+
+	int pointCount = getEquidistantPoint(boxCorner, radius,
+										 _revCenterPt, centerDistance,
+										 &point1,
+										 &point2);
+
+	switch (pointCount) {
+	case 0:
+		return false;
+		break;
+
+	case 1:
+		*intersection = point1;
+		break;
+
+	case 2:
+		if (!testBox.isPointWithin(point1)) {
+			*intersection = point1;
+		} else if (!testBox.isPointWithin(point2)) {
+			*intersection = point2;
+		} else {
+			return false;
+		}
+		break;
+	}
+
+	return true;
+}
+
+bool CCollisionCylinder::circleOutOfObject(const CCollisionBox &targetObject, U32Distance3D *distance, float *timeUsed) {
+	bool validXIntercept = true;
+	bool validYIntercept = true;
+
+	// If this object isn't moving, push it out of the collision...
+	if (_velocity.xyMagnitude() == 0) {
+		forceOutOfObject(targetObject, distance);
+		testObjectIntersection(targetObject, distance);
+		return false;
+	}
+
+	// Keep track of where we started...
+	U32FltPoint3D oldCenter = center;
+
+	// Get the distance between the center of this cylinder and the point it is
+	// revolving around...
+	float centerDistance = (center - _revCenterPt).xyMagnitude();
+
+	// Find out in which direction we are going to revolve around the target point...
+	ERevDirection revDirection = (getRevDirection() == kClockwise) ? kCounterClockwise : kClockwise;
+
+	// Find where this cylinder would touch the box in the x dimension...
+	U32FltPoint2D xIntercept;
+	if (distance->y < 0) {
+		xIntercept.y = targetObject.minPoint.y - radius;
+		float yDist = xIntercept.y - _revCenterPt.y;
+
+		float xInterceptSquared = centerDistance * centerDistance - yDist * yDist;
+
+		if (xInterceptSquared < 0) {
+			validXIntercept = getCornerIntersection(targetObject,
+													*distance,
+													&xIntercept);
+		} else {
+			if (revDirection == kClockwise) {
+				xIntercept.x = sqrt(xInterceptSquared) + _revCenterPt.x;
+			} else {
+				xIntercept.x = -sqrt(xInterceptSquared) + _revCenterPt.x;
+			}
+		}
+	} else if (distance->y > 0) {
+		xIntercept.y = targetObject.maxPoint.y + radius;
+		float yDist = xIntercept.y - _revCenterPt.y;
+
+		float xInterceptSquared = centerDistance * centerDistance - yDist * yDist;
+
+		if (xInterceptSquared < 0) {
+			validXIntercept = getCornerIntersection(targetObject,
+													*distance,
+													&xIntercept);
+		} else {
+			if (revDirection == kClockwise) {
+				xIntercept.x = -sqrt(xInterceptSquared) + _revCenterPt.x;
+			} else {
+				xIntercept.x = sqrt(xInterceptSquared) + _revCenterPt.x;
+			}
+		}
+	} else {
+		validXIntercept = false;
+	}
+
+	// If we found a valid place to back out to, try going there...
+	if (validXIntercept) {
+		center.x = xIntercept.x;
+		center.y = xIntercept.y;
+
+		forceOutOfObject(targetObject, distance);
+		testObjectIntersection(targetObject, distance);
+		return true;
+	}
+
+	// Find where this cylinder would touch the box in the y dimension...
+	U32FltPoint2D yIntercept;
+	if (distance->x < 0) {
+		yIntercept.x = targetObject.minPoint.x - radius;
+		float xDist = yIntercept.x - _revCenterPt.x;
+
+		float yInterceptSquared = centerDistance * centerDistance - xDist * xDist;
+
+		if (yInterceptSquared < 0) {
+			validYIntercept = getCornerIntersection(targetObject,
+													*distance,
+													&yIntercept);
+		} else {
+			if (revDirection == kClockwise) {
+				yIntercept.y = -sqrt(yInterceptSquared) + _revCenterPt.y;
+			} else {
+				yIntercept.y = sqrt(yInterceptSquared) + _revCenterPt.y;
+			}
+		}
+	} else if (distance->x > 0) {
+		yIntercept.x = targetObject.maxPoint.x + radius;
+		float xDist = yIntercept.x - _revCenterPt.x;
+
+		float yInterceptSquared = centerDistance * centerDistance - xDist * xDist;
+
+		if (yInterceptSquared < 0) {
+			validYIntercept = getCornerIntersection(targetObject,
+													*distance,
+													&yIntercept);
+		} else {
+			if (revDirection == kClockwise) {
+				yIntercept.y = sqrt(yInterceptSquared) + _revCenterPt.y;
+			} else {
+				yIntercept.y = -sqrt(yInterceptSquared) + _revCenterPt.y;
+			}
+		}
+	} else {
+		validYIntercept = false;
+	}
+
+	// If we found a valid place to back out to, try going there...
+	if (validYIntercept) {
+		center.x = yIntercept.x;
+		center.y = yIntercept.y;
+
+		forceOutOfObject(targetObject, distance);
+		testObjectIntersection(targetObject, distance);
+		return true;
+	}
+
+	// If we get here, we weren't able to circle out...
+	center = oldCenter;
+	testObjectIntersection(targetObject, distance);
+	return false;
+}
+
+bool CCollisionCylinder::circleOutOfObject(const CCollisionCylinder &targetObject, U32Distance3D *distance, float *timeUsed) {
+	// Get the distance from the target cylinder that we want to be...
+	float targetDistance = radius + targetObject.radius;
+
+	// Get the distance from the revolution center that we want to be...
+	U32FltVector2D sourceVector = center - _revCenterPt;
+	float revDistance = sourceVector.magnitude();
+
+	// Find out in which direction we are going to revolve around the target point...
+	ERevDirection revDirection = (getRevDirection() == kClockwise) ? kCounterClockwise : kClockwise;
+
+	U32FltPoint2D point1;
+	U32FltPoint2D point2;
+
+	int points = getEquidistantPoint(targetObject.center, targetDistance,
+									 _revCenterPt, revDistance,
+									 &point1,
+									 &point2);
+
+	switch (points) {
+	case 0:
+		warning("CCollisionCylinder::circleOutOfObject(): Could not find point of intersection.");
+		return false;
+		break;
+
+	case 1:
+		center.x = point1.x;
+		center.y = point1.y;
+		break;
+
+	case 2:
+		U32FltVector2D vector1 = point1 - _revCenterPt;
+		U32FltVector2D vector2 = point2 - _revCenterPt;
+
+		ERevDirection direction1 = (sourceVector.getRevDirection(vector1));
+		ERevDirection direction2 = (sourceVector.getRevDirection(vector2));
+
+		if (direction1 == direction2) {
+			warning("CCollisionCylinder::circleOutOfObject(): Both directions are the same.  That's weird.");
+		} else if (direction1 == revDirection) {
+			center.x = point1.x;
+			center.y = point1.y;
+		} else {
+			center.x = point2.x;
+			center.y = point2.y;
+		}
+		break;
+	}
+
+	testObjectIntersection(targetObject, distance);
+	forceOutOfObject(targetObject, distance);
+
+	return true;
+}
+
+bool CCollisionCylinder::nudgeObject(const CCollisionBox &targetObject, U32Distance3D *distance, float *timeUsed) {
+	float tFinal = 0;
+
+	// To nudge the cylinder precisely against the box, we need to calculate when the
+	// square root of the sum of the squared distances between the sphere and the three
+	// planes equals the radius of the sphere.
+	// Here we will construct a quadratic equation to solve for time.
+
+	double a = 0;
+	double b = 0;
+	double c = -(radius * radius);
+
+	// See if we are standing on the object...
+	if ((distance->z == (height / 2)) &&
+		(distance->xyMagnitude() == 0)) {
+		return true;
+	}
+
+	// Only do caluclations for the x and y dimensions, as we will handle z seperately...
+	for (int i = X_INDEX; i <= Y_INDEX; ++i) {
+		EDimension dim = (EDimension)i;
+
+		// If the ball is already within the boundaries of the box in a certain dimension,
+		// we don't want to include that dimension in the equation...
+		if ((*distance)[dim] != 0) {
+			a += (_velocity[dim] * _velocity[dim]);
+			b += (2 * _velocity[dim] * (*distance)[dim]);
+			c += ((*distance)[dim] * (*distance)[dim]);
+		}
+	}
+
+	if (((b * b) < (4 * a * c)) || (a == 0)) {
+		tFinal = -getPenetrationTime(targetObject, *distance, Z_INDEX);
+
+		assert(tFinal >= 0);
+	} else {
+		// Now we have two answer candidates, and we want the smallest
+		// of the two that is greater than 0...
+		double t1 = (-b + sqrt(b * b - 4 * a * c)) / (2 * a);
+		double t2 = (-b - sqrt(b * b - 4 * a * c)) / (2 * a);
+
+		double tXY = 0;
+		if ((0 <= t1) && (t1 <= t2)) {
+			tXY = t1;
+		} else if ((0 <= t2) && (t2 <= t1)) {
+			tXY = t2;
+		}
+
+		float tZ = -getPenetrationTime(targetObject, *distance, Z_INDEX);
+		tFinal = ((0 < tZ) && (tZ < tXY)) ? tZ : tXY;
+	}
+
+	// Update the position of the ball...
+	center.x += _velocity.x * tFinal;
+	center.y += _velocity.y * tFinal;
+	center.z += _velocity.z * tFinal;
+	*timeUsed -= tFinal;
+
+	testObjectIntersection(targetObject, distance);
+	return true;
+}
+
+bool CCollisionCylinder::nudgeObject(const CCollisionCylinder &targetObject, U32Distance3D *distance, float *timeUsed) {
+	float collisionTimeFinal;
+
+	U32FltVector3D xyVelocity = _velocity;
+	xyVelocity.z = 0;
+
+	// Find the distance between the two centers that would indicate an exact collision...
+	float intersectionDist = radius + targetObject.radius;
+
+	// Set up a vector pointing from the center of this sphere to the
+	// center of the target cylinder...
+	U32FltVector3D centerVector;
+	centerVector.x = targetObject.center.x - center.x;
+	centerVector.y = targetObject.center.y - center.y;
+
+	float centerDistance = centerVector.xyMagnitude();
+
+	if (centerDistance > intersectionDist) {
+		// Project the center vector onto the velocity vector.
+		// This is the distance along the velocity vector from the center
+		// of the sphere to the point that is parallel with the center
+		// of the cylinder...
+		float parallelDistance = centerVector.projectScalar(xyVelocity);
+
+		// Find the distance between the center of the target object to the
+		// point that that is distance2 units along the the velocity vector...
+		parallelDistance = ((centerDistance * centerDistance) >=
+							(parallelDistance * parallelDistance))
+							   ? parallelDistance
+							   : COPY_SIGN(centerDistance, parallelDistance);
+
+		// Make sure we don't try to sqrt by negative number...
+		if (parallelDistance > centerDistance) {
+			warning("CCollisionCylinder::nudgeObject(): Tried to sqrt by negative number.");
+			centerDistance = parallelDistance;
+		}
+
+		float perdistance = sqrt(centerDistance * centerDistance - parallelDistance * parallelDistance);
+
+		// Now we need to find the point along the velocity vector where the distance 
+		// between that point and the center of the target cylinder equals intersectionDist.
+		// We calculate using distance 2, distance3 and intersectionDist...
+		perdistance = ((intersectionDist * intersectionDist) >=
+						(perdistance * perdistance))
+						   ? perdistance
+						   : COPY_SIGN(intersectionDist, perdistance);
+
+		// Make sure we don't try to sqrt by negative number...
+		if (perdistance > intersectionDist) {
+			warning("CCollisionCylinder::nudgeObject(): Tried to sqrt by negative number.");
+			intersectionDist = perdistance;
+		}
+
+		float xyCollisionDist = parallelDistance - sqrt(intersectionDist * intersectionDist - perdistance * perdistance);
+		collisionTimeFinal = (_velocity.xyMagnitude() == 0) ? 0 : (xyCollisionDist / _velocity.xyMagnitude());
+	} else {
+		collisionTimeFinal = -getPenetrationTime(targetObject, *distance, Z_INDEX);
+	}
+
+	center.x += collisionTimeFinal * _velocity.x;
+	center.y += collisionTimeFinal * _velocity.y;
+	center.z += collisionTimeFinal * _velocity.z;
+	*timeUsed -= collisionTimeFinal;
+
+	testObjectIntersection(targetObject, distance);
+	return true;
+}
+
+float CCollisionCylinder::getPointOfCollision(const CCollisionBox &targetBox, U32Distance3D distance, EDimension dimension) const {
+	if (dimension == Z_INDEX) {
+		if ((distance[dimension] + (height / 2)) < 0) {
+			return targetBox.minPoint[dimension];
+		} else if ((distance[dimension] - (height / 2)) > 0) {
+			return targetBox.maxPoint[dimension];
+		} else {
+			return center[dimension];
+		}
+	} else {
+		if (distance[dimension] < 0) {
+			return targetBox.minPoint[dimension];
+		} else if (distance[dimension] > 0) {
+			return targetBox.maxPoint[dimension];
+		} else {
+			return center[dimension];
+		}
+	}
+}
+
+ERevDirection CCollisionCylinder::getRevDirection() const {
+	if (_movementType != kCircular)
+		warning("CCollisionCylinder::getRevDirection(): We can't get a revolution direction if we aren't moving in a circular fashion.");
+
+	U32FltVector3D targetVector = _revCenterPt - center;
+
+	if ((_velocity.xyMagnitude() != 0) &&
+		(targetVector.xyMagnitude() != 0)) {
+		U32FltVector3D vector1 = targetVector;
+		U32FltVector3D vector2 = _velocity;
+
+		vector1.z = 0;
+		vector2.z = 0;
+
+		U32FltVector3D vector3 = vector1.cross(vector2);
+
+		return (vector3.z > 0) ? kClockwise : kCounterClockwise;
+	} else {
+		warning("CCollisionCylinder::getRevDirection(): Division by zero attempted, ignoring...");
+		return kNone;
+	}
+}
+
+void CCollisionCylinder::forceOutOfObject(const ICollisionObject &targetObject, U32Distance3D *distance) {
+	U32FltVector3D targetVector;
+	U32FltPoint3D intersectionPoint;
+
+	const CCollisionBox *pTargetBox = nullptr;
+	const CCollisionCylinder *pTargetCylinder = nullptr;
+
+	float targetHeight;
+	float targetRadius;
+	U32FltPoint3D targetCenter;
+
+	if (_ignore)
+		return;
+
+	switch (targetObject._objectShape) {
+	case kBox:
+		pTargetBox = static_cast<const CCollisionBox *>(&targetObject);
+
+		for (int i = X_INDEX; i <= Y_INDEX; ++i) {
+			EDimension dim = (EDimension)i;
+
+			if (center[dim] < pTargetBox->minPoint[dim]) {
+				intersectionPoint[dim] = pTargetBox->minPoint[dim];
+			} else if (center[dim] > pTargetBox->maxPoint[dim]) {
+				intersectionPoint[dim] = pTargetBox->maxPoint[dim];
+			} else {
+				intersectionPoint[dim] = center[dim];
+			}
+		}
+
+		targetVector = (center - intersectionPoint);
+		targetVector.z = 0;
+		targetVector = targetVector.normalize() * radius;
+
+		center.x = intersectionPoint.x + targetVector.x;
+		center.y = intersectionPoint.y + targetVector.y;
+		ICollisionObject::testObjectIntersection(targetObject, distance);
+
+		break;
+
+	case kCylinder:
+		pTargetCylinder = static_cast<const CCollisionCylinder *>(&targetObject);
+		targetHeight = pTargetCylinder->height;
+		targetRadius = pTargetCylinder->radius;
+		targetCenter = pTargetCylinder->center;
+
+		// Handle the case where two players are at the exact same spot...
+		if (!((distance->x != 0) || (distance->y != 0)))
+			warning("CCollisionCylinder::forceOutOfObject(): These two cylinders have the same center, so we don't know which direction to push this one out.");
+
+		if (distance->xyMagnitude() == 0) {
+			distance->x = 1;
+		}
+
+		if (distance->z < (targetHeight / 2)) {
+			targetVector.x = distance->x;
+			targetVector.y = distance->y;
+			targetVector.z = 0;
+
+			targetVector = targetVector.normalize() * (radius + targetRadius);
+
+			center.x = targetCenter.x + targetVector.x;
+			center.y = targetCenter.y + targetVector.y;
+			ICollisionObject::testObjectIntersection(targetObject, distance);
+		}
+
+		break;
+
+	default:
+		return;
+	}
+}
+
+U32FltPoint3D CCollisionCylinder::findNearestPoint(const U32FltPoint3D &testPoint) const {
+	U32FltPoint3D cylinderPoint;
+
+	if (testPoint.z <= center.z - (height / 2)) {
+		cylinderPoint.x = testPoint.x;
+		cylinderPoint.y = testPoint.y;
+		cylinderPoint.z = center.z - (height / 2);
+	} else if (testPoint.z >= center.z + (height / 2)) {
+		cylinderPoint.x = testPoint.x;
+		cylinderPoint.y = testPoint.y;
+		cylinderPoint.z = center.z + (height / 2);
+	} else {
+		U32FltVector3D centerVector = testPoint - center;
+		centerVector = centerVector.normalize() * radius;
+
+		cylinderPoint.x = center.x + centerVector.x;
+		cylinderPoint.y = center.y + centerVector.y;
+		cylinderPoint.z = testPoint.z;
+	}
+
+	return cylinderPoint;
+}
+
+void CCollisionCylinder::handleCollision(const CCollisionBox &targetBox, float *timeUsed, U32Distance3D *distance,  bool advanceObject) {
+	// Handle collisions in the z direction.
+	// If we're on something, make sure we aren't going up or down...
+	if (((distance->z >= (height / 2)) && (_velocity.z < 0)) ||
+		((distance->z <= -(height / 2)) && (_velocity.z > 0))) {
+		_velocity.z = 0;
+	}
+
+	// Handle collisions in the xy direction...
+	if (_movementType == kCircular) {
+		_velocity.x = 0;
+		_velocity.y = 0;
+	} else {
+		U32FltPoint2D collisionPoint;
+		collisionPoint.x = getPointOfCollision(targetBox, *distance, X_INDEX);
+		collisionPoint.y = getPointOfCollision(targetBox, *distance, Y_INDEX);
+
+		U32FltVector2D targetVector;
+		targetVector.x = collisionPoint.x - center.x;
+		targetVector.y = collisionPoint.y - center.y;
+
+		// Project the velocity vector onto that vector.
+		// This will give us the component of the velocity vector that collides directly
+		// with the target object. This part of the velocity vector will be lost in the
+		// collision...
+		_velocity -= _velocity.projectVector(targetVector);
+	}
+
+	if (advanceObject) {
+		center.x += (_velocity.x * *timeUsed);
+		center.y += (_velocity.y * *timeUsed);
+		center.z += (_velocity.z * *timeUsed);
+		*timeUsed = 0;
+	}
+
+	return;
+}
+
+void CCollisionCylinder::handleCollision(const CCollisionCylinder &targetCylinder, float *timeUsed, U32Distance3D *distance, bool advanceObject) {
+	// Handle collisions in the z direction.
+	// If we're on something, make sure we aren't going up or down...
+	if (((distance->z >= (height / 2)) && (_velocity.z < 0)) ||
+		((distance->z <= -(height / 2)) && (_velocity.z > 0))) {
+		_velocity.z = 0;
+	}
+
+	// Handle collisions in the xy direction...
+	if (_movementType == kCircular) {
+		// If we are moving in a circular motion, then we must have hit at
+		// least 2 objects this frame, so we stop our movement altogether...
+		_velocity.x = 0;
+		_velocity.y = 0;
+
+		*timeUsed = 1;
+	} else {
+		// Find the vector from the center of this object to the center of
+		// the collision object...
+		U32FltVector2D targetVector = targetCylinder.center - center;
+
+		// Project the velocity vector onto that vector.
+		// This will give us the component of the velocity vector that collides directly
+		// with the target object. This part of the velocity vector will be lost in the
+		// collision...
+		_velocity -= _velocity.projectVector(targetVector);
+
+		// If we are moving forward, do so, then update the vector...
+		if (advanceObject) {
+			float finalMagnitude = _velocity.xyMagnitude();
+
+			if (finalMagnitude != 0) {
+				// Revolve this cylinder around the targer cylinder...
+				_movementType = kCircular;
+				_revCenter = dynamic_cast<const ICollisionObject *>(&targetCylinder);
+				_revCenterPt = targetCylinder.center;
+
+				// Find out in which direction we are going to revolve around the target cylinder...
+				ERevDirection revDirection = getRevDirection();
+
+				// Find the diameter of the circle we are moving around...
+				double movementDiameter = 2 * BBALL_M_PI * (targetCylinder.radius + radius);
+
+				// Find how many radians around the circle we will go...
+				double movementRadians = 2 * BBALL_M_PI * ((finalMagnitude * MAX(*timeUsed, (float)1)) / movementDiameter) * revDirection;
+
+				center.x = (distance->x * cos(movementRadians) - distance->y * sin(movementRadians)) + targetCylinder.center.x;
+				center.y = (distance->x * sin(movementRadians) + distance->y * cos(movementRadians)) + targetCylinder.center.y;
+				testObjectIntersection(targetCylinder, distance);
+
+				// Adjust for rounding errors in pi by pushing this cylinder straight out
+				// or in so that it is in contact in just one location with the target
+				// cylinder...
+				forceOutOfObject(targetCylinder, distance);
+
+				// Calculate the final vector. The final vector should be tangent to the
+				// target cylinder at the point of intersection...
+				_velocity.x = targetVector.y;
+				_velocity.y = targetVector.x;
+
+				_velocity.x = _velocity.x / targetVector.magnitude();
+				_velocity.y = _velocity.y / targetVector.magnitude();
+
+				_velocity.x *= finalMagnitude;
+				_velocity.y *= finalMagnitude;
+
+				if (revDirection == kClockwise) {
+					_velocity.x *= -1;
+				} else {
+					_velocity.y *= -1;
+				}
+			}
+
+			// Update the z position...
+			center.z += _velocity.z * *timeUsed;
+
+			*timeUsed = 0;
+		} else {
+			// Just update the vector...
+			float finalMagnitude = _velocity.xyMagnitude();
+			ERevDirection revDirection = getRevDirection();
+
+			_velocity.x = targetVector.y;
+			_velocity.y = targetVector.x;
+
+			_velocity.x = _velocity.x / targetVector.magnitude();
+			_velocity.y = _velocity.y / targetVector.magnitude();
+
+			_velocity.x *= finalMagnitude;
+			_velocity.y *= finalMagnitude;
+
+			if (revDirection == kClockwise) {
+				_velocity.x *= -1;
+			} else {
+				_velocity.y *= -1;
+			}
+		}
+	}
+}
+
+void CCollisionCylinder::handleCollisions(CCollisionObjectVector *pCollisionVector, float *timeUsed, bool advanceObject) {
+	if (pCollisionVector->size() > 1) {
+		_velocity.x = 0;
+		_velocity.y = 0;
+		return;
+	}
+
+	ICollisionObject::handleCollisions(pCollisionVector, timeUsed, advanceObject);
+}
+
+bool CCollisionCylinder::isOnObject(const CCollisionBox &targetObject, const U32Distance3D &distance) const {
+	return ((distance.z == (height / 2)) &&
+			(distance.xyMagnitude() < radius));
+}
+
+bool CCollisionCylinder::isOnObject(const CCollisionSphere &targetObject, const U32Distance3D &distance) const {
+	return ((distance.z == (height / 2)) &&
+			(distance.xyMagnitude() < (radius + targetObject.radius)));
+}
+
+bool CCollisionCylinder::isOnObject(const CCollisionCylinder &targetObject, const U32Distance3D &distance) const {
+	return ((distance.z == (height / 2)) &&
+			(distance.xyMagnitude() < (radius + targetObject.radius)));
+}
+
+U32BoundingBox CCollisionCylinder::getBoundingBox() const {
+	U32BoundingBox outBox;
+
+	outBox.minPoint.x = center.x - radius;
+	outBox.minPoint.y = center.y - radius;
+	outBox.minPoint.z = center.z - height;
+
+	outBox.maxPoint.x = center.x + radius;
+	outBox.maxPoint.y = center.y + radius;
+	outBox.maxPoint.z = center.z + height;
+
+	return outBox;
+}
+
+U32BoundingBox CCollisionCylinder::getBigBoundingBox() const {
+	U32BoundingBox outBox;
+
+	float xyVelocity = _velocity.xyMagnitude();
+
+	outBox.minPoint.x = center.x - (radius + xyVelocity);
+	outBox.minPoint.y = center.y - (radius + xyVelocity);
+	outBox.minPoint.z = center.z - (height + _velocity.z);
+
+	outBox.maxPoint.x = center.x + (radius + xyVelocity);
+	outBox.maxPoint.y = center.y + (radius + xyVelocity);
+	outBox.maxPoint.z = center.z + (height + _velocity.z);
+
+	return outBox;
+}
+
+void CCollisionCylinder::save() {
+	_positionSaved = true;
+	_safetyPoint = center;
+	_safetyVelocity = _velocity;
+}
+
+void CCollisionCylinder::restore() {
+	if (_positionSaved) {
+		if (_safetyVelocity.magnitude() != 0) {
+			debug("CCollisionCylinder::Restore(): Restoring");
+			center = _safetyPoint;
+
+			_velocity.x = 0;
+			_velocity.y = 0;
+			_velocity.z = 0;
+		}
+	} else {
+		warning("CCollisionCylinder::Restore(): No save point.");
+	}
+}
+
+int CCollisionCylinder::getEquidistantPoint(U32FltPoint2D inPoint1, float distance1, U32FltPoint2D inPoint2, float distance2, U32FltPoint2D *outPoint1, U32FltPoint2D *outPoint2) {
+	double distDiff = (distance2 * distance2) - (distance1 * distance1);
+	double xDiff = (inPoint1.x * inPoint1.x) - (inPoint2.x * inPoint2.x);
+	double yDiff = (inPoint1.y * inPoint1.y) - (inPoint2.y * inPoint2.y);
+
+	double k1 = 0;
+	double k2 = 0;
+
+	if (xDiff != 0) {
+		k1 = (distDiff + xDiff + yDiff) / (2 * (inPoint1.x - inPoint2.x));
+		k2 = (inPoint2.y - inPoint1.y) / (inPoint1.x - inPoint2.x);
+	}
+
+	double a = (k2 * k2) + 1;
+
+	double b = (2 * k1 * k2) -
+			   (2 * k2 * inPoint1.x) -
+			   (2 * inPoint1.y);
+
+	double c = (inPoint1.x * inPoint1.x) +
+			   (inPoint1.y * inPoint1.y) +
+			   (k1 * k1) -
+			   (2 * k1 * inPoint1.x) -
+			   (distance1 * distance1);
+
+	if (((b * b) < (4 * a * c)) || (a == 0)) {
+		return 0;
+	} else {
+		outPoint1->y = (-b + sqrt(b * b - 4 * a * c)) / (2 * a);
+		outPoint2->y = (-b - sqrt(b * b - 4 * a * c)) / (2 * a);
+
+		outPoint1->x = k1 + (k2 * outPoint1->y);
+		outPoint2->x = k1 + (k2 * outPoint2->y);
+
+		if (*outPoint1 == *outPoint2) {
+			return 1;
+		} else {
+			return 2;
+		}
+	}
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/collision/bball_collision_cylinder.h b/engines/scumm/he/basketball/collision/bball_collision_cylinder.h
new file mode 100644
index 00000000000..1f3f66412ee
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_cylinder.h
@@ -0,0 +1,137 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_CYLINDER_H
+#define SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_CYLINDER_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+
+namespace Scumm {
+
+#define MAX_STEP_HEIGHT 50
+	
+// An object's velocity can either be carried
+// out as straight or circular movement...
+enum EMovementType {
+	kStraight = 0,
+	kCircular = 1 // 2D circular motion along the horizon plane
+};
+
+class CCollisionCylinder : public ICollisionObject, public U32Cylinder {
+public:
+	CCollisionCylinder() : ICollisionObject(kCylinder),
+						   height(0),
+						   _shieldRadius(0),
+						   _movementType(EMovementType::kStraight),
+						   _revCenter(nullptr),
+						   _positionSaved(false) {}
+
+	~CCollisionCylinder() {}
+
+	using ICollisionObject::getObjectDistance;
+	float getObjectDistance(const CCollisionSphere &targetObject) const override;
+	float getObjectDistance(const CCollisionBox &targetObject) const override;
+	float getObjectDistance(const CCollisionCylinder &targetObject) const override;
+
+	using ICollisionObject::testObjectIntersection;
+	bool testObjectIntersection(const CCollisionSphere &targetObject, U32Distance3D *distance) const override;
+	bool testObjectIntersection(const CCollisionBox &targetObject, U32Distance3D *distance) const override;
+	bool testObjectIntersection(const CCollisionCylinder &targetObject, U32Distance3D *distance) const override;
+
+	using ICollisionObject::validateCollision;
+	bool validateCollision(const CCollisionCylinder &targetObject, U32Distance3D *distance) override;
+
+	using ICollisionObject::backOutOfObject;
+	bool backOutOfObject(const CCollisionBox &targetObject, U32Distance3D *distance, float *timeUsed) override;
+	bool backOutOfObject(const CCollisionCylinder &targetObject, U32Distance3D *distance, float *timeUsed) override;
+
+	using ICollisionObject::nudgeObject;
+	bool nudgeObject(const CCollisionBox &targetObject, U32Distance3D *distance, float *timeUsed) override;
+	bool nudgeObject(const CCollisionCylinder &targetObject, U32Distance3D *distance, float *timeUsed) override;
+
+	using ICollisionObject::isCollisionHandled;
+	bool isCollisionHandled(const CCollisionSphere &targetObject) const override { return false; };
+
+	void handleCollisions(CCollisionObjectVector *collisionVector, float *timeUsed, bool advanceObject) override;
+	void handleCollision(const CCollisionCylinder &targetCylinder, float *timeUsed, U32Distance3D *distance, bool advanceObject) override;
+	void handleCollision(const CCollisionBox &targetBox, float *pTimeUsed, U32Distance3D *distance, bool advanceObject) override;
+
+	using ICollisionObject::isOnObject;
+	bool isOnObject(const CCollisionSphere &targetObject, const U32Distance3D &distance) const override;
+	bool isOnObject(const CCollisionBox &targetObject, const U32Distance3D &distance) const override;
+	bool isOnObject(const CCollisionCylinder &targetObject, const U32Distance3D &distance) const override;
+
+	U32FltPoint3D findNearestPoint(const U32FltPoint3D &testPoint) const override;
+	int getEquidistantPoint(U32FltPoint2D inPoint1, float distance1, U32FltPoint2D inPoint2, float distance2, U32FltPoint2D *outPoint1, U32FltPoint2D *outPoint2);
+
+	U32BoundingBox getBoundingBox() const override;
+	U32BoundingBox getBigBoundingBox() const override;
+
+	void save() override;
+	void restore() override;
+
+	float height;
+	float _shieldRadius;  // Sometimes we may want ot temporarily increase a player's
+						  // effective collision radius.  This value keeps track of
+						  // how much their radius has been increased by.
+
+	EMovementType _movementType;
+
+	const ICollisionObject *_revCenter;   // If an object is moving with circular motion,
+										  // this is the object it is revolving around.
+	U32FltPoint2D _revCenterPt;           // This is the center of the _revCenter.
+
+protected:
+	float getDimensionDistance(const CCollisionBox &targetObject, EDimension dimension) const;
+	float getDimensionDistance(const CCollisionSphere &targetObject, EDimension dimension) const;
+	float getDimensionDistance(const CCollisionCylinder &targetObject, EDimension dimension) const;
+
+private:
+	bool _positionSaved;
+	U32FltPoint3D _safetyPoint;
+	U32FltVector3D _safetyVelocity;
+
+	bool backStraightOutOfObject(const ICollisionObject &targetObject, U32Distance3D *distance, float *timeUsed);
+
+	bool circleOutOfObject(const CCollisionBox &targetObject, U32Distance3D *distance, float *timeUsed);
+	bool circleOutOfObject(const CCollisionCylinder &targetObject, U32Distance3D *distance, float *timeUsed);
+
+	ERevDirection getRevDirection() const;
+
+	using ICollisionObject::getPenetrationTime;
+	float getPenetrationTime(const CCollisionBox &targetObject, const U32Distance3D &distance, EDimension dimension) const override;
+	float getPenetrationTime(const CCollisionCylinder &targetObject, const U32Distance3D &distance, EDimension dimension) const override;
+
+	float getPointOfCollision(const CCollisionBox &targetBox, U32Distance3D distance, EDimension dimension) const;
+
+	void forceOutOfObject(const ICollisionObject &targetObject, U32Distance3D *distance);
+
+	bool getCornerIntersection(const CCollisionBox &targetObject, const U32Distance3D &distance, U32FltPoint2D *intersection);
+};
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_CYLINDER_H
diff --git a/engines/scumm/he/basketball/collision/bball_collision_node.cpp b/engines/scumm/he/basketball/collision/bball_collision_node.cpp
new file mode 100644
index 00000000000..62b91114a36
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_node.cpp
@@ -0,0 +1,135 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+#include "scumm/he/basketball/collision/bball_collision_sphere.h"
+#include "scumm/he/basketball/collision/bball_collision_box.h"
+#include "scumm/he/basketball/collision/bball_collision_cylinder.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+#include "scumm/he/basketball/collision/bball_collision_node.h"
+#include "scumm/he/basketball/collision/bball_collision_tree.h"
+
+namespace Scumm {
+
+CCollisionNode::CCollisionNode() {
+	for (int i = 0; i < NUM_CHILDREN_NODES; i++) {
+		_child[i] = nullptr;
+	}
+
+	_quadrant.SetMinPoint(0, 0);
+	_quadrant.SetMaxPoint(0, 0);
+
+	_isExternal = false;
+}
+
+CCollisionNode::CCollisionNode(const CCollisionObjectVector &initObjects) : _objectList(initObjects) {
+	for (int i = 0; i < NUM_CHILDREN_NODES; i++) {
+		_child[i] = nullptr;
+	}
+
+	_quadrant.SetMinPoint(0, 0);
+	_quadrant.SetMaxPoint(0, 0);
+
+	_isExternal = false;
+}
+
+CCollisionNode::~CCollisionNode() {
+	if (!_isExternal) {
+		for (int i = 0; i < NUM_CHILDREN_NODES; i++) {
+			assert(_child[i]);
+			delete _child[i];
+			_child[i] = nullptr;
+		}
+	}
+}
+
+U32BoundingBox CCollisionNode::getChildQuadrant(const U32BoundingBox &parentQuadrant, EChildID childID) {
+	int minX, minY;
+	int maxX, maxY;
+
+	int centerX = ((parentQuadrant.getMinPoint().x + parentQuadrant.getMaxPoint().x) / 2);
+	int centerY = ((parentQuadrant.getMinPoint().y + parentQuadrant.getMaxPoint().y) / 2);
+
+	switch (childID) {
+
+	case kChild1:
+		minX = centerX;
+		minY = parentQuadrant.getMinPoint().y;
+		maxX = parentQuadrant.getMaxPoint().x;
+		maxY = centerY;
+		break;
+
+	case kChild2:
+		minX = centerX;
+		minY = centerY;
+		maxX = parentQuadrant.getMaxPoint().x;
+		maxY = parentQuadrant.getMaxPoint().y;
+		break;
+
+	case kChild3:
+		minX = parentQuadrant.getMinPoint().x;
+		minY = centerY;
+		maxX = centerX;
+		maxY = parentQuadrant.getMaxPoint().y;
+		break;
+
+	case kChild4:
+		minX = parentQuadrant.getMinPoint().x;
+		minY = parentQuadrant.getMinPoint().y;
+		maxX = centerX;
+		maxY = centerY;
+		break;
+
+	default:
+		warning("CCollisionNode::getChildQuadrant(): Invalid childID passed to getChildQuadrant");
+		minX = 0;
+		minY = 0;
+		maxX = 0;
+		maxY = 0;
+	}
+
+	U32BoundingBox childQuadrant;
+	childQuadrant.SetMinPoint(minX, minY);
+	childQuadrant.SetMaxPoint(maxX, maxY);
+	return childQuadrant;
+}
+
+void CCollisionNode::searchTree(const U32BoundingBox &searchRange, CCollisionObjectVector *targetList) const {
+	if (searchRange.Intersect(_quadrant)) {
+		if (_isExternal) {
+			// Search through and add points that are within bounds...
+			for (CCollisionObjectVector::size_type objectIndex = 0; objectIndex < _objectList.size(); ++objectIndex) {
+				if (!_objectList[objectIndex]->_ignore) {
+					const ICollisionObject *currentObject = _objectList[objectIndex];
+					targetList->push_back(currentObject);
+				}
+			}
+		} else {
+			// Search all of the children...
+			for (int childID = 0; childID < NUM_CHILDREN_NODES; childID++) {
+				_child[childID]->searchTree(searchRange, targetList);
+			}
+		}
+	}
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/collision/bball_collision_node.h b/engines/scumm/he/basketball/collision/bball_collision_node.h
new file mode 100644
index 00000000000..ccdb465c543
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_node.h
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_NODE_H
+#define SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_NODE_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+
+namespace Scumm {
+
+#define NUM_CHILDREN_NODES 4
+
+enum EChildID {
+	kChild1 = 0,
+	kChild2 = 1,
+	kChild3 = 2,
+	kChild4 = 3
+};
+
+class CCollisionNode {
+public:
+	CCollisionNode();
+	CCollisionNode(const CCollisionObjectVector &initObjects);
+	~CCollisionNode();
+
+	friend class CCollisionObjectTree;
+
+private:
+	void searchTree(const U32BoundingBox &searchRange, CCollisionObjectVector *targetList) const;
+	static U32BoundingBox getChildQuadrant(const U32BoundingBox &parentQuadrant, EChildID childID);
+
+	// Children nodes to this node
+	CCollisionNode *_child[NUM_CHILDREN_NODES];
+
+	// A list of object pointers that are contained within this node
+	CCollisionObjectVector _objectList;
+
+	// The area that is handled by this node
+	U32BoundingBox _quadrant;
+
+	// Whether or not this is a leaf node
+	bool _isExternal;
+};
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_NODE_H
diff --git a/engines/scumm/he/basketball/collision/bball_collision_object.cpp b/engines/scumm/he/basketball/collision/bball_collision_object.cpp
new file mode 100644
index 00000000000..4f545374416
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_object.cpp
@@ -0,0 +1,334 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+#include "scumm/he/basketball/collision/bball_collision_sphere.h"
+#include "scumm/he/basketball/collision/bball_collision_box.h"
+#include "scumm/he/basketball/collision/bball_collision_cylinder.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+#include "scumm/he/basketball/collision/bball_collision_node.h"
+#include "scumm/he/basketball/collision/bball_collision_tree.h"
+
+namespace Scumm {
+
+ICollisionObject::ICollisionObject(EObjectShape shape) : _objectShape(shape),
+														 _objectID(0),
+														 _objectType(kNoObjectType),
+														 _description(""),
+														 _collisionEfficiency(0.0F),
+														 _friction(0.0F),
+														 _soundNumber(0),
+														 _ignore(false) {
+	_velocity.x = 0.0F;
+	_velocity.y = 0.0F;
+	_velocity.z = 0.0F;
+}
+
+bool ICollisionObject::operator==(const ICollisionObject &otherObject) const {
+	return ((otherObject._objectShape == _objectShape) &&
+			(otherObject._objectType == _objectType) &&
+			(otherObject._objectID == _objectID));
+}
+
+float ICollisionObject::getObjectDistance(const ICollisionObject &targetObject) const {
+	float result;
+
+	switch (targetObject._objectShape) {
+	case kNoObjectShape:
+		warning("ICollisionObject::getObjectDistance(): Tried to interact with an object of undefined type");
+		result = 0.0F;
+		break;
+	case kSphere:
+		result = getObjectDistance(dynamic_cast<const CCollisionSphere &>(targetObject));
+		break;
+	case kBox:
+		result = getObjectDistance(dynamic_cast<const CCollisionBox &>(targetObject));
+		break;
+	case kCylinder:
+		result = getObjectDistance(dynamic_cast<const CCollisionCylinder &>(targetObject));
+		break;
+	default:
+		warning("ICollisionObject::getObjectDistance(): Tried to interact with an object of undefined type");
+		result = 0.0F;
+	}
+
+	return result;
+}
+
+bool ICollisionObject::testObjectIntersection(const ICollisionObject &targetObject, U32Distance3D *distance) const {
+	bool result;
+
+	switch (targetObject._objectShape) {
+	case kNoObjectShape:
+		warning("ICollisionObject::testObjectIntersection(): Tried to interact with an object of undefined type");
+		result = false;
+		break;
+	case kSphere:
+		result = testObjectIntersection(dynamic_cast<const CCollisionSphere &>(targetObject), distance);
+		break;
+	case kBox:
+		result = testObjectIntersection(dynamic_cast<const CCollisionBox &>(targetObject), distance);
+		break;
+	case kCylinder:
+		result = testObjectIntersection(dynamic_cast<const CCollisionCylinder &>(targetObject), distance);
+		break;
+	default:
+		warning("ICollisionObject::testObjectIntersection(): Tried to interact with an object of undefined type");
+		result = false;
+	}
+
+	return result;
+}
+
+bool ICollisionObject::validateCollision(const ICollisionObject &targetObject, U32Distance3D *distance) {
+	if (_ignore)
+		return true;
+
+	bool result;
+
+	switch (targetObject._objectShape) {
+	case kNoObjectShape:
+		warning("ICollisionObject::validateCollision(): Tried to interact with an object of undefined type");
+		result = false;
+		break;
+	case kSphere:
+		result = validateCollision(dynamic_cast<const CCollisionSphere &>(targetObject), distance);
+		break;
+	case kBox:
+		result = validateCollision(dynamic_cast<const CCollisionBox &>(targetObject), distance);
+		break;
+	case kCylinder:
+		result = validateCollision(dynamic_cast<const CCollisionCylinder &>(targetObject), distance);
+		break;
+	default:
+		warning("ICollisionObject::validateCollision(): Tried to interact with an object of undefined type");
+		result = false;
+	}
+
+	return result;
+}
+
+bool ICollisionObject::backOutOfObject(const ICollisionObject &targetObject, U32Distance3D *distance, float *timeUsed) {
+	if (_ignore)
+		return true;
+
+	bool result;
+
+	switch (targetObject._objectShape) {
+	case kNoObjectShape:
+		warning("ICollisionObject::backOutOfObject(): Tried to interact with an object of undefined type");
+		result = false;
+		break;
+	case kSphere:
+		result = backOutOfObject(dynamic_cast<const CCollisionSphere &>(targetObject), distance, timeUsed);
+		break;
+	case kBox:
+		result = backOutOfObject(dynamic_cast<const CCollisionBox &>(targetObject), distance, timeUsed);
+		break;
+	case kCylinder:
+		result = backOutOfObject(dynamic_cast<const CCollisionCylinder &>(targetObject), distance, timeUsed);
+		break;
+	default:
+		warning("ICollisionObject::backOutOfObject(): Tried to interact with an object of undefined type");
+		result = false;
+	}
+
+	return result;
+}
+
+bool ICollisionObject::nudgeObject(const ICollisionObject &targetObject, U32Distance3D *distance, float *timeUsed) {
+	if (_velocity.magnitude() == 0)
+		return true;
+
+	if (_ignore)
+		return true;
+
+	if (*timeUsed == 0)
+		return true;
+
+	bool result;
+
+	switch (targetObject._objectShape) {
+	case kNoObjectShape:
+		warning("ICollisionObject::nudgeObject(): Tried to interact with an object of undefined type");
+		result = false;
+		break;
+	case kSphere:
+		result = nudgeObject(dynamic_cast<const CCollisionSphere &>(targetObject), distance, timeUsed);
+		break;
+	case kBox:
+		result = nudgeObject(dynamic_cast<const CCollisionBox &>(targetObject), distance, timeUsed);
+		break;
+	case kCylinder:
+		result = nudgeObject(dynamic_cast<const CCollisionCylinder &>(targetObject), distance, timeUsed);
+		break;
+	default:
+		warning("ICollisionObject::nudgeObject(): Tried to interact with an object of undefined type");
+		result = false;
+	}
+
+	return result;
+}
+
+bool ICollisionObject::isCollisionHandled(const ICollisionObject &targetObject) const {
+	bool result;
+
+	switch (targetObject._objectShape) {
+	case kNoObjectShape:
+		warning("ICollisionObject::isCollisionHandled(): Tried to interact with an object of undefined type");
+		result = false;
+		break;
+	case kSphere:
+		result = isCollisionHandled(dynamic_cast<const CCollisionSphere &>(targetObject));
+		break;
+	case kBox:
+		result = isCollisionHandled(dynamic_cast<const CCollisionBox &>(targetObject));
+		break;
+	case kCylinder:
+		result = isCollisionHandled(dynamic_cast<const CCollisionCylinder &>(targetObject));
+		break;
+	default:
+		warning("ICollisionObject::isCollisionHandled(): Tried to interact with an object of undefined type");
+		result = false;
+	}
+
+	return result;
+}
+
+void ICollisionObject::handleCollisions(CCollisionObjectVector *collisionVector, float *timeUsed, bool advanceObject) {
+	if (_velocity.magnitude() == 0)
+		return;
+
+	if (_ignore)
+		return;
+
+	for (CCollisionObjectVector::const_iterator objectIt = collisionVector->begin(); objectIt != collisionVector->end(); ++objectIt) {
+		const ICollisionObject *pTargetObject = *objectIt;
+
+		U32Distance3D distance;
+		testObjectIntersection(*pTargetObject, &distance);
+
+		switch (pTargetObject->_objectShape) {
+		case kNoObjectShape:
+			warning("ICollisionObject::handleCollisions(): Tried to interact with an object of undefined type");
+			break;
+		case kSphere:
+			handleCollision(*static_cast<const CCollisionSphere *>(pTargetObject), timeUsed, &distance, advanceObject);
+			break;
+		case kBox:
+			handleCollision(*static_cast<const CCollisionBox *>(pTargetObject), timeUsed, &distance, advanceObject);
+			break;
+		case kCylinder:
+			handleCollision(*static_cast<const CCollisionCylinder *>(pTargetObject), timeUsed, &distance, advanceObject);
+			break;
+		default:
+			warning("ICollisionObject::handleCollisions(): Tried to interact with an object of undefined type");
+		}
+	}
+}
+
+bool ICollisionObject::isOnObject(const ICollisionObject &targetObject, const U32Distance3D &distance) const {
+	bool result;
+
+	switch (targetObject._objectShape) {
+	case kNoObjectShape:
+		warning("ICollisionObject::isOnObject(): Tried to interact with an object of undefined type");
+		result = false;
+		break;
+	case kSphere:
+		result = isOnObject(static_cast<const CCollisionSphere &>(targetObject), distance);
+		break;
+	case kBox:
+		result = isOnObject(static_cast<const CCollisionBox &>(targetObject), distance);
+		break;
+	case kCylinder:
+		result = isOnObject(static_cast<const CCollisionCylinder &>(targetObject), distance);
+		break;
+	default:
+		warning("ICollisionObject::isOnObject(): Tried to interact with an object of undefined type");
+		result = false;
+	}
+
+	return result;
+}
+
+U32FltPoint3D ICollisionObject::findNearestPoint(const U32FltPoint3D &testPoint) const {
+	return testPoint;
+}
+
+float ICollisionObject::getPenetrationTime(const ICollisionObject &targetObject, const U32Distance3D &distance, EDimension dimension) const {
+	float result;
+
+	switch (targetObject._objectShape) {
+	case kNoObjectShape:
+		warning("ICollisionObject::getPenetrationTime(): Tried to interact with an object of undefined type");
+		result = 0.0F;
+		break;
+	case kSphere:
+		result = getPenetrationTime(*static_cast<const CCollisionSphere *>(&targetObject), distance, dimension);
+		break;
+	case kBox:
+		result = getPenetrationTime(*static_cast<const CCollisionBox *>(&targetObject), distance, dimension);
+		break;
+	case kCylinder:
+		result = getPenetrationTime(*static_cast<const CCollisionCylinder *>(&targetObject), distance, dimension);
+		break;
+	default:
+		warning("ICollisionObject::getPenetrationTime(): Tried to interact with an object of undefined type");
+		result = 0.0F;
+	}
+
+	return result;
+}
+
+void ICollisionObject::defineReflectionPlane(const ICollisionObject &targetObject, const U32Distance3D &distance, U32Plane *collisionPlane) const {
+	switch (targetObject._objectShape) {
+	case kNoObjectShape:
+		warning("ICollisionObject::defineReflectionPlane(): Tried to interact with an object of undefined type");
+		break;
+	case kSphere:
+		defineReflectionPlane(dynamic_cast<const CCollisionSphere &>(targetObject), distance, collisionPlane);
+		break;
+	case kBox:
+		defineReflectionPlane(dynamic_cast<const CCollisionBox &>(targetObject), distance, collisionPlane);
+		break;
+	case kCylinder:
+		defineReflectionPlane(dynamic_cast<const CCollisionCylinder &>(targetObject), distance, collisionPlane);
+		break;
+	default:
+		warning("ICollisionObject::defineReflectionPlane(): Tried to interact with an object of undefined type");
+	}
+}
+
+void ICollisionObject::defineReflectionPlane(const CCollisionSphere &targetObject, const U32Distance3D &distance, U32Plane *collisionPlane) const {
+	collisionPlane->clear();
+}
+
+void ICollisionObject::defineReflectionPlane(const CCollisionBox &targetObject, const U32Distance3D &distance, U32Plane *collisionPlane) const {
+	collisionPlane->clear();
+}
+
+void ICollisionObject::defineReflectionPlane(const CCollisionCylinder &targetObject, const U32Distance3D &distance, U32Plane *collisionPlane) const {
+	collisionPlane->clear();
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/collision/bball_collision_object.h b/engines/scumm/he/basketball/collision/bball_collision_object.h
new file mode 100644
index 00000000000..1d1d59ab9db
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_object.h
@@ -0,0 +1,193 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_OBJECT_H
+#define SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_OBJECT_H
+
+#ifdef ENABLE_HE
+
+#include "common/str.h"
+
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+
+namespace Scumm {
+
+#define COLLISION_EPSILON               0.5F
+#define COLLISION_SMALL_TIME_INCREMENT  0.05F
+#define COLLISION_BACK_OUT_TIME_LIMIT   20.0F
+
+class CCollisionSphere;
+class CCollisionBox;
+class CCollisionCylinder;
+class CCollisionObjectStack;
+class CCollisionObjectVector;
+
+enum EObjectShape {
+	kNoObjectShape = 0,
+	kSphere = 1,
+	kBox = 2,
+	kCylinder = 3
+};
+
+enum EObjectType {
+	kNoObjectType = 0,
+	kBackboard = 1,
+	kRim = 2,
+	kOtherType = 3,
+	kFloor = 4,
+	kBall = 5,
+	kPlayer = 6
+};
+
+class ICollisionObject {
+public:
+	ICollisionObject(EObjectShape shape = kNoObjectShape);
+	virtual ~ICollisionObject() {}
+
+	// Comparison operator
+	bool operator==(const ICollisionObject &otherObject) const;
+
+	// Get the distance between the outside of this object and the outside of a target object
+	virtual float getObjectDistance(const ICollisionObject &targetObject) const;
+	virtual float getObjectDistance(const CCollisionSphere &targetObject) const { return 0.0F; }
+	virtual float getObjectDistance(const CCollisionBox &targetObject) const { return 0.0F; }
+	virtual float getObjectDistance(const CCollisionCylinder &targetObject) const { return 0.0F; }
+
+	// testObjectIntersection is used to determine if this object is intersecting a target object
+	virtual bool testObjectIntersection(const ICollisionObject &targetObject, U32Distance3D *distance) const;
+	virtual bool testObjectIntersection(const CCollisionSphere &targetObject, U32Distance3D *distance) const { return false; }
+	virtual bool testObjectIntersection(const CCollisionBox &targetObject, U32Distance3D *distance) const { return false; }
+	virtual bool testObjectIntersection(const CCollisionCylinder &targetObject, U32Distance3D *distance) const { return false; }
+
+	// Just because this object is intersecting another, doesn't mean that it collided with
+	// that object. That object could have collided with us. This function verifies, that
+	// we collided with another object...
+	virtual bool validateCollision(const ICollisionObject &targetObject, U32Distance3D *distance);
+	virtual bool validateCollision(const CCollisionSphere &targetObject, U32Distance3D *distance) { return true; }
+	virtual bool validateCollision(const CCollisionBox &targetObject, U32Distance3D *distance) { return true; }
+	virtual bool validateCollision(const CCollisionCylinder &targetObject, U32Distance3D *distance) { return true; }
+
+	// backOutOfObject moves this object backwards along its velocity vector to a point
+	// where it is guaranteed not to be intersecting a target object
+	virtual bool backOutOfObject(const ICollisionObject &targetObject, U32Distance3D *distance, float *timeUsed);
+	virtual bool backOutOfObject(const CCollisionSphere &targetObject, U32Distance3D *distance, float *timeUsed) { return true; }
+	virtual bool backOutOfObject(const CCollisionBox &targetObject, U32Distance3D *distance, float *timeUsed) { return true; }
+	virtual bool backOutOfObject(const CCollisionCylinder &targetObject, U32Distance3D *distance, float *timeUsed) { return true; }
+
+	// nudgeObject moves this object forward along its velocity vector to a point
+	// where it is guaranteed to be touching the target object at the exact point
+	// of collision
+	virtual bool nudgeObject(const ICollisionObject &targetObject, U32Distance3D *distance, float *timeUsed);
+	virtual bool nudgeObject(const CCollisionSphere &targetObject, U32Distance3D *distance, float *timeUsed) { return true; }
+	virtual bool nudgeObject(const CCollisionBox &targetObject, U32Distance3D *distance, float *timeUsed) { return true; }
+	virtual bool nudgeObject(const CCollisionCylinder &targetObject, U32Distance3D *distance, float *timeUsed) { return true; }
+
+	// Some collisions between objects are recorded, but not handled physically. This
+	// function determines whether a collision gets handled or not...
+	virtual bool isCollisionHandled(const ICollisionObject &targetObject) const;
+	virtual bool isCollisionHandled(const CCollisionSphere &targetObject) const { return true; }
+	virtual bool isCollisionHandled(const CCollisionBox &targetObject) const { return true; }
+	virtual bool isCollisionHandled(const CCollisionCylinder &targetObject) const { return true; }
+
+	// handleCollisions alters this objects velocity and position as a result of
+	// a collision with one or more target objects
+	virtual void handleCollisions(CCollisionObjectVector *pCollisionVector, float *timeUsed, bool advanceObject);
+	virtual void handleCollision(const CCollisionSphere &targetObject, float *timeUsed, U32Distance3D *distance, bool advanceObject) {}
+	virtual void handleCollision(const CCollisionBox &targetObject, float *timeUsed, U32Distance3D *distance, bool advanceObject) {}
+	virtual void handleCollision(const CCollisionCylinder &targetObject, float *timeUsed, U32Distance3D *distance, bool advanceObject) {}
+
+	// Determine if this object is resting, running, or rolling on another object.
+	// An object doing this would not necessarily trigger an intersection...
+	virtual bool isOnObject(const ICollisionObject &targetObject, const U32Distance3D &distance) const;
+	virtual bool isOnObject(const CCollisionSphere &targetObject, const U32Distance3D &distance) const { return false; }
+	virtual bool isOnObject(const CCollisionBox &targetObject, const U32Distance3D &distance) const { return false; }
+	virtual bool isOnObject(const CCollisionCylinder &targetObject, const U32Distance3D &distance) const { return false; }
+
+	// Return a 3-dimensional bounding box for this object
+	virtual U32BoundingBox getBoundingBox() const = 0;
+	virtual U32BoundingBox getBigBoundingBox() const = 0;
+
+	// Return the point on the surface of the object that is closest to the input point
+	virtual U32FltPoint3D findNearestPoint(const U32FltPoint3D &testPoint) const;
+
+	// Call this function when it is known that the object is at a legal location and is
+	// not intersecting any other objects
+	virtual void save() {}
+
+	// Put the object at the last place that it was guaranteed to not be intersecting any
+	// other objects, and negate it's velocity vector
+	virtual void restore() {}
+
+	Common::String _description;
+	EObjectShape _objectShape;
+	EObjectType _objectType;
+	int _objectID; // An identifier that uniquely identifies this object
+
+	U32FltVector3D _velocity;
+
+	float _collisionEfficiency; // A multiplier (range 0 to 1) that represents
+								// the percentage of velocity (perpindicular with
+								// the collision) that a target object retains
+								// when it collides with this object
+
+	float _friction; // A multiplier (range 0 to 1) that represents
+					 // the percentage of velocity (parallel with
+					 // the collision) that a target object loses
+					 // when it collides with this object
+
+	int _soundNumber;
+
+	bool _ignore;  // There could be times when you want to ignore
+				   // an object for collision purposes. This flag
+				   // determines that.
+
+	CCollisionObjectVector _objectCollisionHistory; // Stack with pointers to all the
+													// objects that this object collided
+													// with this frame
+
+	CCollisionObjectVector _objectRollingHistory; // Stack with pointers to all the
+												  // objects that this object rolled
+												  // on this frame
+
+protected:
+	// Get the amount of time it took this sphere to penetrate whatever object it is
+	// currently intersecting in the given dimension
+	virtual float getPenetrationTime(const ICollisionObject &targetObject, const U32Distance3D &distance, EDimension dimension) const;
+	virtual float getPenetrationTime(const CCollisionSphere &targetObject, const U32Distance3D &distance, EDimension dimension) const { return 0.0F; }
+	virtual float getPenetrationTime(const CCollisionBox &targetObject, const U32Distance3D &distance, EDimension dimension) const { return 0.0F; }
+	virtual float getPenetrationTime(const CCollisionCylinder &targetObject, const U32Distance3D &distance, EDimension dimension) const { return 0.0F; }
+
+	// Sometimes when this object collides with another object, it may want to treat
+	// the object at the point of collision like a plane. This can help simplify the
+	// collision. defineReflectionPlane defines a plane between this object and the
+	// target object at the point of collision...
+	virtual void defineReflectionPlane(const ICollisionObject &targetObject, const U32Distance3D &distance, U32Plane *collisionPlane) const;
+	virtual void defineReflectionPlane(const CCollisionSphere &targetObject, const U32Distance3D &distance, U32Plane *collisionPlane) const;
+	virtual void defineReflectionPlane(const CCollisionBox &targetObject, const U32Distance3D &distance, U32Plane *collisionPlane) const;
+	virtual void defineReflectionPlane(const CCollisionCylinder &targetObject, const U32Distance3D &distance, U32Plane *collisionPlane) const;
+};
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_OBJECT_H
diff --git a/engines/scumm/he/basketball/collision/bball_collision_player.cpp b/engines/scumm/he/basketball/collision/bball_collision_player.cpp
new file mode 100644
index 00000000000..c840d0f0246
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_player.cpp
@@ -0,0 +1,95 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/basketball/court.h"
+#include "scumm/he/basketball/collision/bball_collision_player.h"
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+#include "scumm/he/basketball/collision/bball_collision_sphere.h"
+#include "scumm/he/basketball/collision/bball_collision_box.h"
+#include "scumm/he/basketball/collision/bball_collision_cylinder.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+#include "scumm/he/basketball/collision/bball_collision_node.h"
+#include "scumm/he/basketball/collision/bball_collision_tree.h"
+
+namespace Scumm {
+
+void CCollisionPlayer::startBlocking(int blockHeight, int blockTime) {
+	assert(blockTime != 0);
+
+	_maxBlockHeight = blockHeight;
+	_blockTime = blockTime;
+
+	int blockIncrement = _maxBlockHeight / _blockTime;
+
+	_blockHeight += blockIncrement;
+	height += blockIncrement;
+}
+
+void CCollisionPlayer::holdBlocking() {
+	int blockIncrement = _maxBlockHeight / _blockTime;
+
+	if (_velocity.z > 0) {
+		if ((_blockHeight + blockIncrement) <= _maxBlockHeight) {
+			_blockHeight += blockIncrement;
+			height += blockIncrement;
+		} else {
+			blockIncrement = _maxBlockHeight - _blockHeight;
+			_blockHeight += blockIncrement;
+			height += blockIncrement;
+
+			assert(_blockHeight == _maxBlockHeight);
+		}
+	} else if (_velocity.z < 0) {
+		if ((_blockHeight - blockIncrement) >= 0) {
+			_blockHeight -= blockIncrement;
+			height -= blockIncrement;
+		} else {
+			blockIncrement = _blockHeight;
+			_blockHeight -= blockIncrement;
+			height -= blockIncrement;
+
+			assert(_blockHeight == 0);
+		}
+	}
+}
+
+void CCollisionPlayer::endBlocking() {
+	height -= _blockHeight;
+	_blockHeight = 0;
+}
+
+bool CCollisionPlayer::testCatch(const ICollisionObject &targetObject, U32Distance3D *distance, CBBallCourt *court) {
+	if (&targetObject == (ICollisionObject *)&court->_virtualBall) {
+		int oldHeight = height;
+		height += _catchHeight;
+
+		bool retVal = ICollisionObject::testObjectIntersection(targetObject, distance);
+
+		height = oldHeight;
+
+		return retVal;
+	} else {
+		return false;
+	}
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/collision/bball_collision_player.h b/engines/scumm/he/basketball/collision/bball_collision_player.h
new file mode 100644
index 00000000000..188f30b09ef
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_player.h
@@ -0,0 +1,68 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_PLAYER_H
+#define SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_PLAYER_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/basketball/court.h"
+#include "scumm/he/basketball/collision/bball_collision_cylinder.h"
+
+namespace Scumm {
+
+#define PLAYER_CATCH_HEIGHT 250
+
+class CBBallCourt;
+
+class CCollisionPlayer : public CCollisionCylinder {
+public:
+	CCollisionPlayer() : _playerHasBall(false),
+						 _playerIsInGame(true),
+						 _blockHeight(0),
+						 _blockTime(0),
+						 _catchHeight(0),
+						 _maxBlockHeight(0) {}
+
+	~CCollisionPlayer() {}
+
+	void startBlocking(int blockHeight, int blockTime);
+	void holdBlocking();
+	void endBlocking();
+
+	bool _playerHasBall;
+	bool _playerIsInGame;
+
+	int _catchHeight; // The extra height that a player gets for detecting the ball.
+
+	bool testCatch(const ICollisionObject &targetObject, U32Distance3D *distance, CBBallCourt *court);
+
+private:
+	int _blockHeight = 0;    // The extra height that a player gets when blocking.
+	int _maxBlockHeight = 0; // The max extra height that a player gets when blocking.
+	int _blockTime = 0;      // The time it takes from the start of a block, to the apex of the block.
+};
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_PLAYER_H
diff --git a/engines/scumm/he/basketball/collision/bball_collision_shields.cpp b/engines/scumm/he/basketball/collision/bball_collision_shields.cpp
new file mode 100644
index 00000000000..13f7a0cabec
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_shields.cpp
@@ -0,0 +1,250 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/basketball/geo_translations.h"
+#include "scumm/he/basketball/court.h"
+
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+#include "scumm/he/basketball/collision/bball_collision_sphere.h"
+#include "scumm/he/basketball/collision/bball_collision_box.h"
+#include "scumm/he/basketball/collision/bball_collision_cylinder.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+#include "scumm/he/basketball/collision/bball_collision_node.h"
+#include "scumm/he/basketball/collision/bball_collision_tree.h"
+#include "scumm/he/basketball/collision/bball_collision_shields.h"
+
+namespace Scumm {
+
+CCollisionShieldVector::CCollisionShieldVector() : _shieldUpCount(0) {
+	CCollisionBox westShield;
+	CCollisionBox northShield;
+	CCollisionBox eastShield;
+	CCollisionBox southShield;
+	CCollisionBox topShield;
+
+	CCollisionBox westShield2;
+	CCollisionBox northShield2;
+	CCollisionBox eastShield2;
+	CCollisionBox southShield2;
+
+	westShield.minPoint.x = 0 - SHIELD_DEPTH;
+	westShield.maxPoint.x = 0;
+	westShield.minPoint.y = 0 - SHIELD_EXTENSION;
+	westShield.maxPoint.y = MAX_WORLD_Y + SHIELD_EXTENSION;
+	westShield.minPoint.z = 0;
+	westShield.maxPoint.z = SHIELD_HEIGHT;
+	westShield._description = "West Shield";
+	westShield._collisionEfficiency = 0.5F;
+	westShield._friction = 0.3F;
+	westShield._objectID = WEST_SHIELD_ID;
+	westShield._ignore = true;
+
+	westShield2.minPoint.x = 0 - SHIELD_DEPTH - BUFFER_WIDTH;
+	westShield2.maxPoint.x = 0 - BUFFER_WIDTH;
+	westShield2.minPoint.y = 0 - SHIELD_EXTENSION;
+	westShield2.maxPoint.y = MAX_WORLD_Y + SHIELD_EXTENSION;
+	westShield2.minPoint.z = 0;
+	westShield2.maxPoint.z = SHIELD_HEIGHT;
+	westShield2._description = "West Shield 2";
+	westShield2._collisionEfficiency = 0.5F;
+	westShield2._friction = 0.3F;
+	westShield2._objectID = BACKUP_WEST_SHIELD_ID;
+	westShield2._ignore = true;
+
+	northShield.minPoint.x = 0 - SHIELD_EXTENSION;
+	northShield.maxPoint.x = MAX_WORLD_X + SHIELD_EXTENSION;
+	northShield.minPoint.y = MAX_WORLD_Y;
+	northShield.maxPoint.y = MAX_WORLD_Y + SHIELD_DEPTH;
+	northShield.minPoint.z = 0;
+	northShield.maxPoint.z = SHIELD_HEIGHT;
+	northShield._description = "North Shield";
+	northShield._collisionEfficiency = 0.5F;
+	northShield._friction = 0.3F;
+	northShield._objectID = NORTH_SHIELD_ID;
+	northShield._ignore = true;
+
+	northShield2.minPoint.x = 0 - SHIELD_EXTENSION;
+	northShield2.maxPoint.x = MAX_WORLD_X + SHIELD_EXTENSION;
+	northShield2.minPoint.y = MAX_WORLD_Y + BUFFER_WIDTH;
+	northShield2.maxPoint.y = MAX_WORLD_Y + SHIELD_DEPTH + BUFFER_WIDTH;
+	northShield2.minPoint.z = 0;
+	northShield2.maxPoint.z = SHIELD_HEIGHT;
+	northShield2._description = "North Shield 2";
+	northShield2._collisionEfficiency = 0.5F;
+	northShield2._friction = 0.3F;
+	northShield2._objectID = BACKUP_NORTH_SHIELD_ID;
+	northShield2._ignore = true;
+
+	eastShield.minPoint.x = MAX_WORLD_X;
+	eastShield.maxPoint.x = MAX_WORLD_X + SHIELD_DEPTH;
+	eastShield.minPoint.y = 0 - SHIELD_EXTENSION;
+	eastShield.maxPoint.y = MAX_WORLD_Y + SHIELD_EXTENSION;
+	eastShield.minPoint.z = 0;
+	eastShield.maxPoint.z = SHIELD_HEIGHT;
+	eastShield._description = "East Shield";
+	eastShield._collisionEfficiency = 0.5F;
+	eastShield._friction = 0.3F;
+	eastShield._objectID = EAST_SHIELD_ID;
+	eastShield._ignore = true;
+
+	eastShield2.minPoint.x = MAX_WORLD_X + BUFFER_WIDTH;
+	eastShield2.maxPoint.x = MAX_WORLD_X + SHIELD_DEPTH + BUFFER_WIDTH;
+	eastShield2.minPoint.y = 0 - SHIELD_EXTENSION;
+	eastShield2.maxPoint.y = MAX_WORLD_Y + SHIELD_EXTENSION;
+	eastShield2.minPoint.z = 0;
+	eastShield2.maxPoint.z = SHIELD_HEIGHT;
+	eastShield2._description = "East Shield 2";
+	eastShield2._collisionEfficiency = 0.5F;
+	eastShield2._friction = 0.3F;
+	eastShield2._objectID = BACKUP_EAST_SHIELD_ID;
+	eastShield2._ignore = true;
+
+	southShield.minPoint.x = 0 - SHIELD_EXTENSION;
+	southShield.maxPoint.x = MAX_WORLD_X + SHIELD_EXTENSION;
+	southShield.minPoint.y = 0 - SHIELD_DEPTH;
+	southShield.maxPoint.y = 0;
+	southShield.minPoint.z = 0;
+	southShield.maxPoint.z = SHIELD_HEIGHT;
+	southShield._description = "South Shield";
+	southShield._collisionEfficiency = 0.5F;
+	southShield._friction = 0.3F;
+	southShield._objectID = SOUTH_SHIELD_ID;
+	southShield._ignore = true;
+
+	southShield2.minPoint.x = 0 - SHIELD_EXTENSION;
+	southShield2.maxPoint.x = MAX_WORLD_X + SHIELD_EXTENSION;
+	southShield2.minPoint.y = 0 - SHIELD_DEPTH - BUFFER_WIDTH;
+	southShield2.maxPoint.y = 0 - BUFFER_WIDTH;
+	southShield2.minPoint.z = 0;
+	southShield2.maxPoint.z = SHIELD_HEIGHT;
+	southShield2._description = "South Shield 2";
+	southShield2._collisionEfficiency = 0.5F;
+	southShield2._friction = 0.3F;
+	southShield2._objectID = BACKUP_SOUTH_SHIELD_ID;
+	southShield2._ignore = true;
+
+	topShield.minPoint.x = 0 - SHIELD_EXTENSION;
+	topShield.maxPoint.x = MAX_WORLD_X + SHIELD_EXTENSION;
+	topShield.minPoint.y = 0 - SHIELD_EXTENSION;
+	topShield.maxPoint.y = MAX_WORLD_Y + SHIELD_EXTENSION;
+	topShield.minPoint.z = SHIELD_HEIGHT;
+	topShield.maxPoint.z = SHIELD_HEIGHT + SHIELD_DEPTH;
+	topShield._description = "Top Shield";
+	topShield._collisionEfficiency = 0.5F;
+	topShield._friction = 0.3F;
+	topShield._objectID = TOP_SHIELD_ID;
+	topShield._ignore = true;
+
+	push_back(westShield);
+	push_back(northShield);
+	push_back(eastShield);
+	push_back(southShield);
+	push_back(topShield);
+
+	push_back(westShield2);
+	push_back(northShield2);
+	push_back(eastShield2);
+	push_back(southShield2);
+}
+
+CCollisionShieldVector::~CCollisionShieldVector() {
+	CCollisionShieldVector::iterator shieldIt;
+
+	for (shieldIt = begin(); shieldIt != end(); ++shieldIt) {
+		shieldIt->_ignore = true;
+	}
+}
+
+int LogicHEBasketball::u32_userRaiseShields(int shieldID) {
+	assert(shieldID < MAX_SHIELD_COUNT || shieldID == ALL_SHIELD_ID);
+
+	CCollisionShieldVector::iterator shieldIt;
+
+	for (shieldIt = _vm->_basketball->_shields->begin(); shieldIt != _vm->_basketball->_shields->end(); ++shieldIt) {
+		// Make sure we don't mess with the backup shields...
+		if (shieldIt->_objectID < MAX_SHIELD_COUNT) {
+			if (((shieldIt->_objectID == shieldID) || (shieldID == ALL_SHIELD_ID)) &&
+				(shieldIt->_ignore == true)) {
+				shieldIt->_ignore = false;
+				++_vm->_basketball->_shields->_shieldUpCount;
+			}
+		}
+	}
+
+	if (shieldID == ALL_SHIELD_ID) {
+		assert(_vm->_basketball->_shields->_shieldUpCount == MAX_SHIELD_COUNT);
+	}
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userLowerShields(int shieldID) {
+	assert(shieldID < MAX_SHIELD_COUNT || shieldID == ALL_SHIELD_ID);
+
+	CCollisionShieldVector::iterator shieldIt;
+
+	for (shieldIt = _vm->_basketball->_shields->begin(); shieldIt != _vm->_basketball->_shields->end(); ++shieldIt) {
+		// Make sure we don't mess with the backup shields...
+		if (shieldIt->_objectID < MAX_SHIELD_COUNT) {
+			if (((shieldIt->_objectID == shieldID) || (shieldID == ALL_SHIELD_ID)) &&
+				(shieldIt->_ignore == false)) {
+				shieldIt->_ignore = true;
+				--_vm->_basketball->_shields->_shieldUpCount;
+			}
+		}
+	}
+
+	if (shieldID == ALL_SHIELD_ID) {
+		assert(_vm->_basketball->_shields->_shieldUpCount == 0);
+	}
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userAreShieldsClear() {
+	int shieldsAreClear = (_vm->_basketball->_shields->_shieldUpCount != MAX_SHIELD_COUNT);
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, shieldsAreClear);
+	return 1;
+}
+
+int LogicHEBasketball::u32_userShieldPlayer(int playerID, int shieldRadius) {
+	CCollisionPlayer *player = _vm->_basketball->_court->getPlayerPtr(playerID);
+
+	if (player->_shieldRadius < shieldRadius) {
+		player->_shieldRadius += PLAYER_SHIELD_INCREMENT_VALUE;
+		player->radius += PLAYER_SHIELD_INCREMENT_VALUE;
+	}
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userClearPlayerShield(int playerID) {
+	CCollisionPlayer *player = _vm->_basketball->_court->getPlayerPtr(playerID);
+
+	player->radius -= player->_shieldRadius;
+	player->_shieldRadius = 0.0F;
+
+	return 1;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/collision/bball_collision_shields.h b/engines/scumm/he/basketball/collision/bball_collision_shields.h
new file mode 100644
index 00000000000..587a875aaea
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_shields.h
@@ -0,0 +1,64 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_SHIELDS_H
+#define SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_SHIELDS_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/basketball/collision/bball_collision_box.h"
+
+namespace Scumm {
+
+#define WEST_SHIELD_ID    0
+#define NORTH_SHIELD_ID   1
+#define EAST_SHIELD_ID    2
+#define SOUTH_SHIELD_ID   3
+#define TOP_SHIELD_ID     4
+#define ALL_SHIELD_ID     5
+#define MAX_SHIELD_COUNT  5
+
+#define BACKUP_WEST_SHIELD_ID  6
+#define BACKUP_NORTH_SHIELD_ID 7
+#define BACKUP_EAST_SHIELD_ID  8
+#define BACKUP_SOUTH_SHIELD_ID 9
+
+#define PLAYER_SHIELD_INCREMENT_VALUE 25
+
+#define SHIELD_DEPTH      1000
+#define SHIELD_HEIGHT     50000
+#define SHIELD_EXTENSION  20000
+#define BUFFER_WIDTH      800
+
+class CCollisionShieldVector : public Common::Array<CCollisionBox> {
+public:
+	CCollisionShieldVector();
+	~CCollisionShieldVector();
+
+	int _shieldUpCount;
+};
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_SHIELDS_H
diff --git a/engines/scumm/he/basketball/collision/bball_collision_sphere.cpp b/engines/scumm/he/basketball/collision/bball_collision_sphere.cpp
new file mode 100644
index 00000000000..5a889062183
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_sphere.cpp
@@ -0,0 +1,662 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+#include "scumm/he/basketball/collision/bball_collision_sphere.h"
+#include "scumm/he/basketball/collision/bball_collision_box.h"
+#include "scumm/he/basketball/collision/bball_collision_cylinder.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+#include "scumm/he/basketball/collision/bball_collision_node.h"
+#include "scumm/he/basketball/collision/bball_collision_tree.h"
+
+namespace Scumm {
+
+float CCollisionSphere::getDimensionDistance(const CCollisionBox &targetObject, EDimension dimension) const {
+	if (center[dimension] < targetObject.minPoint[dimension]) {
+		return (center[dimension] - targetObject.minPoint[dimension]);
+	} else if (center[dimension] > targetObject.maxPoint[dimension]) {
+		return (center[dimension] - targetObject.maxPoint[dimension]);
+	} else {
+		return 0;
+	}
+}
+
+float CCollisionSphere::getDimensionDistance(const CCollisionCylinder &targetObject, EDimension dimension) const {
+	float centerDistance = center[dimension] - targetObject.center[dimension];
+
+	if (dimension == Z_INDEX) {
+		if (centerDistance < -(targetObject.height / 2)) {
+			return (centerDistance + (targetObject.height / 2));
+		} else if (centerDistance > (targetObject.height / 2)) {
+			return (centerDistance - (targetObject.height / 2));
+		} else {
+			return 0;
+		}
+	} else {
+		return centerDistance;
+	}
+}
+
+float CCollisionSphere::getObjectDistance(const CCollisionBox &targetObject) const {
+	U32Distance3D distance;
+
+	distance.x = getDimensionDistance(targetObject, X_INDEX);
+	distance.y = getDimensionDistance(targetObject, Y_INDEX);
+	distance.z = getDimensionDistance(targetObject, Z_INDEX);
+
+	float totalDistance = distance.magnitude() - radius;
+	if (totalDistance < 0)
+		totalDistance = 0;
+
+	return totalDistance;
+}
+
+float CCollisionSphere::getObjectDistance(const CCollisionCylinder &targetObject) const {
+	U32Distance3D distance;
+
+	distance.x = getDimensionDistance(targetObject, X_INDEX);
+	distance.y = getDimensionDistance(targetObject, Y_INDEX);
+	distance.z = getDimensionDistance(targetObject, Z_INDEX);
+
+	float xyDistance = distance.xyMagnitude() - radius - targetObject.radius;
+	if (xyDistance < 0)
+		xyDistance = 0;
+
+	float zDistance = fabs(distance.z) - radius - (targetObject.height / 2);
+	if (zDistance < 0)
+		zDistance = 0;
+
+	float totalDistance = sqrt((xyDistance * xyDistance) + (zDistance * zDistance));
+
+	return totalDistance;
+}
+
+bool CCollisionSphere::testObjectIntersection(const CCollisionBox &targetObject, U32Distance3D *distance) const {
+	// Get the distance between the ball and the bounding box...
+	distance->x = getDimensionDistance(targetObject, X_INDEX);
+	distance->y = getDimensionDistance(targetObject, Y_INDEX);
+	distance->z = getDimensionDistance(targetObject, Z_INDEX);
+
+	// Determine if the ball intersects the bounding box.
+	return (distance->magnitude() < radius);
+}
+
+bool CCollisionSphere::testObjectIntersection(const CCollisionCylinder &targetObject, U32Distance3D *distance) const {
+	// Get the distance between the ball and the cylinder...
+	distance->x = getDimensionDistance(targetObject, X_INDEX);
+	distance->y = getDimensionDistance(targetObject, Y_INDEX);
+	distance->z = getDimensionDistance(targetObject, Z_INDEX);
+
+	if (distance->xyMagnitude() < (radius + targetObject.radius)) {
+		return (fabs(distance->z) < radius);
+	} else {
+		return false;
+	}
+}
+
+bool CCollisionSphere::validateCollision(const CCollisionBox &targetObject, U32Distance3D *distance) {
+	U32FltPoint3D targetPoint = targetObject.findNearestPoint(center);
+	U32FltVector3D centerVector = targetPoint - center;
+	float aMag = _velocity.magnitude();
+	float bMag = centerVector.magnitude();
+	float aDotb = _velocity * centerVector;
+
+	if (aMag == 0) {
+		// If this object isn't moving, this can't be a valid collision...
+		return false;
+	}
+
+	if (bMag == 0) {
+		// bMag is 0, it is possible that this sphere penetrated too far into the target
+		// object. If this is the case, we'll go ahead and validate the collision...
+		return true;
+	}
+
+	double angleCosine = aDotb / (aMag * bMag);
+
+	if (angleCosine > 1) {
+		angleCosine = 1;
+	} else if (angleCosine < -1) {
+		angleCosine = -1;
+	}
+
+	float sourceMovementAngle = acos(angleCosine);
+
+	if (sourceMovementAngle < (BBALL_M_PI / 2)) {
+		return true;
+	} else {
+		return false;
+	}
+}
+
+bool CCollisionSphere::validateCollision(const CCollisionCylinder &targetObject, U32Distance3D *distance) {
+	// Create a vector from the center of the target cylinder to the center of
+	// this sphere. If the sphere is not hitting above or below the cylinder,
+	// negate any z component of the center vector...
+	U32FltVector3D centerVector = targetObject.center - center;
+
+	if (center.z > (targetObject.center.z + targetObject.height / 2)) {
+		centerVector.z = targetObject.center.z + (targetObject.height / 2) - center.z;
+	} else if (center.z < (targetObject.center.z - targetObject.height / 2)) {
+		centerVector.z = targetObject.center.z - (targetObject.height / 2) - center.z;
+	} else if (_velocity.xyMagnitude() != 0) {
+		centerVector.z = 0;
+	}
+
+	float aMag = _velocity.magnitude();
+	float bMag = centerVector.magnitude();
+	float aDotb = _velocity * centerVector;
+
+	if (aMag == 0) {
+		// If this object isn't moving, this can't be a valid collision...
+		return false;
+	}
+
+	if (bMag == 0) {
+		// bMag is 0, it is possible that this sphere penetrated too far into the target
+		// object. If this is the case, we'll go ahead and validate the collision...
+		return true;
+	}
+
+	// Calculate the angle between this object's velocity and the vector from the target
+	// objects center to our center. If the angle between the two is greater than
+	// 90 degrees, then the target object is not colliding with us...
+	double angleCosine = aDotb / (aMag * bMag);
+
+	if (angleCosine > 0) {
+		return true;
+	} else {
+		return false;
+	}
+}
+
+float CCollisionSphere::getPenetrationTime(const CCollisionBox &targetObject, const U32Distance3D &distance, EDimension dimension) const {
+	float collisionDepth1;
+	float collisionDepth2;
+
+	float boxWidth = targetObject.maxPoint[dimension] - targetObject.minPoint[dimension];
+
+	if (distance[dimension] > 0) {
+		collisionDepth1 = (radius - distance[dimension]);
+		collisionDepth2 = (-radius + distance[dimension] - boxWidth);
+	} else if (distance[dimension] < 0) {
+		collisionDepth1 = (-radius - distance[dimension]);
+		collisionDepth2 = (radius - distance[dimension] + boxWidth);
+	} else {
+		return 0;
+	}
+
+	float time1 = (_velocity[dimension] == 0) ? 0 : collisionDepth1 / -_velocity[dimension];
+	float time2 = (_velocity[dimension] == 0) ? 0 : collisionDepth2 / -_velocity[dimension];
+	float tFinal = MIN_GREATER_THAN_ZERO(time1, time2);
+
+	return tFinal;
+}
+
+float CCollisionSphere::getPenetrationTime(const CCollisionCylinder &targetObject, const U32Distance3D &distance, EDimension dimension) const {
+	float collisionDepth;
+
+	if (dimension == Z_INDEX) {
+		if (distance[dimension] > 0) {
+			collisionDepth = (radius - distance[dimension]);
+		} else if (distance[dimension] < 0) {
+			collisionDepth = (-radius - distance[dimension]);
+		} else {
+			collisionDepth = 0;
+		}
+	} else {
+		if (distance[dimension] > 0) {
+			collisionDepth = (radius + targetObject.radius - distance[dimension]);
+		} else if (distance[dimension] < 0) {
+			collisionDepth = (-radius - targetObject.radius - distance[dimension]);
+		} else {
+			collisionDepth = 0;
+		}
+	}
+
+	float tFinal = (_velocity[dimension] == 0) ? 0 : (collisionDepth / -_velocity[dimension]);
+	return tFinal;
+}
+
+bool CCollisionSphere::backStraightOutOfObject(const ICollisionObject &targetObject, U32Distance3D *distance, float *timeUsed) {
+	if (_velocity.magnitude() == 0)
+		return true;
+
+	U32FltPoint3D startPosition = center;
+
+	int loopCounter = 0;
+
+	while (ICollisionObject::testObjectIntersection(targetObject, distance)) {
+		float collisionTimes[3];
+
+		collisionTimes[X_INDEX] = ICollisionObject::getPenetrationTime(targetObject, *distance, X_INDEX);
+		collisionTimes[Y_INDEX] = ICollisionObject::getPenetrationTime(targetObject, *distance, Y_INDEX);
+		collisionTimes[Z_INDEX] = ICollisionObject::getPenetrationTime(targetObject, *distance, Z_INDEX);
+
+		Std::sort(collisionTimes, collisionTimes + Z_INDEX + 1);
+
+		float collisionTime = COLLISION_SMALL_TIME_INCREMENT;
+		if (collisionTimes[2] > 0)
+			collisionTime = collisionTimes[2];
+		if (collisionTimes[1] > 0)
+			collisionTime = collisionTimes[1];
+		if (collisionTimes[0] > 0)
+			collisionTime = collisionTimes[0];
+
+		*timeUsed += collisionTime;
+
+		// If we take too long to back out, something is wrong.
+		// Restore the object to an ok state...
+		if ((*timeUsed > COLLISION_BACK_OUT_TIME_LIMIT) && (*timeUsed != collisionTime)) {
+			warning("CCollisionSphere::backStraightOutOfObject(): It took too long for one object to back out of another.  Ignore and U32 will attempt to correct.");
+			center = startPosition;
+			restore();
+			return false;
+		}
+
+		center.x -= (collisionTime * _velocity.x);
+		center.y -= (collisionTime * _velocity.y);
+		center.z -= (collisionTime * _velocity.z);
+
+		// Make doubly sure we don't loop forever...
+		if (++loopCounter > 500)
+			return false;
+	}
+
+	return true;
+}
+
+bool CCollisionSphere::backOutOfObject(const CCollisionBox &targetObject, U32Distance3D *distance, float *timeUsed) {
+	return backStraightOutOfObject(targetObject, distance, timeUsed);
+}
+
+bool CCollisionSphere::backOutOfObject(const CCollisionCylinder &targetObject, U32Distance3D *distance, float *timeUsed) {
+	return backStraightOutOfObject(targetObject, distance, timeUsed);
+}
+
+bool CCollisionSphere::nudgeObject(const CCollisionBox &targetObject, U32Distance3D *distance, float *timeUsed) {
+	// To nudge the sphere precisely against the box, we need to calculate when the
+	// square root of the sum of the squared distances between the sphere and the three
+	// planes equals the radius of the sphere. Here we will construct a quadratic
+	// equation to solve for time....
+
+	double a = 0;
+	double b = 0;
+	double c = -(radius * radius);
+
+	for (int i = X_INDEX; i <= Z_INDEX; ++i) {
+		EDimension dim = (EDimension)i;
+
+		// If the ball is already within the boundaries of the box in a certain dimension,
+		// we don't want to include that dimension in the equation...
+		if ((*distance)[dim] != 0) {
+			a += (_velocity[dim] * _velocity[dim]);
+			b += (2 * _velocity[dim] * (*distance)[dim]);
+			c += ((*distance)[dim] * (*distance)[dim]);
+		}
+	}
+
+	if (((b * b) < (4 * a * c)) || (a == 0)) {
+		warning("CCollisionSphere::nudgeObject(): Tried to use sqrt on a negative number");
+		return false;
+	}
+
+	// Now we have two answer candidates. We want the smallest of the two that is
+	// greater than 0...
+	double t1 = (-b + sqrt(b * b - 4 * a * c)) / (2 * a);
+	double t2 = (-b - sqrt(b * b - 4 * a * c)) / (2 * a);
+
+	double tFinal = 0;
+	if ((0 <= t1) && (t1 <= t2)) {
+		tFinal = t1;
+	} else if ((0 <= t2) && (t2 <= t1)) {
+		tFinal = t2;
+	}
+
+	// Update the position of the ball...
+	center.x += _velocity.x * tFinal;
+	center.y += _velocity.y * tFinal;
+	center.z += _velocity.z * tFinal;
+	*timeUsed -= tFinal;
+
+	testObjectIntersection(targetObject, distance);
+	return true;
+}
+
+bool CCollisionSphere::nudgeObject(const CCollisionCylinder &targetObject, U32Distance3D *distance, float *timeUsed) {
+	float collisionTimeFinal;
+
+	U32FltVector3D xyVelocity = _velocity;
+	xyVelocity.z = 0;
+
+	// Find the distance between the two centers that would indicate an exact collision...
+	float intersectionDist = radius + targetObject.radius;
+
+	// Set up a vector pointing from the center of this sphere to the center of
+	// the target cylinder...
+	U32FltVector3D centerVector;
+	centerVector.x = targetObject.center.x - center.x;
+	centerVector.y = targetObject.center.y - center.y;
+
+	float centerDistance = centerVector.xyMagnitude();
+
+	if (centerDistance > intersectionDist) {
+		// Project the center vector onto the velocity vector.
+		// This is the distance along the velocity vector from the
+		// center of the sphere to the point that is parallel with the center
+		// of the cylinder...
+		float parallelDistance = centerVector.projectScalar(xyVelocity);
+
+		// Find the distance between the center of the target object to the point that
+		// that is distance2 units along the the velocity vector...
+		parallelDistance = ((centerDistance * centerDistance) >=
+							(parallelDistance * parallelDistance))
+							   ? parallelDistance
+							   : COPY_SIGN(centerDistance, parallelDistance);
+
+		// Make sure we don't try to sqrt by negative number...
+		if (parallelDistance > centerDistance) {
+			warning("CCollisionSphere::nudgeObject(): Tried to sqrt by negative number.");
+			centerDistance = parallelDistance;
+		}
+
+		float perdistance = sqrt(centerDistance * centerDistance - parallelDistance * parallelDistance);
+
+		// Now we need to find the point along the velocity vector where the distance 
+		// between that point and the center of the target cylinder equals intersectionDist.
+		// We calculate using distance 2, distance3 and intersectionDist...
+		perdistance = ((intersectionDist * intersectionDist) >=
+						(perdistance * perdistance))
+						   ? perdistance
+						   : COPY_SIGN(intersectionDist, perdistance);
+
+		// Make sure we don't try to sqrt by negative number...
+		if (perdistance > intersectionDist) {
+			warning("CCollisionSphere::nudgeObject(): Tried to sqrt by negative number.");
+			intersectionDist = perdistance;
+		}
+
+		float xyCollisionDist = parallelDistance - sqrt(intersectionDist * intersectionDist - perdistance * perdistance);
+		collisionTimeFinal = (_velocity.xyMagnitude() == 0) ? 0 : (xyCollisionDist / _velocity.xyMagnitude());
+	} else {
+		collisionTimeFinal = -getPenetrationTime(targetObject, *distance, Z_INDEX);
+	}
+
+	center.x += collisionTimeFinal * _velocity.x;
+	center.y += collisionTimeFinal * _velocity.y;
+	center.z += collisionTimeFinal * _velocity.z;
+	*timeUsed -= collisionTimeFinal;
+
+	testObjectIntersection(targetObject, distance);
+
+	// We may still have a little ways to go in the z direction...
+	if (fabs(distance->z) >= (radius + COLLISION_EPSILON)) {
+		collisionTimeFinal = -getPenetrationTime(targetObject, *distance, Z_INDEX);
+
+		center.x += collisionTimeFinal * _velocity.x;
+		center.y += collisionTimeFinal * _velocity.y;
+		center.z += collisionTimeFinal * _velocity.z;
+		*timeUsed -= collisionTimeFinal;
+
+		testObjectIntersection(targetObject, distance);
+	}
+
+	return true;
+}
+
+void CCollisionSphere::defineReflectionPlane(const CCollisionBox &targetObject, const U32Distance3D &distance, U32Plane *collisionPlane) const {
+	// Find the point of collision...
+	collisionPlane->point = targetObject.findNearestPoint(center);
+
+	// Find the normal of the collision plane...
+	collisionPlane->normal.x = (distance.magnitude() == 0) ? 0 : distance.x / distance.magnitude();
+	collisionPlane->normal.y = (distance.magnitude() == 0) ? 0 : distance.y / distance.magnitude();
+	collisionPlane->normal.z = (distance.magnitude() == 0) ? 0 : distance.z / distance.magnitude();
+
+	collisionPlane->friction = targetObject._friction;
+	collisionPlane->collisionEfficiency = targetObject._collisionEfficiency;
+}
+
+void CCollisionSphere::defineReflectionPlane(const CCollisionCylinder &targetObject, const U32Distance3D &distance, U32Plane *collisionPlane) const {
+	// Find the point of collision...
+	collisionPlane->point = targetObject.findNearestPoint(center);
+
+	// Find the normal of the collision plane...
+	collisionPlane->normal.x = (distance.magnitude() == 0) ? 0 : distance.x / distance.magnitude();
+	collisionPlane->normal.y = (distance.magnitude() == 0) ? 0 : distance.y / distance.magnitude();
+	collisionPlane->normal.z = (distance.magnitude() == 0) ? 0 : distance.z / distance.magnitude();
+
+	collisionPlane->friction = targetObject._friction;
+	collisionPlane->collisionEfficiency = targetObject._collisionEfficiency;
+}
+
+void CCollisionSphere::ReboundOffPlane(const U32Plane &collisionPlane, bool isOnObject) {
+	U32FltVector3D normalVector;
+	U32FltVector3D reflectionVector;
+	U32FltVector3D parallelVector;
+	U32FltVector3D perpendicularVector;
+
+	// Calculate the reflection vector...
+	normalVector = _velocity.projectVector(collisionPlane.normal) * -1;
+	reflectionVector = (normalVector * 2) + _velocity;
+
+	// Break down the components of the refelction vector that are perpendicular
+	// and parallel to the collision plane. This way we can account for drag and
+	// inelastic collisions...
+	perpendicularVector = normalVector;
+	perpendicularVector *= collisionPlane.collisionEfficiency * _collisionEfficiency;
+
+	parallelVector = reflectionVector - normalVector;
+
+	if ((!isOnObject) || ((m_rollingCount % ROLL_SLOWDOWN_FREQUENCY) == 0)) {
+		parallelVector -= ((parallelVector * collisionPlane.friction) + (parallelVector * _friction));
+	}
+
+	// Now put the reflection vector back together again...
+	reflectionVector = parallelVector + perpendicularVector;
+
+	_velocity = reflectionVector;
+}
+
+void CCollisionSphere::increaseVelocity(float minSpeed) {
+	if (_velocity.magnitude() < minSpeed) {
+		// Make sure we're moving along the XY plane...
+		if (_velocity.xyMagnitude() == 0) {
+			// Randomly add some velocity...
+			int random = (g_scumm->_rnd.getRandomNumber(0x7FFF) * 4) / 0x7FFF;
+			switch (random) {
+			case 0:
+				_velocity.x = minSpeed;
+				break;
+
+			case 1:
+				_velocity.x = -minSpeed;
+				break;
+
+			case 2:
+				_velocity.y = minSpeed;
+				break;
+
+			case 3:
+			case 4:
+				_velocity.y = -minSpeed;
+				break;
+
+			default:
+				warning("CCollisionSphere::increaseVelocity(): Invalid random number.");
+			}
+		} else {
+			// Increase the current velocity...
+			float oldVelocity = _velocity.magnitude();
+			_velocity.x = (_velocity.x / oldVelocity) * minSpeed;
+			_velocity.y = (_velocity.y / oldVelocity) * minSpeed;
+			_velocity.z = (_velocity.z / oldVelocity) * minSpeed;
+		}
+	}
+}
+
+void CCollisionSphere::handleCollisions(CCollisionObjectVector *collisionVector, float *timeUsed, bool advanceObject) {
+	Common::Array<U32Plane> planeVector;
+	Common::Array<bool> rollingRecord;
+	U32Plane collisionPlane;
+	U32Distance3D distance;
+	bool isRollingOnObject;
+
+	for (CCollisionObjectVector::const_iterator objectIt = collisionVector->begin();
+		 objectIt != collisionVector->end();
+		 ++objectIt) {
+		const ICollisionObject *currentObject = *objectIt;
+
+		ICollisionObject::testObjectIntersection(*currentObject, &distance);
+
+		// See if we are rolling on this object...
+		isRollingOnObject = ICollisionObject::isOnObject(*currentObject, distance);
+		if (isRollingOnObject) {
+			// See if rolling has slowed to the point that we are stopped.
+			// Never stop on the backboard or rim...
+			if ((currentObject->_objectType == kBackboard) ||
+				(currentObject->_objectType == kRim)) {
+				increaseVelocity(ROLL_SPEED_LIMIT);
+			} else {
+				if (_velocity.xyMagnitude() < ROLL_SPEED_LIMIT) {
+					_velocity.x = 0;
+					_velocity.y = 0;
+				}
+			}
+
+			_velocity.z = 0;
+		} else {
+			// See if rolling has slowed to the point that we are stopped.
+			// Never stop on the backboard or rim...
+			if ((currentObject->_objectType == kBackboard) ||
+				(currentObject->_objectType == kRim)) {
+				increaseVelocity(ROLL_SPEED_LIMIT);
+			}
+		}
+
+		ICollisionObject::defineReflectionPlane(*currentObject, distance, &collisionPlane);
+		planeVector.push_back(collisionPlane);
+		rollingRecord.push_back(isRollingOnObject);
+	}
+
+	collisionVector->clear();
+
+	if (!planeVector.empty()) {
+		collisionPlane = planeVector[0];
+		isRollingOnObject = rollingRecord[0];
+
+		Common::Array<U32Plane>::const_iterator planeIt;
+		Common::Array<bool>::const_iterator recordIt;
+
+		for (planeIt = planeVector.begin(), recordIt = rollingRecord.begin();
+			 planeIt != planeVector.end();
+			 ++planeIt, ++recordIt) {
+
+			collisionPlane.Average(*planeIt);
+			isRollingOnObject = isRollingOnObject || *recordIt;
+		}
+
+		ReboundOffPlane(collisionPlane, isRollingOnObject);
+	}
+
+	if (advanceObject) {
+		// Move the ball for the amount of time remaining...
+		center.x += (_velocity.x * *timeUsed);
+		center.y += (_velocity.y * *timeUsed);
+		center.z += (_velocity.z * *timeUsed);
+
+		*timeUsed = 0;
+	}
+}
+
+bool CCollisionSphere::isOnObject(const CCollisionBox &targetObject, const U32Distance3D &distance) const {
+	float distancePercentage = fabs(distance.z - radius) / radius;
+
+	// See if bouncing has slowed to the point that we are rolling...
+	return ((distancePercentage < .1) &&
+			(distance.xyMagnitude() == 0) &&
+			(fabs(_velocity.z) <= BOUNCE_SPEED_LIMIT));
+}
+
+bool CCollisionSphere::isOnObject(const CCollisionCylinder &targetObject, const U32Distance3D &distance) const {
+	float distancePercentage = fabs(distance.z - radius) / radius;
+
+	// See if bouncing has slowed to the point that we are rolling...
+	return ((distancePercentage < .1) &&
+			(distance.xyMagnitude() <= targetObject.radius) &&
+			(fabs(_velocity.z) <= BOUNCE_SPEED_LIMIT));
+}
+
+U32BoundingBox CCollisionSphere::getBoundingBox() const {
+	U32BoundingBox outBox;
+
+	outBox.minPoint.x = center.x - radius;
+	outBox.minPoint.y = center.y - radius;
+	outBox.minPoint.z = center.z - radius;
+
+	outBox.maxPoint.x = center.x + radius;
+	outBox.maxPoint.y = center.y + radius;
+	outBox.maxPoint.z = center.z + radius;
+
+	return outBox;
+}
+
+U32BoundingBox CCollisionSphere::getBigBoundingBox() const {
+	U32BoundingBox outBox;
+
+	float velocity = _velocity.magnitude();
+
+	outBox.minPoint.x = center.x - (radius + velocity);
+	outBox.minPoint.y = center.y - (radius + velocity);
+	outBox.minPoint.z = center.z - (radius + velocity);
+
+	outBox.maxPoint.x = center.x + (radius + velocity);
+	outBox.maxPoint.y = center.y + (radius + velocity);
+	outBox.maxPoint.z = center.z + (radius + velocity);
+
+	return outBox;
+}
+
+void CCollisionSphere::save() {
+	_positionSaved = true;
+	_safetyPoint = center;
+	_safetyVelocity = _velocity;
+}
+
+void CCollisionSphere::restore() {
+	if (_positionSaved) {
+		if (_safetyVelocity.magnitude() != 0) {
+			debug("CCollisionSphere::Restore(): Restoring");
+			center = _safetyPoint;
+
+			_velocity.x = 0;
+			_velocity.y = 0;
+			_velocity.z = 0;
+		}
+	} else {
+		warning("CCollisionSphere::Restore(): No save point.");
+	}
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/collision/bball_collision_sphere.h b/engines/scumm/he/basketball/collision/bball_collision_sphere.h
new file mode 100644
index 00000000000..8d8cccf4c9e
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_sphere.h
@@ -0,0 +1,108 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_SPHERE_H
+#define SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_SPHERE_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+
+namespace Scumm {
+
+const int BOUNCE_SPEED_LIMIT = 20;      // If the z-direction speed of the ball falls below this limit during a bounce, then the ball will begin to roll.
+const int ROLL_SPEED_LIMIT = 20;        // If the xy-direction speed of the ball falls below this limit during a roll, then the ball will stop.
+const int ROLL_SLOWDOWN_FREQUENCY = 10; // The ball slows down every x frames while rolling.
+
+class CCollisionSphere : public ICollisionObject, public U32Sphere {
+public:
+	CCollisionSphere() : ICollisionObject(kSphere),
+						 m_rollingCount(0),
+						 _positionSaved(false) {}
+
+	~CCollisionSphere() {}
+
+	using ICollisionObject::getObjectDistance;
+	float getObjectDistance(const CCollisionBox &targetObject) const override;
+	float getObjectDistance(const CCollisionCylinder &targetObject) const override;
+
+	using ICollisionObject::testObjectIntersection;
+	bool testObjectIntersection(const CCollisionBox &targetObject, U32Distance3D *pDistance) const override;
+	bool testObjectIntersection(const CCollisionCylinder &targetObject, U32Distance3D *pDistance) const override;
+
+	using ICollisionObject::validateCollision;
+	bool validateCollision(const CCollisionBox &targetObject, U32Distance3D *pDistance) override;
+	bool validateCollision(const CCollisionCylinder &targetObject, U32Distance3D *pDistance) override;
+
+	using ICollisionObject::backOutOfObject;
+	bool backOutOfObject(const CCollisionBox &targetObject, U32Distance3D *pDistance, float *pTimeUsed) override;
+	bool backOutOfObject(const CCollisionCylinder &targetObject, U32Distance3D *pDistance, float *pTimeUsed) override;
+
+	using ICollisionObject::nudgeObject;
+	bool nudgeObject(const CCollisionBox &targetObject, U32Distance3D *pDistance, float *pTimeUsed) override;
+	bool nudgeObject(const CCollisionCylinder &targetObject, U32Distance3D *pDistance, float *pTimeUsed) override;
+
+	void handleCollisions(CCollisionObjectVector *pCollisionVector, float *pTimeUsed, bool advanceObject) override;
+
+	using ICollisionObject::isOnObject;
+	bool isOnObject(const CCollisionBox &targetObject, const U32Distance3D &distance) const override;
+	bool isOnObject(const CCollisionCylinder &targetObject, const U32Distance3D &distance) const override;
+
+	U32BoundingBox getBoundingBox() const override;
+	U32BoundingBox getBigBoundingBox() const override;
+
+	void save() override;
+	void restore() override;
+
+	int m_rollingCount;
+
+
+protected:
+	float getDimensionDistance(const CCollisionBox &targetObject, EDimension dimension) const;
+	float getDimensionDistance(const CCollisionCylinder &targetObject, EDimension dimension) const;
+
+private:
+	bool _positionSaved;
+	U32FltPoint3D _safetyPoint;
+	U32FltVector3D _safetyVelocity;
+
+	bool backStraightOutOfObject(const ICollisionObject &targetObject, U32Distance3D *pDistance, float *pTimeUsed);
+
+	using ICollisionObject::defineReflectionPlane;
+	void defineReflectionPlane(const CCollisionBox &targetObject, const U32Distance3D &distance, U32Plane *collisionPlane) const override;
+	void defineReflectionPlane(const CCollisionCylinder &targetObject, const U32Distance3D &distance, U32Plane *collisionPlane) const override;
+
+	void ReboundOffPlane(const U32Plane &collisionPlane, bool isOnPlane);
+
+	using ICollisionObject::getPenetrationTime;
+	float getPenetrationTime(const CCollisionBox &targetObject, const U32Distance3D &distance, EDimension dimension) const override;
+	float getPenetrationTime(const CCollisionCylinder &targetObject, const U32Distance3D &distance, EDimension dimension) const override;
+
+	void increaseVelocity(float minSpeed);
+};
+
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_SPHERE_H
diff --git a/engines/scumm/he/basketball/collision/bball_collision_stack.cpp b/engines/scumm/he/basketball/collision/bball_collision_stack.cpp
new file mode 100644
index 00000000000..2893e0d4a0b
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_stack.cpp
@@ -0,0 +1,73 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+#include "scumm/he/basketball/collision/bball_collision_sphere.h"
+#include "scumm/he/basketball/collision/bball_collision_box.h"
+#include "scumm/he/basketball/collision/bball_collision_cylinder.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+#include "scumm/he/basketball/collision/bball_collision_node.h"
+#include "scumm/he/basketball/collision/bball_collision_tree.h"
+
+namespace Scumm {
+
+void CCollisionObjectStack::clear() {
+	while (!empty()) {
+		pop_back();
+	}
+}
+
+int CCollisionObjectVector::getMinPoint(EDimension dimension) const {
+	int min = 1000000;
+	for (size_type i = 0; i < size(); i++) {
+		int current = ((*this)[i])->getBoundingBox().minPoint[dimension];
+		if (current < min) {
+			min = current;
+		}
+	}
+	return min;
+}
+
+int CCollisionObjectVector::getMaxPoint(EDimension dimension) const {
+	int max = 0;
+	for (uint i = 0; i < size(); i++) {
+		int current = ((*this)[i])->getBoundingBox().minPoint[dimension];
+		if (current > max) {
+			max = current;
+		}
+	}
+	return max;
+}
+
+bool CCollisionObjectVector::contains(const ICollisionObject &object) const {
+	for (CCollisionObjectVector::const_iterator objectIt = begin(); objectIt != end(); ++objectIt) {
+		if (object == **objectIt) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/collision/bball_collision_stack.h b/engines/scumm/he/basketball/collision/bball_collision_stack.h
new file mode 100644
index 00000000000..b2c21f04950
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_stack.h
@@ -0,0 +1,51 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_STACK_H
+#define SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_STACK_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+
+#include "common/array.h"
+
+namespace Scumm {
+
+class ICollisionObject;
+
+class CCollisionObjectStack : public Common::Array<const ICollisionObject *> {
+public:
+	void clear();
+};
+
+class CCollisionObjectVector : public Common::Array<const ICollisionObject *> {
+public:
+	int getMinPoint(EDimension dimension) const;
+	int getMaxPoint(EDimension dimension) const;
+	bool contains(const ICollisionObject &object) const;
+};
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_STACK_H
diff --git a/engines/scumm/he/basketball/collision/bball_collision_support_obj.h b/engines/scumm/he/basketball/collision/bball_collision_support_obj.h
new file mode 100644
index 00000000000..4feada55d38
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_support_obj.h
@@ -0,0 +1,716 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_SUPPORT_OBJ_H
+#define SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_SUPPORT_OBJ_H
+
+#ifdef ENABLE_HE
+
+#include "common/std/algorithm.h"
+
+namespace Scumm {
+
+#define BBALL_M_PI         3.14159265358979
+#define COPY_SIGN(x, y)    ((x < 0) == (y < 0)) ? x : -x
+
+#define MIN_GREATER_THAN_ZERO(a, b) ((a < 0) ? ((b < 0) ? ((a > b) ? a : b) : b) : ((b < 0) ? a : ((a > b) ? b : a)))
+
+enum EDimension {
+	X_INDEX = 0,
+	Y_INDEX = 1,
+	Z_INDEX = 2
+};
+
+enum ERevDirection {
+	kClockwise = -1,
+	kNone = 0,
+	kCounterClockwise = 1
+};
+
+template<class Type>
+class U32Construct2D {
+
+public:
+	Type x;
+	Type y;
+
+	U32Construct2D() : x(0), y(0) {}
+	U32Construct2D(Type xIn, Type yIn) : x(xIn), y(yIn) {}
+
+	bool operator==(const U32Construct2D<Type> &other) const {
+		return ((other.x == x) && (other.y == y));
+	}
+
+	U32Construct2D<Type> operator-(const U32Construct2D<Type> &other) const {
+		U32Construct2D<Type> newConstruct;
+
+		newConstruct.x = x - other.x;
+		newConstruct.y = y - other.y;
+
+		return newConstruct;
+	}
+
+	U32Construct2D<Type> operator+(const U32Construct2D<Type> &other) const {
+		U32Construct2D<Type> newConstruct;
+
+		newConstruct.x = x + other.x;
+		newConstruct.y = y + other.y;
+
+		return newConstruct;
+	}
+
+	Type operator*(const U32Construct2D<Type> &multiplier) const {
+		return (x * multiplier.x + y * multiplier.y);
+	}
+
+	Type &operator[](EDimension dimension) {
+		assert(dimension <= Y_INDEX);
+		return *(&x + dimension);
+	}
+
+	const Type &operator[](EDimension dimension) const {
+		assert(dimension <= Y_INDEX);
+		return *(&x + dimension);
+	}
+
+	Type magnitude(void) const {
+		return (Type)(sqrt(x * x + y * y));
+	}
+};
+
+template<class Type>
+class U32Construct3D {
+
+public:
+	Type x;
+	Type y;
+	Type z;
+
+	U32Construct3D() : x(0), y(0), z(0) {}
+	U32Construct3D(Type xIn, Type yIn, Type zIn) : x(xIn), y(yIn), z(zIn) {}
+
+	Type magnitude(void) const {
+		return (Type)(sqrt(x * x + y * y + z * z));
+	}
+
+	Type xyMagnitude(void) const {
+		return (Type)(sqrt(x * x + y * y));
+	}
+
+	bool operator==(const U32Construct3D<Type> &other) const {
+		return ((other.x == x) && (other.y == y) && (other.z == z));
+	}
+
+	bool operator!=(const U32Construct2D<Type> &other) const {
+		return ((other.x != x) || (other.y != y));
+	}
+
+	Type &operator[](EDimension dimension) {
+		assert(dimension <= Z_INDEX);
+		return *(&x + dimension);
+	}
+
+	const Type &operator[](EDimension dimension) const {
+		assert(dimension <= Z_INDEX);
+		return *(&x + dimension);
+	}
+
+	U32Construct3D<Type> operator+(const U32Construct3D<Type> &other) const {
+		U32Construct3D<Type> newConstruct;
+
+		newConstruct.x = x + other.x;
+		newConstruct.y = y + other.y;
+		newConstruct.z = z + other.z;
+
+		return newConstruct;
+	}
+
+	Type operator*(const U32Construct3D<Type> &multiplier) const {
+		return (x * multiplier.x + y * multiplier.y + z * multiplier.z);
+	}
+
+	friend U32Construct3D<Type> operator*(const Type multiplier1[4][4], U32Construct3D<Type> multiplier2) {
+		U32Construct3D<Type> newPoint;
+		Type h = 0;
+		int column, row;
+
+		for (row = X_INDEX; row <= Z_INDEX; ++row) {
+			for (column = X_INDEX; column <= Z_INDEX; ++column) {
+				newPoint[(EDimension)row] += multiplier1[row][column] * multiplier2[(EDimension)column];
+			}
+
+			newPoint[(EDimension)row] += multiplier1[row][column];
+		}
+
+		for (column = X_INDEX; column <= Z_INDEX; ++column) {
+			h += multiplier1[row][column] * multiplier2[(EDimension)column];
+		}
+
+		h += multiplier1[row][column];
+
+		(h == 0) ? 0 : newPoint.x /= h;
+		(h == 0) ? 0 : newPoint.y /= h;
+		(h == 0) ? 0 : newPoint.z /= h;
+
+		return newPoint;
+	}
+};
+
+template<class Type>
+class U32Point3D;
+
+template<class Type>
+class U32Point2D : public U32Construct2D<Type> {
+public:
+	U32Point2D() : U32Construct2D<Type>() {}
+	U32Point2D(Type xx, Type yy) : U32Construct2D<Type>(xx, yy) {}
+	U32Point2D(const U32Construct2D<Type> &other) : U32Construct2D<Type>(other.x, other.y) {}
+	U32Point2D(const U32Construct3D<Type> &other) : U32Construct2D<Type>(other.x, other.y) {} // For 3D
+
+	U32Point2D<Type> operator=(const U32Construct3D<Type> &other) {
+		this->x = other.x;
+		this->y = other.y;
+		return *this;
+	}
+
+	U32Construct2D<Type> operator-(const U32Construct3D<Type> &other) const {
+		U32Construct2D<Type> newPoint;
+
+		newPoint.x = this->x - other.x;
+		newPoint.y = this->y - other.y;
+
+		return newPoint;
+	}
+
+	U32Construct2D<Type> operator-(const U32Construct2D<Type> &other) const {
+		U32Construct2D<Type> newPoint;
+
+		newPoint.x = this->x - other.x;
+		newPoint.y = this->y - other.y;
+
+		return newPoint;
+	}
+};
+
+typedef U32Point2D<int> U32IntPoint2D;
+typedef U32Point2D<float> U32FltPoint2D;
+
+template<class Type>
+class U32Point3D : public U32Construct3D<Type> {
+public:
+	U32Point3D() : U32Construct3D<Type>() {}
+	U32Point3D(Type xIn, Type yIn, Type zIn) : U32Construct3D<Type>(xIn, yIn, zIn) {}
+	U32Point3D(const U32Construct3D<Type> &other) : U32Construct3D<Type>(other.x, other.y, other.z) {}
+
+	bool operator==(const U32Construct2D<Type> &other) const {
+		return ((other.x == this->x) && (other.y == this->y));
+	}
+
+	bool operator==(const U32Construct3D<Type> &other) const {
+		return ((other.x == this->x) && (other.y == this->y) && (other.z == this->z));
+	}
+
+	U32Construct3D<Type> operator-(const U32Construct2D<Type> &other) const {
+		U32Construct3D<Type> newPoint;
+
+		newPoint.x = this->x - other.x;
+		newPoint.y = this->y - other.y;
+		newPoint.z = this->z;
+
+		return newPoint;
+	}
+
+	U32Construct3D<Type> operator-(const U32Construct3D<Type> &other) const {
+		U32Construct3D<Type> newPoint;
+
+		newPoint.x = this->x - other.x;
+		newPoint.y = this->y - other.y;
+		newPoint.z = this->z - other.z;
+
+		return newPoint;
+	}
+};
+
+typedef U32Point3D<int> U32IntPoint3D;
+typedef U32Point3D<float> U32FltPoint3D;
+
+template<class Type>
+class U32Vector2D : public U32Construct2D<Type> {
+public:
+	U32Vector2D() : U32Construct2D<Type>() {}
+	U32Vector2D(Type xx, Type yy) : U32Construct2D<Type>(xx, yy) {}
+	U32Vector2D(const U32Construct2D<Type> &other) : U32Construct2D<Type>(other.x, other.y) {}
+	U32Vector2D(const U32Construct3D<Type> &other) : U32Construct2D<Type>(other.x, other.y) {}
+
+	U32Vector2D<Type> normalize() const {
+		Type magnitude = this->magnitude();
+		assert(magnitude > 0);
+
+		U32Vector2D<Type> newVector;
+		newVector.x = this->x / magnitude;
+		newVector.y = this->y / magnitude;
+
+		return newVector;
+	}
+
+	U32Vector2D<Type> operator*(Type multiplier) const {
+		U32Vector2D<Type> newVector;
+
+		newVector.x = multiplier * this->x;
+		newVector.y = multiplier * this->y;
+
+		return newVector;
+	}
+
+	Type operator*(const U32Vector2D<Type> &multiplier) const {
+		return (this->x * multiplier.x + this->y * multiplier.y);
+	}
+
+	const U32Vector2D<Type> &operator*=(Type multiplier) {
+		*this = *this * multiplier;
+		return *this;
+	}
+
+	const U32Vector2D<Type> &rotate(ERevDirection whichDirection, double radians) {
+		U32Point2D<Type> newPoint;
+
+		if (whichDirection == kCounterClockwise) {
+			newPoint.x = this->x * cos(radians) - this->y * sin(radians);
+			newPoint.y = this->x * sin(radians) + this->y * cos(radians);
+		} else {
+			newPoint.x = this->x * cos(radians) + this->y * sin(radians);
+			newPoint.y = this->y * cos(radians) - this->x * sin(radians);
+		}
+
+		this->x = newPoint.x;
+		this->y = newPoint.y;
+		return *this;
+	}
+
+	U32Construct3D<Type> cross(const U32Vector2D<Type> &otherVector) const {
+		U32Construct3D<Type> newVector;
+
+		newVector.x = 0;
+		newVector.y = 0;
+		newVector.z = this->x * otherVector.y - this->y * otherVector.x;
+
+		return newVector;
+	}
+
+	U32Vector2D<Type> transpose(void) const {
+		U32Vector2D<Type> newVector;
+		newVector.x = -this->y;
+		newVector.y = this->x;
+
+		return newVector;
+	}
+
+	Type distance2(const U32Vector2D<Type> &otherVector) const {
+		return ((this->x - otherVector.x) * (this->x - otherVector.x) +
+				(this->y - otherVector.y) * (this->y - otherVector.y));
+	}
+
+	Type distance(const U32Vector2D<Type> &otherVector) const {
+		return (Type)(sqrt((double)(distance2(otherVector))));
+	}
+
+	ERevDirection getRevDirection(const U32Vector2D<Type> &otherVector) const {
+		U32Construct3D<Type> vector3 = this->cross(otherVector);
+
+		if (vector3.z > 0) {
+			return kCounterClockwise;
+		} else if (vector3.z < 0) {
+			return kClockwise;
+		} else {
+			return kNone;
+		}
+	}
+
+	// Project this vector onto otherVector, and return the resulting vector's magnitude.
+	Type projectScalar(const U32Vector2D<Type> &otherVector) const {
+		Type projectionScalar;
+
+		if (otherVector.magnitude() == 0) {
+			projectionScalar = 0;
+		} else {
+			float otherMagnitude = otherVector.magnitude();
+			projectionScalar = (*this * otherVector) / otherMagnitude;
+		}
+
+		return projectionScalar;
+	}
+};
+
+typedef U32Vector2D<int> U32IntVector2D;
+typedef U32Vector2D<float> U32FltVector2D;
+
+template<class Type>
+class U32Vector3D : public U32Construct3D<Type> {
+public:
+	U32Vector3D() : U32Construct3D<Type>() {}
+	U32Vector3D(Type xx, Type yy, Type zz) : U32Construct3D<Type>(xx, yy, zz) {}
+	U32Vector3D<Type>(const U32Construct2D<Type> &other) : U32Construct3D<Type>(other.x, other.y, 0) {}
+	U32Vector3D<Type>(const U32Construct3D<Type> &other) : U32Construct3D<Type>(other.x, other.y, other.z) {}
+
+	U32Vector3D<Type> operator-(const U32Construct2D<Type> &other) const {
+		U32Vector3D<Type> newVector;
+
+		newVector.x = this->x - other.x;
+		newVector.y = this->y - other.y;
+		newVector.z = this->z;
+
+		return newVector;
+	}
+
+	U32Vector3D<Type> operator-(const U32Construct3D<Type> &other) const {
+		U32Vector3D<Type> newVector;
+
+		newVector.x = this->x - other.x;
+		newVector.y = this->y - other.y;
+		newVector.z = this->z - other.z;
+
+		return newVector;
+	}
+
+	Type operator*(const U32Construct3D<Type> &multiplier) const {
+		return (this->x * multiplier.x + this->y * multiplier.y + this->z * multiplier.z);
+	}
+
+	U32Vector3D<Type> &operator+=(const U32Construct3D<Type> &adder) const {
+		*this = *this + adder;
+		return *this;
+	}
+
+	U32Vector3D<Type> &operator-=(const U32Construct3D<Type> &subtractor) {
+		*this = *this - subtractor;
+		return *this;
+	}
+
+	U32Vector3D<Type> operator*(Type multiplier) const {
+		U32Vector3D<Type> newVector;
+
+		newVector.x = multiplier * this->x;
+		newVector.y = multiplier * this->y;
+		newVector.z = multiplier * this->z;
+
+		return newVector;
+	}
+
+	const U32Vector3D<Type> &operator*=(Type multiplier) {
+		*this = *this * multiplier;
+		return *this;
+	}
+
+	U32Vector3D<Type> operator/(Type divider) const {
+		U32Vector3D<Type> newVector;
+
+		newVector.x = (divider == 0) ? 0 : this->x / divider;
+		newVector.y = (divider == 0) ? 0 : this->y / divider;
+		newVector.z = (divider == 0) ? 0 : this->z / divider;
+
+		return newVector;
+	}
+
+	const U32Vector3D<Type> &operator/=(Type multiplier) {
+		*this = *this / multiplier;
+		return *this;
+	}
+
+	U32Vector3D<Type> cross(const U32Vector3D<Type> &otherVector) const {
+		U32Vector3D<Type> newVector;
+
+		newVector.x = this->y * otherVector.z - this->z * otherVector.y;
+		newVector.y = this->z * otherVector.x - this->x * otherVector.z;
+		newVector.z = this->x * otherVector.y - this->y * otherVector.x;
+
+		return newVector;
+	}
+
+	bool operator>(const U32Vector3D<Type> &otherVector) const {
+		return (this->magnitude() > otherVector.magnitude());
+	}
+
+	bool operator<(const U32Vector3D<Type> &otherVector) const {
+		return (this->magnitude() < otherVector.magnitude());
+	}
+
+	// Project this vector onto otherVector, and return the resulting vector
+	U32Vector3D<Type> projectVector(const U32Vector3D<Type> &otherVector) const {
+		Type projectionScalar;
+
+		if (otherVector.magnitude() == 0)
+			projectionScalar = 0;
+		else
+			projectionScalar = (*this * otherVector) / (otherVector.magnitude() * otherVector.magnitude());
+
+		U32Vector3D<Type> newVector = otherVector * projectionScalar;
+		return newVector;
+	}
+
+	// Project this vector onto otherVector, and return the resulting vector's magnitude
+	Type projectScalar(const U32Vector3D<Type> &otherVector) const {
+		Type projectionScalar;
+
+		if (otherVector.magnitude() == 0) {
+			projectionScalar = 0;
+		} else {
+			float otherMagnitude = otherVector.magnitude();
+			projectionScalar = (*this * otherVector) / otherMagnitude;
+		}
+
+		return projectionScalar;
+	}
+
+	U32Vector3D<Type> normalize() const {
+		Type magnitude = this->magnitude();
+		assert(magnitude > 0);
+
+		U32Vector3D<Type> newVector;
+
+		if (magnitude != 0) {
+			newVector.x = this->x / magnitude;
+			newVector.y = this->y / magnitude;
+			newVector.z = this->z / magnitude;
+		}
+
+		return newVector;
+	}
+};
+
+typedef U32Vector3D<int> U32IntVector3D;
+typedef U32Vector3D<float> U32FltVector3D;
+
+class U32Distance3D : public U32Construct3D<float> {
+public:
+	U32Distance3D() : U32Construct3D<float>() {}
+	U32Distance3D(const U32Construct2D<float> &other) : U32Construct3D<float>(other.x, other.y, 0) {}
+	U32Distance3D(const U32Construct3D<float> &other) : U32Construct3D<float>(other.x, other.y, other.z) {}
+
+	U32Distance3D operator-(int subtractor) const {
+		U32Distance3D newDistance;
+
+		if (magnitude() != 0) {
+			newDistance.x = x - ((x * subtractor) / magnitude());
+			newDistance.y = y - ((y * subtractor) / magnitude());
+			newDistance.z = z - ((z * subtractor) / magnitude());
+		}
+
+		return newDistance;
+	}
+
+	U32Distance3D operator-=(int subtractor) {
+		*this = *this - subtractor;
+		return *this;
+	}
+};
+
+struct U32Circle {
+
+	U32FltPoint2D center;
+	float radius;
+};
+
+struct U32Sphere {
+
+	U32FltPoint3D center;
+	float radius;
+};
+
+struct U32Cylinder : public U32Sphere {
+	float height;
+};
+
+struct U32BoundingBox {
+
+	U32IntPoint3D minPoint;
+	U32IntPoint3D maxPoint;
+
+	void SetMinPoint(int x, int y) {
+		minPoint.x = x;
+		minPoint.y = y;
+	}
+
+	void SetMaxPoint(int x, int y) {
+		maxPoint.x = x;
+		maxPoint.y = y;
+	}
+
+	const U32IntPoint3D &getMinPoint(void) const {
+		return minPoint;
+	}
+
+	const U32IntPoint3D &getMaxPoint(void) const {
+		return maxPoint;
+	}
+
+	bool Intersect(const U32BoundingBox &targetBox) const {
+		return ((((minPoint.x <= targetBox.minPoint.x) &&
+				  (maxPoint.x >= targetBox.minPoint.x)) ||
+
+				 ((minPoint.x <= targetBox.maxPoint.x) &&
+				  (maxPoint.x >= targetBox.maxPoint.x)) ||
+
+				 ((targetBox.minPoint.x <= minPoint.x) &&
+				  (targetBox.maxPoint.x >= minPoint.x)) ||
+
+				 ((targetBox.minPoint.x <= maxPoint.x) &&
+				  (targetBox.maxPoint.x >= maxPoint.x)))
+
+				&&
+
+				(((minPoint.y <= targetBox.minPoint.y) &&
+				  (maxPoint.y >= targetBox.minPoint.y)) ||
+
+				 ((minPoint.y <= targetBox.maxPoint.y) &&
+				  (maxPoint.y >= targetBox.maxPoint.y)) ||
+
+				 ((targetBox.minPoint.y <= minPoint.y) &&
+				  (targetBox.maxPoint.y >= minPoint.y)) ||
+
+				 ((targetBox.minPoint.y <= maxPoint.y) &&
+				  (targetBox.maxPoint.y >= maxPoint.y))));
+	}
+
+	const U32IntPoint3D &operator[](int point) const {
+		assert(point <= 1);
+		return ((point == 0) ? minPoint : maxPoint);
+	}
+
+	U32IntPoint3D &operator[](int point) {
+		assert(point <= 1);
+		return ((point == 0) ? minPoint : maxPoint);
+	}
+
+	bool isPointWithin(U32FltPoint2D point) const {
+		return ((minPoint.x <= point.x) && (point.x <= maxPoint.x) &&
+				(minPoint.y <= point.y) && (point.y <= maxPoint.y));
+	}
+};
+
+struct U32Plane {
+	U32FltPoint3D point;
+	U32FltVector3D normal;
+	float collisionEfficiency;
+	float friction;
+
+	// Average this plane with another plane
+	U32Plane &Average(const U32Plane &otherPlane) {
+		point.x = (point.x + otherPlane.point.x) / 2;
+		point.y = (point.y + otherPlane.point.y) / 2;
+		point.z = (point.z + otherPlane.point.z) / 2;
+
+		normal.x = (normal.x + otherPlane.normal.x) / 2;
+		normal.y = (normal.y + otherPlane.normal.y) / 2;
+		normal.z = (normal.z + otherPlane.normal.z) / 2;
+
+		float normalMag = normal.magnitude();
+		normal.x = (normalMag == 0) ? 0 : normal.x / normalMag;
+		normal.y = (normalMag == 0) ? 0 : normal.y / normalMag;
+		normal.z = (normalMag == 0) ? 0 : normal.z / normalMag;
+
+		collisionEfficiency = (collisionEfficiency + otherPlane.collisionEfficiency) / 2;
+		friction = (friction + otherPlane.friction) / 2;
+
+		return *this;
+	}
+
+	void clear() {
+		point = U32FltPoint3D();
+		normal = U32FltVector3D();
+		collisionEfficiency = 0.0F;
+		friction = 0.0F;
+	}
+};
+
+class U32Ray2D {
+public:
+	U32FltPoint2D _origin;
+	U32FltVector2D _direction;
+
+	bool intersection(const U32Ray2D &otherRay, U32FltPoint2D *intersection) const {
+		float numerator = ((otherRay._origin - _origin) * otherRay._direction.transpose());
+		float denominator = _direction * otherRay._direction.transpose();
+
+		if ((denominator == 0) ||
+			(denominator < 0) != (numerator < 0)) {
+			return false;
+		} else {
+			float s = numerator / denominator;
+			assert(s >= 0);
+
+			intersection->x = _origin.x + (_direction.x * s);
+			intersection->y = _origin.y + (_direction.y * s);
+			return true;
+		}
+	}
+
+	bool nearIntersection(const U32Circle &circle, U32FltPoint2D *intersection) const {
+		U32FltVector2D coVector = _origin - circle.center;
+		double b = _direction.normalize() * coVector;
+		double c = (coVector * coVector) - (circle.radius * circle.radius);
+		double d = (b * b) - c;
+
+		if (d < 0) {
+			return false;
+		} else {
+			double t = -b - sqrt(d);
+
+			if (t < 0) {
+				return false;
+			} else {
+				*intersection = _origin + (_direction.normalize() * t);
+				return true;
+			}
+		}
+	}
+
+	bool farIntersection(const U32Circle &circle, U32FltPoint2D *intersection) const {
+		U32FltVector2D coVector = _origin - circle.center;
+		double b = _direction.normalize() * coVector;
+		double c = (coVector * coVector) - (circle.radius * circle.radius);
+		double d = (b * b) - c;
+
+		if (d < 0) {
+			return false;
+		} else {
+			double t = -b + sqrt(d);
+
+			if (t < 0) {
+				return false;
+			} else {
+				*intersection = _origin + (_direction.normalize() * t);
+				return true;
+			}
+		}
+	}
+};
+
+class U32Ray3D {
+public:
+	U32FltPoint3D _origin;
+	U32FltVector3D _direction;
+};
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_SUPPORT_OBJ_H
diff --git a/engines/scumm/he/basketball/collision/bball_collision_tree.cpp b/engines/scumm/he/basketball/collision/bball_collision_tree.cpp
new file mode 100644
index 00000000000..405e8a90060
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_tree.cpp
@@ -0,0 +1,128 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+#include "scumm/he/basketball/collision/bball_collision_sphere.h"
+#include "scumm/he/basketball/collision/bball_collision_box.h"
+#include "scumm/he/basketball/collision/bball_collision_cylinder.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+#include "scumm/he/basketball/collision/bball_collision_node.h"
+#include "scumm/he/basketball/collision/bball_collision_tree.h"
+
+namespace Scumm {
+
+CCollisionObjectTree::CCollisionObjectTree(const CCollisionObjectVector &inputObjects) : _maxHeight(INIT_MAX_HEIGHT),
+																						 _maxObjectsInNode(INIT_MAX_OBJECTS),
+																						 _root(nullptr),
+																						 _errorFlag(false) {
+	initialize(inputObjects);
+}
+
+CCollisionObjectTree::~CCollisionObjectTree() {
+	if (_root) {
+		delete _root;
+		_root = nullptr;
+	}
+}
+
+void CCollisionObjectTree::initialize(const CCollisionObjectVector &inputObjects) {
+	if (_root) {
+		delete _root;
+		_root = nullptr;
+	}
+
+	U32BoundingBox buildRange;
+	buildRange.SetMinPoint(inputObjects.getMinPoint(X_INDEX), inputObjects.getMinPoint(Y_INDEX));
+	buildRange.SetMaxPoint(inputObjects.getMaxPoint(X_INDEX), inputObjects.getMaxPoint(Y_INDEX));
+	_root = buildSelectionStructure(inputObjects, 0, buildRange);
+}
+
+void CCollisionObjectTree::selectObjectsInBound(const U32BoundingBox &bound, CCollisionObjectVector *targetVector) {
+	_root->searchTree(bound, targetVector);
+
+	if (targetVector->size() <= 0) {
+		warning("CCollisionObjectTree::selectObjectsInBound(): (targetVector->size() <= 0) Something went really wrong with a collision, ignore and U32 will attempt to correct.");
+		_errorFlag = true;
+		return;
+	}
+
+	// Here we make sure that we don't have any duplicate objects in our target stack...
+	Std::sort(targetVector->begin(), targetVector->end());
+
+	CCollisionObjectVector::iterator newEnd = Std::unique(targetVector->begin(), targetVector->end());
+	targetVector->erase(newEnd, targetVector->end());
+}
+
+bool CCollisionObjectTree::checkErrors() {
+	if (_errorFlag) {
+		_errorFlag = false;
+		return true;
+	} else {
+		return false;
+	}
+}
+
+CCollisionNode *CCollisionObjectTree::buildSelectionStructure(const CCollisionObjectVector &inputObjects, int currentLevel, const U32BoundingBox &nodeRange) {
+	// Create a new node, containing all of the input points...
+	CCollisionNode *newNode = new CCollisionNode(inputObjects);
+	newNode->_quadrant = nodeRange;
+
+	// See if we are at the final level, or if we have reached our target occupancy...
+	if ((currentLevel == _maxHeight) ||
+		(inputObjects.size() <= _maxObjectsInNode)) {
+		newNode->_isExternal = true;
+	} else {
+		newNode->_isExternal = false;
+
+		// Otherwise, break the inputPoints into 4 new point lists...
+		CCollisionObjectVector newList[NUM_CHILDREN_NODES];
+		U32BoundingBox newRange[NUM_CHILDREN_NODES];
+		int childID;
+
+		for (childID = 0; childID < NUM_CHILDREN_NODES; childID++) {
+			newRange[childID] = CCollisionNode::getChildQuadrant(nodeRange, (EChildID)childID);
+		}
+
+		// Go through each point in the list...
+		for (size_t i = 0; i < inputObjects.size(); i++) {
+			const ICollisionObject *currentObject = inputObjects[i];
+
+			// Figure out which child each point belongs to...
+			for (childID = 0; childID < NUM_CHILDREN_NODES; ++childID) {
+				if (newRange[childID].Intersect(currentObject->getBoundingBox())) {
+					newList[childID].push_back(currentObject);
+				}
+			}
+		}
+
+		// Make recursive calls...
+		for (childID = 0; childID < NUM_CHILDREN_NODES; ++childID) {
+			newNode->_child[childID] = buildSelectionStructure(newList[childID],
+															   currentLevel + 1,
+															   newRange[childID]);
+		}
+	}
+
+	return newNode;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/collision/bball_collision_tree.h b/engines/scumm/he/basketball/collision/bball_collision_tree.h
new file mode 100644
index 00000000000..7fdcc85ddca
--- /dev/null
+++ b/engines/scumm/he/basketball/collision/bball_collision_tree.h
@@ -0,0 +1,64 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_TREE_H
+#define SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_TREE_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+#include "scumm/he/basketball/collision/bball_collision_node.h"
+
+namespace Scumm {
+
+#define INIT_MAX_HEIGHT  10
+#define INIT_MAX_OBJECTS 5
+
+class CCollisionObjectTree {
+public:
+	CCollisionObjectTree() : _maxHeight(INIT_MAX_HEIGHT),
+							 _maxObjectsInNode(INIT_MAX_OBJECTS),
+							 _root(nullptr),
+							 _errorFlag(false) {}
+
+	CCollisionObjectTree(const CCollisionObjectVector &inputObjects);
+	~CCollisionObjectTree();
+
+	void initialize(const CCollisionObjectVector &inputObjects);
+	void selectObjectsInBound(const U32BoundingBox &bound, CCollisionObjectVector *targetVector);
+
+	bool checkErrors();
+
+private:
+	CCollisionNode *buildSelectionStructure(const CCollisionObjectVector &inputObjects, int currentLevel, const U32BoundingBox &nodeRange);
+
+	int _maxHeight;
+	size_t _maxObjectsInNode;
+	CCollisionNode *_root;
+	bool _errorFlag;
+};
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_COLLISION_BBALL_COLLISION_TREE_H
diff --git a/engines/scumm/he/basketball/court.cpp b/engines/scumm/he/basketball/court.cpp
new file mode 100644
index 00000000000..30a5195c4c6
--- /dev/null
+++ b/engines/scumm/he/basketball/court.cpp
@@ -0,0 +1,74 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+
+#include "scumm/he/basketball/basketball.h"
+#include "scumm/he/basketball/court.h"
+
+namespace Scumm {
+
+int CBBallCourt::getPlayerIndex(int playerID) {
+	assert((FIRST_PLAYER <= playerID) && (playerID <= LAST_PLAYER));
+
+	Common::Array<CCollisionPlayer> *playerList = getPlayerListPtr(playerID);
+	for (size_t i = 0; i < playerList->size(); i++) {
+		if ((*playerList)[i]._objectID == playerID) {
+			return i;
+		}
+	}
+
+	warning("CBBallCourt::getPlayerIndex(): Tried to find a player in the player list that was not there.");
+	return 0;
+}
+
+CCollisionPlayer *CBBallCourt::getPlayerPtr(int playerID) {
+	assert((FIRST_PLAYER <= playerID) && (playerID <= LAST_PLAYER));
+
+	Common::Array<CCollisionPlayer> *playerList = getPlayerListPtr(playerID);
+
+	size_t listSize = playerList->size();
+	for (size_t i = 0; i < listSize; i++) {
+		if ((*playerList)[i]._objectID == playerID) {
+			return &((*playerList)[i]);
+		}
+	}
+
+	warning("CBBallCourt::getPlayerPtr(): Tried to find a player in the player list that was not there.");
+	return nullptr;
+}
+
+CCollisionBasketball *CBBallCourt::getBallPtr(int ballID) {
+	CCollisionBasketball *sourceBall;
+
+	if (ballID == _basketBall._objectID) {
+		sourceBall = &_basketBall;
+	} else if (ballID == _virtualBall._objectID) {
+		sourceBall = &_virtualBall;
+	} else {
+		warning("CBBallCourt::getBallPtr(): Invalid ball ID passed to u32_userDetectBallCollision.");
+		sourceBall = &_basketBall;
+	}
+
+	return sourceBall;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/court.h b/engines/scumm/he/basketball/court.h
new file mode 100644
index 00000000000..5f19cbfa471
--- /dev/null
+++ b/engines/scumm/he/basketball/court.h
@@ -0,0 +1,112 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_COURT_H
+#define SCUMM_HE_BASKETBALL_COURT_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/intern_he.h"
+
+#include "scumm/he/basketball/collision/bball_collision_object.h"
+#include "scumm/he/basketball/collision/bball_collision_box.h"
+#include "scumm/he/basketball/collision/bball_collision_basketball.h"
+#include "scumm/he/basketball/collision/bball_collision_player.h"
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+#include "scumm/he/basketball/collision/bball_collision_stack.h"
+#include "scumm/he/basketball/collision/bball_collision_tree.h"
+
+#include "scumm/he/logic_he.h"
+
+namespace Scumm {
+
+// COURT DEFINES
+
+#define MAX_PLAYERS_ON_TEAM  5
+
+#define NO_PLAYER           -1
+#define FIRST_PLAYER         0
+#define AWAY_PLAYER_1        5
+#define LAST_PLAYER          9
+
+#define TEAM_HOME            0
+#define TEAM_AWAY            1
+
+#define SHOT_SPOT_RADIUS     13
+
+#define NO_COURT             0
+#define COURT_DOBBAGUCHI     1
+#define COURT_SMITH_BROS     2
+#define COURT_SANDY_FLATS    3
+#define COURT_QUEENS         4
+#define COURT_PLAYGROUND     5
+#define COURT_SCHEFFLER      6
+#define COURT_POLK           7
+#define COURT_MCMILLAN       8
+#define COURT_CROWN_HILL     9
+#define COURT_MEMORIAL       10
+#define COURT_TECH_STATE     11
+#define COURT_HUMONGOUS      12
+#define COURT_MOON           13
+#define COURT_BARN           14
+#define COURT_COUNT          15
+
+class CBBallCourt {
+public:
+	CBBallCourt() {
+		_objectCount = 0;
+		_backboardIndex[0] = 0;
+		_backboardIndex[1] = 0;
+	}
+
+	~CBBallCourt() {}
+
+	int getPlayerIndex(int playerID);
+	CCollisionPlayer *getPlayerPtr(int playerID);
+	CCollisionBasketball *getBallPtr(int ballID);
+
+	Common::Array<CCollisionPlayer> *getPlayerListPtr(int i) {
+		return (i < AWAY_PLAYER_1) ? &_homePlayerList : &_awayPlayerList;
+	}
+
+	Common::Array<CCollisionPlayer> *getOpponentListPtr(int i) {
+		return (i < AWAY_PLAYER_1) ? &_awayPlayerList : &_homePlayerList;
+	}
+
+	Common::String _name;
+
+	CCollisionBasketball _basketBall;
+	CCollisionBasketball _virtualBall;
+
+	Common::Array<CCollisionBox> _objectList;
+	CCollisionObjectTree _objectTree;
+	Common::Array<CCollisionPlayer> _homePlayerList;
+	Common::Array<CCollisionPlayer> _awayPlayerList;
+	int _objectCount;
+	int _backboardIndex[2];
+	U32Sphere _shotSpot[2];
+};
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_COURT_H
diff --git a/engines/scumm/he/basketball/cursor.cpp b/engines/scumm/he/basketball/cursor.cpp
new file mode 100644
index 00000000000..692e65f9674
--- /dev/null
+++ b/engines/scumm/he/basketball/cursor.cpp
@@ -0,0 +1,141 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/basketball/basketball.h"
+#include "scumm/he/logic_he.h"
+
+namespace Scumm {
+
+#define STICKY_CURSOR_VALUE 2
+
+static void checkCursorBounds(const Common::Point &oldCursorPos, Common::Point *cursorPos) {
+	Common::Rect windowRect(0, 0, 640, 480);
+
+	// Make sure the cursor wasn't pushed out of the window...
+	if ((cursorPos->x < (windowRect.left + 1)) && (oldCursorPos.x > windowRect.left)) {
+		cursorPos->x = (windowRect.left + 1);
+	}
+
+	if ((cursorPos->x > (windowRect.right - 1)) && (oldCursorPos.x < windowRect.right)) {
+		cursorPos->x = (windowRect.right - 1);
+	}
+
+	if ((cursorPos->y < (windowRect.top + 1)) && (oldCursorPos.y > windowRect.top)) {
+		cursorPos->y = (windowRect.top + 1);
+	}
+
+	if ((cursorPos->y > (windowRect.bottom - 1)) && (oldCursorPos.y < windowRect.bottom)) {
+		cursorPos->y = (windowRect.bottom - 1);
+	}
+}
+
+
+int LogicHEBasketball::u32_userUpdateCursorPos(int xScrollVal, int yScrollVal) {
+	int xChange = 0;
+	int yChange = 0;
+
+	Common::Point currentCursorPos = _vm->_mouse;
+
+	// Update that position due to any camera scrolling that happened this frame...
+	Common::Point newCursorPos;
+	newCursorPos.x = currentCursorPos.x - xScrollVal;
+	newCursorPos.y = currentCursorPos.y - yScrollVal;
+
+	// Make sure the cursor wasn't pushed out of the window...
+	checkCursorBounds(currentCursorPos, &newCursorPos);
+
+	// Update the cursor position...
+	_vm->_mouse = newCursorPos;
+
+	// Calculate how much the cursor actually changed this frame...
+	xChange = newCursorPos.x - currentCursorPos.x;
+	yChange = newCursorPos.y - currentCursorPos.y;
+
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, 1);
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, xChange);
+	writeScummVar(_vm1->VAR_U32_USER_VAR_C, yChange);
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userMakeCursorSticky(int lastCursorX, int lastCursorY) {
+	int success = 0;
+
+	int xChange = 0;
+	int yChange = 0;
+
+	Common::Point currentCursorPos = _vm->_mouse;
+
+	Common::Point newCursorPos;
+
+	// Update that position due to any camera scrolling that happened this frame...
+	newCursorPos.x = lastCursorX + ((currentCursorPos.x - lastCursorX) / STICKY_CURSOR_VALUE);
+	newCursorPos.y = lastCursorY + ((currentCursorPos.y - lastCursorY) / STICKY_CURSOR_VALUE);
+
+	// Make sure the cursor wasn't pushed out of the window...
+	checkCursorBounds(currentCursorPos, &newCursorPos);
+
+	// Update the cursor position...
+	_vm->_mouse = newCursorPos;
+
+	// Calculate how much the cursor actually changed this frame...
+	xChange = newCursorPos.x - currentCursorPos.x;
+	yChange = newCursorPos.y - currentCursorPos.y;
+
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, success);
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, xChange);
+	writeScummVar(_vm1->VAR_U32_USER_VAR_C, yChange);
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userCursorTrackMovingObject(int xChange, int yChange) {
+	Common::Point currentCursorPos = _vm->_mouse;
+	Common::Point newCursorPos;
+
+	// Update that position due to any camera scrolling that happened this frame...
+	newCursorPos.x = currentCursorPos.x + xChange;
+	newCursorPos.y = currentCursorPos.y + yChange;
+
+	// Make sure the cursor wasn't pushed out of the window...
+	checkCursorBounds(currentCursorPos, &newCursorPos);
+
+	// Update the cursor position...
+	_vm->_mouse = newCursorPos;
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, 1);
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, xChange);
+	writeScummVar(_vm1->VAR_U32_USER_VAR_C, yChange);
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userGetCursorPos() {
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_mouse.x);
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, _vm->_mouse.y);
+
+	return 1;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/geo_translation.cpp b/engines/scumm/he/basketball/geo_translation.cpp
new file mode 100644
index 00000000000..14b50434930
--- /dev/null
+++ b/engines/scumm/he/basketball/geo_translation.cpp
@@ -0,0 +1,253 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/logic_he.h"
+
+#include "scumm/he/basketball/geo_translations.h"
+
+namespace Scumm {
+
+int LogicHEBasketball::u32_userInitScreenTranslations() {
+	// Find the angle between the left and bottom baseline in the court image...
+	_courtAngle = atan(TRANSLATED_MAX_Y / (double)TRANSLATED_MAX_START_X);
+	assert(_courtAngle != 0);
+	assert(_courtAngle != BBALL_M_PI);
+
+	// The relationship between the location in the game world and the location in pixels
+	// from the bottom of the court can be described by the parametric equation:
+	// 
+	// y = [(x-c)^(1/2) - c^(1/2)] / a^(1/2)
+	//
+	// Here, x is worldPoint.y and y is pixelsFromBottom. This is where we calculate the constant
+	// coefficients a, b, and c.
+	_yTranslationA = (float)((((MAX_WORLD_Y / 2.0) * TRANSLATED_MAX_Y) - (MAX_WORLD_Y * (float)TRANSLATED_MID_Y)) / ((TRANSLATED_MID_Y * TRANSLATED_MID_Y * (float)TRANSLATED_MAX_Y) - (TRANSLATED_MAX_Y * TRANSLATED_MAX_Y * (float)TRANSLATED_MID_Y)));
+	assert(_yTranslationA != 0);
+
+	_yTranslationB = (MAX_WORLD_Y / (float)TRANSLATED_MAX_Y) - (_yTranslationA * TRANSLATED_MAX_Y);
+	_yTranslationC = (_yTranslationB * _yTranslationB) / (4.0 * _yTranslationA);
+	assert(_yTranslationC != 0);
+
+	// The relationship between the location in pixels from the bottom of the court and
+	// the location in world points can be described by the parametric equation:
+	// 
+	// y = ax^2 + bx +c
+	// 
+	// Here, x is pixelsFromBottom and y is worldPoint.y. This is where we calculate the constant
+	// coefficients a, b, and c.
+	_yRevTranslationA = (((MAX_WORLD_Y / (float)2) * TRANSLATED_MAX_Y) - (MAX_WORLD_Y * (float)TRANSLATED_MID_Y)) / ((TRANSLATED_MID_Y * TRANSLATED_MID_Y * (float)TRANSLATED_MAX_Y) - (TRANSLATED_MAX_Y * TRANSLATED_MAX_Y * (float)TRANSLATED_MID_Y));
+	_yRevTranslationB = (MAX_WORLD_Y / (float)TRANSLATED_MAX_Y) - (_yRevTranslationA * TRANSLATED_MAX_Y);
+	_yRevTranslationC = 0;
+
+	// As you move up the screen, the number of world points per screen pixel increases
+	// parametrically. Vice versa for moving down the screen. It may be desirable to
+	// have a top and bottom cutoff point. So there will be a point above the court where
+	// the point to pixel ratio stops increasing and a point below the court where the point
+	// to pixel ratio stops decreasing. Here, the corresponding world points are calculated.
+	_topScalingPointCutoff = _vm->_basketball->u32FloatToInt(
+		_yRevTranslationA * TOP_SCALING_PIXEL_CUTOFF * TOP_SCALING_PIXEL_CUTOFF +
+		_yRevTranslationB * TOP_SCALING_PIXEL_CUTOFF +
+		_yRevTranslationC);
+
+	_bottomScalingPointCutoff = _vm->_basketball->u32FloatToInt(
+		_yRevTranslationA * BOTTOM_SCALING_PIXEL_CUTOFF * BOTTOM_SCALING_PIXEL_CUTOFF +
+		_yRevTranslationB * BOTTOM_SCALING_PIXEL_CUTOFF +
+		_yRevTranslationC);
+
+	assert(_topScalingPointCutoff >= MAX_WORLD_Y);
+	assert(_bottomScalingPointCutoff <= 0);
+
+	return 1;
+}
+
+static U32FltPoint2D World_To_Screen_Translation(const U32FltPoint3D &worldPoint, LogicHEBasketball *logic) {
+	U32FltPoint2D screenPoint; // The point on the screen that corresponds to worldPoint
+	float courtWidth;          // The width of the court in pixels at the at the current y location
+	float xOffset;             // The number of pixels from the left side of the screen to the court at the current y location
+
+	float pixelsFromBottom;
+	float zToYOffset; // Given the current world z coordinate, how many pixels in the y direction should the object be moved
+	float a, c;       // The constant coefficients for the parametric equation which describes the relation between world y coordinates and screen y coordinates
+	float slope;      // The derivative of the above mentioned equation
+
+	assert(MAX_WORLD_X != 0);
+	assert(MAX_WORLD_Y != 0);
+	assert(TRANSLATED_MAX_START_X != 0);
+
+	// Let's find y...
+
+	a = logic->_yTranslationA;
+	c = logic->_yTranslationC;
+
+	// Normally, the game world coordinates compress as you move further up the screen.
+	// This adds to the illusion of depth perception. Sometimes it may be desirable to stop
+	// this compression after a certain point on the screen. This 'if block' handles that
+	// case...
+	if (worldPoint.y < logic->_bottomScalingPointCutoff) {
+		slope = 1 / (2 * sqrt(a * logic->_bottomScalingPointCutoff + a * c));
+		pixelsFromBottom = slope * (worldPoint.y - logic->_bottomScalingPointCutoff) + BOTTOM_SCALING_PIXEL_CUTOFF;
+	} else if (worldPoint.y < logic->_topScalingPointCutoff) {
+		slope = 1 / (2 * sqrt(a * worldPoint.y + a * c));
+		pixelsFromBottom = (sqrt(worldPoint.y + c) - sqrt(c)) / sqrt(a);
+	} else {
+		slope = 1 / (2 * sqrt(a * logic->_topScalingPointCutoff + a * c));
+		pixelsFromBottom = slope * (worldPoint.y - logic->_topScalingPointCutoff) + TOP_SCALING_PIXEL_CUTOFF;
+	}
+
+	screenPoint.y = BB_SCREEN_SIZE_Y - COURT_Y_OFFSET - pixelsFromBottom;
+
+	// Let's find x...
+
+	if (pixelsFromBottom < BOTTOM_SCALING_PIXEL_CUTOFF) {
+		courtWidth = TRANSLATED_NEAR_MAX_X - (2.0 * (BOTTOM_SCALING_PIXEL_CUTOFF / tan(logic->_courtAngle)));
+		xOffset = (tan((BBALL_M_PI / 2.0) - logic->_courtAngle) * BOTTOM_SCALING_PIXEL_CUTOFF) + COURT_X_OFFSET;
+	} else if (pixelsFromBottom < TOP_SCALING_PIXEL_CUTOFF) {
+		courtWidth = (TRANSLATED_NEAR_MAX_X - (2.0 * (pixelsFromBottom / tan(logic->_courtAngle))));
+		xOffset = (tan((BBALL_M_PI / 2.0) - logic->_courtAngle) * pixelsFromBottom) + COURT_X_OFFSET;
+	} else {
+		// Find the width of the court in pixels at the current y coordinate...
+		courtWidth = TRANSLATED_NEAR_MAX_X - (2.0 * (TOP_SCALING_PIXEL_CUTOFF / tan(logic->_courtAngle)));
+
+		// Find the number of pixels beetween the left side of the screen and the beginning of the court at the current y coordinate...
+		xOffset = tan(((BBALL_M_PI / 2.0) - logic->_courtAngle) * TOP_SCALING_PIXEL_CUTOFF) + COURT_X_OFFSET;
+	}
+
+	// Find the screen x based on the world x and y...
+	screenPoint.x = (worldPoint.x * courtWidth / MAX_WORLD_X) + xOffset;
+
+	// Now factor in world z to the screen y coordinate...
+	zToYOffset = (courtWidth / MAX_WORLD_X) * worldPoint.z;
+	screenPoint.y -= zToYOffset;
+
+	return screenPoint;
+}
+
+int LogicHEBasketball::u32_userWorldToScreenTranslation(const U32FltPoint3D &worldPoint) {
+	U32FltPoint2D screenPoint = World_To_Screen_Translation(worldPoint, this);
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(screenPoint.x));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, _vm->_basketball->u32FloatToInt(screenPoint.y));
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userScreenToWorldTranslation(const U32FltPoint2D &screenPoint) {
+	U32FltPoint2D worldPoint; // The point in the game world that corresponds to screenPoint
+	float courtWidth;         // The width of the court in pixels at the at the current y location
+	float xOffset;            // The number of pixels from the left side of the screen to the court at the current y location
+
+	float pixelsFromBottom;
+	float a, b, c; // The constant coefficients for the parametric equation which describes the relation between screen y coordinates and world y coordinates
+	float slope;   // The derivative of the above mentioned equation
+
+	assert(TRANSLATED_MAX_START_X != 0);
+	assert(TRANSLATED_NEAR_MAX_X != 0);
+	assert(TRANSLATED_FAR_MAX_X != 0);
+	assert(TRANSLATED_MAX_Y != 0);
+
+	// Let's find y...
+
+	a = _yRevTranslationA;
+	b = _yRevTranslationB;
+	c = _yRevTranslationC;
+
+	pixelsFromBottom = BB_SCREEN_SIZE_Y - COURT_Y_OFFSET - screenPoint.y;
+
+	// Normally, the game world coordinates compress as you move further up the screen.
+	// This adds to the illusion of depth perception. Sometimes it may be desirable to stop
+	// this compression after a certain point on the screen. This 'if block' handles that
+	// case...
+	if (pixelsFromBottom < BOTTOM_SCALING_PIXEL_CUTOFF) {
+		slope = (2 * a * BOTTOM_SCALING_PIXEL_CUTOFF + b);
+		worldPoint.y = slope * (pixelsFromBottom - BOTTOM_SCALING_PIXEL_CUTOFF) + _bottomScalingPointCutoff;
+	} else if (pixelsFromBottom < TOP_SCALING_PIXEL_CUTOFF) {
+		worldPoint.y = a * pixelsFromBottom * pixelsFromBottom + b * pixelsFromBottom + c;
+	} else {
+		slope = (2 * a * TOP_SCALING_PIXEL_CUTOFF + b);
+		worldPoint.y = slope * (pixelsFromBottom - TOP_SCALING_PIXEL_CUTOFF) + _topScalingPointCutoff;
+	}
+
+	// -Let's find x...
+
+	if (pixelsFromBottom < BOTTOM_SCALING_PIXEL_CUTOFF) {
+		courtWidth = TRANSLATED_NEAR_MAX_X - (2.0 * (BOTTOM_SCALING_PIXEL_CUTOFF / tan(_courtAngle)));
+		xOffset = (tan((BBALL_M_PI / 2.0) - _courtAngle) * BOTTOM_SCALING_PIXEL_CUTOFF) + COURT_X_OFFSET;
+	} else if (pixelsFromBottom < TOP_SCALING_PIXEL_CUTOFF) {
+		// Find the width of the court in pixels at the current y coordinate...
+		courtWidth = TRANSLATED_NEAR_MAX_X - (2.0 * (pixelsFromBottom / tan(_courtAngle)));
+
+		// Find the number of pixels beetween the left side of the screen and the beginning of the court at the current y coordinate...
+		xOffset = (tan((BBALL_M_PI / 2.0) - _courtAngle) * pixelsFromBottom) + COURT_X_OFFSET;
+	} else {
+		// Find the width of the court in pixels at the current y coordinate...
+		courtWidth = TRANSLATED_NEAR_MAX_X - (2.0 * (TOP_SCALING_PIXEL_CUTOFF / tan(_courtAngle)));
+
+		// Find the number of pixels beetween the left side of the screen and the beginning of the court at the current y coordinate...
+		xOffset = (tan((BBALL_M_PI / 2.0) - _courtAngle) * TOP_SCALING_PIXEL_CUTOFF) + COURT_X_OFFSET;
+	}
+
+	// Find the world x based on the screen x and y...
+	assert(courtWidth != 0);
+	worldPoint.x = (screenPoint.x - xOffset) * MAX_WORLD_X / courtWidth;
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(worldPoint.x));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, _vm->_basketball->u32FloatToInt(worldPoint.y));
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userGetCourtDimensions() {
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(MAX_WORLD_X));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, _vm->_basketball->u32FloatToInt(MAX_WORLD_Y));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_C, _vm->_basketball->u32FloatToInt(BASKET_X));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_D, _vm->_basketball->u32FloatToInt(BASKET_Y));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_E, _vm->_basketball->u32FloatToInt(BASKET_Z));
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userComputePointsForPixels(int pixels, int screenYPos) {
+	float points;
+	float pixelsFromBottom;
+	float courtWidth;
+	float courtAngle;
+
+	courtAngle = atan(TRANSLATED_MAX_Y / (double)TRANSLATED_MAX_START_X);
+	pixelsFromBottom = BB_SCREEN_SIZE_Y - COURT_Y_OFFSET - screenYPos;
+
+	if (pixelsFromBottom < 0) {
+		courtWidth = TRANSLATED_NEAR_MAX_X;
+	} else if (pixelsFromBottom < TRANSLATED_MAX_Y) {
+		// Find the width of the court in pixels at the current y coordinate...
+		courtWidth = TRANSLATED_NEAR_MAX_X - (2.0 * (pixelsFromBottom / tan(courtAngle)));
+	} else {
+		// Find the width of the court in pixels at the current y coordinate...
+		courtWidth = TRANSLATED_NEAR_MAX_X - (2.0 * (pixelsFromBottom / tan(courtAngle)));
+	}
+
+	points = (MAX_WORLD_X / courtWidth) * pixels;
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(points));
+
+	return 1;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/geo_translations.h b/engines/scumm/he/basketball/geo_translations.h
new file mode 100644
index 00000000000..8145c5df0a0
--- /dev/null
+++ b/engines/scumm/he/basketball/geo_translations.h
@@ -0,0 +1,67 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_GEO_TRANSLATIONS_H
+#define SCUMM_HE_BASKETBALL_GEO_TRANSLATIONS_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/logic_he.h"
+
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+
+namespace Scumm {
+
+#define BB_SCREEN_SIZE_Y            640     // Please note, this is intentional! It shouldn't be 480!
+
+#define COURT_X_OFFSET              18      // Pixels from left of screen to beginning of court
+#define COURT_Y_OFFSET              33      // Pixels from bottom of screen to beginning of court
+
+#define TRANSLATED_MAX_Y            302     // Pixels from bottom of court to top of court
+#define TRANSLATED_MID_Y            186     // Pixels from bottom of the court to center court
+#define TRANSLATED_DIAGONAL_Y       431     // Pixel length of the baseline
+#define TRANSLATED_MAX_START_X      308     // This is how far over x = 0 is pushed when y = MAX_WORLD_Y
+#define TRANSLATED_FAR_MAX_X        950     // Length of the far side of field ( world_y = MAX_WORLD_Y) when translated
+#define TRANSLATED_NEAR_MAX_X       1564    // Length of near side of field (world_y = MAX_WORLD_Y when translated
+
+// Length and width of the court in game world units
+#define WORLD_UNIT_MULTIPLIER       160
+#define MAX_WORLD_X                 (75 * WORLD_UNIT_MULTIPLIER)
+#define MAX_WORLD_Y                 (50 * WORLD_UNIT_MULTIPLIER)
+
+// Coordinates of the center of the hoop
+#define BASKET_PUSH_BACK_DIST (0.5 * WORLD_UNIT_MULTIPLIER)
+ 
+#define BASKET_X              (int)((5.25 * WORLD_UNIT_MULTIPLIER) - BASKET_PUSH_BACK_DIST)
+#define BASKET_Y              (25 * WORLD_UNIT_MULTIPLIER)
+#define BASKET_Z              (10 * WORLD_UNIT_MULTIPLIER)
+
+// The screen coordinate that sandwich where on the screen scaling occurs;
+// these coordinates are given in pixels from the bottom of the court
+#define TOP_SCALING_PIXEL_CUTOFF    (1000 - COURT_Y_OFFSET)
+#define BOTTOM_SCALING_PIXEL_CUTOFF (0 - COURT_Y_OFFSET)
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_GEO_TRANSLATIONS_H
diff --git a/engines/scumm/he/basketball/obstacle_avoidance.cpp b/engines/scumm/he/basketball/obstacle_avoidance.cpp
new file mode 100644
index 00000000000..a0767e775a7
--- /dev/null
+++ b/engines/scumm/he/basketball/obstacle_avoidance.cpp
@@ -0,0 +1,423 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/basketball/basketball.h"
+#include "scumm/he/basketball/court.h"
+#include "scumm/he/basketball/obstacle_avoidance.h"
+
+#include "common/array.h"
+#include "common/stack.h"
+#include "common/queue.h"
+#include "common/std/set.h"
+
+namespace Scumm {
+
+float Basketball::getAvoidanceDistance(const U32Circle &playerMarker, const CCollisionPlayer &obstacle) {
+	// Figure out how close we want to buzz the obstacle...
+	float passByDistance = (playerMarker.radius + obstacle.radius + OBSTACLE_AVOIDANCE_DISTANCE);
+
+	// Figure out how close we are to the obstacle...
+	float currentDistance = (playerMarker.center - obstacle.center).magnitude();
+
+	// Use the smaller of the two...
+	float avoidanceDistance = MIN(currentDistance, passByDistance);
+	return avoidanceDistance;
+}
+
+CCollisionPlayer *Basketball::detectObstacle(const U32Circle &playerMarker,
+								 int playerID,
+								 const U32FltPoint2D &targetLocation,
+								 bool targetIsObstacle,
+								 U32FltPoint2D *intersection,
+								 CBBallCourt *court) {
+	// Create a ray whose origin is at the player's center, and whose direction
+	// is the player's target vector...
+	U32Ray2D poRay;
+	poRay._origin = playerMarker.center;
+	poRay._direction = (targetLocation - poRay._origin);
+
+	// Cycle through all potential obstacles and see which obstacle is closest...
+	CCollisionPlayer *pFinalObstacle = nullptr;
+	float minDistance = (float)0x7FFFFFFF;
+
+	for (int i = 0; i <= LAST_PLAYER; ++i) {
+		// Get a pointer to the current obstacle...
+		CCollisionPlayer *pCurrentObstacle = court->getPlayerPtr(i);
+
+		// Make sure we are not checking ourselves or a bench player...
+		if ((pCurrentObstacle->_objectID != playerID) &&
+			(pCurrentObstacle->_playerIsInGame)) {
+			U32Circle obstacleMarker;
+			obstacleMarker.center = pCurrentObstacle->center;
+			obstacleMarker.radius = getAvoidanceDistance(playerMarker, *pCurrentObstacle);
+
+			if (poRay.nearIntersection(obstacleMarker, intersection)) {
+				float obstacleDist = (*intersection - playerMarker.center).magnitude();
+
+				float alertDistance = MIN(AVOIDANCE_LOOK_AHEAD_DISTANCE, poRay._direction.magnitude());
+
+				// See if this obstacle is in our way...
+				if (obstacleDist < alertDistance) {
+					// See if the target is within the obstacle...
+					float otDist = (targetLocation - pCurrentObstacle->center).magnitude();
+					if ((otDist > obstacleMarker.radius) || targetIsObstacle) {
+						// See if this obstacle is the closest one yet...
+						if (obstacleDist < minDistance) {
+							pFinalObstacle = pCurrentObstacle;
+							minDistance = obstacleDist;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	return pFinalObstacle;
+}
+
+bool Basketball::avoidObstacle(const U32Circle &playerMarker, const U32FltPoint2D &targetLocation, const CCollisionPlayer &obstacle, ERevDirection whichDirection, U32FltPoint2D *newTarget) {
+	double sinTheta;
+	double theta;
+
+	// Get a vector from the player center to the obstacle center...
+	U32FltVector2D poVector = obstacle.center - playerMarker.center;
+
+	// Get a vector from the target center to the obstacle center...
+	U32FltVector2D toVector = obstacle.center - targetLocation;
+
+	// Figure out how close we want to buzz the obstacle...
+	float avoidanceDistance = getAvoidanceDistance(playerMarker, obstacle);
+	avoidanceDistance += AVOIDANCE_SAFETY_DISTANCE;
+
+	// Figure out if we are avoinding the obstacle in the clockwise or
+	// counter-clockwise direction...
+	if (whichDirection == kNone) {
+		whichDirection = getAvoidanceDirection(playerMarker, targetLocation, obstacle);
+	}
+
+	// Figure out how many radians of roatation away from the center of the
+	// obstacle (using our center as the origin) we need to aim for in order to
+	// perfectly buzz the obstacle...
+	if (poVector.magnitude() == 0) {
+		sinTheta = 0;
+	} else {
+		sinTheta = avoidanceDistance / poVector.magnitude();
+		if ((sinTheta < -MAX_AVOIDANCE_ANGLE_SIN) || (MAX_AVOIDANCE_ANGLE_SIN < sinTheta)) {
+			sinTheta = MAX_AVOIDANCE_ANGLE_SIN;
+		}
+	}
+
+	theta = asin(sinTheta);
+
+	// Create a vector that points in the direction we must now travel...
+	poVector.rotate(whichDirection, theta);
+
+	// This vector will take us as close as we want to go to the obstacle, but
+	// we don't know how far in that direction we need to travel. To get to the,
+	// target we need to buzz the obstacle once, continue on a little bit, then
+	// turn and buzz the obstacle again. Right now we are going to calculate
+	// that second buzz path...
+
+	// We are caluclating from the target to the obstacle, so swap the direction...
+	whichDirection = (whichDirection == kClockwise) ? kCounterClockwise : kClockwise;
+
+	if (toVector.magnitude() == 0) {
+		sinTheta = 0;
+	} else {
+		sinTheta = avoidanceDistance / toVector.magnitude();
+		if (sinTheta < -MAX_AVOIDANCE_ANGLE_SIN) {
+			sinTheta = -MAX_AVOIDANCE_ANGLE_SIN;
+		} else if (sinTheta > MAX_AVOIDANCE_ANGLE_SIN) {
+			sinTheta = MAX_AVOIDANCE_ANGLE_SIN;
+		}
+	}
+
+	theta = asin(sinTheta);
+
+	// Create a vector that points in the direction we must travel after our
+	// initial pass-by of the target...
+	toVector.rotate(whichDirection, theta);
+
+	// Where the player and target vector's meet is where we should aim for...
+	U32Ray2D poRay;
+	poRay._origin = playerMarker.center;
+	poRay._direction = poVector;
+
+	U32Ray2D toRay;
+	toRay._origin = targetLocation;
+	toRay._direction = toVector;
+
+	return poRay.intersection(toRay, newTarget);
+}
+
+ERevDirection Basketball::getAvoidanceDirection(const U32Circle &playerMarker, const U32FltPoint2D &targetLocation, const CCollisionPlayer &obstacle) {
+	U32FltVector3D obstacleVector = obstacle.center - playerMarker.center;
+	U32FltVector3D targetVector = targetLocation - playerMarker.center;
+	U32FltVector3D crossVector = obstacleVector.cross(targetVector);
+
+	return (crossVector.z > 0) ? kCounterClockwise : kClockwise;
+}
+
+bool Basketball::getPathDistance(U32Circle *playerMarker, int playerID, Common::Stack<U32FltPoint2D> *targetStack, ERevDirection lastTurn, float *pathDistance, Common::Queue<U32FltPoint2D> *wayPointQueue, Std::set<int> *obstacleSet, CBBallCourt *court) {
+	U32FltPoint2D intersection;
+
+	// See if we're going for the final target...
+	bool shootingForFinalTarget = (targetStack->size() == 1);
+
+	U32FltPoint2D &currentTarget = targetStack->top();
+
+	// See if there is an obstacle between us and the target...
+	CCollisionPlayer *obstacle = detectObstacle(*playerMarker, playerID, currentTarget, !shootingForFinalTarget,  &intersection, court);
+	if (obstacle) {
+		// Make sure we haven't run into this obstacle already...
+		Std::set<int>::const_iterator obstacleIt = obstacleSet->find(obstacle->_objectID);
+		if (obstacleIt != obstacleSet->end()) {
+			if (targetStack->size() != 1) {
+				targetStack->pop();
+			}
+
+			return false;
+		}
+
+		// Recursively call getPathDistance for both the left and right paths
+		// around the current obstacle...
+		ERevDirection turnDirection = getBestPath(*playerMarker,  playerID, targetStack,
+												  obstacle, lastTurn, pathDistance,
+												  wayPointQueue, obstacleSet, court);
+
+		if (turnDirection == kNone) {
+			if (targetStack->size() != 1) {
+				targetStack->pop();
+			}
+
+			return false;
+		} else {
+			return true;
+		}
+	} else {
+		// Get the distance between the player and the target...
+		float targetDistance = (currentTarget - playerMarker->center).magnitude();
+
+		// Add that distance to the current total for this path...
+		*pathDistance += targetDistance;
+
+		// Advance the player to the target...
+		playerMarker->center = currentTarget;
+
+		// See if this gets us to the final destination...
+		if (shootingForFinalTarget) {
+			return true;
+		} else {
+			wayPointQueue->push(currentTarget);
+			targetStack->pop();
+
+			// Keep on going for the final target...
+			return getPathDistance(playerMarker, playerID, targetStack,
+								   kNone, pathDistance, wayPointQueue,
+								   obstacleSet, court);
+		}
+	}
+}
+
+void Basketball::pushTargetOutOfObstacle(const U32Circle &playerMarker, const CCollisionPlayer &obstacle, Common::Stack<U32FltPoint2D> *targetStack) {
+	// Get the effective radius of the obstacle...
+	float avoidanceDistance = getAvoidanceDistance(playerMarker, obstacle);
+
+	// See if the target is within that radius...
+	if ((targetStack->top() - obstacle.center).magnitude() < avoidanceDistance) {
+		// Make sure this isn't the final target...
+		if (targetStack->size() == 1) {
+			warning("Basketball::pushTargetOutOfObstacle(): Should not be calling this function on the final target");
+		}
+
+		// Make a ray from the player to the target...
+		U32Ray2D playerRay;
+		playerRay._origin = playerMarker.center;
+		playerRay._direction = targetStack->top() - playerMarker.center;
+
+		// Make a circle around the obstacle...
+		U32Circle obstacleCircle;
+		obstacleCircle.center = obstacle.center;
+		obstacleCircle.radius = avoidanceDistance;
+
+		// Find the farthest intersection of the ray and the circle...
+		U32FltPoint2D newTarget;
+		if (playerRay.farIntersection(obstacleCircle, &newTarget)) {
+			targetStack->pop();
+			targetStack->push(newTarget);
+		} else {
+			warning("Basketball::pushTargetOutOfObstacle(): Unable to intersect the player ray with the obstacle circle");
+		}
+	}
+}
+
+ERevDirection Basketball::getBestPath(const U32Circle &playerMarker, int playerID, Common::Stack<U32FltPoint2D> *targetStack, CCollisionPlayer *obstacle,
+						  ERevDirection lastTurn, float *distance, Common::Queue<U32FltPoint2D> *wayPointQueue, Std::set<int> *obstacleSet, CBBallCourt *court) {
+	U32Circle tempLocation;
+
+	float leftDistance = 0;
+	float rightDistance = 0;
+
+	bool leftPath = false;
+	bool rightPath = false;
+
+	Common::Queue<U32FltPoint2D> leftWayPointQueue;
+	Common::Queue<U32FltPoint2D> rightWayPointQueue;
+
+	// Add this obstacle to the obstacle set...
+	obstacleSet->insert(obstacle->_objectID);
+
+	// Clear out any unnecessary interim targets...
+	while (targetStack->size() != 1) {
+		targetStack->pop();
+	}
+
+	// Get the avoidance path to the left...
+	if (kClockwise != lastTurn) {
+		U32FltPoint2D leftTarget;
+
+		if (avoidObstacle(playerMarker, targetStack->top(), *obstacle, kCounterClockwise, &leftTarget)) {
+			tempLocation = playerMarker;
+
+			targetStack->push(leftTarget);
+
+			leftWayPointQueue = *wayPointQueue;
+
+			leftPath = getPathDistance(&tempLocation, playerID, targetStack,
+										kCounterClockwise, &leftDistance, &leftWayPointQueue,
+										obstacleSet, court);
+		}
+	}
+
+	// Get the avoidance path to the right...
+	if (kCounterClockwise != lastTurn) {
+		U32FltPoint2D rightTarget;
+
+		if (avoidObstacle(playerMarker, targetStack->top(),
+						  *obstacle, kClockwise, &rightTarget)) {
+			tempLocation = playerMarker;
+
+			targetStack->push(rightTarget);
+
+			rightWayPointQueue = *wayPointQueue;
+
+			rightPath = getPathDistance(&tempLocation, playerID, targetStack,
+										 kClockwise, &rightDistance, &rightWayPointQueue,
+										 obstacleSet, court);
+		}
+	}
+
+	// See which path is better
+	if (leftPath && rightPath) {
+		*distance = MIN(leftDistance, rightDistance);
+		if (*distance == leftDistance) {
+			*wayPointQueue = leftWayPointQueue;
+			return kCounterClockwise;
+		} else {
+			*wayPointQueue = rightWayPointQueue;
+			return kClockwise;
+		}
+	} else if (leftPath && !rightPath) {
+		*distance = leftDistance;
+		*wayPointQueue = leftWayPointQueue;
+		return kCounterClockwise;
+	} else if (!leftPath && rightPath) {
+		*distance = rightDistance;
+		*wayPointQueue = rightWayPointQueue;
+		return kClockwise;
+	} else {
+		*distance = 0;
+		return kNone;
+	}
+}
+
+int LogicHEBasketball::u32_userGetAvoidancePath(int playerID, const U32FltPoint2D &targetLocation, EAvoidanceType type) {
+	U32FltPoint2D newTarget;
+	U32FltPoint2D intersection;
+
+	// Get a pointer to the source player...
+	CCollisionPlayer *player = _vm->_basketball->_court->getPlayerPtr(playerID);
+
+	// Make sure the player isn't already at its target...
+	if (player->center == targetLocation) {
+		newTarget = targetLocation;
+	} else {
+		// Extract the player's current location...
+		U32Circle playerMarker;
+		playerMarker.center = player->center;
+		playerMarker.radius = player->radius;
+
+		// See if an obstacle was found...
+		CCollisionPlayer *obstacle = _vm->_basketball->detectObstacle(playerMarker, player->_objectID,
+													 targetLocation, false, &intersection,
+													 _vm->_basketball->_court);
+
+		if (obstacle) {
+			if (type == kMultipleObject) {
+				Common::Queue<U32FltPoint2D> wayPointQueue;
+				Common::Stack<U32FltPoint2D> targetStack;
+				Std::set<int> obstacleSet;
+
+				targetStack.push(targetLocation);
+
+				float pathDistance;
+
+				ERevDirection turnDirection = _vm->_basketball->getBestPath(playerMarker, player->_objectID, &targetStack,
+														  obstacle, kNone, &pathDistance, &wayPointQueue,  &obstacleSet,
+														  _vm->_basketball->_court);
+
+				targetStack.pop();
+
+				if (!targetStack.empty()) {
+					warning("LogicHEBasketball::u32_userGetAvoidancePath(): It doesn't look like we calculated things out to the final target.");
+				}
+
+				if (wayPointQueue.empty()) {
+					assert(turnDirection == kNone);
+
+					// We were unable to find a valid path to the target,
+					// so treat this like a single obstacle...
+					if (!_vm->_basketball->avoidObstacle(playerMarker, targetLocation, *obstacle, kNone, &newTarget)) {
+						warning("LogicHEBasketball::u32_userGetAvoidancePath(): Unable to go around the primary obstacle");
+						newTarget = targetLocation;
+					}
+				} else {
+					newTarget = wayPointQueue.front();
+				}
+			} else {
+				if (!_vm->_basketball->avoidObstacle(playerMarker, targetLocation, *obstacle, kNone, &newTarget)) {
+					warning("LogicHEBasketball::u32_userGetAvoidancePath(): Unable to go around the primary obstacle");
+					newTarget = targetLocation;
+				}
+			}
+		} else {
+			newTarget = targetLocation;
+		}
+	}
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(newTarget.x));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, _vm->_basketball->u32FloatToInt(newTarget.y));
+
+	return 1;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/obstacle_avoidance.h b/engines/scumm/he/basketball/obstacle_avoidance.h
new file mode 100644
index 00000000000..58e093a1296
--- /dev/null
+++ b/engines/scumm/he/basketball/obstacle_avoidance.h
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_OBSTACLE_AVOIDANCE_H
+#define SCUMM_HE_BASKETBALL_OBSTACLE_AVOIDANCE_H
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+
+#ifdef ENABLE_HE
+
+namespace Scumm {
+
+#define AVOIDANCE_LOOK_AHEAD_DISTANCE  4000.0F
+#define OBSTACLE_AVOIDANCE_DISTANCE    75
+#define AVOIDANCE_SAFETY_DISTANCE      1
+#define MAX_AVOIDANCE_ANGLE_SIN        1.0F
+
+enum EAvoidanceType {
+	kSingleObject = 0,
+	kMultipleObject = 1
+};
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_OBSTACLE_AVOIDANCE_H
diff --git a/engines/scumm/he/basketball/passing.cpp b/engines/scumm/he/basketball/passing.cpp
new file mode 100644
index 00000000000..91dcd62eea8
--- /dev/null
+++ b/engines/scumm/he/basketball/passing.cpp
@@ -0,0 +1,320 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/basketball/court.h"
+#include "scumm/he/basketball/passing.h"
+
+namespace Scumm {
+
+static float getBallImpactTime(CCollisionSphere *ball, int gravity, int height) {
+	// Solve the equation:
+	// 
+	// z + vz * t - 0.5 * g * t * t = height
+	// 
+	// to find out how long before the ball hits the ground...
+	float a = -.5 * gravity;
+	float b = ball->_velocity.z;
+	float c = ball->center.z - ball->radius - height;
+
+	double tFinal;
+	if (((b * b) < (4 * a * c)) || (a == 0)) {
+		tFinal = 0;
+	} else {
+		// See how long before the ball hits the ground...
+		tFinal = (-b - sqrt(b * b - 4 * a * c)) / (2 * a);
+		tFinal = MAX(0.0, tFinal);
+	}
+
+	return tFinal;
+}
+
+int LogicHEBasketball::u32_userComputeAngleOfPass(int velocity, int hDist, int vDist, int gravity) {
+	assert(hDist > 0);
+
+	double theta = _vm->_basketball->getLaunchAngle(velocity, hDist, vDist, gravity);
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32DoubleToInt(theta));
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userComputeAngleOfBouncePass(int velocity, int hDist, int currentZ, int destZ, int gravity) {
+	double theta;
+	int vDist;
+
+	assert(hDist > 0);
+
+	// Aim the ball at the floor 2/3 the distance between you and your target...
+	hDist = (hDist * 2) / 3;
+	vDist = 0 - currentZ;
+	theta = _vm->_basketball->getLaunchAngle(velocity, hDist, vDist, gravity);
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32DoubleToInt(theta));
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userHitMovingTarget(U32FltPoint2D sourcePlayer, U32FltPoint2D targetPlayer, U32FltVector2D targetVelocity, int passSpeed) {
+	// Calculate the xy speed of the pass. To do this, we need to estimate
+	// the elevation angle of the pass. This is because the actual angle
+	// will be calculated based on the target location, which is what we
+	// are currently getting...
+	float xyPassSpeed = passSpeed * cos((PASS_ANGLE * BBALL_M_PI) / 180);
+
+	// The distance between the target player at future time t and and the
+	// passing player is equal to the pass speed times t. Solve for t...
+	float a = (targetVelocity.x * targetVelocity.x) +
+			  (targetVelocity.y * targetVelocity.y) -
+			  (xyPassSpeed * xyPassSpeed);
+
+	float b = (2 * targetPlayer.x * targetVelocity.x) +
+			  (2 * targetPlayer.y * targetVelocity.y) -
+			  (2 * sourcePlayer.x * targetVelocity.x) -
+			  (2 * sourcePlayer.y * targetVelocity.y);
+
+	float c = (targetPlayer.x * targetPlayer.x) +
+			  (targetPlayer.y * targetPlayer.y) +
+			  (sourcePlayer.x * sourcePlayer.x) +
+			  (sourcePlayer.y * sourcePlayer.y) -
+			  (2 * targetPlayer.x * sourcePlayer.x) -
+			  (2 * targetPlayer.y * sourcePlayer.y);
+
+	// Now we have two answer candidates. We want the smallest of the two that is
+	// greater than 0...
+	double tFinal;
+	if (((b * b) < (4 * a * c)) || (a == 0)) {
+		tFinal = 0.0;
+	} else {
+		double t1 = (-b + sqrt(b * b - 4 * a * c)) / (2 * a);
+		double t2 = (-b - sqrt(b * b - 4 * a * c)) / (2 * a);
+		tFinal = MIN_GREATER_THAN_ZERO(t1, t2);
+		tFinal -= BALL_LEAD_TIME; // Put the player at the target spot a few frames
+								  // before the ball to give them a chance to get ready
+								  // to catch it...
+		tFinal = MAX(0.0, tFinal);
+	}
+
+	assert(tFinal < 50.0);
+
+	U32FltPoint2D targetPoint;
+	targetPoint.x = targetPlayer.x + (targetVelocity.x * tFinal);
+	targetPoint.y = targetPlayer.y + (targetVelocity.y * tFinal);
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(targetPoint.x));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, _vm->_basketball->u32FloatToInt(targetPoint.y));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_C, _vm->_basketball->u32DoubleToInt(tFinal));
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userGetPassTarget(int playerID, const U32FltVector3D &aimVector) {
+	U32Ray3D passerRay;
+
+	// Get our target candidates...
+	Common::Array<CCollisionPlayer> *targetList = (_vm->_basketball->_court->getPlayerListPtr(playerID));
+
+	// Get the passer...
+	CCollisionPlayer *passer = _vm->_basketball->_court->getPlayerPtr(playerID);
+
+	// Set up a ray from the passer, pointing in the direction of the aim vector...
+	passerRay._origin = passer->center;
+	passerRay._direction = aimVector.normalize() * MAX_PASSING_DIST;
+
+	// Get the distance from the passers aim vector to each of the remaining target
+	// candidates...
+	int passTargetID = NO_PLAYER;
+	float leastDist = MAX_PASSING_DIST;
+
+	for (size_t i = 0; i < targetList->size(); ++i) {
+		// Get the current target candidate...
+		CCollisionPlayer *target = &(*targetList)[i];
+
+		// Make sure the target is in the game...
+		if (target->_playerIsInGame) {
+			// Make sure that the passing player is not a target candidate...
+			if (passer->_objectID != target->_objectID) {
+				// Point-line distance formula.
+				U32FltPoint3D *targetCenter = &target->center;
+
+				float u = (((targetCenter->x - passerRay._origin.x) * passerRay._direction.x) +
+						   ((targetCenter->y - passerRay._origin.y) * passerRay._direction.y)) /
+						  (passerRay._direction.magnitude() * passerRay._direction.magnitude());
+
+				if (u >= 0) {
+					U32FltVector3D distance;
+					distance.x = targetCenter->x - (passerRay._origin.x + (u * passerRay._direction.x));
+					distance.y = targetCenter->y - (passerRay._origin.y + (u * passerRay._direction.y));
+					float totalDist = distance.magnitude();
+					if (totalDist < leastDist) {
+						leastDist = totalDist;
+						passTargetID = target->_objectID;
+					}
+				}
+			}
+		}
+	}
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, passTargetID);
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userDetectPassBlocker(int playerID, const U32FltPoint3D &targetPoint) {
+	int blockerPresent = 0;
+
+	// Get our blocker candidates....
+	Common::Array<CCollisionPlayer> *targetList = (_vm->_basketball->_court->getOpponentListPtr(playerID));
+
+	// Get the passer and catcher...
+	CCollisionPlayer *passer = _vm->_basketball->_court->getPlayerPtr(playerID);
+
+	// Set up a ray from the passer, pointing in the direction of the aim vector...
+	U32FltVector2D passVector = targetPoint - passer->center;
+
+	// Get the distance from the passers aim vector to each of the remaining target
+	// candidates...
+	for (size_t i = 0; i < targetList->size(); ++i) {
+		CCollisionPlayer *blocker = &(*targetList)[i];
+
+		if (blocker->_playerIsInGame) {
+			// --- Ray -> Circle intersection test ---
+			// Get a vector from the passer to the potential blocker...
+			U32FltVector2D enemyVector = blocker->center - passer->center;
+
+			// Project that vector onto the pass vector
+			float enemyDistance = enemyVector.projectScalar(passVector);
+
+			// Test to see if the blocker is behind the pass or behind the pass target...
+			float maxBlockerDistance = passVector.magnitude();
+			if ((0 > enemyDistance) || (enemyDistance > maxBlockerDistance)) {
+				continue;
+			}
+
+			// Find the distance between the blocker and the passVector...
+			float mSquared = (enemyVector.magnitude() * enemyVector.magnitude()) -
+							 (enemyDistance * enemyDistance);
+
+			// If that distance is less than the blocker's radius, we are done...
+			if (mSquared <= ((blocker->radius + _vm->_basketball->_court->_basketBall.radius) * (blocker->radius + _vm->_basketball->_court->_basketBall.radius))) {
+				blockerPresent = 1;
+				break;
+			}
+		}
+	}
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, blockerPresent);
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userGetBallIntercept(int playerID, int ballID, int playerSpeed, int gravity) {
+	double tFinal = 0.0;
+
+	CCollisionSphere *ball = _vm->_basketball->_court->getBallPtr(ballID);
+	CCollisionPlayer *player = _vm->_basketball->_court->getPlayerPtr(playerID);
+
+	assert(!ball->_ignore);
+
+	U32Circle playerMarker;
+	playerMarker.center = player->center;
+	playerMarker.radius = player->radius + ball->radius;
+
+	U32Ray2D playerRay;
+	playerRay._origin = player->center;
+	playerRay._direction = player->_velocity;
+
+	U32Ray2D ballRay;
+	ballRay._origin = ball->center;
+	ballRay._direction = ball->_velocity;
+
+	// See if the ball is standing still...
+	if (ballRay._direction.magnitude() == 0) {
+		tFinal = 0.0;
+	} else {
+		// See when the ball is going to fall to the player's height...
+		float tFall = getBallImpactTime(ball, gravity, (player->height / 2));
+
+		// See if the ball is headed straight for us...
+		U32FltPoint2D intersection;
+		if (ballRay.nearIntersection(playerMarker, &intersection)) {
+			// Get the time till the ball reaches the player...
+			float tPlayerImpact = (ball->center - player->center).xyMagnitude() / ball->_velocity.xyMagnitude();
+
+			// Decide if it's best to stay put, or run back where the ball will fall to...
+			tFinal = MAX(tPlayerImpact, tFall);
+		} else {
+			// The distance between the target player at future time t and and the
+			// ball is equal to the playerSpeed times t. Solve for t...
+			float a = (ball->_velocity.x * ball->_velocity.x) +
+					  (ball->_velocity.y * ball->_velocity.y) -
+					  (playerSpeed * playerSpeed);
+
+			float b = (2 * ball->center.x * ball->_velocity.x) +
+					  (2 * ball->center.y * ball->_velocity.y) -
+					  (2 * player->center.x * ball->_velocity.x) -
+					  (2 * player->center.y * ball->_velocity.y);
+
+			float c = (ball->center.x * ball->center.x) +
+					  (ball->center.y * ball->center.y) +
+					  (player->center.x * player->center.x) +
+					  (player->center.y * player->center.y) -
+					  (2 * ball->center.x * player->center.x) -
+					  (2 * ball->center.y * player->center.y);
+
+			if (((b * b) < (4 * a * c)) || (a == 0)) {
+				// Now see if we can get away with just going the way we were...
+				if (playerRay.intersection(ballRay, &intersection)) {
+					tFinal = (ball->center - intersection).xyMagnitude() / ball->_velocity.xyMagnitude();
+				}
+			} else {
+				// Find the closest place we could intercept the ball...
+				tFinal = (-b - sqrt(b * b - 4 * a * c)) / (2 * a);
+				if (tFinal < 0) {
+					tFinal = (-b + sqrt(b * b - 4 * a * c)) / (2 * a);
+				}
+
+				// See if the ball will come down low enough to catch by that time...
+				tFinal = MAX((double)tFall, tFinal);
+			}
+
+			// Now see if we're just better off just going the way we were...
+			if (playerRay.intersection(ballRay, &intersection)) {
+				float tFutureImpact = (ball->center - intersection).xyMagnitude() / ball->_velocity.xyMagnitude();
+				tFinal = MAX((double)tFutureImpact, tFinal);
+			}
+		}
+
+		// Now see if we're better off just chasing the ball...
+		tFinal = MAX(0.0, tFinal);
+	}
+
+	U32FltPoint2D targetPoint;
+	targetPoint.x = ball->center.x + (ball->_velocity.x * tFinal);
+	targetPoint.y = ball->center.y + (ball->_velocity.y * tFinal);
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(targetPoint.x));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, _vm->_basketball->u32FloatToInt(targetPoint.y));
+
+	return 1;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/passing.h b/engines/scumm/he/basketball/passing.h
new file mode 100644
index 00000000000..b62c2704413
--- /dev/null
+++ b/engines/scumm/he/basketball/passing.h
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_PASSING_H
+#define SCUMM_HE_BASKETBALL_PASSING_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/logic_he.h"
+
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+
+namespace Scumm {
+
+#define BALL_LEAD_TIME        2
+#define MAX_PASSING_DIST      15000
+#define MAX_BLOCKER_DISTANCE  (6 * WORLD_UNIT_MULTIPLIER)
+#define PASS_ANGLE            30
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_PASSING_H
diff --git a/engines/scumm/he/basketball/shooting.cpp b/engines/scumm/he/basketball/shooting.cpp
new file mode 100644
index 00000000000..2969d93dc1e
--- /dev/null
+++ b/engines/scumm/he/basketball/shooting.cpp
@@ -0,0 +1,181 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+#include "scumm/he/basketball/collision/bball_collision.h"
+#include "scumm/he/basketball/court.h"
+#include "scumm/he/basketball/geo_translations.h"
+#include "scumm/he/basketball/shooting.h"
+
+namespace Scumm {
+
+int LogicHEBasketball::u32_userComputeInitialShotVelocity(int theta, int hDist, int vDist, int gravity) {
+	double velocity;
+	double trajectoryAngle;
+	double targetAngle;
+
+	assert(gravity > 0);
+
+	if ((hDist == 0) && (vDist == 0)) {
+		velocity = 0;
+	} else {
+		trajectoryAngle = (theta * BBALL_M_PI) / 180;
+
+		// Find the angle between the horizon and the line between the source and the target...
+		targetAngle = atan2(vDist, hDist);
+
+		// If the shot is impossible, then return a 0 for velocity, otherwise calculate the
+		// required velocity...
+		if ((theta == 90) && (hDist == 0)) {
+			velocity = sqrt(abs(vDist) * 2 * gravity);
+		} else if (((targetAngle >= 0) && (targetAngle < (BBALL_M_PI / 2))) &&
+				   ((trajectoryAngle <= targetAngle) || (trajectoryAngle >= (BBALL_M_PI / 2)))) {
+			velocity = 0;
+		} else if (((targetAngle >= (BBALL_M_PI / 2) && (targetAngle < BBALL_M_PI))) &&
+				   ((trajectoryAngle >= targetAngle) || (trajectoryAngle <= (BBALL_M_PI / 2)))) {
+			velocity = 0;
+		} else if (((targetAngle >= -BBALL_M_PI && (targetAngle < -(BBALL_M_PI / 2)))) &&
+				   ((trajectoryAngle >= targetAngle) || (trajectoryAngle <= (BBALL_M_PI / 2)))) {
+			velocity = 0;
+		} else if (((targetAngle >= -(BBALL_M_PI / 2) && (targetAngle < 0))) &&
+				   ((trajectoryAngle <= targetAngle) || (trajectoryAngle >= (BBALL_M_PI / 2)))) {
+			velocity = 0;
+		} else {
+			float numerator = gravity * hDist * hDist;
+			float denominator = (2 * vDist * cos(trajectoryAngle) * cos(trajectoryAngle)) +
+								(2 * hDist * sin(trajectoryAngle) * cos(trajectoryAngle));
+
+			assert((numerator / denominator) >= 0);
+
+			if ((numerator / denominator) < 0) {
+				velocity = 0;
+			} else {
+				velocity = sqrt(numerator / denominator);
+			}
+
+			// Hack, because this is off somehow, or because to go in the hoop,
+			// the ball should hit a little in front...
+			velocity -= 4;
+		}
+	}
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32DoubleToInt(velocity));
+
+	return 1;
+}
+
+static double getShotAngle(int hDist, int vDist) {
+	double degrees;
+	float hUnits;
+	float a, b, c;
+
+	assert(hDist >= 0);
+
+	if (vDist == 0) {
+		if (hDist == 0)
+			degrees = MAX_SHOT_ANGLE;
+		else
+			degrees = MIN_SHOT_ANGLE;
+	} else {
+		hUnits = hDist / -(float)vDist;
+
+		a = (float)((2.0 - 4.0) / ((TEN_FOOT_SHOT_ANGLE - MIN_SHOT_ANGLE) * 4.0 * 2.0 - 4.0 * (MAX_SHOT_ANGLE - MIN_SHOT_ANGLE)));
+		b = a / ((MAX_SHOT_ANGLE - MIN_SHOT_ANGLE) * a - 1.0);
+		c = MIN_SHOT_ANGLE;
+
+		degrees = 1.0 / (a * (hUnits + 1.0) * (hUnits + 1.0)) + 1.0 / (b * (hUnits + 1.0)) + c;
+	}
+
+	return degrees;
+}
+
+int LogicHEBasketball::u32_userComputeAngleOfShot(int hDist, int vDist) {
+	double degrees;
+
+	degrees = getShotAngle(hDist, vDist);
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32DoubleToInt(degrees));
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userComputeBankShotTarget(U32FltPoint3D basketLoc, int ballRadius) {
+	U32FltPoint3D targetPoint;
+	float backboardDist;
+	CCollisionBox *backboard;
+
+	if (basketLoc.x < (MAX_WORLD_X / 2)) {
+		// Left basket...
+		backboard = &(_vm->_basketball->_court->_objectList[_vm->_basketball->_court->_backboardIndex[LEFT_BASKET]]);
+		backboardDist = basketLoc.x - backboard->maxPoint.x;
+	} else {
+		// Right basket...
+		backboard = &(_vm->_basketball->_court->_objectList[_vm->_basketball->_court->_backboardIndex[RIGHT_BASKET]]);
+		backboardDist = basketLoc.x - backboard->minPoint.x;
+	}
+
+	targetPoint.x = basketLoc.x - (2 * backboardDist);
+	targetPoint.y = basketLoc.y;
+	targetPoint.z = basketLoc.z + ballRadius;
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(targetPoint.x));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, _vm->_basketball->u32FloatToInt(targetPoint.y));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_C, _vm->_basketball->u32FloatToInt(targetPoint.z));
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userComputeSwooshTarget(const U32FltPoint3D &basketLoc, int ballRadius) {
+	U32FltPoint3D targetPoint;
+
+	targetPoint.x = basketLoc.x;
+	targetPoint.y = basketLoc.y;
+	targetPoint.z = basketLoc.z + ballRadius;
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(targetPoint.x));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, _vm->_basketball->u32FloatToInt(targetPoint.y));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_C, _vm->_basketball->u32FloatToInt(targetPoint.z));
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userDetectShotMade(const U32Sphere &basketball, const U32IntVector3D &ballVector, int gravity, int whichBasket) {
+	int shotWasMade = 0;
+	U32Distance3D distance;
+
+	distance.x = basketball.center.x - _vm->_basketball->_court->_shotSpot[whichBasket].center.x;
+	distance.y = basketball.center.y - _vm->_basketball->_court->_shotSpot[whichBasket].center.y;
+	distance.z = basketball.center.z - _vm->_basketball->_court->_shotSpot[whichBasket].center.z;
+
+	if (distance.magnitude() <= (_vm->_basketball->_court->_shotSpot[whichBasket].radius + basketball.radius)) {
+		if (ballVector.z < 0) {
+			shotWasMade = 1;
+		}
+	}
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, shotWasMade);
+
+	return 1;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/basketball/shooting.h b/engines/scumm/he/basketball/shooting.h
new file mode 100644
index 00000000000..3dae9534ae9
--- /dev/null
+++ b/engines/scumm/he/basketball/shooting.h
@@ -0,0 +1,44 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_BASKETBALL_SHOOTING_H
+#define SCUMM_HE_BASKETBALL_SHOOTING_H
+
+#ifdef ENABLE_HE
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/logic_he.h"
+
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+
+namespace Scumm {
+
+#define MAX_SHOT_ANGLE        90  // Unless you're doing a behind the back shot, this is the highest angle possible to shoot from
+#define TEN_FOOT_SHOT_ANGLE   75  // The shot angle used from ten feet out; the shot angle curve is defined by this point
+#define MIN_SHOT_ANGLE        45  // This is the lowest angle a player will ever shoot from
+#define SHOT_ANGLE_CUTOFF     3   // This is the horizontal distance / vertical distance ratio where the shot angle is at its lowest; any shot behind this point uses the minimum shot angle
+#define SHOT_OFFSET_DISTANCE  200 // This is the distance above the basket that the shooter shoots at
+
+} // End of namespace Scumm
+
+#endif // ENABLE_HE
+
+#endif // SCUMM_HE_BASKETBALL_SHOOTING_H
diff --git a/engines/scumm/he/basketball/trajectory.cpp b/engines/scumm/he/basketball/trajectory.cpp
new file mode 100644
index 00000000000..485dde8f257
--- /dev/null
+++ b/engines/scumm/he/basketball/trajectory.cpp
@@ -0,0 +1,112 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/basketball/basketball.h"
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+
+namespace Scumm {
+
+int LogicHEBasketball::u32_userComputeTrajectoryToTarget(const U32FltPoint3D &sourcePoint, const U32FltPoint3D &targetPoint, int speed) {
+	U32FltVector3D trajectory; // The final trajectory of the object
+	float xDist, yDist, zDist; // The distance that the object will move in xyz space
+	float hDist, totalDist;
+	float hAngle, vAngle;      // The angle of trajectory from the x axis and from the xy plane
+	float hSpeed;              // The speed of the object along the xy plane
+
+	xDist = targetPoint.x - sourcePoint.x;
+	yDist = targetPoint.y - sourcePoint.y;
+	zDist = targetPoint.z - sourcePoint.z;
+
+	hDist = sqrt((xDist * xDist) + (yDist * yDist));
+	totalDist = sqrt((hDist * hDist) + (zDist * zDist));
+
+	if (totalDist < speed)
+		speed = (int)(totalDist + 0.5F);
+
+	hAngle = atan2(yDist, xDist);
+	vAngle = atan2(zDist, totalDist);
+
+	hSpeed = speed * cos(vAngle);
+
+	trajectory.x = hSpeed * cos(hAngle);
+	trajectory.y = hSpeed * sin(hAngle);
+	trajectory.z = speed * sin(vAngle);
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(trajectory.x));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, _vm->_basketball->u32FloatToInt(trajectory.y));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_C, _vm->_basketball->u32FloatToInt(trajectory.z));
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userComputeLaunchTrajectory(const U32FltPoint2D &sourcePoint, const U32FltPoint2D &targetPoint, int launchAngle, int iVelocity) {
+	U32FltVector3D trajectory; // The final trajectory of the object
+	float xDist, yDist;        // The distance that the object will move in xyz space
+	float vAngle;              // The angle of trajectory from the x axis and from the xy plane
+	float hAngle;
+	float hVelocity;           // The speed of the object along the xy plane
+
+	xDist = targetPoint.x - sourcePoint.x;
+	yDist = targetPoint.y - sourcePoint.y;
+
+	hAngle = atan2(yDist, xDist);
+	vAngle = (launchAngle * BBALL_M_PI) / 180;
+
+	hVelocity = iVelocity * cos(vAngle);
+
+	trajectory.x = hVelocity * cos(hAngle);
+	trajectory.y = hVelocity * sin(hAngle);
+	trajectory.z = iVelocity * sin(vAngle);
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(trajectory.x));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_B, _vm->_basketball->u32FloatToInt(trajectory.y));
+	writeScummVar(_vm1->VAR_U32_USER_VAR_C, _vm->_basketball->u32FloatToInt(trajectory.z));
+
+	return 1;
+}
+
+int LogicHEBasketball::u32_userComputeAngleBetweenVectors(const U32FltVector3D &vector1, const U32FltVector3D &vector2) {
+	float radiansCosine;
+	float radians;
+	float angle;
+
+	if ((vector1.magnitude() * vector2.magnitude()) == 0) {
+		angle = 0;
+	} else {
+		radiansCosine = (vector1 * vector2) / (vector1.magnitude() * vector2.magnitude());
+
+		if (radiansCosine > 1) {
+			radiansCosine = 1;
+		} else if (radiansCosine < -1) {
+			radiansCosine = -1;
+		}
+
+		radians = acos(radiansCosine);
+		angle = (radians * 180) / BBALL_M_PI;
+	}
+
+	writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(angle));
+
+	return 1;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/intern_he.h b/engines/scumm/he/intern_he.h
index e08f66afee2..c420128f4bc 100644
--- a/engines/scumm/he/intern_he.h
+++ b/engines/scumm/he/intern_he.h
@@ -46,11 +46,13 @@ class Sprite;
 class CUP_Player;
 
 class Moonbase;
+class Basketball;
 #endif
 
 class ScummEngine_v60he : public ScummEngine_v6 {
 #ifdef ENABLE_HE
 	friend class Moonbase;
+	friend class Basketball;
 #endif
 protected:
 	enum SubOpType {
@@ -84,6 +86,7 @@ public:
 #ifdef ENABLE_HE
 public:
 	Moonbase *_moonbase;
+	Basketball *_basketball;
 #endif
 
 public:
@@ -587,6 +590,7 @@ class ScummEngine_v90he : public ScummEngine_v80he {
 #endif
 #endif
 	friend class Moonbase;
+	friend class Basketball;
 	friend class MoviePlayer;
 	friend class Sprite;
 	friend class Wiz;
@@ -844,6 +848,8 @@ protected:
 class ScummEngine_v100he : public ScummEngine_v99he {
 friend class AI;
 friend class Moonbase;
+friend class LogicHEBasketball;
+friend class Basketball;
 
 // The following engine versions use sub opcodes from this version
 friend class ScummEngine_v71he;
@@ -1123,6 +1129,8 @@ protected:
 	byte VAR_U32_USER_VAR_D;
 	byte VAR_U32_USER_VAR_E;
 	byte VAR_U32_USER_VAR_F;
+	byte VAR_U32_USER_VAR_G;
+	byte VAR_U32_USER_VAR_H;
 
 	byte VAR_REMOTE_START_SCRIPT;
 	byte VAR_NETWORK_AVAILABLE;
diff --git a/engines/scumm/he/logic/basketball.cpp b/engines/scumm/he/logic/basketball.cpp
deleted file mode 100644
index 2a82281ef77..00000000000
--- a/engines/scumm/he/logic/basketball.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * 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 3 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, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "scumm/he/intern_he.h"
-#include "scumm/he/logic_he.h"
-
-namespace Scumm {
-
-/**
- * Logic code for:
- *     Backyard Basketball
- */
-class LogicHEbasketball : public LogicHE {
-public:
-	LogicHEbasketball(ScummEngine_v90he *vm) : LogicHE(vm) {}
-
-	int versionID() override;
-	int32 dispatch(int op, int numArgs, int32 *args) override;
-
-private:
-	int op_1012();
-	int op_1050(int32 *args);
-	int op_1053();
-
-	// op_1050 loads court object data
-	enum CourtObjectType {
-		kObjectTypeBackboard = 1,
-		kObjectTypeRim = 2,
-		kObjectTypeOther = 3,
-		kObjectTypeFloor = 4
-	};
-
-	struct CourtObject {
-		Common::String name;
-		CourtObjectType type;
-		uint32 data[10];
-	};
-
-	Common::Array<CourtObject> _courtObjects;
-	uint32 _backboardObjectLeft, _backboardObjectRight;
-};
-
-int LogicHEbasketball::versionID() {
-	return 1;
-}
-
-int32 LogicHEbasketball::dispatch(int op, int numArgs, int32 *args) {
-	int res = 0;
-
-	switch (op) {
-	case 1001:
-		break;
-
-	case 1006:	// WORLD_TO_SCREEN_TRANSLATION
-		break;
-
-	case 1011:	// INIT_SCREEN_TRANSLATIONS
-		break;
-
-	case 1012:	// GET_COURT_DIMENSIONS
-		res = op_1012();
-		break;
-
-	case 1035:	// COMPUTE_POINTS_FOR_PIXELS
-		break;
-
-	case 1050:	// INIT_COURT
-		res = op_1050(args);
-		break;
-
-	case 1051:	// INIT_BALL
-		break;
-
-	case 1052:	// INIT_PLAYER
-		break;
-
-	case 1053:	// DEINIT_COURT
-		res = op_1053();
-		break;
-
-	case 1056:	// DETECT_BALL_COLLISION
-		break;
-
-	case 1057:	// DETECT_PLAYER_COLLISION
-		break;
-
-	case 1058:	// GET_LAST_BALL_COLLISION
-		break;
-
-	case 1060:	// INIT_VIRTUAL_BALL
-		break;
-
-	case 1064:	// RASIE_SHIELDS
-		break;
-
-	case 1067:	// IS_PLAYER_IN_BOUNDS
-		break;
-
-	case 1073:	// SET_BALL_LOCATION
-		break;
-
-	case 1075:	// SET_PLAYER_LOCATION
-		break;
-
-	case 1076:	// GET_PLAYER_LOCATION
-		break;
-
-	case 1080:	// IS_PLAYER_IN_GAME
-		break;
-
-	case 1081:	// IS_BALL_IN_GAME
-		break;
-
-	case 1090:	// UPDATE_CURSOR_POS
-		break;
-
-	case 1091:	// MAKE_CURSOR_STICKY
-		break;
-
-	case 1513:	// INIT_NETWORK_SYSTEM_KLUDGE
-		break;
-
-	default:
-		LogicHE::dispatch(op, numArgs, args);
-	}
-
-	return res;
-}
-
-int LogicHEbasketball::op_1012() {
-	writeScummVar(108, 12000);
-	writeScummVar(109, 8000);
-	writeScummVar(110, 760);
-	writeScummVar(111, 4000);
-	writeScummVar(112, 1600);
-	return 1;
-}
-
-int LogicHEbasketball::op_1050(int32 *args) {
-	// This function loads the court data
-	static const char *const courtNames[] = {
-		"Dobbaguchi", "Jocindas", "SandyFlats", "Queens",
-		"Park", "Scheffler", "Polk", "McMillan",
-		"CrownHill", "Memorial", "TechState", "Garden",
-		"Moon", "Barn"
-	};
-
-	Common::Path courtFileName = Common::Path(Common::String::format("data/courts/%s.cof", courtNames[args[0] - 1]));
-
-	Common::File file;
-	if (!file.open(courtFileName))
-		error("Could not open file '%s'", courtFileName.toString(Common::Path::kNativeSeparator).c_str());
-
-	debug(0, "Loading court data from '%s'", courtFileName.toString(Common::Path::kNativeSeparator).c_str());
-
-	// First, read in the header
-	file.readUint32LE(); // Header size (?)
-
-	char version[6];
-	file.read(version, 5);
-	version[5] = 0;
-
-	if (strcmp(version, "01.05"))
-		error("Invalid court version field: %s", version);
-
-	uint32 objectCount = file.readUint32LE();
-
-	for (uint32 i = 0; i < objectCount; i++) {
-		char nameBuf[100];
-		memset(nameBuf, 0, sizeof(nameBuf));
-
-		uint32 nameLength = file.readUint32LE();
-		assert(nameLength < sizeof(nameBuf) - 1);
-		file.read(nameBuf, nameLength);
-
-		CourtObject object;
-		object.name = nameBuf;
-		object.type = (CourtObjectType)file.readUint32LE();
-		for (uint32 j = 0; j < 10; j++)
-			object.data[j] = file.readUint32LE();
-
-		debug(1, "Found court object '%s' - Type %d", nameBuf, object.type);
-
-		// Store backboard object indices for later
-		if (object.type == kObjectTypeBackboard) {
-			if (object.data[7] + object.data[4] / 2 >= 6000)
-				_backboardObjectRight = i;
-			else
-				_backboardObjectLeft = i;
-		}
-
-		_courtObjects.push_back(object);
-	}
-
-	// TODO: Some other variables are initialized with constants here
-
-	return 1;
-}
-
-int LogicHEbasketball::op_1053() {
-	_courtObjects.clear();
-	// TODO: This also calls op_1065 with one argument (5)
-
-	return 1;
-}
-
-LogicHE *makeLogicHEbasketball(ScummEngine_v90he *vm) {
-	return new LogicHEbasketball(vm);
-}
-
-} // End of namespace Scumm
diff --git a/engines/scumm/he/logic/basketball_logic.cpp b/engines/scumm/he/logic/basketball_logic.cpp
new file mode 100644
index 00000000000..6cfa81fc87e
--- /dev/null
+++ b/engines/scumm/he/logic/basketball_logic.cpp
@@ -0,0 +1,688 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/logic_he.h"
+#include "scumm/he/basketball/basketball.h"
+#include "scumm/he/basketball/collision/bball_collision.h"
+#include "scumm/he/basketball/geo_translations.h"
+
+namespace Scumm {
+
+// Opcodes
+#define WORLD_TO_SCREEN_TRANSLATION           1006
+#define WORLD_TO_SCREEN_TRANSLATION_PARAMS       3
+#define SCREEN_TO_WORLD_TRANSLATION           1010
+#define SCREEN_TO_WORLD_TRANSLATION_PARAMS       2
+#define INIT_SCREEN_TRANSLATIONS              1011
+#define INIT_SCREEN_TRANSLATIONS_PARAMS          0
+#define GET_COURT_DIMENSIONS                  1012
+#define GET_COURT_DIMENSIONS_PARAMS              0
+
+#define COMPUTE_INITIAL_SHOT_VELOCITY         1030
+#define COMPUTE_INITIAL_SHOT_VELOCITY_PARAMS     4
+#define COMPUTE_TRAJECTORY_TO_TARGET          1031
+#define COMPUTE_TRAJECTORY_TO_TARGET_PARAMS      7
+#define COMPUTE_LAUNCH_TRAJECTORY             1032
+#define COMPUTE_LAUNCH_TRAJECTORY_PARAMS         6
+#define COMPUTE_ANGLE_OF_SHOT                 1033
+#define COMPUTE_ANGLE_OF_SHOT_PARAMS             2
+#define COMPUTE_ANGLE_OF_PASS                 1034
+#define COMPUTE_ANGLE_OF_PASS_PARAMS             4
+#define COMPUTE_POINTS_FOR_PIXELS             1035
+#define COMPUTE_POINTS_FOR_PIXELS_PARAMS         2
+#define COMPUTE_ANGLE_OF_BOUNCE_PASS          1036
+#define COMPUTE_ANGLE_OF_BOUNCE_PASS_PARAMS      5
+#define COMPUTE_BANK_SHOT_TARGET              1037
+#define COMPUTE_BANK_SHOT_TARGET_PARAMS          4
+#define COMPUTE_SWOOSH_TARGET                 1038
+#define COMPUTE_SWOOSH_TARGET_PARAMS             4
+#define DETECT_SHOT_MADE                      1039
+#define DETECT_SHOT_MADE_PARAMS                  9
+#define COMPUTE_ANGLE_BETWEEN_VECTORS         1040
+#define COMPUTE_ANGLE_BETWEEN_VECTORS_PARAMS     6
+#define HIT_MOVING_TARGET                     1041
+#define HIT_MOVING_TARGET_PARAMS                 7
+#define GET_PASS_TARGET                       1042
+#define GET_PASS_TARGET_PARAMS                   3
+#define DETECT_PASS_BLOCKER                   1043
+#define DETECT_PASS_BLOCKER_PARAMS               3
+#define GET_BALL_INTERCEPT                    1044
+#define GET_BALL_INTERCEPT_PARAMS                4
+
+#define INIT_COURT                            1050
+#define INIT_COURT_PARAMS                        1
+#define INIT_BALL                             1051
+#define INIT_BALL_PARAMS                         8
+#define INIT_PLAYER                           1052
+#define INIT_PLAYER_PARAMS                       7
+#define DEINIT_COURT                          1053
+#define DEINIT_COURT_PARAMS                      0
+#define DEINIT_BALL                           1054
+#define DEINIT_BALL_PARAMS                       0
+#define DEINIT_PLAYER                         1055
+#define DEINIT_PLAYER_PARAMS                     1
+#define DETECT_BALL_COLLISION                 1056
+#define DETECT_BALL_COLLISION_PARAMS             8
+#define DETECT_PLAYER_COLLISION               1057
+#define DETECT_PLAYER_COLLISION_PARAMS           8
+#define GET_LAST_BALL_COLLISION               1058
+#define GET_LAST_BALL_COLLISION_PARAMS           1
+#define GET_LAST_PLAYER_COLLISION             1059
+#define GET_LAST_PLAYER_COLLISION_PARAMS         1
+#define INIT_VIRTUAL_BALL                     1060
+#define INIT_VIRTUAL_BALL_PARAMS                 8
+#define DEINIT_VIRTUAL_BALL                   1061
+#define DEINIT_VIRTUAL_BALL_PARAMS               0
+#define PLAYER_OFF                            1062
+#define PLAYER_OFF_PARAMS                        1
+#define PLAYER_ON                             1063
+#define PLAYER_ON_PARAMS                         1
+#define RAISE_SHIELDS                         1064
+#define RAISE_SHIELDS_PARAMS                     1
+#define LOWER_SHIELDS                         1065
+#define LOWER_SHIELDS_PARAMS                     1
+#define FIND_PLAYER_CLOSEST_TO_BALL           1066
+#define FIND_PLAYER_CLOSEST_TO_BALL_PARAMS       1
+#define IS_PLAYER_IN_BOUNDS                   1067
+#define IS_PLAYER_IN_BOUNDS_PARAMS               1
+#define ARE_SHIELDS_CLEAR                     1068
+#define ARE_SHIELDS_CLEAR_PARAMS                 0
+#define SHIELD_PLAYER                         1069
+#define SHIELD_PLAYER_PARAMS                     2
+#define CLEAR_PLAYER_SHIELD                   1070
+#define CLEAR_PLAYER_SHIELD_PARAMS               1
+#define IS_BALL_IN_BOUNDS                     1071
+#define IS_BALL_IN_BOUNDS_PARAMS                 0 
+#define GET_AVOIDANCE_PATH                    1072
+#define GET_AVOIDANCE_PATH_PARAMS                4
+#define SET_BALL_LOCATION                     1073
+#define SET_BALL_LOCATION_PARAMS                 4
+#define GET_BALL_LOCATION                     1074
+#define GET_BALL_LOCATION_PARAMS                 1
+#define SET_PLAYER_LOCATION                   1075
+#define SET_PLAYER_LOCATION_PARAMS               4
+#define GET_PLAYER_LOCATION                   1076
+#define GET_PLAYER_LOCATION_PARAMS               1
+#define START_BLOCK                           1077
+#define START_BLOCK_PARAMS                       3
+#define HOLD_BLOCK                            1078
+#define HOLD_BLOCK_PARAMS                        1
+#define END_BLOCK                             1079
+#define END_BLOCK_PARAMS                         1
+#define IS_PLAYER_IN_GAME                     1080
+#define IS_PLAYER_IN_GAME_PARAMS                 1
+#define IS_BALL_IN_GAME                       1081
+#define IS_BALL_IN_GAME_PARAMS                   1
+
+#define UPDATE_CURSOR_POS                     1090
+#define UPDATE_CURSOR_POS_PARAMS                 2
+#define MAKE_CURSOR_STICKY                    1091
+#define MAKE_CURSOR_STICKY_PARAMS                2
+#define CURSOR_TRACK_MOVING_OBJECT            1092
+#define CURSOR_TRACK_MOVING_OBJECT_PARAMS        2
+#define GET_CURSOR_POSITION                   1093
+#define GET_CURSOR_POSITION_PARAMS               0
+
+#define AI_GET_OPEN_SPOT                      1100
+#define AI_GET_OPEN_SPOT_PARAMS                 10
+#define AI_GET_OPPONENTS_IN_CONE              1101
+#define AI_GET_OPPONENTS_IN_CONE_PARAMS          6
+
+#define U32_CLEAN_UP_OFF_HEAP	              1102
+#define U32_CLEAN_UP_OFF_HEAP_PARAMS          0
+
+#define DRAW_DEBUG_LINES                      1500
+#define DRAW_DEBUG_LINES_PARAMS                  0
+
+#define ADD_DEBUG_GEOM                        1501
+
+int LogicHEBasketball::versionID() {
+	return 1;
+}
+
+int32 LogicHEBasketball::dispatch(int cmdID, int paramCount, int32 *params) {
+	U32FltPoint3D flt3DPoint1, flt3DPoint2;
+	U32FltPoint2D flt2DPoint1, flt2DPoint2;
+	U32IntVector3D int3DVector1;
+	U32FltVector3D flt3DVector1, flt3DVector2;
+	U32FltVector2D flt2DVector1;
+	U32Sphere sphere1;
+
+	int retValue = 0;
+
+	switch (cmdID) {
+
+	case INIT_SCREEN_TRANSLATIONS:
+		assert(paramCount == INIT_SCREEN_TRANSLATIONS_PARAMS);
+
+		retValue = u32_userInitScreenTranslations();
+		break;
+
+	case WORLD_TO_SCREEN_TRANSLATION:
+		assert(paramCount == WORLD_TO_SCREEN_TRANSLATION_PARAMS);
+
+		flt3DPoint1.x = (float)params[0];
+		flt3DPoint1.y = (float)params[1];
+		flt3DPoint1.z = (float)params[2];
+
+		retValue = u32_userWorldToScreenTranslation(flt3DPoint1);
+		break;
+
+	case SCREEN_TO_WORLD_TRANSLATION:
+		assert(paramCount == SCREEN_TO_WORLD_TRANSLATION_PARAMS);
+
+		flt2DPoint1.x = (float)params[0];
+		flt2DPoint1.y = (float)params[1];
+
+		retValue = u32_userScreenToWorldTranslation(flt2DPoint1);
+		break;
+
+	case GET_COURT_DIMENSIONS:
+		assert(paramCount == GET_COURT_DIMENSIONS_PARAMS);
+
+		retValue = u32_userGetCourtDimensions();
+		break;
+
+	case COMPUTE_TRAJECTORY_TO_TARGET:
+		assert(paramCount == COMPUTE_TRAJECTORY_TO_TARGET_PARAMS);
+
+		flt3DPoint1.x = (float)params[0];
+		flt3DPoint1.y = (float)params[1];
+		flt3DPoint1.z = (float)params[2];
+
+		flt3DPoint2.x = (float)params[3];
+		flt3DPoint2.y = (float)params[4];
+		flt3DPoint2.z = (float)params[5];
+
+		retValue = u32_userComputeTrajectoryToTarget(flt3DPoint1, flt3DPoint2, params[6]);
+		break;
+
+	case COMPUTE_LAUNCH_TRAJECTORY:
+		assert(paramCount == COMPUTE_LAUNCH_TRAJECTORY_PARAMS);
+
+		flt2DPoint1.x = (float)params[0];
+		flt2DPoint1.y = (float)params[1];
+
+		flt2DPoint2.x = (float)params[2];
+		flt2DPoint2.y = (float)params[3];
+
+		retValue = u32_userComputeLaunchTrajectory(flt2DPoint1, flt2DPoint2, params[4], params[5]);
+		break;
+
+	case COMPUTE_ANGLE_BETWEEN_VECTORS:
+		assert(paramCount == COMPUTE_ANGLE_BETWEEN_VECTORS_PARAMS);
+
+		flt3DVector1.x = (float)params[0];
+		flt3DVector1.y = (float)params[1];
+		flt3DVector1.z = (float)params[2];
+
+		flt3DVector2.x = (float)params[3];
+		flt3DVector2.y = (float)params[4];
+		flt3DVector2.z = (float)params[5];
+
+		retValue = u32_userComputeAngleBetweenVectors(flt3DVector1, flt3DVector2);
+		break;
+
+	case COMPUTE_INITIAL_SHOT_VELOCITY:
+		assert(paramCount == COMPUTE_INITIAL_SHOT_VELOCITY_PARAMS);
+		retValue = u32_userComputeInitialShotVelocity(params[0], params[1], params[2], params[3]);
+		break;
+
+	case COMPUTE_ANGLE_OF_SHOT:
+		assert(paramCount == COMPUTE_ANGLE_OF_SHOT_PARAMS);
+		retValue = u32_userComputeAngleOfShot(params[0], params[1]);
+		break;
+
+	case COMPUTE_ANGLE_OF_PASS:
+		assert(paramCount == COMPUTE_ANGLE_OF_PASS_PARAMS);
+		retValue = u32_userComputeAngleOfPass(params[0], params[1], params[2], params[3]);
+		break;
+
+	case COMPUTE_ANGLE_OF_BOUNCE_PASS:
+		assert(paramCount == COMPUTE_ANGLE_OF_BOUNCE_PASS_PARAMS);
+		retValue = u32_userComputeAngleOfBouncePass(params[0], params[1], params[2], params[3], params[4]);
+		break;
+
+	case HIT_MOVING_TARGET:
+		assert(paramCount == HIT_MOVING_TARGET_PARAMS);
+
+		flt2DPoint1.x = (float)params[0];
+		flt2DPoint1.y = (float)params[1];
+		flt2DPoint2.x = (float)params[2];
+		flt2DPoint2.y = (float)params[3];
+		flt2DVector1.x = (float)params[4];
+		flt2DVector1.y = (float)params[5];
+
+		retValue = u32_userHitMovingTarget(flt2DPoint1, flt2DPoint2, flt2DVector1, params[6]);
+		break;
+
+	case GET_PASS_TARGET:
+		assert(paramCount == GET_PASS_TARGET_PARAMS);
+
+		flt3DVector1.x = (float)params[1];
+		flt3DVector1.y = (float)params[2];
+
+		retValue = u32_userGetPassTarget(params[0], flt3DVector1);
+		break;
+
+	case DETECT_PASS_BLOCKER:
+		assert(paramCount == DETECT_PASS_BLOCKER_PARAMS);
+
+		flt3DVector1.x = (float)params[1];
+		flt3DVector1.y = (float)params[2];
+
+		retValue = u32_userDetectPassBlocker(params[0], flt3DVector1);
+		break;
+
+	case GET_BALL_INTERCEPT:
+		assert(paramCount == GET_BALL_INTERCEPT_PARAMS);
+
+		retValue = u32_userGetBallIntercept(params[0], params[1], params[2], params[3]);
+		break;
+
+	case COMPUTE_POINTS_FOR_PIXELS:
+		assert(paramCount == COMPUTE_POINTS_FOR_PIXELS_PARAMS);
+		retValue = u32_userComputePointsForPixels(params[0], params[1]);
+		break;
+
+	case COMPUTE_BANK_SHOT_TARGET:
+		assert(paramCount == COMPUTE_BANK_SHOT_TARGET_PARAMS);
+
+		flt3DPoint1.x = (float)params[0];
+		flt3DPoint1.y = (float)params[1];
+		flt3DPoint1.z = (float)params[2];
+
+		retValue = u32_userComputeBankShotTarget(flt3DPoint1, params[3]);
+		break;
+
+	case COMPUTE_SWOOSH_TARGET:
+		assert(paramCount == COMPUTE_SWOOSH_TARGET_PARAMS);
+
+		flt3DPoint1.x = (float)params[0];
+		flt3DPoint1.y = (float)params[1];
+		flt3DPoint1.z = (float)params[2];
+
+		retValue = u32_userComputeSwooshTarget(flt3DPoint1, params[3]);
+		break;
+
+	case INIT_COURT:
+		assert(paramCount == INIT_COURT_PARAMS);
+		retValue = u32_userInitCourt(params[0]);
+		break;
+
+	case DEINIT_COURT:
+		assert(paramCount == DEINIT_COURT_PARAMS);
+		retValue = u32_userDeinitCourt();
+		break;
+
+	case INIT_BALL:
+		assert(paramCount == INIT_BALL_PARAMS);
+
+		flt3DPoint1.x = (float)params[0];
+		flt3DPoint1.y = (float)params[1];
+		flt3DPoint1.z = (float)params[2];
+
+		flt3DVector1.x = (float)params[3];
+		flt3DVector1.y = (float)params[4];
+		flt3DVector1.z = (float)params[5];
+
+
+		debug("Basketball INIT (%.6f,%.6f,%.6f)", flt3DPoint1.x, flt3DPoint1.y, flt3DPoint1.z);
+		retValue = u32_userInitBall(flt3DPoint1, flt3DVector1, params[6], params[7]);
+		break;
+
+	case DEINIT_BALL:
+		assert(paramCount == DEINIT_BALL_PARAMS);
+
+		retValue = u32_userDeinitBall();
+		break;
+
+	case INIT_VIRTUAL_BALL:
+		assert(paramCount == INIT_VIRTUAL_BALL_PARAMS);
+
+		flt3DPoint1.x = (float)params[0];
+		flt3DPoint1.y = (float)params[1];
+		flt3DPoint1.z = (float)params[2];
+
+		flt3DVector1.x = (float)params[3];
+		flt3DVector1.y = (float)params[4];
+		flt3DVector1.z = (float)params[5];
+
+		retValue = u32_userInitVirtualBall(flt3DPoint1, flt3DVector1, params[6], params[7]);
+		break;
+
+	case DEINIT_VIRTUAL_BALL:
+		assert(paramCount == DEINIT_VIRTUAL_BALL_PARAMS);
+
+		retValue = u32_userDeinitVirtualBall();
+		break;
+
+	case INIT_PLAYER:
+		assert(paramCount == INIT_PLAYER_PARAMS);
+
+		flt3DPoint1.x = (float)params[1];
+		flt3DPoint1.y = (float)params[2];
+		flt3DPoint1.z = (float)params[3];
+
+		retValue = u32_userInitPlayer(params[0], flt3DPoint1, params[4], params[5], (params[6] != 0));
+		break;
+
+	case DEINIT_PLAYER:
+		assert(paramCount == DEINIT_PLAYER_PARAMS);
+		retValue = u32_userDeinitPlayer(params[0]);
+		break;
+
+	case PLAYER_OFF:
+		assert(paramCount == PLAYER_OFF_PARAMS);
+		retValue = u32_userPlayerOff(params[0]);
+		break;
+
+	case PLAYER_ON:
+		assert(paramCount == PLAYER_ON_PARAMS);
+		retValue = u32_userPlayerOn(params[0]);
+		break;
+
+	case SET_BALL_LOCATION:
+		assert(paramCount == SET_BALL_LOCATION_PARAMS);
+
+		flt3DPoint1.x = (float)params[0];
+		flt3DPoint1.y = (float)params[1];
+		flt3DPoint1.z = (float)params[2];
+
+		debug(3, "Basketball SET_BALL_LOCATION (%.6f,%.6f,%.6f)",
+			  flt3DPoint1.x, flt3DPoint1.y, flt3DPoint1.z);
+
+		_vm->_basketball->_court->getBallPtr(params[3])->center = flt3DPoint1;
+
+		retValue = 1;
+		break;
+
+	case GET_BALL_LOCATION:
+		assert(paramCount == GET_BALL_LOCATION_PARAMS);
+
+		flt3DPoint1 = _vm->_basketball->_court->getBallPtr(params[0])->center;
+
+		debug(3, "Basketball GET_BALL_LOCATION (%.6f,%.6f,%.6f), (converted as %d,%d,%d)",
+			flt3DPoint1.x, flt3DPoint1.y, flt3DPoint1.z,
+			_vm->_basketball->u32FloatToInt(flt3DPoint1.x),
+			_vm->_basketball->u32FloatToInt(flt3DPoint1.y),
+			_vm->_basketball->u32FloatToInt(flt3DPoint1.z));
+
+		writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(flt3DPoint1.x));
+		writeScummVar(_vm1->VAR_U32_USER_VAR_B, _vm->_basketball->u32FloatToInt(flt3DPoint1.y));
+		writeScummVar(_vm1->VAR_U32_USER_VAR_C, _vm->_basketball->u32FloatToInt(flt3DPoint1.z));
+
+		retValue = 1;
+		break;
+
+	case SET_PLAYER_LOCATION:
+		assert(paramCount == SET_PLAYER_LOCATION_PARAMS);
+
+		flt3DPoint1.x = (float)params[0];
+		flt3DPoint1.y = (float)params[1];
+		flt3DPoint1.z = (float)params[2];
+
+		flt3DPoint1.z += (_vm->_basketball->_court->getPlayerPtr(params[3])->height / 2);
+		_vm->_basketball->_court->getPlayerPtr(params[3])->center = flt3DPoint1;
+
+		retValue = 1;
+		break;
+
+	case GET_PLAYER_LOCATION:
+		assert(paramCount == GET_PLAYER_LOCATION_PARAMS);
+
+		flt3DPoint1 = _vm->_basketball->_court->getPlayerPtr(params[0])->center;
+		flt3DPoint1.z -= (_vm->_basketball->_court->getPlayerPtr(params[0])->height / 2);
+
+		writeScummVar(_vm1->VAR_U32_USER_VAR_A, _vm->_basketball->u32FloatToInt(flt3DPoint1.x));
+		writeScummVar(_vm1->VAR_U32_USER_VAR_B, _vm->_basketball->u32FloatToInt(flt3DPoint1.y));
+		writeScummVar(_vm1->VAR_U32_USER_VAR_C, _vm->_basketball->u32FloatToInt(flt3DPoint1.z));
+
+		retValue = 1;
+		break;
+
+	case DETECT_BALL_COLLISION:
+		assert(paramCount == DETECT_BALL_COLLISION_PARAMS);
+
+		flt3DPoint1.x = (float)params[0];
+		flt3DPoint1.y = (float)params[1];
+		flt3DPoint1.z = (float)params[2];
+
+		flt3DVector1.x = (float)params[3];
+		flt3DVector1.y = (float)params[4];
+		flt3DVector1.z = (float)params[5];
+		debug(3, "Basketball DETECT_BALL_COLLISION will be put at (%.6f,%.6f,%.6f)", flt3DPoint1.x, flt3DPoint1.y, flt3DPoint1.z);
+
+		retValue = u32_userDetectBallCollision(flt3DPoint1, flt3DVector1, params[6], params[7]);
+		break;
+
+	case DETECT_PLAYER_COLLISION:
+		assert(paramCount == DETECT_PLAYER_COLLISION_PARAMS);
+
+		flt3DPoint1.x = (float)params[1];
+		flt3DPoint1.y = (float)params[2];
+		flt3DPoint1.z = (float)params[3];
+
+		flt3DVector1.x = (float)params[4];
+		flt3DVector1.y = (float)params[5];
+		flt3DVector1.z = (float)params[6];
+
+		retValue = u32_userDetectPlayerCollision(params[0], flt3DPoint1, flt3DVector1, (params[7] != 0));
+		break;
+
+	case GET_LAST_BALL_COLLISION:
+		assert(paramCount == GET_LAST_BALL_COLLISION_PARAMS);
+
+		retValue = u32_userGetLastBallCollision(params[0]);
+		break;
+
+	case GET_LAST_PLAYER_COLLISION:
+		assert(paramCount == GET_LAST_PLAYER_COLLISION_PARAMS);
+
+		retValue = u32_userGetLastPlayerCollision(params[0]);
+		break;
+
+	case DETECT_SHOT_MADE:
+		assert(paramCount == DETECT_SHOT_MADE_PARAMS);
+
+		sphere1.center.x = (float)params[0];
+		sphere1.center.y = (float)params[1];
+		sphere1.center.z = (float)params[2];
+		sphere1.radius = (float)params[6];
+
+		int3DVector1.x = params[3];
+		int3DVector1.y = params[4];
+		int3DVector1.z = params[5];
+		debug(3, "Basketball DETECT_SHOT_MADE sphere (%.6f,%.6f,%.6f), vector (%d,%d,%d)",
+			flt3DPoint1.x, flt3DPoint1.y, flt3DPoint1.z,
+			int3DVector1.x, int3DVector1.y, int3DVector1.z);
+
+		retValue = u32_userDetectShotMade(sphere1, int3DVector1, params[7], params[8]);
+		break;
+
+	case RAISE_SHIELDS:
+		assert(paramCount == RAISE_SHIELDS_PARAMS);
+
+		retValue = u32_userRaiseShields(params[0]);
+		break;
+
+	case LOWER_SHIELDS:
+		assert(paramCount == LOWER_SHIELDS_PARAMS);
+
+		retValue = u32_userLowerShields(params[0]);
+		break;
+
+	case FIND_PLAYER_CLOSEST_TO_BALL:
+		assert((paramCount == 0) || (paramCount == FIND_PLAYER_CLOSEST_TO_BALL_PARAMS));
+
+		if (paramCount == 0) {
+			retValue = u32_userGetPlayerClosestToBall();
+		} else {
+			retValue = u32_userGetPlayerClosestToBall(params[0]);
+		}
+
+		break;
+
+	case IS_PLAYER_IN_BOUNDS:
+		assert(paramCount == IS_PLAYER_IN_BOUNDS_PARAMS);
+
+		retValue = u32_userIsPlayerInBounds(params[0]);
+		break;
+
+	case IS_BALL_IN_BOUNDS:
+		assert(paramCount == IS_BALL_IN_BOUNDS_PARAMS);
+
+		retValue = u32_userIsBallInBounds();
+		break;
+
+	case ARE_SHIELDS_CLEAR:
+		assert(paramCount == ARE_SHIELDS_CLEAR_PARAMS);
+
+		retValue = u32_userAreShieldsClear();
+		break;
+
+	case SHIELD_PLAYER:
+		assert(paramCount == SHIELD_PLAYER_PARAMS);
+
+		retValue = u32_userShieldPlayer(params[0], params[1]);
+		break;
+
+	case CLEAR_PLAYER_SHIELD:
+		assert(paramCount == CLEAR_PLAYER_SHIELD_PARAMS);
+
+		retValue = u32_userClearPlayerShield(params[0]);
+		break;
+
+	case GET_AVOIDANCE_PATH:
+		assert(paramCount == GET_AVOIDANCE_PATH_PARAMS);
+
+		flt2DPoint1.x = (float)params[1];
+		flt2DPoint1.y = (float)params[2];
+
+		retValue = u32_userGetAvoidancePath(params[0], flt2DPoint1, (EAvoidanceType)params[3]);
+		break;
+
+	case START_BLOCK:
+		assert(paramCount == START_BLOCK_PARAMS);
+		assert((FIRST_PLAYER <= params[0]) && (params[0] <= LAST_PLAYER));
+
+		(_vm->_basketball->_court->getPlayerPtr(params[0]))->startBlocking(params[1], params[2]);
+
+		retValue = 1;
+		break;
+
+	case HOLD_BLOCK:
+		assert(paramCount == HOLD_BLOCK_PARAMS);
+		assert((FIRST_PLAYER <= params[0]) && (params[0] <= LAST_PLAYER));
+
+		(_vm->_basketball->_court->getPlayerPtr(params[0]))->holdBlocking();
+
+		retValue = 1;
+		break;
+
+	case END_BLOCK:
+		assert(paramCount == END_BLOCK_PARAMS);
+		assert((FIRST_PLAYER <= params[0]) && (params[0] <= LAST_PLAYER));
+
+		(_vm->_basketball->_court->getPlayerPtr(params[0]))->endBlocking();
+
+		retValue = 1;
+		break;
+
+	case IS_PLAYER_IN_GAME:
+		assert(paramCount == IS_PLAYER_IN_GAME_PARAMS);
+		assert((FIRST_PLAYER <= params[0]) && (params[0] <= LAST_PLAYER));
+
+		writeScummVar(_vm1->VAR_U32_USER_VAR_A, ((_vm->_basketball->_court->getPlayerPtr(params[0]))->_playerIsInGame) ? 1 : 0);
+
+		retValue = 1;
+		break;
+
+	case IS_BALL_IN_GAME:
+		assert(paramCount == IS_BALL_IN_GAME_PARAMS);
+
+		writeScummVar(_vm1->VAR_U32_USER_VAR_A, ((_vm->_basketball->_court->getBallPtr(params[0]))->_ignore) ? 0 : 1);
+
+		retValue = 1;
+		break;
+
+	case UPDATE_CURSOR_POS:
+		assert(paramCount == UPDATE_CURSOR_POS_PARAMS);
+
+		retValue = u32_userUpdateCursorPos(params[0], params[1]);
+		break;
+
+	case MAKE_CURSOR_STICKY:
+		assert(paramCount == MAKE_CURSOR_STICKY_PARAMS);
+
+		retValue = u32_userMakeCursorSticky(params[0], params[1]);
+		break;
+
+	case CURSOR_TRACK_MOVING_OBJECT:
+		assert(paramCount == CURSOR_TRACK_MOVING_OBJECT_PARAMS);
+
+		retValue = u32_userCursorTrackMovingObject(params[0], params[1]);
+		break;
+
+	case GET_CURSOR_POSITION:
+		assert(paramCount == GET_CURSOR_POSITION_PARAMS);
+
+		retValue = u32_userGetCursorPos();
+		break;
+
+	case AI_GET_OPEN_SPOT:
+		assert(paramCount == AI_GET_OPEN_SPOT_PARAMS);
+		retValue = u32_userGetOpenSpot(
+			params[0],
+			U32FltVector2D((float)params[1], (float)params[2]),
+			U32FltVector2D((float)params[3], (float)params[4]),
+			U32FltVector2D((float)params[5], (float)params[6]),
+			(params[7] != 0),
+			U32FltVector2D((float)params[8], (float)params[9])); 
+		break;
+
+	case AI_GET_OPPONENTS_IN_CONE:
+		assert(paramCount == AI_GET_OPPONENTS_IN_CONE_PARAMS);
+		retValue = _vm->_basketball->numOpponentsInCone(
+			params[0],
+			(((float)params[1]) / 65536),
+			U32FltVector2D((float)params[2], (float)params[3]),
+			U32FltVector2D((float)params[4], (float)params[5]));
+		break;
+
+	case U32_CLEAN_UP_OFF_HEAP:
+		// No-op
+		retValue = 1;
+		break;
+
+	default:
+		break;
+	}
+
+	return retValue;
+}
+
+LogicHE *makeLogicHEbasketball(ScummEngine_v100he *vm) {
+	return new LogicHEBasketball(vm);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/logic_he.cpp b/engines/scumm/he/logic_he.cpp
index cd0f93f201b..0cd2afc316e 100644
--- a/engines/scumm/he/logic_he.cpp
+++ b/engines/scumm/he/logic_he.cpp
@@ -98,7 +98,7 @@ LogicHE *LogicHE::makeLogicHE(ScummEngine_v90he *vm) {
 		return makeLogicHEbaseball2001(vm);
 
 	case GID_BASKETBALL:
-		return makeLogicHEbasketball(vm);
+		return makeLogicHEbasketball((ScummEngine_v100he *)vm);
 
 	case GID_MOONBASE:
 		return makeLogicHEmoonbase((ScummEngine_v100he *)vm);
diff --git a/engines/scumm/he/logic_he.h b/engines/scumm/he/logic_he.h
index abadd87ee6a..d0293d3f4cb 100644
--- a/engines/scumm/he/logic_he.h
+++ b/engines/scumm/he/logic_he.h
@@ -25,6 +25,9 @@
 #include "scumm/resource.h"
 #include "scumm/scumm.h"
 #include "scumm/he/wiz_he.h"
+#include "scumm/he/basketball/basketball.h"
+#include "scumm/he/basketball/collision/bball_collision_support_obj.h"
+#include "scumm/he/basketball/obstacle_avoidance.h"
 
 namespace Scumm {
 
@@ -78,9 +81,93 @@ LogicHE *makeLogicHEfootball(ScummEngine_v90he *vm);
 LogicHE *makeLogicHEfootball2002(ScummEngine_v90he *vm);
 LogicHE *makeLogicHEsoccer(ScummEngine_v90he *vm);
 LogicHE *makeLogicHEbaseball2001(ScummEngine_v90he *vm);
-LogicHE *makeLogicHEbasketball(ScummEngine_v90he *vm);
+LogicHE *makeLogicHEbasketball(ScummEngine_v100he *vm);
 LogicHE *makeLogicHEmoonbase(ScummEngine_v100he *vm);
 
+
+// Declarations for Basketball logic live here
+// instead of in basketball_logic.cpp since
+// they are defined in multiple files...
+class LogicHEBasketball : public LogicHE {
+public:
+	LogicHEBasketball(ScummEngine_v100he *vm) : LogicHE(vm) { _vm1 = vm; }
+
+	int versionID() override;
+	int32 dispatch(int op, int numArgs, int32 *args) override;
+
+	float _courtAngle = 0.0F;
+	float _yTranslationA = 0.0F;
+	float _yTranslationB = 0.0F;
+	float _yTranslationC = 0.0F;
+	float _yRevTranslationA = 0.0F;
+	float _yRevTranslationB = 0.0F;
+	float _yRevTranslationC = 0.0F;
+	int _topScalingPointCutoff = 0;
+	int _bottomScalingPointCutoff = 0;
+
+protected:
+	int u32_userInitCourt(int courtID);
+	int u32_userInitBall(U32FltPoint3D &ballLocation, U32FltVector3D &bellVelocity, int radius, int ballID);
+	int u32_userInitVirtualBall(U32FltPoint3D &ballLocation, U32FltVector3D &bellVelocity, int radius, int ballID);
+	int u32_userInitPlayer(int playerID, U32FltPoint3D &playerLocation, int height, int radius, bool bPlayerIsInGame);
+	int u32_userPlayerOff(int playerID);
+	int u32_userPlayerOn(int playerID);
+	int u32_userDetectBallCollision(U32FltPoint3D &ballLocation, U32FltVector3D &ballVector, int recordCollision, int ballID);
+	int u32_userDetectPlayerCollision(int playerID, U32FltPoint3D &playerLocation, U32FltVector3D &playerVector, bool bPlayerHasBall);
+	int u32_userGetLastBallCollision(int ballID);
+	int u32_userGetLastPlayerCollision(int playerID);
+	int u32_userDeinitCourt();
+	int u32_userDeinitBall();
+	int u32_userDeinitVirtualBall();
+	int u32_userDeinitPlayer(int playerID);
+
+	int u32_userInitScreenTranslations(void);
+	int u32_userWorldToScreenTranslation(const U32FltPoint3D &worldPoint);
+	int u32_userScreenToWorldTranslation(const U32FltPoint2D &screenPoint);
+	int u32_userGetCourtDimensions();
+	int u32_userComputePointsForPixels(int pixels, int yPos);
+
+	int u32_userComputeTrajectoryToTarget(const U32FltPoint3D &sourcePoint, const U32FltPoint3D &targetPoint, int speed);
+	int u32_userComputeLaunchTrajectory(const U32FltPoint2D &sourcePoint, const U32FltPoint2D &targetPoint, int launchAngle, int iVelocity);
+	int u32_userComputeAngleBetweenVectors(const U32FltVector3D &vector1, const U32FltVector3D &vector2);
+
+	// Court shields
+	int u32_userRaiseShields(int shieldID);
+	int u32_userLowerShields(int shieldID);
+	int u32_userAreShieldsClear();
+
+	// Player shields
+	int u32_userShieldPlayer(int playerID, int shieldRadius);
+	int u32_userClearPlayerShield(int playerID);
+
+	int u32_userComputeAngleOfShot(int hDist, int vDist);
+	int u32_userComputeBankShotTarget(U32FltPoint3D basketLoc, int ballRadius);
+	int u32_userComputeSwooshTarget(const U32FltPoint3D &basketLoc, int ballRadius);
+	int u32_userComputeInitialShotVelocity(int theta, int hDist, int vDist, int gravity);
+	int u32_userDetectShotMade(const U32Sphere &basketball, const U32IntVector3D &ballVector, int gravity, int whichBasket);
+	int u32_userComputeAngleOfPass(int iVelocity, int hDist, int vDist, int gravity);
+	int u32_userComputeAngleOfBouncePass(int iVelocity, int hDist, int currentZ, int destZ, int gravity);
+	int u32_userHitMovingTarget(U32FltPoint2D sourcePlayer, U32FltPoint2D targetPlayer, U32FltVector2D targetVelocity, int passSpeed);
+	int u32_userGetPassTarget(int playerID, const U32FltVector3D &aimVector);
+	int u32_userDetectPassBlocker(int playerID, const U32FltPoint3D &targetPoint);
+	int u32_userGetBallIntercept(int playerID, int ballID, int playerSpeed, int gravity);
+	int u32_userGetAvoidancePath(int playerID, const U32FltPoint2D &targetLocation, EAvoidanceType type);
+
+	int u32_userGetPlayerClosestToBall(int teamIndex);
+	int u32_userGetPlayerClosestToBall();
+	int u32_userIsPlayerInBounds(int playerID);
+	int u32_userIsBallInBounds();
+	int u32_userGetOpenSpot(int whichPlayer, U32FltVector2D upperLeft, U32FltVector2D lowerRight, U32FltVector2D from, bool attract, U32FltVector2D attract_point);
+
+	int u32_userUpdateCursorPos(int xScrollVal, int yScrollVal);
+	int u32_userMakeCursorSticky(int lastCursorX, int lastCursorY);
+	int u32_userCursorTrackMovingObject(int xChange, int yChange);
+	int u32_userGetCursorPos();
+
+private:
+	ScummEngine_v100he *_vm1;
+};
+
 } // End of namespace Scumm
 
 #endif
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 3d94bd012a6..d5955da6515 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -142,6 +142,26 @@ endif
 ifdef ENABLE_HE
 MODULE_OBJS += \
 	he/animation_he.o \
+	he/basketball/ai.o \
+	he/basketball/basketball.o \
+	he/basketball/collision/bball_collision.o \
+	he/basketball/collision/bball_collision_basketball.o \
+	he/basketball/collision/bball_collision_box.o \
+	he/basketball/collision/bball_collision_cylinder.o \
+	he/basketball/collision/bball_collision_node.o \
+	he/basketball/collision/bball_collision_object.o \
+	he/basketball/collision/bball_collision_player.o \
+	he/basketball/collision/bball_collision_shields.o \
+	he/basketball/collision/bball_collision_sphere.o \
+	he/basketball/collision/bball_collision_stack.o \
+	he/basketball/collision/bball_collision_tree.o \
+	he/basketball/court.o \
+	he/basketball/cursor.o \
+	he/basketball/geo_translation.o \
+	he/basketball/obstacle_avoidance.o \
+	he/basketball/passing.o \
+	he/basketball/shooting.o \
+	he/basketball/trajectory.o \
 	he/cup_player_he.o \
 	he/gfx_comp/aux_comp.o \
 	he/gfx_comp/mrle_comp.o \
@@ -161,7 +181,7 @@ MODULE_OBJS += \
 	he/wizwarp_he.o \
 	he/localizer.o \
 	he/logic/baseball2001.o \
-	he/logic/basketball.o \
+	he/logic/basketball_logic.o \
 	he/logic/football.o \
 	he/logic/funshop.o \
 	he/logic/moonbase_logic.o \
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index 19b93764c88..0b9f01d9530 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -70,7 +70,7 @@ struct SaveInfoSection {
 
 #define SaveInfoSectionSize (4+4+4 + 4+4 + 4+2)
 
-#define CURRENT_VER 119
+#define CURRENT_VER 120
 #define INFOSECTION_VERSION 2
 
 #pragma mark -
@@ -1753,7 +1753,8 @@ void ScummEngine::saveLoadWithSerializer(Common::Serializer &s) {
 	// Save/load misc stuff
 	//
 	s.syncArray(_verbs, _numVerbs, (_game.version < 7 && _language != Common::HE_ISR) ? syncWithSerializerDef : syncWithSerializerV7orISR);
-	s.syncArray(vm.nest, 16, syncWithSerializer);
+	s.syncArray(vm.nest, 16, syncWithSerializer, VER(0), VER(119));
+	s.syncArray(vm.nest, kMaxScriptNestingHE, syncWithSerializer, VER(120));
 	s.syncArray(_sentence, 6, syncWithSerializer);
 	s.syncArray(_string, 6, syncWithSerializer);
 	s.syncArray(_colorCycle, 16, syncWithSerializer);
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index 0b859c5071c..b8c5b2ae99b 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -335,7 +335,8 @@ void ScummEngine::runScriptNested(int script) {
 
 	updateScriptPtr();
 
-	if (vm.numNestedScripts >= kMaxScriptNesting)
+	// Backyard Basketball is one of the games which requires more than 15 nested scripts
+	if (vm.numNestedScripts >= (_game.heversion >= 99 ? kMaxScriptNestingHE : kMaxScriptNesting))
 		error("Too many nested scripts");
 
 	nest = &vm.nest[vm.numNestedScripts];
diff --git a/engines/scumm/script.h b/engines/scumm/script.h
index bf5b0223c12..195c913233c 100644
--- a/engines/scumm/script.h
+++ b/engines/scumm/script.h
@@ -113,7 +113,8 @@ enum {
 	/**
 	 * The maximal 'nesting' level for scripts.
 	 */
-	kMaxScriptNesting = 15
+	kMaxScriptNesting = 15,
+	kMaxScriptNestingHE = 40
 };
 
 struct VirtualMachineState {
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 46e6920fb78..3f4171ace9a 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -72,6 +72,7 @@
 #include "scumm/players/player_he.h"
 #include "scumm/resource.h"
 #include "scumm/he/resource_he.h"
+#include "scumm/he/basketball/basketball.h"
 #include "scumm/he/moonbase/moonbase.h"
 #include "scumm/scumm_v0.h"
 #include "scumm/scumm_v8.h"
@@ -667,7 +668,8 @@ ScummEngine_v6::ScummEngine_v6(OSystem *syst, const DetectorResult &dr)
 ScummEngine_v60he::ScummEngine_v60he(OSystem *syst, const DetectorResult &dr)
 	: ScummEngine_v6(syst, dr) {
 #ifdef ENABLE_HE
-	_moonbase = 0;
+	_moonbase = nullptr;
+	_basketball = nullptr;
 #endif
 
 	memset(_hInFileTable, 0, sizeof(_hInFileTable));
@@ -824,16 +826,22 @@ ScummEngine_v100he::ScummEngine_v100he(OSystem *syst, const DetectorResult &dr)
 	if (_game.id == GID_MOONBASE)
 		_moonbase = new Moonbase(this);
 
+	if (_game.id == GID_BASKETBALL)
+		_basketball = new Basketball(this);
+
 	VAR_U32_USER_VAR_A = 0xFF;
 	VAR_U32_USER_VAR_B = 0xFF;
 	VAR_U32_USER_VAR_C = 0xFF;
 	VAR_U32_USER_VAR_D = 0xFF;
 	VAR_U32_USER_VAR_E = 0xFF;
 	VAR_U32_USER_VAR_F = 0xFF;
+	VAR_U32_USER_VAR_G = 0xFF;
+	VAR_U32_USER_VAR_H = 0xFF;
 }
 
 ScummEngine_v100he::~ScummEngine_v100he() {
 	delete _moonbase;
+	delete _basketball;
 }
 
 ScummEngine_vCUPhe::ScummEngine_vCUPhe(OSystem *syst, const DetectorResult &dr) : Engine(syst){
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index e96e3c98bfb..c249740d960 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -524,6 +524,7 @@ class ScummEngine : public Engine, public Common::Serializable {
 	friend class MacGuiImpl;
 	friend class MacIndy3Gui;
 	friend class MacLoomGui;
+	friend class LogicHEBasketball;
 
 public:
 	/* Put often used variables at the top.
diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp
index 75e4372bf6e..9eaa29c942f 100644
--- a/engines/scumm/vars.cpp
+++ b/engines/scumm/vars.cpp
@@ -368,12 +368,17 @@ void ScummEngine_v100he::setupScummVars() {
 		VAR_REMOTE_START_SCRIPT = 98;
 		VAR_NETWORK_AVAILABLE = 100;
 		VAR_NETWORK_RECEIVE_ARRAY_SCRIPT = 101;
+	}
+
+	if (_game.id == GID_MOONBASE || _game.id == GID_BASKETBALL) {
 		VAR_U32_USER_VAR_A = 108;
 		VAR_U32_USER_VAR_B = 109;
 		VAR_U32_USER_VAR_C = 110;
 		VAR_U32_USER_VAR_D = 111;
 		VAR_U32_USER_VAR_E = 112;
 		VAR_U32_USER_VAR_F = 113;
+		VAR_U32_USER_VAR_G = 114;
+		VAR_U32_USER_VAR_H = 115;
 	}
 }
 #endif


Commit: 8d23d0799dcacdbc68a13094174f9c5dadb6b3ca
    https://github.com/scummvm/scummvm/commit/8d23d0799dcacdbc68a13094174f9c5dadb6b3ca
Author: AndywinXp (andywinxp at gmail.com)
Date: 2024-09-16T13:01:49+03:00

Commit Message:
SCUMM: AKOS: Workaround invalid memory access during scaling

Changed paths:
    engines/scumm/akos.cpp


diff --git a/engines/scumm/akos.cpp b/engines/scumm/akos.cpp
index 04a83cfed39..9a14f5889d8 100644
--- a/engines/scumm/akos.cpp
+++ b/engines/scumm/akos.cpp
@@ -503,7 +503,7 @@ void AkosRenderer::byleRLEDecode(ByleRLEData &dataBlock) {
 	color = dataBlock.repColor;
 	height = _height;
 
-	scaleytab = &dataBlock.scaleTable[dataBlock.scaleYIndex];
+	scaleytab = &dataBlock.scaleTable[MAX<int>(0, dataBlock.scaleYIndex)]; // Avoid invalid mem reads in Basketball...
 	maskbit = revBitMask(dataBlock.x & 7);
 	mask = _vm->getMaskBuffer(dataBlock.x - (_vm->_virtscr[kMainVirtScreen].xstart & 7), dataBlock.y, _zbuf);
 
@@ -583,7 +583,7 @@ void AkosRenderer::byleRLEDecode(ByleRLEData &dataBlock) {
 				height = _height;
 				y = dataBlock.y;
 
-				scaleytab = &dataBlock.scaleTable[dataBlock.scaleYIndex];
+				scaleytab = &dataBlock.scaleTable[MAX<int>(0, dataBlock.scaleYIndex)]; // Avoid invalid mem reads in Basketball...
 				lastColumnX = dataBlock.x;
 
 				if (_scaleX == 255 || dataBlock.scaleTable[dataBlock.scaleXIndex] < _scaleX) {
@@ -807,6 +807,18 @@ byte AkosRenderer::paintCelByleRLE(int xMoveCur, int yMoveCur) {
 
 		startScaleIndexY = scaletableSize - yMoveCur;
 		for (i = 0; i < yMoveCur; i++) {
+			// WORKAROUND: Backyard Basketball sends out yMoveCur values higher than 128!
+			// This triggers ASAN, because it tries to reach a negative index of compData.scaleTable[].
+			if (startScaleIndexY < 0) {
+				debug(8, "AkosRenderer::paintCelByleRLE(): Negative startScaleIndexY: %d; actor (%d), scaletableSize (%d), yMoveCur (%d), working around it...",
+					_actorID, startScaleIndexY, scaletableSize, yMoveCur);
+				if (compData.scaleTable[0] < _scaleY)
+					compData.y -= step;
+
+				startScaleIndexY++;
+				continue;
+			}
+
 			if (compData.scaleTable[startScaleIndexY++] < _scaleY)
 				compData.y -= step;
 		}
@@ -814,6 +826,15 @@ byte AkosRenderer::paintCelByleRLE(int xMoveCur, int yMoveCur) {
 		rect.top = rect.bottom = compData.y;
 		startScaleIndexY = scaletableSize - yMoveCur;
 		for (i = 0; i < _height; i++) {
+			// WORKAROUND: See above...
+			if (startScaleIndexY < 0) {
+				if (compData.scaleTable[0] < _scaleY)
+					rect.bottom++;
+
+				startScaleIndexY++;
+				continue;
+			}
+
 			if (compData.scaleTable[startScaleIndexY++] < _scaleY)
 				rect.bottom++;
 		}




More information about the Scummvm-git-logs mailing list