Aller au contenu

Photo

Experienced modder, basic scripting issues


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

#1
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
Hello folks,

I've been a 'lurker' on these forums for quite some time and finally thought I should show myself. I'm looking for help.

Firstly, a bit of background info. I've been modding PC games for more than half my life, but I hadn't touched the DA:O toolset until recently. I've previously produced a lot of custom content for NWN, as well as other non-RPGs. So basically, I know what I'm doing. I recently downloaded the Dragon Age toolset and started creating my custom world.

So far I have created several linked areas, added ambient effects and sounds, created fully interactive NPCs with dialogue, added party followers and merchants, created new plots, added cutscenes, used conversations to set plot flags and update the journal/codex, and several other bits and pieces.

There's just one critical part of my modding that has thus far eluded me... the scripting. Now, I have been able to advance the plot of my module through setting flags in conversations, but relying solely on that means I'm missing out on a lot of the fancy stuff that I want to use.

Back when I was modding Neverwinter Nights, I learnt my craft from an in-depth scripting walkthrough that explained how everything linked together and explained the basic structure of the script. From that initial explanation, I was able to pick up the rest quickly and intuitively (I'm a fast learner) and create some great mods. However, I have not been able to find anything like this for DA:O. My attempts at scripting so far have been unsuccessful because I don't understand the basic structure of the commands and the interaction of the different functions and constants.

I'm not a C++ programmer. That said, I didn't have any trouble in understanding the NWN scripting like a second language. All I needed was for someone to give me an overview of the basic construction of a script, and I was away.

Now I really need the same sort of thing for the scripting conventions in Dragon Age. The Wiki hasn't helped me because I'm just mimicking, not understanding. I've done everything else in the toolset to a high standard, and it's frustrating that I can't seem to figure out the scripts.

So, my pleas for assistance boil down into two questions:-
1 - Is there an in-depth scripting tutorial out there that explains the structure of the script and how to use basic conventions and functions?
2 - If not, could someone please help me get to grips with the basics one-to-one? I won't need many lessons and will pick up a lot of stuff intuitively.

Thanks for taking the time to read this. I want to contribute to the community and am 90% ready to release some awesome mods, but this last 10% has counfounded me. Please help me overcome this final hurdle!

Modifié par BlackSheep42, 05 août 2012 - 08:32 .


#2
sea-

sea-
  • Members
  • 264 messages
First, read this:

http://social.biowar...ipting_tutorial

Dragon Age handles most in-game interaction through scripting, and in fact just about everything in the game uses scripts, making the game extremely moddable for those who actually know what they're doing. Scripts can be linked to each other by using the #include declaration at the top of a script. This means that you will gain access to everything in a given script when you include it. You will need to include both your own scripts and BioWare's core scripts quite often to get a script to do what you want.

Events are one of the most fundamental things to learn, and are created by the engine and sent to individual objects. For example, a script assigned to an area will intercept area-related events - by default, this handles all the game functionality. What you need to do to create custom content is intercept events and then write functions that perform various actions.

An easy example is checking if enemies are killed in an area. Let's take this:

#include "events_h"
#include "plt_clear_The_hut"
#include "wrappers_h"

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

switch(nEventType)
{
case EVENT_TYPE_TEAM_DESTROYED:
{
if(GetEventInteger(ev,0) == 1)
{
WR_SetPlotFlag(PLT_CLEAR_THE_HUT, MONSTERS_SLAIN, TRUE);
}
break;
}
}
HandleEvent(ev, RESOURCE_SCRIPT_AREA_CORE);
}

This is directly from the DA Builder Wiki.

The three #includes at the top tell the game to include the events_h and wrappers_h scripts (containing more constants, functions, etc.) and plt_clear_The_hut is a plot file, where the plot flag names are used as constants, allowing you to update them in a script rather than a conversation.

