Creating universal scripts by placing variables on areas
This scripting forum is to discuss scripting and for those needing help scripting to get help from others.

Moderators: Mermut, rdjparadis

Post Reply
User avatar
Winterhawk99
Posts: 1629
Joined: Thu Oct 08, 2009 12:00 am
ctp: Yes
nwnihof: Yes
Location: Pa.
Contact:

Creating universal scripts by placing variables on areas

Post by Winterhawk99 »

I'm using areas as an example here. You can create more scripts on Placeables, NPCs, Items and anything else that has an advanced tab on their properties and variable editing on the variable tab of the advance properties.

So what we are looking for is a script that can so a lot of different things so that you don't need to keep rewriting similar scripts to clog up a module. The example I will be using is an area damage script that is versatile enough to handle any type of extreme or mildly painful environmental condition a builder can think of. It should also be able to handle different amounts of damage during different time periods and different types of damage. It goes something like this as an example:

lets say that a group of people want to explore Antarctica and reach the south pole. As they start the journey and go through areas its going to get colder. So perhaps you want to have a check for cold damage and make your cold damage checks closer together at greater saves for more points as they journey area to area. Now lets say they find an active volcano with lots of caves to explore. Some are so hot they need to check for fire damage. Some have acid all over and they have to check with acid damage. We are going to handle all of this with one script.

The first thing we need to do is to create a function to define what ambient damage is and another to handle and do the damage to pcs entering the area.

* a few notes first I'm doing this a little backwards so its easier to understand normally you would write the script and plug the variables in but here its easier to explain all this in a backward sort of way.*

*Also the script was created by Mermut for harvest moon it is a marvel of elegance in scripting.*

Ok back to our regularly featured program. Here is your function include:

Code: Select all

// Ambient Area Damage Function
// Roll iCount of iDie to determine damage of iDamageType to oPC while
// they are in oArea if they fail a fort save of iFortDC. The Fort
// Save/damage runs every fHB_Time seconds.
// The values for everything except oPC and oArea are pulled from variables
// set on the area.
void AmbientDamage(object oPC, object oArea);

void DoDamage(object oPC, object oArea, int iDie, int iCount, int iType, int iFortDC, float fDelay)
{
    // check again for PC in the area in case the PC has left
    // during the delay
    if (GetArea(oPC) != oArea)
        return;

    // Determine the amount of damage by converting iDie into the
    // appropriate d#() function
    int iDamage;
    if (iDie <= 2)
        iDamage = d2(iCount);
    else if (iDie == 3)
        iDamage = d3(iCount);
    else if (iDie < 6)
        iDamage = d4(iCount);
    else if (iDie < 8)
        iDamage = d6(iCount);
    else if (iDie < 10)
        iDamage = d8(iCount);
    else if (iDie < 12)
        iDamage = d10(iCount);
    else if (iDie < 20)
        iDamage = d12(iCount);
    else if (iDie < 100)
        iDamage = d20(iCount);
    else // if iDie >= 100
        iDamage = d100(iCount);

    // run the fort save, if they fail, do damage
    if (!FortitudeSave(oPC, iFortDC, SAVING_THROW_TYPE_NONE, oPC))
    {
        effect eDamage = EffectDamage(iDamage, iType);
        ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage,oPC);
    }
    DelayCommand(fDelay, DoDamage(oPC, oArea, iDie, iCount, iType, iFortDC, fDelay));
}

void AmbientDamage(object oPC, object oArea)
{
    // only do damage to creatures in the area, exclude DM avatars
    if (GetArea(oPC) != oArea || GetIsDM(oPC))
        return;

    int iDie = GetLocalInt(oArea, "iDie");
    int iCount = GetLocalInt(oArea, "iCount");
    int iType = GetLocalInt(oArea, "iDamageType");
    int iFortDC = GetLocalInt(oArea, "iFortDC");
    float fDelay = GetLocalFloat(oArea, "fHB_Time");

    // if no damage type or amount is set, exit
    if (iDie == 0 || iType == 0)
        return;

    // set default values for iCount, iFortDC and iHB_Time if they are unset
    if (iCount <= 0)
        iCount = 1;
    if (iFortDC <= 0)
        iFortDC = 10;
    if (fDelay <= 0.5)
        fDelay = 10.0;

    DoDamage(oPC, oArea, iDie, iCount, iType, iFortDC, fDelay);


}


