Creating A New Monster For Invasion
This tutorial assumes you already have basic knowledge of unreal code but it is not essential if you want to mess around and see what changes you can make and learn from.
The easiest way is to take a current skin you like (in this tutorial we will use the "Droid2K4" skin by Apoptoid) and extend an already existing monster class.
We want our Droid to be able to hold a weapon, like the nalifighter can, and not shoot fire like a gasbag, so we will extend the class SMPNalifighter by Satore in this tutorial because it already has alot of the required information for a monster to hold a weapon. (Because your new class extends an existing class, you can change any of the default properties you want, because it inherits them from the extended class)
This tutorial will also show you how to code your monster to have configurable health and weapon properties in a .ini file : PLS NOTE any monster extended from the SMPNaliFighter will not work with the stat point system in RPG. This is due to it holding a weapon apparently :S
Also please note that by extending the SMPNalifighter, your monster will depend upon the satoremonsterpack, to avoid this, copy the SMPNalifighter.uc file into your Classes folder and change it so it extends "Monster" and not "SMPMonster". There are a few default poperties of the SMPNaliFighter that might also need to be commented out to avoid it depending on the satoremonsterpack. To comment out a line of code simply add "//" infront of the line or the part of code you wish to be commented out. These are the lines that should be commented out:
HitSound(0)=Sound'satoreMonsterpackv120.Nali.injur1n'
HitSound(1)=Sound'satoreMonsterpackv120.Nali.injur2n'
HitSound(2)=Sound'satoreMonsterpackv120.Nali.injur1n'
HitSound(3)=Sound'satoreMonsterpackv120.Nali.injur2n'
DeathSound(0)=Sound'satoreMonsterpackv120.Nali.death1n'
DeathSound(1)=Sound'satoreMonsterpackv120.Nali.death2n'
SoundFootsteps(0)=Sound'satoreMonsterpackv120.Nali.walkC'
SoundFootsteps(1)=Sound'satoreMonsterpackv120.Nali.walkC'
SoundFootsteps(2)=Sound'satoreMonsterpackv120.Nali.walkC'
SoundFootsteps(3)=Sound'satoreMonsterpackv120.Nali.walkC'
SoundFootsteps(4)=Sound'satoreMonsterpackv120.Nali.walkC'
SoundFootsteps(5)=Sound'satoreMonsterpackv120.Nali.walkC'
ControllerClass=Class'satoreMonsterpackv120.SMPNaliFighterController'
Mesh=SkeletalMesh'satoreMonsterpackv120.Nali1'
Skins(0)=Texture'satoreMonsterpackv120.Skins.JNali1'
Skins(1)=Texture'satoreMonsterpackv120.Skins.JNali1'
If you wish to keep the AI from satoremonsterpack then change "ControllerClass=Class'satoreMonsterpackv120.SMPNaliFighterController' in the default properties to ControllerClass=Class'mymonsterfile.SMPNaliFighterController' and simply copy the SMPNaliFighter.uc into your new "Classes" folder.
So first we want to create a new .uc file and extend the SMPNaliFighter, we also want to tell the monster that it has friends and not to attack them. We do this by using the function bool SameSpeciesAs, which we will add soon. Your new class should look something like this:
class MyDroid2K4 extends SMPNaliFighter;
We want to tell it where the configurable properties can be configured for this monster so we add "config 'name of ini file'", in this case the satoremonsterpackini file, so lets add that line of code.
class MyDroid2K4 extends SMPNaliFighter config(satoreMonsterPack);
Now lets tell it that health and weapon can be configured in the ini file, we use function prebeginplay for the health. The weapon variable has been inherited.
class MyDroid2K4 extends SMPNaliFighterconfig(satoreMonsterPack);
var config float fHealth;
var() config array<string> WeaponClassName;
var() config bool bNoThrowWeapon; //lets also tell it that its option to throw the weapon is configurable
function PreBeginPlay()
{
Super.PreBeginPlay();
Health = fHealth;
}
Now lets tell the monster that it has friends and not to hurt them!! we use function bool SameSpeciesAs for this and put the relevant class names in there like this:
class MyDroid2K4 extends SMPNaliFighterconfig(satoreMonsterPack); var config float fHealth; var() config array<string> WeaponClassName; var() config bool bNoThrowWeapon; function PreBeginPlay() { Super.PreBeginPlay(); Health = fHealth; } function bool SameSpeciesAs(Pawn P) { return ( Monster(P) != none && (P.IsA('SMPTitan') || P.IsA('SMPQueen') || P.IsA('SMPNaliFighter')|| P.IsA('Skaarj') || P.IsA('SkaarjPupae'))); ///here we have told it to be friend with those certain monsters (and any that are a subclass of it. Add more if you wish) }
Lets change the default deathsound inherited from the SMPNaliFighter to one of our own choosing. We need to add the following lines of code for this and set a new variable.
class MyDroid2K4 extends SMPNaliFighterconfig(satoreMonsterPack); var config float fHealth; var Sound DeathSoundINI[50]; var() config array<string> WeaponClassName; var() config bool bNoThrowWeapon; function PreBeginPlay() { Super.PreBeginPlay(); Health = fHealth; } function bool SameSpeciesAs(Pawn P) { return ( Monster(P) != none && (P.IsA('SMPTitan') || P.IsA('SMPQueen') || P.IsA('SMPNaliFighter')|| P.IsA('Skaarj') || P.IsA('SkaarjPupae'))); } simulated function PlayDying(class<DamageType> DamageType, vector HitLoc) { AmbientSound = None; bCanTeleport = false; bReplicateMovement = false; bTearOff = true; bPlayedDeath = true; LifeSpan = RagdollLifeSpan; GotoState('Dying'); ///tells it to bring up this function next. Velocity += TearOffMomentum; BaseEyeHeight = Default.BaseEyeHeight; SetInvisibility(0.0); PlayDirectionalDeath(HitLoc); SetPhysics(PHYS_Falling); PlaySound(DeathSoundINI[Rand(50)], SLOT_Pain,1000*TransientSoundVolume, true,800); //.our new variable is here, ive called it DeathSoundINI so it doesnt conflict with its parent class (SMPNaliFigther), and changed the default number of random sounds from 4 to 50 }
why not have our monster explode in a cool way when it dies? with some nice effects created from xEmitters, so lets add the "dying" code for this to happen. If you dont want any effects, that fine, just leave this bit out.class MyDroid2K4 extends SMPNaliFighterconfig(satoreMonsterPack); var config float fHealth; var Sound DeathSoundINI[50]; var() config array<string> WeaponClassName; var() config bool bNoThrowWeapon; function PreBeginPlay() { Super.PreBeginPlay(); Health = fHealth; } function bool SameSpeciesAs(Pawn P) { return ( Monster(P) != none && (P.IsA('SMPTitan') || P.IsA('SMPQueen') || P.IsA('SMPNaliFighter')|| P.IsA('Skaarj') || P.IsA('SkaarjPupae'))); } simulated function PlayDying(class<DamageType> DamageType, vector HitLoc) { AmbientSound = None; bCanTeleport = false; bReplicateMovement = false; bTearOff = true; bPlayedDeath = true; LifeSpan = RagdollLifeSpan; GotoState('Dying'); ///tells it to bring up this function next. Velocity += TearOffMomentum; BaseEyeHeight = Default.BaseEyeHeight; SetInvisibility(0.0); PlayDirectionalDeath(HitLoc); SetPhysics(PHYS_Falling); PlaySound(DeathSoundINI[Rand(50)], SLOT_Pain,1000*TransientSoundVolume, true,800); } State Dying { ignores AnimEnd, Trigger, Bump, HitWall, HeadVolumeChange, PhysicsVolumeChange, Falling, BreathTimer; function Landed(vector HitNormal) { SetPhysics(PHYS_None); if ( !IsAnimating(0) ) LandThump(); Super.Landed(HitNormal); Spawn(class'xEmitter');//replace xEmitter with the class of the effect you want, for example: class'ShockComboCore' Spawn(class'xEmitter');//have as many as you want. But the more you have the more extreme and laggy it might be! } simulated function Timer() { if ( !PlayerCanSeeMe() ) Destroy(); else if ( LifeSpan <= DeResTime && bDeRes == false ) StartDeRes(); else SetTimer(0.2, true); /////how long the monster hangs around on screen whilst the efects are going on, so we want him to blow up and not be seen, so ive set this to a very quick time of 0.2 seconds } }
Now for the most fun part, changing the default properties! Here are the most relevant default properties for getting your monster just right, but you can open up the parent class or this class in the editor when it has been compiled and have a look at what you can change.
defaultproperties { WeaponClassName(0)="XWeapons.LinkGun" //with our monster this property is just for show, we will be changing its weapon and health in the ini file because we have made it configurable. FireRateScale=1.50000 //how fast this monster fires the weapon ScoringValue=5 //how many points you get when you kill it. GibGroupClass=Class'XEffects.xBotGibGroup'///our monster is made of metal so we want it to explode with metal, so we use the correct GibGroupClass here. RagdollOverride="Droid2k4" //set this to the name of the skin/model you are using. If you get errors with this, then remove this line and it will inherit the SMPNaliFighter Ragdoll. fHealth=1000 //how much health, 1000 being 1 hit for instagib,configurable in the satoremonsterpackini file, so this will be overwritten with the ini file. Just like the weapon class. HitSound(0)=Sound'GameSounds.UDamageFire'//whatever hit sound you want. Because this is a robot it is only correct to change the hit sounds from nalifighters to a more robot sound. HitSound(1)=Sound'GameSounds.UDamageFire' HitSound(2)=Sound'GameSounds.UDamageFire' HitSound(3)=Sound'GameSounds.UDamageFire' DeathSoundINI(0)=Sound'PlayerSounds.RobotCrunch3'///it has 50 to choose from thanks to the variable we made earlier. here i have only added 4 for it to pick from at random. DeathSoundINI(1)=Sound'PlayerSounds.RobotCrunch4' DeathSoundINI(2)=Sound'PlayerSounds.RobotCrunch3' DeathSoundINI(3)=Sound'PlayerSounds.RobotCrunch4' Mesh=SkeletalMesh'droid2k4.Droid_Alien'//you must put the mesh class of the skin/model (in this case the droid2k4) you chose for it to actually look like it. You can find the correct mesh name if you open the animation file in the browser. DrawScale=2.000000 ////this is where you change the size of the monster. Play around with this property in the editor to get the desired size. PrePivot=(Z=15.000000)///changing the size of the monster will affect its pivot (the centre of the monster) you must line this up again to the centre of the monster, the collision properties below will help you immensely. Skins(0)=Texture'Droid2k4Skins.Base.Headtex'//here is where you tell it what skin it will have. i have put in the correct class names of the textures for the droid. But if you want it to have the invisibility combo effect for example, then add that texture name here instead. Skins(1)=Texture'Droid2k4Skins.Base.Bodytex' CollisionRadius=60.000000 ////how big the collision is (where you can hit the monster), to really get these properties correct you have to look at them in the editor and play around.(remember to put radii view on in the editor) CollisionHeight=75.000000 Mass=500 //how big of a mess is made when it gets gibbed bNoThrowWeapon=True///we dont want the monster to throw away its weapon!! bNoTeleFrag=True //if set to true, you can't telefrag it, good for bosses. bCanCrouch=True //do you want it to be able to crouch? }
Now the finished code should look like this!
class MyDroid2K4 extends SMPNaliFighterconfig(satoreMonsterPack); var config float fHealth; var Sound DeathSoundINI[50]; var() config array<string> WeaponClassName; var() config bool bNoThrowWeapon; function PreBeginPlay() { Super.PreBeginPlay(); Health = fHealth; } function bool SameSpeciesAs(Pawn P) { return ( Monster(P) != none && (P.IsA('SMPTitan') || P.IsA('SMPQueen') || P.IsA('SMPNaliFighter')|| P.IsA('Skaarj') || P.IsA('SkaarjPupae'))); } simulated function PlayDying(class<DamageType> DamageType, vector HitLoc) { AmbientSound = None; bCanTeleport = false; bReplicateMovement = false; bTearOff = true; bPlayedDeath = true; LifeSpan = RagdollLifeSpan; GotoState('Dying'); ///tells it to bring up this function next. Velocity += TearOffMomentum; BaseEyeHeight = Default.BaseEyeHeight; SetInvisibility(0.0); PlayDirectionalDeath(HitLoc); SetPhysics(PHYS_Falling); PlaySound(DeathSoundINI[Rand(50)], SLOT_Pain,1000*TransientSoundVolume, true,800); } State Dying { ignores AnimEnd, Trigger, Bump, HitWall, HeadVolumeChange, PhysicsVolumeChange, Falling, BreathTimer; function Landed(vector HitNormal) { SetPhysics(PHYS_None); if ( !IsAnimating(0) ) LandThump(); Super.Landed(HitNormal); Spawn(class'xEmitter'); Spawn(class'xEmitter'); } simulated function Timer() { if ( !PlayerCanSeeMe() ) Destroy(); else if ( LifeSpan <= DeResTime && bDeRes == false ) StartDeRes(); else SetTimer(0.2, true); } } defaultproperties { WeaponClassName(0)="XWeapons.LinkGun" FireRateScale=1.50000 ScoringValue=5 GibGroupClass=Class'XEffects.xBotGibGroup' RagdollOverride="Droid2k4" fHealth=1000 HitSound(0)=Sound'GameSounds.UDamageFire' HitSound(1)=Sound'GameSounds.UDamageFire' HitSound(2)=Sound'GameSounds.UDamageFire' HitSound(3)=Sound'GameSounds.UDamageFire' DeathSoundINI(0)=Sound'PlayerSounds.RobotCrunch3' DeathSoundINI(1)=Sound'PlayerSounds.RobotCrunch4' DeathSoundINI(2)=Sound'PlayerSounds.RobotCrunch3' DeathSoundINI(3)=Sound'PlayerSounds.RobotCrunch4' Mesh=SkeletalMesh'droid2k4.Droid_Alien' DrawScale=2.000000 PrePivot=(Z=15.000000) Skins(0)=Texture'Droid2k4Skins.Base.Headtex' Skins(1)=Texture'Droid2k4Skins.Base.Bodytex' CollisionRadius=60.000000 CollisionHeight=75.000000 Mass=500 bNoThrowWeapon=True bNoTeleFrag=True bCanCrouch=True }
Two last things, dont forget to add the correct classname to the MonsterTable in the satoremonsterpack ini file and now we have coded our monster to have configurable health and weapons, we need to add the following lines of code to the bottom satoremonsterpack ini file, where you can change them to whatever you want :)
[mymonsterfile.myDroid2k4] bNoThrowWeapon=True fHealth=2000 WeaponClassName=XWeapons.ShockRifle