変なことをしなければWinMugen用のキャラクターはLinuxMugenやMUGEN1.0でも動作します。ここはMUGEN1.0への対応 (専用化) についてウチがやっていることのメモです。
とりあえずSFFはMUGEN1.0用にV2化します。幸いにもsprmake2というツールがあるので利用しましょう。さらにウチではSAEを基本的に使っているので、楽してSFFv1からSFFv2へ変換するためのスクリプトを組みました。
sae2sffv2.py
#!python
# -*- coding: utf-8 -*-
import sys
import os
import re
import csv
"""
指定ディレクトリの中を指定パターンで検索する
"""
def listFiles(dir, pattern):
matcher = re.compile(pattern)
fileList = []
for root, dirs, files in os.walk(dir):
for file in files:
if matcher.match(file):
fileList.append(os.path.join(root, file)[len(dir) + 1:].replace('\\', '/'))
return fileList
"""
スプライトレコード
"""
class Record:
def __init__(self, csvRow):
self.file = csvRow[0]
self.group = int(csvRow[1])
self.number = int(csvRow[2])
self.x = int(csvRow[3])
self.y = int(csvRow[4])
self.shared = (int(csvRow[5][:1]) is 1)
def __str__(self):
line = '%d, %d, %s, %d, %d' % (self.group, self.number, self.file.replace(' ', '_'), self.x, self.y)
# if not self.shared:
# line = line + "\t? usepal = -1"
return line
class SpriteList:
def __init__(self, input):
reader = csv.reader(input)
self.records = []
for row in reader:
self.records.append(Record(row))
def __iter__(self):
for record in self.records:
yield record
"""
SFF version2定義クラス (for sprmake2)
"""
class SFFv2:
def __init__(self, input, name):
self.sprites = SpriteList(input)
self.name = name
self.compress5 = 'lz5'
self.compress8 = 'rle8'
self.compress24 = 'none'
self.decompressOnLoad = True
self.detectDuplicates = True
self.autoCrop = True
self.detectPaletteDuplicates = True
self.discardDuplicatePalettes = True
self.reversePalette = False
self.reversePngPalette = False
self.palettes = {}
def addPalette(self, group, no, fileName):
if group not in self.palettes:
self.palettes[group] = {}
self.palettes[group][no] = fileName
def setDefaultPalette(self, group, no):
self.defaultPalette = (group, no)
def output(self, out):
self.outputBase(out)
self.outputDefaultOptions(out)
self.outputPalettes(out)
self.outputSprites(out)
def outputBase(self, out):
out.write("[Output]\n")
out.write("filename = %s\n" % self.name)
out.write("\n")
def outputDefaultOptions(self, out):
out.write("[Option]\n")
out.write(";input.dir =\n")
out.write("sprite.compress.5 = %s\n" % self.compress5)
out.write("sprite.compress.8 = %s\n" % self.compress8)
out.write("sprite.compress.24 = %s\n" % self.compress24)
out.write("sprite.decompressonload = %d\n" % (1 if self.decompressOnLoad else 0))
out.write("sprite.detectduplicates = %d\n" % (1 if self.detectDuplicates else 0))
out.write("sprite.autocrop = %d\n" % (1 if self.autoCrop else 0))
out.write("pal.detectduplicates = %d\n" % (1 if self.detectPaletteDuplicates else 0))
out.write("pal.discardduplicates = %d\n" % (1 if self.discardDuplicatePalettes else 0))
out.write("pal.reverseact = %d\n" % (1 if self.reversePalette else 0))
out.write("pal.reversepng = %d\n" % (1 if self.reversePngPalette else 0))
out.write("\n")
def outputPalettes(self, out):
out.write("[Pal]\n")
for group in self.palettes:
palettes = self.palettes[group]
for no in palettes:
out.write("%d, %d, %s, 0, 255\n" % (group, no, palettes[no]))
out.write("\n")
# out.write("[Option]\n")
# out.write("sprite.usepal = %d, %d\n" % (self.defaultPalette[0], self.defaultPalette[1]))
# out.write("\n")
def outputSprites(self, out):
shared = False
out.write("[Option]\n") # 独自パレット
out.write("sprite.usepal = -1\n")
out.write("[Sprite]\n")
for sprite in self.sprites:
#sys.stderr.write("%s, %d\n" %(sprite.group == 9000, sprite.group))
if not shared:
if ((sprite.group == 9000 or sprite.group == 0) and sprite.number is 0):
#sys.stderr.write("%d, %d\n" % (sprite.group, sprite.number))
shared = True
out.write("[Option]\n")
out.write("sprite.usepal = %d, %d\n" % (self.defaultPalette[0], self.defaultPalette[1]))
#out.write("sprite.removecolors = 0, 255\n")
out.write("[Sprite]\n")
else:
if not sprite.shared:
shared = False
out.write("[Option]\n")
out.write("sprite.usepal = -1\n")
out.write("[Sprite]\n")
out.write("%s\n" % sprite)
out.write("\n")
def usage():
sys.stderr.write("USAGE: %s <sff name> [<input csv> [<output file>]]\n" % (sys.argv[0]))
sys.stderr.write("\t<sff name>\tName of SFF File Name\n")
sys.stderr.write("\t<input csv>\tSff Air Editor style SFF Export CSV\n")
sys.stderr.write("\t<output file>\tOutput File Name\n")
sys.stderr.write("\n")
sys.stderr.write("<input csv> and <output file> can be omitted. If omitted these elements, uses <STDIN> for input and <STDOUT> for output.\n")
if __name__ == '__main__':
input = sys.stdin
output = sys.stdout
name = None
if len(sys.argv) > 1:
name = sys.argv[1]
else:
usage()
exit()
if not name is None:
if len(sys.argv) > 2:
input = file(sys.argv[2], "r")
if len(sys.argv) > 3:
output = file(sys.argv[3], "w")
sffv2 = SFFv2(input, name)
palettes = listFiles(os.getcwd(), ".*act$")
for i in range(0, len(palettes)):
sffv2.addPalette(1, i + 1, palettes[i])
sffv2.setDefaultPalette(1, 1)
sffv2.output(output)
else:
sys.stderr.write("Error: No given sff name\n")
# for file in listFiles(os.getcwd(), '.*\\.act$'):
# print file
renames.awk
BEGIN {
FS = ",";
}
{
printf("rename %s %s\n", $1, gensub(/[[:space:]]/, "_", "g", $1));
}
この二つのスクリプトは
sae2sffv2.pyでsprmake2用defファイルを作成する
sae2sffv2.pyはファイル名部分を"<通し番号> <グループ>-<番号>" のファイル名を"<通し番号>_<グループ>-<番号>" に変換する
renames.awkからファイル名変換用batファイルを出力する
batを実行してファイル名を変換する
sprmake2にdefファイルを渡してSFFv2形式のSFFを作成する
という手順で使用する。
ファイル名を変換する理由は、単にsprmake2がスペースを含むファイル名を扱えないからである。
SFFを変換したら、キャラクター側で使う色を設定する
defに[Palette Keymap]セクションを書き、どのキーとパレット番号が対応するかを設定する。
[Palette Keymap]
a = 1 ; 1P
b = 2 ; 2P
c = 3 ; 3P
x = 4 ; 4P
y = 5 ; 5P
z = 6 ; 6P
a2 = 7 ; 7P
b2 = 8 ; 8P
c2 = 9 ; 9P
x2 = 10 ; 10P
y2 = 11 ; 11P
z2 = 12 ; 12P
パレットを設定しただけでは実は色を変更することは出来ない。実態は5900番ステートの先頭にある次の記述である。
[State 5900: Set Palette]
type = RemapPal
trigger1 = 1
source = 1, 1
dest = 1, PalNo
つまり、初期化処理のひとつとして選択されているパレットに色を変更しているのである。
ここを調整すればパレット番号だけでなく、パレットグループも変えることが可能と思われる。
MUGEN1.0では画面の高さと幅を取得する「GameWidth」「GameHeight」トリガーが追加された。これを用いて、画面下に張り付くタイプのゲージの位置を調整する。具体的にはGameHeight - 10などとして、画面下からどれだけ離れるか、を設定することになる。
その他画面下が基準となるエフェクトや、画面外の位置を基準とするようなエフェクト・動作の調整を行う。
MUGEN1.0ではゲーム難易度を取得するためのAILevelトリガーが追加されている。このトリガーは0~8の値をとる模様。0は人操作、1以上はCPU操作である。
さしあたってはAIレベルにAILevelトリガーを設定する。なお、このトリガーを使用するとAI起動用コマンドならびに起動用ヘルパーを用いることなく、最速でAI・人操作の判断を行える。