Author Topic: Scripted DM module  (Read 563 times)

Legacy_Celludriel

  • Newbie
  • *
  • Posts: 6
  • Karma: +0/-0
Scripted DM module
« on: March 09, 2014, 06:58:03 pm »


               

Hey,


 


A while ago I read a dissertation about AI in computer games, more in specific making them feel alive.  It went on in detail about a system of needs an actor would have in the world, and how he would find ways to satisfy these needs.  The best example being put out being the Sims games of the past years.  You build them a house with stuff and they get hungry so they go cook, or want to take a bath and go to the bathroom.


 


So that got me thinking back on my NWN script days and I got the itch again to try something likewise on my own.  But then I started thinking about what NWN actually is.  It's a computer representation of D&D where a DM would tell a story and it plays out with the players doing actions triggering events ...  So this sounds terribly familiar with how a regular computer program works.  You do an action like press a button and an event gets send to an eventhandler that launches some backendservice to handle the event.  This could perfectly be replicated in the NWN2 or NWN1 engine with not that much trouble.  In principle ... what I did tend to forget is that it was way to long ago that I scripted in it let alone know any of the libraries and the Aurora quirks like no arrays and no list, sets or maps ... hell to be without for a java programmer !


 


However I wouldn't let that stop me and started to design a system of scripted dm's.  Basicly what it comes down to is that you will have invicible placeables that will act as a DM and guide a story or handle the daily life of a village.  I started out with something simple, a village needs x amount of grain so that a miller can turn it into flower and a baker can use that flower to make bread.  When the villagers get hungry they will come and fetch that bread. 


 


With a prove of concept like that I could go on to bigger braver things.  Like for instance orcs in the mountains multiply after x hearthbeats , raising their need for expantion and when that need is met , an orc camp would appear on a random spawn location in the world.  When to much orc camps appear the villages get restless and put out a quest on a notice board for a PC to clear out an orc camp.  This as just a small thing of what would be possible.  Each event/story would then be guided by a scripted DM invincible object somewhere in the world.


 


I started scripting and got a system running with a fixed need of 5 grain to send a farmer off to a field to do some farming.  The backbone is working , however actually making a farmer walk over to the right waypoint, looping a digging animation, and such is giving me more headaches then I would like for such a simple action.


 


So my question after this lenghty introduction , is there somewhere a nice tool that let you search through the NWN2 script library for functions cause the toolkit script IDE is well terrible.  I found a BioSearcher program , but that one doesn't seem to be working at least not on my system, it crashes with what I believe a Nullpointer on a memory address ... it's Delphi and I'm a java dev so it's hard for me to figure out the exception being thrown.


 


this is the script I'm trying to complete my problems are:


 


- How can I find out if a creature is allready present at the waypoint


- How can I loop constantly a digging animation


- How can I make the farmer walk to the waypoint in the first place without getting stuck behind a rock or house or ... well whatever (I know waypoints in NWN2 where a drag)


 


void handleFarmActivity(object farmer){

    string phase = GetLocalString(farmer, VAR_ACTIVITY_PHASE);

    PrintString("FARMING PHASE:" + phase);

        

    if(phase == PHASE_INITIALIZE){

        //farmer should go to a farm waypoint that is not occupied yet    

        //once reached upgrade phase to PHASE_FARM            

    }

    

    if(phase == PHASE_FARM){

        //set a duration local int on the farmer measured in HB ticks if not present

        //each time we enter this phase, decrease duration with one

        //on duration zero create one GRAIN token in the farmer inventory and upgrade phase to PHASE_DELIVER

    }

    

    if(phase == PHASE_DELIVER){

        //let the farmer go to the area stock object

        //once arrived move grain object from farmer inventory to stock object inventory

        //upgrade phase to PHASE_COMPLETED

    }

    

    if(phase == PHASE_COMPLETED){

        //just let the farmer walk around a bit the DM will assign him a new task soon

    }

    

    return;

}

 



               
               

               
            

