Author Topic: Structures within Structures? Shouldn't this work?  (Read 718 times)

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Structures within Structures? Shouldn't this work?
« on: March 24, 2016, 12:49:23 pm »


               

Im building this using the nss compiler on linux.


(Think its the advanced compiler)


 


 




struct Power
{
string PowerName;
int FeatID; //-1 for no feat
int IsPassive; //1 for yes, 0 for no: Determines if the Heartbeat will apply effect or not
int EffectType; // Used for Passives
int TimeOfDayActive; //Used to determine when ability is active or usable (TIME_BOTH for always);
int EffectNumber1;
int EffectNumber2;
int LevelOfPower;
int VisualEffect; //If there is an associated visual (auras etc)
};
struct Genome
{
    string GenomeID;
    string Description;
    struct Power Level1Effect;
    struct Power Level2Effect;
    struct Power Level3Effect;
    struct Power Level4Effect;
    struct Power Level5Effect;
    struct Power Level6Effect;
    struct Power Level7Effect;
    struct Power Level8Effect;
    struct Power Level9Effect;
    struct Power Level10Effect;
};
 


 

However, this method gets compile errors:

 

 



 

void CreateGenome(struct InhumanGenome genome){
int i = GetGenomeCount();
i++;
object oMod = GetModule();
string GenomeID = genome.GenomeID;
 
SetLocalInt(oMod,GENOME_NAME_TO_ID+"_"+GenomeID,i);
SetLocalString(oMod,GENOME_ID_TO_NAME+"_"+IntToString(i),GenomeID);
SetGenomePower(i, genome.Level1Effect);
SetGenomePower(i, genome.Level2Effect);
SetGenomePower(i, genome.Level3Effect);
SetGenomePower(i, genome.Level4Effect);
SetGenomePower(i, genome.Level5Effect);
SetGenomePower(i, genome.Level6Effect);
SetGenomePower(i, genome.Level7Effect);
SetGenomePower(i, genome.Level8Effect);
SetGenomePower(i, genome.Level9Effect);
SetGenomePower(i, genome.Level10Effect);
 
WriteTimestampedLogEntry("Created Genome Structure:"+GenomeID+" with ID:"+IntToString(i));
 
}
 


 


I hate these forums - half my post just got cut off


 


GRRR


 


Anyway - I am trying to compile, but I get the following:


 



genome_inc.nss(321): Error: Element "Level1Effect" is not a member of the structure

genome_inc.nss(321): Error: Required argument missing in call to "SetGenomePower"

genome_inc.nss(322): Error: Element "Level2Effect" is not a member of the structure

igenome_inc.nss(322): Error: Required argument missing in call to "SetGenomePower"

genome_inc.nss(323): Error: Element "Level3Effect" is not a member of the structure

genome_inc.nss(323): Error: Required argument missing in call to "SetGenomePower"

genome_inc.nss(324): Error: Element "Level4Effect" is not a member of the structure


...



 


 


Does nwscript not handle nested structures?


               
               

               
            

Legacy_meaglyn

  • Hero Member
  • *****
  • Posts: 1451
  • Karma: +0/-0
Structures within Structures? Shouldn't this work?
« Reply #1 on: March 24, 2016, 01:14:30 pm »


               

The compiler I use clearly does not. I think that's what you are using too.  I don't have the game handy to try it with the inbox compiler.   Drat, I wish I had saved the old nwnscript grammar post I dug up a few years back instead of bookmarking it. The ephemeral web has swallowed it...



               
               

               
            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Structures within Structures? Shouldn't this work?
« Reply #2 on: March 24, 2016, 01:25:11 pm »


               

Yeah, think I am using the compiler that you sent me a while back.


It would be ideal if the compiler could handle nested structures, but I am suspicious that this topic possibly came up before.


If it could handle nested structures, I would imagine that shayans subrace engine would have used them, opposed to the storing of rather gratuitously large amounts of data on the module.



               
               

               
            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Structures within Structures? Shouldn't this work?
« Reply #3 on: March 24, 2016, 01:51:40 pm »


               

Managed to get around the issue by using references to the structures, instead of nesting of structures.


 


Usage:



struct Genome ElectricalGenome;
ElectricalGenome.GenomeID = "Electrical Manipulation";
ElectricalGenome.Description = "Bearers of this genome are resistent to electrical discharges and can create electrical currents with their bodies.";
 
struct Power Power1;
Power1.PowerName = "Electrical Regeneration";
Power1.FeatID = -1;
Power1.IsPassive = TRUE;
Power1.EffectType = EFFECT_TYPE_REGENERATE;
Power1.TimeOfDayActive = TIME_BOTH;
Power1.EffectNumber1 = 2; //2 hp
Power1.EffectNumber2 = 10;  //every 10 seconds
Power1.LevelOfPower = 1;
Power1.VisualEffect = VFX_DUR_AURA_BLUE_LIGHT;
 
