Entries in doing and learning (7)

Tuesday
09Mar2010

Why Companies Lay People Off - Preparing for that Possibility

Through my long career losing one's job is a very traumatic experience to anyone the first time around. It actually gets easier subsequent times since much of the initial shock and pain found in the first layoff has taught a professional how to be better prepared for it.

In the recent economic turmoil many more professionals have found themselves on different paths than what they thought they would experience. For many it is their first time or so long since they have gone through the experience that their pain, confusion and angst is understandably high. Even the most seasoned professional looks at the situation with some degree of frustration and confusion. After all they were doing fairly important work and now suddenly they are out of an organization they have given so much of their efforts to.

I often find myself giving advice to many professionals in these situations from my own personal perspective in that I have gone through it myself several times. Many have found my advice useful so I thought I would share some of my thoughts on the subject:

  • Why is a company laying off? Answer - economic reality bites. Let's face it, many companies are not necessarily well prepared for change especially rapid, downward changes. So when they do take actions, it is often more of a reaction than any truly planned action. This is not to say that any organization releases employees lightly. Quite to the contrary they are loathe to do so. However it is not something that they adequately prepare for nor practice until times call for it. Quite simply it is beyond an organization's control on when they lay people off. It just happens.
  • So why am I being laid off? Answer - it's nothing personal, it's just business. Most companies view their employees as valuable resources not necessarily as individuals. Even in the most individual-oriented company, everyone has a value that has to be weighed by the company process in terms of being retained or released. For example younger more agile individuals even those who have contributed significantly to a company can find themselves released while others are retained. Why? The company weighs their value to the company. If a person only contributes in a certain way that can be more or less valuable to a company versus others who do more. To be honest there is no real "formula" or "scientific process" being applied. It is matter of perception by decision makers beyond an individuals control.
  • Was it something that "I" did to get laid off? Answer - Plain and simply no. In almost all instances of being laid off I can personally recall every "mishap" that might have caused my being released. From personal experience that is the wrong avenue to take. While it is true that one might have made mistakes, if they had been heinous enough to cause a layoff more than likely one would have been released due to disciplinary actions prior to being laid off. One of the biggest wastes of energy is looking at a company and imagining any number of scenarios as to why one was laid off. It ranges from "so-and-so was out to get me", "I should have explained my value more", "I should have done more tasks", etc. I have seen executives of profitable divisions who did outstanding work, engineers with PhDs and patents, and sales personnel who brought large deals to an organization released in the same wave as other professionals who were not as well known. It is never one's fault for being affected by a layoff in such dire economic times. 
  • What do I do now? Answer - Simple. Find a new opportunity. Note how I always call it an opportunity and not a job? This is an important distinction. An opportunity is something that interests you whereas a job is something that does not. It is the passion driven by interest that attracts employers. If you are interested in doing something it shows.

One of the major perspectives someone being laid off tends to forget is how their departure among the number of their fellow employees impacts their organization. I am not merely talking about immediate work efforts but rather morale as a whole. How an organization deals with a layoff is not only important to those leaving, but those staying as well. While it is true that many employees will react out of fear of losing their positions, energy that is fear-based lasts only a short while. Employees will become resentful, fearful and stressed resulting in any number of issues to an organization ranging from late projects, to health-related absences, to a poisoning of the company culture - just to name a few. Essentially when a lay off occurs, it erodes the goodwill between employee and employer. How well the transition is managed determines how much erosion occurs and how soon the healing process begins. I often advise those who are let go that their worries are over and their course of action is clear. Those that remain behind are the ones that need sympathy and support as their turmoil will take a long time to settle down.

One of the greatest lessons being laid off teaches a professional is how to be entrepreneurial - to find opportunities, to network, to grow, to find business own their own, to face repeated rejection with optimism and enthusiasm, and to ultimately believe in themselves. As companies continue to lower their costs by letting employees go, they are in my opinion inadvertantly creating a newer, better workfoce that will have several traits:

  • More self-reliant and self-motivated.
  • More focused on doing what they themselves are capable of doing for others both inside and outside of an organization.
  • More confidant in their capabilities and values in delivering quality, affordable and effective solutions to customers.
  • More capable of making significant decisions that affect their lives with greater clarity and determination than before.
  • More unwilling to "settle" for something as opposed to going out and "making" it happen.

These are not bad traits to have in any employee. It is just that employers will find a more mindful, independent workforce than they previously thought. This workforce will not only have the talent but the the motivation to focus on what interests them. For any professional going through a transition focus on the future, not the past. Trust me, it helps.

 

Thursday
21Jan2010

Keeping Up To Date - How To Adjust to Challenging Times

I read an article today from The Economist entitled "The Trap: The Curse of Long-Term Unemployment Will Bedevil the Economy". There are some very true and fascinating comments in this article which have been stated over and over again by other experts.

In today's workplace there are many observations about the changing landscape such as this one from Vocii which demonstrates how some CEOs are adjusting to the times. Yet for many professionals these concepts used by executives are elusive. One of the core principles I have attempted to pass along to other colleagues has been the concept of keeping current and competitive with ones skills. This is what CEOs and executives do, this is what all working professionals should do. This is especially true of technical professionals.

Technology changes rapidly creating a great number of challenges and opportunities at the same time. It creates more of a level playing field though it has it's extremes. Often times I come across numerous technical professionals that spend all their time working job searches and resume changes, yet totally ignoring the fact that their skills may in fact need ot be honed as well. Admittedly looking for the next opportunity is very important as is having a very good resume demonstrating your value. However, in the end you need to possess the skills that organizations want. Most have very good skills but need some refinement. Others need to learn whole new skill sets.

In my experience keeping abreast of technology is part of a technical professionals career. Admittedly indepth knowledge on the detailed technology may not be as pressing depending on your exact role, but one cannot be even a CTO or CIO without some passing knowledge of the latest approaches. I run into many technical professionals with very deep knowledge in a few skills or such a broad aptitude with little capability of applying modern solutions to significant problems.

I often use my own approach in keeping skills current as a means to inspire others. I mentor several startups in their business models and technical executions. I also mentor numerous working professionals by helping them deliver innovative solutions into their areas of responsibility which allows them to gain new skills while leveraging existing knowledge areas. I also help many technical implementers by assisting in their deployments, solutions or even at times their next generation solutions. All of these efforts allow me to stay in touch with various aspects of my industry that interest me, assist various levels of businesses to accomplish strategic goals, learn new, exciting and innovative ways to look at problems and solve them, and help with hands-on technology that I consider fun and intriguing.

Also in a prior post, I mentioned building my own personal technical environment with which to explore new technologies, build up my own knowledge, and not apply my own concepts to problems that may be of benefit to myself in my career but to others as well.