void main() is C syntax which says that a function does not want to receive any arguments from any other outside script (additional info, like specific variables). This is what you will use most often, except for plot scripts, where you will want to use void StartingConditional() instead. In C and C++, there are a lot more things you can do (see http://faq.cprogramm...swer=1044841143) but for Toolset scripting purposes, it'll be one of these two in most cases. Just remember, when in doubt, look at BioWare's own scripts to see how they did something.

switch(nEventType) runs through all events intercepted - the nEventType, as you can see in the variable declarations above, gets the event type for the current event.

case EVENT_TYPE_TEAM_DESTROYED checks specifically for the team destroyed event, which is sent by the engine to the current area script every time a team is killed (creatures, in 99% of cases, but theoretically it can work for placeables too).

if(GetEventInteger(ev,0) == 1) is an if statement checking to see whether the relevant plot flag is being updated properly - in this case, we're checking if it's 1 and only executing then (so if the plot flag is set to 0, it won't trigger the same code). Usually this check isn't really necessary and is just done for safety's sake.

WR_SetPlotFlag(PLT_CLEAR_THE_HUT, MONSTERS_SLAIN, TRUE); is a function included in wrappers_h, and lets you update a given plot flag to true or false. Note that you can also add another true or false at the end to call the script associated with that plot (so if you want, say, an NPC to talk to the player after the player kills some enemies, you can handle that in the plot script rather than the area script).

The break; statement tells the game to exit from a switch() statement. If you have a switch, you will always need to use a break, usually under each case you have, otherwise the script will go on to perform other actions you probably don't want.

HandleEvent(ev, RESOURCE_SCRIPT_AREA_CORE); is standard code that you will want to use in most scripts, but the script you specify (here shown as a constant) will change based on application. This function effectively tells the game to "handle" any other events by passing them to the area_core script that contains all the default code the game needs to make areas work, as it were. In other words, anything "standard" that you need to do should be passed to the applicable core script; anything custom you want to do needs to be written in your own script.

I cannot overstate the importance of learning syntax. C is not that hard, but if you approach it purely from a scripting perspective you will learn much more slowly. I highly recommend doing just a bit of reading on C syntax, because it will help you immensely in understanding how the game works and how you can do your own scripting to achieve your goals. There are a lot of things you can do that many beginners pass up, even stuff like use of arrays and loops, which the Builder Wiki doesn't really cover but can be indispensable. For instance:

object[] oStatues = GetTeam(100, OBJECT_TYPE_CREATURE);
int nSize = GetArraySize(oStatues);
int index;

for (index = 0; index < nSize; index++)
{
SetCreatureIsStatue(oStatues[index], TRUE);
}

vs.

object oPC = GetHero();

object oStatue1 = UT_GetNearestObjectByTag(oPC, "statue_1");
object oStatue2 = UT_GetNearestObjectByTag(oPC, "statue_2");
object oStatue3 = UT_GetNearestObjectByTag(oPC, "statue_3");
object oStatue4 = UT_GetNearestObjectByTag(oPC, "statue_4");
object oStatue5 = UT_GetNearestObjectByTag(oPC, "statue_5");
object oStatue6 = UT_GetNearestObjectByTag(oPC, "statue_6");
object oStatue7 = UT_GetNearestObjectByTag(oPC, "statue_7");

SetCreatureIsStatue(oStatue1, TRUE);
SetCreatureIsStatue(oStatue2, TRUE);
SetCreatureIsStatue(oStatue3, TRUE);
SetCreatureIsStatue(oStatue4, TRUE);
SetCreatureIsStatue(oStatue5, TRUE);
SetCreatureIsStatue(oStatue6, TRUE);
SetCreatureIsStatue(oStatue7, TRUE);

You tell me which one looks quicker, cleaner and more flexible (hint: the first one).

I also recommend you look at BioWare's own scripts. Find a simple plot in the main campaign module, like for one of the random encounters, and then check its associated script to see how BioWare handled it. The vast majority of things you will be doing script-wise will be related to plot scripts, because that's where most of the gameplay/story stuff happens, and thankfully there are template scripts available (like plot_h, if I recall) that you can simply modify to your own needs.

Modifié par sea-, 04 août 2012 - 03:49 .


#3
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
I have put this script in my module. The plot is called "sandbox_questgiver1" and one of the flags in the quest is called "AREA_CLEAR". I created the following script and set it as the script for the ara "mill_interior".

#include "events_h"
#include "plt_sandbox_questgiver1"
#include "wrappers_h"

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

switch(nEventType)
{
case EVENT_TYPE_TEAM_DESTROYED:
{
if(GetEventInteger(ev,0) == 1)
{
WR_SetPlotFlag(PLT_SANDBOX_QUESTGIVER1, AREA_CLEAR, TRUE);
}
break;
}
}
HandleEvent(ev, RESOURCE_SCRIPT_AREA_CORE);
}

When the team of darkspawn in the mill are destroyed, nothing happens. The journal doesn't update like I've programmed it to, and the questgiver doesn't recognise that the area is clear. I've followed the Wiki example to the letter and it doesn't work. How the heck am I supposed to learn from a tutorial when the tutorial example doesn't work?

#4
Karma

Karma
  • Members
  • 391 messages
Has the "team" option of all the monsters in the area been set to 1?

#5
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
Ugh... The team option of the monsters was set to 1 in the Object Inspector, but when I opened the resource to double-check, it was still set to -1. Needless to say, once it was changed in the resourse itself, it all worked. *facepalm*

Since getting this to work, I have written 48 working event scripts and a few GDAs for good measure. Like I said, I'm a fast learner. :) My next step is as sea- said... I need to understand the conventions of the language to be able to link events together and make my scripts more efficient.

For example, I want to compile a script that includes several different events, rather than creating a new nss file every time I want to initiate a new event. Looking through the official campaign scripts still leaves me scratching my head a bit and I haven't been able to find a GENERAL overview of logic within scripting. The specific examples are all well and good, but if there's a basic C++ conventions tutorial out there I'm sure it would help. :)

Also, something I picked up from reading online tutorials is the incredibly useful UT_AddItemToInventory function, but this doesn't appear to be listed in the toolset's core scripting functions. It still works fine when I add it in though. What's up with that?

#6
Karma

Karma
  • Members
  • 391 messages
You may want to check out the EVENT_TYPE_* constants either by turning on the Help window in the toolset or using the dalexicon.net website. The website will allow you to search for the different types of events, so you know what you have available.

You can definitely combine several event scripts into fewer scripts, but you can't use all types of events in all types of scripts. The events I use most frequently are module events, plot events, and area events. Plots are especially useful for combining a bunch of scripts into one. Each main plot flag can run a bit of script when it's set. Defined flags basically just listen for things to happen and return "yes, it's happened" or "no, it hasn't." (It's good for setting up conditions in dialogue.)

#7
sea-

sea-
  • Members
  • 264 messages

For example, I want to compile a script that includes several different events, rather than creating a new nss file every time I want to initiate a new event. Looking through the official campaign scripts still leaves me scratching my head a bit and I haven't been able to find a GENERAL overview of logic within scripting. The specific examples are all well and good, but if there's a basic C++ conventions tutorial out there I'm sure it would help. :)

You're not programming; C is just the syntax used for scripting. Therefore programming guides on how the language itself works may not be helpful beyond syntax itself. The best is to simply learn how BioWare does stuff that you want to do yourself. Going beyond the scenarios etc. they made is important, but not really necessary if your goal is to build good gameplay.

Also, something I picked up from reading online tutorials is the incredibly useful UT_AddItemToInventory function, but this doesn't appear to be listed in the toolset's core scripting functions. It still works fine when I add it in though. What's up with that?

It's included in utilities_h. If you have utilities_h included at the start of a script, you can use the function.

#8
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
Ah, I see. :) satans_karma, I've actually been looking at a lot of the tutorial modules you created to get an idea of the structure of a script... yours are good for that IMHO because everything is neatly set out in sections and they're easier to dissect than the official campaing scripts.

At some point I'd also be interested in talking to you about advanced follower options, e.g. initial tactics setup and voicesets for new companions.

One question for today: If I am using a custom rewards.GDA file for my module, will its mere presence in the override folder ensure that it is used by the module, or do I need to point to it specifically in my module script? If so, what's the line I'd need?

Thanks for your replies, both of you, by the way. So far my forays into scripting have been modestly successful.

#9
Karma

Karma
  • Members
  • 391 messages

BlackSheep42 wrote...

At some point I'd also be interested in talking to you about advanced follower options, e.g. initial tactics setup and voicesets for new companions.


Sure, no problem. Just drop me a PM whenever you're ready for it.

BlackSheep42 wrote...

One question for today: If I am using a custom rewards.GDA file for my module, will its mere presence in the override folder ensure that it is used by the module, or do I need to point to it specifically in my module script? If so, what's the line I'd need?


Your rewards.gda should be called something like rewards_xxx.gda. I put my rewards_xxx.gda in  ...\\addins\\yourmodule\\module\\override. You can point to it in your plot files. There's a "Reward" column for it. Just use the drop down to find your custom reward from the list. (I think there's some weird bug with the XP reward though. IIRC it doesn't give the amount you specify.)

#10
BlackSheep42

BlackSheep42
  • Members
  • 26 messages
No, it gives ~50% of the specified value, but that's fine... I'll just enter double what I want my character to receive. I'm creating a standalone module so thought it would be easiest to deal with quest rewards through the plot file rather than scripting, I just wondered whether I had to make the game aware that I was doing that or whether it would pick it up automatically. Nice to know it takes the easiest option on this occasion. :)