An attempt to document EarthBound battle background distortion effects ====================================================================== Ref: http://starmen.net/pkhack/misc/AF908_notes.txt First, we'll set up a test battle: Group 01, the lone Spiteful Crow. Next, let's change the battle to use background #1. That'll do to start with. Then, to better enable examination of the distortion effects, we'll change all the movement settings to 0. Okay, first, it appears that the byte labeled "screen shift" in fact controls palette animation. Setting it to zero caused palette animation to cease. Also note that the palette animations actually "animate" the current palette, rather than swapping out additional palettes. Just an interesting point. Let's disregard that for now, though, and examine the effect bytes. BG #1 normally has this effect sequence: 60 0 0 0. Changing it to all 0 results in no effects whatsoever. Now, the question seems to be: do each of these bytes signify a wholly different meaning, or is it just a "sequence" of effects from the same source? Let's try 0 60 0 0. Well, [0 60 0 0], [60 0 0 0], and [60 60 0 0] all appear to have the same effect. (alternate lines sliding in opposite directions with wobbly accel). However, [0 0 60 0] produces no effect at all. In fact, it almost seems like the last two bytes have no effect. Maybe we should check to make sure that there are actually some BGs that use the second two bytes. Well, BG #68 has the sequence [28 29 2A 00], which does suggest that each byte is another element in a series of distortion effects that take place over time. Let's give that one a whirl in the spiteful crow battle. (NOTE: perhaps some effects (like 0 or 60?) don't "end"?) Hm. [28 29 2A 00] is a lot of blobby effects, but it's kinda hard to tell where one ends and another begins. Let's try [28 3C 2A 00]. >:D Hm. The 3C effect took over immediately. I guess the effects are evaluated in a weird order? Let's try [3C 29 2A 00]. Hrm. No matter what I do, it doesn't want to switch from one to the other. I wonder if there's something else influencing it? Buh. Well, let's try documenting some of the effects, at least: The first 20 appear to be the standard side-to-side oscillation, with varying levels of shear and amplitude. 1-19: standard, side to side with shear 20: looks a lot like #1, only alternate lines move in opposite directions. I suspect that there's just a small set of subroutines to do the transforms, with various parameters for shear, amplitude, acceleration, and so on. Oh. Well, in fact that looks like that's exactly what it is. BG Palette Animation ==================== Alright, time to figure this out. As far as I can tell, there are 6 bytes related to palette effects in a BG entry: BG Entry ----------------------- AA Graphics BB Palette CC Bits per pixel DD Palette animation EE Palette animation FF Palette animation GG Palette animation HH Palette animation II Palette animation speed? JJ Movement[0] .. .. .. Nintendo arr: 215655 = E15455 Nintendo gfx: 21569E = E1549E Gfx pointer table: 0xAD9A1 Arr pointer table: 0xADB3D Old gfx: CBA5D4 => BA7D4 Old arr: CB899D => B8B9D New gfx: 350200 => F50000 New arr: 351200 => F51000 Palette scrolling ----------------- DD - scrolling type 0 = no scrolling 1 = scroll right 2 = scroll right, allow secondary scrolling 3 = scroll left EE - Primary scrolling start color FF - Primary scrolling final color GG - Secondary scrolling final color HH - Secondary scrolling final color II - Scrolling speed (0 = none, 1 = very fast, 255 = very slow) Any values above 15 for the scrolling color indexes is out-of-bounds on the palette, and can produce weird (and sometimes awesome) effects. Hmm... I thought I had figured this stuff out in more detail. Ah well, I guess for the editor, I needn't concern myself with out-of-bounds colors, since their contents are not entirely predictable. So, anyhow, we need to switch around the drawing framework just a little. A Distorter object needs to be able to draw its distorted frames using rotated palettes, so obviously it needs to be supplied with more than just a bitmap to begin with. Then again, what is the Distorter except just a specification for a distortion effect, with a drawing method thrown in? Perhaps Distortion should be a simple struct to contain the parameters of a distortion, and Graphics itself should include a drawing method that takes a distortion as a parameter? This is slightly problematic as well - it would also need to take a palette animation effect parameter, and since these are two separate concerns both dependent on a common timescale, it may make more sense to have them managed together by something that is not simply a graphics object. Especially since whatever does the drawing in fact needs to be supplied with a _set_ of palette/distortion effects, and needs to compute which one is in effect at any given moment based on their individual durations. Computing the current effect... That was one of the tricky bits I started working on a while ago. Oh. I think I remember now. The more recent versions are on Anacreon. BG Distortion effects: ---------------------- From http://starmen.net/pkhack/misc/AF908_notes.txt Type: 0 - KHHHHZZT (invalid) 1 - horizontal 2 - horizontal, interlaced opposing 3 - vertical 4 - anything above 3, it seems, just defaults to horizontal interlaced So, apparently there's no vertical interlacing :P The vertical distortion doesn't work the way the horizontal ones do; instead of ripples, it's a wave of compression and stretching... Presumably, the parameters have different meanings, so we'll make two lists: Vertical Distortions: ===================== AABB - Unknown CC - Type (03) DDEE - Frequency FFGG - Amplitude HH - ?? IIJJ - Compression KKLL - Still possible acceleration in DDEE? MMNN - Still possible acceleration in FFGG? OO - Speed PPQQ - Compression acceleration Discovery Notes: Speed: note that speed doesn't actually appear to be from 1 to 255. It seems that: 1 - 127: increasing speed 128: zero speed 129 to 255: decreasing speed Where a speed of 1 is the same as a speed of 255. Seems like this may be a signed value? FFGG - appears to still be an amplitude. it seems to be 256 times the actual up-down shift amplitude. Now, these shifts are a bit different from the horizontal ones. It appears to be a sinewave "ripple" vertically traversing the image, but I'm not completely sure what the effect is of the value of the ripple at a given point in space and time. Actually, wait! It appears to simply be a vertical offset. That is, at the current spatial coordinate L, given a time T, the resulting sinewave shift S actually identifies the offset from L to the line of the image that will be displayed at spatial coordinate L. This should produce the stretching effect observed, I think. Yep, got it in one! HH - still unknown? IIJJ - compression/stretching. is equal to 256 times actual compression factor, but the high (sign) bit also seems to control whether every other line in the image is "offset" by 128 pixels, according to the following scheme: compression is positive: high bit = 0 ==> don't offset high bit = 1 ==> offset compression is negative: high bit = 0 ==> offset high bit = 1 ==> don't offset Now, how does this compression work, exactly? Well, if the factor is, say, 2, then every other line is effectively omitted - that is, each line is actually drawn using the bitmap data from line*2, wrapped around by 256. PPQQ - acceleration of compression factor Horizontal Distortions ====================== AABB - Unknown CC - Type (01 or 02) DD EE - Frequency: peaks per 256 vertical pixels FF GG - Amplitude: pixel distance peak to trough HH - Appears to have no effect IIJJ - Appears to have no effect KKLL - Increase in frequency per step MMNN - Increase in amplitude per step OO - Speed PPQQ - Appears to have no effect Boy, that's a lot of "no effect" bytes. I wonder if there's something we're missing... Anyway, let's try to describe this with a handy formula: Basically, each line in the image gets scrolled horizontally by a certain distance. That distance is determined by a sinusoidal function... The function depends on the ampl, freq, time in ticks, _and_ the line number. I think this should do it: shift = A * sin(F * (line - ticks*speed)) For the interlaced mode, just invert the value of 'shift' for every other line. With the frequency at 4 (4 peaks per 256 pixels) At speed = 1, it takes about 60 frames for one point to go from peak to trough... At speed = 2, it takes about 30 frames At speed = 3, it takes about 21 frames At speed = 4, it takes about 15 frames With the frequency at 8 (8 peaks per 256 pixels) At speed = 1, it takes about 60 frames for one point to go from peak to trough At speed = 2, it takes about 30 frames So, obviously the Ripple Frequency is the "spatial frequency", and the speed is the temporal frequency. Spatial frequency accounts for position on the screen, and temporal frequency accounts for the shifting in time. Thus the function really should be: shift = C1*Amp * sin(C2 * F*line - C3 * ticks*speed) Where C1 and C2 are constants. Okay, this looks like it's correct in essentials, but it needs specific tweaking to fit observations. Ripple freq: 1024 Amplitude: 8192 Speed: 1 This results in 4 wavelengths per 256 pixels, and an effective amplitude of 15. C1 = 1/512. In 256 lines, there will be 4 full wavelengths. Thus, C2 * Amplitude * 256 = 8*pi C2 * 1024 * 256 = 8*pi, so C2 must be 8pi/(1024*256). In 60 frames, a given point (y value) will go from 1 to -1. That is to say, it will traverse 2*pi radians Thus, C3 * speed * (60) = 2*pi, so C3 must be 2pi/60. Effect Management ================= Well, we've pretty much completely figured out how the individual effects work. Let's have a stab at the organization... So, first, we should probably find an entry that is known to _successfully_ use more than one effect. Background 0xE0 (Giygas) is one example. So, let's have a peek, shall we? >:D Looks like it uses the following effects: 106 - horizontal out 107 - horizontal in 126 - vertical out 127 - vertical in Note that it does indeed start with entry 107. The order in which the effects actually occur seems to be: 107, 126, 127, 106 or, in terms of position: 2, 3, 4, 1. Interesting. This background's palette animation and movement bytes are all zero. Thus, if there's anything that affects distortion switching, it's within the distortions themselves. Ah ha! Entry 107 has values in both of the remaining "unknown" fields. Unknown 1 = 500 (I'm guessing this is duration in frames??) Unknown 2 = 10 (still no idea what this is) Let's try futzing with it. Double ah ha! Setting Unknown 1 to zero eliminated all switching; effect 107 simply continued until its amplitude wrapped around, and so onward. So, Unknown 1 must be the duration - the amount of time to wait before switching to the next background effect. It appears to be 2 * the number of frames to wait. (Note: I'm seeing a pattern of multiplying by two when dealing with things that are tied to the framecount. I think that maybe the battle backgrounds run at 30fps...) Setting Unknown 2 appears to affect a small "misalignment" between the 107 and 106 phases. It's, well, kinda weird. I'm guessing it's a tweakable factor for making different effects run together smoothly. 106 and 107 both have this set at 10 by default. 126 and 127 have it set at 2. Question mark?? (Note that the following offsets assume your ROM has a header) First, there's a table of battle background information at 0xADEA1 - it consists of 326 entires that are each 17 bytes long. The format is as follows: [pre] Byte# Meaning ==================================================== 0 Graphics/Arrangement Number 1 Palette Number 2 Bits per pixel (Either 2 or 4) 3 Palette animation info 4 Palette animation info - palette scrolling? 5 Palette animation info - palette scrolling? 6 Palette animation info - seems to control how many colors of the palette are scrolled? 7 Palette animation info 8 Palette animation speed 9 Mov 10 Mov 11 Mov 12 Mov 13 Effects 14 Effects 15 Effects 16 Effects [/pre]