I look at things in terms of investments such as say learning Java. It is a pretty simple proposition to purchase a book, load the most popular software components such as the JVM, Maven, Eclipse, etc. and learn some of the concepts. Even purchasing a copy of VMware can help professionals gain a better understanding of virtualization. Using virtualized environments to help demonstrate how to spin up/spin down resources helps others to see practical benefits, considerations and how to apply that to their own environments. Ultimately what I attempt to do is motivate and inspire other professionals to do more with resources available to them. To think on their own and to grow on their own. Ultimately being able to rely on yourself, being active and focused, and learning new skills is a requirement in today's hyper-competitive landscape.

Thursday
07Jan2010

Learning C++ - Who Says You Learn Nothing from Games?

Recently I have been involved in training and teaching my younger sibling and his friends programming. Not just any programming mind you, but game programming. Admittedly this effort is not full on programming, but rather programming basics with C++. What I find fascinating is why they want to learn - video games.

A big thing for video games nowadays is the concept of creating your own modifications to an existing game called "modding". This has been around for a while now with a lot of games having very strong communities around modifications. First-person shooting games such as Call of Duty and Counterstrike had many mods. However like anything else the games in today's world have come so very far in terms of mods.

One of the more interesting games with a very rich, and confusing, toolset is Dragon Age Origins from EA and Bioware. They have a social portal and a wiki for the many consumers of their game and the modders who want to create new content with them.

The toolset itself is a very rich and powerful platform with a lot of capabilities to create new experiences in the game. The toolset is 100% free to anyone who has a copy of the game. It comes with a very rich and powerful GUI and a lot of tools for models, skins, textures, etc. What is also very interesting is how the toolset introduces certain concepts such as code check-ins/check-outs, group modifications, methods on testing, creating sharable code/modules, etc. Many of these concepts are in fact quite common in today's developer organizations and are quite frankly not very well explored in many college classes. The fact that there is a global community of modders for Dragon Age like so many other games allows people to see how others view their work.

One of the most challenging aspects of Dragon Age is that it actually requires a certain degree of programming most notably C++ in what they call scripts. These are essentially pluggable code snippets that allow someone making a mod to do quite a few things.

For example let's say someone wants to add a newly created item to their character's inventory. One of the sites provides an excellent tutorial on how to do this. This example allows someone making a mod to build upon it accordingly.

// All module events
#include "utility_h"
#include "wrappers_h"
#include "events_h"
void main()
{
    event ev   = GetCurrentEvent();
    int nEvent = GetEventType(ev);
    Log_Events("", ev);
    switch (nEvent)
    {
        ////////////////////////////////////////////////////////////////////////
        // Sent by: The engine
        // When: The module loads from a save game, or for the first time. This event can fire more than
        //       once for a single module or game instance.
        ////////////////////////////////////////////////////////////////////////
        case EVENT_TYPE_MODULE_LOAD:
        {

            // get the object which contains the player
            object oPlayer = GetHero();

            // get the object which contains the item we are looking for
            object oCarmor = GetObjectByTag("alt_start_armor");
            object oCgloves = GetObjectByTag("alt_start_gloves");
            object oCboots = GetObjectByTag("alt_start_boots");
            object oCsword = GetObjectByTag("alt_start_sword");
            object oCshield = GetObjectByTag("alt_start_shield");

            if (!IsObjectValid(oCarmor))
                UT_AddItemToInventory(R"alt_start_armor.uti",1);

            if (!IsObjectValid(oCgloves))
                UT_AddItemToInventory(R"alt_start_gloves.uti",1);

            if (!IsObjectValid(oCboots))
                UT_AddItemToInventory(R"alt_start_boots.uti",1);

            if (!IsObjectValid(oCsword))
                UT_AddItemToInventory(R"alt_start_sword.uti",1);

            if (!IsObjectValid(oCshield))
                UT_AddItemToInventory(R"alt_start_shield.uti",1);


            // get out of case statement
            break;
        }
        default:
        {
            break;
        }
    }
}

 

Such scripts are actually used by the game engine itself to achieve certain goals such as those included in the core engine:

#include "ability_h"
#include "effects_h"
#include "events_h"
#include "config_h"
#include "ai_main_h_2"
#include "global_objects_h"
#include "sys_injury"
#include "sys_autoscale_h"
#include "sys_itemsets_h"
#include "sys_traps_h"
#include "approval_h"
#include "sys_autolevelup_h"
#include "sys_rewards_h"
#include "tutorials_h"

#include "plt_tut_combat_salve"
#include "plt_tut_fatigue"
#include "plt_tut_armor_archer"
#include "plt_tut_first_gift"

#include "stats_core_h"

const int APPROVAL_DEATH_PENALTY = -3;

void   _ScheduleResurrectionAttempt(object oCreature)
{
    DelayEvent(6.0f, oCreature, Event(EVENT_TYPE_PARTY_MEMBER_RES_TIMER));
}



// -----------------------------------------------------------------------------
// @brief: Post resurrection event. Trigger soundset on player, add injury and
// approval penalties
// @author: Georg
// -----------------------------------------------------------------------------
int HandleEvent_Resurrection(object oCreature, event ev)
{
    int bApplyInjury = GetEventInteger(ev,0);
    if (bApplyInjury)
    {
        PlaySoundSet(oCreature, SS_EXPLORE_HEAL_ME);
        Injury_DetermineInjury(oCreature);
    }

    // redo itemset bonuses
    ItemSet_Update(oCreature);

    // CUT!
    //int nFollower = Approval_GetFollowerIndex(OBJECT_SELF);
    //if(nFollower != -1)
    //    Approval_ChangeApproval(nFollower, APPROVAL_DEATH_PENALTY);

    return TRUE;

}

// -----------------------------------------------------------------------------
// Spawn Event Handler
//
// Purpose:
// -- Set Stats
// -- Add Abilities
//
// -----------------------------------------------------------------------------
int HandleEvent_Spawn(event ev);
int HandleEvent_Spawn(event ev)
{

    if (!IsHero(OBJECT_SELF))
    {
        AS_InitCreature(OBJECT_SELF);
    }
    else
    {
        // ---------------------------------------------------------------------
        // Hero character gets his heartbeat event initialized here.
        // Followers get theirs when they are hired.
        // ---------------------------------------------------------------------
        InitHeartbeat(OBJECT_SELF, CONFIG_CONSTANT_HEARTBEAT_RATE);
    }

    return TRUE;

}

