🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Developing the ideal scripting language feature set

Started by
37 comments, last by Ravyne 20 years, 1 month ago
I think it OO features are very useful, a non programmer may not, so as long as it is not required, whats the harm of having the features for those who want them?

BTW, my new favourite scripting language is Squirrel
Its a mixture between Python and Lua and has many of the features described above. I used to use Lua, then a lot of Python, and now I only use Squirrel for scripting!

[edited by - issch on May 20, 2004 6:53:35 AM]
Advertisement
I totally disagree with you Raduprv. The concept of having a layer of more abstract (and thus easier for non-pogrammers) functions is not flawed because of that. I could just as easily have written:

Remember player entering area, as ThePlayer
Tell ThePlayer, "Hi!"
Wait for 5 seconds
Tell ThePlayer, "Welcome to my humble cottage."

"Tell" is one of those functions that acts like giving an order to the NPC like Kylotan said. It will check if the action is possible first, like checking if the player left the house while the NPC was "Wait"ing, or if the player died. Don''t tell me that it doesn''t look intuitive for a non-programmer - congratulations, you just increased your scripters/modders population about 1000%

Hmm I don''t see an obvious use for a lot of those suggestions. Maybe I''m wrong, but I think that the game should provide the objects and classes, and the scripter just plays around with them. For my game, I have a generic "object" class that applies to everything - from an item, to an NPC or monster, or even a player. Most of those things would be kinda useless ;p It has everything it needs and for a simple object it still manages to stay pretty small So, I don''t see a need to have an "apple" and an "orange" classes that inherit from a "fruit" class, which in turn inherits from the "item" class... It''s so much easier to just let each object have a list of attached functions (for the events like OnHit and OnKill) and variables (properties). Maybe someone here can convince me otherwise
Well, yeah, having an english like scripting language can be an advantage for a beginner, but then again, implementing a very high level scripting language is very complicated (for the one that implements it), and no matter what you say, there is less flexibility. With such a method, for example, you effectively cut off any actions between "enter house" and "welcome here". That might not be desirable. Maybe the I want to add a chance for the player to fail entering the house. This particular example is not obvious, but still, there is a problem.
Encouraging mediocrity is not good. You get better results if the scripter spends a few days actually learning the basics of programming, rather than allow anyone to write scripts within 5 minutes.
quote: Original post by Jotaf
Remember player entering area, as ThePlayer
Tell ThePlayer, "Hi!"
Wait for 5 seconds
Tell ThePlayer, "Welcome to my humble cottage."

Who''s doing the telling? Somebody in the cottage, I assume.

Two points spring to mind. If the owner of the cottage is out, or if he''s been killed or rendered otherwise incapable of speech, or you''re invisible, or whatever, then this shouldn''t happen when you ''enter the area''.

Raduprv has the right idea. Although, the logic presented above works just fine, although the syntax obviously sucks. But I hope that was just pseudocode, anyway.

Firstly, we tell the owner to say "Hi!" to the player. The key point here is that the instruction isn''t "make the game say hi", the instruction is "make the owner say hi". The owner will walk to within earshot of the player, and speak his part. If a sound is associated with the speach, it will be used, and any lip-syncing effects will be played out.

Secondly, he waits 5 seconds. We can only wonder why he waits for 5 seconds before saying anything. More sensible would be for him to wait until you enter his home.

Then, we again tell the owner to say something to you. As before, he''ll move to within earshot. A question here is what does he do if you''ve moved away. Logic dictates he doesn''t follow you to the other side of the world -- you wouldn''t be in his home any more.

Real people don''t follow a simple linear script. They try to follow several scripts at once, and prioritise them when they are incompatible.

For example, suppose a man and woman are living in the cottage. They''re married. The ''directives'' for an NPC include several about what to do when something happens to your loved ones. They also include directives about how to treat strangers, people you know, injured people, people who are attacking you, people who have attacked your enemies, etc.

You walk up to the cottage. The owners are outside as you approach. You stand there for a few seconds, and one of them says "hi." You tip your hat in return, and they invite you in for a cup of tea.

You stagger to the cottage, dripping blood. The owners are outside as you approach. The moment they see you, one of them runs into the house and emerges with a stimpack (or healing scroll or whatever).

You run past the owners into the cottage. They run in and attempt to manhandle you out.

