Custom Object tutorial/Creating an enemy

From SRB2 Wiki
Jump to navigation Jump to search
  Custom Object tutorial [view]

Chapter 1: Basic knowledgeChapter 2: File structureChapter 3: Syntax issues and conventionsChapter 4: Creating an enemyChapter 5: Sounds and spritesChapter 6: Advanced techniques

Now that you know how a SOC file works and what all the attributes mean, it's time to create your own enemy. We will use the example from chapter 2 as a basis.

Modifying the parameters

As a first step, change the parameters of State S_POSS_RUN1 in the following way:

State S_POSS_RUN1
SpriteName = PNTY
SpriteFrame = A
Duration = 80
Next = S_POSS_RUN1
Action = A_BunnyHop
Var1 = 0
Var2 = 0

Save your file, run SRB2 with your SOC and start a new game in Greenflower Zone Act 1. You'll immediately recognize that all blue Crawlas now turn into little hopping balls when they are seeing you. Now let's take a look at how we did that: By changing Next to S_POSS_RUN1 we made sure the state keeps looping back to itself. That means the Crawlas will stay in their SeeState the whole time. By changing Duration we made sure the balls don't hop at insane speeds. The state is now 80 tics long, which is a bit more than two seconds. A_BunnyHop makes the enemy jump, while Var1 defines the vertical jumping force. And finally, changing the SpriteName changed the Object's look. Note that now all blue Crawlas in game behave this way.

Effects with multiple states

Okay, this effect might be somewhat funny, but it's rather useless and makes the Crawlas even more harmless than before. Let's change that with a more complicated behavior. Delete the old State S_POSS_RUN1 and paste this new code into your file:

State S_POSS_RUN1
SpriteName = PNTY
SpriteFrame = A
Duration = 80
Next = S_POSS_RUN2
Action = A_BunnyHop
Var1 = 8
Var2 = 12

State S_POSS_RUN2
SpriteName = PNTY
SpriteFrame = A
Duration = 5
Next = S_POSS_RUN1
Action = A_FaceTarget
Var1 = 0
Var2 = 0

Save, run SRB2 with your SOC and visit GFZ1 once again. Now the balls keep jumping and following you. This can actually get quite challenging. But how does it work?

Our Object's state sequence now consists of two states that keep looping. The first one is very similar to the last one we created, except this time Var2 defines an additional horizontal jumping force into the direction the Object is facing. After the jump has been performed, our next state makes sure that the Object faces towards the player's new position with the action A_FaceTarget. Then the whole sequence repeats over and over once again. This is a good example how a pretty simple construction of actions can create something new and interesting.

Editing Object type blocks

But you should not be satisfied just yet. The Object still looks like a Crawla as long as it does not see you, its first jump is not aimed and its 3D size is far too big for the small ball sprite. We'll change this in our last step. Delete everything inside your SOC file and paste this new code:

State S_POSS_STND
SpriteName = PNTY
SpriteFrame = A
Duration = 5
Next = S_POSS_STND
Action = A_Look
Var1 = 0
Var2 = 0

State S_POSS_RUN1
SpriteName = PNTY
SpriteFrame = A
Duration = 80
Next = S_POSS_RUN2
Action = A_BunnyHop
Var1 = 8
Var2 = 12

State S_POSS_RUN2
SpriteName = PNTY
SpriteFrame = A
Duration = 5
Next = S_POSS_RUN1
Action = A_FaceTarget
Var1 = 0
Var2 = 0

Object MT_BLUECRAWLA
MapThingNum = 100
SpawnState = S_POSS_STND
SpawnHealth = 1
SeeState = S_POSS_RUN2
SeeSound = sfx_None
ReactionTime = 32
AttackSound = sfx_None
PainState = S_NULL
PainChance = 200
PainSound = sfx_None
MeleeState = S_NULL
MissileState = S_NULL
DeathState = S_XPLD1
XDeathState = S_NULL
DeathSound = sfx_pop
Speed = 3
Radius = 4*FRACUNIT
Height = 8*FRACUNIT
DispOffset = 0
Mass = 100
Damage = 0
ActiveSound = sfx_None
Flags = MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE
RaiseState = S_NULL

Hit save and test everything if you want to see the results. Note that everything we did was including the default state S_POSS_STND and changing the highlighted values. This means that the sprites were also changed for the SpawnState, the size was adjusted and the SeeState was changed to S_POSS_RUN2. This makes the enemy use A_FaceTarget before making the first jump, so that it jumps in your direction. Congratulations, now you have created your first enemy!

Using freeslots