// -----------------------------------------------------------------------------
// Perception Disappear Event Handler
// Parameters:
// -- Obj(0): Creature appearing
//
// Purpose:
// -- Ends Delayed shout loop
// -- Sets combat mode to false if no hostiles are around anymore
// -----------------------------------------------------------------------------
int HandleEvent_PerceptionDisappear(event ev);
int HandleEvent_PerceptionDisappear(event ev)
{

    object oDisappearer = GetEventObject(ev, 0); //GetEventCreator(ev);

    // -----------------------------------------------------------------
    // If we unperceive a hostile object, and it's the last perceived
    // hostile, drop out of combat.
    // -----------------------------------------------------------------
    if (IsObjectHostile(oDisappearer,OBJECT_SELF))
    {
        Combat_HandleCreatureDisappear(OBJECT_SELF, oDisappearer);
    }
    else if(!IsObjectValid(oDisappearer)) // For cases where creatures are destroyed when dead (spirit, explodes)
    {
        if (!IsPartyPerceivingHostiles(OBJECT_SELF))
        {

            if (!IsPartyDead())
            {
                #ifdef DEBUG
                Log_Trace(LOG_CHANNEL_COMBAT, "HandleEvent_PerceptionDisappear", "STOPPING COMBAT FOR PARTY!");
                #endif
               /* ResurrectPartyMembers();

                // ------------------------------------------------------------------
                // ... we switch the game back to explore mode.
                // Note: This switches CombatState on all party members as party of
                //       the GameModeChange Module Level Event
                // ------------------------------------------------------------------
                WR_SetGameMode(GM_EXPLORE);*/
                DelayEvent(1.0f, GetModule(), Event(EVENT_TYPE_DELAYED_GM_CHANGE));
            }
        }
    }

    // -------------------------------------------------------------
    // Event was fully handled, do not fall through to rules_core
    // -------------------------------------------------------------
    return TRUE;
}

// -----------------------------------------------------------------------------
// Item Equip Event Handler
// -- sets 'prefer ranged' flag is equipping a ranged weapon in the main hand
//
//  Params:
//      int (0) - the inventory slot the item was equipped to
//      obj (0) - the item
// -----------------------------------------------------------------------------
int HandleEvent_Equip(event ev);
int HandleEvent_Equip(event ev)
{
    object oItem = GetEventObject(ev, 0);
    int nEquipByPlayer = GetEventInteger(ev, 1);

    #ifdef DEBUG
    Log_Trace(LOG_CHANNEL_TEMP,"player_core","itm:" + ToString(oItem) +" abi:" + ToString(GetItemAbilityId(oItem)));
    #endif

    #ifdef SKYNET
    TrackItemEvent(GetEventType(ev),OBJECT_SELF,oItem);
    #endif

    // Handle Item Set Tracking here
    ItemSet_Update(OBJECT_SELF);

    if(nEquipByPlayer)
    {
        if(GetBaseItemType(oItem) == BASE_ITEM_TYPE_ARMOR_HEAVY ||
            GetBaseItemType(oItem) == BASE_ITEM_TYPE_ARMOR_LIGHT ||
            GetBaseItemType(oItem) == BASE_ITEM_TYPE_ARMOR_MASSIVE ||
            GetBaseItemType(oItem) == BASE_ITEM_TYPE_ARMOR_MEDIUM)
                WR_SetPlotFlag(PLT_TUT_FATIGUE, TUT_FATIGUE_1, TRUE);
        if(GetBaseItemType(oItem) == BASE_ITEM_TYPE_ARMOR_HEAVY ||
                GetBaseItemType(oItem) == BASE_ITEM_TYPE_ARMOR_MASSIVE)
                WR_SetPlotFlag(PLT_TUT_ARMOR_ARCHER, TUT_ARMOR_ARCHER_1, TRUE);
    }



     // ------------------------------------------------------------------------
     // Temporary item enchantment code
     // ------------------------------------------------------------------------
     int nSlot = GetEventInteger(ev,0);
     if (nSlot == INVENTORY_SLOT_MAIN || (nSlot == INVENTORY_SLOT_OFFHAND && GetItemType(oItem) == ITEM_TYPE_WEAPON_MELEE) )
     {
         if (HasEnchantments(OBJECT_SELF))
         {
             EffectEnchantment_HandleEquip(oItem, OBJECT_SELF);
         }
     }


     return FALSE; // FALSE IS IMPORTANT HERE! DO NOT CHANGE!
}


// -----------------------------------------------------------------------------
// Item UnEquip Event Handler
//
//  Params:
//      int (0) - the inventory slot the item was removed from
//      obj (0) - the item
// -----------------------------------------------------------------------------
int HandleEvent_UnEquip(event ev);
int HandleEvent_UnEquip(event ev)
{
    object oItem = GetEventObject(ev, 0);

    #ifdef SKYNET
    TrackItemEvent(GetEventType(ev),OBJECT_SELF,oItem);
    #endif

    // Handle Item Set Tracking here
    ItemSet_Update(OBJECT_SELF);


     // ------------------------------------------------------------------------
     // Temporary item enchantment code
     // ------------------------------------------------------------------------
     int nSlot = GetEventInteger(ev,0);
     if (nSlot == INVENTORY_SLOT_MAIN || (nSlot == INVENTORY_SLOT_OFFHAND && GetItemType(oItem) == ITEM_TYPE_WEAPON_MELEE) )
     {
        if (HasEnchantments(OBJECT_SELF))
         {
             EffectEnchantment_HandleUnEquip(oItem, OBJECT_SELF);
         }
     }

    // -------------------------------------------------------------------------
    // Disable modal abilities that have their condition changed.
    // #define ABILITY_CONDITION_NONE          0x0
    // #define ABILITY_CONDITION_MELEEWEAPON   0x1
    // #define ABILITY_CONDITION_SHIELD        0x2
    // #define ABILITY_CONDITION_RANGEDWEAPON  0x4
    // #define ABILITY_CONDITION_BEHINDTARGET  0x8
    // #define ABILITY_CONDITION_DUALWEAPONS 0x040
    // #define ABILITY_CONDITION_2HWEAPON 0x080

    // -------------------------------------------------------------------------
    int[] abi = GetConditionedAbilities(OBJECT_SELF, 0xC7);
    int nSize = GetArraySize(abi);
    int i;
    for (i = 0; i < nSize; i++)
    {
        Effects_RemoveUpkeepEffect(OBJECT_SELF,abi[i]);
    }


     if (nSlot == INVENTORY_SLOT_CHEST)
     {
         #ifdef DEBUG
         Log_Trace(LOG_CHANNEL_COMBAT_GORE,"player_core:HandleEquip", "All gore removed due to changing armor");
         #endif

        Gore_RemoveAllGore(OBJECT_SELF);
     }




     return FALSE; // FALSE IS IMPORTANT HERE! DO NOT CHANGE!
}