You run up to the cottage and start beating the owners. They run in, bolt the doors, close the windows and snipe at you.

These are complicated behavioural patterns. Using a linear script, this would be very difficult. OOP could only help to a limited degree. Using a priority-based system, this would still be difficult, but not as difficult. It also makes it easier to introduce new behavioural patterns.
quote:
Hmm I don''t see an obvious use for a lot of those suggestions. Maybe I''m wrong, but I think that the game should provide the objects and classes, and the scripter just plays around with them. For my game, I have a generic "object" class that applies to everything - from an item, to an NPC or monster, or even a player. Most of those things would be kinda useless ;p It has everything it needs and for a simple object it still manages to stay pretty small So, I don''t see a need to have an "apple" and an "orange" classes that inherit from a "fruit" class, which in turn inherits from the "item" class... It''s so much easier to just let each object have a list of attached functions (for the events like OnHit and OnKill) and variables (properties). Maybe someone here can convince me otherwise

The obvious problem with your design is that, if you can''t add new classes within the scripting language, useful modding becomes impossible -- you can''t easily add new weapons, new inventory items, new monsters, or whatever.

The scripting language is limited to literally that -- scripting.

Whilst pure scripting is useful, for modding to take off, the scripting language must make it as easy as possible for a novice modder to make mods that can coexist with other mods. For example, add a weapon with one mod, a creature with another, a spell with another.

OOP is the only paradigm that I am aware of that could make that kind of integration possible. And it''s obviously only possible if the full feature-set of OOP is exposed to the scripting language -- including the ability to create new classes.

Raduprv says that programmers should be required to learn how to program before writing programs. I agree.

Anyone who''s created levels in e.g. DOOM, Quake or Abuse will recall how it is possible to construct complicated behaviour that appears ''scripted'', without actually writing programs; you instead plug components together. I suppose most other engines provide similar functionality.

The point I''m attempting to make here is that a programming language is not necessarily the best approach for ''scripting'' a game. A lot can be done by fitting together ''components''.

A secondary point is that, in my opinion, this kind of ''codeless script'' is actually OOP. It''s a kind of object-oriented flowchart.

So what do we end up with? We have four different concepts which we''re attempting to combine into one.

We have a priority based ''directive script'' which is almost an AI system.

We have a linear ''event script'' which is simply a series of commands. This is a true ''script''.

We have a linear ''component graph'' which is similar to an event script, but can be built interactively over the top of the game world.

We have a structured ''mod script'' which is, effectively, a full programming language.

Most games make some of these distinctions. Quake 3 can be modded in C, and uses a component graph for doing stuff like doors, setting patrol routes, and the like. Unreal and Abuse are similar, but using UnrealScript and Lisp instead, respectively.

FreeSpace has priority directives, and uses a LISP-style language for event scripts. Now that the source is available, you can mod it, but there''s no builtin modding feature. The use of priority directives makes complicated tasks easy to program. You can instruct a group of ships to perform a particular task, but if some of the ships are already engaged in a higher priority task, they won''t, until that task is complete.

This flexibility makes it easier to improve the AI. Suppose a fighter has a priority 90 order to protect a cruiser. Then all fighters are given a priority 70 order to attack Foobar class enemy fighters. So long as the cruiser is not being attacked, the fighter can engage these enemies. If the cruiser is attacked, it must return to defend it. But, a good AI engine could reconcile the two directives and cause the defending fighter to attack those Foobars that are attacking its charge in preference to other attackers. Without priority-based scripts, that kind of smarts would be difficult to introduce later.

Component graphs appear to be the most common form of ''scripting''. Most RTS games have waypoints and triggers to which units are connected.

It can be confusing, however, to schedule delayed actions, and very complicated graphs can be hard to follow. This is why Hexen had a, very simple, scripting language, rather than exclusively using
CoV
Saying that it''s "complicated" to implement is not a valid excuse. If everyone would stick by that we would be years behind in our technology. Anyways yeah that was intended as pseudo-code, but maybe a scripting language with this syntax wouldn''t be that bad Come on guys, why do you keep insisting that everyone *should* learn programming? How the hell did you come up with that dogma? Remember that the reason why Starcraft has such a huge and dedicated community of mapmakers is not just because of the game, it''s also because the trigger system doesn''t look like C++, like Warcraft III''s scripting system does. Almost any Starcraft gamer has tried creating maps with triggers; the same cannot be said about Warcraft III.