ElectricalGenome.Level1Effect = CreateGenomePower(Power1);
 
CreateGenome(ElectricalGenome);


Once again - my post gets truncated.....annoying


 


 


Anyway, the changes I made are:



 
struct Power
{
string PowerName;
int FeatID; //-1 for no feat
int IsPassive; //1 for yes, 0 for no: Determines if the Heartbeat will apply effect or not
int EffectType; // Used for Passives
int TimeOfDayActive; //Used to determine when ability is active or usable (TIME_BOTH for always);
int EffectNumber1;
int EffectNumber2;
int LevelOfPower;
int VisualEffect; //If there is an associated visual (auras etc)
};
struct InhumanGenome
{
    string GenomeID;
    string Description;
int Level1Effect;
int Level2Effect;
int Level3Effect;
int Level4Effect;
int Level5Effect;
int Level6Effect;
int Level7Effect;
int Level8Effect;
int Level9Effect;
int Level10Effect;
};
 

int CreateGenomePower(struct Power power)
{
 
string powername = power.PowerName;
if(powername == ""){ return 0;}
 
WriteTimestampedLogEntry("Adding power:"+powername+" to modules collection of powers.");
 
 
int FeatId = power.FeatID;
int Passive = power.IsPassive;
int EffectType = power.EffectType;
int TimeOfDay = power.TimeOfDayActive;
int num1 = power.EffectNumber1;
int num2 = power.EffectNumber2;
int lvl = power.LevelOfPower;
int Visual = power.VisualEffect;
 
WriteTimestampedLogEntry("FeatID:"+IntToString(FeatId));
WriteTimestampedLogEntry("EffectType:"+IntToString(EffectType));
WriteTimestampedLogEntry("Passive:"+IntToString(Passive));
WriteTimestampedLogEntry("TimeOfDay:"+IntToString(TimeOfDay));
WriteTimestampedLogEntry("num1:"+IntToString(num1));
WriteTimestampedLogEntry("num2:"+IntToString(num2));
WriteTimestampedLogEntry("lvl:"+IntToString(lvl));
WriteTimestampedLogEntry("Visual:"+IntToString(Visual));
 
object oModule = GetModule();
 
//Get current power count for this inhuman
int PowerIDLastPower = GetLocalInt(oModule,POWERID_FOR_GENOME);
PowerIDLastPower++;
WriteTimestampedLogEntry("This power will have the ID:"+IntToString(PowerIDLastPower));
 
SetLocalString(oModule,POWER_STORAGE_NAME+IntToString(PowerIDLastPower),powername);
SetLocalInt(oModule,POWER_STORAGE_FEATID+IntToString(PowerIDLastPower),FeatId);
SetLocalInt(oModule,POWER_STORAGE_IS_PASSIVE+IntToString(PowerIDLastPower),Passive);
SetLocalInt(oModule,POWER_STORAGE_EFFECT_TYPE+IntToString(PowerIDLastPower),EffectType);
SetLocalInt(oModule,POWER_STORAGE_TIMEOFDAY+IntToString(PowerIDLastPower),TimeOfDay);
SetLocalInt(oModule,POWER_STORAGE_NUMBER1+IntToString(PowerIDLastPower),num1);
SetLocalInt(oModule,POWER_STORAGE_NUMBER2+IntToString(PowerIDLastPower),num2);
SetLocalInt(oModule,POWER_STORAGE_LEVEL_OF_POWER+IntToString(PowerIDLastPower),lvl);
SetLocalInt(oModule,POWER_STORAGE_VISUAL+IntToString(PowerIDLastPower),Visual);
 
//Update the amount of powers for this genome
SetLocalInt(oModule,POWERID_FOR_GENOME,PowerIDLastPower);
return PowerIDLastPower;
}

 

