|
|
|
Page 1 of 1
|
[ 4 posts ] |
|
|
Selmak
|
Posted: Fri, Jan 07 2011, 22:47 PM |
|
|

Player
Joined: 17 Dec 2004
|
How to write a logic check that's easy to read.Let's say that you have a script which does its thing only when certain conditions are met. Now, you could put all the checks on one line, like this: Code: void main() {
object oThisArea = GetArea(OBJECT_SELF); if ( GetIsAreaAboveGround( oThisArea ) && GetIsAreaInterior( oThisArea ) && ( GetIsNight() || GetIsDusk() ) ) WriteTimestampedLogEntry("Holy brackets, Batman!");
} That's not too hard to figure out. It will write a timestamped log entry if the object the script is run on (OBJECT_SELF) is in an interior area, above ground, at dusk or during the night. Instead though, you could write this: Code: void main() {
object oThisArea = GetArea(OBJECT_SELF); int bCheck, bIsDark;
//We check to see if it is Night or Dusk bIsDark = GetIsNight() || GetIsDusk();
//We check to see if OBJECT_SELF is above ground bCheck = GetIsAreaAboveGround( oThisArea );
//We check to see if OBJECT_SELF is above ground AND in an interior area bCheck = bCheck && GetIsAreaInterior( oThisArea );
//We check to see if OBJECT SELF is above ground and in an interior area AND that it is dark. bCheck = bCheck && bIsDark;
if (bCheck) WriteTimestampedLogEntry("Holy logic check, Batman!");
} What this does is temporarily store the result of the ongoing logic operation in a variable bCheck. At the end, if bCheck is true then the log entry is made, if it isn't (if one of the checks returns a false value) then nothing happens. Breaking the logic into steps makes it easier to see what you're doing and why, and if something goes wrong it's easier to debug. For example, you could send a debug message to the PC (if OBJECT_SELF is a PC of course) that shows the results of each step of the check, so that you can move from one area to another and see if the check works as it should. To get this check to work in a module (just for example) you could modify the start of the script like this: Code: object oPC = GetEnteringObject(); object oThisArea = GetArea(oPC); Then, you could attach it to the OnEnter event of an area or areas and it would function whenever something enters those areas.
|
|
|
|
 |
|
Selmak
|
Posted: Mon, Jan 10 2011, 8:58 AM |
|
|

