Mapping AnotherEden

I'm finally nearing the end of the 1st season main story; I have a boss in the Dimensional Vortex to fight (which I think I should level for), and what appears to be another chapter after that, probably a final dungeon & boss. My party's exhausted, beat down from so many magic-only enemies. Is it too much to want some simple foes Lokido can punch to death?

I played through Lokido's story quests, and… uh, I'm very surprised at a Japanese game handling this subject even remotely well. Lokido's rapidly become my favorite PC for story and smashing heads.

But in the mean time, there's an event! Update, head to Unigan's west gate, and start the quest, and you'll soon get Dierdre as a loaner char; once you finish a short quest she'll be a permanent PC. She's currently a ★★☆☆, but as you complete quest objectives she'll unlock 3-4. And there's a new dungeon with a material giving rewards for how much you collect; I think if you collect the quest, it won't be time-limited, but that's not clear, so I'm going to grind it to completion as fast as I can.

One thing that's been on my mind is the need to map. Early on, every dungeon is linear, super obvious, and generally has 2-4 areas before the boss. Later, there's backtracking, side areas with high-level horrors, switches and chemical triggers… So I'm back to mapping like it's 1984! Well, sort of. Graph paper's unnecessary for this, so I just make line graphs in my notebook, stairs (!, v, ^) are numbered by floor they go to, * is a sparkly, $ is a treasure chest, < is a horror, ? is an area I haven't done yet (spoilers for the Dimensional Vortex):

And I'm starting to make a levelling guide for my own use, just a list of areas with level of monsters & horrors. Then when I have another fresh summon, I can just drag them through slightly-harder areas with a healer to keep them alive, and be up to full level in minimal time.

Pulled a couple more times, got some perfectly nice ★★★★ chars and many junkers, but nothing I really needed, except an upgrade to Ciel. Been grinding Another Dungeon whenever I have time, and got tomes for a bunch of classes I don't have, and 1 book for Rakshasa, so I could have a ★★★★ frog samurai if I wanted… I'll hold off on that. I want to upgrade Riica and Amy (and once I finish the main story, Feinne) first; Aldo and Cyrus do nothing that a non-party char can't do better. There's just a stupid number of these ★★★ junk chars, I wish they were good for something; if they had story quests, or anything. I can level them for chrono stones, that's about it.

I'm still enamored of this game's scenery & music, and good hard crunchy turn-based combat. I can nuke most bosses long before they even hurt me, because I level, gear up, and prepare for their weaknesses and attacks. Filling in the last few items in the store can be a pain, though: Grind an area, get 3/5 of the mats I need for one thing. Go back tomorrow.

Buncha spoilery screenshots:

What I'm Watching: Appleseed (1988)

As I noted in Alphaville, Appleseed covers similar ground. Been a few years, so I rewatched it.

But back up a bit to the manga. Shirow Masamune's first manga was Black Magic, about a computer-controlled society of animal-people on a habitable Venus, 60 million years ago when the Earth is full of dangerous dinosaurs, and a powerful young sorceress and her friends who hang out at the Onimal bar fighting the AI throughout the solar system. Rogue AI death machines (in that case cute little "M-66" infiltration/assassination robots) are released, death and mayhem ensue, civilization falls because people lazily give up control to the machines. It's a fantastic book, but too silly at times for the message he wanted to send. There is an "M-66 Black Magic" anime about just the robots but set on modern Earth, incredibly dumb though it does have some T&A which young Mark enjoyed.

Appleseed's 4-volume manga is a reboot of similar ideas, set after nuclear war, with an artificial city controlled by an AI "Gaia", populated by bioroids (in the manga, they go into detail about just how artificial they are; the older ones are more machine than biological and tied directly into Gaia) as servants to a fraction of Humanity. But servants with power don't remain servants. Athena, city administrator biodroid, is torn between wanting to get rid of the Humans entirely, and fulfilling the original mission of the city; and ultimately she's just a tool of Gaia. Wasteland survivors have been brought into the city and haven't really been domesticated, but are trying to make the city work. And terrorists want to tear down the system.