// -----------------------------------------------------------------------------
// Inventory Event Handler
// -- Does nothing right now
// -----------------------------------------------------------------------------

int HandleEvent_InventoryEvent(event ev);
int HandleEvent_InventoryEvent(event ev)
{
    int nEventType = GetEventType(ev);
    object oOwner = GetEventCreator(ev);
    object oItem = GetEventObject(ev, 0);

    // -------------------------------------------------------------------------
    // Georg: Stores process their inventory events immediately, even while the
    //        gamestate is paused. We need to pass this information on to
    //        any sub events generated by the equip script or they'll get
    //        queued up until after the UI quits, causing all kind of havok
    // -------------------------------------------------------------------------
    int bProcessImmediate = GetEventInteger(ev,0);


    switch(nEventType)
    {
        case EVENT_TYPE_INVENTORY_ADDED:
        {


            //If the item acquired has the ITEM_SEND_ACQUIRED_EVENT variable set,
            //send an event to the module so that custom scripting can be done.
            int bSendCampaignEvent = GetLocalInt(oItem, ITEM_SEND_ACQUIRED_EVENT);
            if ( bSendCampaignEvent != 0 )
            {
                SendEventCampaignItemAcquired(GetModule(), oItem, bProcessImmediate);
            }

            if(GetBaseItemType(oItem) == BASE_ITEM_TYPE_QUICK)
            {
                int nItemAbility = GetItemAbilityId(oItem);
                if(nItemAbility == ITEM_ABILITY_HEALING_SALVE ||
                   nItemAbility == ITEM_ABILITY_HEALING_SALVE_1 ||
                   nItemAbility == ITEM_ABILITY_HEALING_SALVE_2 ||
                   nItemAbility == ITEM_ABILITY_HEALING_SALVE_3 ||
                   nItemAbility == ITEM_ABILITY_HEALING_SALVE_4)
                    WR_SetPlotFlag(PLT_TUT_COMBAT_SALVE, TUT_COMBAT_SALVE_1, TRUE);
            }
            else if(GetBaseItemType(oItem) == BASE_ITEM_TYPE_GIFT)
                WR_SetPlotFlag(PLT_TUT_FIRST_GIFT, TUT_FIRST_GIFT_1, TRUE);

            break;
        }
        case EVENT_TYPE_INVENTORY_REMOVED:
        {

            // If the item removed has ITEM_SEND_LOST_EVENT set send the event.
            int bSendCampaignEvent = GetLocalInt( oItem, ITEM_SEND_LOST_EVENT );

            if ( bSendCampaignEvent )
                SendEventCampaignItemLost( GetModule(), oItem, bProcessImmediate );

            break;

        }
    }
    return TRUE;
}


// -----------------------------------------------------------------------------
// Death Event andler.
// Purpose:
// -- Clears AI target.
// -- Prints log message.
// -----------------------------------------------------------------------------
int HandleEvent_Death(event ev);
int HandleEvent_Death(event ev)
{
    // -------------------------------------------------------------------------
    // The death effect has been applied to this creature, either by losing hit points
    // or by explicit calling of the effect.
    // -------------------------------------------------------------------------
    object oKiller = GetEventCreator(ev);
    int    bPartyWipe = IsPartyDead();

    SetCreatureFlag(OBJECT_SELF,CREATURE_RULES_FLAG_DYING,FALSE);

    // -------------------------------------------------------------------------
    // SkyNet creature death tracking event.
    // -------------------------------------------------------------------------
    #ifdef SKYNET
    TrackObjectDeath(ev);
    #endif

    // -------------------------------------------------------------------------
    // Clear the object's perception list
    // -------------------------------------------------------------------------
    ClearPerceptionList(OBJECT_SELF);

    AI_Threat_UpdateDeath(OBJECT_SELF);

    // -------------------------------------------------------------------------
    // If the party was wiped, set gamemode dead.
    // -------------------------------------------------------------------------
    if (bPartyWipe)
    {

        int iDeathHint = GetLocalInt(GetModule(), DEATH_HINT);

        //If module variable "DEATH_HINT" is not zero, use it.
        if(iDeathHint != 0)
        {
            SetDeathHint(iDeathHint, 205);
        }
        else
        {
            //If DEATH_HINT is zero, use loop to determine statistics on party.
            object[] oParty = GetPartyList(GetHero());
            int nSize = GetArraySize(oParty);
            int i;
            int iLevelCounter;
//          int iTacticCounter;
//          int iStaminaCounter;
            object oCurrent;
            for(i = 0; i < nSize; i++)
            {
                oCurrent = oParty[i];
                if(GetCanLevelUp(oCurrent) == TRUE)
                {iLevelCounter += 1;}
            }
            //Fire if party member needs to level up.
            if(iLevelCounter > 0)
            {
                  SetDeathHint(2, 205);
            }else
            {
            //Random Death hint
                int iRows = GetM2DARows(272);
                int nRand = Random(iRows) + 1;
                nRand = GetM2DARowIdFromRowIndex(272, nRand);
                SetDeathHint(nRand, 272);
            }
            SetLocalInt(GetModule(), DEATH_HINT, 0);
        }

        #ifdef DEBUG
        Log_Trace(LOG_CHANNEL_COMBAT_DEATH, "player_core.HandleOnDeath","Everyone dead, changing game mode...");
        #endif
        WR_SetGameMode(GM_DEAD);
    }
    else
    {
        // ---------------------------------------------------------------------
        // handle any plot-specific logic for a follower death
        // currently needed only for Wynne's special ability
        // ---------------------------------------------------------------------
        SendModuleHandleFollowerDeath(OBJECT_SELF);

        SetCombatState(OBJECT_SELF,FALSE);

        // ---------------------------------------------------------------------
        // If we are in explore mode, schedule auto resurrection
        // ---------------------------------------------------------------------
        if (GetGameMode() == GM_EXPLORE)
        {
            //------------------------------------------------------------------
            // Sorry, summoned creatures can't be revived.
            //------------------------------------------------------------------
            if (!IsSummoned(OBJECT_SELF))
            {
                _ScheduleResurrectionAttempt(OBJECT_SELF);
            }
        }

        // ---------------------------------------------------------------------
        // This handles the 'party member slain' message;
        // ---------------------------------------------------------------------
        object[] aAlly = GetNearestObjectByGroup(OBJECT_SELF, GetGroupId(OBJECT_SELF), OBJECT_TYPE_CREATURE,1, 1, 0, 0);
        if (GetArraySize(aAlly)>0)
        {
            SSPlaySituationalSound(aAlly[0],SOUND_SITUATION_PARTY_MEMBER_SLAIN, oKiller);
        }

    }

    return TRUE;
}


