Aller au contenu

Photo

New Technique for storing integers on a character.


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

#1
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages
I wanted to store variables on a character which persisted perpetually, without issues caused by local int's, items or skins, and something which allows a player to jump from single player game to single player game without an isssue since i cannot use a database like i can in multiplayer.

The method of accomplishing this is via using feats which are set up to be invisible. I set up multiple feats in a contiguous block, and turn them on and off to store a binary number. They don't have a category so the player cannot see them, they just work as boolean registers. This is the main limitation, you need a custom feats.2da but it's very good for features related to feats and classes which require customizing that 2da to begin with.

This is not intended for massive amounts of data, there are likely issues caused by having too many feats, and using local ints is a lot faster. This is for critical integers related to your build, which need to be in a safer place than on items or stored as integers on the player himself. I am thinking this is good for psionic power points, mana points, custom skill points and other features which require us to sidestep the entire engine to implement ( like the language points ). To use more than 1000 feats for this would likely require testing for unforseen bugs, and my thoughts are about 200 or so is all that will be needed at the very most. ( there are issues when you have more than 255 spells for example )

2 example rows for feats which i am actually using, this actually goes from row 8800 to 8807 to handle this one range:
8800	FEAT_FEATGROUP_LANGPOINTS1	10000	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	FEAT_FEATGROUP_PRIORLORE1	6	0	****	****	****	****	0	****	1	0	****	****	0	0	****	****	****
8801	FEAT_FEATGROUP_LANGPOINTS2	10000	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	****	FEAT_FEATGROUP_LANGPOINTS2	6	0	****	****	****	****	0	****	1	0	****	****	0	0	****	****	****


Then i include my math library which i added the following two functions ( forgive the formatting as this site messing it up ):
// stores an integer onto a character as a set of feats
void CSLIntegerToFeatGroup( int iInteger, int iStartingFeat, int iEndingFeat, object oTarget = OBJECT_SELF )
{
	int iRow = 0;
	int iCurrentBit = 1;
	int iTotalRows = (iEndingFeat-iStartingFeat);
	//SendMessageToPC(GetFirstPC(),"Integer "+IntToString(iInteger)+" to feat. "+IntToString(iStartingFeat)+" to "+IntToString(iEndingFeat));
	for (iRow = 0; iRow <= iTotalRows; iRow++) 
	{
		if ( iInteger & ( 1 << iRow ) ) // the syntax for getting a given bit is  "1 << iColumn" which shifts the value by that amount
		{
			FeatAdd( oTarget, iStartingFeat+iRow, FALSE, FALSE, FALSE );
			//SendMessageToPC(GetFirstPC(),"1 - Adding Feat "+IntToString(iStartingFeat+iRow)+" Bit="+IntToString(iRow));
		}
		else
		{
			FeatRemove( oTarget, iStartingFeat+iRow );
			//SendMessageToPC(GetFirstPC(),"0 - Removing Feat "+IntToString(iStartingFeat+iRow)+" Bit="+IntToString(iRow));
		}
	}
}

// stores an integer onto a character as a set of feats
int CSLFeatGroupToInteger(  int iStartingFeat, int iEndingFeat, object oTarget = OBJECT_SELF  )
{
	int iInteger;
	int iRow = 0;
	int iCurrentBit = 1;
	int iTotalRows = (iEndingFeat-iStartingFeat);
	//SendMessageToPC(GetFirstPC(),"Getting Integer from feat. "+IntToString(iStartingFeat)+" to "+IntToString(iEndingFeat));
	for (iRow = 0; iRow <= iTotalRows; iRow++) 
	{
		if ( GetHasFeat( iStartingFeat+iRow, oTarget, TRUE ) )
		{
			iInteger |= ( 1 << iRow ); // the syntax for getting a given bit is  "1 << iColumn" which shifts the value by that amount
			//SendMessageToPC(GetFirstPC(),"1 - Has Feat "+IntToString(iStartingFeat+iRow)+" Bit="+IntToString(iRow));
		}
	}
	return iInteger;
}


