Aller au contenu

Photo

My first multi-event script - variable defined without type


  • Veuillez vous connecter pour répondre
29 réponses à ce sujet

#1
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
Hiya,

Most of my scripting experiments so far have been successful with single-event-handling scripts. But this won't work for big areas, so I've tried compiling 2 events into a single script for the first time this morning. As I expected, it didn't work first time. :) So I was wondering if anyone could explain to me why not.

---------------------------------------------------------
#include "plot_h"
#include "utility_h"
#include "wrappers_h"

#include "plt_sandbox_retrieve_sword"

void main()
{
    int bEventHandled;
   
    event evCurrent = GetCurrentEvent();
    int EventType = GetEventType(evCurrent);
    int nPlotFlag = GetEventInteger(evCurrent, 1);
   
    object oHero = GetHero();
   
    // Handle specific events

    switch(nEventType)
    {
        case EVENT_TYPE_CAMPAIGN_ITEM_ACQUIRED:
        {
            string sItemTag = GetTag(GetEventObject(evCurrent, 0));
               
            if(sItemTag == "gen_im_wep_mel_lsw_fam")
            {
                // Set the plot flag and update the journal
                WR_SetPlotFlag(PLT_SANDBOX_RETRIEVE_SWORD, SWORD_IN_INVENTORY, TRUE);
               
                bEventHandled = TRUE;
            }
            break;
        }
    }
   
    switch(nPlotFlag)
    {
        case QUEST_COMPLETED:
        {
            // Remove the sword from the inventory
            RemoveItemsByTag(oHero, "gen_im_wep_mel_lsw_fam");
           
            // Reward the hero with 100 coppers
            AddCreatureMoney (100, oHero);
           
            break;
        }
    }
   
    if(!bEventHandled)
    {
        HandleEvent(evCurrent, RESOURCE_SCRIPT_MODULE_CORE);
    }
}
---------------------------------------------------------

The error occurs on the line starting 'switch(nEventType)' - variable defined without type. So I assume I've not pointed to something which I should have done, but AFAIK I have followed the same conventions as the script I was copying from and I'm not yet script-savvy enough to identify what's wrong/missing.

Just FYI, the script is intended to recognise when an item enters the player inventory and raise the appropriate plot flag.

Thanks a lot!

Modifié par BlackSheep42, 08 août 2012 - 11:58 .


#2
Karma

Karma
  • Members
  • 391 messages
I believe EVENT_TYPE_CAMPAIGN_ITEM_ACQUIRED is a module event. That needs to go in your module script, not in a plot script. Also QUEST_COMPLETED belongs as a main flag (up above, where you put the campaign item event). You put it in the Defined Flag section. Defined flags can't do anything. They can only return TRUE or FALSE.

#3
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
Dropping the ITEM_ACQUIRED event into the module script generates the same error message. Moving the QUEST_COMPLETED node to an nEventType and saving it as a standalone single-event script file makes it compile correctly. I need the first one to work in order to make the second one accessible, however...

#4
Karma

Karma
  • Members
  • 391 messages
Change "int EventType = GetEventType(evCurrent);" to "int nEventType = GetEventType(evCurrent);"

Modifié par satans_karma, 08 août 2012 - 04:32 .


#5
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
It actually is nEvent in the toolset, I guess I must have randomly cut that out during the copy+paste process. That's not what's stopping it from compiling anyway; when posted in the module script the error now appears on the line starting 'string sIteamTag'.

This is the bit of the process that tutorials can't help with. I can reproduce any script in a tutorial, but until I understand the conventions of script construction and the logic with which the system reads the script, I won't be able to write my own. :) I might focus on the non-scripting parts of my module until I've found a site somewhere that can teach me what I need to know for now.

#6
Karma

Karma
  • Members
  • 391 messages
EDIT: I just tried compiling what you had (with the plot parts commented out), and it compiled for me.

Modifié par satans_karma, 08 août 2012 - 06:39 .


#7
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
Oh yeah, individually they both worked for me too, when there's nothing else in the script. It's when I try and add stuff in that I don't quite understand how it works yet. I'll have to crack it at some point though, else my modules will be pretty light on plot!