// -----------------------------------------------------------------------------
// Load tactics event handler
// -- Currently this just uses a naive method to populate tactics with valid
// -- skills.
// -----------------------------------------------------------------------------
int HandleEvent_LoadTactics(object oCreature, event ev);
int HandleEvent_LoadTactics(object oCreature, event ev)
{
    int nPresetID = GetEventInteger(ev, 0);
    Chargen_LoadPresetsTable(oCreature, nPresetID);

    return TRUE;
}

// -----------------------------------------------------------------------------
// Use ability immediately.
// -- Some player abilities are used immediately, bypassing the ai command queue
// -- in order to process them while the game is paused.
// -- Currently this is only used for the crafting GUI.
// -----------------------------------------------------------------------------
int HandleEvent_UseAbilityImmediate(object oCreature, event ev);
int HandleEvent_UseAbilityImmediate(object oCreature, event ev)
{

    int nAbility = GetEventInteger(ev, 0);
    ShowCraftingGUI(nAbility);

    return TRUE;
}

void main()
{
    event ev = GetCurrentEvent();
    int nEventType = GetEventType(ev);

    // Setting this to true will prevent the script from invoking rules_core
    int bEventHandled = FALSE;

    // Prevent log spam
    #ifdef DEBUG
    if (nEventType != EVENT_TYPE_HEARTBEAT2)
        Log_Events("", ev);
    #endif

    switch(nEventType)
    {
        // ---------------------------------------------------------------------
        // Fired by engine when creature is spawned.
        // ---------------------------------------------------------------------
        case EVENT_TYPE_SPAWN:
        {
            // Only do this once...
            if(!GetLocalInt(OBJECT_SELF, CREATURE_SPAWNED))
            {
                SetLocalInt(OBJECT_SELF, CREATURE_SPAWNED, 1);
                bEventHandled = HandleEvent_Spawn(ev);
            }
            break;
        }

        // ---------------------------------------------------------------------
        // Handle Inventory Added / Removed Events
        //  Params:
        //      int (0) - the inventory slot the item added or removed from
        //      obj (0) - the item
        // ---------------------------------------------------------------------
        case EVENT_TYPE_INVENTORY_REMOVED:
        case EVENT_TYPE_INVENTORY_ADDED:
        {
            bEventHandled = HandleEvent_InventoryEvent(ev);
            break;
        }

        // ---------------------------------------------------------------------
        // Handle Perception Disappear Events
        // ---------------------------------------------------------------------
        case EVENT_TYPE_PERCEPTION_DISAPPEAR:
        {
            bEventHandled = HandleEvent_PerceptionDisappear(ev);
            break;
        }

        // -----------------------------------------------------------------
        // Damage over time tick event.
        // This is activated from EffectDOT and keeps rescheduling itself
        // while DOTs are in effect on the creature
        // -----------------------------------------------------------------
        case EVENT_TYPE_DOT_TICK:
        {
            if (!IsDead() && !IsDying())
            {
              Effects_HandleCreatureDotTickEvent();
            }

            bEventHandled = TRUE;
            break;
        }

        // ---------------------------------------------------------------------
        // @brief Heartbeat event generated by engine in response to InitHeartbeat()
        // ---------------------------------------------------------------------
        case EVENT_TYPE_HEARTBEAT2:
        {
             // No heartbeat for dead people
             if (IsDeadOrDying(OBJECT_SELF))
               return;

            // gradual mana/stamina regen in combat
            if(GetGameMode() == GM_COMBAT)
            {
                float fCurrentManaStamina = GetCurrentManaStamina(OBJECT_SELF);
                float fCurrentStaminaRegen = GetCreatureProperty(OBJECT_SELF, PROPERTY_ATTRIBUTE_REGENERATION_STAMINA_COMBAT, PROPERTY_VALUE_BASE);
                float fNewStaminaRegen = fCurrentStaminaRegen;
                if(fCurrentManaStamina <= 25.0) // fastest regen
                    fNewStaminaRegen = REGENERATION_STAMINA_COMBAT_DEFAULT + 3.5;
                else if(fCurrentManaStamina <= 50.0) // mid regen
                    fNewStaminaRegen = REGENERATION_STAMINA_COMBAT_DEFAULT + 1.0;

                else // more than 50 -> slowest regen
                    fNewStaminaRegen = REGENERATION_STAMINA_COMBAT_DEFAULT + 0.5;

                SetCreatureProperty(OBJECT_SELF, PROPERTY_ATTRIBUTE_REGENERATION_STAMINA_COMBAT, fNewStaminaRegen);
            }

            // Check for traps
            Trap_RunDetectionPulse(OBJECT_SELF);


            // Track movements for stats
          //  if (IsHero(OBJECT_SELF)) STATS_TrackWalkedDistance();


             // ----------------------------------------------------------------
             // Generate SkyNet Position Tracking Event
             // http://georg/SkyNetWeb - Talk to georg if you have questions
             // Note: For development telemetry only - will not work in SHIP exectuables.
             // ----------------------------------------------------------------
             #ifdef SKYNET
             if (IsHero(OBJECT_SELF))
             {
                TrackPos();
             }
             #endif

             if (LOG_ENABLED)
             {
                if (IsImmortal(OBJECT_SELF))
                {
                    command cCommand = GetCurrentCommand(OBJECT_SELF);
                    if(GetCommandType(cCommand) != 38) // death blow command (engine turns follower immortal during death blows)
                    {
                     //   Warning ("Warning: " + ToString(OBJECT_SELF) + " seems to be immortal, which is probably a bug. Hero tag: " + GetTag(GetHero())+"Please file a bug through SkyNet to Yaron");
                        DEBUG_PrintToScreen("Warning: PC object " + ToString(OBJECT_SELF) + " is immortal!", 15 + Random(2), 2.0f);
                    }
                }
                DEBUG_PrintToScreen("Difficulty " + ToString(GetGameDifficulty()) + "", 11, 2.0f);
             }


             bEventHandled = TRUE;
             break;
        }

        // -----------------------------------------------------------------
        // Legacy Heartbeat event. Left for the consumption of modders.
        // Be careful with it, it's not nice to run on a lot of creatures...
        // -----------------------------------------------------------------
        case EVENT_TYPE_HEARTBEAT:
        {
             bEventHandled = TRUE;
             break;
        }

        case EVENT_TYPE_EQUIP:
        {
            bEventHandled = HandleEvent_Equip(ev);
            break;
        }

        case EVENT_TYPE_UNEQUIP:
        {
            bEventHandled = HandleEvent_UnEquip(ev);
            break;
        }

        // ---------------------------------------------------------------------
        // Fires first time a party member is added to the party
        // For plot followers: follower recruited (added to pool)
        // For other followers: UT_Hire called
        // Owner: Yaron
        // ---------------------------------------------------------------------
        case EVENT_TYPE_PARTY_MEMBER_HIRED:
        {
            int nScaled = GetLocalInt(OBJECT_SELF, FOLLOWER_SCALED);
            int nShowPartyPicker = GetEventInteger(ev, 0);
            int nMinLevel = GetEventInteger(ev, 1);
            int bPreventLevelup = GetEventInteger(ev, 2);

            #ifdef DEBUG
            Log_Trace(LOG_CHANNEL_EVENTS, "player_core.EVENT_TYPE_PARTY_MEMBER_HIRED",
                "show party picker: " + IntToString(nShowPartyPicker));
            #endif

            int bSummoned  =  IsSummoned(OBJECT_SELF);

            // -----------------------------------------------------------------
            // @author georg Initialize Follower Heartbeat.
            // Note: This is terminated in EVENT_TYPE_PARTY_MEMBER_FIRED.
            // -----------------------------------------------------------------
            if (!bSummoned)
            {
                // Heartbeat check moved to WR_SetFollowerState
                //InitHeartbeat(OBJECT_SELF, CONFIG_CONSTANT_HEARTBEAT_RATE);

                // checking tactics presets
                // It is fine to do this more than once
                Chargen_EnableTacticsPresets(OBJECT_SELF);
            }

            // -----------------------------------------------------------------
            // @author yaron
            // This can fire only once - when first hired
            // -----------------------------------------------------------------
            if(!nScaled && !bSummoned && !IsHero(OBJECT_SELF))
            {
                SetLocalInt(OBJECT_SELF, FOLLOWER_SCALED, 1);
                int nPackage = GetPackage(OBJECT_SELF);
                int nPackageClass = GetM2DAInt(TABLE_PACKAGES, "StartingClass", nPackage);


                // set behavior
                int nBehavior = GetM2DAInt(TABLE_PACKAGES, "FollowerBehavior", nPackage);
                if(nBehavior >= 0)
                    SetAIBehavior(OBJECT_SELF, nBehavior);

                // -------------------------------------------------------------
                // <scaling>
                //
                // NOTE: creature was scaled already in creature_core - in here
                // we clear him completely and reconstruct from scratch
                // -------------------------------------------------------------
                Chargen_InitializeCharacter(OBJECT_SELF);

                // -------------------------------------------------------------
                // Apply race and class modifiers.
                // -------------------------------------------------------------
                Chargen_SelectRace(OBJECT_SELF,GetCreatureRacialType(OBJECT_SELF));
                Chargen_SelectCoreClass(OBJECT_SELF,GetCreatureCoreClass(OBJECT_SELF));

                // -------------------------------------------------------------
                // yaron: Scale followers to level.
                // -------------------------------------------------------------
                int nTargetLevel;
                int nPlayerLevel = GetLevel(GetHero());
                if(nPlayerLevel >= 13 || nPlayerLevel == 1 || !_UT_GetIsPlotFollower(OBJECT_SELF))
                    nTargetLevel = nPlayerLevel;
                else
                    nTargetLevel = nPlayerLevel + 1;
                int nMinLevel = GetM2DAInt(TABLE_PACKAGES, "MinLevel", nPackage);
                if(nMinLevel > 0 && nMinLevel > nTargetLevel)
                    nTargetLevel = nMinLevel;
                #ifdef DEBUG
                Log_Trace(LOG_CHANNEL_EVENTS, "player_core.EVENT_TYPE_PARTY_MEMBER_HIRED",
                        "Target level: " + IntToString(nTargetLevel));
                #endif

                if(nPackageClass != CLASS_MONSTER_ANIMAL)
                {
                    // -------------------------------------------------------------
                    // Follower leveled one level higher than the player unless the player
                    // is too high level.
                    // -------------------------------------------------------------

                    int nXp = RW_GetXPNeededForLevel(Max(nTargetLevel, 1));

                    #ifdef DEBUG
                    Log_Trace(LOG_CHANNEL_EVENTS, "player_core.EVENT_TYPE_PARTY_MEMBER_HIRED",
                            "Giving XP: " + IntToString(nXp));
                    #endif

                    int nState = GetFollowerState(OBJECT_SELF);
                    string sFollowerState = _GetFollowerStateName(nState);

                    #ifdef DEBUG
                    Log_Trace(LOG_CHANNEL_EVENTS, "player_core.EVENT_TYPE_PARTY_MEMBER_HIRED",
                            "Follower state: " + sFollowerState);
                    #endif
                    RewardXP(OBJECT_SELF, nXp, FALSE, FALSE);
                }

                // -------------------------------------------------------------
                // add hidden approval talents
                // -------------------------------------------------------------
                int nIndex = Approval_GetFollowerIndex(OBJECT_SELF);
                Approval_AddFollowerBonusAbility(nIndex, 0);

                // Find specialization
                int nSpecAbility = GetM2DAInt(TABLE_PACKAGES, "switch1_class", nPackage); // followers can have only 1 advanced class
                if(nSpecAbility > 0)
                {
                    #ifdef DEBUG
                    Log_Trace(LOG_CHANNEL_EVENTS, "player_core.EVENT_TYPE_PARTY_MEMBER_HIRED",
                        "Adding spec ability: " + IntToString(nSpecAbility));
                    #endif
                    AddAbility(OBJECT_SELF, nSpecAbility);
                }

                // -------------------------------------------------------------
                // This spends all available attribute and stat points on the
                // creature according to the levelup table.
                // -------------------------------------------------------------

                AL_DoAutoLevelUp(OBJECT_SELF, TRUE);

                if(bPreventLevelup)
                {
                    #ifdef DEBUG
                    Log_Trace(LOG_CHANNEL_EVENTS, "player_core.EVENT_TYPE_PARTY_MEMBER_HIRED",
                        "Preventing creature from levelling up");
                    #endif
                    SetLocalInt(OBJECT_SELF, CREATURE_REWARD_FLAGS, 1);
                }

                // load tactics
                int nTableID = GetM2DAInt(TABLE_PACKAGES, "FollowerTacticsTable", nPackage);
                if (nTableID != -1)
                {
                    #ifdef DEBUG
                    Log_Trace(LOG_CHANNEL_EVENTS, "player_core.EVENT_TYPE_PARTY_MEMBER_HIRED",
                        "Loading follower tactics from table: " + IntToString(nTableID));
                    #endif
                    int nRows = GetM2DARows(nTableID);
                    int nMaxTactics = GetNumTactics(OBJECT_SELF);
                    #ifdef DEBUG
                    Log_Trace(LOG_CHANNEL_EVENTS, "player_core.EVENT_TYPE_PARTY_MEMBER_HIRED",
                        "Loading follower tactics from table: " + IntToString(nTableID) + ", row: " + IntToString(nRows));
                    #endif

                    int nTacticsEntry = 1;
                    int i;
                    for (i = 1; i <= nRows && nTacticsEntry <= nMaxTactics; ++i)
                    {
                        int bAddEntry = FALSE;
                        int nTargetType = GetM2DAInt(nTableID, "TargetType", i);
                        int nCondition = GetM2DAInt(nTableID, "Condition", i);
                        int nCommandType = GetM2DAInt(nTableID, "Command", i);
                        int nCommandParam = GetM2DAInt(nTableID, "SubCommand", i);

                        #ifdef DEBUG
                        Log_Trace(LOG_CHANNEL_EVENTS, "player_core.EVENT_TYPE_PARTY_MEMBER_HIRED",
                            "adding tactics: " + IntToString(i));
                        #endif
                        int nUseType = GetM2DAInt(TABLE_COMMAND_TYPES, "UseType", nCommandType);
                        if (nUseType == 0)
                        {
                            bAddEntry = TRUE;
                        }
                        else
                        {
                            bAddEntry = HasAbility(OBJECT_SELF, nCommandParam);
                        }

                        if (bAddEntry)
                        {
                            SetTacticEntry(OBJECT_SELF, nTacticsEntry, TRUE, nTargetType, nCondition, nCommandType, nCommandParam);
                            ++nTacticsEntry;
                        }
                    }
                }

                // @author yaron
                // DEBUG - scale items
                #ifdef DEBUG
                if(GetLocalInt(GetModule(), DEBUG_ENABLE_PARTY_ITEM_SCALING) == 1)
                {

                    Log_Trace(LOG_CHANNEL_EVENTS, "player_core.EVENT_TYPE_PARTY_MEMBER_HIRED",
                        "DEBUG - scaling items - THIS CODE SHOULD NOT RUN NORMALLY");
                    DEBUG_ScaleFolloweItems(OBJECT_SELF);
                }
                #endif
                //if this is Alistair - show the tutorial
                if (GetTag(OBJECT_SELF) == "gen00fl_alistair")
                {
                    BeginTrainingMode(TRAINING_SESSION_FOLLOWERS_AND_TACTICS);
                }
            }

            if(nShowPartyPicker && GetLocalInt(GetArea(OBJECT_SELF), AREA_DEBUG) == FALSE)
            {
                SetPartyPickerGUIStatus(PP_GUI_STATUS_USE);
                ShowPartyPickerGUI();
            }

            bEventHandled = TRUE;
            break;
        }


        // ---------------------------------------------------------------------
        // Fires an active or locked-active party member is removed from the
        // active party
        // ---------------------------------------------------------------------
        case EVENT_TYPE_PARTY_MEMBER_FIRED:
        {
            // NOTE: this event actually does not fire in many cases
            // follower-fired code in better put in WR_SetFollowerState

            break;
        }

        // ---------------------------------------------------------------------
        // Sent by engine when henchman or player is selected.
        // ---------------------------------------------------------------------
        case EVENT_TYPE_ON_SELECT:
        {
            SSPlaySituationalSound(OBJECT_SELF, SOUND_SITUATION_SELECTED);
            bEventHandled = TRUE;
            break;
        }

        // ---------------------------------------------------------------------
        // Sent by engine when henchman or player is given an order.
        // ---------------------------------------------------------------------
/*        case EVENT_TYPE_ON_ORDER_RECEIVED:
        {
            SSPlaySituationalSound(OBJECT_SELF, SOUND_SITUATION_ORDER_RECEIVED,GetEventTarget(ev));
            bEventHandled = TRUE;
            break;
        }*/
        case 94 : /*EVENT_TYPE_PLAYER_COMMAND_ADDED:*/
        {
            SSPlaySituationalSound(OBJECT_SELF, SOUND_SITUATION_ORDER_RECEIVED, GetEventTarget(ev), GetEventInteger(ev, 0));
            bEventHandled = TRUE;
            break;
        }




        // ---------------------------------------------------------------------
        // Sent by engine when creature is killed.
        // ---------------------------------------------------------------------
        case EVENT_TYPE_DEATH:
        {
            bEventHandled = HandleEvent_Death(ev);
            break;
        }

        // ---------------------------------------------------------------------
        // Resurrection timer used if a creature dies in explore mode.
        // ---------------------------------------------------------------------
        case EVENT_TYPE_PARTY_MEMBER_RES_TIMER:
        {
            if (GetGameMode() == GM_EXPLORE)
            {
                ResurrectCreature(OBJECT_SELF);
            }
            break;
        }

        // ---------------------------------------------------------------------
        // Creature is resurrected. Fired by effect_resurrection.OnApply
        // ---------------------------------------------------------------------
        case EVENT_TYPE_RESURRECTION:
        {
            bEventHandled = HandleEvent_Resurrection(OBJECT_SELF, ev);
            break;
        }

        // ---------------------------------------------------------------------
        // Creature is spawned. Fired by sys_rewards_h.RewardXP
        // ---------------------------------------------------------------------
        case EVENT_TYPE_PLAYER_LEVELUP:
        {
            #ifdef SKYNET
            TrackPartyMemberEvent(nEventType, OBJECT_SELF, OBJECT_INVALID, GetLevel(OBJECT_SELF));
            #endif

            UI_DisplayMessage(OBJECT_SELF, UI_MESSAGE_LEVELUP);
            break;
        }

        case EVENT_TYPE_LOAD_TACTICS_PRESET:
        {
            bEventHandled = HandleEvent_LoadTactics(OBJECT_SELF, ev);
            break;
        }

        //----------------------------------------------------------------------
        // Sent by engine when player clicks on object.
        //----------------------------------------------------------------------
        case EVENT_TYPE_PLACEABLE_ONCLICK:
        {
            // Pass event along to the placeable being clicked on.
            SignalEvent(GetEventTarget(ev), ev);
            bEventHandled = TRUE;
            break;
        }

        case EVENT_TYPE_USE_ABILITY_IMMEDIATE:
        {
            bEventHandled = HandleEvent_UseAbilityImmediate(OBJECT_SELF, ev);
            break;
        }

    }


    if (!bEventHandled)
    {
        HandleEvent(ev, RESOURCE_SCRIPT_RULES_CORE);
    }
}

 