Ok so you notice you have quite a bit of variables here. I'm not going over this function line by line there are other tutorials for that. Let's take a look at the area script:

Code: Select all

#include "24areaevents"
#include "mer_amb_damage"

void main()
{
    object oPC = GetEnteringObject();
    object oArea = GetArea(oPC);
    int iTimeStopCastings = GetLocalInt(oArea, "TIME_STOP_ACTIVE");

    // Set it so monsters use spellhooks too
    SetLocalInt(oArea, "X2_L_WILD_MAGIC", TRUE);

    DelayCommand(0.4, AmbientDamage(oPC, oArea));

    if (iTimeStopCastings > 0)
    {
        int iTimeStopCastTime = GetLocalInt(oArea, "TIME_STOP_CAST");
        float iTimeStopDuration = IntToFloat((GetTimeHour()*60 +
                    GetTimeMinute())*60 + GetTimeSecond() - iTimeStopCastTime) + 9.75;
        effect eTime = EffectCutsceneParalyze();
        DelayCommand(0.75, ApplyEffectToObject(DURATION_TYPE_TEMPORARY,
                                        eTime, oPC, iTimeStopDuration));
    }

    if (GetIsPC(oPC) && !GetIsDM(oPC) && !GetIsDMPossessed(oPC))
    {
        SetLocalObject(oPC, "CURRENTAREA", oArea);
        SetLocalInt(oArea, "PCINAREA", GetLocalInt(oArea, "PCINAREA") + 1);
        DelayCommand(0.5,ExecuteScript("24subraceenter", oPC));
    }

    // Send area description, if one exists
    if (GetIsPC(oPC))
    {
        string sDescription = GetLocalString(oArea, "sDescription");
        if (sDescription != "")
        {
            string sTag = GetTag(oArea) + "_d";
            // only send PC description once per server restart
            // so they don't get too annoyed
            if (!GetLocalInt(oPC, sTag))
            {
                DelayCommand(5.0, FloatingTextStringOnCreature(sDescription, oPC, FALSE));
                SetLocalInt(oPC, sTag, TRUE);
            }
        }
    }

    // if the area is set to make snow drifts, so do
    if (GetLocalInt(oArea, "MakeSnow"))
        ExecuteScript("mer_makesnow", oArea);

    SetAreaEventType(EVENT_AREA_ONENTER);
    DelayCommand(0.1, ExecuteScript(GetTag(oArea), oArea));

    int count;
    int iNumExecuteScripts = GetLocalInt(oArea, "ONENTER_EXECUTE");
    for (count = 1; count <= iNumExecuteScripts; count++)
    {
        ExecuteScript(GetLocalString(oArea,
                "ONENTER_EXECUTE_" + IntToString(count)), oArea);
    }
}
Ok on the second line of this script the area on enter script you see: #include "mer_amb_damage" So this is the script the function above is used.

And on the 13th line of the script you see the function in use: DelayCommand(0.4, AmbientDamage(oPC, oArea));

We still have the question where are you getting the variables. Well the script extracts the variables from the areas themselves.

Now open up your area properties box. Once you get your area properties box go to the advanced tab. From there at the bottom of the list is the word variables with a button beside it. If you hit that button you a box will appear with two separation lines. it will have NAME in the first section-- TYPE in the second-- VALUE in the third. This is where you plug in the variables for the script to work. I'm using the variables from an area in Harvest Moon:

fHB_Time -----------------Float ------------ 20
iCount -------------------- Int----------------- 2
iDamageType ------------ Int----------------32 (Cold damage taken from 2da format)
iDie ----------------------- Int------------------6
iFortDC ------------------ Int------------------25
NoRest ------------------- Int--------------------1
NoRestMessage---------- String-------------- It is to cold to rest you need to find Shelter

So if you plug in all these variables - the NoRest and NoRestMessage being optional and some others you have a functioning script that can do any iDamageType by 2da reference in the amount of dice that you want on any area and can change it up by putting different integers on different areas. One script thats all you need when you use the variable boxes on areas. You can also make scripts for peaceables to do multiple tasks using this basic layout.
CTP team member
http://www.harvestmoonconsortium.com
Chief cook and bottle washer for Harvest Moon

Post Reply