void ApplyAbilityData(int UniqueID, struct InhumanGenome genome){
 
int Level1Effect = genome.Level1Effect;
int Level2Effect = genome.Level2Effect;
int Level3Effect = genome.Level3Effect;
int Level4Effect = genome.Level4Effect;
int Level5Effect = genome.Level5Effect;
    int Level6Effect = genome.Level6Effect;
int Level7Effect = genome.Level7Effect;
int Level8Effect = genome.Level8Effect;
int Level9Effect = genome.Level9Effect;
int Level10Effect = genome.Level10Effect;
object oModule = GetModule(); //
SetLocalInt(oModule,ABILITY_ROSTER+IntToString(UniqueID)+"_1",Level1Effect);
SetLocalInt(oModule,ABILITY_ROSTER+IntToString(UniqueID)+"_2",Level2Effect);
SetLocalInt(oModule,ABILITY_ROSTER+IntToString(UniqueID)+"_3",Level3Effect);
SetLocalInt(oModule,ABILITY_ROSTER+IntToString(UniqueID)+"_4",Level4Effect);
SetLocalInt(oModule,ABILITY_ROSTER+IntToString(UniqueID)+"_5",Level5Effect);
SetLocalInt(oModule,ABILITY_ROSTER+IntToString(UniqueID)+"_6",Level6Effect);
SetLocalInt(oModule,ABILITY_ROSTER+IntToString(UniqueID)+"_7",Level7Effect);
SetLocalInt(oModule,ABILITY_ROSTER+IntToString(UniqueID)+"_8",Level8Effect);
SetLocalInt(oModule,ABILITY_ROSTER+IntToString(UniqueID)+"_9",Level9Effect);
SetLocalInt(oModule,ABILITY_ROSTER+IntToString(UniqueID)+"_10",Level10Effect);
 
}
 

void CreateInhumanGenome(struct InhumanGenome genome){
int i = GetGenomeCount();
i++;
object oMod = GetModule();
string GenomeID = genome.GenomeID;
 
SetLocalInt(oMod,INHUMAN_NAME_TO_ID+"_"+GenomeID,i);
SetLocalString(oMod,INHUMAN_ID_TO_NAME+"_"+IntToString(i),GenomeID);
ApplyAbilityData(i,genome);
 
WriteTimestampedLogEntry("Created Genome Structure:"+GenomeID+" with ID:"+IntToString(i));
 
}

 



The benefit of this approach is that you can declare effects / abilities for a subrace or as I call it in my project, a Genome, and then have it shared across multiple subraces.

Other subrace engines - if you wanted a regen effect on 10 subraces, all of them with the same regen rate, that would be 10 definitions of that passive ability.


This approach I am going for, means it is defined once, but can be reused by multiple genomes / subraces.


It potentially makes it more memory efficient if you are storing a pointer to the effect data, opposed to storing instances of the data itself.


 


Because the CreateGenomePower returns an int that references the power, it means I can use that int for multiple genomes/subraces to have the same effect applied to them.


               
               

               
            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Structures within Structures? Shouldn't this work?
« Reply #4 on: March 24, 2016, 05:50:43 pm »


               

Further enhancement I made to the system is to allow unlimited amount of abilities/powers per gene.

These genes could then be stored against subraces or individual players/characters.



Resulting in unique combinations of abilities.

The idea is to make a modular/reusable engine for passive abilities and sub-racial traits.


 


Eg: A character might have Charisma bonus from being half elven, but one of their ancestors may have been of fiendish descent, attributing them 10% immunity to negative damage.


 


To do this in the engine:


Declare the abilities

struct Genome ElvenGenomeWithInfernal;
ElvenGenomeWithInfernal.GenomeID = "Elven heritage & Fiendish ancestors";
ElvenGenomeWithInfernal.Description = "Having both Elven traits and a dash of demonic blood, this gene provides Charisma +5 and +10% Negative Damage resist.";
 
 
struct Power Power1;
Power1.PowerName = "Charisma bonus";
Power1.FeatID = -1;
Power1.IsPassive = TRUE;
Power1.EffectType = EFFECT_TYPE_ABILITY_INCREASE;
Power1.TimeOfDayActive = TIME_BOTH;
Power1.EffectNumber1 = ABILITY_TYPE_CHARISMA; //INCREASE CHARISMA
Power1.EffectNumber2 = 5;  //BY 5
Power1.LevelOfPower = 1;
Power1.VisualEffect = -1; // No visual effect
int CharismaBonus = CreateGenomePower(Power1);
 
struct Power Power2;
Power2.PowerName = "Immune to 10% Negative";
Power2.FeatID = -1;
Power2.IsPassive = TRUE;
Power2.EffectType = EFFECT_TYPE_DAMAGE_IMMUNITY_INCREASE;
Power2.TimeOfDayActive = TIME_BOTH;
Power2.EffectNumber1 = DAMAGE_TYPE_NEGATIVE; //Immunity to negative
Power2.EffectNumber2 = 10;  // 10%
Power2.LevelOfPower = 1;
Power2.VisualEffect = -1; // no vfx
int NegativeResist10= CreateGenomePower(Power2);
 
 
CreateGenome(ElvenGenomeWithInfernal);
AddPowerToGenomeStruct(ElvenGenomeWithInfernal,CharismaBonus );
AddPowerToGenomeStruct(ElvenGenomeWithInfernal,NegativeResist10);


 

               
               

               
            

