Objectives: Main Logic

This is the internal logic for an objective, which is managed by a higher level mission. It is written in the exp language created by John Haggerty.

Exp Cheatsheet:

  • aslongas - keeps the current scope alive "as long as" the expression remains true
  • whenever - Runs the following statement repeatedly, once each time the expression is true, ending the statement when it becomes false, and waiting for it to become true again when the statement ends
  • meantime - Whenever may be paired with "meantime" to provide a statement that will be run when the expression is false
  • thread - Exp "threads" are asynchronous function calls. They are akin to coroutines in some other languages: they take turns executing and so are not truly asynchronous.
  • localthread - A localthread is a thread that is guaranteed to end when the scope it is defined in is exited. You can customize the scope it is attached to with the @ symbol.
  • cleanup - After a cleanup block is passed during execution, the statement (or block of statements) after cleanup is guaranteed to run when the scope is exited.
#if SERVER
// Returns true if completed successfully
shared maywait bool fn AObjectiveBase::RunObjective()
{
    assert( HasFinishedInit() )

    AMissionBase? mission = Mission
    assert( mission )
    assert( !mission.IsMissionCompleted() )
    assert( !mission.GetActiveObjective(), "Cannot start an objective when another is active." )

    bool wasSuccessfull = false

    SetObjectiveState_Internal( EMissionObjectiveState.ACTIVE )

    cleanup
    {
        thread OnObjectiveCompletedBlueprint( wasSuccessfull )

        if ( wasSuccessfull )
        {
            if ( OnSuccessGiveUpgrade )
                thread GiveUpgradeOnObjectiveSuccess()

            thread GiveObjectiveMetaAwards()
            thread RespawnAnyDeadPlayers()
        }

        if ( inPlay )
        {
            EMissionObjectiveState completedState = wasSuccessfull ? EMissionObjectiveState.COMPLETED_SUCCESS : EMissionObjectiveState.COMPLETED_FAILURE
            SetObjectiveState_Internal( completedState )
        }

        if ( IsInPlay(HudMarker) )
            HudMarker!.DestroyActor()
    }

    thread OnObjectiveStartedBlueprint()

    localthread whenever IsInPlay(HudMarker) HudMarkerTrackingThread() // sets the hud marker based on custom settings

    aslongas inPlay
    {
        if ( IsInPlay(LoadedEncounterDirector) )
        {
            EEncounterState state = LoadedEncounterDirector!.GetCurrentState()

            if ( state == EEncounterState.Idle )
                LoadedEncounterDirector!.StartEncounter()
            else if ( state == EEncounterState.Paused )
                LoadedEncounterDirector!.UnPauseEncounter()
        }

        cleanup
        {
            if ( IsInPlay(LoadedEncounterDirector) && LoadedEncounterDirector!.IsEncounterRunning() )
                LoadedEncounterDirector!.PauseEncounter()
        }

        // 'activate' other actors from the objective
        foreach ( actor in LoadedActorsToActivateOnStart )
        {
            if ( actor.inPlay )
                actor.OnActivatedByOtherActor( ActorActivationInfo{SourceActor = this, ...} )
        }

        // the main logic of the objective happens here...
        endwhen ForceComplete
            wasSuccessfull = ObjectiveThink()

        if ( ForceComplete )
            wasSuccessfull = true

        if ( wasSuccessfull )
        {
            foreach ( actor in LoadedActorsToActivateOnSuccess )
            {
                if ( actor.inPlay )
                    actor.OnActivatedByOtherActor( ActorActivationInfo{SourceActor = this, ...} )
            }
        }

        wait DelayBeforeMarkComplete
    }

    return wasSuccessfull
}


// Override this function to drive all your objective logic.
// Returns true if completed successfully
shared virtual maywait
bool fn AObjectiveBase::ObjectiveThink()
{
    return true
}
#endif // SERVER