You''re worrying so much about the linearity of this script. That''s being nitpicky since you see this all the time in almost every game there is (even the latest RPGs like Final Fantasy and stuff). Anyways if you want to make this NPC behave like a real person would... Let it have an action queue with priorities associated with actions. Instead of actually ordering the NPC to do this stuff right away, it would add these actions to the queue ("Wait" included):

Say "Hi!" - priority 10 (fairly low)
Wait 5 - priority 10
Say "Welcome ..." - priority 10

The NPC has different scripts associated with different events. In addition to this script there''s also, for example, another one for when the player goes hostile. Because of the priorities, some actions take precedence, and you can have a treshold for high-priority actions so that when the player goes hostile the NPC "forgets" about anything below priority 50. If your system is flexible enough to support different threads of scripts, I don''t see how this would not work.

And Mayrel, I made it clear in my last post why I couldn''t see a use for heavy OOP. The example you gave, about creating a weapon, is perfectly possible using my system: create a generic object, give it a weapon name and a weapon image, attach it some scripts so it behaves like a weapon (OnPickUp, OnFire...), and some variables with parameters like damage and speed. Why would you need a set of classes with inheritance and all of that? You''re complicating something that can be much simplier. I''m still waiting for someone to prove me wrong.
quote: Original post by Jotaf
Saying that it''s "complicated" to implement is not a valid excuse. If everyone would stick by that we would be years behind in our technology.

Well, actually, we are years behind.
quote:
Anyways yeah that was intended as pseudo-code, but maybe a scripting language with this syntax wouldn''t be that bad Come on guys, why do you keep insisting that everyone *should* learn programming? How the hell did you come up with that dogma?

Firstly, whether your script looks like English or not, it''s still a program. You may be seeing ''learn to program'' and thinking it means ''learn to program in a C-style language''. What it should be taken as is something like ''learn to translate a high-level conceptual description of desired behaviour into an actual software system''.

For complicated logic, writing programs is far easier than using triggers, or a similar system. Therefore, learning to program, although difficult in the short term, will make it easier to construct complicated. This is not to say that trigger-based systems are a bad idea. They have significant benefits when used appropriately.

Overall, the actual dogma is ''use appropriate tools for appropriate tasks''. Sometimes programming languages are the most appropriate tools.
quote:
Remember that the reason why Starcraft has such a huge and dedicated community of mapmakers is not just because of the game, it''s also because the trigger system doesn''t look like C++, like Warcraft III''s scripting system does. Almost any Starcraft gamer has tried creating maps with triggers; the same cannot be said about Warcraft III.

I''d argue that the game is the primary reason. I''d also argue that triggers are simple to use not because they''re not programming languages, but because they''re more appropriate for the task than programming languages.
quote:
You''re worrying so much about the linearity of this script. That''s being nitpicky since you see this all the time in almost every game there is (even the latest RPGs like Final Fantasy and stuff).

In fairness, Final Fantasy is highly linear.
quote:
Anyways if you want to make this NPC behave like a real person would... The NPC has different scripts associated with different events. ... you can have a treshold for high-priority actions so that when the player goes hostile the NPC "forgets" about anything below priority 50. If your system is flexible enough to support different threads of scripts, I don''t see how this would not work.

Although this is obviously the right thing to do for the most part, I''m not sure about "forgetting" low priority things.

Although some low priority things may be inappropriate for an NPC who is hostile towards you, some may not. Suppose an NPC has a low priority directive to water the flowers in the evening. A real person who was attacked by somebody probably wouldn''t stop his watering flowers, unless he was rendered physically incapable of doing so.

The right way to do this, IMO, is to associate each directive with a description of its context. Some of these context-dependancies may be inherent.

For example, a normal "npc42, heal player" directive may automatically be ignored if the intended target is now an enemy. You could override this with something akin to "npc42, heal player anyway".

For speech, we might not be able to be so automatic. I''d suggest seperating conversations into a seperate component, and making them modal, rather like in Deus Ex or the Infinity Engine. For random acts of speech, you''d have something like "npc42, if player is friend, say ''Welcome to my humble abode.'' to player."

