////////////////////////////////////////////////////////////////////////// // renderHUD void GaGameUnit::renderHUD( ScnCanvasComponentRef Canvas, BcFixed TimeFraction ) { if( TeamID_ > 1 ) { return; } if( Behaviour_ != BEHAVIOUR_DEAD ) { BcU32 TextureIdx = Desc_.Type_; BcFixedVec2d GamePosition( getInterpolatedPosition( TimeFraction ) ); const BcReal ScaleFactor = 32.0f; BcVec2d Position( GamePosition.x(), GamePosition.y() ); BcVec2d Size( Desc_.Size_.x(), Desc_.Size_.y() ); Position *= ScaleFactor; Size *= ScaleFactor; // Draw range if selected. const BcReal MaxRangeRadius = Desc_.Range_ / ScaleFactor; // Draw health bar. BcFixed HealthFraction = Health_ / Desc_.Health_; BcVec2d HealthBarPosition = Position - Size * 0.5f + BcVec2d( 2.0f, -6.0f ); BcVec2d HealthBarSize = BcVec2d( HealthFraction * ( Size.x() - 4.0f ), 2.0f ); RsColour HealthColour = RsColour::GREEN ; if( HealthFraction <= 0.5f ) { HealthColour = RsColour::YELLOW; } if( HealthFraction <= 0.25f ) { HealthColour = RsColour::RED; } Canvas->drawSprite( HealthBarPosition, HealthBarSize, 0, HealthColour * RsColour( 1.0f, 1.0f, 1.0f, 0.75f ), 3 ); // Draw cool down bar. BcFixed MaxAttackTime = BcFixed( 1.0f ) / Desc_.RateOfAttack_; BcFixed CoolDownFraction = BcFixed( 1.0f ) - ( AttackTimer_ / MaxAttackTime ); BcVec2d CoolDownBarPosition = Position - Size * 0.5f + BcVec2d( 2.0f, -4.0f ); BcVec2d CoolDownBarSize = BcVec2d( CoolDownFraction * ( Size.x() - 4.0f ), 2.0f ); RsColour CoolDownColour = RsColour::PURPLE; Canvas->drawSprite( CoolDownBarPosition, CoolDownBarSize, 0, CoolDownColour * RsColour( 1.0f, 1.0f, 1.0f, 0.75f ), 3 ); } }
////////////////////////////////////////////////////////////////////////// // doAttack void GaGameUnit::doAttack( BcU32 TargetID ) { // Check in range. if( inRangeForAttack( TargetID ) == RANGE_IN ) { // If we can attack, launch projectile. if( AttackTimer_ <= 0.0f ) { AttackTimer_ = BcFixed( 1.0f ) / Desc_.RateOfAttack_; GaGameUnit* pTargetUnit = pSimulator_->getUnit( TargetID ); // Movement prediction. BcU32 PredictionIterations = 6; BcFixedVec2d TargetPosition = pTargetUnit->CurrState_.Position_; for( BcU32 Idx = 0; Idx < PredictionIterations; ++Idx ) { BcFixed FlyDistance = ( TargetPosition - CurrState_.Position_ ).magnitude(); BcFixed FlyTime = FlyDistance / Desc_.pDamageUnit_->MoveSpeed_; TargetPosition = TargetPosition + pTargetUnit->CurrState_.Velocity_ * FlyTime * pSimulator_->getSimulationRate(); } // Fire projectile. GaGameUnit* pGameProjectile = pSimulator_->addUnit( *Desc_.pDamageUnit_, TeamID_ + 2, CurrState_.Position_ ); pGameProjectile->setBehaviourDamage( TargetPosition ); } } }
////////////////////////////////////////////////////////////////////////// // tickBehaviour void GaGameUnit::tickBehaviour( BcFixed Delta ) { #if PSY_DEBUG static BcU32 BreakID = BcErrorCode; if( ID_ == BreakID ) { BcBreakpoint; } #endif switch( Behaviour_ ) { case BEHAVIOUR_IDLE: { CurrState_.Velocity_ = BcFixedVec2d( 0.0f, 0.0f ); BcU32 NearestUnit = pSimulator_->findNearestUnit( CurrState_.Position_, ID_, 1 << ( 1 - TeamID_ ) ); if( NearestUnit != BcErrorCode ) { doAttack( NearestUnit ); } } break; case BEHAVIOUR_GUARD: { CurrState_.Velocity_ = BcFixedVec2d( 0.0f, 0.0f ); BcU32 NearestUnit = pSimulator_->findNearestUnit( CurrState_.Position_, ID_, 1 << ( 1 - TeamID_ ) ); if( NearestUnit != BcErrorCode ) { doAttack( NearestUnit ); } } break; case BEHAVIOUR_MOVE: { BcU32 NearestUnit = pSimulator_->findNearestUnit( CurrState_.Position_, ID_, 1 << ( 1 - TeamID_ ) ); if( IsAttackMove_ && inRangeForAttack( NearestUnit ) == RANGE_IN ) { CurrState_.Velocity_ = BcFixedVec2d( 0.0f, 0.0f ); doAttack( NearestUnit ); } else { // Reset attack timer. AttackTimer_ = BcMax( AttackTimer_, BcFixed( Desc_.CoolDownMultiplier_ ) / Desc_.RateOfAttack_ ); // Make longer whe moving. AttackTimer_ = BcMin( BcFixed( 1.0f ) / Desc_.RateOfAttack_, AttackTimer_ + ( BcFixed( 1.0f ) / Desc_.RateOfAttack_ ) * ( Delta * 0.05f ) ); // Calculate velocity vector. CurrState_.Velocity_ = BcFixedVec2d( MoveTargetPosition_ - CurrState_.Position_ ).normal() * Desc_.MoveSpeed_; BcFixed MoveDistance = Desc_.MoveSpeed_ * Delta; if( ( CurrState_.Position_ - MoveTargetPosition_ ).magnitudeSquared() < ( MoveDistance * MoveDistance ) ) { CurrState_.Position_ = MoveTargetPosition_; setBehaviourIdle(); } } } break; case BEHAVIOUR_ATTACK: { TRange Range = inRangeForAttack( TargetUnitID_ ); if( Range == RANGE_IN ) { doAttack( TargetUnitID_ ); } else { // Reset attack timer. AttackTimer_ = BcMax( AttackTimer_, BcFixed( Desc_.CoolDownMultiplier_ ) / ( Desc_.RateOfAttack_ ) ); // Make longer whe moving. AttackTimer_ = BcMin( BcFixed( 1.0f ) / Desc_.RateOfAttack_, AttackTimer_ + ( BcFixed( 1.0f ) / Desc_.RateOfAttack_ ) * ( Delta * 0.05f ) ); // Get unit position. GaGameUnit* pUnit = pSimulator_->getUnit( TargetUnitID_ ); if( pUnit != NULL ) { BcFixedVec2d Direction = ( getPosition() - pUnit->getPosition() ).normal() * ( ( Desc_.MinRange_ + Desc_.Range_ ) * 0.5f ); MoveTargetPosition_ = pUnit->getPosition() + Direction; } else { // Attack move to previous target. IsAttackMove_ = BcTrue; Behaviour_ = BEHAVIOUR_MOVE; } // Calculate velocity vector. CurrState_.Velocity_ = BcFixedVec2d( MoveTargetPosition_ - CurrState_.Position_ ).normal() * Desc_.MoveSpeed_; // If we moved further away, then stop and set target. BcFixed CurrMagnitudeSquared = ( CurrState_.Position_ - MoveTargetPosition_ ).magnitudeSquared(); BcFixed PrevMagnitudeSquared = ( PrevState_.Position_ - MoveTargetPosition_ ).magnitudeSquared(); if( CurrMagnitudeSquared > PrevMagnitudeSquared ) { setBehaviourIdle(); } } } break; case BEHAVIOUR_DAMAGE: { // Calculate velocity vector. CurrState_.Velocity_ = BcFixedVec2d( MoveTargetPosition_ - CurrState_.Position_ ).normal() * Desc_.MoveSpeed_; BcFixed MoveDistance = Desc_.MoveSpeed_ * Delta; if( ( CurrState_.Position_ - MoveTargetPosition_ ).magnitudeSquared() < ( MoveDistance * MoveDistance ) ) { CurrState_.Position_ = MoveTargetPosition_; // Cause damage. pSimulator_->applyDamage( MoveTargetPosition_, Desc_.Range_, Desc_.Health_ ); // Debug point! pSimulator_->addDebugPoint( getPosition(), Desc_.Range_, RsColour::YELLOW ); if( Desc_.pHitSound_ != NULL ) { GaTopState::pImpl()->playSound( Desc_.pHitSound_, MoveTargetPosition_ ); } // Set as dead. setBehaviourDead(); } } break; } // Attack timer. if( AttackTimer_ > 0.0f ) { AttackTimer_ -= Delta; } else if( AttackTimer_ < 0.0f ) { AttackTimer_ = 0.0f; } // Death. if( Health_ <= 0.0f ) { Health_ = 0.0f; Behaviour_ = BEHAVIOUR_DEAD; } }
////////////////////////////////////////////////////////////////////////// // getMaxAttackTime BcFixed GaGameUnit::getMaxAttackTime() const { return BcFixed( 1.0f ) / Desc_.RateOfAttack_; }
////////////////////////////////////////////////////////////////////////// // onMouseEvent eEvtReturn GaGameComponent::onMouseEvent( EvtID ID, const OsEventInputMouse& Event ) { // Convert to rendering space. OsClient* pClient = OsCore::pImpl()->getClient( 0 ); BcReal HW = static_cast< BcReal >( pClient->getWidth() ) / 2.0f; BcReal HH = static_cast< BcReal >( pClient->getHeight() ) / 2.0f; CursorPosition_.set( Event.MouseX_ - HW, Event.MouseY_ - HH ); GameCursorPosition_ = CursorPosition_ / 32.0f; EndGameCursorPosition_ = GameCursorPosition_; if( MouseDown_ && ( StartGameCursorPosition_ - EndGameCursorPosition_ ).magnitudeSquared() > BcFixed( 8.0f ) ) { BoxSelection_ = BcTrue; } if( ID == osEVT_INPUT_MOUSEDOWN ) { StartGameCursorPosition_ = GameCursorPosition_; MouseDown_ = BcTrue; BoxSelection_ = BcFalse; } else if( ID == osEVT_INPUT_MOUSEUP ) { EndGameCursorPosition_ = GameCursorPosition_; MouseDown_ = BcFalse; GaGameUnitIDList FoundUnits; if( BoxSelection_ ) { pSimulator_->findUnits( FoundUnits, StartGameCursorPosition_, EndGameCursorPosition_, BcErrorCode, 1 << TeamID_ ); } else { pSimulator_->findUnits( FoundUnits, GameCursorPosition_, 0.8f, BcErrorCode, 1 << TeamID_ ); while( FoundUnits.size() > 1 ) { FoundUnits.pop_back(); } } // If we found units, then set selection. if( FoundUnits.size() > 0 ) { UnitSelection_ = FoundUnits; } else { // If we aren't box selection do action. if( BoxSelection_ == BcFalse ) { BcU32 TargetUnitID = BcErrorCode; // Determine if it's an attack move or not. pSimulator_->findUnits( FoundUnits, GameCursorPosition_, 0.8f, BcErrorCode, 1 << ( 1 - TeamID_ ) ); while( FoundUnits.size() > 1 ) { FoundUnits.pop_back(); } if( FoundUnits.size() == 1 ) { TargetUnitID = FoundUnits[ 0 ]; } // Otherwise, tell found units to move. BcFixedVec2d CentralPosition; BcFixed Divisor; for( BcU32 Idx = 0; Idx < UnitSelection_.size(); ++Idx ) { GaGameUnit* pGameUnit( pSimulator_->getUnit( UnitSelection_[ Idx ] ) ); if( pGameUnit != NULL ) { CentralPosition += pGameUnit->getPosition(); Divisor += 1.0f; } } if( UnitSelection_.size() > 0 && Divisor > 0.0f ) { CentralPosition /= Divisor; GameCursorPosition_ = BcFixedVec2d( ( GameCursorPosition_.x() ), ( GameCursorPosition_.y() ) ); BcFixed PlayfieldHW = 1280.0f * 0.5f / 32.0f; BcFixed PlayfieldHH = 720.0f * 0.5f / 32.0f; for( BcU32 Idx = 0; Idx < UnitSelection_.size(); ++Idx ) { GaGameUnit* pGameUnit( pSimulator_->getUnit( UnitSelection_[ Idx ] ) ); if( pGameUnit != NULL ) { if( TargetUnitID == BcErrorCode ) { GaGameUnitMoveEvent Event; Event.UnitID_ = pGameUnit->getID(); Event.Position_ = ( ( pGameUnit->getPosition() - CentralPosition ) * 0.0f ) + GameCursorPosition_; Event.Position_.x( BcClamp( Event.Position_.x(), -PlayfieldHW, PlayfieldHW ) ); Event.Position_.y( BcClamp( Event.Position_.y(), -PlayfieldHH, PlayfieldHH ) ); Event.IsAttackMove_ = AttackMove_; pSimulator_->publish( gaEVT_UNIT_MOVE, Event ); } else { GaGameUnitAttackEvent Event; Event.UnitID_ = pGameUnit->getID(); Event.TargetUnitID_ = TargetUnitID; pSimulator_->publish( gaEVT_UNIT_ATTACK, Event ); } } } } // Toggle off attack move. AttackMove_ = BcFalse; } else { // If we were box selecting clear selection. UnitSelection_.clear(); } } BoxSelection_ = BcFalse; } return evtRET_PASS; }
* * * **************************************************************************/ #include "GaGameComponent.h" #include "GaTopState.h" ////////////////////////////////////////////////////////////////////////// // Units static GaGameUnitDescriptor GGameProjectile_Soldier = { 5, BcFixedVec2d( 0.5f, 0.5f ), // Unit size. BcFixed( 32.0f ), // Move speed. BcFixed( 2.0f ), // Rate of attack. BcFixed( 1.0f ), // Cool down mult for rate of attack. BcFixed( 0.1f ), // Range. BcFixed( 0.1f ), // Range. BcFixed( 7.0f ), // Health. BcFalse, // Armoured. NULL, // Sound NULL, NULL, NULL, }; static GaGameUnitDescriptor GGameProjectile_Archer =