Legacy_ehye_khandee

  • Hero Member
  • *****
  • Posts: 1415
  • Karma: +0/-0
Scripted DM module
« Reply #1 on: March 09, 2014, 11:10:50 pm »


               

This may help.



//How can I find out if a creature is allready present at the waypoint -START
object oFarmer = GetObjectByTag(TAG_OF_FARMER_NPC);
object oGoalLocation = GetWaypointByTag(TAG_OF_WAYPOINT);
if GetIsObjectValid(oFarmer)// farmer found check distance from goal
{
    if(GetDistanceBetween(oFarmer,oGoalLocation) < 5.0f)
    {
    // oFarmer IS within five meters of the goal ... you may want to tickle your animation here.
    }
    else
    {
    AssignCommand(oFarmer,ActionMoveToObject(GetWaypointByTag(TAG_OF_WAYPOINT)));
    }
}
else
{
// spawn a farmer because there are none in the module yet.
}
// How can I find out if a creature is allready present at the waypoint -END

And to your second question:



// How can I loop constantly a digging animation - START
AssignCommand(oFarmer,PlayAnimation(int nAnimation, float fSpeed=1.0, float fSeconds=0.0));
// How can I loop constantly a digging animation - END

In the above code, select a looping animation (nAnimation) and loop it as long as you please (fSeconds) to achieve the desired result.


 


 


As far as the last question you had, BUILD SMART, avoid placing objects across the red lines you see in the toolkit as it can monkey up pathing for NPCs in some cases (this has been improved since NWN was first released and iirc NWN was always better than NWN2 in that regard perhaps due to less than optimal walkmesh configurations).


 


Hope this helps.


 


Be well. Game on.


GM_ODA


               
               

               
            

Legacy_Celludriel

  • Newbie
  • *
  • Posts: 6
  • Karma: +0/-0
Scripted DM module
« Reply #2 on: March 10, 2014, 06:45:53 am »


               

thanks


 


GetDistanceBetween(object, object) is going to give me all I need to finish I believe.


 


It got me the sollution for the WP as well.  I could create a closed circuit "highway" of waypoints.  That highway would go along all the important "work places" in the area.  When I give the order to go work somewhere, I just first send the npc to the closest highway node and let it traverse it untill it is closer then the next node in the highway to the workplace, or close in a vicinity of x meters, should the previous condition never be met (he might be walking around the highway indef otherwise).


 


This code is triggered on a heartbeat, Wouldn't AssignCommand(oFarmer,ActionMoveToObject(GetWaypointByTag(TAG_OF_WAYPOINT))); queue move action after move action each 6 seconds ?  I could ofcourse do a getCurrentAction(object) on the farmer to see if it allready has a move action, but that would be another check '<img'>.  I rather keep my hb scripts nice and short.  However this might not always be feasable.



               
               

               
            

Legacy_Celludriel

  • Newbie
  • *
  • Posts: 6
  • Karma: +0/-0
Scripted DM module
« Reply #3 on: March 11, 2014, 05:06:10 pm »


               

This is what I eventually came up with, almost done I just need to make him spawn a grain bag into the stock.  Just putting it here for future reference and if people need some inspiration.