To simplify things you can set a context that must hold throughout a script.

npc42, stopping if player is no longer a friend, do       say "Hi!" to player, then       wait for 5 seconds, then       say "Welcome ..." to player. 


Or, in a slightly more conventional (and easier to parse) language,

trap (npc42.makeEnemy(player)) {  npc42.say("Hi!", player);  npc42.wait(5);  npc42.say("Welcome", player);} 


quote:
And Mayrel, I made it clear in my last post why I couldn''t see a use for heavy OOP. The example you gave, about creating a weapon, is perfectly possible using my system: create a generic object, give it a weapon name and a weapon image, attach it some scripts so it behaves like a weapon (OnPickUp, OnFire...), and some variables with parameters like damage and speed. Why would you need a set of classes with inheritance and all of that? You''re complicating something that can be much simplier. I''m still waiting for someone to prove me wrong.

?

You don''t need a set of classes to define a new weapon. You''d only need one. But how, precisely, would you create a weapon with your system?

Something like this?

guassgun = make_object();gaussgun.name = "Gauss Gun";gaussgun.inv_icon = ''icons/gaussgun'';gaussgun.item_model = ''models/weapons/gaussgun/item'';gaussgun.inv_model = ''models/weapons/gaussgun/inv'';gaussgun.ammo = ''ammo/charge'';gaussgun.on_fire = generic_weapon.on_fire;gaussgun.on_pickup = generic_weapon.on_pickup;// All the other scripts that you don''t know about need to be// defined here.add_inventory_item(player, gaussgun); 


Would this need to be done each time a new gauss gun is created? What object would represent all gauss guns, if there was such an object?

How is the above significantly different from this:

guassgun generic_weapon {  name = "Gauss Gun";  inv_icon = ''icons/gaussgun'';  item_model = ''models/weapons/gaussgun/item'';  inv_model = ''models/weapons/gaussgun/inv'';  ammo = ''ammo/charge'';};player.add_inventory_item(new gaussgun); 


I mean, apart from the fact that the ''non-OOP'' version requires extra typing, doesn''t name an object that represents all instantances of the gauss gun, and doesn''t automatically deal with generic_weapon having extra fields and methods associated with it that must be defined by realisations of the generic_weapon template.
CoV
Years behind relative to what?

About people learning how to program to be able to write scripts: You seem to be missing entirely the concept of "layers". It''s perfectly reasonable to have a layer of easy functions/syntax for dummies, and other layers of increasing complexity, but also increased control. That''s how BioWare did it for their popular Aurora set (for NeverWinter Nights campaigns). And you can see every type of modules for that game, from campaigns with simple scripts yet really well balanced stats, original creatures, engaging stories, done by the non-programmer types, to the complex campaigns done by hard-code programmers/scripters, with items customized to their fullest, and alternate damage systems and monster demographics. You shouldn''t force anyone to learn to deal with pointers and complicated syntax just to display a "hello" over an NPC''s head

quote: Overall, the actual dogma is ''use appropriate tools for appropriate tasks''. Sometimes programming languages are the most appropriate tools.


That''s exactly what I was saying
As for the Starcraft example, ask anyone who tried creating UMSs in Starcraft and trigger-driven maps in Warcraft III.

I think we''re on the right track to create a scripting language compatible with inteligent NPCs Your idea of keeping a context is really good, but it needs some more automation. Checking if what the NPC is going to do is ok at the time should not be handled by the script, or you''re going to end up typing "don''t do this if the target becomes an enemy" everywhere.

Sorry, I forgot something really important when I asked if classes were really necessary: You do this when creating an object type, not an instance of an object. Although it is possible to add more variables and functions as the game progressing (adding fire to an arrow for example). Now that I think about it, it can be seen as a limited form of classing: each object instance inherits the "default" behaviour from its "object type" definition, and then can have its own behaviour on top of the default. Maybe this architecture depends too much on the game... because whatever implementation I can think about for this, it gets the job done, and it''s so easy to use.
quote: Original post by Jotaf
Years behind relative to what?

Almost all programming languages are years behind Common Lisp, for example.
quote:
About people learning how to program to be able to write scripts: You seem to be missing entirely the concept of "layers". It''s perfectly reasonable to have a layer of easy functions/syntax for dummies, and other layers of increasing complexity, but also increased control.