The 1988 movie covers the first volume, sort of, and a bit of the others, and doesn't use the appleseed of the title. There's been a bunch of remakes, but the original's the only one that addresses the moral issues at all. The first two CGI films (Appleseed (2004) and Appleseed Ex Machina (2007)) are unspeakably bad action flicks with preposterous mega-boob physics and cartoon blowjob-doll face for Deunan (who is not so endowed in the manga or anime), and while I haven't seen the reboot CGI flick Appleseed Alpha (2014), it's a "prequel" which has nothing to do with the manga. There's also a TV series Appleseed XIII (2013) which is more action flicks about WOO DEUNAN SHOOT GUNS.

I wouldn't classify any of these exactly as "cyberpunk", because they're not about the street finding new uses for the military-industrial complex's technology; they're about the military-industrial complex. Hard SF, and in the original with a political axe to grind against AI.

I plan to reread Ghost in the Shell's 3 volumes of manga as well, and then I'll comment on the competent but over-simplified 1995 movie and the other junk around that franchise, which follows a similar pattern.

So, read comic books for big ideas, kids, don't look at fucking moving pictures. But I'll talk about the moving picture anyway.

Obviously, this is peak '80s. Like more '80s than the '80s were. Big hair, shoulder pads in women's suits, pastel colors, neon, sleek but sharp vehicles instead of little melted blobs, battlesuits that look like perfect Japanese motorcycles instead of piles of scrap metal held together with hot glue. The music is new wave and smooth jazz, what the Kids Today™ call "synthwave" but this is real, not synthetic, synth music. Cel animation is expensive and backgrounds are pretty static, there's none of this bullshit of using 3D CGI with light cel shading to pretend you're drawing something, no, Human animators toiled over every frame. If you don't like the '80s aesthetic, get the fuck out, you're not welcome here.

Cop Karon and artist Freya ("Fleia") are soon separated by her suicide, from feeling as trapped in a gilded cage as their pet birds, and as we see later in the film, the city's bioroid administration does not respond with kindness and care, but with clinical research on the survivor.

Cute but deadly Deunan (possibly modeled on Markie Post) and cyborg smoothy Briareus (Richard Roundtree in a cyborg bunny face?) are in ESWAT, cleaning up the messes normal cops can't, and a cyborg terrorist getting away and killing a few of their buddies gets them motivated to investigate, though on-screen that largely consists of them wearing trenchcoats, busting down doors, and body-bagging potential informants.

Hitomi, a bioroid who rescued the main characters and many more Humans from the wasteland and acts as their social worker, gets back into the city, in what might be my favorite view of any city: She wakes on a helicopter reflected in solar panels, rushes to the other side to see the city in light. It's only a momentary shot, but makes me think the city might not be so bad. Hitomi's the heart of the manga, and the anime tries its best, with limited screen time. The party at the Onimal bar (a relic from the Black Magic manga) is the only time her faux-Human relations really come up: She loves all her rescued strays, and her would-be boyfriend/pathetic stalker isn't really enough for that love.

The bioroids as machines isn't touched on much in the anime; those other than Hitomi are shown only as drones or would-be tyrants like Athena, and they're DNA-edited and grown in tanks, but just how much of a replaceable part most of them are isn't brought up until Athena tries to decide who lives and who dies.

The Human Liberation Front terrorists do eventually discuss their motives and objectives, to get hold of a giant spider-tank which is the prototype for a fleet of spider-tanks to be directly operated by Gaia; then Humanity will be totally cut off from power. But to get it, they have to lock out Gaia, and there's a key for that. A failsafe which, very deliberately, only Human sympathizers can use.

The action scenes in this aren't Gundam quality, and they're not bloody like many later versions, but they're fine for telling the story. The couple of times the terrorists fight up close brings home just how deadly Landmates (mecha) are in close combat and as mobile infantry/artillery. I'm not sure the "BAN LANDMATES" graffiti is ever visible in the anime, but it's constant in the manga, and kind of an in-joke for old anime fans. While the anime has cyborgs with various levels of replacement, there's no robots, which are a major element of the manga, as a thing even lower than bioroids but also threatening to replace Humanity.

Where this falls down is the final sequence inside Gaia; they have maybe 10 minutes to squeeze in half a volume of arguments and action. In the manga, this is a place where Deunan has to make a moral decision which will change the course of Human history: Free will and endless wars, or inhuman tyranny, or is there a third path? Here, it's just resetting a machine, and what the machines think of that isn't discussed.

