mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-24 11:36:22 +00:00
EMI/GRIM: Implement collision handling for EMI
- for solving the Monkey Kombat puzzle it is necessary that a kombat is triggered when Guybrush runs into one of the monkeys on the map - implement SetActorCollisionMode and SetActorCollisionScale - add EMI-specific calculations for retrieving the sphere data for the actors (and some minor refactoring for better readability) The following changes may alter the behaviour in GRIM, too: - in Actor::collisionHandlerCallback(), call the collision handler for both affected objects (that is necessary since only one actor may have a collision handler associated in lua but it may happen that the character with the handler is standing still and the other one is run into him) - Actor::handleCollisionTo() did only update a given position to ensure that there is no collision - so it is necessary to actually check for collisions and execute the lua callback if necessary (via Actor::handleCollisionWith())
This commit is contained in:
parent
d8d9614baa
commit
ce9239412d
@ -41,6 +41,7 @@
|
||||
#include "engines/grim/emi/costumeemi.h"
|
||||
#include "engines/grim/emi/skeleton.h"
|
||||
#include "engines/grim/emi/costume/emiskel_component.h"
|
||||
#include "engines/grim/emi/modelemi.h"
|
||||
|
||||
#include "common/foreach.h"
|
||||
|
||||
@ -704,11 +705,11 @@ void Actor::moveTo(const Math::Vector3d &pos) {
|
||||
mode = CollisionSphere;
|
||||
}
|
||||
|
||||
Math::Vector3d v = pos - _pos;
|
||||
Math::Vector3d moveVec = pos - _pos;
|
||||
foreach (Actor *a, g_grim->getActiveActors()) {
|
||||
handleCollisionWith(a, mode, &v);
|
||||
handleCollisionWith(a, mode, &moveVec);
|
||||
}
|
||||
_pos += v;
|
||||
_pos += moveVec;
|
||||
}
|
||||
|
||||
void Actor::walkForward() {
|
||||
@ -1787,9 +1788,11 @@ Math::Vector3d Actor::handleCollisionTo(const Math::Vector3d &from, const Math::
|
||||
}
|
||||
|
||||
Math::Vector3d p = pos;
|
||||
Math::Vector3d moveVec = pos - _pos;
|
||||
foreach (Actor *a, Actor::getPool()) {
|
||||
if (a != this && a->isInSet(_setName) && a->isVisible()) {
|
||||
p = a->getTangentPos(from, p);
|
||||
handleCollisionWith(a, _collisionMode, &moveVec);
|
||||
}
|
||||
}
|
||||
return p;
|
||||
@ -1800,10 +1803,10 @@ Math::Vector3d Actor::getTangentPos(const Math::Vector3d &pos, const Math::Vecto
|
||||
return dest;
|
||||
}
|
||||
|
||||
Model *model = getCurrentCostume()->getModel();
|
||||
Math::Vector3d p = _pos + model->_insertOffset;
|
||||
float size = model->_radius * _collisionScale;
|
||||
|
||||
Math::Vector3d p;
|
||||
float size;
|
||||
if (!getSphereInfo(false, size, p))
|
||||
return dest;
|
||||
Math::Vector2d p1(pos.x(), pos.y());
|
||||
Math::Vector2d p2(dest.x(), dest.y());
|
||||
Math::Segment2d segment(p1, p2);
|
||||
@ -1830,6 +1833,48 @@ Math::Vector3d Actor::getTangentPos(const Math::Vector3d &pos, const Math::Vecto
|
||||
return dest;
|
||||
}
|
||||
|
||||
void Actor::getBBoxInfo(Math::Vector3d &bboxPos, Math::Vector3d &bboxSize) const {
|
||||
if (g_grim->getGameType() == GType_MONKEY4) {
|
||||
bboxPos = Math::Vector3d(0, 0, 0);
|
||||
bboxSize = Math::Vector3d(0, 0, 0);
|
||||
} else {
|
||||
Model *model = getCurrentCostume()->getModel();
|
||||
bboxPos = model->_bboxPos;
|
||||
bboxSize = model->_bboxSize;
|
||||
}
|
||||
}
|
||||
|
||||
bool Actor::getSphereInfo(bool adjustZ, float &size, Math::Vector3d &p) const {
|
||||
if (g_grim->getGameType() == GType_MONKEY4) {
|
||||
EMICostume *costume = static_cast<EMICostume *>(getCurrentCostume());
|
||||
if (!costume) {
|
||||
warning("Actor::getSphereInfo: actor \"%s\" has no costume", getName().c_str());
|
||||
return false;
|
||||
}
|
||||
EMIChore *chore = costume->_wearChore;
|
||||
if (!chore) {
|
||||
warning("Actor::getSphereInfo: actor \"%s\" has no chore", getName().c_str());
|
||||
return false;
|
||||
}
|
||||
EMIModel *model = chore->getMesh()->_obj;
|
||||
assert(model);
|
||||
p = _pos + *(model->_center);
|
||||
// pre-scale factor of 0.8 was guessed by comparing with the original game
|
||||
size = model->_radius * _collisionScale * 0.8f;
|
||||
} else {
|
||||
Model *model = getCurrentCostume()->getModel();
|
||||
assert(model);
|
||||
|
||||
p = _pos + model->_insertOffset;
|
||||
// center the sphere on the model center.
|
||||
if (adjustZ) {
|
||||
p.z() += model->_bboxSize.z() / 2.f;
|
||||
}
|
||||
size = model->_radius * _collisionScale;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Actor::handleCollisionWith(Actor *actor, CollisionMode mode, Math::Vector3d *vec) const {
|
||||
if (actor->_collisionMode == CollisionOff || actor == this) {
|
||||
return false;
|
||||
@ -1839,25 +1884,21 @@ bool Actor::handleCollisionWith(Actor *actor, CollisionMode mode, Math::Vector3d
|
||||
return false;
|
||||
}
|
||||
|
||||
Model *model1 = getCurrentCostume()->getModel();
|
||||
Model *model2 = actor->getCurrentCostume()->getModel();
|
||||
|
||||
Math::Vector3d p1 = _pos + model1->_insertOffset;
|
||||
Math::Vector3d p2 = actor->_pos + model2->_insertOffset;
|
||||
|
||||
float size1 = model1->_radius * _collisionScale;
|
||||
float size2 = model2->_radius * actor->_collisionScale;
|
||||
Math::Vector3d p1, p2;
|
||||
float size1, size2;
|
||||
// you may ask: why center the sphere of the first actor only (by setting adjustZ to true)?
|
||||
// because it seems the original does so.
|
||||
// if you change this code test this places: the rocks in lb and bv (both when booting directly in the
|
||||
// set and when coming in from another one) and the poles in xb.
|
||||
if (!this->getSphereInfo(true, size1, p1) ||
|
||||
!actor->getSphereInfo(false, size2, p2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CollisionMode mode1 = mode;
|
||||
CollisionMode mode2 = actor->_collisionMode;
|
||||
|
||||
if (mode1 == CollisionSphere && mode2 == CollisionSphere) {
|
||||
// center the sphere on the model center.
|
||||
p1.z() += model1->_bboxSize.z() / 2.f;
|
||||
// you may ask: why center the sphere of the first actor only? because it seems the original does so.
|
||||
// if you change this code test this places: the rocks in lb and bv (both when booting directly in the
|
||||
// set and when coming in from another one) and the poles in xb.
|
||||
|
||||
Math::Vector3d pos = p1 + *vec;
|
||||
float distance = (pos - p2).getMagnitude();
|
||||
if (distance < size1 + size2) {
|
||||
@ -1875,6 +1916,13 @@ bool Actor::handleCollisionWith(Actor *actor, CollisionMode mode, Math::Vector3d
|
||||
warning("Collision between box and box not implemented!");
|
||||
return false;
|
||||
} else {
|
||||
Math::Vector3d bboxSize1, bboxSize2;
|
||||
Math::Vector3d bboxPos1, bboxPos2;
|
||||
|
||||
// get bboxSize and bboxPos for the current and the colliding actor
|
||||
this->getBBoxInfo(bboxPos1, bboxSize1);
|
||||
actor->getBBoxInfo(bboxPos2, bboxSize2);
|
||||
|
||||
Math::Rect2d rect;
|
||||
|
||||
Math::Vector3d bboxPos;
|
||||
@ -1889,8 +1937,8 @@ bool Actor::handleCollisionWith(Actor *actor, CollisionMode mode, Math::Vector3d
|
||||
|
||||
if (mode1 == CollisionBox) {
|
||||
pos = p1 + *vec;
|
||||
bboxPos = pos + model1->_bboxPos;
|
||||
size = model1->_bboxSize;
|
||||
bboxPos = pos + bboxPos1;
|
||||
size = bboxSize1;
|
||||
scale = _collisionScale;
|
||||
yaw = _yaw;
|
||||
|
||||
@ -1900,8 +1948,8 @@ bool Actor::handleCollisionWith(Actor *actor, CollisionMode mode, Math::Vector3d
|
||||
radius = size2;
|
||||
} else {
|
||||
pos = p2;
|
||||
bboxPos = p2 + model2->_bboxPos;
|
||||
size = model2->_bboxSize;
|
||||
bboxPos = p2 + bboxPos2;
|
||||
size = bboxSize2;
|
||||
scale = actor->_collisionScale;
|
||||
yaw = actor->_yaw;
|
||||
|
||||
@ -2004,6 +2052,11 @@ void Actor::collisionHandlerCallback(Actor *other) const {
|
||||
objects.add(other);
|
||||
|
||||
LuaBase::instance()->callback("collisionHandler", objects);
|
||||
|
||||
LuaObjects objects2;
|
||||
objects2.add(other);
|
||||
objects2.add(this);
|
||||
LuaBase::instance()->callback("collisionHandler", objects2);
|
||||
}
|
||||
|
||||
Math::Vector3d Actor::getWorldPos() const {
|
||||
|
@ -552,6 +552,9 @@ private:
|
||||
Math::Vector3d getSimplePuckVector() const;
|
||||
void calculateOrientation(const Math::Vector3d &pos, Math::Angle *pitch, Math::Angle *yaw, Math::Angle *roll);
|
||||
|
||||
void getBBoxInfo(Math::Vector3d &bboxPos, Math::Vector3d &bboxSize) const;
|
||||
bool getSphereInfo(bool adjustZ, float &size, Math::Vector3d &pos) const;
|
||||
|
||||
Common::String _name;
|
||||
Common::String _setName; // The actual current set
|
||||
|
||||
|
@ -798,14 +798,23 @@ void Lua_V2::SetActorCollisionMode() {
|
||||
Actor *actor = getactor(actorObj);
|
||||
assert(actor);
|
||||
int mode = (int)lua_getnumber(modeObj);
|
||||
// From _actors.lua
|
||||
// COLLISION_OFF = 0
|
||||
// COLLISION_BOX = 1
|
||||
// COLLISION_SPHERE = 2
|
||||
|
||||
// FIXME: set collision mode
|
||||
//actor->func(mode);
|
||||
warning("Lua_V2::SetActorCollisionMode: implement opcode. Mode %d", mode);
|
||||
Actor::CollisionMode m;
|
||||
switch (mode) {
|
||||
case Actor::CollisionOff:
|
||||
m = Actor::CollisionOff;
|
||||
break;
|
||||
case Actor::CollisionBox:
|
||||
m = Actor::CollisionBox;
|
||||
break;
|
||||
case Actor::CollisionSphere:
|
||||
m = Actor::CollisionSphere;
|
||||
break;
|
||||
default:
|
||||
warning("Lua_V2::SetActorCollisionMode(): wrong collisionmode: %d, using default 0", mode);
|
||||
m = Actor::CollisionOff;
|
||||
}
|
||||
actor->setCollisionMode(m);
|
||||
}
|
||||
|
||||
void Lua_V2::SetActorCollisionScale() {
|
||||
@ -819,9 +828,7 @@ void Lua_V2::SetActorCollisionScale() {
|
||||
assert(actor);
|
||||
|
||||
float scale = lua_getnumber(scaleObj);
|
||||
// FIXME: set collision scale
|
||||
//actor->func(scale);
|
||||
warning("Lua_V2::SetActorCollisionScale: implement opcode. Scale %f", scale);
|
||||
actor->setCollisionScale(scale);
|
||||
}
|
||||
|
||||
void Lua_V2::GetActorPuckVector() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user