Introduction to Iscripting (continued)
Our next stop is to take a look at a set of related animations, the GndAttkInit, GndAttkRpt and GndAttkToIdle animations:
MarineGndAttkInit:
playfram 0x00 # Frame set 0
wait 1
playfram 0x11 # Frame set 1
wait 1
playfram 0x22 # Frame set 2
MarineGndAttkInit should look pretty familiar at this point, because we've seen this set of frames a lot. It's the three framesets of the Marine lowering its gun. The Junker, however, doesn't have any animation of preparing to fire, so we don't actually need this animation at all! Let's delete MarineGndAttkInit, and then modify the Marine's header:
...
GndAttkInit MarineGndAttkRpt
AirAttkInit MarineGndAttkRpt
...
Why are we changing the header? SC always uses the GndAttkInit/AirAttkInit animations for the first attack, so we can't just leave them empty. Instead, we'll just point them to GndAttkRpt, which will serve as our attack animation for all cases.
MarineGndAttkRpt:
wait 1
nobrkcodestart
playsnd 69 # Bullet\TMaFir00.wav
attackwith 1
playfram 0x33 # Frame set 3
wait 1
playfram 0x22 # Frame set 2
wait 1
playfram 0x33 # Frame set 3
wait 1
playfram 0x22 # Frame set 2
wait 1
playfram 0x33 # Frame set 3
wait 1
playfram 0x22 # Frame set 2
wait 1
nobrkcodeend
gotorepeatattk
ignorerest
Here we see a few new opcodes.
nobrkcodestart prevents the animation from being interrupted by any orders (aside from dying).
nobrkcodeend "unlocks" the animation and allows it to be interrupted again. The Marine has an automatic rifle that fires for about half a second (rather than a single shot like the Ghost), so it'd look rather odd if it stopped firing in the middle of its animation.
We also encounter for the first time the
playsnd opcode (similar to playsndbtwn) and
attackwith.
attackwith 1/2 causes the unit to attack with either its ground weapon (1) or its air weapon (2).
Finally, at the end of the script we have
gotorepeatattk, which causes the unit to attack the target it's attacking again (once the cooldown is up), and
ignorerest which just stops the animation until the next time it is told to play an animation. Putting it all together, the Marine firing animation can be summarized as the following:
- Play firing sound
- Attack the target
- Flash between framesets 0x33 (gun drawn with muzzle flash) and 0x22 (gun drawn with no muzzle flash) three tiems
- Attack the target again
- Stop executing the script until told to carry out another animation
Again, we don't have an attack animation, so let's simplify the animation:
MarineGndAttkRpt:
wait 1
nobrkcodestart
playsnd 69 # Bullet\TMaFir00.wav
attackwith 1
wait 1
nobrkcodeend
gotorepeatattk
ignorerest
MarineGndAttkToIdle:
wait 1
goto MarineLocal00
We have also removed the content of MarineGndAttkToIdle for the same reason. Note that we still need a MarineGndAttkToIdle animation (or need to have it point to something else in the header) or SC will crash when the unit stops attacking. In this case, I've decided to just point it back to our "wait and do nothing" local animation.
Finally, let's modify the walking animation:
MarineWalking:
move 4
wait 1
playfram 0x55 # Frame set 5
move 4
wait 1
playfram 0x66 # Frame set 6
move 4
wait 1
playfram 0x77 # Frame set 7
move 4
wait 1
playfram 0x88 # Frame set 8
move 4
wait 1
playfram 0x99 # Frame set 9
move 4
wait 1
playfram 0xaa # Frame set 10
move 4
wait 1
playfram 0xbb # Frame set 11
move 4
wait 1
playfram 0xcc # Frame set 12
move 4
wait 1
playfram 0x44 # Frame set 4
goto MarineWalking
We've seen most of this already, the only new opcode present is
move, which moves the unit X pixels forward at the end of the current tick. This is basically what we want already, except the Junker doesn't have a frameset 0xcc, so let's remove that line. Therefore, our final script should look something like this:
# ----------------------------------------------------------------------------- #
# This header is used by images.dat entries:
# 239 Marine (terran\marine.grp)
.headerstart
IsId 78
Type 12
Init MarineInit
Death MarineDeath
GndAttkInit MarineGndAttkRpt
AirAttkInit MarineGndAttkRpt
Unused1 [NONE]
GndAttkRpt MarineGndAttkRpt
AirAttkRpt MarineGndAttkRpt
CastSpell [NONE]
GndAttkToIdle MarineGndAttkToIdle
AirAttkToIdle MarineGndAttkToIdle
Unused2 [NONE]
Walking MarineWalking
WalkingToIdle MarineWalkingToIdle
SpecialState1 [NONE]
.headerend
# ----------------------------------------------------------------------------- #
MarineInit:
imgul 240 0 0 # Marine Shadow (terran\tmaShad.grp)
MarineWalkingToIdle:
playfram 0x00 # Frame set 0
wait 1
playfram 0x11 # Frame set 1
wait 1
playfram 0x22 # Frame set 2
wait 1
MarineLocal00:
wait 125
goto MarineLocal00
MarineDeath:
playsndbtwn 276 277 # Terran\MARINE\TMaDth00.WAV | Terran\MARINE\TMaDth01.WAV
imgol 332 0 0 # TerranBuildingExplosionsmall (thingy\tBangS.grp)
wait 3
end
MarineGndAttkRpt:
wait 1
nobrkcodestart
playsnd 69 # Bullet\TMaFir00.wav
attackwith 1
wait 1
nobrkcodeend
gotorepeatattk
ignorerest
MarineGndAttkToIdle:
wait 1
goto MarineLocal00
MarineWalking:
move 4
wait 1
playfram 0x55 # Frame set 5
move 4
wait 1
playfram 0x66 # Frame set 6
move 4
wait 1
playfram 0x77 # Frame set 7
move 4
wait 1
playfram 0x88 # Frame set 8
move 4
wait 1
playfram 0x99 # Frame set 9
move 4
wait 1
playfram 0xaa # Frame set 10
move 4
wait 1
playfram 0xbb # Frame set 11
move 4
wait 1
playfram 0x44 # Frame set 4
goto MarineWalking
Now that we've finished that up, save the script we're editing, then save the entire file as iscript.bin. In PyMPQ, add iscript.bin under the path
scripts\iscript.bin. We also need to add the Junker and Junker Shadow GRPs as
unit\terran\marine.grp and
unit\terran\tmaShad.grp respectively.
Go ahead and update the MPQ file in your mod and load it up. If you've done everything correctly, the Junker should be properly animated:
You might be wondering "why didn't we need to modify the Marine Shadow iscript?" To answer that, let's take a look at the entry for Marine Shadow:
# ----------------------------------------------------------------------------- #
# This header is used by images.dat entries:
# 001 Scourge Shadow (zerg\avenger.grp)
...
# 957 Ursadon (Ice) Shadow (neutral\ncicShad.grp)
.headerstart
IsId 275
Type 1
Init ShadowHeaderInit
Death ShadowHeaderDeath
.headerend
# ----------------------------------------------------------------------------- #
ShadowHeaderInit:
wait 1
followmaingraphic
goto ShadowHeaderInit
ShadowHeaderDeath:
wait 1
end
The first thing you'll notice is that this iscript entry is used by a
ton of images. In fact, every shadow in the game uses this iscript entry. But how can this be? Every shadow GRP has different numbers of frames and animations.
The key is the
followmaingraphic opcode. This opcode causes an overlay/underlay to display the same frame number as its parent graphic. As long as you make sure that the shadow graphic has the same number of frame as the parent, you can simply use this entry and not worry about creating a new iscript entry for the shadow.
Now that we know the basics of iscripting, let's spice up our Junker's animation with two simple tricks - giving it a missile barrage attack, as well as a close range flamethrower.
First, let's do some preliminary groundwork in units.dat and weapons.dat. We'll start by making a copy of the Flame Thrower weapon and changing the max range:
Then we'll assign that weapon to the Marine:
Then, let's modify the Marine's air weapon and change it from the Gauss Rifle to a Missile attack, similar to the Hellfire Missile Pack the Goliath uses:
Now let's go back to the Marine's iscript entry. First, let's add the close-combat flamethrower attack by adding this line at the beginning of the GndAttkRpt animation:
MarineGndAttkInit:
trgtrangecondjmp 48 MarineFlamethrower
wait 1
...
trgtrangecondjmp is an opcode that causes the animation to jump to the specified label if its attack target is within the given range. In this case, we have specified a rather short range so that the Marine only uses its flamethrower at close range. Now we need to create the MarineFlamethrower label:
MarineFlamethrower:
nobrkcodestart
imgol 421 0 0 # FlameThrower (thingy\flamer.grp)
attkshiftproj 24
wait 1
attkshiftproj 52
wait 1
attkshiftproj 80
wait 5
ignorerest
First we spawn the "Flamethrower" image overlay on top of the Marine (the same one that the Firebat uses). Then we use
attkshiftproj, an opcode that spawns a weapon a number of pixels in front of the unit. StarCraft doesn't really have the idea of "line splash" like you might think. While the Firebat's weapon appears to have a hitbox like this:
What it actually looks like is closer to this:
Namely several small projectile hits, arrayed in a line in front of the unit. This helps make sense of the weapons.dat entry for Flame Thrower:
Note that you provide
radii for splash, not a length/width, and even the outer radius used by the Flame Thrower weapon is much smaller than the Firebat's actual attack. Now that you have seen the iscript we (and the Firebat) are using, this should all make sense.
You might be wondering "where is the corresponding nobrkcodeend for this animation?" The answer is that it is actually in the
flamethrower overlay's iscript! This makes it easier to prevent the unit from moving away until the flamethrower graphic has finished.
We have a few other changes we need to make to the attack animations, starting with GndAttkRpt:
MarineGndAttkRpt:
trgtrangecondjmp 48 MarineFlamethrower
wait 1
nobrkcodestart
playsnd 69 # Bullet\TMaFir00.wav
attackwith 2
wait 1
attackwith 2
wait 1
attackwith 2
wait 1
nobrkcodeend
gotorepeatattk
ignorerest
Here we've done two things. First of all, we've changed the parameter given to the
attackwith opcode so that the Marine attacks with its air weapon instead. Remember that we changed the ground weapon to our Flame Thower copy, so if we want it to use its missiles on ranged targets, we need to specify the air weapon. Once the attack animation has actually begun, SC doesn't care about what weapon is actually being used, so we're free to use the air weapon even in the ground attack animation. Because the weapon has both "Air" and "Ground" target flags checked in weapons.dat, it is capable of doing damage to both targets.
We've also added a couple more "attackwith" opcodes so that the Marine fires 3 bursts of missiles while attacking. We've added some wait opcodes in between so that the missiles are staggered, rather than being stacked on top of each other.
We're almost done. The only thing left to do is to create an Air attack animation:
MarineAirAttkRpt:
wait 1
nobrkcodestart
playsnd 69 # Bullet\TMaFir00.wav
attackwith 2
wait 1
attackwith 2
wait 1
attackwith 2
wait 1
nobrkcodeend
gotorepeatattk
ignorerest
Previously, we were OK using a shared animation for both air and ground attacks. But we don't want to trigger the flamethrower when attacking air units, so we need a separate animation. Don't forget to change the headers too:
Init MarineInit
Death MarineDeath
GndAttkInit MarineGndAttkInit
AirAttkInit MarineAirAttkInit
Unused1 [NONE]
GndAttkRpt MarineGndAttkInit
AirAttkRpt MarineAirAttkInit
CastSpell [NONE]
GndAttkToIdle MarineGndAttkToIdle
AirAttkToIdle MarineGndAttkToIdle
Unused2 [NONE]
Walking MarineWalking
WalkingToIdle MarineWalkingToIdle
SpecialState1 [NONE]
Now that we've finished, go ahead and try these changes out in game:
In the next part of the tutorial, we'll put some graphical polish on our new Marines by learning how to change icons and wireframes.
Post has been edited 2 time(s), last time on Sep 4 2017, 12:11 am by Voyager7456.