#8
Karma

Karma
  • Members
  • 391 messages
These are the most commonly needed scripts:
  • One and only one module script
This is set up by going to Manage Modules --> Properties --> Script. It handles a variety of different events. The list is found here. (If you are extending the single player game, you should not have it call module_core at the end.)

  • One plot script for each plot
This is set up in the "Script" option of the plot file. There really aren't many plot events - just the top one for the main flags and the bottom one for the defined flags. The cases will be the names of your flags.

  • One script for each special area
This is not strictly necessary unless you want something special to happen when you enter or exit (e.g. play a cutscene, save the game, start a conversation with a follower, trigger a plot when a team of monsters are killed, activate/deactivate certain items/creatures/placeables in that area depending on plot conditions, etc.). If nothing special happens in the area, then you can leave the script set to area_core. The list of area events is found here.

  • One trigger script for each special trigger
The generic trigger scripts work for most triggers you would probably use, but there may be a few cases where you want a trigger to do something more than what the generic ones can do. This is set up in the "Script" option of the trigger file. The list of trigger events is found here.

  • One placeable script for each special placeable
The generic placeable script works for most placeables you would use, but just like triggers, there may be a few cases where you want a placeable to do something more than what the placeable_core script can do. This is set up in the "Script" option of the placeable file. The list of placeable events can be found here.

  • Several PRCSCR scripts
If you plan to extend the original campaign, you will probably have to add various things (creatures, placeables, merchants, etc.) into an existing area. Since it is unwise to overwrite the area file itself to add these things in, PRCSCR scripts should be used instead. More information on these types of scripts and how to trigger them can be found here. Templates for this type of script can be found here.

There are other types of scripts you may find you need. The list of events for the different kinds of scripts can be found here.

EDIT: Added in information on PRCSCR for posterity.

Modifié par satans_karma, 11 août 2012 - 05:07 .

  • DarthGizka et sapphim aiment ceci

#9
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
Very useful post, thank you.

I have successfully integrated the CAMPAIGN_ITEM_ACQUIRED event into the core module script, where it is sitting happily alongside my scripts for setting the party picker, etc. It compiles fine and works correctly. It also works in tandem with the area script that includes the script to raise the plot flag. So that little issue's been solved.

Now I have another question. :) When I added another #include plot script to the core module script, it refused to compile even before I'd added any events. Simply removing the #include line again made it compile correctly. I therefore assume that the module script doesn't like having more than one plot included.

So my question is, what's the best way to include CAMPAIGN_ITEM_ACQUIRED events for multiple plots? I've taken a look at the official campaign and there's a line in the core module script that commands another script to fire whenever a campaign item is acquired, and that script then deals with all of the events with regard to plot flags. Is that the best way of doing it for my own module?

This is how it looks in the official campaign module script:-

case EVENT_TYPE_CAMPAIGN_ITEM_ACQUIRED:
{
HandleEvent(ev, RESOURCE_SCRIPT_MODULE_ITEM_ACQUIRED);
bEventHandled = TRUE;
break;
}

However, the script used to handle these events is called 'sp_module_item_acq'. What would be the correct command line to use in this case? If it's the same line as used in the official script, how will the game know which custom script to point to?

(Hope that question's clear)

#10
Karma

Karma
  • Members
  • 391 messages

BlackSheep42 wrote...

Now I have another question. :) When I added another #include plot script to the core module script, it refused to compile even before I'd added any events. Simply removing the #include line again made it compile correctly. I therefore assume that the module script doesn't like having more than one plot included.

You can have as many includes as you want (though you should minimize the number because, as the name implies, the entirety of the included script is included in your main script). I *believe* that you do not need to include scripts that were already included in an include (if that makes any sense). The only time I've gotten errors in compiling on the include line was because I didn't format it correctly or I accidentally duplicated the include. You should post the part of the script kicking up the errors.

BlackSheep42 wrote...

...what's the best way to include CAMPAIGN_ITEM_ACQUIRED events for multiple plots?

