Example #1
0
Activity CNPC_Crow::NPC_TranslateActivity( Activity eNewActivity )
{
	if ( IsFlying() && eNewActivity == ACT_IDLE )
	{
		return ACT_FLY;
	}

	if ( eNewActivity == ACT_FLY )
	{
		if ( m_flSoarTime < gpGlobals->curtime )
		{
			//Adrian: This should be revisited.
			if ( random->RandomInt( 0, 100 ) <= 50 && m_bSoar == false && GetAbsVelocity().z < 0 )
			{
				m_bSoar = true;
				m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 1, 4 );
			}
			else
			{
				m_bSoar = false;
				m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 3, 5 );
			}
		}

		if ( m_bSoar == true )
		{
			return (Activity)ACT_CROW_SOAR;
		}
		else
			return ACT_FLY;
	}

	return BaseClass::NPC_TranslateActivity( eNewActivity );
}
bool FlyCameraController::OnPreEvent( const irr::SEvent& event )
{
	if ( IsFlying() )
	{
		return true;
	}

	return false;
}
Example #3
0
int Game_Vehicle::GetAltitude() const {
	if (!IsFlying())
		return 0;
	else if (IsAscending())
		return (SCREEN_TILE_SIZE - data()->remaining_ascent) / (SCREEN_TILE_SIZE / TILE_SIZE);
	else if (IsDescending())
		return data()->remaining_descent / (SCREEN_TILE_SIZE / TILE_SIZE);
	else
		return SCREEN_TILE_SIZE / (SCREEN_TILE_SIZE / TILE_SIZE);
}
Example #4
0
void CNPC_Crow::UpdateEfficiency( bool bInPVS )	
{
	if ( IsFlying() )
	{
		SetEfficiency( ( GetSleepState() != AISS_AWAKE ) ? AIE_DORMANT : AIE_NORMAL ); 
		SetMoveEfficiency( AIME_NORMAL ); 
		return;
	}

	BaseClass::UpdateEfficiency( bInPVS );
}
bool FlyCameraController::PreRender3D()
{
	if ( IsFlying() )
	{
		if ( Animator_->hasFinished() )
		{
			ToSwitchCamera_->getSceneManager()->setActiveCamera(ToSwitchCamera_);
			ActiveCamera_->remove();

			ActiveCamera_ = nullptr;
			ToSwitchCamera_ = nullptr;
			Animator_ = nullptr;
		}
	}

	return false;
}
Example #6
0
void cPawn::HandleFalling(void)
{
	/* Not pretty looking, and is more suited to wherever server-sided collision detection is implemented.
	The following condition sets on-ground-ness if
	The player isn't swimming or flying (client hardcoded conditions) and
	they're on a block (Y is exact) - ensure any they could be standing on (including on the edges) is solid or
	they're on a slab (Y significand is 0.5) - ditto with slab check
	they're on a snow layer (Y divisible by 0.125) - ditto with snow layer check
	*/

	static const auto HalfWidth = GetWidth() / 2;
	static const auto EPS = 0.0001;

	/* Since swimming is decided in a tick and is asynchronous to this, we have to check for dampeners ourselves.
	The behaviour as of 1.8.9 is the following:
	- Landing in water alleviates all fall damage
	- Passing through any liquid (water + lava) and cobwebs "slows" the player down,
	i.e. resets the fall distance to that block, but only after checking for fall damage
	(this means that plummeting into lava will still kill the player via fall damage, although cobwebs
	will slow players down enough to have multiple updates that keep them alive)
	- Slime blocks reverse falling velocity, unless it's a crouching player, in which case they act as standard blocks.
	They also reset the topmost point of the damage calculation with each bounce,
	so if the block is removed while the player is bouncing or crouches after a bounce, the last bounce's zenith is considered as fall damage.

	With this in mind, we first check the block at the player's feet, then the one below that (because fences),
	and decide which behaviour we want to go with.
	*/
	BLOCKTYPE BlockAtFoot = (cChunkDef::IsValidHeight(POSY_TOINT)) ? GetWorld()->GetBlock(POS_TOINT) : E_BLOCK_AIR;

	/* We initialize these with what the foot is really IN, because for sampling we will move down with the epsilon above */
	bool IsFootInWater = IsBlockWater(BlockAtFoot);
	bool IsFootInLiquid = IsFootInWater || IsBlockLava(BlockAtFoot) || (BlockAtFoot == E_BLOCK_COBWEB);  // okay so cobweb is not _technically_ a liquid...
	bool IsFootOnSlimeBlock = false;

	/* The "cross" we sample around to account for the player width/girth */
	static const struct
	{
		int x, z;
	} CrossSampleCoords[] =
	{
		{ 0, 0 },
		{ 1, 0 },
		{ -1, 0 },
		{ 0, 1 },
		{ 0, -1 },
	};

	/* The blocks we're interested in relative to the player to account for larger than 1 blocks.
	This can be extended to do additional checks in case there are blocks that are represented as one block
	in memory but have a hitbox larger than 1 (like fences) */
	static const struct
	{
		int x, y, z;
	} BlockSampleOffsets[] =
	{
		{ 0, 0, 0 },  // TODO: something went wrong here (offset 0?)
		{ 0, -1, 0 },  // Potentially causes mis-detection (IsFootInWater) when player stands on block diagonal to water (i.e. on side of pool)
	};

	/* Here's the rough outline of how this mechanism works:
	We take the player's pointlike position (sole of feet), and expand it into a crosslike shape.
	If any of the five points hit a block, we consider the player to be "on" (or "in") the ground. */
	bool OnGround = false;
	for (size_t i = 0; i < ARRAYCOUNT(CrossSampleCoords); i++)
	{
		/* We calculate from the player's position, one of the cross-offsets above, and we move it down slightly so it's beyond inaccuracy.
		The added advantage of this method is that if the player is simply standing on the floor,
		the point will move into the next block, and the floor() will retrieve that instead of air. */
		Vector3d CrossTestPosition = GetPosition() + Vector3d(CrossSampleCoords[i].x * HalfWidth, -EPS, CrossSampleCoords[i].z * HalfWidth);

		/* We go through the blocks that we consider "relevant" */
		for (size_t j = 0; j < ARRAYCOUNT(BlockSampleOffsets); j++)
		{
			Vector3i BlockTestPosition = CrossTestPosition.Floor() + Vector3i(BlockSampleOffsets[j].x, BlockSampleOffsets[j].y, BlockSampleOffsets[j].z);

			if (!cChunkDef::IsValidHeight(BlockTestPosition.y))
			{
				continue;
			}

			BLOCKTYPE Block = GetWorld()->GetBlock(BlockTestPosition);
			NIBBLETYPE BlockMeta = GetWorld()->GetBlockMeta(BlockTestPosition);

			/* we do the cross-shaped sampling to check for water / liquids, but only on our level because water blocks are never bigger than unit voxels */
			if (j == 0)
			{
				IsFootInWater |= IsBlockWater(Block);
				IsFootInLiquid |= IsFootInWater || IsBlockLava(Block) || (Block == E_BLOCK_COBWEB);  // okay so cobweb is not _technically_ a liquid...
				IsFootOnSlimeBlock |= (Block == E_BLOCK_SLIME_BLOCK);
			}

			/* If the block is solid, and the blockhandler confirms the block to be inside, we're officially on the ground. */
			if ((cBlockInfo::IsSolid(Block)) && (cBlockInfo::GetHandler(Block)->IsInsideBlock(CrossTestPosition - BlockTestPosition, Block, BlockMeta)))
			{
				OnGround = true;
			}
		}
	}

	/* So here's the use of the rules above: */
	/* 1. Falling in water absorbs all fall damage */
	bool FallDamageAbsorbed = IsFootInWater;

	/* 2. Falling in liquid (lava, water, cobweb) or hitting a slime block resets the "fall zenith".
	Note: Even though the pawn bounces back with no damage after hitting the slime block,
	the "fall zenith" will continue to increase back up when flying upwards - which is good */
	bool ShouldBounceOnSlime = true;

	if (IsPlayer())
	{
		auto Player = static_cast<cPlayer *>(this);

		/* 3. If the player is flying or climbing, absorb fall damage */
		FallDamageAbsorbed |= Player->IsFlying() || Player->IsClimbing();

		/* 4. If the player is about to bounce on a slime block and is not crouching, absorb all fall damage  */
		ShouldBounceOnSlime = !Player->IsCrouched();
		FallDamageAbsorbed |= (IsFootOnSlimeBlock && ShouldBounceOnSlime);
	}
	else
	{
		/* 5. Bouncing on a slime block absorbs all fall damage  */
		FallDamageAbsorbed |= IsFootOnSlimeBlock;
	}

	/* If the player is not crouching or is not a player, shoot them back up.
	NOTE: this will only work in some cases; should be done in HandlePhysics() */
	if (IsFootOnSlimeBlock && ShouldBounceOnSlime)
	{
		// TODO: doesn't work too well, causes dissatisfactory experience for players on slime blocks - SetSpeedY(-GetSpeedY());
	}

	// TODO: put player speed into GetSpeedY, and use that.
	// If flying, climbing, swimming, or going up...
	if (FallDamageAbsorbed || ((GetPosition() - m_LastPosition).y > 0))
	{
		// ...update the ground height to have the highest position of the player (i.e. jumping up adds to the eventual fall damage)
		m_LastGroundHeight = GetPosY();
	}

	if (OnGround)
	{
		auto Damage = static_cast<int>(m_LastGroundHeight - GetPosY() - 3.0);
		if ((Damage > 0) && !FallDamageAbsorbed)
		{
			TakeDamage(dtFalling, nullptr, Damage, Damage, 0);

			// Fall particles
			GetWorld()->BroadcastParticleEffect(
				"blockdust",
				GetPosition(),
				{ 0, 0, 0 },
				(Damage - 1.f) * ((0.3f - 0.1f) / (15.f - 1.f)) + 0.1f,  // Map damage (1 - 15) to particle speed (0.1 - 0.3)
				static_cast<int>((Damage - 1.f) * ((50.f - 20.f) / (15.f - 1.f)) + 20.f),  // Map damage (1 - 15) to particle quantity (20 - 50)
				{ { GetWorld()->GetBlock(POS_TOINT - Vector3i(0, 1, 0)), 0 } }
			);
		}

		m_bTouchGround = true;
		m_LastGroundHeight = GetPosY();
	}
	else
	{
		m_bTouchGround = false;
	}

	/* Note: it is currently possible to fall through lava and still die from fall damage
	because of the client skipping an update about the lava block. This can only be resolved by
	somehow integrating these above checks into the tracer in HandlePhysics. */
}
Example #7
0
void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
{
	if (m_ClientHandle != NULL)
	{
		if (m_ClientHandle->IsDestroyed())
		{
			// This should not happen, because destroying a client will remove it from the world, but just in case
			m_ClientHandle = NULL;
			return;
		}
		
		if (!m_ClientHandle->IsPlaying())
		{
			// We're not yet in the game, ignore everything
			return;
		}
	}

	m_Stats.AddValue(statMinutesPlayed, 1);
	
	if (!a_Chunk.IsValid())
	{
		// This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83)
		return;
	}
	
	super::Tick(a_Dt, a_Chunk);
	
	// Handle charging the bow:
	if (m_IsChargingBow)
	{
		m_BowCharge += 1;
	}
	
	// Handle updating experience
	if (m_bDirtyExperience)
	{
		SendExperience();
	}

	if (GetPosition() != m_LastPos) // Change in position from last tick?
	{
		// Apply food exhaustion from movement:
		ApplyFoodExhaustionFromMovement();
		
		cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this);
		m_ClientHandle->StreamChunks();
	}

	BroadcastMovementUpdate(m_ClientHandle);

	if (m_Health > 0)  // make sure player is alive
	{
		m_World->CollectPickupsByPlayer(this);

		if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge()))
		{
			FinishEating();
		}
		
		HandleFood();
	}
	
	if (m_IsFishing)
	{
		HandleFloater();
	}

	// Update items (e.g. Maps)
	m_Inventory.UpdateItems();

	// Send Player List (Once per m_LastPlayerListTime/1000 ms)
	cTimer t1;
	if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime())
	{
		m_World->SendPlayerList(this);
		m_LastPlayerListTime = t1.GetNowTime();
	}

	if (IsFlying())
	{
		m_LastGroundHeight = (float)GetPosY();
	}
}
Example #8
0
bool cPlayer::SaveToDisk()
{
	cFile::CreateFolder(FILE_IO_PREFIX + AString("players"));

	// create the JSON data
	Json::Value JSON_PlayerPosition;
	JSON_PlayerPosition.append(Json::Value(GetPosX()));
	JSON_PlayerPosition.append(Json::Value(GetPosY()));
	JSON_PlayerPosition.append(Json::Value(GetPosZ()));

	Json::Value JSON_PlayerRotation;
	JSON_PlayerRotation.append(Json::Value(GetYaw()));
	JSON_PlayerRotation.append(Json::Value(GetPitch()));
	JSON_PlayerRotation.append(Json::Value(GetRoll()));

	Json::Value JSON_Inventory;
	m_Inventory.SaveToJson(JSON_Inventory);

	Json::Value root;
	root["position"]       = JSON_PlayerPosition;
	root["rotation"]       = JSON_PlayerRotation;
	root["inventory"]      = JSON_Inventory;
	root["health"]         = m_Health;
	root["xpTotal"]        = m_LifetimeTotalXp;
	root["xpCurrent"]      = m_CurrentXp;
	root["air"]            = m_AirLevel;
	root["food"]           = m_FoodLevel;
	root["foodSaturation"] = m_FoodSaturationLevel;
	root["foodTickTimer"]  = m_FoodTickTimer;
	root["foodExhaustion"] = m_FoodExhaustionLevel;
	root["world"]          = GetWorld()->GetName();
	root["isflying"]       = IsFlying();

	if (m_GameMode == GetWorld()->GetGameMode())
	{
		root["gamemode"] = (int) eGameMode_NotSet;
	}
	else
	{
		root["gamemode"] = (int) m_GameMode;
	}

	Json::StyledWriter writer;
	std::string JsonData = writer.write(root);

	AString SourceFile;
	Printf(SourceFile, "players/%s.json", GetName().c_str() );

	cFile f;
	if (!f.Open(SourceFile, cFile::fmWrite))
	{
		LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", GetName().c_str(), SourceFile.c_str());
		return false;
	}
	if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size())
	{
		LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str()); 
		return false;
	}

	// Save the player stats.
	// We use the default world name (like bukkit) because stats are shared between dimensions/worlds.
	cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats);
	if (!StatSerializer.Save())
	{
		LOGERROR("Could not save stats for player %s", GetName().c_str());
		return false;
	}

	return true;
}
Example #9
0
//-----------------------------------------------------------------------------
// Purpose: Handles all flight movement because we don't ever build paths when
//			when we are flying.
// Input  : flInterval - Seconds to simulate.
//-----------------------------------------------------------------------------
bool CNPC_Crow::OverrideMove( float flInterval )
{
	if ( GetNavigator()->GetPath()->CurWaypointNavType() == NAV_FLY && GetNavigator()->GetNavType() != NAV_FLY )
	{
		SetNavType( NAV_FLY );
	}

	if ( IsFlying() )
	{
		if ( GetNavigator()->GetPath()->GetCurWaypoint() )
		{
			if ( m_flLastStuckCheck <= gpGlobals->curtime )
			{
				if ( m_vLastStoredOrigin == GetAbsOrigin() )
				{
					if ( GetAbsVelocity() == vec3_origin )
					{
						float flDamage = m_iHealth;
						
						CTakeDamageInfo	dmgInfo( this, this, flDamage, DMG_GENERIC );
						GuessDamageForce( &dmgInfo, vec3_origin - Vector( 0, 0, 0.1 ), GetAbsOrigin() );
						TakeDamage( dmgInfo );

						return false;
					}
					else
					{
						m_vLastStoredOrigin = GetAbsOrigin();
					}
				}
				else
				{
					m_vLastStoredOrigin = GetAbsOrigin();
				}
				
				m_flLastStuckCheck = gpGlobals->curtime + 1.0f;
			}

			if (m_bReachedMoveGoal )
			{
				SetIdealActivity( (Activity)ACT_CROW_LAND );
				SetFlyingState( FlyState_Landing );
				TaskMovementComplete();
			}
			else
			{
				SetIdealActivity ( ACT_FLY );
				MoveCrowFly( flInterval );
			}

		}
		else
		{
			SetSchedule( SCHED_CROW_IDLE_FLY );
			SetFlyingState( FlyState_Flying );
			SetIdealActivity ( ACT_FLY );
		}
		return true;
	}
	
	return false;
}
Example #10
0
//-----------------------------------------------------------------------------
// Purpose: Returns the best new schedule for this NPC based on current conditions.
//-----------------------------------------------------------------------------
int CNPC_Crow::SelectSchedule( void )
{
	if ( HasCondition( COND_CROW_BARNACLED ) )
	{
		// Caught by a barnacle!
		return SCHED_CROW_BARNACLED;
	}

	//
	// If we're flying, just find somewhere to fly to.
	//
	if ( IsFlying() )
	{
		return SCHED_CROW_IDLE_FLY;
	}

	//
	// If we were told to fly away via our FlyAway input, do so ASAP.
	//
	if ( HasCondition( COND_CROW_FORCED_FLY ) )
	{
		ClearCondition( COND_CROW_FORCED_FLY );
		return SCHED_CROW_FLY_AWAY;
	}

	//
	// If we're not flying but we're not on the ground, start flying.
	// Maybe we hopped off of something? Don't do this immediately upon
	// because we may be falling to the ground on spawn.
	//
	if ( !( GetFlags() & FL_ONGROUND ) && ( gpGlobals->curtime > 2.0 ) && m_bOnJeep == false )
	{
		return SCHED_CROW_FLY_AWAY;
	}

	//
	// If we heard a gunshot or have taken damage, fly away.
	//
	if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
	{
		return SCHED_CROW_FLY_AWAY;
	}

	if ( m_flDangerSoundTime <= gpGlobals->curtime )
	{
		if ( HasCondition( COND_HEAR_DANGER ) || HasCondition( COND_HEAR_COMBAT ) )
		{
			m_flDangerSoundTime = gpGlobals->curtime + 10.0f;
			return SCHED_CROW_FLY_AWAY;
		}
	}

	//
	// If someone we hate is getting WAY too close for comfort, fly away.
	//
	if ( HasCondition( COND_CROW_ENEMY_WAY_TOO_CLOSE ) )
	{
		ClearCondition( COND_CROW_ENEMY_WAY_TOO_CLOSE );

		m_nMorale = 0;
		return SCHED_CROW_FLY_AWAY;
	}

	//
	// If someone we hate is getting a little too close for comfort, avoid them.
	//
	if ( HasCondition( COND_CROW_ENEMY_TOO_CLOSE ) && m_flDangerSoundTime <= gpGlobals->curtime )
	{
		ClearCondition( COND_CROW_ENEMY_TOO_CLOSE );

		if ( m_bOnJeep == true )
		{
			m_nMorale = 0;
			return SCHED_CROW_FLY_AWAY;
		}

		if ( m_flEnemyDist > 400 )
		{
			return SCHED_CROW_WALK_AWAY;
		}
		else if ( m_flEnemyDist > 300 )
		{
			m_nMorale -= 1;
			return SCHED_CROW_RUN_AWAY;
		}
	}

	switch ( m_NPCState )
	{
		case NPC_STATE_IDLE:
		case NPC_STATE_ALERT:
		case NPC_STATE_COMBAT:
		{
			if ( !IsFlying() )
			{
				if ( m_bOnJeep == true )
				     return SCHED_IDLE_STAND;

				//
				// If we are hanging out on the ground, see if it is time to pick a new place to walk to.
				//
				if ( gpGlobals->curtime > m_flGroundIdleMoveTime )
				{
					m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 10.0f, 20.0f );
					return SCHED_CROW_IDLE_WALK;
				}

				return SCHED_IDLE_STAND;
			}

			// TODO: need idle flying behaviors!
		}
	}

	return BaseClass::SelectSchedule();
}
Example #11
0
bool cPlayer::SaveToDisk()
{
	cFile::CreateFolder(FILE_IO_PREFIX + AString("players/"));  // Create the "players" folder, if it doesn't exist yet (#1268)
	cFile::CreateFolder(FILE_IO_PREFIX + AString("players/") + m_UUID.substr(0, 2));

	// create the JSON data
	Json::Value JSON_PlayerPosition;
	JSON_PlayerPosition.append(Json::Value(GetPosX()));
	JSON_PlayerPosition.append(Json::Value(GetPosY()));
	JSON_PlayerPosition.append(Json::Value(GetPosZ()));

	Json::Value JSON_PlayerRotation;
	JSON_PlayerRotation.append(Json::Value(GetYaw()));
	JSON_PlayerRotation.append(Json::Value(GetPitch()));
	JSON_PlayerRotation.append(Json::Value(GetRoll()));

	Json::Value JSON_Inventory;
	m_Inventory.SaveToJson(JSON_Inventory);

	Json::Value JSON_EnderChestInventory;
	cEnderChestEntity::SaveToJson(JSON_EnderChestInventory, m_EnderChestContents);

	Json::Value root;
	root["position"]            = JSON_PlayerPosition;
	root["rotation"]            = JSON_PlayerRotation;
	root["inventory"]           = JSON_Inventory;
	root["enderchestinventory"] = JSON_EnderChestInventory;
	root["health"]              = m_Health;
	root["xpTotal"]             = m_LifetimeTotalXp;
	root["xpCurrent"]           = m_CurrentXp;
	root["air"]                 = m_AirLevel;
	root["food"]                = m_FoodLevel;
	root["foodSaturation"]      = m_FoodSaturationLevel;
	root["foodTickTimer"]       = m_FoodTickTimer;
	root["foodExhaustion"]      = m_FoodExhaustionLevel;
	root["isflying"]            = IsFlying();
	root["lastknownname"]       = GetName();
	root["SpawnX"]              = GetLastBedPos().x;
	root["SpawnY"]              = GetLastBedPos().y;
	root["SpawnZ"]              = GetLastBedPos().z;

	if (m_World != NULL)
	{
		root["world"] = m_World->GetName();
		if (m_GameMode == m_World->GetGameMode())
		{
			root["gamemode"] = (int) eGameMode_NotSet;
		}
		else
		{
			root["gamemode"] = (int) m_GameMode;
		}
	}
	else
	{
		// This happens if the player is saved to new format after loading from the old format
		root["world"]    = m_LoadedWorldName;
		root["gamemode"] = (int) eGameMode_NotSet;
	}

	Json::StyledWriter writer;
	std::string JsonData = writer.write(root);

	AString SourceFile = GetUUIDFileName(m_UUID);

	cFile f;
	if (!f.Open(SourceFile, cFile::fmWrite))
	{
		LOGWARNING("Error writing player \"%s\" to file \"%s\" - cannot open file. Player will lose their progress.",
			GetName().c_str(), SourceFile.c_str()
		);
		return false;
	}
	if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size())
	{
		LOGWARNING("Error writing player \"%s\" to file \"%s\" - cannot save data. Player will lose their progress. ",
			GetName().c_str(), SourceFile.c_str()
		);
		return false;
	}

	// Save the player stats.
	// We use the default world name (like bukkit) because stats are shared between dimensions/worlds.
	cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats);
	if (!StatSerializer.Save())
	{
		LOGWARNING("Could not save stats for player %s", GetName().c_str());
		return false;
	}

	return true;
}