Files
2026-02-02 04:50:13 +01:00

239 lines
6.5 KiB
C++

/* 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() {
delete _court;
delete _shields;
}
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