If you have tons and tons of campaign items, then you may want to consider doing it in another script like the official campaign does. However, if there are only a few, you could do it in your module script (that's what I do).

I'm copying and pasting my CAMPAIGN_ITEM_ACQUIRED event down below.
_________________________________________________________________
        case EVENT_TYPE_CAMPAIGN_ITEM_ACQUIRED:
        {
            object oAcquirer = GetEventCreator(ev);
            object oItemAcquired = GetEventObject(ev, 0);

            if (GetTag(oItemAcquired) == "kc_dragonspeak_breastplate")
            {
                DisplayFloatyMessage(oAcquirer, "Breastplate of Found", FLOATY_MESSAGE, 16777215, 1.5);
                WR_SetPlotFlag(PLT_GENPT_QUEST_CULLEN, CULLEN_QUEST_ARMOR_FOUND, TRUE);
            }
            if (GetTag(oItemAcquired) == "kc_aeonar_key_jail1")
            {
                DisplayFloatyMessage(oAcquirer, "Key 1 Found", FLOATY_MESSAGE, 16777215, 1.5);
                WR_SetPlotFlag(PLT_GENPT_QUEST_JOWAN, JOWAN_QUEST_KEY1_FOUND, TRUE);
            }
            if (GetTag(oItemAcquired) == "kc_aeonar_key_jail2")
            {
                DisplayFloatyMessage(oAcquirer, "Key 2 Found", FLOATY_MESSAGE, 16777215, 1.5);
                WR_SetPlotFlag(PLT_GENPT_QUEST_JOWAN, JOWAN_QUEST_KEY2_FOUND, TRUE);
            }
            break;
        }
_________________________________________________________________

The "if(GetTag() == " ")" part will tell the game how to handle the event for that specific item only and will pass the event on for all other items. (I probably should have used if-else instead of just if, but you get the idea.) If this is a standalone mod, you'll have to ask someone else if there is anything else that you'd need to do.


BlackSheep42 wrote...

What would be the correct command line to use in this case? If it's the same line as used in the official script, how will the game know which custom script to point to?

If you do decide that you want to use a separate script (which I would recommend against unless you have very many campaign items), you would write a new script in the same format as sp_module_item_acq and use the same HandleEvent() function in your module script, but you would change "RESOURCE_SCRIPT_MODULE_ITEM_ACQUIRED" to "R"insert_script_name_here.ncs"." (The .ncs *may* be a .nss instead. I'm fairly certain you would use the compiled script, but I could be mistaken.) Just make sure that you handle just your own events and don't inadvertantly override ALL CAMPAIGN_ITEM_ACQUIRED events.

Alternatively, if you wanted to keep your module script tidy and didn't want to deal with the hassle of making sure you don't override the wrong events, you could write a custom function in a separate script that you would list as an include in your module script. (If this is going to be a fairly complex mod, you'll probably need several custom functions at some point anyway, and they can all be put into the same script.)

Modifié par satans_karma, 10 août 2012 - 03:52 .


#11
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
Everything else about the mod is already pretty complex, I just need to write the scripts to make it all work. :) But let's say for now that I just want the core module script to fire CAMPAIGN_ITEM_ACQUIRED events for two separate plots. I've uploaded a comparison screenshot of my work where the error occurs:-

Posted Image

In the image on the left, I have typed in the #include for my other plot file. You can see the error generated at the bottom of the image, which appears when I add just the #include. Nothing has been added in the main body of the script.

In the image on the right, I have removed that single line from the script and saved again. You can see it has compiled correctly.

#12
MerAnne

MerAnne
  • Members
  • 1 157 messages
It looks like you (because 'sandbox' isn't a DA toolbox prefix) have the same variable/flag (i.e. a duplicate) used in the two plot flag. Doesn't seem logical for that to cause a problem, but that is what the error message is saying. I often have multiple plot files included so just multiple plot files shouldn't cause a problem

#13
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
Ah, of course! *facepalm* That's embarrassingly obvious. Both plots had a generic flag called 'QUEST_COMPLETED', so adding the second #include was trying to add two plot constants with the same name to the library. Even though the module script wasn't associated with a QUEST_COMPLETED flag, the potential for conflict was there, hence the error.

I've added prefixes to both plots flags now (CF for Camp Follower and RTS for the other one) to differentiate them. The script is no longer confused.

D'oh...

Modifié par BlackSheep42, 10 août 2012 - 06:52 .


#14
MerAnne

MerAnne
  • Members
  • 1 157 messages
ahem - you know there is a REASON that I knew what that error message meant so quickly, right? At least I remember the reason that I get error messages ;-)

#15
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
*sigh* Next problem that seemingly defies logic:

#include "plot_h"
#include "utility_h"
#include "wrappers_h"

#include "plt_sandbox_retrieve_sword"
#include "plt_sandbox_camp_follower"

void main()
{
int bEventHandled;

event evCurrent = GetCurrentEvent();
int nEventType = GetEventType(evCurrent);

object oHero = GetHero();

// Handle specific events

switch(nEventType)
{
case RTS_QUEST_COMPLETED:
{
// Remove the sword from the inventory
RemoveItemsByTag(oHero, "sandbox_plot_sword");

break;
}

case CF_QUEST_COMPLETED:
{
RemoveItemsByTag(oHero, "sandbox_amelias_ring");

break;
}
}

if(!bEventHandled)
{
HandleEvent(evCurrent, RESOURCE_SCRIPT_MODULE_CORE);
}
}

This area script compiles correctly, but doesn't work in the game (i.e. the items are not actually removed from the inventory). But that's not the only issue. Whenever I try changing the plot flag RTS_QUEST_COMPLETED to one of the other flags in that plot, e.g. RTS_SWORD_RETURNED, the script suddenly doesn't compile any more.

It raises the following error:
sandbox_area1_main.nss(29): Multiple case constant statements within switch (while compiling var_constants_h.nss)

What does this mean? There were 2 case statements before and it compiled fine. There were 2 case statements after the change and it failed to compile.

1 - Is there any obvious reason why the above script compiles fine in the toolset and then doesn't work in the game? (The item's tag is correct and I have set it to send ITEM_ACQUIRED events in the Variables)

2 - What does the above error mean, and why does it show up for one case constant and not for the other?

#16
MerAnne

MerAnne
  • Members
  • 1 157 messages
I don't think that GetCurrentEvent is going to capture your flags therefore the case statements are never really being called from the switch.  I suggest switching (pardon the pun, ok, maybe not since I'm not that repentent:D) to an if/else if structure.