Legacy_Valgav

  • Full Member
  • ***
  • Posts: 118
  • Karma: +0/-0
Structures within Structures? Shouldn't this work?
« Reply #5 on: March 24, 2016, 07:57:20 pm »


               

What exactly AddPowerToGenomeStruct() does? I can't find the definition



               
               

               
            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Structures within Structures? Shouldn't this work?
« Reply #6 on: March 24, 2016, 10:05:40 pm »


               Must have forgot to attach that.

But basically it links the power to the genome.

Means when the genome is processed we can loop from 1 to x to get the id of all associated power ids.


Uses setlocalint with a string identifier that uses the genome id and an incrementing number.
               
               

               
            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Structures within Structures? Shouldn't this work?
« Reply #7 on: March 24, 2016, 10:06:22 pm »


               The theory is that a player can have many genes, which then in turn have 1 or more abilities associated with them
               
               

               
            

Legacy_Valgav

  • Full Member
  • ***
  • Posts: 118
  • Karma: +0/-0
Structures within Structures? Shouldn't this work?
« Reply #8 on: March 24, 2016, 10:49:45 pm »


               

So you just loop through elven_genom_x where x is number as long as can get variable? Sounds pretty simple but what will happen if players will lose bonus that is not the last one? You have to manually renumber them?



               
               

               
            

Legacy_Proleric

  • Hero Member
  • *****
  • Posts: 1750
  • Karma: +0/-0
Structures within Structures? Shouldn't this work?
« Reply #9 on: March 25, 2016, 08:12:34 am »


               

The code in the first post compiles fine with the vanilla Bioware compiler.



               
               

               
            

Legacy_meaglyn

  • Hero Member
  • *****
  • Posts: 1451
  • Karma: +0/-0
Structures within Structures? Shouldn't this work?
« Reply #10 on: March 25, 2016, 12:55:39 pm »


               

Thanks Proleric. I forgot to try that last night. I was afraid that would be the case.  There is something a little squirrelly with the way this compiler handles structs. It does a lot of other things well though '<img'>



               
               

               
            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Structures within Structures? Shouldn't this work?
« Reply #11 on: March 25, 2016, 05:54:14 pm »


               

Nice to know its just a limitation of the compiler, but that being said- it did force me to move in a direction which I actually think is probably better.


Eg: Define the 'powers' pseudo-class, and then reference it, opposed to redefining it.


 


 


So to stay in the terminology :


Players have a Genome - > Which is a collection of Genes.


A Gene will have a collection of abilities (passives or attached feats)


 


This system implying a genetic makeup, sort of suggests that a player/character shouldn't ordinarily be able to lose abilities.


 


Eg: Once they acquire a gene, they get the abilities associated with it permanently. 


So renumbering shouldn't be necessary.


 


That being said, if I decide to implement removal of genes, then yeah, renumbering shouldn't be too tricky, I just need to keep track of how many genes every player/character has and then reindex them.


 


Using a database would probably be better for this sort of data manipulation.


 


While I am working on this system, I think I will also code each Gene so it can have other activation criteria.


Eg: Underground, Day& Night, Outdoors, Natural Areas, Weather activation.


 


Eg: Imagine an aquatic subrace - who gains +regen in the rain.


 


Using nwnx_funcs I can also code it to detect what sort of terrain the player is standing on, and then use that to activate other genes also.


Eg: When standing in water => Regeneration & AC Bonus etc


 


 


 


In case it wasn't obvious from some of the code snippets that slipped into my above posts - This system is actually being inspired by Marvel Agents of Shield, with the Inhuman story arc. (Inhumans being humans who inherited alien dna from thousands of years past, but the DNA gets activated through a process called Terrigenesis)


 


I plan to define several dozen ability genes, and when players log in for the first time, they are allocated a combination of genes.


The combination allocated will appear to be random, however I will use data tracking to see the distribution of abilities over time, to make sure that its an even spread of abilities.


 


The players then have to complete a story arc, which awakens their 'inhuman' genetics.


So up until that point, they have no idea what genetic powers they will have.


 


It could be something like +X to ability scores (something mundane),


True Seeing at night time etc,


or it could be something radical like gaining access to a powerset (feats with spell like abilities)


Eg: Shooting Electricity, or even mind control.


 


Some genes will also have detrimental effects too.


Eg: Weakness to sunlight - (ala - vampire)


 


 


This system, while being used for Inhumans in my server, will also be re-usable for a subrace system also.


Eg: Define a 'weakness to sunlight' gene - then I can have that as an inhuman ability, but also as an attribute for vampires.