What is really fascinating is the motivation to learn. The modders get to see the results of what they do right away. In certain cases they get to see what many people have to say. They also learn what certain programmatic approaches do and why they are used. The game serves as a practical and more importantly a relatable means of applying what they learn.

In many cases this is one the most crucial items lacking in many organizations and from many professionals trying to teach programming to up and coming individuals. Their inability to relate concepts to others is what drives much of the barrier to collaboration and cooperation. And from my experience, many large organizations simply find excuses not to pursue better approaches because they simply do not know how to do so or do not have the desire to do so.

In any event I find it very comforting to know that there are probably quite a few talented individuals coming up in the technology field from a wide and varied background. I also like the fact that video games have a lot to teach people about computer programming and science than what most people think.

Wednesday
06Jan2010

2010 - I Predict as "The Year of Innovation"

As the overall economy starts to recover, there are signs of improvement everywhere. Admittedly we have a long way to go but recent stories about various innovations are proving that the year 2010 will be definitely a great year.

Why do I think this? Well there are the big items of course such as the new Google Nexus One, the rumors of the Apple tablet, and many other wonderful things being unveiled at CES. These innovations are fundamentally changing the markets, the consumer experience, and the professional lives of the men and women working in these areas. They are achieving this in spite of being in one of the worse economic times in history. Where many larger brand names have been solely focusing on the typical management cost controls (i.e. layoffs, etc.) more forward thinking companies like Google and Apple have been creating new value propositions that are poised to take off. This sort of activity fundamentally differentiates the leaders and innovators, from the status quo.