// Remove the sword from the inventory
if (RTS_QUEST_COMPLETED) RemoveItemsByTag(oHero, "sandbox_plot_sword");
//Remove ring from inventory
else if (CF_QUEST_COMPLETED) RemoveItemsByTag(oHero, "sandbox_amelias_ring");
 
when there is more than on line of code associated with the if/else if, you would have to use the brackets {} to indicate beginning and end.  Or two if statements.  It is only 2 lines so it isn't like there will be an incredible lag because you don't pick the most efficient way to write this.

You'll have to check the syntax since I have written in a tad too many languages, I don't remember if it is 'else if' or 'if else', but the logic is there so you should be able to figure it out.  case/switch statements are wonderful things, but for one little line of code?  not worth the effort (to me) to figure out how to get it to work.

I also suggest learning how to create log files or use DisplayFloaty.  I keep putting off the log files but I use DisplayFloaty with a vengenance! Some method of knowing what the program is actually doing is crucial in figuring out bugs.

#17
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
I'm going to look back on this thread in a few months and laugh at my own innocent incomprehension. But right now... bleurgh.

The example you give there, that wouldn't work because the two events are associated with different plot files. An 'else if' statement would only be applicble if two events were part of the same case constant.

Modifié par BlackSheep42, 11 août 2012 - 12:12 .


#18
MerAnne

MerAnne
  • Members
  • 1 157 messages
Then two separate 'if' statements. The case statement wouldn't have worked because the 'break' would have caused it to jump out of the switch/case statement causing the second case statement to never be reached. At least as I remember the logic of the switch/case/break and if it is the same in the toolset.

