//:://///////////////////////////////////////////// //:: ka_i_weapon.nss //:: Copyright (c) 2003 Karl Adamaitis //::////////////////////////////////////////////// /* common defines and functionality for placeable weapon objects. */ //::////////////////////////////////////////////// //:: Created By: Karl Adamaitis //:: Created On: Aug 30, 2002 //::////////////////////////////////////////////// // int StartingConditional() { return 1; } // // common range definitions // int KA_MAX_WEAPON_RANGE = 280; int KA_MIN_WEAPON_RANGE = 20; float KA_WEAPON_BLAST_RADIUS = 12.0; // meters // DAMAGE - amount of variable damage this placeable weapon inflicts. int WEAPON_DAMAGE = d6(3) + 2; // // prototypes // // move placeable weapon object forwards/backwards a distance void KaWeaponMove(object oOBJ, object oPC, int iDistMove); // adjust placeable weapon object's range forwards/backwards a distance void KaWeaponRange(object oOBJ, object oPC, int iDistChangeRange); // rotate placeable weapon object clockwise/counter-clockwise void KaWeaponRotate(object oOBJ, object oPC, float fDegrees); // fire the placeable weapon. if a commander is given then the target is an enemy within the striking area of the catapult's projectile. void KaWeaponFire(object oOBJ, object oCommander = OBJECT_INVALID, object oTarget = OBJECT_INVALID, int iMaxVariation = 20); // separate function for area effect in order to properly // time the impact with the area-damage event. void KaPrivWeaponAreaEffect(location locImpact, float fRadius, effect eEffect); // adjust the location within a variance of meters location KaPrivCreateLocationVariance(location loc, int iMeters); // string table function string StringTable(int nStringRef, string sLCID = "en"); // // functions: // void KaWeaponMove(object oOBJ, object oPC, int iDistMove) { vector vecCurrentPosition = GetPosition(oOBJ); float fCurrentFacing = GetFacing(oOBJ); vector vecDifference; // adjust the distance (positive or negative integer values) vecDifference.x = iDistMove * cos(fCurrentFacing); vecDifference.y = iDistMove * sin(fCurrentFacing); vector vecNewPosition; vecNewPosition.x = vecCurrentPosition.x + vecDifference.x; vecNewPosition.y = vecCurrentPosition.y + vecDifference.y; // create the new location (by definition; area, vector, and orientation). location locNewPosition = Location(GetArea(oOBJ), vecNewPosition, fCurrentFacing); // new position is calculated - get the tag for support of different weapon objects // NOTE: the tag string and blueprint resource string MUST be the same. use the // custom weapon objects created that have this condition set. otherwise, the // newly created object will be default without the resource definitions. string stagBlueprintRes = GetTag(oOBJ); // remove the current instance of weapon object and replace in new location DestroyObject(oOBJ); object oNewWeapon = CreateObject( OBJECT_TYPE_PLACEABLE, stagBlueprintRes, locNewPosition); // relaunch the conversation file with PC/object for further command processing // NOTE: requires command conversation file (give recursive behaviour). AssignCommand(oNewWeapon, ActionStartConversation(oPC, "", TRUE)); } void KaWeaponRange(object oOBJ, object oPC, int iDistChangeRange) { // get the current range value int iRange = GetLocalInt(oOBJ, "KA_WEAPON_RANGE"); // increment or decrement the range iRange = iRange + iDistChangeRange; // verify range of weapon is above minimum and below maximum allowed if (KA_MIN_WEAPON_RANGE > iRange) { // adjust the range to minimum distance iRange = KA_MIN_WEAPON_RANGE; // meters } else if (KA_MAX_WEAPON_RANGE < iRange) { // adjust the range to maximum distance iRange = KA_MAX_WEAPON_RANGE; // meters } AssignCommand(oOBJ, ActionSpeakString( StringTable(0) + //"Range: " IntToString(iRange) )); // set the local integer value - referenced when weapon is discharged AssignCommand(oOBJ, SetLocalInt(oOBJ, "KA_WEAPON_RANGE", iRange)); // relaunch the conversation file with PC/object for further command processing // NOTE: requires command conversation file (give recursive behaviour). AssignCommand(oOBJ, ActionStartConversation(oPC, "", TRUE)); } void KaWeaponRotate(object oOBJ, object oPC, float fDegrees) { float fCurrent = GetFacing(oOBJ); float fNew = fCurrent - fDegrees; // add/remove degrees (i.e. clockwise/counter-clockwise) if (fNew <= 0.0) { // rotation has come about full-circle fNew += 360.0; } SetFacing(fNew); // relaunch the conversation file with PC/object for further command processing // NOTE: requires command conversation file (give recursive behaviour). AssignCommand(oOBJ, ActionStartConversation(oPC, "", TRUE)); } void KaWeaponFire( object oOBJ, object oCommander, // OBJECT_INVALID object oTarget, // OBJECT_INVALID int iMaxVariation) // 20m { if (!GetIsObjectValid(oOBJ)) { WriteTimestampedLogEntry("KA_ERROR: KaWeaponFire - invalid object oOBJ"); return; // error } // verify that the maximum variation is valid if (iMaxVariation <= 1) { // too small, increase and log an error iMaxVariation = 2; WriteTimestampedLogEntry("KA_ERROR: KaWeaponFire - the maximum variation is too small, setting to minimum of: " + IntToString(iMaxVariation)); } // calculate the impact location based on orientation and range of weapon float fCurrentFacing = GetFacing(oOBJ); int iRange = GetLocalInt(oOBJ, "KA_WEAPON_RANGE"); if (KA_MIN_WEAPON_RANGE >= iRange) { // adjust the range to minimum distance iRange = KA_MIN_WEAPON_RANGE; // meters } vector vecDelta; vecDelta.x = iRange * cos(fCurrentFacing); vecDelta.y = iRange * sin(fCurrentFacing); vector vecImpact; vector vecPosition = GetPosition(oOBJ); vecImpact.x = vecPosition.x + vecDelta.x; vecImpact.y = vecPosition.y + vecDelta.y; // create the impact location from area location locImpact = Location(GetArea(oOBJ), vecImpact, fCurrentFacing); if (GetIsObjectValid(oTarget)) { // variation for the impact location vector vecTarget = GetPosition(oTarget); int iXPosNeg = 1; // x coordinate positive/negative targeting adjustment if (d2(1) == 1) { iXPosNeg = -1; // random negative adjustment override } int iYPosNeg = 1; // y coordinate positive/negative targeting adjustment if (d2(1) == 1) { iYPosNeg = -1; // random negative adjustment override } // create the new target impact location randomizing x and y coordinates separately vecTarget.x = vecTarget.x + (iXPosNeg * Random(iMaxVariation)); vecTarget.y = vecTarget.y + (iYPosNeg * Random(iMaxVariation)); // create the new location for the impact based on variation calculated above // NOTE: using target's area, vector for location and facing direction from weapon. locImpact = Location(GetArea(oTarget), vecTarget, fCurrentFacing); } // override the impact location if a commander is given and the catapult's commander // is enemies with an object within a location. used for PC's firing catapult to // support friendlies. this is performed by using the previously calculated location // of impact and finding the nearest enemy within range to the catapult's commander. if (GetIsObjectValid(oCommander)) { // NOTE: do not use GetNearestCreatureToLocation with REPUTATION as causes AV in // module on patch 1.29, nor does this make sense as the location does not provide // data member in struct that represents the reputation of commander. int iCount = 1; int iMaxCount = 4; int b_enemy = FALSE; // TRUE to terminate search object oEnemyFound = GetNearestCreatureToLocation( CREATURE_TYPE_IS_ALIVE, TRUE, locImpact, iCount ); while (GetIsObjectValid(oEnemyFound) && (iCount <= iMaxCount) && (FALSE == b_enemy)) { // verify if this really is an enemy of the catapult's commander b_enemy = GetIsEnemy(oEnemyFound, oCommander); if (TRUE == b_enemy) { // verify that the found enemy is within the maximum allowed range from impact location location locEnemy = GetLocation(oEnemyFound); float fDist = GetDistanceBetweenLocations(locImpact, locEnemy); float fMaxVariation = IntToFloat(iMaxVariation); if (fDist < fMaxVariation) { // enemy is within maximum variation from impact location, override the impact location // with a new impact location that is close to the new target. location locOrigImpact = locImpact; locImpact = KaPrivCreateLocationVariance( locOrigImpact, // <-- original calculated impact location 3 // <-- maximum variance from the new found enemy target within max variance of impact ); } else { // no enemy was found that meets all conditions, leave the bool // to true to terminate the search and just fire the catapult. // the nearest enemy is outside of the maximum distance from the // impact location of the catapult as aimed by the catapult's // commander. the commander must re-aim the catapult. } } // increment the count looking for another enemy iCount++; oEnemyFound = GetNearestCreatureToLocation( CREATURE_TYPE_IS_ALIVE, TRUE, locImpact, iCount ); } } float fPauseBefore = 1.1; AssignCommand(oOBJ, ClearAllActions()); AssignCommand(oOBJ, DelayCommand(fPauseBefore, ActionCastSpellAtLocation( SPELL_FIREBALL, locImpact, METAMAGIC_MAXIMIZE, TRUE, PROJECTILE_PATH_TYPE_BALLISTIC, TRUE))); } location KaPrivCreateLocationVariance(location loc, int iMeters) { if (0 == iMeters) { // no variance return loc; } // validate the location's area if (!GetIsObjectValid(GetAreaFromLocation(loc))) { WriteTimestampedLogEntry("KA_ERROR: KaPrivCreateLocationVariance (ka_i_weapon) - invalid area object for location"); return loc; } vector vecPos = GetPositionFromLocation(loc); object oArea = GetAreaFromLocation(loc); // validate returned vector position if (vecPos.x == 0.0 && vecPos.y == 0.0 && vecPos.z == 0.0) { WriteTimestampedLogEntry("KA_ERROR: KaPrivCreateLocationVariance (ka_i_weapon) - invalid vector returned from location"); return loc; } // randomize within given meter(s) of original vector position float fX = (IntToFloat(Random(10) * iMeters) / 10.0); int iXPosNeg = Random(2); if (iXPosNeg) { // positive variance vecPos.x += fX; //WriteTimestampedLogEntry("positive variance X - " + FloatToString(fX)); } else if (vecPos.x > fX) { // negative variance vecPos.x -= fX; //WriteTimestampedLogEntry("negative variance X - " + FloatToString(fX)); } float fY = (IntToFloat(Random(10) * iMeters) / 10.0); int iYPosNeg = Random(2); if (iYPosNeg) { // positive variance vecPos.y += fY; //WriteTimestampedLogEntry("positive variance Y - " + FloatToString(fY)); } else if (vecPos.y > fY) { // negative variance vecPos.y -= fY; //WriteTimestampedLogEntry("negative variance Y - " + FloatToString(fY)); } location locReturn = Location(oArea, vecPos, 0.0); return locReturn; } //////////////////////////////////////////////////////////////////////////////// // string table at end of file ///////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////// //////////////////////////////// /////////////////////////// ////////////////////// ///////////////// // // // Locale Language // "en" English // "it" Italian // "nl" Dutch // "es" Spanish // "fr" French // "de" German // // int - the string's associated reference in the sudo string table. // string - locale identifier, by default this is the English language. // string StringTable(int nStringRef, string sLCID) { string sString = "Error: Invalid string reference in localized string table in ka_i_weapon, for string reference: " + IntToString(nStringRef); // check the local of the language if ("en" == sLCID) { // English // switch on the string reference from parameter switch (nStringRef) { // // KaWeaponRange // case 0: sString = "Range: "; break; } } return sString; }