/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Patrick Boenzli */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_COLLISION_REACTION #include "collision_handle.h" #include "world_entity.h" #include "collision.h" #include "collision_event.h" #include "collision_reaction.h" #include "cr_object_damage.h" #include "cr_physics_ground_walk.h" #include "debug.h" using namespace std; /** * standard constructor * @todo this constructor is not jet implemented - do it */ CollisionHandle::CollisionHandle (WorldEntity* owner, CREngine::CRType type) { this->setClassID(CL_COLLISION_HANDLE, "CollisionHandle"); this->owner = owner; this->type = type; this->bCollided = false; this->bDispatched = true; this->collisionReaction = NULL; this->bContinuousPoll = false; this->bStopOnFirstCollision = false; switch( type) { case CREngine::CR_PHYSICS_STEP_BACK: // this->collisionReaction = new CRPhysicsGroundWalk(); this->bContinuousPoll = true; break; case CREngine::CR_PHYSICS_GROUND_WALK: this->collisionReaction = new CRPhysicsGroundWalk(); this->bContinuousPoll = true; break; case CREngine::CR_OBJECT_DAMAGE: this->collisionReaction = new CRObjectDamage(); this->bStopOnFirstCollision = true; break; default: break; }; } /** * standard deconstructor */ CollisionHandle::~CollisionHandle () { // delete what has to be deleted here if( this->collisionReaction != NULL) delete this->collisionReaction; } /** * restores the CollisionHandle to its initial state */ void CollisionHandle::reset() { this->flushCollisions(); } /** * add more filter targets to this collision handle * @param classID the classid to look for */ void CollisionHandle::addTarget(long target) { // make sure there is no dublicate std::vector::iterator it = this->targetList.begin(); for( ; it < this->targetList.end(); it++) if( (*it) == target) return; // add element PRINTF(0)("addTarget: %i \n", target); this->targetList.push_back(target); } /** * registers a new Collision Object * @param entityA WorldEntity A of the collision * @param entityB WorldEntity B of the collision * if a there is already a collision object with the same stats * registration will be skipped and the last collision object is returned */ Collision* CollisionHandle::registerCollision(WorldEntity* entityA, WorldEntity* entityB) { //first get the collision object, multiple sources Collision* c; if( this->collisionList.empty() || ((this->collisionList.back())->getEntityA() != entityA && (this->collisionList.back())->getEntityB() != entityB )) { c = CREngine::getInstance()->popCollisionObject(); c->collide(entityA, entityB); this->collisionList.push_back(c); // now register it as a shared collision with the other collision entity CollisionHandle* ch = entityB->getCollisionHandle(this->type); if( ch != NULL) ch->registerSharedCollision(c); } else c = this->collisionList.back(); return c; } /** * register a Collision to the Collision handle. * @param collision the collision object to register * * This is used for internal collision registration: sharing the collision objects between Collision Reactions * Therefore dispatching it only once */ void CollisionHandle::registerSharedCollision(Collision* collision) { // fist check if we are listening for this Collision if( !this->filterCollision(collision)) return; // set the state to not dispatched this->bDispatched = false; this->bCollided = true; collision->setEntityBCollide(true); this->collisionList.push_back(collision); } /** * this is the function to be called on a collision event for this handle * @param collision the collision objects containing all collision informations */ void CollisionHandle::registerCollisionEvent(CollisionEvent* collisionEvent) { if( !this->filterCollisionEvent(collisionEvent)) return; // set the state to not dispatched this->bDispatched = false; this->bCollided = true; // checks if these WorldEntities have already collided or if its a new collision -> create a new Collision object Collision* c = this->registerCollision(collisionEvent->getEntityA(), collisionEvent->getEntityB()); c->setEntityACollide(true); c->registerCollisionEvent(collisionEvent); PRINTF(0)("Registering Collision Event: %s, %s\n", collisionEvent->getEntityA()->getClassName(), collisionEvent->getEntityB()->getClassName()); } /** * flushes the collision list */ void CollisionHandle::flushCollisions() { this->collisionList.clear(); } /** * handles the collisions and react according to algorithm */ void CollisionHandle::handleCollisions() { // if continuous poll poll the reaction if( this->bContinuousPoll && !this->bCollided) { this->collisionReaction->update(this->owner); return; } // collision reaction calculations (for every collision there will be a reaction) vector::iterator it = this->collisionList.begin(); for(; it < this->collisionList.end(); it++) { if( !(*it)->isDispatched()) { this->collisionReaction->reactToCollision(*it); (*it)->flushCollisionEvents(); } } // now set state to dispatched this->bDispatched = true; this->bCollided = false; this->flushCollisions(); } /** * filter out the CollisionEvents that are not wanted * @param collisionEvent the collision event to filter */ bool CollisionHandle::filterCollisionEvent(CollisionEvent* collisionEvent) { vector::iterator it = this->targetList.begin(); for(; it < this->targetList.end(); it++) { if( collisionEvent->getEntityA() == this->owner) { if( collisionEvent->getEntityB()->isA((ClassID)(*it))) { PRINTF(0)("I am: %s colliding with: %s is a %i filter ok\n", owner->getClassName(), collisionEvent->getEntityB()->getClassName(), *it); return true; } } else { if( collisionEvent->getEntityA()->isA((ClassID)(*it))) { PRINTF(0)("I am: %s colliding with: %s is a %i filter ok\n", owner->getClassName(), collisionEvent->getEntityA()->getClassName(), *it); return true; } } } return false; } /** * filter Collisions that are not wanted to be reacted to * @param collision the collision object to filter */ bool CollisionHandle::filterCollision(Collision* collision) { vector::iterator it = this->targetList.begin(); for(; it < this->targetList.end(); it++) { if( collision->getEntityA() == this->owner) { if( collision->getEntityA()->isA((ClassID)(*it))) return true; } else { if( collision->getEntityB()->isA((ClassID)(*it))) return true; } } return false; }