Don't forget the WR_GetPlotFlag(your_plot_file_name, your_flag_name) to get the value of the flag.

Nah, you won't even look back. You'll have bigger and more complex coding problems to deal with ;-)

#19
Karma

Karma
  • Members
  • 391 messages
There is a more fundamental problem with this script than cases vs. ifs. You said this is an area script, but this is not using the proper area events. You appear to be mixing scripts again. This time it looks like you're mixing plot scripts with module scripts. Different types of scripts have different templates.

An area script looks like this:
_______________________________________________________________
#include "log_h"
#include "utility_h"
#include "wrappers_h"
#include "events_h"
#include "2da_constants_h"

void main()
{
event ev = GetCurrentEvent();
int nEventType = GetEventType(ev);
string sDebug;
object oPC = GetHero();
object oParty = GetParty(oPC);
int nEventHandled = FALSE;

switch(nEventType)
{
///////////////////////////////////////////////////////////////////////
// Sent by: The engine
// When: it is for playing things like cutscenes and movies when
// you enter an area, things that do not involve AI or actual game play
////////////////////////////////////////////////////////////////////////
case EVENT_TYPE_AREALOAD_SPECIAL:
{
break;
}
///////////////////////////////////////////////////////////////////////
// Sent by: The engine
// When: for things you want to happen while the load screen is still up,
// things like moving creatures around
////////////////////////////////////////////////////////////////////////
case EVENT_TYPE_AREALOAD_PRELOADEXIT:
{
break;
}
////////////////////////////////////////////////////////////////////////
// Sent by: The engine
// When: fires at the same time that the load screen is going away,
// and can be used for things that you want to make sure the player sees.
////////////////////////////////////////////////////////////////////////
case EVENT_TYPE_AREALOAD_POSTLOADEXIT:
{
break;
}
////////////////////////////////////////////////////////////////////////
// Sent by: The engine
// When: A creature enters the area
////////////////////////////////////////////////////////////////////////
case EVENT_TYPE_ENTER:
{
object oCreature = GetEventCreator(ev);

break;
}
////////////////////////////////////////////////////////////////////////
// Sent by: The engine
// When: A creature exits the area
////////////////////////////////////////////////////////////////////////
case EVENT_TYPE_EXIT:
{
object oCreature = GetEventCreator(ev);

break;
}
}
if (!nEventHandled)
{
HandleEvent(ev, RESOURCE_SCRIPT_AREA_CORE);
}
}
_______________________________________________________________

If you open up a blank script, you can have the toolset insert blank templates for the several different types of scripts I mentioned in a previous post. Next to the green C, you'll see an icon that looks like a piece of paper. If you click on that icon, you'll see a list of script templates. Make sure your scripts conform to those templates. :)

EDIT: The template may not list ALL of the possible event types for that script type. The post I made with the event type lists will tell you what other event types you can add to the corresponding template.

Modifié par satans_karma, 11 août 2012 - 03:35 .


#20
sea-

sea-
  • Members
  • 264 messages
To clarify things further: certain events are only sent to certain types of resources. Your plot updates don't work because the game engine doesn't send the plot update event to the area script, it sends it to the chosen plot script. As a rule, anything that occurs when a plot is updated *needs* to take place inside the plot script, for instance making a character move from X to Y, or rewarding the player with XP and gold (if you don't use the built-in rewards system, which I personally avoid because I find it less flexible).

There are exceptions. When enemies are killed in an area and you need to update a plot, for instance, the logic for that needs to go in the area script, because the event for team death is sent to the area script, not your plot script. Similarly, if you need to update an area's state when the player enters (i.e. spawn an NPC when a quest is in progress) you will need to insert logic into the area script that checks the plot flag state.

Here's a significantly cut-down example from my own mod:

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

switch(nEventType)
{
case EVENT_TYPE_AREALOAD_PRELOADEXIT:
{
//check to make sure player has found info from Olen
if ((WR_GetPlotFlag(PLT_BLOODY_COMPETITION, BLOODY_COMPETITION_OLEN_LETTER_FOUND) == TRUE) ||
(WR_GetPlotFlag(PLT_BLOODY_COMPETITION, BLOODY_COMPETITION_OLEN_INTERROGATED) == TRUE))
{
object oTrigger = UT_GetNearestObjectByTag(oPC, "talk_trigger_generic_jasmine");

UT_TeamAppears(27834); //enables Jasmine & thugs
SetObjectActive(oTrigger, TRUE);
}
break;
}
}
HandleEvent(ev, RESOURCE_SCRIPT_AREA_CORE);
}

