In my current I use a state machine + conditions & AI properties (e.g. stuck, alerted or aggressiveness to name a few, stored in local variables or in entity skills if needed) + if/else branches which calls the necessarily functions.

I also find Half Life 1's way to be interesting, which is goal oriented (I guess), interesting part of it:

Quote:
Tasks
Tasks are short atomic behaviors that are defined for a specific purpose. For instance, most actors in Half-Life support the following: TASK_WALK_PATH, TASK_CROUCH, TASK_STAND, TASK_GUARD, TASK_STEP_FORWARD, TASK_DODGE_RIGHT, TASK_FIND_COVER_FROM_ENEMY, TASK_EAT, TASK_STOP_MOVING, TASK_TURN_LEFT, TASK_REMEMBER. These are defined as enumerations in the header file, and implemented as C++ methods.

Conditions
Conditions are used to express the situation of an actor in the world. Since everything is hard-coded, conditions can be expressed very compactly as a bitfield, but in this case it limits the different conditions to 32. For example, conditions are COND_NO_AMMO_LOADED, COND_SEE_HATE, COND_SEE_FEAR, COND_SEE_DISLIKE, COND_ENEMY_OCCLUDED, COND_ENEMY_TOOFAR, COND_HEAVY_DAMAGE, COND_CAN_MELEE_ATTACK2, COND_ENEMY_FACING_ME.

Schedules
A schedule is composed out of a series of tasks (with arbitrary parameters), and given a bitfield of conditions to help specify when this schedule invalid. The basic schedule object also has a name to help with debugging.

Goals
On a higher level, goals are made up of multiple schedules. The logic in the goal can select a schedule as necessary based on which task failed, and what the current context is. The goals in Half-Life include GOAL_ATTACK_ENEMY, GOAL_MOVE, GOAL_TAKE_COVER, GOAL_MOVE_TARGET and GOAL_EAT.

source -> http://aigamedev.com/open/article/halflife-sdk/