Player
Joined: 17 Dec 2004
|
How to code a one-way triggerThe basic premise is this: you want something to happen when a PC goes one way, but nothing if they go the other way. Or at the very least, you want something different to happen depending on which way they go. What we do is figure out the facing to the other side of the trigger by using a waypoint, and then compare that to the direction the PC is facing when they left the trigger. Or rather, we find the difference between the two facings in degrees. Code: void main() { // Since this applies to a trigger, you could check this when a PC exits or // enters, depending on what you want to do. object oPC = GetExitingObject(); float fPCFacing = GetFacing( oPC ); float fAngle, fResult;
// This waypoint has a tag that matches it to the specific trigger. This // means you can use this script with many triggers. The waypoint here is // used to establish which direction the PC will activate the trigger. object oWP1 = GetWaypointByTag( "WP_" + GetTag(OBJECT_SELF) + "_1" );
// Vectors store x y and z co-ordinates in a location. vector vVector1, vVector2, vVector3; // This is to store the PC's facing to note in the log file. string sReading;
// GetPositionFromLocation takes the supplied location and extracts the // vector in an x y z format we can use. In this case we're subtracting the // PC's co-ordinates from the waypoint's co-ordinates, giving us the vector // between those two points. vVector1 = GetPositionFromLocation( GetLocation( oPC ) ); vVector2 = GetPositionFromLocation( GetLocation( oWP1 ) ); vVector3 = vVector2 - vVector1;
// Then we turn that resulting vector back into meaningful information, an // angle that shows which way the PC would have to go to get to the // waypoint. fAngle = VectorToAngle( vVector3 );
// Now we need to subtract the smaller number from the bigger number. if ( fPCFacing > fAngle ) fResult = fPCFacing - fAngle; else fResult = fAngle - fPCFacing;
// This will turn the PC's facing into a string with three characters, // without any decimals. sReading = FloatToString( fPCFacing, 3, 0 );
// If fResult is less than 45 degrees, the trigger is considered to be // activated and the appropriate message is sent. If fResult is more // than 45 degrees, the trigger is not activated, but a message is logged //anyway for debugging purposes. if ( fResult < 45.0 ) WriteTimestampedLogEntry( "PC activated the trigger at a facing of " + sReading ); else WriteTimestampedLogEntry( "PC did not activate the trigger at a facing of " + sReading ); } You will need to put this in the OnExit event of a trigger to get it to work, and you'll need a matching waypoint too. What I mean by that is if your trigger has the tag 'foo', your waypoint's tag must be 'WP_foo_1' or it won't use it. Another way of doing this idea is to store the vector as a series of three floats on the trigger, or store the desired facing as a float, but this means that you need to drill down into the trigger's variables list in order to see its settings. A matching waypoint means you have an easily-moved point in the area to refer to.
|
|
|
|
 |
|
Selmak
|
Posted: Wed, Jan 12 2011, 11:26 AM |
|
|

Player
Joined: 17 Dec 2004
|
How to add an item property depending on alignmentSuppose you've discovered the joy of tag-based scripting and you want an item activated script to behave differently depending on the alignment of the character using the item. In this example, we'll look at using a switch/case statement to set up a different temporary item property (item buff in other words) depending on the character's specific alignment. Firstly, there is no GetSpecificAlignment function. So we need to figure out the PC's alignment on both the Law/Chaos axis and the Good/Evil axis separately first. Code: // Why are these defined as constants, you might be thinking. Well, in order to // use these in a switch/case statement, they have to be constants. // // The existing alignment constants are 1 for neutral, 2 for lawful, // 3 for chaotic, 4 for good and 5 for evil. So to get these values we simply // multiply the good/evil number by 6, and add the law/chaos number on. // // You can't assign the value of an expression to constants like you can with // variables, not even if the expression is based on another constant. const int ALIGNMENT_LAWFUL_GOOD = 26; const int ALIGNMENT_NEUTRAL_GOOD = 25; const int ALIGNMENT_CHAOTIC_GOOD = 27; const int ALIGNMENT_LAWFUL_NEUTRAL = 8; const int ALIGNMENT_TRUE_NEUTRAL = 7; const int ALIGNMENT_CHAOTIC_NEUTRAL = 9; const int ALIGNMENT_LAWFUL_EVIL = 32; const int ALIGNMENT_NEUTRAL_EVIL = 31; const int ALIGNMENT_CHAOTIC_EVIL = 33;
void main() {
// We're calling this script because an item has been activated. We need // to known which item. object oItem = GetItemActivated();
// So we check to see if this item is actually valid. The ! here means NOT, // which reverses the logical meaning of whatever it is put in front of, // true becomes false and false becomes true. if (!GetIsObjectValid(oItem)) return;
object oPC = GetItemActivator();
// Again, just checking to see if the item is actually possessd by a valid // character. if (!GetIsObjectValid(oPC)) return;
// This is pretty straightforward, gets the aligment of the PC in terms of // Law/chaos and good/evil int nLawChaos = GetAlignmentLawChaos(oPC); int nGoodEvil = GetAlignmentGoodEvil(oPC);
// Then we turn those values into a single number that should correspond to // one of the constants we have defined above. int nAlignment = nGoodEvil*6 + nLawChaos;
itemproperty ipACBonusVersus;
// The switch/case allows us to avoid a lot of tedious // if (nAlignment = ALIGNMENT_SOME_THING) clauses. Note the curly braces // enclosing the case staements, the colon after each case, and the // break statement after each case is finished. // // As you can see, each alignment gets a different bonus item property when // the item is activated. switch (nAlignment){
case ALIGNMENT_LAWFUL_GOOD: ipACBonusVersus = ItemPropertyACBonusVsSAlign( IP_CONST_ALIGNMENT_CE, 3 ); break;
case ALIGNMENT_NEUTRAL_GOOD: ipACBonusVersus = ItemPropertyACBonusVsAlign( IP_CONST_ALIGNMENTGROUP_EVIL, 2 ); break;
case ALIGNMENT_CHAOTIC_GOOD: ipACBonusVersus = ItemPropertyACBonusVsSAlign( IP_CONST_ALIGNMENT_LE, 3 ); break;
case ALIGNMENT_LAWFUL_NEUTRAL: ipACBonusVersus = ItemPropertyACBonusVsAlign( IP_CONST_ALIGNMENTGROUP_CHAOTIC, 2 ); break;
case ALIGNMENT_TRUE_NEUTRAL: ipACBonusVersus = ItemPropertyACBonus( 1 ); break;
case ALIGNMENT_CHAOTIC_NEUTRAL: ipACBonusVersus = ItemPropertyACBonusVsAlign( IP_CONST_ALIGNMENTGROUP_LAWFUL, 2 ); break;
case ALIGNMENT_LAWFUL_EVIL: ipACBonusVersus = ItemPropertyACBonusVsSAlign( IP_CONST_ALIGNMENT_CG, 3 ); break;
case ALIGNMENT_NEUTRAL_EVIL: ipACBonusVersus = ItemPropertyACBonusVsAlign( IP_CONST_ALIGNMENTGROUP_GOOD, 2 ); break;
case ALIGNMENT_CHAOTIC_EVIL: ipACBonusVersus = ItemPropertyACBonusVsSAlign( IP_CONST_ALIGNMENT_LG, 3 ); break;
default: break; }
// What we do here is then check that an actual item property has been set // up. Which it should have been, unless the default statement was executed // (it is the only one which doesn't set anything up) which probably // indicates a bug. // // Note here that we're adding an item property without checking whether // another item property of the same type exists. That's okay for this // example, but if you wanted to add an item property for real you would // want to include 'x2_inc_itemprop' and use IPSafeAddItemProperty instead, // because it has a provision to check for stacking. if ( GetIsItemPropertyValid( ipACBonusVersus ) ) { WriteTimestampedLogEntry("Item property can be added. Attempting to add it now."); AddItemProperty( DURATION_TYPE_TEMPORARY, ipACBonusVersus, oItem, TurnsToSeconds( 2 ) ); } else WriteTimestampedLogEntry("Item property can't be added."); }
|
|
|
|
 |
|
Selmak
|
Posted: Mon, Feb 07 2011, 17:59 PM |
|
|

Player
Joined: 17 Dec 2004
|
Check that a starting character has the right ability scores for their raceSo if you're writing a module and you don't want it to enforce legal characters for some reason, but you want it to check that new characters are legit in terms of ability scores, here's an example of the sort of check you could use: Code: // So we have here a function outside the main script. It helps us to avoid // doing the same code over and over.
int GetBuyPoints( int nHAS ){
// We're finding the number of buy points for one ability. // Should be 0 when initialised anyway, but can't hurt. int nBuyPoints = 0;
// So long as this Human Ability Score is above 8... while ( nHAS > 8 ) { // If it is indeed more than 16, knock one point off the score and // give us 3 Buy Points, please! if ( nHAS > 16 ) { nHAS--; nBuyPoints = nBuyPoints + 3; } // If it is not bigger than 16 but it is bigger than 14, we // want 2 Buy Points, please! else if ( nHAS > 14 ) { nHAS--; nBuyPoints = nBuyPoints + 2; } // Otherwise, we want one Buy Point for one ability point off. else { nHAS--; nBuyPoints++; } }
// This tells the calling function how many buy points were given back. return nBuyPoints; }
void main() {
// Going to need to store some things. int nStr, nDex, nCon, nWis, nInt, nCha; int nRace, nBonus, nTotalBuyPoints;
// Who last used this object? object oPC = GetLastUsedBy();
// Is this PC not level 1? if ( GetHitDice( oPC ) != 1 ) // We don't need to examine this PC any further. return;
nStr = GetAbilityScore( oPC, ABILITY_STRENGTH, TRUE ); nDex = GetAbilityScore( oPC, ABILITY_DEXTERITY, TRUE ); nCon = GetAbilityScore( oPC, ABILITY_CONSTITUTION, TRUE ); nWis = GetAbilityScore( oPC, ABILITY_WISDOM, TRUE ); nInt = GetAbilityScore( oPC, ABILITY_INTELLIGENCE, TRUE ); nCha = GetAbilityScore( oPC, ABILITY_CHARISMA, TRUE );
// What race is this PC? nRace = GetRacialType( oPC );
// Is that race valid i.e. does it exist? if ( nRace == RACIAL_TYPE_INVALID ) SendMessageToPC( oPC, "Your race is invalid!" );
// Now we subtract the bonuses or penalties from the ability scores. // This leaves us with the ability scores a human character would have. // N.B. If the character has a subrace then we need to lookup the values // for that subrace, this only covers base races.
nBonus = StringToInt( Get2DAString("racialtypes", "StrAdjust", nRace) ); nStr = nStr - nBonus;
nBonus = StringToInt( Get2DAString("racialtypes", "DexAdjust", nRace) ); nDex = nDex - nBonus;
nBonus = StringToInt( Get2DAString("racialtypes", "ConAdjust", nRace) ); nCon = nCon - nBonus;
nBonus = StringToInt( Get2DAString("racialtypes", "WisAdjust", nRace) ); nWis = nWis - nBonus;
nBonus = StringToInt( Get2DAString("racialtypes", "IntAdjust", nRace) ); nInt = nInt - nBonus;
nBonus = StringToInt( Get2DAString("racialtypes", "ChaAdjust", nRace) ); nCha = nCha - nBonus;
// We set our Total Buy Points to 0. nTotalBuyPoints = 0; // We call the function above using the human ability scores as parameters, // and get the total. nTotalBuyPoints = GetBuyPoints( nStr ) + GetBuyPoints( nDex ) + GetBuyPoints( nCon ); nTotalBuyPoints = nTotalBuyPoints + GetBuyPoints( nWis ) + GetBuyPoints( nInt ) + GetBuyPoints( nCha );
// The total should be 30 for a level 1 character. if ( nTotalBuyPoints != 30 ) SendMessageToPC( oPC, "Your ability scores are incorrect." );
} The idea here is first to subtract the racial bonuses and penalties, which are added after your bought points, even though during character generation they are already added when you reach the ability scores screen. This brings the ability scores back to what they would be if the human race were selected. Next the ability points are 'sold off' at the rate they were bought, until you get to 8, which is the start point for every ability score for a human before the 30 points are spent. The main function calls the GetBuyPoints function (saving me having to write the same code just with different variables) and totals all the buy points. If it's not a total of 30, the ability points aren't legitimate for the base race which has been selected. Obviously this does not take into account any subrace modifiers, as these aren't defined in racialtypes.2da, so you would need to get it to look up the modifiers that have been defined in the module's subrace functions. Easy enough done with a case/switch statement. 
|
|
|
|
 |
|
Page 1 of 1
|
[ 4 posts ] |
|
Who is online |
Users browsing this forum: No registered users and 3 guests |
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum
|
|