You appear to be suggesting that it''d be okay to have several different scripting languages for a game, each at differing levels of functionality and syntactical complexity. That''s an astonishingly bad idea.

Whilst it''s desirable that modifications that a lot of people will want to make will be simple to make, it''s also desirable that it be easy for people to learn how to make more complicated modifications. You don''t do that by forcing them to learn a new scripting language each time they ''gain a level'' in modding ability.

The right way to do it is to provide high-level functions that provide commonly used functionality, and low-level functions that provide uncommonly used functionality.

It is extremely important that it be easy to combine high-level and low-level functionality in one script. Without this ability, expert developers would be forced to use only low-level functionality, or to split their scripts up. Either way, novice developers would find it much harder to learn to use advanced functionality.
quote:
You shouldn''t force anyone to learn to deal with pointers and complicated syntax just to display a "hello" over an NPC''s head

Well, no. Appropriate tools for appropriate tasks.

display-message-icon NPC "hello"

Although you should probably use something more like:

display-speech NPC ''speech/villager/male/hello''

Then the speech resource can specify text, sound and lipsync animation, and the engine can automagically select the appropriate language version of the resource.
quote:
I think we''re on the right track to create a scripting language compatible with inteligent NPCs Your idea of keeping a context is really good, but it needs some more automation. Checking if what the NPC is going to do is ok at the time should not be handled by the script, or you''re going to end up typing "don''t do this if the target becomes an enemy" everywhere.

The trouble is how do we determine if a particular act isn''t something you do to an enemy, or to a friend?

As a simple example, consider an NPC who has to direct you down a dangerous mountain path. One route leads easily to the bottom, another to a hidden temple where you receive healing, blessings and enchanted weapons, and the final to almost certain death.

If in the course of your adventuring you have greatly angered the NPC, he will lead you to almost certain death. If you have done nothing of note to him, he will lead you easily to the bottom. If you have been especially kind to him, he will lead you to the hidden temple.

Obviously, the choice of path the NPC takes depends upon the NPC''s opinion of you. In this particular instance, the script would have to apply a mutually exclusive context to each path the NPC could take.

Now suppose the NPC only knows one path: the path to the hidden temple. Could we get away with not having to specify context? I don''t see how. The NPC still wouldn''t show you to the temple if you angered him, and you''d still have to specify some kind of rule in the script as to when he''ll show you to the temple and when he won''t.

Observe that we can''t just say the NPC won''t carry out his player-related scripted functions if he doesn''t like the player. What if the NPC knew the path to almost certain death instead of the hidden temple? He''d want to show a player he didn''t like the way.

In the real world, all our actions towards other people are based upon our knowledge of them, or lack thereof. For an NPC to behave reasonably, all the actions of the NPC must be similarly justified.

Sometimes the justification of actions is implied. An NPC will never attack someone he likes unless explicitly told to do so. If an enemy of you comes looking for you, an NPC that has seen you but doesn''t like you would point you out to your enemy. Conversely, if the NPC likes you, he''ll act like you don''t exist.

Sometimes it is beyond the capabilities of AI to dream up a suitable course of action. AI can be arbitrarily complex, of course, depending upon how much CPU time you have going spare. If an NPC knew the location of the temple, knew there were high-level priests and armourers in the temple, and liked you, he might be able to figure out that he should show you to it without having to be told.
quote:
Sorry, I forgot something really important when I asked if classes were really necessary: You do this when creating an object type, not an instance of an object. Although it is possible to add more variables and functions as the game progressing (adding fire to an arrow for example). Now that I think about it, it can be seen as a limited form of classing: each object instance inherits the "default" behaviour from its "object type" definition, and then can have its own behaviour on top of the default. Maybe this architecture depends too much on the game... because whatever implementation I can think about for this, it gets the job done, and it''s so easy to use.

Classes are not object orientation. They are an aspect of a variety of object orientation. You appear to be describing a prototype-based OOP languages, where objects are constructed from other objects, rather than special-purpose class objects.

I''d argue that you''ll want it to be easy to get a listing of the generic ''types of object''. i.e. If you want to put a sword in a map, you''ll want to see a list of the kinds of swords, not a list of all the swords that exist anywhere in the game.