★★★★☆, it'd be 5 if they'd ever adapted the rest of the manga, but nobody seems interested in making movies with political philosophy against AI control, I wonder why.

How to Configure Vim

I just had the unfortunate experience of using the standard vimrc on ubuntu (which I loathe, but it's the most stable OS on this VPS), and now I sympathize with people who think they can't use Vim. So here's my much less annoying vimrc. You'll want to make a ~/tmp folder if you don't have one. Adjust the augroups for the languages you use; I was forcing some syntax files to load because they had bad defaults like PHP as HTML, no idea if that's improved.

" Mark Damon Hughes vimrc file.
"
" To use it, copy it to ~/.vimrc

set nocompatible    " Use Vim defaults (much better!)
filetype plugin on
set magic
set nrformats=

set errorbells
set nomore wrapscan noignorecase nohlsearch noshowmatch
set backspace=indent,eol,start

"set smarttab expandtab shiftwidth=4
set nosmarttab noexpandtab shiftwidth=8 tabstop=8

set encoding=utf-8 fileencoding=utf-8
set listchars=tab:__,eol:$,nbsp:@

set backup backupdir=~/tmp dir=~/tmp
set viminfo='100,f1,<100

set tw=80       " I use this default, and override it in the autogroups below

" ctrl-] is used by telnet/ssh, so tags are unusable; I use Ctrl-J instead.
map <C-J> <C-]>

" Don't use Ex mode, use Q for formatting
map Q gq

map <Tab> >>
vmap <Tab> >
map <S-Tab> <<
vmap <S-Tab> <

" Always have syntax highlighting on
syntax on
hi Identifier guifg=blue
hi Statement term=bold guifg=blue
set guifont=Menlo_Regular:h14
set guioptions=aAemr
set showtabline=2
set mousemodel=popup_setpos

augroup c
    au!
    autocmd BufRead,BufNewFile *.c set ai tw=0
augroup END

augroup html
    au!
    autocmd BufRead,BufNewFile *.html set tw=0 ai
    autocmd BufRead,BufNewFile *.html source $VIMRUNTIME/syntax/html.vim
augroup END

augroup java
    au!
    autocmd BufRead,BufNewFile *.java set tw=0 ai
augroup END

augroup objc
    au!
    autocmd BufRead,BufNewFile *.m,*.h set ai tw=0
augroup END

augroup php
    au!
    autocmd BufRead,BufNewFile *.php,*.inc set tw=0 ai et
    autocmd BufRead,BufNewFile *.php,*.inc source $VIMRUNTIME/syntax/php.vim
augroup END

augroup python
    au!
    autocmd BufRead,BufNewFile *.py set ai tw=0
augroup END

What I'm Watching: Alphaville (1965)

I've seen Jean-Luc Goddard's bizarre SF film every decade or two. It makes no more sense each time but a different kind of non-sense. A Rorschach test for where society is.

Lemmy Caution 003 (Eddie Constantine, reprising his role from Poison Ivy and something like 20 other spy flicks), ostensibly a reporter for Pravda, wanders a bizarre artificial city, with a short target list. Everyone he meets is a drone, but often a drone with a non-drone social role, such as the "Seductress, Level 3" (what's level 1 or 2?!), or the scientists "Heckel & Jeckel" (from the cartoon crows). Or, ultimately, Dr. Nosferatu née Von Braun; they're as subtle as supervillains ever are.

His former colleague Henri Dickson (Akim Tamirof) is a dissolute wreck, unable to adapt to life in Alphaville, but unable or unwilling to betray it or leave before it consumes him. A buck for a room, a buck for a beer, and a buck for a whore, is a good way to kill a man. The analogy that ants started as individuals, with art and creativity, and then became part of the hive and now have nothing… it's very like Frank Herbert's Hellstrom's Hive, but that relies on biology not technology. The theme's also used in Shirow Masamune's Appleseed (read the manga, but the '80s anime deals with this part exceptionally well too), where the 20% natural Humans (using the term loosely for cyborgs like Deunan and Briareous) in Olympus are maintained by 80% Bioroids who pass as Human under the city's all-controlling artificial intelligence, and still the Humans despair and kill themselves because they're birds in a cage.