The first enemy we created replaced one of the default enemies (the blue Crawla). But what if we do not want to replace something but create an entirely new Thing that is meant to be placed onto a new custom map? In this case, we need to use freeslots. Let's assume we wanted to turn the enemy we created earlier into an independent Object type. In this case, every state and Object type definition we used has to be given a new name, and those names have to be declared in a freeslot block at the top of the SOC. Additionally, the MapThingNum must be set to a new, unused value. The new code should look something like this:

Freeslot
S_BOUNCY_STND
S_BOUNCY_RUN1
S_BOUNCY_RUN2
MT_BOUNCYBALL
 
State S_BOUNCY_STND
SpriteName = PNTY
SpriteFrame = A
Duration = 5
Next = S_BOUNCY_STND
Action = A_Look
Var1 = 0
Var2 = 0

State S_BOUNCY_RUN1
SpriteName = PNTY
SpriteFrame = A
Duration = 80
Next = S_BOUNCY_RUN2
Action = A_BunnyHop
Var1 = 8
Var2 = 12

State S_BOUNCY_RUN2
SpriteName = PNTY
SpriteFrame = A
Duration = 5
Next = S_BOUNCY_RUN1
Action = A_FaceTarget
Var1 = 0
Var2 = 0
 
Object MT_BOUNCYBALL
MapThingNum = 1337
SpawnState = S_BOUNCY_STND
SpawnHealth = 1
SeeState = S_BOUNCY_RUN2
SeeSound = sfx_None
ReactionTime = 32
AttackSound = sfx_None
PainState = S_NULL
PainChance = 200
PainSound = sfx_None
MeleeState = S_NULL
MissileState = S_NULL
DeathState = S_XPLD1
XDeathState = S_NULL
DeathSound = sfx_pop
Speed = 3
Radius = 4*FRACUNIT
Height = 8*FRACUNIT
DispOffset = 0
Mass = 100
Damage = 0
ActiveSound = sfx_None
Flags = MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE
RaiseState = S_NULL

Now no default data is overwritten and the Object type can be used independently. You can also see that the freeslot block is very simple: It's nothing more than a header called Freeslot, following by the names of the custom states and Object types we defined in each following line. The names you use for your states and Object types are up to you, but make sure they are not too short and generic, to reduce chances that somebody used the same names in a different SOC.

Implementation

Now the Object type can be implemented into your map. Paste the code into a SOC lump in your WAD or PK3 file. As mentioned in the first chapter, SOC lumps in a PK3 file must be put in the SOC/ folder. SOC lumps in a WAD file must have a name that is either MAINCFG or OBJCTCFG or begins with SOC_. To put an Object into your map, place a random Thing where you want your new Object to be and change its type to the new MapThingNum. In our case, that's 1337. If you are using Zone Builder and have the WAD or PK3 with the SOC script loaded as a resource file, the new Thing type will appear in the Thing type list in a new "Custom Things" category under the name MT_BOUNCYBALL. However, because your custom Thing type isn't listed in the Zone Builder configuration file, it will display a red question mark graphic. This isn't a problem, however. If you run the game, the Object will be there and attack you like it's supposed to.

If you want, you can use special comments in the SOC file to specify a name and sprite to display for your custom Thing type in Zone Builder, as well as a category to list it under: Directly above the start of the Object type definition, add a line with #$Name <name>, where <name> is the name you want to display. To set the sprite, add another line with #$Sprite <sprite>, where <sprite> is the name of the sprite you want to display. The sprite should be located in srb2.srb or one of the currently loaded files. To make the Thing type appear in a different category than "Custom Things", add a line with #$Category <category>, where <category> is the name of the category in which it should be displayed. This can be either an existing category or a new one. In our example, the Object type configuration could now look like this:

#$Name Bouncy Ball
#$Sprite PNTYA1
#$Category Enemies
Object MT_BOUNCYBALL
MapThingNum = 1337
SpawnState = S_BOUNCY_STND
SpawnHealth = 1
SeeState = S_BOUNCY_RUN2
SeeSound = sfx_None
ReactionTime = 32
AttackSound = sfx_None
PainState = S_NULL
PainChance = 200
PainSound = sfx_None
MeleeState = S_NULL
MissileState = S_NULL
DeathState = S_XPLD1
XDeathState = S_NULL
DeathSound = sfx_pop
Speed = 3
Radius = 4*FRACUNIT
Height = 8*FRACUNIT
DispOffset = 0
Mass = 100
Damage = 0
ActiveSound = sfx_None
Flags = MF_ENEMY|MF_SPECIAL|MF_SHOOTABLE
RaiseState = S_NULL
  Custom Object tutorial [view]

Chapter 1: Basic knowledgeChapter 2: File structureChapter 3: Syntax issues and conventionsChapter 4: Creating an enemyChapter 5: Sounds and spritesChapter 6: Advanced techniques