#include "adv_include" #include "inc_helper_funcs" #include "cbt_constant" #include "gun_configure" ////////////////////////////////////////////////////// // D20 COMBAT SYSTEM OVERRIDE CONSTANT DECLARATIONS // ////////////////////////////////////////////////////// // Unload ammo that is currently inside a gun void GUN_UnloadAmmo(object oPC, object oGun); // Load ammo into a gun. Will immobilize oPC for fDelay amount of time. // Not intended to be called by itself. Use GUN_ReloadAmmo to perform the reloading // iAmmoOverride = GUN_AMMUNITION_PRIORITY_* void GUN_LoadAmmo(object oPC, object oGun, int iAmmoOverride = -1); // Returns the number of bullets a gun's magazine can hold at one time. //int GUN_GetGunMagazineSize(object oItem); // Reloads ammo into oPC's gun. // bDualWield should be TRUE if oPC is trying to reload two guns. FALSE otherwise. void GUN_ReloadAmmo(object oPC, object oGun, int bDualWield = FALSE); // Switches oWeapon's mode of fire. // Semi-Automatic : Standard mode - One click fires one shot // 3-Round Burst : Alternate mode - One click fires three shots // Note that dual wielding first twice as many shots. (I.E: 6 shots will be fired if set on 3-Round Burst mode) // If there is not enough ammo to fire all shots, as many as are possible will be fired. void GUN_SwitchWeaponMode(object oWeapon); void GUN_UnloadAmmo(object oPC, object oGun) { string sResref; int iAmmo = GUN_GetGunInfo(oGun).iAmmoType; int iAmmoPriority = GetLocalInt(oGun, GUN_TEMP_AMMO_PRIORITY); // Weapon uses no ammo, end. if(iAmmo <= 0){return;} object oAmmo = GetItemInSlot(INVENTORY_SLOT_ARROWS, oPC); // Ammo not valid. Just end if(!GetIsObjectValid(oAmmo)) return; // Check the ammo resref to see if we're dealing with basic ammo, or another type sResref = GetResRef(oAmmo); // Basic bullets if(sResref == GUN_FIREARM_MAGAZINE_RESREF) { sResref = GUN_NORMAL_AMMO_BOX_PREFIX + IntToString(iAmmo); } // Enhanced bullets else if(sResref == GUN_ENHANCED_FIREARM_MAGAZINE_RESREF) { sResref = GUN_ENHANCED_AMMO_BOX_PREFIX + IntToString(iAmmo); } // Incendiary bullets else if(sResref == GUN_INCENDIARY_FIREARM_MAGAZINE_RESREF) { sResref = GUN_INCENDIARY_AMMO_BOX_PREFIX + IntToString(iAmmo); } // Prevents the ammo from firing the module OnUnequip script // which would duplicate the ammo rounds SetLocalInt(oAmmo, GUN_TEMP_UNLOADING_AMMO, 1); int iStackSize = GetItemStackSize(oAmmo); CreateItemOnObjectSafe(sResref, oPC, iStackSize); DestroyObject(oAmmo); // Remove persistent bullet counter DeleteLocalInt(oGun, GUN_MAGAZINE_BULLET_COUNT); UpdateItemName(oGun); } void GUN_LoadAmmo(object oPC, object oGun, int iAmmoOverride = -1) { object oDatabase = GetItemPossessedBy(oPC, PC_DATABASE); struct GunInfoStruct stGunInfo = GUN_GetGunInfo(oGun); int iAmmoPriority = GetLocalInt(oGun, GUN_TEMP_AMMO_PRIORITY); // An ammo override was set - we're now looking this type of ammo instead of what the gun is set to look for. if(iAmmoOverride > -1) iAmmoPriority = iAmmoOverride; string sTag; string sMagazineResref; // Basic ammo is priority here if(iAmmoPriority == GUN_AMMUNITION_PRIORITY_BASIC) { sMagazineResref = GUN_FIREARM_MAGAZINE_RESREF; sTag = GUN_NORMAL_AMMO_BOX_PREFIX + IntToString(stGunInfo.iAmmoType); } // Enhanced ammo is priority here else if(iAmmoPriority == GUN_AMMUNITION_PRIORITY_ENHANCED) { sMagazineResref = GUN_ENHANCED_FIREARM_MAGAZINE_RESREF; sTag = GUN_ENHANCED_AMMO_BOX_PREFIX + IntToString(stGunInfo.iAmmoType); } // Incendiary ammo is priority here else if(iAmmoPriority == GUN_AMMUNITION_PRIORITY_INCENDIARY) { sMagazineResref = GUN_INCENDIARY_FIREARM_MAGAZINE_RESREF; sTag = GUN_INCENDIARY_AMMO_BOX_PREFIX + IntToString(stGunInfo.iAmmoType); } // Item equipped doesn't match the gun. Stop the loading if(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC) != oGun) { DeleteLocalInt(oPC, GUN_TEMP_RELOADING_GUN); return; } // PC decided to cancel reloading if(GetLocalInt(oPC, GUN_TEMP_RELOADING_GUN) == 0) return; object oAmmo = GetFirstItemInInventory(oPC); int iAmmoFound; // As long as more ammo items are found and clip isn't full, // keep converting ammo boxes to magazines while(GetIsObjectValid(oAmmo) && iAmmoFound < stGunInfo.iMagazineSize) { // Currently selected item matches the tag of the ammo we're looking for if(GetTag(oAmmo) == sTag) { int iStackSize = GetItemStackSize(oAmmo); if(iStackSize + iAmmoFound > stGunInfo.iMagazineSize) { iStackSize = iStackSize - (stGunInfo.iMagazineSize - iAmmoFound); iAmmoFound = stGunInfo.iMagazineSize; SetItemStackSize(oAmmo, iStackSize); break; } else if(iStackSize + iAmmoFound <= stGunInfo.iMagazineSize) { DestroyObject(oAmmo); iAmmoFound = iAmmoFound + iStackSize; } } // Look for another ammo box oAmmo = GetNextItemInInventory(oPC); } // Found no enhanced ammunition. Switch to basic ammunition and run this function again. if(iAmmoFound == 0) { if(iAmmoPriority == GUN_AMMUNITION_PRIORITY_ENHANCED || iAmmoPriority == GUN_AMMUNITION_PRIORITY_INCENDIARY) { GUN_LoadAmmo(oPC, oGun, GUN_AMMUNITION_PRIORITY_BASIC); return; } } // Create an ammo magazine on PC and equip it automatically oAmmo = CreateItemOnObjectSafe(sMagazineResref, oPC, iAmmoFound); // Prevent exploits by making the loaded ammo cursed SetItemCursedFlag(oAmmo, TRUE); SetLocalInt(oAmmo, GUN_TEMP_AMMO_TYPE, stGunInfo.iAmmoType); // Store bullet count on the firearm SetLocalInt(oGun, GUN_MAGAZINE_BULLET_COUNT, iAmmoFound); // October 16, 2010 - Added a check to make sure the ammo created is on the inventory of the player. // Otherwise, PCs could exploit this by reloading constantly with a full inventory (causing the created ammo to drop to the ground) // PCs could then pick up the ammo and equip it - bypassing ammo caps on firearms if(GetItemPossessor(oAmmo) != oPC) { DestroyObject(oAmmo); CreateItemOnObjectSafe(sTag, oPC, iAmmoFound); FloatingTextStringOnCreature(ColorTokenRed() + "Reloading failed. Your inventory is too full or you have no ammo.", oPC, FALSE); DeleteLocalInt(oPC, GUN_TEMP_RELOADING_GUN); } else { // Equip the ammo into the appropriate slot AssignCommand(oPC, ActionEquipItem(oAmmo, INVENTORY_SLOT_ARROWS)); DelayCommand(0.5, DeleteLocalInt(oPC, GUN_TEMP_RELOADING_GUN)); // Mark ammunition type (basic, enhanced, etc) SetLocalInt(oGun, GUN_TEMP_AMMO_LOADED_TYPE, iAmmoPriority); } UpdateItemName(oGun); } void GUN_ReloadAmmo(object oPC, object oGun, int bDualWield = FALSE) { object oDatabase = GetItemPossessedBy(oPC, PC_DATABASE); // Time it takes to reload this weapon float fDelay; string sResref = GetResRef(oGun); struct GunInfoStruct stGunInfo = GUN_GetGunInfo(oGun); // PC can't reload non-guns if(stGunInfo.iAmmoType == 0) { FloatingTextStringOnCreature(ColorTokenRed() + "Cannot reload that weapon!", oPC, FALSE); return; } // PC cancels reloading if he or she selects it again if(GetLocalInt(oPC, GUN_TEMP_RELOADING_GUN) == 1) { FloatingTextStringOnCreature(ColorTokenPurple() + "Reloading canceled.", oPC, FALSE); DeleteLocalInt(oPC, GUN_TEMP_RELOADING_GUN); // Allow PC to be commanded again SetCommandable(TRUE, oPC); return; } float fAnimationSpeed; // Determine reloading speed switch(stGunInfo.iReloadSetting) { case IP_CONST_FIREARM_RELOAD_SPEED_VERY_SLOW: { fDelay = GUN_RELOAD_SPEED_VERY_SLOW; fAnimationSpeed = 0.4; break; } case IP_CONST_FIREARM_RELOAD_SPEED_SLOW: { fDelay = GUN_RELOAD_SPEED_SLOW; fAnimationSpeed = 0.5; break; } case IP_CONST_FIREARM_RELOAD_SPEED_MEDIUM: { fDelay = GUN_RELOAD_SPEED_MEDIUM; fAnimationSpeed = 0.6; break; } case IP_CONST_FIREARM_RELOAD_SPEED_FAST: { fDelay = GUN_RELOAD_SPEED_FAST; fAnimationSpeed = 0.7; break; } case IP_CONST_FIREARM_RELOAD_SPEED_VERY_FAST: { fDelay = GUN_RELOAD_SPEED_VERY_FAST; fAnimationSpeed = 0.9; break; } } // First unload the ammo GUN_UnloadAmmo(oPC, oGun); // Mark player as reloading (prevents them from firing shots and other actions) SetLocalInt(oPC, GUN_TEMP_RELOADING_GUN, 1); // Play animations effect eSound = EffectVisualEffect(CBT_OVR_SOUND_GUN_RELOAD); int iAnimation; float fAnimationLength; float fSoundDelay; location lLocation = GetLocation(oPC); if (bDualWield) { //fAnimationSpeed = 0.7; fAnimationLength = 0.0; fSoundDelay = 2.8; iAnimation = CBT_OVR_ANIMATION_RELOAD_DUAL; } else { //fAnimationSpeed = 0.7; fAnimationLength = 0.0; iAnimation = CBT_OVR_ANIMATION_RELOAD; } // Player will be set to uncommandable during the time it takes to reload the weapon. AssignCommand(oPC, ClearAllActions(TRUE)); AssignCommand(oPC, PlayAnimation(iAnimation, fAnimationSpeed, fAnimationLength)); AssignCommand(oPC, SetCommandable(FALSE, oPC)); AssignCommand(oPC, DelayCommand(fDelay, SetCommandable(TRUE, oPC))); ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eSound, lLocation); if (bDualWield) { DelayCommand(fSoundDelay, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eSound, GetLocation(oPC))); } DelayCommand(fDelay, GUN_LoadAmmo(oPC, oGun)); } void GUN_SwitchWeaponMode(object oWeapon) { object oPC = GetItemPossessor(oWeapon); string sResref = GetResRef(oWeapon); struct GunInfoStruct stGunInfo = GUN_GetGunInfo(oWeapon); int iCurMode = GetLocalInt(oWeapon, GUN_TEMP_CURRENT_RATE_OF_FIRE); // Weapon cannot change rate of fire or the rate of fire // item property is missing if(stGunInfo.iRateOfFire == -1) { SendMessageToPC(oPC, ColorTokenRed() + "You cannot change this weapon's mode of fire."); return; } // Semi-Auto cannot change rate of fire else if(stGunInfo.iRateOfFire == IP_CONST_FIREARM_ROF_SEMI_AUTOMATIC) { SendMessageToPC(oPC, ColorTokenRed() + "This weapon has no alternative modes of fire."); return; } // Automatic can switch between Semi-Auto and 3-Round Burst else if(stGunInfo.iRateOfFire == IP_CONST_FIREARM_ROF_AUTOMATIC) { // Currently in Semi-Auto mode. Switch to Automatic. if(iCurMode == 0) { SetLocalInt(oWeapon, GUN_TEMP_CURRENT_RATE_OF_FIRE, GUN_WEAPON_MODE_THREE_ROUND_BURST); SendMessageToPC(oPC, ColorTokenPurple() + "Rate of Fire: 3-Round Burst" + ColorTokenEnd()); } // Currently in 3-Round Burst mode. Switch to Semi-Auto. else { DeleteLocalInt(oWeapon, GUN_TEMP_CURRENT_RATE_OF_FIRE); SendMessageToPC(oPC, ColorTokenPurple() + "Rate of Fire: Semi-Auto" + ColorTokenEnd()); } } } void GUN_SwitchAmmoPriority(object oWeapon) { object oPC = GetItemPossessor(oWeapon); string sResref = GetResRef(oWeapon); int iCurrentAmmoPriority = GetLocalInt(oWeapon, GUN_TEMP_AMMO_PRIORITY); struct GunInfoStruct stGunInfo = GUN_GetGunInfo(oWeapon); // Currently using basic ammunition - prioritize enhanced ammo if(iCurrentAmmoPriority == 0 && stGunInfo.bEnhancedAvailable) { SetLocalInt(oWeapon, GUN_TEMP_AMMO_PRIORITY, GUN_AMMUNITION_PRIORITY_ENHANCED); SendMessageToPC(oPC, ColorTokenPurple() + "Ammunition Priority: Enhanced" + ColorTokenEnd()); } // Currently using enhanced or basic ammunition (if enhanced is not available) - prioritize incendiary ammo else if(iCurrentAmmoPriority != 2 && stGunInfo.bIncendiaryAvailable) { SetLocalInt(oWeapon, GUN_TEMP_AMMO_PRIORITY, GUN_AMMUNITION_PRIORITY_INCENDIARY); SendMessageToPC(oPC, ColorTokenPurple() + "Ammunition Priority: Incendiary" + ColorTokenEnd()); } // Prioritize basic ammo on all other circumstances else { DeleteLocalInt(oWeapon, GUN_TEMP_AMMO_PRIORITY); SendMessageToPC(oPC, ColorTokenPurple() + "Ammunition Priority: Basic" + ColorTokenEnd()); } } // Error checking //void main(){}