The extreme conformity and social control making everyone march along to their destruction is very familiar. The execution theatres are more humane than our current model of persecuting anyone who has ever made Human mistakes; they get shot and drowned by pretty girls, while we continue persecuting them forever. One can never expiate sin in 21st C society.

The croaking voice they used as the voice of the computer Alpha 60 is awful; HAL 9000 in 1968 is more like the real future of Siri. And yet obviously you should not ever watch movies in dub, only in the original language with subtitles. The slideshow/exhibition as art of the artificial society is plausibly unbearable to watch for a normal Human, not one of the mutants/controlled people of the city.

The metaphor the city uses of distant planets and galaxies confuses many viewers, and for that matter I don't know that Goddard didn't mean it literally himself even though it's bullshit; but it makes more sense that Alphaville is what it seems, a city in some isolated wasteland, transformed by Alpha 60, and everyone has been taught they're on a distant planet, along with all the other Newspeak and programming they endure. There's no evidence of spaceships, they drive in and out of Alphaville, and it's merely set 10 years in the "future" (stated: 30 years since America & Soviets got nuclear weapons, so 1975 from the film's 1965). And of course Lemmy Caution makes no sense wandering into a science fiction world, rather than just doing his James Bond-like spy shit in a mad genius's experiment.

The city looks great as long as all you can see are dark skylines and brightly-lit office windows, but in the occasional lit street scenes it looks like a sad ex-Warsaw Pact country. Star Trek managed a few large-scale sets in 1966, even though it often cheated and just had a matte and some styrofoam rocks, not actual street scenes. The car chase later is comically bad, slow-motion and visibly just a shitty suburb with cars saved from a wrecker.

The nonsense of "love" in the film is the one thing I really can't stand. Lemmy and Natacha have no connection, barely any interaction; she might at best be infatuated with the weird stranger, and her charms are superficial. I know, the French don't necessarily need such depth to start calling it "amour", but this is a weekend fling.

(reading "bible" aka redacted Newspeak dictionary): "Conscience. Not there. So nobody here knows what it means anymore."
—Natacha Von Braun (Anna Karina) describing Silicon Valley

My Rorschach reading for this viewing is conscienceless men using artificial intelligence to plan the destruction of Humanity and annihilation of home systems, to wit Facebook and Google (Amazon, Microsoft, and Apple being more of a Brave New World/THX-1138 style of consumerist dystopia). Sadly it is not so simple to solve as a few bullets and asking an AI some dumb riddles to blow its fuses (Alphaville, Logan's Run, and Star Trek got their computers from the same substandard maker, it seems). HAL goes crazy, but he's not defeated by anything as lame as a Sphinx.

I… IN… FIN, the ending spells. Is Alpha 60 saying "I am finished", or is it trying to spell infinity?

★★★★★ but it's madness.

Writing a Doorgame

So, how did I write this? Mystic BBS has a Pascal-based scripting, but I didn't really like using that; it's very powerful, but a pain in the ass to code/compile/test on, and it's just different enough from normal FreePascal to annoy me. And then tried to use Mystic's "integrated Python", which just doesn't work; I tried everything to make it find Python. And it's 2.7, so bleh. BUT: Adding a doorgame with the menu system lets you specify any executable, including a Python 3.7 script, and pass in a few command-line arguments about user and directories, so that's what I did (though the server I'm using only has Python 3.6, which hasn't been a problem yet, but be aware)…

The BBS handles all the redirection from network to stdin/stdout, otherwise I could use inetd for the same thing, but then I'd have to deal with all the login and networking state. Doing this as a door simplifies programming back to 1970s level!

I'll eventually open source it, but while I'm iterating and early on, I want to keep some of my secrets.

ANSI Color

There's ANSI color everywhere, of course. curses is fucking awful and archaic, and built to handle many incompatible terminals, which isn't a problem anymore. Mystic uses the same trick I'm using, and many others back in the day: Strings have escape codes |XX in them. Mystic uses numbers for colors, which is awful, and has hundreds of BBS-specific strings, which I have no need of. So all I did is this:

# set to 1200, 9600, etc. to simulate modem transmission rate
kModemSpeed = 0
kLinesPerScreen = 24

kAnsiEsc = "\x1b["

#      0   1   2   3   4   5   6   7   8   9   10
kAnsiBoxLine =  ["─",   "│",    "┌",    "┬",    "┐",    "├",    "┼",    "┤",    "└",    "┴",    "┘",]
kAnsiBoxDouble =["═",   "║",    "╔",    "╦",    "╗",    "╠",    "╬",    "╣",    "╚",    "╩",    "╝",]
kAnsiBoxHeavy = ["━",   "┃",    "┏",    "┳",    "┓",    "┣",    "╋",    "┫",    "┗",    "┻",    "┛",]

# ansiFilter, called by write/writeln, replaces |XX with escape codes:
kAnsiEscapeCode = {
    "CD": kAnsiEsc+"J",         # Clear Down
    "CL": kAnsiEsc+"2K",            # Clear Line
    "CS": kAnsiEsc+"2J"+kAnsiEsc+"H",   # Clear Screen
    "DI": kAnsiEsc+"2m",            # Dim
    "ES": "\x1b",               # Escape ASCII 27
    "GG": "\x07",               # Bell ASCII 7
    "HR": kAnsiBoxDouble[0] * 79,       # Horizontal Ruler
    "LI": kAnsiEsc+"1m",            # Light/Bright
    "PI": "|",              # Pipe char |
    "RE": kAnsiEsc+"0m",            # Reset Attributes
    "UN": kAnsiEsc+"4m",            # Underscore

    "FK": kAnsiEsc+"30m",           # Foreground Black
    "FR": kAnsiEsc+"31m",           # Foreground Red
    "FG": kAnsiEsc+"32m",           # Foreground Green
    "FY": kAnsiEsc+"33m",           # Foreground Yellow
    "FB": kAnsiEsc+"34m",           # Foreground Blue
    "FM": kAnsiEsc+"35m",           # Foreground Magenta
    "FC": kAnsiEsc+"36m",           # Foreground Cyan
    "FW": kAnsiEsc+"37m",           # Foreground White

    "LK": kAnsiEsc+"1;30m",         # Foreground Light Black
    "LR": kAnsiEsc+"1;31m",         # Foreground Light Red
    "LG": kAnsiEsc+"1;32m",         # Foreground Light Green
    "LY": kAnsiEsc+"1;33m",         # Foreground Light Yellow
    "LB": kAnsiEsc+"1;34m",         # Foreground Light Blue
    "LM": kAnsiEsc+"1;35m",         # Foreground Light Magenta
    "LC": kAnsiEsc+"1;36m",         # Foreground Light Cyan
    "LW": kAnsiEsc+"1;37m",         # Foreground Light White

    "BK": kAnsiEsc+"40m",           # Background Black
    "BR": kAnsiEsc+"41m",           # Background Red
    "BG": kAnsiEsc+"42m",           # Background Green
    "BY": kAnsiEsc+"43m",           # Background Yellow
    "BB": kAnsiEsc+"44m",           # Background Blue
    "BM": kAnsiEsc+"45m",           # Background Magenta
    "BC": kAnsiEsc+"46m",           # Background Cyan
    "BW": kAnsiEsc+"47m",           # Background White
}

def ansiFilter(s):
    """Replaces |XX codes with values defined in `kAnsiEscapeCode`."""
    out = ""
    slen = len(s)
    i = 0
    while i < slen:
        c = s[i]
        i += 1
        if c == "|":
            esc = s[i:i+2]
            i += 2
            out += kAnsiEscapeCode.get(esc, esc)
        else:
            out += c
    return out

def ansiGoto(pt):
    return "%s%s;%sH" % (kAnsiEsc, pt[1], pt[0])

def readln(prompt):
    """Calls input after filtering prompt."""
    return input(ansiFilter(prompt))

def write(s, end=None):
    """Filters `s` and writes to stdout, writes `end` if needed, flushes stdout."""
    if type(s) != str:
        s = str(s)
    lines = 0
    out = ansiFilter(s)
    for c in out:
        sys.stdout.write(c)
        if kModemSpeed:
            time.sleep(1.0/kModemSpeed)
        if c == '\n':
            lines += 1
        if lines >= kLinesPerScreen:
            readln("|RE|FC[MORE]|RE")
            lines = 0
    if end:
        sys.stdout.write(end)
    sys.stdout.flush()

def writeln(s):
    """Calls write with `end='\n'`."""
    write(s, end='\n')

Now if I want someone to talk in bold cyan (which I chose as my standard NPC color), I just
writeln("|RE|LC%s says, \"Mellow greetings, sir!\"|RE" % (npc["name"],))
RE before and after text is important, resetting colors from previous strings.

The one problem is I can't use | in ASCII art, so to generate my big character logos, I write a script mysticFiglet.zsh, making : the vertical char:

figlet -f small "$*"|sed "-es/|/:/g"

Flow Control

The great part about a doorgame is, no event loop. Oh, there's a REPL of sorts:

    def run(self):
        self.running = True
        try:
            : Python 3.8: while line := self.prompt():
            while self.running:
                line = self.prompt()
                if line == None:
                    break
                words = line.split()
                if len(words) == 0:
                    continue
                self.parseWords(words)
            self.save()
            self.doHelp("?", "_outro")
            return 0
        except (EOFError, KeyboardInterrupt):
            self.save()
            return 0
        except Exception:
            writeln("|GG|RE|BR|FWERROR! Something went wrong.|RE")
            logging.error("", exc_info=True)
            self.save()
            return 1

It just blocks at input (called by readln, called by prompt), and continues on until self.running = False, or an exception is thrown. Whee! The usual game loop makes it impossible to get stateful input without leaving giant memos of current state, so the event loop can keep cycling at 60fps.

Data Files

One solution would be to just make everything in classes, and use Python's pickle to archive everything. But the problem is that's pure binary, you can't easily hack on it or see what's happened, and on load you can't change any values. So instead I only use primitive types and archive everything to and from JSON. Floor maps and Players have a toData() method which produces a JSON-compatible data structure, and their initializers take a data structure and pull out or translate the values they need. It's a little more work, but means I can debug faster and add new fields faster.

The floors use a super-simple data file format, with an ASCII roguelike map and a series of command lines to create NPCs, traps, text, etc. The weapons and armor are listed in an even simpler format, with space-delimited fields.

Both the command lines and item formats are just run thru a tokenizer I wrote, that reads words, "strings", numbers, and [lists]:

type    name        cost    stat    bonus   damage  inflicts    twoh
wpn "Club"      5   Str 0   3   [blunt]     0

becomes:

{'type': 'wpn', 'name': 'Club', 'cost': 5, 'bonus': 0, 'damage': 3, 'inflicts': ['blunt'], 'twoh': 0}

Mystic Dungeon BBS

As previously mentioned, I'm interested in BBS's.
So I set mine up! After 30 years, The Dungeon is back as The Mystic Dungeon BBS!

mystic-dungeon-2019-02-23

Connect with: telnet mysticdungeon.club 1666
(soon I'll have a proper domain, and SSL cert so you can ssh host 2666)

Set your terminal for black background, 80x25, and UTF-8, I dunno what it'll do to DOS CP, but everything's Unicode now.

If your Mac doesn't ship with a telnet anymore, you can grab the previous OS's one from a backup, or port install inetutils and then use gtelnet.

Of course visit the Doors to try the Mystic Dungeon doorgame!

Dungeon-2019-02-23-23.56.20
Dungeon-2019-02-23-23.56.45

I wrote the start of this last weekend in a Ballmer Peak, and have been adding to it since. Now it has a town and a dungeon with 8 levels, 24 kinds of monsters, combat, an innkeeper for resting, a merchant for buying equipment, and banker for saving money between deaths.

Coming up are potions and other useful items, traps, chests (which are often trapped), and magic.

Right now, only Fighters make any sense to play, though different races have some advantages (Dwarf especially for long-range darkvision in the dungeon, since I don't sell light sources yet!). The character shown is a bit cheaty on cash and XP; it'll take a long, long time to reach that level legit.

Only Solutions Tuesday Music

Time to code like it's 1985, all fucked up on soda1 and listening to movie soundtracks:


  1. Pepsi, which is now ugh. I also drank (still do) very strong tea, just steep breakfast tea until it's undrinkable, then drink. I didn't switch to coffee, Jolt, and Coke until college.