void handleFarmingBehavior(object farmer){
    string phase = GetLocalString(farmer, VAR_ACTIVITY_PHASE);
    string phase_status = GetLocalString(farmer, VAR_PHASE_STATUS);
    PrintString("FARMING PHASE:" + phase);
        
    if(phase == PHASE_INITIALIZE){    
        PrintString("ENTERING:" + phase);
        //farmer should go to a farm waypoint that is not occupied yet    
        //once reached upgrade phase to PHASE_FARM   
        object farmWp = GetNearestObjectByTag("DM_WP_FARM", farmer);
        if(farmWp != OBJECT_INVALID){
            PrintString("Valid farmWp");
            location farmLocation = GetLocation(farmWp);
            PrintString("phase status: " + phase_status);
            if(phase_status == ""){
                AssignCommand(farmer, ClearAllActions());            
                AssignCommand(farmer,ActionMoveToLocation(farmLocation));
                AssignCommand(farmer, ActionPlayAnimation(ANIMATION_LOOPING_SHOVELING, 1.0, 300.0));
                SetLocalString(farmer, VAR_PHASE_STATUS, STATE_RUNNING);
            }else if(phase_status == STATE_RUNNING){
                object creature = GetFirstObjectInShape(SHAPE_CUBE, 2.5, farmLocation, FALSE, OBJECT_TYPE_CREATURE);                
                if(creature != OBJECT_INVALID && creature == farmer){                                        
                    SetLocalString(farmer, VAR_ACTIVITY_PHASE, PHASE_FARM);
                    SetLocalString(farmer, VAR_PHASE_STATUS, "");
                }else{
                    //the farmer probably had a problem and never reached the farming point lets try again
                    AssignCommand(farmer, ClearAllActions());            
                    AssignCommand(farmer,ActionMoveToLocation(farmLocation));
                    AssignCommand(farmer, ActionPlayAnimation(ANIMATION_LOOPING_SHOVELING, 1.0, 300.0));
                }
            }
        }else{
            //no farming possibility set phase to completed so cleanup can happen
            SetLocalString(farmer, VAR_ACTIVITY_PHASE, PHASE_COMPLETED);
            SetLocalString(farmer, VAR_PHASE_STATUS, "");
        }        
    }
    
    if(phase == PHASE_FARM){
        AssignCommand(farmer, ActionPlayAnimation(ANIMATION_LOOPING_SHOVELING, 1.0, 300.0));    
        if(phase_status == ""){
            SetLocalInt(farmer, VAR_PHASE_DURATION, 5);
            SetLocalString(farmer, VAR_PHASE_STATUS, STATE_RUNNING);
        }else if(phase_status == STATE_RUNNING){
            int duration = GetLocalInt(farmer, VAR_PHASE_DURATION);
            if(duration == 0){
                SetLocalString(farmer, VAR_ACTIVITY_PHASE, PHASE_DELIVER);
                SetLocalString(farmer, VAR_PHASE_STATUS, "");                
            }
        }        
    }
    
    if(phase == PHASE_DELIVER){
        object stockLocationObject = GetNearestObjectByTag("DM_VILLAGE_STOCK", farmer);
        location stockLocation = GetLocation(stockLocationObject);
        if(phase_status == ""){
            AssignCommand(farmer, ClearAllActions());
            AssignCommand(farmer,ActionMoveToLocation(stockLocation));
            SetLocalString(farmer, VAR_PHASE_STATUS, STATE_RUNNING);
        }else if(phase_status == STATE_RUNNING){
            object creature = GetFirstObjectInShape(SHAPE_CUBE, 2.5, stockLocation, FALSE, OBJECT_TYPE_CREATURE);                
            if(creature != OBJECT_INVALID && creature == farmer){
                //TODO: we need to put a grain item in the chest
                                
                //Finish the farmers activity by putting the phase to completed                                        
                SetLocalString(farmer, VAR_ACTIVITY_PHASE, PHASE_COMPLETED);
                SetLocalString(farmer, VAR_PHASE_STATUS, "");
            }else{
                //the farmer probably had a problem and never reached the delivery point lets try again
                AssignCommand(farmer, ClearAllActions());            
                AssignCommand(farmer,ActionMoveToLocation(stockLocation));
            }        
        }
    }
    
    if(phase == PHASE_COMPLETED){
        if(phase_status == ""){
            AssignCommand(farmer, ClearAllActions());
            AssignCommand(farmer,ActionRandomWalk());
            SetLocalString(farmer, VAR_PHASE_STATUS, STATE_RUNNING);
        }
    }
    
    return;
}