Jumper::CanopyOpening::CanopyOpening(Jumper* jumper, NxActor* phFreeFall, NxActor* phFlight, MatrixConversion* mcFlight, PilotchuteSimulator* pc, CanopySimulator* c, NxVec3 fla, NxVec3 fra, NxVec3 rla, NxVec3 rra) :
    JumperAction( jumper )
{
    // set action properties
    _actionTime = 0.0f;
    _blendTime = 0.2f;
    _endOfAction = false;
    _phActor = phFlight;
    _matrixConversion = mcFlight;
    _pilotchute = pc;
    _canopy = c;
    _frontLeftAnchor  = fla;
    _frontRightAnchor = fra;
    _rearLeftAnchor   = rla;
    _rearRightAnchor  = rra;
    _initialLD = phFlight->getLinearDamping();

    // activate jumper body simulator
    Matrix4f sampleLTM = Jumper::getCollisionFC( _clump )->getFrame()->getLTM();
    phFlight->setGlobalPose( wrap( sampleLTM ) );
    phFlight->wakeUp();
    phFlight->setLinearVelocity( phFreeFall->getLinearVelocity() );
    phFlight->setAngularVelocity( phFreeFall->getAngularVelocity() );    

    // connect & open canopy    
    _canopy->connect(
        _phActor, 
        _frontLeftAnchor,
        _frontRightAnchor,
        _rearLeftAnchor,
        _rearRightAnchor,
        Jumper::getFrontLeftRiser( _clump ),
        Jumper::getFrontRightRiser( _clump ),
        Jumper::getRearLeftRiser( _clump ),
        Jumper::getRearRightRiser( _clump )
    );

	// age
	if (_jumper->getCanopyReserveSimulator() && _jumper->getCanopyReserveSimulator() == _canopy) {
		++jumper->getVirtues()->equipment.reserve.age;
	} else {
		++jumper->getVirtues()->equipment.canopy.age;
	}

    // retrieve pilotchute velocity
    float pcVel = pc->getPhActor()->getLinearVelocity().magnitude();
	float anglVel = phFreeFall->getAngularVelocity().magnitude();

    // retrieve pilotchute reference velocity
	database::Canopy* canopyInfo = _canopy->getGearRecord();
	database::Pilotchute* pcInfo;
	if (_jumper->getCanopyReserveSimulator() == _canopy) {
		pcInfo = canopyInfo->pilots;
	} else {
		assert( canopyInfo->numPilots > _jumper->getVirtues()->equipment.pilotchute );
		pcInfo = canopyInfo->pilots + _jumper->getVirtues()->equipment.pilotchute;
	}

    // probability of lineover
    float lineoverProb  = _jumper->getVirtues()->getLineoverProbability( pcVel / pcInfo->Vrec );
	// no slider has not smaller probability
	if (_jumper->getVirtues()->equipment.sliderOption != soRemoved) {
		lineoverProb *= 0.3f;
	}
	// reserves have 70% less lineover probability
	if (_jumper->getCanopyReserveSimulator() == _canopy) {
		lineoverProb *= 0.3f;
	}
    bool  leftLineover  = ( getCore()->getRandToolkit()->getUniform( 0, 1 ) < lineoverProb );
    bool  rightLineover = !leftLineover && ( getCore()->getRandToolkit()->getUniform( 0, 1 ) < lineoverProb );
    float leftLOW  = leftLineover ? getCore()->getRandToolkit()->getUniform( 0.5, 1.0f ) : 0;
    float rightLOW = rightLineover ? getCore()->getRandToolkit()->getUniform( 0.5, 1.0f ) : 0;

    // probability of linetwists
	// add probability if spinning
	//if( _jumper->isPlayer() ) getCore()->logMessage( "angular velocity component on deployment: %2.10f", anglVel * 0.13f );
    float linetwistsProb = _jumper->getVirtues()->getLinetwistsProbability( pcVel ) + anglVel * 0.13f;
    float linetwistsDice = getCore()->getRandToolkit()->getUniform( 0.0f, 1.0f );
	
	// probability of linetwists because of incorrect body position
	NxVec3 motionDir = _jumper->getFreefallActor()->getLinearVelocity(); motionDir.normalize();
    NxVec3 canopyDown  = _jumper->getFreefallActor()->getGlobalPose().M.getColumn(2); canopyDown.normalize();
    float relativity = 1.0f + fabs(canopyDown.dot( motionDir ));
	linetwistsProb *= relativity;

	// reserves have 50% less linetwist probability
	if (_jumper->getCanopyReserveSimulator() == _canopy) {
		linetwistsProb *= 0.5f;
	}
	
	// base canopies gave 40% less linetwist probability
	else if (!canopyInfo->skydiving) {
		linetwistsProb *= 0.4f;
	}

    bool  linetwists = ( linetwistsDice <= linetwistsProb );
	//if( _jumper->isPlayer() ) {
		//getCore()->logMessage( "linetwists prob: %3.5f", linetwistsProb );
		//getCore()->logMessage( "linetwists dice: %3.5f", linetwistsDice );
	//} else {
		//getCore()->logMessage( "linetwists prob BOT: %3.5f", linetwistsProb );
		//getCore()->logMessage( "linetwists dice BOT: %3.5f", linetwistsDice );
	//}

    // generate linetwists (positive is righttwist, negative is lefttwist)
	// the smaller the canopy, the heavier the linetwist
	float sq = canopyInfo->square * 2.0f;
	// 300 canopy: 120; 840
	// 200 canopy: 320; 1040
	// 100 canopy: 520; 1240
    float linetwistsAngle = getCore()->getRandToolkit()->getUniform( 720 - sq, 1540 - sq );
	if (linetwistsAngle > 1080.0f) {
		linetwistsAngle = 1440.0f;
	} else if (linetwistsAngle < 320.0f) {
		linetwistsAngle = 180.0f;
	}

    float sign = getCore()->getRandToolkit()->getUniform( -1, 1 );
    sign = sign < 0.0f ? -1.0f : ( sign > 0.0f ? 1.0f : 0.0f );
    linetwistsAngle *= sign;

    if( !linetwists ) linetwistsAngle = 0.0f;

	// test, force linetwist
	//if (!_jumper->isPlayer() && _jumper->getCanopyReserveSimulator() != _canopy) {
	//	linetwists = true;
	//	linetwistsAngle = 1440.0f;
	//}

    // offheading : canopy turns by specified angle
    float minTurn = 30.0f; // rigging skill = 0.0 
    float maxTurn = 10.0f;  // rigging skill = 1.0 
    float rigging = _jumper->getVirtues()->getRiggingSkill(); assert( rigging >= 0 && rigging <= 1 );
    float turn    = minTurn * ( 1 - rigging ) + maxTurn * rigging;
    float angle = getCore()->getRandToolkit()->getUniform( -turn, turn );
    //if( _jumper->isPlayer() ) getCore()->logMessage( "additional turn (offheading): %2.1f", angle );
    //if( !_jumper->isPlayer() ) angle = 0;


    Vector3f sampleX( sampleLTM[0][0], sampleLTM[0][1], sampleLTM[0][2] );
    Vector3f sampleY( sampleLTM[1][0], sampleLTM[1][1], sampleLTM[1][2] );
    Vector3f sampleZ( sampleLTM[2][0], sampleLTM[2][1], sampleLTM[2][2] );
    Vector3f sampleP( sampleLTM[3][0], sampleLTM[3][1], sampleLTM[3][2] );
    // orient canopy towards jumper velocity
    sampleZ = wrap( phFlight->getLinearVelocity() );
    sampleZ *= -1;
    sampleZ.normalize();
    sampleY = _jumper->getClump()->getFrame()->getAt() * -1;
    sampleX.cross( sampleY, sampleZ );
    sampleX.normalize();
    sampleY.cross( sampleZ, sampleX );
    sampleY.normalize();
    sampleLTM.set( 
        sampleX[0], sampleX[1], sampleX[2], 0.0f,
        sampleY[0], sampleY[1], sampleY[2], 0.0f,
        sampleZ[0], sampleZ[1], sampleZ[2], 0.0f,
        0.0f, 0.0f, 0.0f, 1.0f
    );
    // turn canopy by random angle
    sampleLTM = Gameplay::iEngine->rotateMatrix( sampleLTM, sampleZ, angle );
    // move clump behind jumper
    sampleP -= sampleZ * 80.0f;

    sampleLTM[3][0] = sampleP[0];
    sampleLTM[3][1] = sampleP[1];
    sampleLTM[3][2] = sampleP[2];

    _canopy->open( wrap( sampleLTM ), _phActor->getLinearVelocity(), leftLOW, rightLOW, linetwistsAngle );

    // reconnect pilotchute to canopy
    _canopy->getClump()->getFrame()->getLTM();
    _pilotchute->connect( 
        _canopy->getNxActor(), 
        CanopySimulator::getPilotCordJoint( _canopy->getClump() ),
        _canopy->getPilotAnchor()
    );
	//_pilotchute->setFreebag(_canopy == _jumper->getCanopyReserveSimulator()); set in jumper constructor and jumper::fireReserveCanopy()

    // put to sleep freefall simulator
    phFreeFall->putToSleep();
    phFreeFall->raiseActorFlag( NX_AF_DISABLE_COLLISION );

    // show risers
    Jumper::getRisers( _clump )->setFlags( engine::afRender );

    // animation controller
    engine::IAnimationController* animCtrl = _clump->getAnimationController();

    // capture blend source
    animCtrl->captureBlendSrc();

    // reset animation mixer
    for( unsigned int i=0; i<engine::maxAnimationTracks; i++ )
    {
        if( animCtrl->getTrackAnimation( i ) ) animCtrl->setTrackActivity( i, false );
    }

    // setup animation
    animCtrl->setTrackAnimation( 0, &openingSequence );
    animCtrl->setTrackActivity( 0, true );
    animCtrl->setTrackSpeed( 0, 0.75f );
    animCtrl->setTrackWeight( 0, 1.0f );
    animCtrl->resetTrackTime( 0 );
    animCtrl->advance( 0.0f );

    // capture blend destination
    animCtrl->captureBlendDst();
    animCtrl->blend( 0.0f );
}
Exemple #2
0
void enemy::wprinAI(float elapsed)
{
	unsigned int totalBossResource = (unsigned int) ((1 - healthFraction()) / (global_data->getValue(DATA_BOSSDATA, magic, 8) * 0.01f));

	if (bossPhase < 10 && healthFraction() < 1)
	{
		global_map->forceDialogue(global_data->getValue(DATA_BOSSDATA, magic, 0) + 10);
		bossPhase = 10;
		global_map->forceMusic(BOSSMUSIC);
	}

	switch(bossPhase)
	{
	case 0:
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
	case 6:
	case 7:
	case 8:
	case 9:
		if (!global_map->dialogueActive())
		{
			global_map->forceDialogue(global_data->getValue(DATA_BOSSDATA, magic, 0) + bossPhase);
			bossPhase += 1;
			if (bossPhase == 10)
				global_map->forceMusic(BOSSMUSIC);
		}
		break;
	case 10:
		//combat start
		if (!global_map->dialogueActive())
		{
			{
				//pick a position BESIDES the one you are at already
				unsigned int pPick = rand() % 2;
				switch(bossProb)
				{
				case 0:
					bossProb = 1 + pPick;
					break;
				case 1:
					if (pPick == 0)
						bossProb = 0;
					else
						bossProb = 2;
					break;
				case 2:
					bossProb = pPick;
					break;
				}
			}

			//teleport to that position
			global_map->magic(this, global_data->getValue(DATA_BOSSDATA, magic, 7), TYPE_PLAYER);
			x = 1.0f * global_data->getValue(DATA_BOSSDATA, magic, bossProb * 2 + 1) - getMaskWidth() / 2;
			y = global_map->getHeight() - (global_data->getValue(DATA_BOSSDATA, magic, bossProb * 2 + 2) - getMaskHeight())
											- (1 + GENERATOR_BOTTOMBORDER) * TILESIZE;

			//reset timer
			bossTimer = 0;

			//pick an attack pattern based on the position
			switch(bossProb)
			{
			case 0:
			case 1:
				flying = false;
				if (bossProb == 0)
					facingX = 1;
				else
					facingX = -1;

				if (rand() % 2 == 0)
					bossPhase = 11;
				else
					bossPhase = 14;
				break;
			case 2:
				flying = true;

				if (rand() % 2 == 0)
					bossPhase = 13;
				else
					bossPhase = 15;
				break;
			}

			if ((unsigned int) bossResource < totalBossResource)
			{
				//summon a monster instead of what you picked
				bossPhase = 12;

				bossResource += 1;
			}
		}
		break;
	case 11:
		//attack
		{
			if (bossTimer > 0 && !attacking())
				bossPhase = 10; //teleport again
			else
			{
				startAttack();
				bossTimer += elapsed;
			}
		}
		break;
	case 12:
		//summon
		{
			float bTOld = bossTimer;
			bossTimer += elapsed;
			float sPoint = global_data->getValue(DATA_BOSSDATA, magic, 9) * 0.01f;
			if (bossTimer >= sPoint && bTOld < sPoint)
			{
				//summon
				unsigned int sXA = 0;
				creature *sP = sampleP();
				if (sP->getX() + sP->getMaskWidth() / 2 < global_map->getWidth() / 2)
					sXA += 1;
				float sX = global_data->getValue(DATA_BOSSDATA, magic, 11 + sXA) * 1.0f;
				float sY = global_map->getHeight() - (1 + GENERATOR_BOTTOMBORDER) * TILESIZE;
				unsigned int sTypeA = 0;
				if (bossResource > global_data->getValue(DATA_BOSSDATA, magic, 13))
					sTypeA += 1;
				global_map->summon(sX, sY, global_data->getValue(DATA_BOSSDATA, magic, 14 + sTypeA), true, true); //it's a boss so it doesn't give EXP
			}
			else if (bossTimer > global_data->getValue(DATA_BOSSDATA, magic, 10) * 0.01f)
				bossPhase = 10; //teleport
		}

		break;
	case 13:
		//poison rain
		{
			if (bossTimer == 0)
				global_map->magic(this, global_data->getValue(DATA_BOSSDATA, magic, 16), TYPE_PLAYER);
			bossTimer += elapsed;
			if (bossTimer >= global_data->getValue(DATA_BOSSDATA, magic, 17) * 0.01f)
				bossPhase = 10; //use teleport again
		}
		break;
	case 14:
		//moon burst
		{
			if (bossTimer == 0)
				global_map->magic(this, global_data->getValue(DATA_BOSSDATA, magic, 18), TYPE_PLAYER);
			bossTimer += elapsed;
			if (bossTimer >= global_data->getValue(DATA_BOSSDATA, magic, 19) * 0.01f)
				bossPhase = 10; //use teleport again
		}
		break;
	case 15:
		//moon blast
		{
			float oldBT = bossTimer;
			bossTimer += elapsed;
			float dropInterval = global_data->getValue(DATA_BOSSDATA, magic, 23) * 0.01f;
			unsigned int dropR = (unsigned int) (bossTimer / dropInterval);
			while (oldBT <= dropR * dropInterval && oldBT <= global_data->getValue(DATA_BOSSDATA, magic, 24) * 0.01f)
			{
				//use a spell
				float angle = (rand() % 100) * 0.01f * (float) M_PI;
				global_map->addProjectile(getX() + getMaskWidth() / 2, getY() + getMaskHeight() / 2, cos(angle), sin(angle),
											global_data->getValue(DATA_BOSSDATA, magic, 21), global_data->getValue(DATA_BOSSDATA, magic, 22),
											DATA_NONE, TYPE_ENEMY, DATA_NONE, false);
				global_map->magic(this, global_data->getValue(DATA_BOSSDATA, magic, 20), TYPE_PLAYER);
				oldBT += dropInterval;
			}
			if (bossTimer >= global_data->getValue(DATA_BOSSDATA, magic, 25) * 0.01f)
				bossPhase = 10; //use teleport again
		}
		break;

	}
}