Please login or register.

Login with username, password and session length
Advanced search  


Welcome to FFH, where all your dreams come true!

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Messages - lirmont

Pages: [1] 2 3 ... 24
Alright, here it is (same link): download.

It's not 100%, but I believe it will suit your purposes: (1) combo box drop downs use available space (sometimes less options are shown on purpose, though), (2) no controls overlap, and (3) the "Patch ISO" screens are sizable now. Things I know are still wrong: (1) doesn't handle big fonts, (2) controls near edges of parents sometimes get cut off (change to smaller font), and (3) animation tab takes a long time to load (but it looks nice).

Here is a screenshot:

The setting for how many drop down items to show can be overridden easily enough in the code. I'll start working on the rest of the layouts now.

Spam / Re: Purses as weapons
« on: July 17, 2014, 05:07:59 AM »
Little Ol' Lady, Lv. 99
Master Bag Lady (Lv. 8)

  • Accumulate Sweets
  • Squint
  • Pinch
  • Head Shake

  • Ignore Speed Limit
  • Equip EZ-Reacher
  • Cooking +3
  • Kitsch +5
  • Thrift

Okay, then please check the "Items" tab for me of this version: download. It takes a lot of work to reorganize the layout into this format, but, if that tab is laid out how you expect (undo any changes you've done to the XP theme), I don't mind doing it for the rest of them. This version also benefits from the whole form's size, meaning you can see more options at any given time. Again, the "Items" tab is the only one I've done it for, and that took over an hour.

This is what it looks like:

The goal of the version I reconfigured wasn't to let you use the font you had (i.e. the problem font). The goal was to let you set Tahoma (i.e. the font it was designed for) in the theme so that it will look like it was designed to look. There are two problems: (1) the font and (2) the button size XP's button skins add to the really small, nearly absolutely positioned numeric up/down controls. The first issue causes overlaps like the "Effect:" over the combo box; this is the font's fault (i.e. temporarily switch to Tahoma in the theme). The second issue causes overlaps like when the numeric up/down controls would overlap each other (i.e. you could turn the theme to classic in XP which doesn't have those huge button skins, but the version I reconfigured turns that off just for the application).

This Version

Here's a version of that program that doesn't turn visual styles on: download. There were also a couple of other things that weren't configured to have the font changed (the Patch ISO windows, for instance; also, the item heights of the ENTD unit list). This addresses those. As per the previous post, it looks for the font you set in the theme under "Message Box" (whereas the previous version didn't). The visual styles are turned off to prevent the blue buttons (which are large) from winding up on the numeric up/down controls.


  • Open FFTPatcher to the window you're having trouble with.
  • Right-click somewhere empty on the desktop.
  • Select "Properties" from the context menu.
  • Select the "Appearance" tab.
  • Click the "Advanced" button.
  • Choose a different font and size until the program looks like you want it to (hit "OK" then "Apply"; NOTE: the program probably expects Tahoma 8pt).
  • Optional: Change the font back afterwards.