This simply makes a talk trigger and a team of characters appear when the player is on the correct quest.

There is one more thing you need to pay attention to. When you update a plot flag in an area script, for example when a team is killed, and you want something to happen, there are two ways of doing it. The first is to simply put the function into your area script after the plot updates, for instance, UT_Talk(oChar, oPC);. But, you can also put this function in the plot script instead, and when you use WR_SetPlotFlag(PLT_PLOT, PLT_FLAG, TRUE);, add a second "true" in there so it looks like WR_SetPlotFlag(PLT_PLOT, PLT_FLAG, TRUE, TRUE); - this tells the game to call the plot script itself. There's no real right or wrong way to do this, but just keep it in mind in case you find your area events that update plot flags not actually triggering the events you want them to.

Between gathering plot updates, and area event updates, that's 99% of the scripting work that goes into scripting a module, unless you are getting involved with editing creature scripts (for custom boss battles etc.), making new abilities, and so on.

You're very close to having figured things out, just keep in mind that it's not so much the end result of an action you need to be concerned with, but how it's triggered - if it's a plot flag that's updated, you need to put your code in its plot script, and if you need to check if something in an area has changed, you need to put the code in the area script.

EDIT: Quick note on switch/case/break: a switch will run all the events underneath, and will check each case to see if it conforms to the logic you specify - a bit like an if statement, but more elegant. Any time you call break;, the switch immediately exits. Therefore, any time you want some code to execute and nothing more, you need to literally break; out of the switch.

An easy example:

switch (nInteger)
{
case 1:
//do code here
break;
case 3:
//do code here
break;
case 9:
//do code here
break;
}

So, this bit will check nInteger for its value, and execute different code if its value is 1, 3 or 9, and then it will leave the switch statement and continue. As a general rule, you always want a break; before the next case - you might even need two or more if you have more complicated logic, like an if/else statement. See here for more details: http://msdn.microsof...a(v=vs.80).aspx

Modifié par sea-, 11 août 2012 - 05:04 .


#21
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
Thanks a lot guys. :-) The pieces are slowly falling into place. Or quickly falling into place, I suppose, since I've technically only been scripting for about a week.

Satans_karma, thanks for highlighting my issue with cross-templating! Single-event scripts have always seemed to work for me but not when I try adding multiple events, and I suspect that might be a big part of why.

Sea-, that's a very helpful post, thank you. Particularly that last edit which explains the logic of switch/case construction. I've been searching for an explanation in simple terms for a while! Do you need to add the case numbers or is that just for illustration purposes? FYI, I will be using a custom GDA for the rewards because my mod will be standalone and that method therefore seems even simpler than writing a cover-all script for plot rewards.

Whilst the scripting has been befuddling me I've been working on other areas of the module, e.g. levels, characters, weapons, etc. It looks like there will be around 40-50 new areas, over 200 new characters and around 30 new weapons and armour items. No doubt this will bring its own questions lol, but I'll cross those bridges when I come to them. The aim is to be finished by Christmas...

Modifié par BlackSheep42, 11 août 2012 - 06:28 .


#22
sea-

sea-
  • Members
  • 264 messages
Those are just for illustrative purposes. If you're checking plot flags, for instance, you would type case PLOT_FLAG: or whatever they're called.

Here's a simple plot script from an early quest of mine, where the player has to rid a tavern of some rowdy mercenaries, and can do so by, among other things, buying them drinks or outright bribing them.

#include "log_h"
#include "utility_h"
#include "wrappers_h"
#include "plot_h"

#include "plt_unwanted_visitors"