However it is not only the larger names making impacts. Smaller and younger companies are also making names for themselves in areas that were not conceived of more than a few short years ago. Some examples are AteBits, Inc., Card.ly, Seesmic, and CitySourced to name a few. All of these companies emerged during difficult economic times, using extraordinary vision and innovation, making simple and powerful services, and on budgets that in many cases amounted to far less than the office supply allowances for many large companies.

The economic downturn has convinced many that their personal and professional lives are truly in their own hands and as such they have started to create value on their own. However lead by a new way of creating value for their products via mechanisms like Amazon Web Services, Apple's App Store, Android App Store and Twitter's Open APIs, many new companies and brands are emerging by leveraging the offerings created by these companies to create their own unique value. The men and women taking this approach are intelligent, driven, insightful and highly skilled learning what they need as they go which makes them excellent professionals not only for what they do now but what they are doing in the future.

It is through the circumstance of a bad economy that has fueled this engine of innovation and professional growth. Given the amount of time it will take the economy to recover and the amount of people taking this course, this is creating a whole new workforce and opportunity that will fundamentally change how businesses operate in the future. Let's take the telecom/handset/mobile space. Why would talent want to work for the traditional status quo providers when all the opportunity, excitement and innovation is occurring in the new areas created by Apple and Google? The cost of entry is low, the rewards very high, and far more satisfying than working with any larger company. In many cases the same cost cutting and "hunker down" attitudes have come at immeasurable cost of talent acquisition. Most talented individuals no longer see working at the existing companies as being in their best interests. This will take