For this reason, any object which is intended to be used as a template for building new objects upon should be marked as such. One easy way to mark such objects is to use the term commonly used in OOP to describe such objects -- ''class''.
CoV
Ok... If lisp is so great, I think you should tell us what''s so good about it (no I''m not being sarcastic) The topic of this thread is "what makes a good scripting language" after all!

When I said "layers", I meant inside the same scripting language. Like different functions and operators that can be used to achieve the same results, but each layer is geared more towards programmers or non-programmers. You misunderstood completely. But yeah it''s like you said later in your post.

With justification for an NPC''s actions, you''re going into the area of planning AI. A simple planning system would give you all the "water the flowers", "go home to have lunch" sort of stuff (it would be especially inteligent if the actions list has priorities too). And it would solve the problem of the NPC that is guiding the player down the hill. There''s a set of actions that the NPC can perform, each one has a list of pre-conditions (the NPC doesn''t execute the action unless these are met) and a list of effects. You can have powerful planning for a game even if each condition or effect is just a boolean value: "pick up item X" requires "item X is carriable" and "I''m near item X", and achieves "carying item X". Whenever the NPC wants to have something in its inventory, whatever it is, it will execute this action. But first the pre-conditions must be met; so if it''s not near the item, it will try to get there (using a "go to X" action that achieves this effect, so the pre-condition is met). There can be multiple ways to achieve the same effect, like different ways to kill the player.

For whatever reason, the NPC wants to get the player down the hill. It choses the action "guide player to place X". There are no relevant pre-conditions so it does that. Meanwhile, the player''s actions might trigger a new condition that must be met: make the player happy or kill the player (depending on what triggered it). It will see that the action "show the temple to the player" has the "player is happy" effect, and "lead the player to certain death" has the "player is dead" effect. See how a simple pre-condition triggers a chain of actions that seems very natural and "human"? A system like this can be much more robust and solve whatever you throw at it. You can code the actions using scripts, but I don''t think that a single script should handle the whole event of leading a player down hill, because it''s just too complex. Leave that for the planning system.

Ok. So you can say that there''s a "longsword" class, and you can create an instance of that class that has all the default behaviour. Then you can add different functions to it so it becomes a "poisoned longsword". This is really simple and straightforward to implement and use. What aspects of OOP would be useful for a structure like this?
What about...
// the player comes to NPC''s house...// 100 = best feeling, 0 = worst, 50 = average-(s)he just met the playerif (npcFeeling(homeowner, player) == 100){        say(homeowner, "Hi %playername%! Nice to see you!");        delay(1000);        say(player, "Hi");        delay(1000);        if (health(player) < 100)        {                say(homeowner, "Oh, you''re injured! Wait a minute");                walkToObject(homeowner, mapObj("desk"));                npcDelay(homeowner, 1000);                walkTo(homeowner, player);                say(homeowner, "Here, take this...");                give(homeowner, player, "health_potion");                // player''s script may react and icrease health...        }}elseif (npcFeeling(homeowner, player) >= 50){        say(homeowner, "Hi stranger!");        if (health(player) < 60)        {                say(homeowner, "Oh, you''re injured! Wait a minute");                walkToObject(homeowner, mapObj("desk"));                npcDelay(homeowner, "10");                walkTo(homeowner, player);                say(homeowner, "Here, take this...");                give(homeowner, player, "health_potion");                // player''s script may react and icrease health...        }        // conversation where the player meets the homeowner}if (npcFeeling(homeowner, player) < 20){        say(homeonwer, "Go away!");        walkTo(homeowner, player);        hit(homeowner, player, 10);        walkTo(player, door);        loadMapPart("outside");}// more 


The above works almost like Mayrel''s suggestion about feelings but it doesn''t require a OOP implementation or language-side help. It is written in a very basic procedural manner, in an easy-to-implement scripting language (actually the above is syntax-correct NerveBreak code :-).

Priorities can also be done in a procedural language like:

function homeownerFeedFish(){        //}function homeownerHelpPlayer(){        //}setNpcPriority(homeowner, 10, homeownerFeedFish);setNpcPriority(homeowner, 60, homeownerHelpPlayer); 

--Slashstone - www.slashstone.comNerveBreak free scripting system.

This topic is closed to new replies.

Advertisement