int StartingConditional()
{
event eParms = GetCurrentEvent(); // Contains all input parameters
int nType = GetEventType(eParms); // GET or SET call
string strPlot = GetEventString(eParms, 0); // Plot GUID
int nFlag = GetEventInteger(eParms, 1); // The bit flag # being affected
object oParty = GetEventCreator(eParms); // The owner of the plot table for this script
object oConversationOwner = GetEventObject(eParms, 0); // Owner on the conversation, if any
int nPlotType = GetEventInteger(eParms, 5);
int bIsTutorial = GetM2DAInt(TABLE_PLOT_TYPES, "IsTutorial", nPlotType);
int bIsCodex = GetM2DAInt(TABLE_PLOT_TYPES, "IsCodex", nPlotType);
int nResult = FALSE; // used to return value for DEFINED GET events
object oPC = GetHero();

plot_GlobalPlotHandler(eParms); // any global plot operations, including debug info

if(nType == EVENT_TYPE_SET_PLOT) // actions -> normal flags only
{
int nValue = GetEventInteger(eParms, 2); // On SET call, the value about to be written (on a normal SET that should be '1', and on a 'clear' it should be '0')
int nOldValue = GetEventInteger(eParms, 3); // On SET call, the current flag value (can be either 1 or 0 regardless if it's a set or clear event)
// IMPORTANT: The flag value on a SET event is set only AFTER this script finishes running!

switch(nFlag)
{
//takes money from player
case MERCS_BRIBED:
{
if (nValue==1)
{
UT_MoneyTakeFromObject(oPC,0,10,0);
}
break;
}
//takes ale from player
case MERCS_GAVE_ALE:
{
if (nValue==1)
{
UT_RemoveItemFromInventory(R"ale.uti", 2, oPC);
}
break;
}
//convinced mercs to leave - mercs disappear
case MERCS_LEFT:
{
if (nValue==1)
{
if (WR_GetPlotFlag(PLT_UNWANTED_VISITORS, MERCS_ATTACK))
{
UT_TeamExit(1010, TRUE, "at_outskirts_exit");
}
else
{
UT_TeamExit(1010, FALSE, "at_outskirts_exit");
}

RewardXPParty(250);
}
break;
}

//once quest is complete
case QUEST_COMPLETE:
{
if (nValue==1)
{
object oAneza = Party_GetActiveFollowerByTag("aneza");

RewardXPParty(500);
UT_AddItemToInventory(R"gen_im_qck_coating_201.uti", 1);
UT_AddItemToInventory(R"wine.uti", 1);

AdjustFollowerApproval(oAneza, 5, TRUE);

DoAutoSave();
}
break;
}
}
}
else // EVENT_TYPE_GET_PLOT -> defined conditions only
{
switch(nFlag)
{
//defined flag to check for bribe money
case MERCS_MONEY_CHECK:
{
nResult = UT_MoneyCheck(oPC,0,10,0);
break;
}
//defined flag to check for ale
case MERCS_ALE_CHECK:
{
int nAleCount = 2;
int nPlayerAle = UT_CountItemInInventory(R"ale.uti", oPC);

if (nPlayerAle >= nAleCount)
{
nResult = TRUE;
}
break;
}
}
}

plot_OutputDefinedFlag(eParms, nResult);
return nResult;
}


Pay attention to the defined flags at the bottom, where I check if the player has X money in one or an ale item in the other. Defined flags can't execute functions themselves; they only check if something is true or false (nResult). I use UT_MoneyCheck for the money check because it returns true/false if the player has the specified amount of money, but for the ales check, I have to manually set true on nResult if the player has enough, because UT_CountItemInInventory returns the number of items, not true/false.

Higher up in the script, under the main plot flags section, I have to use separate flags to actually remove the money or ales from the player's inventory, because, as I said, defined flags don't execute anything, they just return true/false so you can check more complicated results (such as, say, if the player has collected several items, or if the player has killed a certain number of X enemy type). That's also why, if you load a plot into a conversation, you can't actually set defined flags, only main flags.

Here's the area script which handles whether some NPCs should be there or not after the player deals with them (possibly unnecessary but it's just a failsafe in case they should reappear after exiting, for instance if the player leaves before they do and they never actually exit).