Usage is simple, the functions basically do all the work.

The parameters for each function.
int iInteger - the integer being set
int iStartingFeat - the first feat in the range defined in feats.2da
int iEndingFeat - the last feat in the range defined in feats.2da
oTarget = character you store the value on.

The first function sets the integer.
void CSLIntegerToFeatGroup( int iInteger, int iStartingFeat, int iEndingFeat, object

This function retrieves it.
int CSLFeatGroupToInteger( int iStartingFeat, int iEndingFeat, object oTarget = OBJECT_SELF )

Note that this uses 8 bits ( 0-7 ) to store a number from 1-255. To store the largest number you can use in game you'd need 31 bits. Storing larger numbers than 255 in 8 rows would result in seemingly random results as it will just ignore parts of the number, very similar to an odometer when it uses more spaces than are available it just starts at zero again.
1    =   0 - 1
2    =   0 - 3
3    =   0 - 7
4    =   0 - 15
5    =   0 - 31
6    =   0 - 63
7    =   0 - 127
8    =   0 - 255
9    =   0 - 511
10   =   0 - 1023
11   =   0 - 2047
12   =   0 - 4095
13   =   0 - 8191
14   =   0 - 16383
15   =   0 - 32767
16   =   0 - 65535
17   =   0 - 131071
18   =   0 - 262143
19   =   0 - 524287
20   =   0 - 1048575
21   =   0 - 2097151
22   =   0 - 4194303
23   =   0 - 8388607
24   =   0 - 16777215
25   =   0 - 33554431
26   =   0 - 67108863
27   =   0 - 134217727
28   =   0 - 268435455
29   =   0 - 536870911
30   =   0 - 1073741823
31*  =   0 - 2147483647


This is how i am using it, to store language points and also increase language points when a characters base lore goes up after leveling up.
int CSLLanguageGetLanguagePoints( object oPlayer )
{
	int iLanguagePoints = CSLFeatGroupToInteger( 8800, 8807, oPlayer );
	
	// increases in lore base rank are added directly to language points
	int iCurrentLore = GetSkillRank(SKILL_LORE, oPlayer, TRUE );	
	int iPreviousLore = CSLFeatGroupToInteger( 8808, 8815, oPlayer );
	if ( iCurrentLore > iPreviousLore )
	{
		CSLIntegerToFeatGroup( iCurrentLore, 8808, 8815, oPlayer );
		iLanguagePoints += ( iCurrentLore - iPreviousLore );
		CSLIntegerToFeatGroup( iLanguagePoints, 8800, 8807, oPlayer );
	}
	return iLanguagePoints;
}


This will be part of the next release of the CSL library, once i'm finished implementing all the features related to this.

#2
dethia

dethia
  • Members
  • 146 messages
hmm have you considered using the player file tag? You can set the tag on a player character which when exported (or saved) alters the bic file with the updated tag. The value can never be changed back to blank as far as I know.

In any case depending on the field length of the tag field, one can write a function using some delimiters that keeps track of information like variable id, name and value, albeit it would have to be a string variable (so always converting). Just something to consider as it doesn't seem like it will be too hard to do though I may be wrong.

((Edited for typo

Modifié par dethia, 01 février 2011 - 03:09 .


#3
painofdungeoneternal

painofdungeoneternal
  • Members
  • 1 799 messages
yep i do that too, for the database serial number which quite a few PW's are doing. I also have code for handling multiple items in a tag which i use to store data on AOE's in their tag.

This is another option, which won't cause conflicts with that technique, and has a lot less over head when changing values quickly on the character, especially if you are doing multiple things. 200 bits gives you 14 digits from 1-255 ( of course you can do something like that by position as well using a string )

The more options you have as a scripter the better.

Modifié par painofdungeoneternal, 01 février 2011 - 03:27 .