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

418 lines
10 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/>.
*
*/
/*
* Copyright (C) 2006-2010 - Frictional Games
*
* This file is part of HPL1 Engine.
*/
#include "hpl1/engine/physics/Body2D.h"
#include "hpl1/engine/graphics/Mesh2d.h"
#include "hpl1/engine/math/Math.h"
#include "hpl1/engine/physics/CollideData2D.h"
#include "hpl1/engine/physics/Collider2D.h"
#include "hpl1/engine/scene/Entity2D.h"
#include "hpl1/engine/scene/Node2D.h"
namespace hpl {
//////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cBody2D::cBody2D(const tString &asName, cMesh2D *apMesh, cVector2f avSize, cCollider2D *apCollider, int alID)
: iEntity2D(asName) {
mvSize = avSize;
mpMesh = apMesh;
mpCollider = apCollider;
mpCollMesh = mpMesh->CreateCollisonMesh(0, mvSize);
mpBaseCollMesh = mpMesh->CreateCollisonMesh(0, mvSize);
/*for(int i=0;i<(int)mpCollMesh->mvPos.size();i++)
{
Log("Pos%d: %s\n", i,mpCollMesh->mvPos[i].ToString().c_str());
}
for(int i=0;i<(int)mpCollMesh->mvNormal.size();i++)
{
Log("Norm%d: %s\n", i,mpCollMesh->mvNormal[i].ToString().c_str());
}*/
// Log("------------\n");
// Set some default values to the properties
mfMaxVel = 0;
mfAcc = 1;
mfAirFriction = 0.005f;
mfGroundFriction = 0.3f;
mfGravity = 0.4f;
mfMaxGravityVel = 3;
mbCollidable = false;
mbCollides = true;
mbMoved = false;
mbOnGround = false;
mbGroundFrictionX = false;
mbGroundFrictionY = false;
mvCollideCount = 0;
mvMovement = 0;
mbAttachToGround = false;
mbAttachBodies = true;
mpParentBody = NULL;
mpNode = NULL;
mlID = alID;
mlCollideFlag = eFlagBit_0;
mlCollideType = eFlagBit_1;
}
//-----------------------------------------------------------------------
cBody2D::~cBody2D() {
hplDelete(mpCollMesh);
hplDelete(mpBaseCollMesh);
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
void cBody2D::UpdateLogic(float afTimeStep) {
cVector3f vStartPos = GetPosition();
if (mbAttachToGround) {
if (mpParentBody != NULL) {
// What can be done is to add this to the force, and then att the end redcue the same amout
// This will allow the body to add the velocity to it's jumps for example.
cVector3f vMovement = mpParentBody->GetMovement(); // mpParentBody->GetPosition() - mpParentBody->GetLastPosition();
SetPosition(GetPosition() + vMovement);
}
}
cCollideData2D CollideData;
// Update Gravity
mbGroundFrictionX = false;
if (mbCollides && mfGravity > 0) {
cVector3f vTempPos = GetPosition();
float fXSize = mvSize.x;
cRect2f Rect(vTempPos.x - fXSize / 2, vTempPos.y + mfGravity,
fXSize, mvSize.y / 2);
if (mpCollider->CollideRect(Rect, GetCollideFlag(), NULL)) {
// We are still on ground.
mbOnGround = true;
mbGroundFrictionX = true;
// Log("Standing on ground\n");
} else {
// If the player used to be on ground, add some extra force
if (mbOnGround) {
mvForce.y += mfGravity * 2;
}
mbOnGround = false;
mvForce.y += mfGravity;
if (mvForce.y > mfMaxGravityVel)
mvForce.y = mfMaxGravityVel;
}
}
// Add the Y Axis and check for collision
AddPosXY(cVector2f(0, mvForce.y));
if (mbCollides) {
UpdateCollisionMesh();
UpdateBoundingBox();
if (mpCollider->CollideBody(this, &CollideData)) {
if (mvLastCollidePos.y == GetPosition().y)
mvCollideCount.y++;
if (mvForce.y > 0) {
if (mbAttachToGround) {
// If collided with a body, make it the new reference frame.
if (CollideData.mlstBodies.size() > 0) {
tBody2DListIt it = CollideData.mlstBodies.begin();
if (*it != mpParentBody) {
if (mpParentBody != NULL)
DetachBody(mpParentBody);
SetParentBody(*it);
(*it)->AttachBody(this);
// Log("Attaching!\n");
}
} else {
if (mpParentBody != NULL) {
DetachBody(mpParentBody);
mpParentBody = NULL;
// Log("Detaching!\n");
}
}
}
mbOnGround = true;
mvForce.y = 0;
}
if (mvCollideCount.y > 0) {
mvForce.y = 0;
}
mvLastCollidePos.y = GetPosition().y;
mbGroundFrictionX = true;
} else {
mvCollideCount.y = 0;
if (!mbOnGround)
mvLastCollidePos.y = -10000;
// mbGroundFrictionX = false;
}
if (mbOnGround == false && mbAttachToGround) {
if (mpParentBody != NULL) {
DetachBody(mpParentBody);
mpParentBody = NULL;
// Log("Detaching!\n");
}
}
}
// Add the X Axis and check for collision
AddPosXY(cVector2f(mvForce.x, 0));
if (mbCollides) {
UpdateCollisionMesh();
UpdateBoundingBox();
if (mpCollider->CollideBody(this, NULL)) {
if (mvLastCollidePos.x == GetPosition().x)
mvCollideCount.x++;
if (mvCollideCount.x > 0) {
mvForce.x = 0;
}
mvLastCollidePos.x = GetPosition().x;
} else {
mvCollideCount.x = 0;
mvLastCollidePos.x = -10000;
}
}
// Update the force
float fAngle = 0, fStrength = 0;
cMath::GetAngleFromVector(mvForce, &fAngle, &fStrength);
fStrength -= mfAirFriction; // This is the air friction.
// It should be combined with the friction of the collided material.
if (fStrength < 0)
fStrength = 0;
SetForce(fAngle, fStrength);
// Don't do any friction if the body has moved.
// if(!mbMoved) Or?
{
if (mbGroundFrictionX && mvForce.x != 0) {
// Log("GroundFriction!\n");
// Log("Force: %s\n",mvForce.ToString().c_str());
if (mvForce.x > 0) {
mvForce.x -= mfGroundFriction;
if (mvForce.x < 0)
mvForce.x = 0;
} else {
mvForce.x += mfGroundFriction;
if (mvForce.x > 0)
mvForce.x = 0;
}
// Log("Force: %s\n",mvForce.ToString().c_str());
}
}
mvMovement = GetPosition() - vStartPos;
/*tBody2DListIt BodyIt = mlstAttachedBodies.begin();
for(;BodyIt != mlstAttachedBodies.end();BodyIt++)
{
cBody2D* pBody = *BodyIt;
pBody->SetPosition(pBody->GetPosition() + vMovement);
}*/
if (mpNode) {
mpNode->SetPosition(GetWorldPosition());
}
mbMoved = false;
}
//-----------------------------------------------------------------------
void cBody2D::Move(float afValue) {
float fAngle = 0, fStrength = 0;
cVector2f vForwardVec = cMath::GetVectorFromAngle2D(mvRotation.z, 1);
cVector2f vMovement = cMath::ProjectVector2D(mvForce, vForwardVec);
cMath::GetAngleFromVector(vMovement, &fAngle, &fStrength);
if (fStrength < mfMaxVel) {
float fTempAcc = mfAcc;
if (fStrength + fTempAcc > mfMaxVel) {
fTempAcc -= (fStrength + fTempAcc) - mfMaxVel;
}
mvForce += vForwardVec * fTempAcc;
mbMoved = true;
}
}
//-----------------------------------------------------------------------
void cBody2D::AddForce(float afAngle, float afStrength) {
cVector2f vForce = cMath::GetVectorFromAngle2D(afAngle, afStrength);
AddForce(vForce);
}
//-----------------------------------------------------------------------
void cBody2D::AddForce(const cVector2f &avForce) {
mvForce += avForce;
}
//-----------------------------------------------------------------------
void cBody2D::SetForce(float afAngle, float afStrength) {
cVector2f vForce = cMath::GetVectorFromAngle2D(afAngle, afStrength);
SetForce(vForce);
}
//-----------------------------------------------------------------------
void cBody2D::SetForce(const cVector2f &avForce) {
mvForce = avForce;
}
//-----------------------------------------------------------------------
const cRect2f &cBody2D::GetBoundingBox() {
return mBoundingBox;
}
//-----------------------------------------------------------------------
bool cBody2D::UpdateBoundingBox() {
cVector2f vSize;
/*if(mvRotation.z != 0)
{
//Only Temp...
float fMaxSize = sqrt(mvSize.x*mvSize.x + mvSize.y*mvSize.y);
vSize.x = fMaxSize;
vSize.y = fMaxSize;
}
else*/
{
vSize = mvSize;
}
mBoundingBox = cRect2f(cVector2f(GetWorldPosition().x - vSize.x / 2,
GetWorldPosition().y - vSize.y / 2),
vSize);
return true;
}
//-----------------------------------------------------------------------
void cBody2D::UpdateCollisionMesh() {
cVector2f vPos(GetPosition().x, GetPosition().y);
for (int i = 0; i < (int)mpCollMesh->mvPos.size(); i++) {
mpCollMesh->mvPos[i] = vPos + mpBaseCollMesh->mvPos[i];
}
}
//-----------------------------------------------------------------------
cCollisionMesh2D *cBody2D::GetCollisionMesh() {
return mpCollMesh;
}
//-----------------------------------------------------------------------
void cBody2D::AttachBody(cBody2D *apBody) {
mlstAttachedBodies.push_back(apBody);
}
//-----------------------------------------------------------------------
void cBody2D::DetachBody(cBody2D *apBody) {
tBody2DListIt it = mlstAttachedBodies.begin();
for (; it != mlstAttachedBodies.end(); it++) {
if (*it == apBody) {
mlstAttachedBodies.erase(it);
break;
}
}
}
//-----------------------------------------------------------------------
void cBody2D::SetParentBody(cBody2D *apBody) {
mpParentBody = apBody;
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
void cBody2D::AddPosXY(cVector2f avPosAdd) {
cVector3f vPos = GetPosition();
vPos += avPosAdd;
SetPosition(vPos);
}
//-----------------------------------------------------------------------
} // namespace hpl