The font is wide (i.e. change it in the theme to one that's less wide with less vertical spacing).

Final Fantasy Tactics Hacking / Re: Waveform/Instrument Injection
« on: July 06, 2014, 01:53:03 AM »
Something else maybe worth looking at is that PSound finds 2 really short audio files in BATTLE.BIN. I found them at 0x2d4e0-0x2d760 (185568-186208) and 0x2eaa7-0x2eca6 (191143-191654). Might be an easy target for a quick customization. I think the first one might be the gil award plink (it's 0.05s). I don't know what the second one is, but it's 0.04s.

Final Fantasy Tactics Hacking / Waveform/Instrument Injection
« on: July 04, 2014, 06:35:12 AM »
New instruments in FFT!


Have not decoded the actual definitions for instruments yet. Have managed to replace the wave data (WAVESET.WD) that they operate on.

Involved files:
  • WAVESET.WD (wave data)
  • The ISO it's on.

  • Drag WAVESET.WD into the PSound program.
  • PSound will show 85 waves (0..84).
  • Pick the sound you want to replace.
  • Export the sound (after configuring PSound to not add extra lead out).
  • Document how long the sound is for reference in picking a replacement .WAV file (NOTE: .WAV file should be 22050Hz, 16-bit PCM).
  • Open WAVESET.WD in a hex editor.
  • The first waveform in that file is at 0x0b40; you can identify waves after this point in the file by a sequence of 16 or more consecutive empty bytes; the data streams start as soon as the first non-zero 2-byte set is encountered (like 0x00 0x02 or 0x05 0x02).
  • Jump to the waveform you want to replace.
  • Determine the length to the next set of 16 empty bytes from the start of the data set. This number is the length in bytes of the data streams in the waveform.
  • Use the following program I wrote to encode your wave file like: python WaveFileName.wav LengthInBytes
  • There is now a file containing the data: WaveFileName.wav.bytes
  • In a hex editor that supports paste overwrite (re: the HxD program), paste over the existing data streams back in the WAVESET.WD file.
  • Clear PSound's playlist, and reload the file by dragging it into the playlist area.
  • If you copied the data over correctly, you'll hear the new wave. If not, you'll hear a stock, choppy wave that PSound plays when it can't decode the data (you can just think of it as an error beep).
  • When you're happy with it, you'll need to replace the WAVESET.WD file in the ISO.
  • To do this part manually, open your ISO in a hex editor, and search for the text string: dwdsP (or the hex string 64776473502016CE). This is the start of the WAVESET.WD file in the ISO.
  • Locate the wave you want to replace after that point.
  • Waveforms contain an unbroken line of 2-byte commands that you can see clearly in a standard 16-byte view (re: look for the column of 0x02, 0x06, or 0x03 bytes). Highlight the data stream until you encounter non-file data (i.e. where there would be a two byte command like 0x00 0x02 there is instead a FE FF). Record this value as the initial write for the file.
  • Now, continue highlighting all the way down to just before the next set of consecutive 16 empty bytes (0x03 in the second command byte signals the last stream of the waveform). Copy this data into an ANSI text file (like 0010.txt), removing the spaces between bytes with a replace all tool.
  • Now, re-encode the replacement WAV file against the ISO's data, like: python WaveFileName.wav LengthInBytes TextFileWithBytesFromISO.txt InitialWriteAsNumberOrHexadcimal
  • The content of the resulting .bytes file can be copied over the data stream in the ISO in the same way as the file (paste write).
  • Your ISO now contains a different sound than FFT shipped with.

Usage (for the test in the video on a Japanese ISO of FFT).
Code: (Command Line) [Select]
python C1.wav 6816 C1.txt 0x360
python C2.wav 3808 C2.txt 0xb0
python C3.wav 3936 C3.txt 0x1c0
python C4.wav 1472 C4.txt 0x250
python C5.wav 3104 C5.txt 0x480
NOTE: Requires installing Python and several dependencies: numpy and scipy.

Code: (Python) [Select]
import numpy as np
from itertools import product
from import read

# Pass through values that line up with offsets: 0x0 to 0xf.
acceptedValues = [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, -1.0, -0.875, -0.75, -0.625, -0.5, -0.375, -0.25, -0.125]
acceptedValuesMaxArray = np.array([0.875, -1, ])
allValueRange = np.array(acceptedValues)
# SYNONYMS: 0x06 and 0x03. 0x06 may be some kind of off switch to something 0x02 can do in effects. 0x03 signals the last block follows. They don't do anything special to the data, though.
amplitudeCommands = [0x02, ]
# Cutoff seemed like < 0x5d. Also, cannot use anything evenly divisible by any in range: (0xd, 0xe, 0xf). However, everything above the first 16 (with the exception of the 0x5x block, which is a pass through exactly? the same as 00) are effects, and can safely be ignored for the purposes of writing pre-amplified data into the waveform.
exponents = [i for i in range(0, 0x10) if (i == 0) or ((i % 0xd) > 0) and ((i % 0xe) > 0) and ((i% 0xf) > 0)]
negativePowersOfTwo = list(product(amplitudeCommands, exponents))

# Yield successive n-sized chunks from l. Used to slice bytes of an incoming wave into 28-sample sets (encodes to 14 bytes).
def chunks(l, n):
for i in xrange(0, len(l), n):
yield l[i:i+n]

# Convert sample value (expects 16-bit signed integer) into a value from -1 to 1.
def getSampleValue(n):
thisSampleValue = np.nan_to_num(n) / 32767.0
return thisSampleValue

# Rounds actual data to data represented in the encoding scheme.
def roundToNearestAcceptedValue(sampleValue, valueRange = None):
valueRange = valueRange if valueRange is not None else allValueRange
delta = None
actualNumber = None
for possibility in valueRange:
thisDelta = abs(sampleValue - possibility)
if (delta is None) or (thisDelta < delta):
delta = thisDelta
actualNumber = possibility
return actualNumber

# For values of 0x0 to 0xc in command 0x02, yield: 2**(-n). Values in 0x1x range are a concave falloff. Values in 0x2x range are a convex falloff. Values in 0x3x range are a whole sine wave. Values in 0x4x range are the first 4/5th's of a sine wave.
def getModifier(value):
return 1 / float(2 ** value)

# Get the closest amplitude modifier in the encoding scheme. Intended for use with maximum values.
def getClosestModifier(value, valueRange = acceptedValuesMaxArray, negativePowersOfTwo = negativePowersOfTwo):
# If the value is not zero, go looking for it as a max/min value in the sliding table of values.
if value != 0:
delta = None
actualNumber = None
actualModifier = None
for idx, (command, power) in enumerate(negativePowersOfTwo):
# Typically, get the value of: 1 / 2**power.
modifier = getModifier(power)
# See if any of the maximum values are now equivalent to the supplied value.
for possibility in (valueRange * modifier):
thisDelta = abs(value - possibility)
# If this value is closer than previous values, store it as new solution to best-fit question.
if (delta is None) or (thisDelta < delta):
# Try to escape early. The delta is an exact match.
if thisDelta == 0:
return actualModifier
# Otherwise, store best and continue.
delta = thisDelta
actualNumber = possibility
actualModifier = idx
return actualModifier
# If the value is zero, it can be represented in the pass through, which will be data set (2, 0) at index 0 of negativePowersOfTwo.
return 0

# Translate the 28-sample set into a 16-byte data stream of form: AACC 00112233445566778899AABBCCDD
def getBytesForStream(dataStream):
samples = np.array([getSampleValue(rawValue) for rawValue in dataStream])
maximumValue = samples[abs(samples).argmax()] #max([abs(sample) for sample in samples])
# Store bytes for wave amplitude modifier and stream.
modifierBytes = [0x0, 0x2]
bytes = []
# If the maximum value is not in the default max values list, look it up. Majority case.
modifierIndex = getClosestModifier(maximumValue)
command, power = negativePowersOfTwo[modifierIndex]
thisModifier = getModifier(power)
modifierBytes = [power, command]
scaledDefaultValues = allValueRange * thisModifier
roundedValues = [roundToNearestAcceptedValue(sampleValue, valueRange = scaledDefaultValues) for sampleValue in samples]
# Find indices of values in list.
scaledDefaultValues = list(scaledDefaultValues)
indices = [scaledDefaultValues.index(roundedValue) for roundedValue in roundedValues]
# Get (1, 2), (3, 4), ... (n, n + 1).
groupedIndices = [(indices[i], indices[i+1]) for i in range(0, len(indices), 2)]
# Write the bytes out.
bytes = [] + modifierBytes
for start, finish in groupedIndices:
# Calculate the 2-dimensional array offset in hex of the sliding table values.
x = start * (16 ** 0) + finish * (16 ** 1)
return bytes

# Process all the data of the incoming wave.
def processData(data, samplesPerByte = 2, waveFormBytesPerStream = 14, limit = None):
# Typically: 2 compression modulation bytes followed by compressing 28 bytes into 14 bytes such that 1 byte equals 2 samples. Total: 16 bytes per stream.
fourteenByteDataStreams = chunks(data, waveFormBytesPerStream * samplesPerByte)
# Store return data in string (intended for pasting in a hex editor).
result = ""
totalBytes = 0
# For each stream, bet the bytes.
for dataStream in fourteenByteDataStreams:
theseBytes = getBytesForStream(dataStream)
count = len(theseBytes)
# If this is the last stream to be written, signal with 0x03 instead of 0x02 (or 0x06). Then quit.
if (limit is not None) and (totalBytes + count) >= limit:
theseBytes = theseBytes[0:limit - totalBytes]
theseBytes[1] = 0x03
result += "".join(["%02X" % byte for byte in theseBytes])
totalBytes += len(theseBytes)
# Otherwise, add in form of AACC00112233445566778899AABBCCDD.
result += "".join(["%02X" % byte for byte in theseBytes])
totalBytes += len(theseBytes)
return result

# For arguments, accept: 37 (or) 0x25, 0x800 (or) 2048, etc.
def hexOrInteger(inputString):
return int(inputString)
except ValueError:
return int(inputString, 16)
return 0

# Usage: python WaveFile.wav
# Crossed Streams Usage (short): python WaveFile.wav OtherStream.txt InitialWriteFor
# Crossed Streams Usage: python WaveFile.wav OtherStream.txt InitialWriteFor OtherStreamWriteFor ThisStreamWriteFor
if __name__ == "__main__":
import sys
# Arguments: No input file.
if len(sys.argv) == 1:
print "No input file. Usage: python %s WaveFile.wav\nCross Stream Usage: python %s WaveFile.wav OtherStream.txt InitialWriteFor OtherStreamWriteFor ThisStreamWriteFor\nNOTE: ANSI files only." % (sys.argv[0], sys.argv[0])
# Arguments: No length.
if len(sys.argv) == 2:
# Turn length into an integer.
sys.argv[2] = hexOrInteger(sys.argv[2])
# Arguments: Cross stream file.
if len(sys.argv) == 3:
# Arguments: Cross stream initial write for.
if len(sys.argv) == 4:
# Turn initial write for into integer.
sys.argv[4] = hexOrInteger(sys.argv[4])
# Arguments: Cross into other stream for.
if len(sys.argv) == 5:
# Turn cross into other stream for into integer.
sys.argv[5] = hexOrInteger(sys.argv[5])
# Arguments: Cross back into this stream for.
if len(sys.argv) == 6:
# Turn cross back into this stream for into integer.
sys.argv[6] = hexOrInteger(sys.argv[6])
# Arguments.
thisFilename, waveFilename, length, fileWithStreamFromISO, initialWriteFor, otherStreamWriteFor, thisStreamWriteFor = sys.argv
# Print back the filename for reference.
print waveFilename
# Read data.
rate, data = read(waveFilename)
# Get the bytes of the file body.
result = processData(data, limit = length)
if fileWithStreamFromISO is not None:
crossedStream = ""
parts = []
currentFileCaret = 0
currentResultCaret = 0
resultLength = len(result)
# Read the other stream.
with open(fileWithStreamFromISO, "r") as f:
stream =
# Initial write (write X bytes of the result).
count = initialWriteFor * 2
currentFileCaret += count
currentResultCaret += count
# Enter into a loop of crossing back and forth between the other stream and the result stream.
while currentResultCaret < resultLength:
# Write from other stream.
count = otherStreamWriteFor * 2
currentFileCaret += count
# Write from this stream.
if currentResultCaret < resultLength:
count = thisStreamWriteFor * 2
actualEnd = min(resultLength, currentResultCaret + count)
count = (actualEnd - currentResultCaret)
currentFileCaret += count
currentResultCaret += count
crossedStream = "".join(parts)
# Print the byte count of the combined streams.
print "Bytes:", (len(crossedStream) / 2)
# Write the bytes out to a text file (for copying into a hex editor).
with open("%s.bytes" % waveFilename, "w+") as f:
# Print the byte count of the result.
print "Bytes:", (len(result) / 2)
# Write the bytes out to a text file (for copying into a hex editor).
with open("%s.bytes" % waveFilename, "w+") as f:

Final Fantasy Tactics Hacking / Re: SMD Parser (WIP)
« on: June 27, 2014, 10:00:02 AM »
Let the most bizarre game of "Name That Instrument" begin: Instruments.


Disgaea's "Red Moon" (as found on VGMusic)

Instruments 41 and 42 (main, accompaniment).


I have discovered what offset 0x1A-0x1B (bytes 26 and 27) is (part of the 3rd unknown block on this page: Music/SMD). This sound file demonstrates a kind of reverb for values of 0 (0x0 0x0), 1084 (0x02 0x1e), and 15364 (0x04 0x3c; what FFT SMDs have): listen.


I'm sure it's been explained elsewhere about the SMD format, but most of the stuff is not complicated (considering they're mostly just MIDI files, which is a mature specification even back in 1997).

To help visualize the songs, I've added the feature to plot the notes and instruments out (because I'm having trouble finding suitable alternatives for the samples FFT used). If you can understand something like the graphs, I can probably set it up to interactively change the note (because the graphing application already supports interactivity). However, that won't cover any of the effects (and MIDI files depend on them to make engaging music).

The following are note graphs for two songs (songs provided).

LvUp L (i.e. "MUSIC_24.SMD") (listen)

KUMA BALL (i.e. "MUSIC_25.SMD", Mr. Bear Goes to the Ball) (listen)
Bass line only.

View: larger.

Final Fantasy Tactics Hacking / SMD Parser (WIP)
« on: June 25, 2014, 06:41:21 PM »
I know there's already something that will get a MIDI into SMD format. I was curious to get the notes out of an SMD, though. This is the file "MUSIC_24.SMD" (LvUp L) with varying MIDI instruments: MUSIC_24.mp3.

As soon as I finish cleaning up the code, I'll post a link here. I believe some of the unknowns point to the information the BGM test shows (an actual title, author, and help text), but I wasn't that curious.

Tethical / Re: Tethical Channel Trailer
« on: April 20, 2014, 05:34:07 AM »
You mean the stuff they tweet?

Code: [Select]
Why is there a mic?

@gordonramseypage Hell if I know. #rampage?

@gordonramseypage Wer hat dich denn aus dem Käfig herausgelassen? ("Who let you out of your cage?")

@gordonramseypage Моя хата скраю #тихо ("It's not my business"; idiom; #quiet)

@montajeRequerido (assembly required)
@gordonramseypage Él que tiene boca se equivoca. ("We all make mistakes"; idiom)
(Don't care; idiom, "I don't have a camel in the caravan.") @gordonramseypage ليس لي فيها نقة ولا جمل
lam-yeh-seen lam-yeh feh-yeh-heh-alef noon-qaf'teh_marbuta(heh) waw-lam-alef jeem-meem-lam

@gordonramseypage 知らないwwwww ("I don't know, lol.")

Tethical / Re: Installation instructions
« on: April 20, 2014, 12:57:39 AM »
I guess you can consider it bleak. But, the actual project is:
  • About 90% done;
  • Can already target Windows, Linux, and Mac (because of Panda3d);
  • Can be run in a web browser (because of Panda3d);
  • Has a tool to create quality 2d effects;
  • Has a tool to create quality bitmap fonts;
  • Has the ability to display any language (multi-language support), including non-spoken (like Alchemy) and dead languages;
  • Has a tool to easily recolor assets, including while the game is running;
  • Has a tool to create mechanic functions via drag-and-drop;
  • Has a tool to visually preview mechanic functions, including a visual representation of what kind of number to expect out of it;
  • Has a tool to visually preview and edit map tiles;
  • Has a tool to visually preview and edit targeting functions;
  • Has a tool to visually preview and music files, including their tags;
  • Has a tool to visually preview and edit items;
  • Has a tool to visually preview and edit conversations;
  • Has a tool to add and edit races, genders, and names;
  • Has a tool to add and edit skills; and,
  • Has a double-click to test event feature.

I always wonder how a word like "bleak" can be brought up in the face of Kivutar's year(s) of work and my three years of work on this project. I mean, there are videos clearly showing what it's capable of, right? Programming isn't something that necessarily needs to change over time. Once it's done, it's done. There's just a ton of stuff to do as relates to an sRPG, and I've done most of it, which means all you'd have to do to use my work is configure the engine the way you want to use it (and provide content). That means you've got something like 110,000 lines of code at your disposal when you use the engine. When you use the Sprite Animator to create content, that's 32,000 lines. The Font Tool is 43,000 lines. The Sprite Remixer is 15,500 lines. That's 200,500 lines of programming you don't have to do. This being an engine (rather than a game), isn't that the goal -- to not have to write code? Is that outcome bleak?

Tethical / Re: Installation instructions
« on: April 19, 2014, 08:36:31 AM »
Yes, that was my plan. The current plan is cutting that to 1/10th of what I wanted to accomplish, aiming for just $2000, but also providing a playable tech demo related to sRPGs (working on this now), open access for the Kickstarter campaign month to the three tools I've written (the Sprite Animator, Unicode Font Tool, and the Sprite Remixer), a playable demo related to card games (done), and a youtube channel filled with videos that introduce or further explain the claims I make about what the engine can already do (mostly done; Assuming that goal is even reached, more things from the original plan will be incorporated as stretch goals are reached. I'm taking longer to do the planning than I originally said I would because I don't want to have any regrets about this next campaign. If it's not successful, I have to walk away from the project and return myself to the job market.

Tethical / Re: Installation instructions
« on: April 19, 2014, 01:13:34 AM »
The IRC thing and the online demo (which requires running the server as a web application) was something Kivutar was offering back in 2012. He's still tangentially involved in the project, but I think the general lack of interest in such an IRC channel and the online demo may have contributed to his decision not to maintain it. This website (FFH), as far as I understand, is available in part because of him, though, and I've been developing that code since then. There will be a playable tech demo soon, but it will be standalone (rather than something played online).

Tethical / Re: Installation instructions
« on: April 18, 2014, 04:30:12 PM »
That .p3d file is just the files located here wrapped up into a bundle:

You'll need the Panda3d Software Development Kit (SDK) and Python 2.7 (though Panda3d's SDK comes with a pre-configured python shell called ppython.exe). That can be found here: Python 2.7 can be found here:

The two files you'd want to run (i.e. double-click) are located at /server/ and /client/

Note that you may need to make a file like "panda3d.pth" in the Python27 installation folder (or you can put it in /Python27/Lib/site-packages) for Python to find it:
Code: (pth file) [Select]


Tethical / Tethical Channel Trailer
« on: April 13, 2014, 11:44:07 PM »
Channel Trailer

Switch to high-definition to see how it looked on my screen.



First, I want to thank everybody who's contributed sprites to this website over the years; this video would have been a lot harder without you. In many ways, this is a culmination of my own efforts, too. While, yes, they've been used to super-impose Twitter into an sRPG context, at least that can be done in a straightforward manner now. Earlier in the development of this engine, we were suggesting that you just replace whatever character you want with a special symbol. That means you might have typed "A" for the Twitter logo to show up. Now, you can just type an appropriate Unicode symbol, like the "Bird" (U+1F426; 128038) in "Miscellaneous Symbols and Pictographs" (U+1F300 - U+1F5FF). You can also properly comply with Twitter's somewhat meticulous guidelines for using their logo, too. That would have been a nightmare before when one font took a whole day to make! Now, the Unicode Font Tool can spit out huge fonts with a variety of colors and variants without too much effort from a user.

And the styles available might even turn out really good-looking, too!

And, for that rare occasion where I decided to actually paint on a sprite:

Thanks for watching!

Spriting / Re: Choto's maybe sprites
« on: April 09, 2014, 01:21:04 AM »
Those arms are pretty damn convincing. I feel like maybe the left arm is swinging out when it should be swinging in on its farthest back frame. I know you mentioned flashing; that left hand looks disconnected when it's behind the body. I think everybody who knows who Umaro is would recognize it immediately.

General / Re: Someone sent me this scary story
« on: April 03, 2014, 11:12:38 PM »
I am intrigued. What are "tree droppings"? Also, do people still call their friends? They'd probably text.



It's late, you know you should have left the party a bit earlier, but you needed one last drink. You didn't drive because your friend's house isn't too far from your apartment. You came alone; thus, you leave alone. The party was a blast, and you had a big, intoxicated smile on your face as you hugged everyone and exited to the street.

The street was unusually dark due to the street lamps being out, and this area never had a high volume of traffic anyway: so it's just you — alone.

You walk at an even pace, adjusting yourself to the brisk night air and hoping to not look too drunk to a roving police officer with a quota to meet. Your street is narrow with thick foliage on either side. It's often referred to as "Horror Highway" on the account of it looking so stereotypically horror movie-esque. That thought was unsettling at the moment, so you pushed it out of your mind by the thinking of the cute person who you finally got the courage to approach to ask for their number.

You feel a slight chill. You have on your favorite sweater, so you still feel okay. The alcohol is warming your blood as well. Your beloved iPod was lost last week, so the night air and autumn leaves rustling are the only sound track of your walk. Just as you begin to sing your favorite song in your head, you think you hear something... a slight pitter-patter, like footsteps. Looking behind you, you see nothing but the blackness of the night and tree droppings. Turning around, you feel silly: "You're drunk," you say to yourself... but you hear it again! Pitter-pat-crunch! Pitter-pat-crunch! — Footsteps in the leaves! You turn again. No one is there, but you aren't quite sure this time. Time to pick up the pace.

What could it be? Another party-goer? No, they would have called out to you. A deer? Possible, very possible. The more possibles right now, the better to calm your nerves. It seems that this walk is strangely long; the street stretches far in front of you as your feet move in slow-motion. Now you wish you drove tonight.

It only took a minute for the sound to come back again: pitter-pat-crunch, pitter-pat-crunch. It was closer than the previous two times. You stop this time and swing all the way around! The sounds stop, but you see something very vaguely in the darkness, something almost invisible. A nondescript figure is behind you, and, for a quick second, you wonder how long it's been behind you and how you'd missed it before. It didn't move. Neither could you for a second, but you quickly got your wits about you, turned, and began to jog: you have to make it home! The sounds began, and your worst fear was realized: it was chasing you! Crunch! Crunch! Crunch! You found yourself unleashing speed and strength you hadn't called upon in years and ran for your life. Is it a robber or killer? No time to think, just run.

Finally, you make it to your door, and you jump up the stairs to your apartment, tripping on the top one. Keys already in hand, you hurriedly unlock your front door. The crunching had stopped, but you didn't care as you slammed and relocked your door, collapsing on the couch after. It probably was a neighborhood kid messing with you or a stray animal you surmised, adrenaline wearing down now. You sigh with relief and begin to check the rest of the rooms and windows in your apartment. Check. Check. Check. All clear. You're alone.

Heading up to bed, you ponder what had happened: were you drunk and imagining things? You consider calling your friends, but its too late, and you are too tired: it can wait. You slip off your clothes, and you get into bed with only a slight bit of fear left. Snuggling deep into that comfy bed of yours, slowly being over taken by sleep...

"I'm so very glad that you didn't find me. You wouldn't understand, — they never understand — and it makes me... It makes me have to do something drastic. The deep morass of solitude is unforgiving and harsh to ones like me... the ones deemed 'mentally unfit'. They don't understand my needs — they don't understand... They don't — they don't — they don't! But you do... Even if you don't know you do. I love watching you sleep — I love you... you are mine.

"Rest easy, you are alone... with me.


Pages: [1] 2 3 ... 24