Mod Ideas/Killstick
NOTE:
Currently in the process of a complete recode, will post once complete
Description
The Killstick is based off of a weapon used in the online comic [Megatokyo].
How it works
Here's the basics: this weapon works much like the normal sniper rifle. however, when this weapon is fully charged, it consumes all ammo to create a huge explosion, simmilar to the ion cannnon. To charge the weapon you hold the trigger, pull trigger normally for normal shots
Unreal Script Code
I already have the majority of the code for this down.
General Description
This weapons combines code from at least 4 different weapons in UT2003: the zoom super shock rifle, the sniper rifle, the shield gun (charging), and the redeemer/ion cannon. I started by extending my main weapon class from zoom super shock rifle so the gun could zoom, this also allowed me to use the shock rifle beam code for the fire effect. I then started working on adding the charge function by looking at the code for the shield gun and grabbing bits of what i needed. I attempted to use the code from the shield gun for the charge effect, but things got weird (see code once it's up). I then looked at the sniper rilfe code and found what lines enabled headshots and copied those. Finally, i found the Mod Ideas/NukeRifles here and liked how he did the spawning of a dummy projectile to get the redeemer explosion, and i did the same.
Killstick.uc
//Created by Syntax_Error class Killstick extends ZoomSuperShockRifle config(user); //So weapon preference settings are stored alongside all the standard weapons. simulated function float ChargeBar() //this sets what the charge bar measures { return FMin(1,FireMode[0].HoldTime/KillstickFire(FireMode[0]).MaxHoldTime); } defaultproperties { ItemName="Killstick" //The text you see when you switch to the weapon and in various other places. IconMaterial=Material'InterfaceContent.Hud.SkinA' //Texture where the icon to be show in the weapon bar is. IconCoords=(X1=322,Y1=372,X2=444,Y2=462) //Position of icon in that texture. //vars for zoom reticle innerArrowsX=42.000000 innerArrowsY=42.000000 ArrowColor=(R=255,A=255) TargetColor=(B=255,G=255,R=255,A=255) NoTargetColor=(B=200,G=200,R=200,A=255) FocusColor=(B=126,G=90,R=71,A=215) ChargeColor=(B=255,G=255,R=255,A=255) RechargeOrigin=(X=600.000000,Y=330.000000) RechargeSize=(X=10.000000,Y=-180.000000) bShowChargingBar=True //shows charge bar FireModeClass(0)=KillstickFire FireModeClass(1)=KillstickZoom DrawScale=0.4 Mesh=mesh'mtWeapon.killstick' //What you see in 1st person. BobDamping=2.3 //How much the weapon moves about as you walk. PickupClass=class'KillstickPickup' EffectOffset=(X=100,Y=10,Z=7.5) //Where effects are drawn. Used for start location of InstantFire beam effects. //animations SelectAnim="Select" PutDownAnim="PutDown" DisplayFOV=60 PlayerViewOffset=(X=-3,Y=6,Z=-3) //Position of the weapon in 1st person PlayerViewPivot=(Pitch=0,Roll=0,Yaw=0) //Rotation of the model in 1st person. UV2Texture=Material'XGameShaders.WeaponEnvShader' //What does this do? AttachmentClass=class'KillstickAttachment'//third person mesh SelectSound=Sound'WeaponSounds.LinkGun.SwitchToLinkGun' SelectForce="SwitchToLightningGun" //The various 'Force' variables are for force feedback mice etc. AIRating=0.69 //How much bots want to use this weapon. CurrentRating=0.69 //priority of weapon Priority=13 DefaultPriority=13 InventoryGroup=9 //Which slot the weapon occupies. GroupOffset=4 //This must be different to all other weapons that occupy the same InventoryGroup. }
KillstickFire.uc
//Created by Syntax_Error //::TODO:: bigger beam on charged shot, adjust damage on charged shot //:::::::: no double charge/flash effects class KillstickFire extends ShockBeamFire; var sound ChargeSound; var Emitter ChargeEmitter; /********this is the code i used for the charge effect********* simulated function DestroyEffects() //destorys effects { if (chargingemitter != None) { chargingemitter.Destroy(); } Super.DestroyEffects(); } simulated function InitEffects() //initalises effects { if ( Level.NetMode != NM_DedicatedServer ) { ChargingEmitter= Spawn(class'KillstickChargeEffect'); //chargingemitter.mRegenPause = true; //::part of xEffects:: } Super.InitEffects(); } function DrawMuzzleFlash(Canvas Canvas) //draws and places charge effect { if (ChargeEmitter != None && HoldTime > 0.0 && !bNowWaiting) { ChargeEmitter.SetLocation( Weapon.GetEffectStart() ); Canvas.DrawActor( ChargeEmitter, false, false, Weapon.DisplayFOV ); } if (FlashEmitter != None) { FlashEmitter.SetLocation( Weapon.GetEffectStart() ); if ( Weapon.WeaponCentered() ) FlashEmitter.SetRotation(Weapon.Instigator.GetViewRotation()); else FlashEmitter.SetRotation(Weapon.Rotation); Canvas.DrawActor( FlashEmitter, false, false, Weapon.DisplayFOV ); } if ( (Instigator.AmbientSound == ChargeSound) && ((HoldTime <= 0.0) || bNowWaiting) ) Instigator.AmbientSound = None; } ********************************************************************/ function PlayPreFire() //this execs when the trigger is pulled { Weapon.LoopAnim('Charge', 1, 0); //loop charging animation until trigger is released weapon.PlayOwnedSound(ChargeSound, SLOT_Interact, TransientSoundVolume,,,, false); //plays charging sound } function DoFireEffect() { local Vector StartTrace; local Rotator R, Aim; Instigator.MakeNoise(1.0); // the to-hit trace always starts right in front of the eye StartTrace = Instigator.Location + Instigator.EyePosition(); Aim = AdjustAim(StartTrace, AimError); R = rotator(vector(Aim) + VRand()*FRand()*Spread); DoTrace(StartTrace, R); } function DoTrace(Vector Start, Rotator Dir) { local Vector X; X = vector(Dir); TracePart(Start,Start+X*TraceRange,X,Dir,Instigator); } function bool AllowMultiHit() { return false; } function TracePart(Vector Start, Vector End, Vector X, Rotator Dir, Pawn Ignored) { local Vector HitLocation, HitNormal; local Actor Other; local float dist; Other = Ignored.Trace(HitLocation, HitNormal, End, Start, true); if ( (Other != None) && (Other != Ignored) ) { if ( !Other.bWorldGeometry ) { Other.TakeDamage(DamageMax, Instigator, HitLocation, Momentum*X, DamageType); HitNormal = Vect(0,0,0); if ( (Pawn(Other) != None) && (HitLocation != Start) && AllowMultiHit() ) TracePart(HitLocation,End,X,Dir,Pawn(Other)); } } else { HitLocation = End; HitNormal = Vect(0,0,0); } SpawnBeamEffect(Start, Dir, HitLocation, HitNormal, 0); //headshot check and damage def if(Other.GetClosestBone( HitLocation, X, dist, 'head', 8 ) == 'head') Other.TakeDamage(70 * 2, Instigator, HitLocation, Momentum*X, Class'DamTypeKillstickHeadShot'); if( Instigator.bDeleteMe != true ) { if(HoldTime>=2.4) //spawn the dummy if weapon is fully charged { Spawn(class'KillstickDummy',Instigator,, HitLocation - (100*Normal(HitLocation - Start)) , Dir); Weapon.Ammo[0].UseAmmo(50,true); //consumes all ammo if fully charged } } } function SpawnBeamEffect(Vector Start, Rotator Dir, Vector HitLocation, Vector HitNormal, int ReflectNum) { local ShockBeamEffect Beam; if ( (Instigator.PlayerReplicationInfo.Team != None) && (Instigator.PlayerReplicationInfo.Team.TeamIndex == 1) ) Beam = Spawn(BeamEffectClass,,, Start, Dir); else Beam = Spawn(BeamEffectClass,,, Start, Dir); if (ReflectNum != 0) Beam.Instigator = None; // prevents client side repositioning of beam start Beam.AimAt(HitLocation, HitNormal); } function PlayFiring() //this execs when trigger is released { //DestroyEffects(); //destroy charging effect Super.PlayFiring(); } defaultproperties { BeamEffectClass=Class'KillstickFireEffect' //FireSound=Sound'WeaponSounds.TAGRifle.IonCannonBlast' ChargeSound=Sound'WeaponSounds.TAGRifle.TAGFireB' FireForce="LightningGunFire" PreFireAnim="charge" bFireOnRelease=True //don't fire untill trigger is released MaxHoldTime=2.5 // how long trigger must be held to fully charge TraceRange=17000 //Maximum range of the weapon. Momentum=1000 //How much the victim is pushed when hit. AmmoClass=class'KillstickAmmo' //ammo class call AmmoPerFire=1 //how much ammo used per shot DamageType=class'DamTypeKillstick' //damage type class call DamageMin=70 //same damage as sniper rifle DamageMax=70 FireRate=1.800000//How fast it fires. same rate as sniper rifle BotRefireRate=1.8 //How fast bots should try to fire. AimError=800 //How accurate bots are. bPawnRapidFireAnim=false //These settings controll how your view shakes when you fire. ShakeOffsetMag=(X=-15.0,Y=0.0,Z=10.0) ShakeOffsetRate=(X=-4000.0,Y=0.0,Z=4000.0) ShakeOffsetTime=1.6 ShakeRotMag=(X=0.0,Y=0.0,Z=0.0) ShakeRotRate=(X=0.0,Y=0.0,Z=0.0) ShakeRotTime=2 FlashEmitterClass=Class'KillstickFlash1st' //mussle flash effect }
KillstickDummy.uc
//Created by Syntax_Error //dummy projectile class for spawning the explosion //borrowed lotsa code from the redemmer class KillstickDummy extends Projectile; var RedeemerTrail SmokeTrail; // camera shakes // var() vector ShakeRotMag; // how far to rot view var() vector ShakeRotRate; // how fast to rot view var() float ShakeRotTime; // how much time to rot the instigator's view var() vector ShakeOffsetMag; // max view offset vertically var() vector ShakeOffsetRate; // how fast to offset view vertically var() float ShakeOffsetTime; // how much time to offset view var bool bExploded; auto state InstantBoom //go immediatly to explosion state { Begin: BlowUp(Location); } /**************commented out, not used**************** simulated function Destroyed() { if ( SmokeTrail != None ) SmokeTrail.Destroy(); Super.Destroyed(); } simulated function PostBeginPlay() { local vector Dir; if ( bDeleteMe || IsInState('Dying') ) return; Dir = vector(Rotation); Velocity = speed * Dir; if ( Level.NetMode != NM_DedicatedServer) { SmokeTrail = Spawn(class'RedeemerTrail',self,,Location - 40 * Dir); SmokeTrail.SetBase(self); } } event bool EncroachingOn( actor Other ) { if ( Other.bWorldGeometry ) return true; return false; } **************************************************/ simulated function ProcessTouch (Actor Other, Vector HitLocation) { if ( Other != instigator ) Explode(HitLocation,Vect(0,0,1)); } simulated function Explode(vector HitLocation, vector HitNormal) { BlowUp(HitLocation); } simulated function PhysicsVolumeChange( PhysicsVolume Volume ) { } simulated function Landed( vector HitNormal ) { BlowUp(Location); } simulated function HitWall(vector HitNormal, actor Wall) { BlowUp(Location); } function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<DamageType> damageType) { if ( Damage > 0 ) { if ( InstigatedBy == None ) BlowUp(Location); else { Spawn(class'SmallRedeemerExplosion'); SetCollision(false,false,false); HurtRadius(Damage, DamageRadius*0.125, MyDamageType, MomentumTransfer, Location); Destroy(); } } } simulated event FellOutOfWorld(eKillZType KillType) { BlowUp(Location); } function BlowUp(vector HitLocation) { Spawn(class'NewIonEffect',,, HitLocation - 100 * Normal(Velocity), Rot(0,16384,0)); MakeNoise(1.0); SetPhysics(PHYS_None); bHidden = true; GotoState('Dying'); } state Dying { function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<DamageType> damageType) {} function BeginState() { bHidden = true; SetPhysics(PHYS_None); SetCollision(false,false,false); if ( !bExploded ) { Spawn(class'IonCore',,, Location, Rotation); ShakeView(); } InitialState = 'Dying'; if ( SmokeTrail != None ) SmokeTrail.Destroy(); } function ShakeView() { local Controller C; local PlayerController PC; local float Dist, Scale; for ( C=Level.ControllerList; C!=None; C=C.NextController ) { PC = PlayerController(C); if ( PC != None && PC.ViewTarget != None ) { Dist = VSize(Location - PC.ViewTarget.Location); if ( Dist < DamageRadius * 2.0) { if (Dist < DamageRadius) Scale = 1.0; else Scale = (DamageRadius*2.0 - Dist) / (DamageRadius); C.ShakeView(ShakeRotMag*Scale, ShakeRotRate, ShakeRotTime, ShakeOffsetMag*Scale, ShakeOffsetRate, ShakeOffsetTime); } } } } Begin: PlaySound(sound'WeaponSounds.TAGRifle.IonCannonBlast'); HurtRadius(Damage, DamageRadius*0.125, MyDamageType, MomentumTransfer, Location); Sleep(0.5); HurtRadius(Damage, DamageRadius*0.300, MyDamageType, MomentumTransfer, Location); Sleep(0.2); HurtRadius(Damage, DamageRadius*0.475, MyDamageType, MomentumTransfer, Location); Sleep(0.2); HurtRadius(Damage, DamageRadius*0.650, MyDamageType, MomentumTransfer, Location); Sleep(0.2); HurtRadius(Damage, DamageRadius*0.825, MyDamageType, MomentumTransfer, Location); Sleep(0.2); HurtRadius(Damage, DamageRadius*1.000, MyDamageType, MomentumTransfer, Location); Destroy(); } defaultproperties { ShakeRotMag=(Z=250.000000) ShakeRotRate=(Z=2500.000000) ShakeRotTime=6.000000 ShakeOffsetMag=(Z=10.000000) ShakeOffsetRate=(Z=200.000000) ShakeOffsetTime=10.000000 Speed=1000.000000 MaxSpeed=1000.000000 Damage=250.000000 DamageRadius=1500.000000 MomentumTransfer=200000.000000 MyDamageType=Class'DamTypeKillstick' LightType=LT_Steady LightEffect=LE_QuadraticNonIncidence LightBrightness=255.000000 LightRadius=6.000000 LightHue=28 //DrawType=DT_StaticMesh //removed mesh //StaticMesh=StaticMesh'WeaponStaticMesh.RedeemerMissile' bDynamicLight=True bNetTemporary=False //AmbientSound=Sound'WeaponSounds.Misc.redeemer_flight' DrawScale=0.500000 AmbientGlow=96 bUnlit=False SoundRadius=100.000000 SoundVolume=255 TransientSoundVolume=1.000000 TransientSoundRadius=5000.000000 CollisionRadius=24.000000 CollisionHeight=12.000000 bProjTarget=True bFixedRotationDir=True RotationRate=(Roll=50000) DesiredRotation=(Roll=30000) ForceType=FT_DragAlong ForceRadius=100.000000 ForceScale=5.000000 }
TODO
-charging effect either will not display or will spawn in the wrong location until weapon is charged <– Experiment with PlayerOffset and SmallWeaponOffset to align the effect up with the weapon
-damage adjustment for ammo on charged shot not yet implimented
-larger beam for charged shot (is this possible?) –done, spawning larger beam now and in the right direction
-texturing on all meshes –started texturing, going inda slow for now
Syntax_Error
Discussion
EntropicLqd: Sounds very similar to Mod Ideas/Charging Shock Rifle
Syntax_Error: It is, except it doesn't auto charge, you must hold the trigger to charge, much like the shield gun
lemme restate the charge effect problem: when the effect initializes, it auto spawns on the weapon base, and only attaches to the weapon when it is charged... weird huh
still have not had a chance to get to my own computer yet >_< it'll put the code up as soon as i get a chance
Syntax_Error: Yeay, code is up, this isnt everything, but it's a start. I'll put up individual pages as soon as i can.
Syntax_Error: Sorry for the long time away, doing taxes isnt much fun. looking for the Special DVD ed. of 2004 now, and will transfer all MOD stuff to it.
Syntax_Error: Holy #$&^ it has been a long time since i update here. Major changes to code, and most of the problems have been fixed. only annoying one left is the whole double charge/mussleflash effect (i'll get a screen up to show you) planning on putting in reload so if u have 50 ammo you can fire multiple large blasts (just not quickly ) also starting up the dual shotgun weapon, suggestions for alt-fire are welcome.
Foxpaw: For a shotgun weapon, there's a couple of things that come to mind for a secondary fire. Basically, shotguns are neat because there is a variety of ammunition available for them. Their lack of rifling allows for much more flexible ammunition. Anyways, although it wouldn't be entirely realistic, you could have primary fire be one type of ammo, while secondary fire was the other. For instance, one could be slugs and the other buckshot. Or one could fire "less lethal" beanbag rounds that would slow down or otherwise hinder your opponent to set up for another (more lethal) shot. Flares would be somewhat less useful. Flechette rounds are available, but would behave similar to buckshot in-game (only better!) Incendiary rounds are available, though I'm not really sure what they're for. I think smaller slug-bearing sabots may also be available.
Also you could have one fire be semi-auto and the other pump-action. Select fire shotguns have more power in pump-action mode because in semi-auto mode a significant amount of the expanding powder gases are used to cycle the weapon, leaving less to propel the projectile.
Anyways, just thought I'd toss out some ideas. A good secondary fire is essential to prevent it from being just a tracefire flak cannon.
Syntax_Error: Thanks for the suggestions, I really like the semi-auto and pump-aciton idea. That should work well with what i had planned for dual wielding: where primary fire is a powerful double shot, and alt is rapid, alternating single shots. If you only have one shotgun, primary would be pump, while alt would be the less powerful semi-auto. Would the spead change for each one? To say it another way, which type is more accurate? FYI, once I get this started I'll put up a seperate wiki page, and maybe start a page for my info.
Foxpaw: Hmm, that's a tough question. Pump action would have higher chamber pressures which would give the buckshot more momentum leaving the barrel. That might cause it to stay on course more, making it have less spread. On the other hand, the faster moving pump-action buckshot would be less stable in flight due to it's higher velocity. One of these factors would likely outweigh the other - but I don't know which one.