It will be interesting to see how the existing companies see these changes and how long they wait to adjust to them. After all, they are being outmaneuvered right now at a time that challenges the core of who they are. And in my opinion many existing companies are either dying by inches or barely holding on whereas their more innovative counterparts are thriving and growing. 2010 will indeed be an interesting year!

Friday
30Oct2009

Trust Your Instincts - Do Not Overthink A Problem

There are many times technical professionals tend to spend a lot of time planning a course of action. This is not entirely unexpected. Most technically oriented individuals want to know the exact specifications or requirements so that they know what they have to accomplish. I am sure we have all heard phrases like "if we do not know what the requirements are, we cannot code to it" or something similar. While I appreciate this mindset for many routine issues, I often find that the more challenging a problem is with little to no prior solution, the less beneficial this process becomes. It more often turns into roadblock with a lot of time spent beyond simple deliberation. When planning reaches this stage I refer to it as "Overthinking a Problem". At which point I push my teams into doing something rather than thinking about something.

This would seem a bit strange to most individuals and it definitely is to members outside of a technical department within a business when they hear about it. Fear is the reason for such delaying tactics. It tends to cover risks to the business all the way to the individual's reputation. Afterall to many senior professionals they perceive their expertise as an asset and their ignorance as a liability. However not exploring or branching out to solve problems in a different way leads to what I call technological stagnation within a company. Ultimately this means that what the business receives from technical staffs is not what they want which can lead to outsourcing or reductions in staff and budget. In almost every business I have worked in, they expect their technical groups to be the leaders in technology not followers.

On a recent project, business users wanted the ability to analyze a range of data more free form than their current reporting tools and across a wider scope of all ready approved ranges. Their solution was constructed on a traditional data warehousing approach with a reporting tool front end. While this met the majority of their needs, their more critical needs were not being addressed by the solution in a timely manner. Ultimately I offered using search engine technology which could easily access the data they needed while providing a faster and more flexible way to get to their data. While the idea was interesting to the users, the technical groups immediately began debating the merits and risks of such a solution. In an effort to bring better focus and clarity to the arguments, and to quite frankly cut down the time being wasted in these meetings which I had to participate in, I created a proof of concept on the users data set using Apache Solr within a week. The following meeting I demonstrated the solution complete with the top functionality that the users had requested such as ability to search for information, faceted search along known dimensions, and created some simple AJAX screens so that the users could get some of their most common questions answered immediately such as top results for a search, historical results, top statistics for a particular term, and the ability to export the data out to spreadsheets so that they could perform their own analysis when needed. During my entire presentation there were numerous questions from my peers in the technical groups. Most I could answer with my demonstration, a few I could not but could be justified using the proof of concept as a model. In the end the solution was accepted and approved for the business users. What would normally have been a three to four month effort with a high probability of not being deployed, turned into a one month effort that had a very happy user community.

While it is true that businesses have more risks due to their nature, they often tend to create cultures where risk taking has to be extensively and exhaustively analyzed. In businesses where their products have to stand the test of time such as airplanes or bridges this makes a lot of sense. However most companies apply this level of thinking to intranet-based software which in my opinion is just wrong. Software needs to move quickly to meet user needs. Internet companies have long held a tradition of trusting their instincts, not overthinking problems, and rolling something out so that users can interact with it. Doing this quickly and with quality is achievable and allows them to meet their customers needs far more quickly than traditional methods. Speed is no longer just the providence of internet companies but normal companies as well especially with regard to technology-based solutions. As a result, many long standing technology professionals are finding themselves having to adjust to a speed and viewpoint that is highly uncomfortable yet necessary.

Ultimately this skill of trusting your instinct is absolutely necessary for career growth because in my opinion doing something is worth a lot more than thinking about it.