Choosing a scripting language for game and implementing it
- by Radius
Hello, I am currently developing a 3D Action/RPG game in C++, and I would like some advice in choosing a scripting language to program the AI of the game. My team comes from a modding background, and in fact we are still finishing work on a mod of the game Gothic. In that game (which we also got our inspiration from) the language DAEDALUS (created by Piranha Bytes, the makers of the game) is used. Here is a full description of said language.
The main thing to notice about this is that it uses instances moreso than classes. The game engine is closed, and so one can only guess about the internal implementation of this language, but the main thing I am looking for in a scripting language (which ideally would be quite similar but preferably also more powerful than DAEDALUS) is the fact that there are de facto 3 'separations' of classes - ie classes, instances and (instances of instances?).
I think it will be easier to understand what I want if I provide an example. Take a regular NPC. First of all you have a class defined which (I understand) mirrors the (class or structure) inside the engine:
CLASS C_NPC
{
VAR INT id ; // absolute ID des NPCs
VAR STRING name [5] ; // Namen des NPC
VAR STRING slot ;
VAR INT npcType ;
VAR INT flags ;
VAR INT attribute [ATR_INDEX_MAX] ;
VAR INT protection [PROT_INDEX_MAX];
VAR INT damage [DAM_INDEX_MAX] ;
VAR INT damagetype ;
VAR INT guild,level ;
VAR FUNC mission [MAX_MISSIONS] ;
var INT fight_tactic ;
VAR INT weapon ;
VAR INT voice ;
VAR INT voicePitch ;
VAR INT bodymass ;
VAR FUNC daily_routine ; // Tagesablauf
VAR FUNC start_aistate ; // Zustandsgesteuert
// **********************
// Spawn
// **********************
VAR STRING spawnPoint ; // Beim Tod, wo respawnen ?
VAR INT spawnDelay ; // Mit Delay in (Echtzeit)-Sekunden
// **********************
// SENSES
// **********************
VAR INT senses ; // Sinne
VAR INT senses_range ; // Reichweite der Sinne in cm
// **********************
// Feel free to use
// **********************
VAR INT aivar [50] ;
VAR STRING wp ;
// **********************
// Experience dependant
// **********************
VAR INT exp ; // EXerience Points
VAR INT exp_next ; // EXerience Points needed to advance to next level
VAR INT lp ; // Learn Points
};
Then, you can also define prototypes (which set some default values). But how you actually define an NPC is like this:
instance BAU_900_Ricelord (Npc_Default) //Inherit from prototype Npc_Default
{
//-------- primary data --------
name = "Ryzowy Ksiaze";
npctype = NPCTYPE_GUARD;
guild = GIL_BAU;
level = 10;
voice = 12;
id = 900;
//-------- abilities --------
attribute[ATR_STRENGTH] = 50;
attribute[ATR_DEXTERITY] = 10;
attribute[ATR_MANA_MAX] = 0;
attribute[ATR_MANA] = 0;
attribute[ATR_HITPOINTS_MAX]= 170;
attribute[ATR_HITPOINTS] = 170;
//-------- visuals --------
// animations
Mdl_SetVisual (self,"HUMANS.MDS");
Mdl_ApplyOverlayMds (self,"Humans_Arrogance.mds");
Mdl_ApplyOverlayMds (self,"HUMANS_DZIDA.MDS");
// body mesh ,bdytex,skin,head mesh ,headtex,teethtex,ruestung
Mdl_SetVisualBody (self,"Hum_Body_CookSmith",1,1,"Hum_Head_FatBald",91 , 0,-1);
B_Scale (self);
Mdl_SetModelFatness(self,2);
fight_tactic = FAI_HUMAN_STRONG;
//-------- Talente --------
Npc_SetTalentSkill (self,NPC_TALENT_1H,1);
//-------- inventory --------
CreateInvItems (self, ItFoRice,10);
CreateInvItem (self, ItFoWine);
CreateInvItems(self, ItMiNugget,40);
EquipItem (self, Heerscherstab);
EquipItem (self, MOD_AMULETTDESREISLORDS);
CreateInvItem (self, ItMi_Alchemy_Moleratlubric_01);
//CreateInvItem (self,ItKey_RB_01);
EquipItem (self, Ring_des_Lebens);
//-------------Daily Routine-------------
daily_routine = Rtn_start_900;
};
FUNC VOID Rtn_start_900 ()
{
TA_Boss (07,00,20,00,"NC_RICELORD");
TA_SitAround (20,00,24,00,"NC_RICELORD_SIT");
TA_Sleep (24,00,07,00,"NC_RICEBUNKER_10");
};
As you can see, the instance declaration is more like a constructor function, setting values and calling functions from within. This still wouldn't pose THAT much of a problem, if not for one more thing: multiple copies of this instance. For example, you can spawn multiple BAU_900_Ricelord's, and each of them keeps track of its own AI state, hitpoints etc.
Now I think the instances are represented as ints (maybe even as the id of the NPC) inside the engine, as whenever (inside the script) you use the expression BAU_900_Ricelord it can be only assigned to an int variable, and most functions that operate on NPCs take that int value. However to directly modify its hitpoints etc you have to do something like var C_NPC npc = GetNPC(Bau_900_Ricelord); npc.attribute[ATR_HITPOINTS] = 10; ie get the actual C_NPC object that represents it.
To finally recap - is it possible to get this kind of behaviour in any scripting languages you know of, or am I stuck with having to make my own? Or maybe there is an even better way of representing NPC's and their behaviours that way. The IDEAL language for scripting for me would be C#, as I simply adore that language, but somehow I doubt it is possible or indeed feasible to try and implement a similar kind of behaviour in C#.
Many thanks