#include "events_h"
#include "wrappers_h"
#include "utility_h"

#include "plt_unwanted_visitors"

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

switch(nEventType)
{
case EVENT_TYPE_AREALOAD_PRELOADEXIT:
{
//checks if mercs should be inactive, makes them disappear if true
if (WR_GetPlotFlag(PLT_UNWANTED_VISITORS, MERCS_LEFT) == TRUE)
{
UT_TeamAppears(1010, FALSE);
}
break;
}
}
HandleEvent(ev, RESOURCE_SCRIPT_AREA_CORE);
}


If you wanted to handle the death of their team, you'd insert something like this into the switch:

case EVENT_TYPE_TEAM_DESTROYED:
{
if (GetEventInteger(ev,0) == 1010)
{
WR_SetPlotFlag(PLT_UNWANTED_VISITORS, MERCS_DEAD, TRUE);
//insert whatever other code you want here, i.e. UT_Talk to make an NPC talk to the player when they're dead
}
break;
}


Also note how I use a lot of team-related commands. In general, when you are dealing with multiple NPCs or objects, it's faster and easier to use team functions, because they handle gathering all the objects into variables for you, and scale (so instead of editing code when you add a new NPC, you just need to make sure you've assigned the right team). Also note that these team commands work on all object types that can have teams, including placeables. This makes it very fast and easy to perform basic actions on large numbers of objects in your levels without having to write a lot of code yourself.

As for rewards scripting... well, I just find that sticking the rewards in the plot script is faster and easier than using a 2DA file, though it's really up to you. Personally I think having an extra file can potentially open more doors for conflicts with the core game resources or other mods, but if it works, it works.

Modifié par sea-, 11 août 2012 - 07:14 .


#23
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
I've spent the last few days deleting all of my scripts and starting the whole thing from scratch, haha. I thought it would be easier than trying to troubleshoot every error.

The good news is that all event scripts are now in the right place (module, area or plot) and 95% of them have compiled correctly. There's actually just one error, in the defined flag section of a plot script. Here is the bit of script that the toolset doesn't seem to like:-

    else EVENT_TYPE_GET_PLOT: // EVENT_TYPE_GET_PLOT -> defined conditions only
    {
        switch(nFlag)
        {
            case SS_SWORD_IN_INVENTORY:
            {
                // If the sword has been acquired, raises defined flag
                if(WR_GetPlotFlag(PLT_SANDBOX_SS_SWORD, SS_SWORD_FOUND))
                {
                    nResult = TRUE;
                }
                
                break;
            }
            
            case SS_PELT_IN_INVENTORY:
            {
                // If the wolf pelt has been acquired, raises defined flag
                if(WR_GetPlotFlag(PLT_SANDBOX_SS_PELT, SS_PELT_FOUND))
                {
                    nResult = TRUE;
                }

                break;
            }
   
        }

        break;
    }

The error tells me "No semicolon after expression", which seems self-explanatory, but I can't identify where said semi-colon is missing. I've compared my script against other scripts that deal with defined flags (Thanks to satans_karma for your downloadable modules) and I seem to have the same semi-colons in the same places.

The section above this, which deals with the main plot flags, compiled fine on its own before I added the defined flag section.

Your help is much appreciated.

Modifié par BlackSheep42, 15 août 2012 - 09:17 .


#24
MerAnne

MerAnne
  • Members
  • 1 157 messages
else EVENT_TYPE_GET_PLOT:


I don't think the syntax for this else statement is correct. I don't think any punctuation is needed. And it should be an 'else if' instead of an 'else'. The 'else' may very well compile, but I wouldn't use it. That is the only place that I seen 'strange' punctuation. Other than that, there is always commenting things out until you find what REALLY causes the error. 'C like' compilers aren't always the most friendly when it comes to identifying the exact location of an error.

You're also using more punctuation than I think is strictly necessary and some indentation would be nice, but that may be an issue with doing the copy/paste from toolset to forum.

#25
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
That statement is taken directly from the script template. I haven't modified that.

(And yes, the indentation was lost in the copy + paste)