/* $Id$ */ #include "world.hpp" #include #include #include "wopsettings.hpp" #include "theme.hpp" #include "event.hpp" #include "udpdata.hpp" #include "random.hpp" #include "avatarworm.hpp" #include "explosion.hpp" #include "blueglow.hpp" #include "goal.hpp" #include "shieldcorona.hpp" #include "invisiblecorona.hpp" #include "smoke.hpp" #include "grenade.hpp" #include "mine.hpp" #include "missile.hpp" #include "homingmissile.hpp" #include "guidedmissile.hpp" #include "helicopter.hpp" #include "flag.hpp" #include "rope.hpp" #include "hook.hpp" #include "staticgun_i.hpp" #include "ball.hpp" #include "bonushealth.hpp" #include "bonusshield.hpp" #include "bonusinvisible.hpp" #include "bonusfuel.hpp" #include "bonushomingvirus.hpp" #include "spritesequence.cpp" #include "spriteset.cpp" #include "wopsprites.cpp" #include "message.hpp" #include "skwoermzone.hpp" /**********************************************************/ #define USE_NONCOLLIDABLE_POOL /**********************************************************/ World::World( int sizeX, int sizeY ) : m_particles( *this ), m_bonusmanager( this, localRnd.getUint32() ), m_frame( 0 ), m_noncollidablePool( this ), m_spriteInterface( NULL ), m_nextPlayerID( 0 ), m_focusedObject( NULL ) { // Object::m_worldPointer = this; m_map = new ServerMap( sizeX, sizeY ); m_scorekeeper.initialize( this ); m_szProbability = WopSettings::getInstance()->getSZProb(); m_mutex = SDL_CreateMutex(); } /**********************************************************/ World::~World() { LOG( 1 ) INFO( "World::~World: starting destructor\n" ); reset(); delete m_map; SDL_DestroyMutex( m_mutex ); LOG( 1 ) INFO( "World::~World: done\n" ); } /**********************************************************/ // These constants define the separators between to theme names, // if they are printed due to a non-valid theme name passed at // command line or in the WoP configuration file // THEME_NAME_HEAD is printed before each theme name // THEME_NAME_TAIL is printed after each theme name #define THEME_NAME_HEAD " \033[0;1m- " #define THEME_NAME_TAIL "\033[0m\n" bool World::createFromTheme() { SDL_mutexP( m_mutex ); const char fn[] = "World::createFromTheme:"; const WopSettings* settings = WopSettings::getInstance(); // check validity of theme name if( ! CHECK(settings->isAvailableTheme(settings->getTheme()), "%s \"%s\" is not a valid theme name.\n\n" " Available Themes:\n", fn, settings->getTheme()) ) { // if theme name is not valid, print list of available themes settings->printAvailableThemes( std::cerr, THEME_NAME_HEAD, THEME_NAME_TAIL ); std::cout << endl; return false; } ////////////////////////////////////////////// // load the theme ////////////////////////////////////////////// String themePath( settings->getData() ); themePath += '/'; themePath += THEME_CONFIG_PATH; Theme theme( settings->getTheme(), m_random.getUint32() & 0xffff ); ASSERT( theme.load((char*)themePath, THEME_CONFIG), "%S could not load theme \"%s/"THEME_CONFIG ":%s\"\n", fn, (char*)themePath, (char*)theme.getName() ); ////////////////////////////////////////////// // build the map from the theme ////////////////////////////////////////////// // if the loaded theme defines its own map size, set it now if( theme.definesOwnSize() ) { // adapt the global settings if( WopSettings::getInstance()->getWidth() != theme.getWidth() ) { WopSettings::getInstance()->setWidth( theme.getWidth() ); } if( WopSettings::getInstance()->getHeight() != theme.getHeight() ) { WopSettings::getInstance()->setHeight( theme.getHeight() ); } // build new map, if necessary if( m_map->getSizeX() != (Uint32)theme.getWidth() || m_map->getSizeY() != (Uint32)theme.getHeight() ) { LOG(2) INFO( "%s theme \"%s\" defines its own map size %dx%d.\n" " Overwriting current map size %dx%d.\n", fn, (char*)theme.getName(), theme.getWidth(), theme.getHeight(), m_map->getSizeX(), m_map->getSizeY() ); // delete old map and create new one delete m_map; ASSERT( NULL != (m_map = new ServerMap(theme.getWidth(), theme.getHeight())), "World::createFromTheme: could not create server map " "of size %dx%d\n", theme.getWidth(), theme.getHeight() ); } } ServerMap* serverMap = dynamic_cast( m_map ); ASSERT( serverMap, "World::createFromTheme: no ServerMap to create theme\n" ); // first load eventual configuration for initial objects if( theme.hasObjectConfigPath() ) { String objectPath( settings->getData() ); objectPath += '/'; objectPath += theme.getObjectConfigPath(); } ////////////////////////////////////////////// // Build map from theme // NOTE: This method also creates and initializes objects. This // might seem inconsistent, since thereby also objects are // created, and therefore the world itself is changed. But // these objects must be inserted in the map after earth // and before map stuff (to restrict the map stuff placement // by locked areas). ////////////////////////////////////////////// m_map->createFromTheme( theme, *this ); ////////////////////////////////////////////// // load the settings for the bonus object handling ////////////////////////////////////////////// String bonusPath( settings->getData() ); bonusPath += '/'; bonusPath += theme.getBonusConfigPath(); ASSERT( m_bonusmanager.loadSettings(bonusPath), "%s could not load bonus configuration from file \"%s\"\n", fn, (char*)bonusPath ); bonusPath.reset(); SDL_mutexV( m_mutex ); return true; } /**********************************************************/ bool World::createFromThemeMaps( const String& background, const String& diggable, const String& undiggable, const String& indestructible ) { const char fn[] = "World::createFromThemeMaps:"; const WopSettings* const settings = WopSettings::getInstance(); INFO( "%s building map from theme defined by the maps\n" " background : %s\n" " diggable : %s\n" " undiggable : %s\n" " indestructible : %s\n", fn, background.getString(), diggable.getString(), undiggable.getString(), indestructible.getString() ); // load the image file defining the map size and the background Sprite spriteItem; ASSERT( spriteItem.loadImage(background.getString()), "%s could not load \"%s\"\n", fn, background.getString() ); // check the size of the resulting map ASSERT( spriteItem.getWidth() >= settings->getMinWidth() && spriteItem.getWidth() <= settings->getMaxWidth() && spriteItem.getHeight() >= settings->getMinHeight() && spriteItem.getHeight() <= settings->getMaxHeight(), "%s the background file \"%s\" has size (%d,%d), but " "the allowed size lies in boundaries %d <= x <= %d " "and %d <= y <= %d\n", fn, background.getString(), spriteItem.getWidth(), spriteItem.getHeight(), settings->getMinWidth(), settings->getMaxWidth(), settings->getMinHeight(), settings->getMaxHeight() ); // adapt the global settings if( WopSettings::getInstance()->getWidth() != spriteItem.getWidth() ) { WopSettings::getInstance()->setWidth( spriteItem.getWidth() ); } if( WopSettings::getInstance()->getHeight() != spriteItem.getHeight() ) { WopSettings::getInstance()->setHeight( spriteItem.getHeight() ); } // build new map, if necessary if( m_map->getSizeX() != (Uint32)spriteItem.getWidth() || m_map->getSizeY() != (Uint32)spriteItem.getHeight() ) { LOG(2) INFO( "%s background file \"%s\" defines map size %dx%d.\n" " Overwriting current map size %dx%d.\n", fn, background.getString(), spriteItem.getWidth(), spriteItem.getHeight(), m_map->getSizeX(), m_map->getSizeY() ); // delete old map and create new one delete m_map; ASSERT( NULL != (m_map = new ServerMap(spriteItem.getWidth(), spriteItem.getHeight())), "%s: could not create server map of size %dx%d\n", fn, spriteItem.getWidth(), spriteItem.getHeight() ); } //////////////////////////////////////// //////////////////////////////////////// // place background file ASSERT( false, "%s, %d: NOT IMPLEMENTED\n", __FILE__, __LINE__ ); //////////////////////////////////////// //////////////////////////////////////// // load maps of diggable, undiggable, indestructible map stuff const String nullString( "NULL" ); const int materials[] = { MapStuffSet::DIGGABLE, MapStuffSet::UNDIGGABLE, MapStuffSet::INDESTRUCTIBLE }; const String* files[] = { &diggable, &undiggable, &indestructible }; const char* matstr[] = { "diggable", "undiggable", "indestructible" }; for( int i = 0; i < 3; i++ ) { if( *files[i] != nullString ) { LOG(2) INFO( "%s: map of %s mapstuff is defined as \"NULL\"\n" " -> skipping diggable mapstuff\n", fn, matstr[i] ); } else { ASSERT( spriteItem.loadImage(diggable.getString()), "%s could not load \"%s\"\n", fn, files[i]->getString() ); m_map->placeMapStuffItem( &spriteItem, 0, 0, materials[i] ); } } return true; } /**********************************************************/ void World::setupGameMode() { m_scorekeeper.setupGameMode(); } /**********************************************************/ void World::optimizeForClient( SDL_PixelFormat* format, Uint32 flags ) { if ( !dynamic_cast( m_map ) ) { ServerMap* serverMap = dynamic_cast( m_map ); ASSERT( serverMap, "World::optimizeForClient: map type unknown\n" ); LOG( 4 ) INFO( "World::optimizeForClient: Creating optimized map for the client\n" ); ClientMap* optimizedMap = new ClientMap( *serverMap, format, flags ); ASSERT( optimizedMap, "World::optimizeForClient: could not optimize map\n" ); LOG( 4 ) INFO( "World::optimizeForClient: Deleting original server map\n" ); delete m_map; m_map = optimizedMap; } else { INFO( "World::optimizeForClient: map is already optimized\n" ); } } /**********************************************************/ void World::addPlayer( Player* p ) { SDL_mutexP( m_mutex ); LOG( 2 ) INFO( "World::addPlayer: adding player with id %d in frame %d\n", p->getPlayerID(), m_frame ); // create a new avatar object, attach it to the player object p->attachAvatar( dynamic_cast(newObject(p->getAvatarType())) ); // initialize new avatar p->getAvatar()->spawnAvatar(); // update the sprite selection // IMPORTANT: // this cannot be done in the constructor of class Avatar // since updateSpriteSelection() will cause a call of // the pure virtual function getID(), which is implemented // only in the derived classes. p->getAvatar()->updateSpriteSelection(); // add the player to the world's player list m_player.push_back( p ); printPlayerVectorInfo(); m_scorekeeper.resetPlayerScore( p->getPlayerID() ); SDL_mutexV( m_mutex ); } /**********************************************************/ void World::removePlayer( const Uint8 id ) { SDL_mutexP( m_mutex ); Player* player = getPlayerByID( id ); if ( !player ) { DBG( 2 ) INFO( "World::removePlayer: unknown player id %d\n", id ); return; } LOG( 2 ) INFO( "World::removePlayer: removing player with id %d in frame %d\n", id, m_frame ); // remove the avatar from the world's list of // collidable objects so that it is not drawn any more //Object *avatar = m_collidables.swap_unhook( player->getAvatar() ); Object* avatar = player->getAvatar(); const vector::iterator avatarIt = std::find( m_collidables.begin(), m_collidables.end(), avatar ); DBG( 1 ) ASSERT( avatarIt != m_collidables.end(), "World::removePlayer: player could not be found\n" ); m_collidables.erase( avatarIt ); // check, if the colored instance can be deleted bool InstanceIsFree = true; for( Uint8 p = 0; p < m_player.size(); p++ ) { if( m_player[p]->getPlayerID() != id && m_player[p]->getAvatar()->getID() == avatar->getID() && m_player[p]->getAvatar()->getColorInstance() == avatar->getColorInstance() ) { InstanceIsFree = false; break; } } // remove the avatar's colored instance from the sprite object if( InstanceIsFree ) { ((WopSpritesWorldInterface*)getSpriteInterface())-> removeColoredInstance( *avatar ); } // detach the avatar from the player, who leaves the world // NOTE: the order is important here (1) detach (2) delete player->detachAvatar(); delete avatar; // remove the player from the world's player list vector::iterator playerIt = std::find( m_player.begin(), m_player.end(), player ); m_player.erase( playerIt ); printPlayerVectorInfo(); SDL_mutexV( m_mutex ); } /**********************************************************/ Player* World::getPlayerByID( const Uint8 id ) const { SDL_mutexP( m_mutex ); Player* player = NULL; for ( Uint8 p = 0; p < m_player.size(); p++ ) { if ( m_player[p]->getPlayerID() == id ) { player = m_player[p]; break; } } SDL_mutexV( m_mutex ); return player; } /**********************************************************/ Uint8 World::getPlayerIndex( const Uint8 id ) const { SDL_mutexP( m_mutex ); for ( Uint8 p = 0; p < m_player.size(); p++ ) { if ( m_player[p]->getPlayerID() == id ) { SDL_mutexV( m_mutex ); return p; } } SDL_mutexV( m_mutex ); return 255; } /**********************************************************/ Object* World::newObject( const int objectID ) { SDL_mutexP( m_mutex ); Object* object = NULL; // create object switch( objectID ){ case AVATAR_WORM : object = NEW AvatarWorm(); break; case GRENADE : object = NEW Grenade(); break; case MINE : object = NEW Mine(); break; case BALL : object = NEW Ball(); break; case MISSILE : object = NEW Missile(); break; case HOMING_MISSILE : object = NEW HomingMissile(); break; case GUIDED_MISSILE : object = NEW GuidedMissile(); break; case HELICOPTER : object = NEW Helicopter(); break; case BONUS_HEALTH : object = NEW BonusHealth( this ); break; case BONUS_SHIELD : object = NEW BonusShield( this ); break; case BONUS_INVISIBLE : object = NEW BonusInvisible( this ); break; case BONUS_FUEL : object = NEW BonusFuel( this ); break; case BONUS_HOMING_VIRUS : object = NEW BonusHomingVirus( this ); break; #ifdef USE_NONCOLLIDABLE_POOL // explosions are administrated by the non collidable pool case EXPLOSION_15 : object = m_noncollidablePool.newExplosion( EXPLOSION_15 - SMALLEST_EXPLOSION ); break; case EXPLOSION_40 : object = m_noncollidablePool.newExplosion( EXPLOSION_40 - SMALLEST_EXPLOSION ); break; case EXPLOSION_60 : object = m_noncollidablePool.newExplosion( EXPLOSION_60 - SMALLEST_EXPLOSION ); break; case EXPLOSION_80 : object = m_noncollidablePool.newExplosion( EXPLOSION_80 - SMALLEST_EXPLOSION ); break; // smoke is administrated by the non collidable pool case SMOKE_15 : object = m_noncollidablePool.newSmoke( SMOKE_15 - SMALLEST_SMOKE ); break; case SMOKE_30 : object = m_noncollidablePool.newSmoke( SMOKE_30 - SMALLEST_SMOKE ); break; #else case EXPLOSION_15 : object = NEW Explosion(); static_cast(object) ->setSize( EXPLOSION_15 - SMALLEST_EXPLOSION ); break; case EXPLOSION_40 : object = NEW Explosion(); static_cast(object) ->setSize( EXPLOSION_40 - SMALLEST_EXPLOSION ); break; case EXPLOSION_60 : object = NEW Explosion(); static_cast(object) ->setSize( EXPLOSION_60 - SMALLEST_EXPLOSION ); break; case EXPLOSION_80 : object = NEW Explosion(); static_cast(object) ->setSize( EXPLOSION_80 - SMALLEST_EXPLOSION ); break; case SMOKE_15 : object = NEW Smoke(); static_cast(object) ->setSize( SMOKE_15 - SMALLEST_SMOKE ); break; case SMOKE_30 : object = NEW Smoke(); static_cast(object) ->setSize( SMOKE_30 - SMALLEST_SMOKE ); break; #endif case BLUEGLOW_25 : object = NEW BlueGlow( this, BLUEGLOW_25 - SMALLEST_BLUEGLOW ); break; case GOAL_50 : object = NEW Goal(); break; case SHIELD_CORONA : object = NEW ShieldCorona(); break; case INVISIBLE_CORONA : object = NEW InvisibleCorona(); break; case ROPE : object = NEW Rope(); break; case HOOK : object = NEW Hook(); break; case STATIC_GUN_I: object = NEW StaticGunI( this ); break; case STATIC_GUN_I_CHASSIS: object = NEW StaticGunIChassis(); break; default: break; } DBG( 1 ) ASSERT( object != NULL, "World::newObject: non-valid " "object ID %d\n", objectID ); object->setWorldPointer( this ); // update the sequence length. IMPORTANT: // this cannot be done in the constructor of class Object // since updateSpriteSelection() will cause a call of // the pure virtual function getID(), which is implemented // only in the derived classes. object->updateSequenceLength(); // insert it in the correct vector if( Object::isCollidable( objectID ) ) { m_collidables.push_back( dynamic_cast( object )); } else { m_noncollidables.push_back( dynamic_cast( object )); } SDL_mutexV( m_mutex ); return object; } /**********************************************************/ void World::deleteObject( Object* const object ) { #ifdef USE_NONCOLLIDABLE_POOL // First, unhook the object from the object lists in the world. unhookObject( object ); // Try to remove the object from the pool of non collidable // objects. The pool will check, if the object is of that // type, that is administration by this pool. In this case // the pool will remove internally the object from the list // of used objects and return true, which means that we are // not allowed to delete the object, because it is administrated // by the pool itself. if( ! m_noncollidablePool.removeObject(object) ) { delete object; } #else unhookObject( object ); delete object; #endif } /**********************************************************/ void World::unhookObject( const Object* const object ) { SDL_mutexP( m_mutex ); if( object->isCollidable() ) { const vector::iterator collIt = std::find( m_collidables.begin(), m_collidables.end(), object ); if( collIt != m_collidables.end() ) m_collidables.erase( collIt ); else CHECK( false, "World::deleteObject: collidable object could not be found\n" ); // check if focus was on the unhooked object => look for new focus object if( object == m_focusedObject ) scanObjectsForFocus(); } else { const vector::iterator noncollIt = std::find( m_noncollidables.begin(), m_noncollidables.end(), object ); if( noncollIt != m_noncollidables.end() ) m_noncollidables.erase( noncollIt ); else CHECK( false, "World::deleteObject: noncollidable object could not be found\n" ); } SDL_mutexV( m_mutex ); } /**********************************************************/ void World::applyEvents( const EchoMessage* echoMessage ) { SDL_mutexP( m_mutex ); // Process the actions of all players Uint8 numberPlayers = m_player.size(); for( Uint8 p = 0; p < numberPlayers; p++ ) { const Event& event = echoMessage->event[p]; Player* player = getPlayer(p); Avatar* avatar = player->getAvatar(); if( ! avatar->isLiving() ) { // check if player wants to respawn if ( event.testAnyActions( Event::SPAWN ) ) { avatar->spawnAvatar(); } continue; } // do we have an up-to-date event packet? if( ! event.testAnyActions( Event::N_A ) ) { // reset moving, jumping, and digging avatar->setState( Avatar::MOVING_ANY, false ); avatar->setState( Avatar::JUMPING , false ); avatar->setState( Avatar::DIGGING , false ); // handle aiming if( ! event.testAnyActions( Event::UP | Event::DOWN )) { avatar->setState( Avatar::AIMING_ANY, false ); } else { if( event.testAnyActions( Event::DOWN )) { avatar->setState( Avatar::AIMING_DOWN, true ); avatar->setState( Avatar::AIMING_UP , false ); } else { // this must be Event::UP avatar->setState( Avatar::AIMING_UP , true ); avatar->setState( Avatar::AIMING_DOWN, false ); } } // handle moving if( event.testAnyActions( Event::RIGHT )) { avatar->setState( Avatar::MOVING_RIGHT, true ); } if( event.testAnyActions( Event::LEFT )) { avatar->setState( Avatar::MOVING_LEFT, true ); } // handle jumping if( event.testAnyActions( Event::JUMP )) avatar->setState( Avatar::JUMPING, true ); // handle digging if( event.testAnyActions( Event::DIG ) ) avatar->setState( Avatar::DIGGING, true ); // handle weapon selection const Uint32 weapon = event.getWeapon(); if ( weapon >= 1 && weapon <= Player::m_MAX_WEAPONS-Player::m_IGNORE_N_LAST_WEAPONS /* because of rope weapons */) player->setActiveWeapon( weapon-1 ); } avatar->setState( Avatar::ROPE_ON, false ); avatar->setState( Avatar::ROPE_OFF, false ); avatar->setState( Avatar::ROPE_IN, false ); avatar->setState( Avatar::ROPE_OUT, false ); avatar->setState( Avatar::ROPE_REL, false ); // handle rope events if( event.testAnyActions( Event::ROPE_ON ) ) { avatar->setState( Avatar::ROPE_ON, true ); } if( event.testAnyActions( Event::ROPE_OFF ) ) { avatar->setState( Avatar::ROPE_OFF, true ); } if( event.testAnyActions( Event::ROPE_IN ) ) { avatar->setState( Avatar::ROPE_IN, true ); } if( event.testAnyActions( Event::ROPE_OUT ) ) { avatar->setState( Avatar::ROPE_OUT, true ); } if( event.testAnyActions( Event::ROPE_REL ) ) { avatar->setState( Avatar::ROPE_REL, true ); } // handle shoot events avatar->setState( Avatar::SHOOTING, false ); if( event.testAnyActions( Event::SHOOT ) ) avatar->setState( Avatar::SHOOTING, true ); if ( event.testAnyActions( Event::DEBUG ) ) { m_map->displayFlags(); } } SDL_mutexV( m_mutex ); } /**********************************************************/ void World::damageObject( const Particles::ParticleData& p ) { SDL_mutexP( m_mutex ); CollidableObject* hitObject = getCollidableAt( ROUND( p.pos.x ), ROUND( p.pos.y ) ); if( hitObject ) { const Player* const shooter = getPlayerByID( p.owner ); if( shooter ) { LOG( 5 ) INFO( "World::damageObject: player '%s' hit something\n", static_cast( shooter->getName() )); hitObject->applyDamage( p ); } else { LOG( 1 ) CHECK( false, "World::damageObject: cannot determine shooter\n" ); } } SDL_mutexV( m_mutex ); } /**********************************************************/ #define SZ_RADIUS 90.0 #define SZ_DECAY_RADIUS 40.0 #define SZ_CREATION_RADIUS 90.0 #define SZ_SECONDARY_RADIUS 120.0 #define SZ_REVAMP_RADIUS 120.0 void World::revampSkwoermZone( const unsigned int localSZ ) { for( unsigned int otherSZ = 0; otherSZ < m_skwoermZones.size(); otherSZ++ ) { if( ! m_skwoermZones[otherSZ]->isAlive() ) { const real dist2 = (m_skwoermZones[localSZ]->getPos()- m_skwoermZones[otherSZ]->getPos()).abs2(); if( dist2 <= SZ_REVAMP_RADIUS*SZ_REVAMP_RADIUS && m_skwoermZones[otherSZ]->getNAttachedObjects() > 0 ) { m_skwoermZones[otherSZ]->keepAlive(); revampSkwoermZone( otherSZ ); } } } } /**********************************************************/ void World::doTimestep() { SDL_mutexP( m_mutex ); // update particles m_particles.doTimestep(); // update collidable objects for( unsigned int i = 0; i < m_collidables.size(); i++ ) { m_collidables[i]->update(); if( m_collidables[i]->removeMeAfterUpdate() ) { deleteObject( m_collidables[i] ); i--; } } // update noncollidable objects for( unsigned int i = 0; i < m_noncollidables.size(); i++ ) { m_noncollidables[i]->update(); if( m_noncollidables[i]->removeMeAfterUpdate() ) { deleteObject( m_noncollidables[i] ); i--; } } // update the bonus creation m_bonusmanager.updatePlacement(); // update the players' weapons for ( Uint8 p = 0; p < m_player.size(); p++ ) { Player* player = m_player[p]; for ( int i = 0; i < Player::m_MAX_WEAPONS; i++ ) { player->getWeapon( i )->doTimestep( getDT( player->getAvatar()->getPos() ) ); } } // Skwoerm zones only have to be treated if creation probability > 0 if ( m_szProbability > 0.0 ) { //LOG( 2 ) INFO( "World::doTimestep: look for new Skwoermzone events\n" ); // look for new Skwoermzone events for( Uint8 p1 = 0; p1 < m_player.size(); p1++ ) { Avatar* avatar1 = m_player[p1]->getAvatar(); if( avatar1->isLiving() && ! avatar1->isGrounded() ) { for( Uint8 p2 = p1 + 1; p2 < m_player.size(); p2++ ) { Avatar* avatar2 = m_player[p2]->getAvatar(); if( avatar2->isLiving() && ! avatar2->isGrounded() ) { const real dist2 = (avatar2->getPos() - avatar1->getPos()).abs2(), dist2In15Frames = (avatar2->getPos() - avatar1->getPos() + (avatar2->getVel() - avatar1->getVel())*15.0).abs2(), minDist2 = min( dist2, dist2In15Frames ); // look for new SkwoermZone candidates if( minDist2 < SZ_CREATION_RADIUS*SZ_CREATION_RADIUS ) { // create SZs with certain probability if ( m_random.getNormedReal() < m_szProbability ) { if( ! avatar1->isInSkwoermZone() ) { // add skwoerm zone to avatar 1 SkwoermZone* sz = new SkwoermZone( avatar1, 0.2 ); sz->setRadii( SZ_RADIUS, SZ_DECAY_RADIUS ); m_skwoermZones.push_back( sz ); } if( ! avatar2->isInSkwoermZone() ) { // add skwoerm zone to avatar 2 SkwoermZone* sz = new SkwoermZone( avatar2, 0.2 ); sz->setRadii( SZ_RADIUS, SZ_DECAY_RADIUS ); m_skwoermZones.push_back( sz ); } } } } } } } // create secondary SZ for players that are nearby for( unsigned int s = 0; s < m_skwoermZones.size(); s++ ) { SkwoermZone* sz = m_skwoermZones[s]; if( sz->isAlive() ) { for( Uint8 p = 0; p < m_player.size(); p++ ) { Avatar* avatar = m_player[p]->getAvatar(); if( avatar->isLiving() && ! avatar->isInSkwoermZone() && (avatar->getPos()-sz->getPos()).abs2() < SZ_SECONDARY_RADIUS*SZ_SECONDARY_RADIUS ) { // add skwoerm zone to avatar SkwoermZone* newSz = new SkwoermZone( avatar, 0.2 ); newSz->setRadii( SZ_RADIUS, SZ_DECAY_RADIUS ); m_skwoermZones.push_back( newSz ); } } } } // look for pairs of SZs which are close enough together to stay alive for( unsigned int s1 = 0; s1 < m_skwoermZones.size(); s1++ ) { SkwoermZone* sz1 = m_skwoermZones[s1]; for ( unsigned int s2 = s1+1; s2 < m_skwoermZones.size(); s2++ ) { SkwoermZone* sz2 = m_skwoermZones[s2]; const real dist2 = (sz2->getPos() - sz1->getPos()).abs2(), dist2In15Frames = (sz2->getPos() - sz1->getPos() + (sz2->getVel() - sz1->getVel())*15.0).abs2(), minDist2 = min( dist2, dist2In15Frames ); if( minDist2 < SZ_SECONDARY_RADIUS*SZ_SECONDARY_RADIUS && sz1->getNAttachedObjects() > 0 && sz2->getNAttachedObjects() > 0) { sz1->keepAlive(); sz2->keepAlive(); } } } // traverse SZs to spread ALIVE state for( unsigned int s = 0; s < m_skwoermZones.size(); s++ ) { SkwoermZone* sz = m_skwoermZones[s]; if( sz->isAlive() && sz->getNAttachedObjects() > 0 ) revampSkwoermZone( s ); } for( std::vector::iterator it = m_skwoermZones.begin(); it != m_skwoermZones.end(); it++ ) { SkwoermZone* sz = *it; sz->update(); if( sz->removeMeAfterUpdate() ) { m_skwoermZones.erase( it-- ); delete sz; } } } SDL_mutexV( m_mutex ); } /**********************************************************/ real World::getDT( const Vector& pos ) const { real dt = 1.0; std::vector::const_iterator it; for ( it = m_skwoermZones.begin(); it != m_skwoermZones.end(); it++ ) { SkwoermZone* st = *it; if( st->containsPoint( pos )) { const real dt2 = st->getDT( pos ); if( dt2 < dt ) dt = dt2; } } DBG( 3 ) CHECK( dt >= 0.2 && dt <= 1.0, "World::getDT: invalid dt (%f)\n", dt ); return dt; } /**********************************************************/ void World::draw( SDL_Surface* surface, ClientWopSprites& sprites, SDL_Rect& mapRect ) const { SDL_mutexP( m_mutex ); // draw the noncollidable objects and ropes, part 1 for( unsigned int i = 0; i < m_noncollidables.size(); i++ ) { NonCollidableObject* obj = m_noncollidables[i]; // ropes must be drawn seperately, since they do not use sprites if( obj->getID() == ROPE ) { if ( SDL_MUSTLOCK( surface ) ) SDL_LockSurface( surface ); Rope* const rope = dynamic_cast( obj ); // draw the rope line Graphics::drawLine( surface, rope->getStartIntPositionX() - mapRect.x, rope->getStartIntPositionY() - mapRect.y, rope->getEndIntPositionX() - mapRect.x, rope->getEndIntPositionY() - mapRect.y, rope->getColor() ); // for charged ropes we must draw a lightning if( rope->isCharged() ) { Vector sparkPosOld( rope->getStartPosition() ), sparkPosNew, ropeVector( rope->getEndPosition() - sparkPosOld ); for( real pos = 0.1 + 0.1 * localRnd.getNormedReal(); pos < 1.0; pos += 0.1 + 0.1 * localRnd.getNormedReal() ) { sparkPosNew = rope->getStartPosition() + ropeVector * pos + localRnd.getVector( 30.0 * (0.25 - (0.5-pos)*(0.5-pos)) ); // draw this lightning line Graphics::drawLine( surface, ROUND( sparkPosOld.x ) - mapRect.x, ROUND( sparkPosOld.y ) - mapRect.y, ROUND( sparkPosNew.x ) - mapRect.x, ROUND( sparkPosNew.y ) - mapRect.y, localRnd.getColor( 0x9999ff, 0xeeeeff )); sparkPosOld = sparkPosNew; } // draw finish lightning line Graphics::drawLine( surface, ROUND( sparkPosOld.x ) - mapRect.x, ROUND( sparkPosOld.y ) - mapRect.y, rope->getEndIntPositionX() - mapRect.x, rope->getEndIntPositionY() - mapRect.y, localRnd.getColor( 0x9999ff, 0xeeeeff )); // delete the charge flag (it will be set // anew, if we hold on electrifying something) rope->setCharged( false ); } if ( SDL_MUSTLOCK( surface ) ) SDL_UnlockSurface( surface ); // the ordinary objects are drawn using sprites } else { if( obj->isVisible() && obj->getDrawBeforeCollidableObjects() ) sprites.draw( *obj, obj->getIntPosX() - mapRect.x, obj->getIntPosY() - mapRect.y, surface ); } } // draw the collidable objects for( unsigned int i = 0; i < m_collidables.size(); i++ ) { CollidableObject* obj = m_collidables[i]; if( obj->isVisible() ) sprites.draw( *obj, obj->getIntPosX() - mapRect.x, obj->getIntPosY() - mapRect.y, surface ); } // draw the particles m_particles.draw( surface, mapRect ); // draw the noncollidable objects, part 2 for( unsigned int i = 0; i < m_noncollidables.size(); i++ ) { NonCollidableObject* obj = m_noncollidables[i]; if( obj->isVisible() && ! obj->getDrawBeforeCollidableObjects() ) sprites.draw( *obj, obj->getIntPosX() - mapRect.x, obj->getIntPosY() - mapRect.y, surface ); } /* std::vector::const_iterator it; Uint32 red = SDL_MapRGB( surface->format, 255, 0, 0 ); Uint32 blue = SDL_MapRGB( surface->format, 0, 0, 255 ); for ( it = m_skwoermZones.begin(); it != m_skwoermZones.end(); it++ ) { SkwoermZone* st = *it; Vector center = st->getPosition(); real radius = st->getRadius(); real decayRadius = st->getDecayRadius(); Graphics::drawCircle( surface, static_cast( center.x - mapRect.x), static_cast( center.y - mapRect.y ), static_cast( radius ), red ); Graphics::drawCircle( surface, static_cast( center.x - mapRect.x ), static_cast( center.y - mapRect.y ), static_cast( radius - decayRadius ), blue ); } */ SDL_mutexV( m_mutex ); } /**********************************************************/ /* World* World::createAndDeserialize( Uint8*& bufferPointer ) { ASSERT( false, "World::createAndDeserialize: don't use me, I'm broken\n" ); World* newObject = NEW World( 1, 1 ); newObject->getMap()->deserialize( bufferPointer ); Uint8 numberPlayers; Serialize::deserialize( bufferPointer, numberPlayers ); for ( Uint8 p = 0; p < numberPlayers; p++ ) { Player* player = NEW Player(); player->deserialize( bufferPointer ); newObject->m_player.push_back( player ); } return newObject; } */ /**********************************************************/ // The common methods related to serialization. For a description look // at the comments in map.hpp Uint32 World::getSerializeBufferSize() const { Uint32 bufferSize = 0; ADD_TAG_SIZE( bufferSize ); // frame number bufferSize += Serialize::sizeOf(); ADD_TAG_SIZE( bufferSize ); // server settings bufferSize += WopSettings::getInstance()->getSerializeBufferSize(); ADD_TAG_SIZE( bufferSize ); // map ServerMap* serverMap = dynamic_cast( m_map ); if ( serverMap ) { bufferSize += serverMap->getSerializeBufferSize(); } else { CHECK( false, "World::getSerializeBufferSize: map is already optimized\n" ); ClientMap* clientMap = dynamic_cast( m_map ); bufferSize += clientMap->getSerializeBufferSize(); } ADD_TAG_SIZE( bufferSize ); // collidable objects bufferSize += Serialize::sizeOf(); // vector length for( Uint32 i = 0; i < m_collidables.size(); i++ ) { ADD_TAG_SIZE( bufferSize ); bufferSize += Serialize::sizeOf(); // object ID bufferSize += m_collidables[i]->getSerializeBufferSize(); } ADD_TAG_SIZE( bufferSize ); // noncollidable objects bufferSize += Serialize::sizeOf(); // vector length for( Uint32 i = 0; i < m_noncollidables.size(); i++ ) { ADD_TAG_SIZE( bufferSize ); bufferSize += Serialize::sizeOf(); // object ID bufferSize += m_noncollidables[i]->getSerializeBufferSize(); } ADD_TAG_SIZE( bufferSize ); bufferSize += Serialize::sizeOf(); ADD_TAG_SIZE( bufferSize ); bufferSize += Serialize::sizeOf(); for ( Uint32 i = 0; i < m_skwoermZones.size(); i++ ) { ADD_TAG_SIZE( bufferSize ); bufferSize += m_skwoermZones[i]->getSerializeBufferSize(); } ADD_TAG_SIZE( bufferSize ); // player list bufferSize += Serialize::sizeOf(); for ( Uint8 p = 0; p < m_player.size(); p++ ) { bufferSize += Serialize::sizeOf(); // index of the avatar bufferSize += m_player[p]->getSerializeBufferSize(); } ADD_TAG_SIZE( bufferSize ); // particles bufferSize += m_particles.getSerializeBufferSize(); ADD_TAG_SIZE( bufferSize ); // random bufferSize += m_random.getSerializeBufferSize(); ADD_TAG_SIZE( bufferSize ); // scorekeeper bufferSize += m_scorekeeper.getSerializeBufferSize(); ADD_TAG_SIZE( bufferSize ); // bonus manager bufferSize += m_bonusmanager.getSerializeBufferSize(); ADD_TAG_SIZE( bufferSize ); // pool for noncollidable objects bufferSize += m_noncollidablePool.getSerializeBufferSize(); ADD_TAG_SIZE( bufferSize ); return bufferSize; } /**********************************************************/ void World::serialize( Uint8*& bufferPointer ) const { // this macro must stay in the first line START_SERIALIZED_SIZE_CHECK( bufferPointer ); SERIALIZE_TAG( bufferPointer ) // frame number Serialize::serialize( m_frame, bufferPointer ); SERIALIZE_TAG( bufferPointer ) // server settings WopSettings::getInstance()->serializeServerSettings( bufferPointer ); SERIALIZE_TAG( bufferPointer ) // map ServerMap* serverMap = dynamic_cast( m_map ); if ( serverMap ) { serverMap->serialize( bufferPointer ); } else { CHECK( false, "World::serialize: map is already optimized\n" ); ClientMap* clientMap = dynamic_cast( m_map ); clientMap->serialize( bufferPointer ); } SERIALIZE_TAG( bufferPointer ) // bonus manager // Must be DEserialized before all collidable objects, because // the bonus manager also serializes the bonus limits and the // deserialization of collidable objects might already create // bonus objects, which register at the bonus manager. m_bonusmanager.serialize( bufferPointer ); SERIALIZE_TAG( bufferPointer ) m_noncollidablePool.serialize( bufferPointer ); SERIALIZE_TAG( bufferPointer ) // collidable objects // Must he serialized before the player list, since in the // deserialization process the players will be linked with their // avatars, that must be present at this time. Serialize::serialize( static_cast(m_collidables.size()), bufferPointer ); for( Uint32 i = 0; i < m_collidables.size(); i++ ) { Serialize::serialize( // object ID static_cast(m_collidables[i]->getID()), bufferPointer ); m_collidables[i]->serialize( bufferPointer ); // object itself SERIALIZE_TAG( bufferPointer ) } SERIALIZE_TAG( bufferPointer ) // noncollidable objects Serialize::serialize( static_cast(m_noncollidables.size()), bufferPointer ); for( Uint32 i = 0; i < m_noncollidables.size(); i++ ) { SERIALIZE_TAG( bufferPointer ) Serialize::serialize( // object ID static_cast(m_noncollidables[i]->getID()), bufferPointer ); m_noncollidables[i]->serialize( bufferPointer ); // object itself } SERIALIZE_TAG( bufferPointer ) Serialize::serialize( m_szProbability, bufferPointer ); SERIALIZE_TAG( bufferPointer ) Serialize::serialize( static_cast(m_skwoermZones.size()), bufferPointer ); for( Uint32 i = 0; i < m_skwoermZones.size(); i++ ) { SERIALIZE_TAG( bufferPointer ) m_skwoermZones[i]->serialize( bufferPointer ); // object itself } SERIALIZE_TAG( bufferPointer ) // player list Serialize::serialize( static_cast( m_player.size() ), bufferPointer ); for ( Uint8 p = 0; p < m_player.size(); p++ ) { // find index of the attached avatar for( Uint32 i = 0; i < m_collidables.size(); i++ ) { if( m_collidables[i] == m_player[p]->getAvatar() ) { Serialize::serialize( i, bufferPointer ); break; } DBG( 1 ) ASSERT( i < m_collidables.size()-1, "World::serialize: could not find avatar for " "player \"%s\"\n", m_player[p]->getName().getString() ); } m_player[p]->serialize( bufferPointer ); } SERIALIZE_TAG( bufferPointer ) // particles m_particles.serialize( bufferPointer ); SERIALIZE_TAG( bufferPointer ) // random m_random.serialize( bufferPointer ); SERIALIZE_TAG( bufferPointer ) // scorekeeper m_scorekeeper.serialize( bufferPointer ); SERIALIZE_TAG( bufferPointer ) // this macro must stay in the last line END_SERIALIZED_SIZE_CHECK( bufferPointer, World ); } /**********************************************************/ void World::deserialize( Uint8*& bufferPointer ) { DESERIALIZE_TAG( bufferPointer ) // frame number Serialize::deserialize( bufferPointer, m_frame ); DESERIALIZE_TAG( bufferPointer ) // server settings WopSettings::getInstance()->deserializeServerSettings( bufferPointer ); m_scorekeeper.setGameMode( WopSettings::getInstance()->getGameMode() ); DESERIALIZE_TAG( bufferPointer ) // deserialize the map DBG( 2 ) INFO( "World::deserialize: deserializing of 'map' started\n" ); ServerMap* serverMap = dynamic_cast( m_map ); if ( serverMap ) { serverMap->deserialize( bufferPointer ); } else { CHECK( false, "World::deserialize: map is already optimized\n" ); ClientMap* clientMap = dynamic_cast( m_map ); clientMap->deserialize( bufferPointer ); } DBG( 2 ) INFO( "World::deserialize: deserializing of 'map' ended\n" ); DESERIALIZE_TAG( bufferPointer ) // bonus manager // Must be DEserialized before all collidable objects, because // the bonus manager also serializes the bonus limits and the // deserialization of collidable objects might already create // bonus objects, which register at the bonus manager. DBG( 2 ) INFO( "World::deserialize: deserializing of 'bonus manager' started\n" ); m_bonusmanager.deserialize( bufferPointer ); DBG( 2 ) INFO( "World::deserialize: deserializing of 'bonus manager' ended\n" ); DESERIALIZE_TAG( bufferPointer ) DBG( 2 ) INFO( "World::deserialize: deserializing of 'non collidable pool' started\n" ); m_noncollidablePool.deserialize( bufferPointer ); DBG( 2 ) INFO( "World::deserialize: deserializing of 'non collidable pool' ended\n" ); DESERIALIZE_TAG( bufferPointer ) // collidable objects // Must be deserialized befor the player list, because the player // list will read an index for each player, which is the index of // each player's avatar in the object list. DBG( 2 ) INFO( "World::deserialize: deserializing of 'collidable objects' started\n" ); Uint32 numObjects = 0; Sint32 objectID = -1; Serialize::deserialize( bufferPointer, numObjects ); for( Uint32 i = 0; i < numObjects; i++ ) { DBG( 2 ) INFO( "World::deserialize: deserializing 'collidable object' %i of %i\n", i, numObjects ); Serialize::deserialize( bufferPointer, objectID ); // get object from the world Object* object = newObject( objectID ); // deserialize object object->deserialize( bufferPointer ); DESERIALIZE_TAG( bufferPointer ) } DBG( 2 ) INFO( "World::deserialize: deserializing of 'collidable objects' ended\n" ); DESERIALIZE_TAG( bufferPointer ) // noncollidable objects DBG( 2 ) INFO( "World::deserialize: deserializing of 'noncollidable objects' started\n" ); numObjects = 0; Serialize::deserialize( bufferPointer, numObjects ); for( Uint32 i = 0; i < numObjects; i++ ) { DBG( 2 ) INFO( "World::deserialize: deserializing of 'noncollidable objects': object %i of %i\n", i, numObjects ); DESERIALIZE_TAG( bufferPointer ) // deserialize the object ID Serialize::deserialize( bufferPointer, objectID ); DBG( 3 ) INFO( "World::deserialize: object type: %s\n", Object::getIDString(objectID) ); // get object from the world Object* object = newObject( objectID ); // deserialize object object->deserialize( bufferPointer ); } DESERIALIZE_TAG( bufferPointer ) Serialize::deserialize( bufferPointer, m_szProbability ); DESERIALIZE_TAG( bufferPointer ) // skwoerm times DBG( 2 ) INFO( "World::deserialize: deserializing of 'skwoerm zones' started\n" ); numObjects = 0; Serialize::deserialize( bufferPointer, numObjects ); for( Uint32 i = 0; i < numObjects; i++ ) { DBG( 2 ) INFO( "World::deserialize: deserializing of 'skwoerm zones': skwoerm zone %i of %i\n", i, numObjects ); DESERIALIZE_TAG( bufferPointer ) SkwoermZone* sz = new SkwoermZone(); // deserialize object sz->deserialize( bufferPointer ); m_skwoermZones.push_back( sz ); } AttachableRegistrar::getInstance()->startIDToPtrConversion(); AttachableRegistrar::getInstance()->reset(); DBG( 2 ) INFO( "World::deserialize: deserializing of 'noncollidable objects' ended\n" ); DESERIALIZE_TAG( bufferPointer ) // deserialize the player list DBG( 2 ) INFO( "World::deserialize: deserializing of 'players' started\n" ); Uint8 numberPlayers; Serialize::deserialize( bufferPointer, numberPlayers ); m_player.clear(); for ( Uint8 p = 0; p < numberPlayers; p++ ) { // read index of the player's avatar in noncollidable object list Uint32 avatarIndex; Serialize::deserialize( bufferPointer, avatarIndex ); // create player Player* player = NEW Player(); player->deserialize( bufferPointer ); m_player.push_back( player ); // link player with its avatar player->attachAvatar( static_cast(m_collidables[avatarIndex]) ); DBG( 1 ) ASSERT( m_collidables[avatarIndex]->getID() <= LAST_AVATAR, "World::deserialize: linked player with a non-avatar " "object (ID %d == \"%s\")\n", m_collidables[avatarIndex]->getID(), ObjectIDString[m_collidables[avatarIndex]->getID()] ); } DBG( 2 ) INFO( "World::deserialize: deserializing of 'players' ended\n" ); DESERIALIZE_TAG( bufferPointer ) // particles DBG( 2 ) INFO( "World::deserialize: deserializing of 'particles' started\n" ); m_particles.deserialize( bufferPointer ); DBG( 2 ) INFO( "World::deserialize: deserializing of 'particles' ended\n" ); DESERIALIZE_TAG( bufferPointer ) // random DBG( 2 ) INFO( "World::deserialize: deserializing of 'random' started\n" ); m_random.deserialize( bufferPointer ); DBG( 2 ) INFO( "World::deserialize: deserializing of 'random' ended\n" ); DESERIALIZE_TAG( bufferPointer ) // scorekeeper DBG( 2 ) INFO( "World::deserialize: deserializing of 'scorekeeper' started\n" ); m_scorekeeper.deserialize( bufferPointer ); DBG( 2 ) INFO( "World::deserialize: deserializing of 'scorekeeper' ended\n" ); DESERIALIZE_TAG( bufferPointer ) } void World::reset() { SDL_mutexP( m_mutex ); // reset frame counter to zero m_frame = 0; // detach all avatars, they will die after that anyway for( Uint8 p = 0; p < m_player.size(); p++ ) { m_player[p]->detachAvatar(); delete m_player[p]; } // delete all collidable objects for( unsigned int i = 0; i < m_collidables.size(); i++ ) { delete m_collidables[i]; } m_collidables.clear(); // delete all noncollidable objects for( unsigned int i = 0; i < m_noncollidables.size(); i++ ) { #ifdef USE_NONCOLLIDABLE_POOL // Try to remove the object from the pool of non collidable // objects. The pool will check, if the object is of that // type, that is administration by this pool. In this case // the pool will remove internally the object from the list // of used objects and return true, which means we are not // allowed to delete the object, because it will be deleted // by the pool itself. if( ! m_noncollidablePool.isGeneratedByThisPool( m_noncollidables[i]->getID()) ) { delete m_noncollidables[i]; } #else delete m_noncollidables[i]; #endif } m_noncollidables.clear(); // reset the pool of noncollidable objects m_noncollidablePool.reset(); m_particles.reset(); // clear all skwoerm times for ( unsigned int i = 0; i < m_skwoermZones.size(); i++ ) { delete m_skwoermZones[i]; } m_skwoermZones.clear(); m_scorekeeper.reset(); m_bonusmanager.reset(); // Note: The map m_map is resetted automatically during the next // deserialization SDL_mutexV( m_mutex ); } /**********************************************************/ void World::dump() { SDL_mutexP( m_mutex ); INFO( "World status:\n" ); INFO( "\tFrame number: %u\n", m_frame ); for ( int i = 0; i < int( m_player.size() ); i++ ) { Player* p = m_player[i]; Avatar* a = p->getAvatar(); INFO( "\tPlayer %d position: (%f, %f)\n", i, a->getPosX(), a->getPosY() ); } SDL_mutexV( m_mutex ); } void World::dump( std::ostream& out ) const { using namespace std; SDL_mutexP( m_mutex ); out << "***********" << endl; out << "* Players *" << endl; out << "***********" << endl; for ( unsigned int i = 0; i < m_player.size(); i++ ) m_player[i]->dump( out ); out << "**********************" << endl; out << "* Collidable objects *" << endl; out << "**********************" << endl; for ( unsigned int i = 0; i < m_collidables.size(); i++ ) m_collidables[i]->dump( out ); SDL_mutexV( m_mutex ); } /**********************************************************/ void World::printPlayerVectorInfo() { INFO( "World::printPlayerVectorInfo:\n" ); INFO( "\tnPlayers: %i\n", m_player.size() ); for ( int i = 0; i < int( m_player.size() ); i++ ) { Player* p = m_player[i]; INFO( "\tplayer %d: id: %i player/team id: %i name: %s\n", i, p->getPlayerID(), p->getPlayerOrTeamID(), p->getName().getString() ); } } /**********************************************************/ CollidableObject* World::getCollidableAt( const int x, const int y ) { SDL_mutexP( m_mutex ); DBG( 4 ) { CollidableObject* foundObject = NULL; for( unsigned int o = 0; o < m_collidables.size(); o++ ) { if( m_collidables[o]->testHit( x, y ) ) { if( foundObject == NULL ) { foundObject = m_collidables[o]; } else { CHECK( false, "World::getCollidableAt: found more " "than one object at (%i, %i): %s (%i, %i, %i, %i) and " "%s (%i, %i, %i, %i)\n", x, y, ObjectIDString[foundObject->getID()], foundObject->getIntPosX(), foundObject->getIntPosY(), foundObject->getCollRectWidth(), foundObject->getCollRectHeight(), ObjectIDString[m_collidables[o]->getID()], m_collidables[o]->getIntPosX(), m_collidables[o]->getIntPosY(), m_collidables[o]->getCollRectWidth(), m_collidables[o]->getCollRectHeight() ); String comment; comment.format( "World::getCollidableAt: found more than one object at (%i, %i)", x, y ); m_map->saveFlags( comment ); } } } if( foundObject ) { SDL_mutexV( m_mutex ); return foundObject; } } else { for( unsigned int o = 0; o < m_collidables.size(); o++ ) { if( m_collidables[o]->testHit( x, y ) ) { SDL_mutexV( m_mutex ); return m_collidables[o]; } } } LOG( 1 ) CHECK( false, "World::getCollidableAt: cannot find any collidable " "object at (%i, %i)\n", x, y ); SDL_mutexV( m_mutex ); return NULL; } /**********************************************************/ void World::scanObjectsForFocus() { LOG( 3 ) INFO( "World::scanObjectsForFocus: scanning\n" ); m_focusedObject = NULL; int maxPriority = -1000; for( vector::iterator collIt = m_collidables.begin(); collIt != m_collidables.end(); collIt++ ) { const int priority = (*collIt)->getFocusPriority(); LOG( 4 ) INFO( "World::scanObjectsForFocus: object '%s' with " "priority %i\n", (*collIt)->getIDString(), priority ); if( priority > maxPriority ) { m_focusedObject = *collIt; maxPriority = priority; } } LOG( 4 ) { if( m_focusedObject ) { INFO( "World::scanObjectsForFocus: focusing '%s' with " "priority %i\n", m_focusedObject->getIDString(), maxPriority ); } } } /**********************************************************/ void World::focusNextAvatar() { INFO( "World::focusNextAvatar: start %i\n", m_player.size() ); bool focusLocalPlayer = false; // no players here yet? => nothing to do if( m_player.size() == 0 ) return; vector::iterator localPlayerIt = m_player.end(), focusedPlayerIt = m_player.end(), nextFocusedPlayerIt; // look for focused and local player for( vector::iterator playerIt = m_player.begin(); playerIt != m_player.end(); playerIt++ ) { // is local player? if( (*playerIt)->isLocalPlayer() ) localPlayerIt = playerIt; // is focused player? if( (*playerIt)->getAvatar()->getFocusPriority() >= FOCUS_PRIO_FORCE_FOCUS ) focusedPlayerIt = playerIt; } // there is no local player? => nothing to do if( localPlayerIt == m_player.end() ) return; // is the local player's avatar living? => focus local player if( (*localPlayerIt)->getAvatar()->isLiving() ) { focusLocalPlayer = true; nextFocusedPlayerIt = localPlayerIt; } // no forced focus on any player? => local player is currently focused if( focusedPlayerIt == m_player.end() ) focusedPlayerIt = localPlayerIt; // reset forced focus in all collidable objects for( vector::iterator collIt = m_collidables.begin(); collIt != m_collidables.end(); collIt++ ) { if( (*collIt)->getFocusPriority() >= FOCUS_PRIO_FORCE_FOCUS ) (*collIt)->setFocusPriority( (*collIt)->getFocusPriority() - FOCUS_PRIO_FORCE_FOCUS ); } if( ! focusLocalPlayer ) { // choose next players' avatar as focus object nextFocusedPlayerIt = focusedPlayerIt; do { nextFocusedPlayerIt++; if( nextFocusedPlayerIt == m_player.end() ) { nextFocusedPlayerIt = m_player.begin(); } if( (*nextFocusedPlayerIt)->getAvatar()->isLiving() ) break; } while( focusedPlayerIt != nextFocusedPlayerIt ); } // set focus priority for newly focused player (*nextFocusedPlayerIt)->getAvatar()->setFocusPriority( FOCUS_PRIO_FORCE_FOCUS + (*nextFocusedPlayerIt)->getAvatar()->getFocusPriority() ); scanObjectsForFocus(); INFO( "World::focusNextAvatar: end\n" ); } /**********************************************************/