Beacon Weapon Change Tutorial for Multi-Projectile Weapons By Sanguinus (based on tutorial by BadShot which can be found at http://www.mage-tower.com/BadShot/) Add these functions to the bottom of item.cs: function MPWBP(%data, %obj) { if ((%obj.client.player.projectilenumber == 0) || (%obj.client.player.projectilenumber $= "")) // Default projectile bottomPrint(%obj.client, $WeaponDescription[%obj.getMountedImage($WeaponSlot).item.getName()], 5); else if (%obj.client.player.projectilenumber == 1) // ADDITIONAL projectile 1 bottomPrint(%obj.client, $Projectile1Description[%obj.getMountedImage($WeaponSlot).item.getName()], 5); else if (%obj.client.player.projectilenumber == 2) // ADDITIONAL projectile 2 bottomPrint(%obj.client, $Projectile2Description[%obj.getMountedImage($WeaponSlot).item.getName()], 5); else if (%obj.client.player.projectilenumber == 3) // ADDITIONAL projectile 3 bottomPrint(%obj.client, $Projectile3Description[%obj.getMountedImage($WeaponSlot).item.getName()], 5); else if (%obj.client.player.projectilenumber == 4) // ADDITIONAL projectile 4 bottomPrint(%obj.client, $Projectile4Description[%obj.getMountedImage($WeaponSlot).item.getName()], 5); else if (%obj.client.player.projectilenumber == 5) // ADDITIONAL projectile 5 bottomPrint(%obj.client, $Projectile5Description[%obj.getMountedImage($WeaponSlot).item.getName()], 5); else // Default weapon/projectile message bottomPrint(%obj.client, $WeaponDescription[%obj.getMountedImage($WeaponSlot).item.getName()], 5); } which handles bottomprinting the descriptions (NOTE: it is required that any bottomprint you currently have in your mod is removed) and function ChangeProjectileNumber(%data, %obj) { %numaddproj = %obj.client.player.getMountedImage($WeaponSlot).numaddprojectiles; if (%obj.client.player.projectilenumber $= "") %obj.client.player.projectilenumber = 1; else if (%obj.client.player.projectilenumber < %numaddproj) %obj.client.player.projectilenumber += 1; else if (%obj.client.player.projectilenumber >= %numaddproj) %obj.client.player.projectilenumber = 0; else {} } which changes the projectile number now, edit the onbeacon function like so (armor beacon functions won't work correctly if you've got them in and you try using this tutorial, so don't bother... send me an e-mail or post any questions about using it with other keys) I DO reccommend reading this through so you have an understanding of what I did... but you can probably get away with C&Ping it over your normal beacon function... function Beacon::onUse(%data, %obj) { %numaddproj = %obj.client.player.getMountedImage($WeaponSlot).numaddprojectiles; // look for 3 meters along player's viewpoint for interior or terrain %searchRange = 3.0; %mask = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType; // get the eye vector and eye transform of the player %eyeVec = %obj.getEyeVector(); %eyeTrans = %obj.getEyeTransform(); // extract the position of the player's camera from the eye transform (first 3 words) %eyePos = posFromTransform(%eyeTrans); // normalize the eye vector %nEyeVec = VectorNormalize(%eyeVec); // scale (lengthen) the normalized eye vector according to the search range %scEyeVec = VectorScale(%nEyeVec, %searchRange); // add the scaled & normalized eye vector to the position of the camera %eyeEnd = VectorAdd(%eyePos, %scEyeVec); // see if anything gets hit %searchResult = containerRayCast(%eyePos, %eyeEnd, %mask, 0); if(!%searchResult ) { // no terrain/interior collision within search range if((%obj.inv[%data.getName()] > 0) && (%numaddproj $= "") || (%numaddproj = 0)) { messageClient(%obj.client, 'MsgBeaconNoSurface', '\c2Cannot place beacon. Too far from surface.'); return 0; } else { messageClient(%obj.client, 'MsgBeaconNoSurface', '\c2Cannot place beacon, changing weapon mode instead.'); ChangeProjectileNumber(%data, %obj); MPWBP(%data, %obj); return 0; } } else { %searchObj = GetWord(%searchResult, 0); if(%searchObj.getType() & ($TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType) ) { // if there's already a beacon where player is aiming, switch its type // otherwise, player can't deploy a beacon there if(%searchObj.getDataBlock().getName() $= DeployedBeacon) { switchBeaconType(%searchObj); return 0; } else { if((%numaddproj $= "") || (%numaddproj = 0)) { messageClient(%obj.client, 'MsgBeaconNoSurface', '\c2Cannot place beacon. Not a valid surface.'); return 0; } else { messageClient(%obj.client, 'MsgBeaconNoSurface', '\c2Cannot place beacon, changing weapon mode instead.'); ChangeProjectileNumber(%data, %obj); MPWBP(%data, %obj); return 0; } } } else if(%obj.inv[%data.getName()] <= 0) { if((%numaddproj $= "") || (%numaddproj = 0)) { return 0; } else { messageClient(%obj.client, 'MsgBeaconNoSurface', '\c2You have no remaining beacons, changing weapon mode instead.'); ChangeProjectileNumber(%data, %obj); MPWBP(%data, %obj); return 0; } } } // newly deployed beacons default to "target" type if($TeamDeployedCount[%obj.team, TargetBeacon] >= $TeamDeployableMax[TargetBeacon]) { if((%numaddproj $= "") || (%numaddproj = 0)) { messageClient(%obj.client, 'MsgDeployFailed', '\c2Your team's control network has reached its capacity for this item.~wfx/misc/misc.error.wav'); return 0; } else { messageClient(%obj.client, 'MsgBeaconNoSurface', '\c2You have no remaining beacons, changing weapon mode instead.'); ChangeProjectileNumber(%data, %obj); MPWBP(%data, %obj); return 0; } } %terrPt = posFromRaycast(%searchResult); %terrNrm = normalFromRaycast(%searchResult); %intAngle = getTerrainAngle(%terrNrm); // getTerrainAngle() function found in staticShape.cs %rotAxis = vectorNormalize(vectorCross(%terrNrm, "0 0 1")); if (getWord(%terrNrm, 2) == 1 || getWord(%terrNrm, 2) == -1) %rotAxis = vectorNormalize(vectorCross(%terrNrm, "0 1 0")); %rotation = %rotAxis @ " " @ %intAngle; %obj.decInventory(%data, 1); %depBeac = new BeaconObject() { dataBlock = "DeployedBeacon"; position = VectorAdd(%terrPt, VectorScale(%terrNrm, 0.05)); rotation = %rotation; }; $TeamDeployedCount[%obj.team, TargetBeacon]++; %depBeac.playThread($AmbientThread, "ambient"); %depBeac.team = %obj.team; %depBeac.sourceObject = %obj; // give it a team target %depBeac.setTarget(%depBeac.team); MissionCleanup.add(%depBeac); } now add this to the weapon mount function found in weapons.cs %obj.client.player.projectilenumber = %obj.client.favproj[%this.item]; MPWBP(%data, %obj); so the func looks like this: if(%obj.getClassName() !$= "Player") return; %obj.client.player.projectilenumber = %obj.client.favproj[%this.item]; MPWBP(%data, %obj); and add this to the weapon unmount function found just beneath the mount: %obj.client.favproj[%this.item] = %obj.client.player.projectilenumber; so it looks like this: %obj.client.favproj[%this.item] = %obj.client.player.projectilenumber; %obj.client.setWeaponsHudActive(%this.item, 1);] also add this to the top of the Armor::damageobject function in player.cs %obj.client.favproj[%this.item] = %obj.client.player.projectilenumber; it's my kinda hacky way to save the right projectile when the player dies... then modify the shapebaseimagedata::onfire func in projectiles.cs to read like this (again, I reccommend reading, but you can C&P) function ShapeBaseImageData::onFire(%data, %obj, %slot) { //<-- Sanguinus -- 2 (next line and if/else block) %numaddproj = %obj.client.player.getMountedImage($WeaponSlot).numaddprojectiles; if (%numaddproj >= 1) { if (%obj.client.player.projectilenumber == 1) //First additional projectile { %datap = %data.projectile2; %datapt = %data.projectiletype2; } else if (%obj.client.player.projectilenumber == 2) //Second additional projectile { %datap = %data.projectile3; %datapt = %data.projectileType3; } else if (%obj.client.player.projectilenumber == 3) //Third additional projectile { %datap = %data.projectile4; %datapt = %data.projectileType4; } else if (%obj.client.player.projectilenumber == 4) //Fourth additional projectile { %datap = %data.projectile5; %datapt = %data.projectileType5; } else if (%obj.client.player.projectilenumber == 5) //Fifth additional projectile { %datap = %data.projectile6; %datapt = %data.projectileType6; } else //Default projectile { %datap = %data.projectile; %datapt = %data.projectileType; } } else { %datap = %data.projectile; %datapt = %data.projectileType; } %data.lightStart = getSimTime(); if( %obj.station $= "" && %obj.isCloaked() ) { if( %obj.respawnCloakThread !$= "" ) { Cancel(%obj.respawnCloakThread); %obj.setCloaked( false ); %obj.respawnCloakThread = ""; } else { if( %obj.getEnergyLevel() > 20 ) { %obj.setCloaked( false ); %obj.reCloak = %obj.schedule( 500, "setCloaked", true ); } } } if( %obj.client > 0 ) { %obj.setInvincibleMode(0 ,0.00); %obj.setInvincible( false ); // fire your weapon and your invincibility goes away. } %vehicle = 0; if(%data.usesEnergy) { if(%data.useMountEnergy) { %useEnergyObj = %obj.getObjectMount(); if(!%useEnergyObj) %useEnergyObj = %obj; %energy = %useEnergyObj.getEnergyLevel(); %vehicle = %useEnergyObj; } else %energy = %obj.getEnergyLevel(); if(%data.useCapacitor && %data.usesEnergy) { if( %useEnergyObj.turretObject.getCapacitorLevel() < %data.minEnergy ) { return; } } else if(%energy < %data.minEnergy) return; } if(%data.projectileSpread) { %vector = %obj.getMuzzleVector(%slot); %x = (getRandom() - 0.5) * 2 * 3.1415926 * %data.projectileSpread; %y = (getRandom() - 0.5) * 2 * 3.1415926 * %data.projectileSpread; %z = (getRandom() - 0.5) * 2 * 3.1415926 * %data.projectileSpread; %mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z); %vector = MatrixMulVector(%mat, %vector); //<-- Sanguinus -- 2 -- next 9 lines %p = new (%datapt)() { dataBlock = %datap; initialDirection = %vector; initialPosition = %obj.getMuzzlePoint(%slot); sourceObject = %obj; sourceSlot = %slot; vehicleObject = %vehicle; }; } else { //<-- Sanguinus -- 2 -- next 9 lines %p = new (%datapt)() { dataBlock = %datap; initialDirection = %obj.getMuzzleVector(%slot); initialPosition = %obj.getMuzzlePoint(%slot); sourceObject = %obj; sourceSlot = %slot; vehicleObject = %vehicle; }; } if (isObject(%obj.lastProjectile) && %obj.deleteLastProjectile) %obj.lastProjectile.delete(); %obj.lastProjectile = %p; %obj.deleteLastProjectile = %data.deleteLastProjectile; MissionCleanup.add(%p); // AI hook if(%obj.client) %obj.client.projectile = %p; if(%data.usesEnergy) { if(%data.useMountEnergy) { if( %data.useCapacitor ) { %vehicle.turretObject.setCapacitorLevel( %vehicle.turretObject.getCapacitorLevel() - %data.fireEnergy ); } else %useEnergyObj.setEnergyLevel(%energy - %data.fireEnergy); } else %obj.setEnergyLevel(%energy - %data.fireEnergy); } else %obj.decInventory(%data.ammo,1); return %p; } then the last step is to add a line with this syntax to the top of weapons.cs for EVERY weapon in your mod: $WeaponDescription["YourWepNameHere"] = "Description"; //this line acts as the description for the first projectile AND the weapon in general if it only has one projectile then add lines with this syntax as needed: $Projectile1Description["YourWepNameHere"] = "Secondary Projectile Description"; $Projectile2Description["YourWepNameHere"] = "Tertiary Projectile Description"; etc. Please give credit to me and badshot when you use this... thanks! (PUT BADSHOT FIRST!)