shingoushori's dialy

音信号処理を専ら研究していた元博士後期課程の学生によるメモ

PythonでSimutransの街の座標をずらしてみた

Simutrans(http://japanese.simutrans.com)は, 素晴らしい.

"日本の役場セット" ( アドオン/建物2 - Simutrans日本語化・解説 ) には, ニヤニヤしてしまいます.

しかし, 一段階大きい庁舎に変わる時に, 別のところに移る & 元の場所が適当な市内建築に置き換わる.
...(略)... というわけで, とりあえずその界隈に改めて,
元々の庁舎と同じ建物をランドマークとして建てられるようにしよう ... というのを、前回やりました
Simutransで, "日本の役場セット"をランドマークとして遺せるように魔改造してみた - shingoushori's dialy

その後、あれこれセーブデータとにらめっこしていたところ、単に1つの街に複数の庁舎を建てるだけなら、魔改造したpakを用意する必要はないことがわかりました

さらに、新庁舎が建った時、元々の庁舎を削除させないだけなら、街の座標を元々の庁舎の座標からずらすだけで良いことがわかりました

ずらし方は完全に探れてはいません
とりあえず多分庁舎の北西の角が街の座標なので
そこから北西方向に1マスずつずらしてみました

さて、ここで今回眉間にシワが寄り頭を掻きむしったのが、
1つの街に関する情報の取得です
これまで扱ってきたような、特定のtagの木にまとまっていません
仕方がないので、セーブデータを見比べて傾向を捉えた怪しい探り方をしています
実に危うい

↓サンプルコードです。
↓※絶対に元のデータを上書きしないでください。
↓※データや設定が破壊されても、責任は一切負えません。
↓version=0.120.4, 自作改造パック pak.japan_custom 用になっていますが ... 適宜正しい値になっていないと、面白いくらい開けないデータになります、多分
↓私はそれで苦戦しました
↓さらに、文字コードとかあれこれ読み込みが繊細です。

# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

import xml.etree.ElementTree as ET

filename_in = 'test_in.sve'
filename_out = 'test_out.sve'

f = open(filename_in)
data1 = f.read()  # ファイル終端まで全て読んだデータを返す
f.close()

data2 = data1.replace("<![CDATA[","<CDATA>")
data2 = data2.replace("]]>","</CDATA>")

root = ET.fromstring(unicode(data2.encode('utf-8')))

# マップのサイズを取得
N_x = int(root[0][0].text)
N_y = int(root[0][13].text)
print str(N_x) + " " + str(N_y)

root_iter = root.iter()
while True:
    try:
        i = root_iter.next()
        if (i.tag == 'planquadrat_t'):
          break
        if (i.tag == 'CDATA'):
          j = root_iter.next()
          if (j.tag == 'koord'):
            print(i.text + " " + j[0].text + " " + j[1].text)
            j_x = int(j[0].text)
            j_y = int(j[1].text)
            if (j_x == 0) :
              j_x = 2 # 元庁舎のx方向の幅の最大値が 2だとする
            else :
              j_x = j_x - 1
            if (j_y == 0) :
              j_y = 2 # 元庁舎のy方向の幅の最大値が 2だとする
            else :
              j_y = j_y - 1
            j[0].text = str(j_x)
            j[1].text = str(j_y)
            print(" -> " + j[0].text + " " + j[1].text)
    except StopIteration:
        break

data3 = ET.tostring(root, encoding="utf-8")

data4 = "<?xml version=\"1.0\"?>\n" + data3
data4 = data4.replace("<CDATA>","<![CDATA[")
data4 = data4.replace("</CDATA>","]]>")
data4 = data4.replace("<CDATA />","<![CDATA[]]>")
data4 = data4.replace("<Simutrans pak=\"pak.japan_custom\" version=\"0.120.4\">","<Simutrans version=\"0.120.4\" pak=\"pak.japan_custom\">")

f = open(filename_out,'w') # 書き込みモードで開く
f.write(data4)  # 引数の文字列をファイルに書き込む
f.close()

Simutransで, "日本の役場セット"をランドマークとして遺せるように魔改造してみた

Simutrans(http://japanese.simutrans.com)は, 素晴らしい.

"日本の役場セット" ( アドオン/建物2 - Simutrans日本語化・解説 ) には, ニヤニヤしてしまいます.

しかし, 一段階大きい庁舎に変わる時に, 別のところに移る & 元の場所が適当な市内建築に置き換わる.
ここで, 置き換わった市内建築がしょんぼりなのだ.
その界隈は元々は役場があった訳で, 都心である. そこが急に 1マスずつに細々とした建物になるのである.
... というわけで, とりあえずその界隈に改めて,
元々の庁舎と同じ建物をランドマークとして建てられるようにしよう と思ったのです.


↓ 実験中 ... ↓

"日本の役場セット" ( アドオン/建物2 - Simutrans日本語化・解説 ) は, 
アドインの元データが見つからなかったので, バイナリをいじってしまいます.
↓ 前回バイナリをいじった時の記事はこちら ↓

 

<無理やり直し手法>

バイナリエディタで, 該当箇所を書き換える.

筆者はmacがメインでして,
バイナリエディタには 0xED ( http://www.suavetech.com/0xed/0xed.html ) を使いました.

[核心部]
・0すたーとの16進数で 00050 byte の 05 を 01 に変更する
[必要な修正] : 名前を変える
・TEXT ... の後ろの文字列
その前 に 00 を挟んだ xx が文字列長+1
例えば 02_CITY では 08

そんな感じでとりあえずなんとかできました
うまくいかないと Simutrans 自体の起動ができなくなるので,
魔改造対象のアドイン, 追加するpak, 追加するセーブデータ のバックアップは
マメに取っておくべきです. 恐ろしや

PythonでSimutransの特定の行と列の格子点にある特定の種類の道路の接続方向を揃えてみた

素敵な交通シミュレーション Simutrans で、
↓人工地盤っぽい感じで格子状に道路+線路を張り巡らしたいのです、私は
Simutransで人工地盤してみる - shingoushori's dialy

...タイトルが長くなりました
やりたいことは、↓こんな感じです


輝く都市!成長する都市!そして、崩れ落ちる新しき建築物したいですね!

揃えるのは大変なので、Pythonしちゃいます
手でやるのは本当に大変 手が滑ってめちゃくちゃな方向に接続されると崩れ落ちます、本当に!

今回のポイント ... サンプルの制限事項です
・"MOD_Slab_Skelton_Piller_Road_ELV" : これが 特定の種類の道路 の名前です なお、これは自作の道路です
・道路の"接続方向" のみしか確認していません "制限付き接続方向"については上書きしたつもりではいません
・特定の行と列の格子点 のつもりでしたが、書き換えミスで、特定の行と列 全部を書き換えています ... まぁ、こちらの方が更なる開発ができますね!
・揃える接続方向は 5 = 1(toN)+4(toS) です

[追記]
やはり、乱暴でした
"接続方向"の先に接続対象が存在しないところが大量にできてしまいます
すると、その接続方向に従って存在しない接続対象に行こうとするものがある時、Segmentation fault: 11 で落ちるようです
たとえそれが、車両でなく 歩行者, 乗降客であっても
ですので、このサンプルコードで実に危険です ... ...

↓サンプルコードです。
↓※絶対に元のデータを上書きしないでください。
↓※データや設定が破壊されても、責任は一切負えません。
↓version=0.120.4, 自作改造パック pak.japan_custom 用になっていますが ... 適宜正しい値になっていないと、面白いくらい開けないデータになります、多分
↓私はそれで苦戦しました
↓さらに、文字コードとかあれこれ読み込みが繊細です。

# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

import xml.etree.ElementTree as ET

filename_in = 'test_in.sve'
filename_out = 'test_out.sve'

f = open(filename_in)
data1 = f.read()  # ファイル終端まで全て読んだデータを返す
f.close()

data2 = data1.replace("<![CDATA[","<CDATA>")
data2 = data2.replace("]]>","</CDATA>")

root = ET.fromstring(unicode(data2.encode('utf-8')))

# マップのサイズを取得
N_x = int(root[0][0].text)
N_y = int(root[0][13].text)

count_p = 0
count_x = 0
count_y = 0
for planquadrat in root.iter('planquadrat_t'):
  count_x = count_p % N_x
  count_y = count_p / N_x
  if (count_x % 5 == 0) or (count_x % 40 == 19) or (count_x % 40 == 21) \
    or (count_y % 5 == 0) or (count_y % 40 == 19) or (count_y % 40 == 21):
    planquadrat_iter = planquadrat.iter('strasse_t')
    for strasse in planquadrat_iter:
      isTarget = False
      for elem in strasse.iter('CDATA'):
        if elem.text == "MOD_Slab_Skelton_Piller_Road_ELV":
          isTarget = True
          print str(count_x) + " , " + str(count_y)
        else:
          isTarget = False
      if isTarget:
        for weg in strasse.iter('weg_t'):
          weg[1].text = str(1+4) # 1:toN, 2:toE, 4:toS, 8:toW
  count_p = count_p + 1

data3 = ET.tostring(root, encoding="utf-8")

data4 = "<?xml version=\"1.0\"?>\n" + data3
data4 = data4.replace("<CDATA>","<![CDATA[")
data4 = data4.replace("</CDATA>","]]>")
data4 = data4.replace("<CDATA />","<![CDATA[]]>")
data4 = data4.replace("<Simutrans pak=\"pak.japan_custom\" version=\"0.120.4\">","<Simutrans version=\"0.120.4\" pak=\"pak.japan_custom\">")

f = open(filename_out,'w') # 書き込みモードで開く
f.write(data4)  # 引数の文字列をファイルに書き込む
f.close()

PythonでSimutransの特定の行と列の海底のレベルを水面まで持ち上げてみた

素敵な交通シミュレーション Simutrans で、
↓人工地盤っぽい感じで格子状に道路+線路を張り巡らしたいのです、私は
Simutransで人工地盤してみる - shingoushori's dialy

... 今回は、特定の行と列の海底のレベルを持ち上げてみました
高架を通すにも、海底のレベルが水面と同じじゃないと建てられないのです
自由に分岐したいので、橋では物足りないのです

今回入っている箇所を見つけた値は以下の3つ
・地面タイルの気候
・陸地の標高
・陸地の ... 水面のレベル ?

こんな感じになりました



↓サンプルコードです。
↓※絶対に元のデータを上書きしないでください。
↓※データや設定が破壊されても、責任は一切負えません。
↓version=0.120.4, 自作改造パック pak.japan_custom 用になっていますが ... 適宜正しい値になっていないと、面白いくらい開けないデータになります、多分
↓私はそれで苦戦しました
↓さらに、文字コードとかあれこれ読み込みが繊細です。

# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

import xml.etree.ElementTree as ET

filename_in = 'test_in.sve'
filename_out = 'test_out.sve'

f = open(filename_in)
data1 = f.read()  # ファイル終端まで全て読んだデータを返す
f.close()

data2 = data1.replace("<![CDATA[","<CDATA>")
data2 = data2.replace("]]>","</CDATA>")


root = ET.fromstring(unicode(data2.encode('utf-8')))


# マップのサイズを取得
N_x = int(root[0][0].text)
N_y = int(root[0][13].text)

count_p = 0
count_x = 0
count_y = 0
for planquadrat in root.iter('planquadrat_t'):
  count_x = count_p % N_x
  count_y = count_p / N_x
  if (count_x % 5 == 0) or (count_y % 5 == 0) \
    or (count_x % 40 == 19) or (count_x % 40 == 21) \
    or (count_y % 40 == 19) or (count_y % 40 == 21):
    if planquadrat[0].text == "2":
      # "1" : 地中海性気候 ?, "2" : 海
      n_grund = 0
      planquadrat_iter = planquadrat.iter('grund_t')
      for grund in planquadrat_iter:
        if n_grund == 0:
          grund[0].text = grund[1].text
          # grund[0].text : 標高 ?
          # grund[1].text : 海面のレベル ?
        n_grund = n_grund + 1
  count_p = count_p + 1

data3 = ET.tostring(root, encoding="utf-8")

data4 = "<?xml version=\"1.0\"?>\n" + data3
data4 = data4.replace("<CDATA>","<![CDATA[")
data4 = data4.replace("</CDATA>","]]>")
data4 = data4.replace("<CDATA />","<![CDATA[]]>")
data4 = data4.replace("<Simutrans pak=\"pak.japan_custom\" version=\"0.120.4\">","<Simutrans version=\"0.120.4\" pak=\"pak.japan_custom\">")

f = open(filename_out,'w') # 書き込みモードで開く
f.write(data4)  # 引数の文字列をファイルに書き込む
f.close()

PythonでSimutransの特定の行と列を平坦化してみた

素敵な交通シミュレーション Simutrans で、
↓人工地盤っぽい感じで格子状に道路+線路を張り巡らしたいのです、私は
Simutransで人工地盤してみる - shingoushori's dialy

... そこで今回は、セーブデータをいじり、特定の行と列を一気に平坦化してみました.
道路を敷設するにも、敷設できない方向の坂が付いていると、一旦平坦化しないといけないので、とりあえずpythonで簡略化しちゃいます.
(セーブデータは、xml ファイルで書き出し読み込みしています. )

・特定の行と列 1: 0+5*k (k={0,1,2,...}) 番目
 駅の範囲が 2マス設定のままなので、道路は5マスおきです. 頑張れ、バス!
・特定の行と列 1: 20+40*k±1 (k={0,1,2,...}) 番目
 幹線として、3マス幅の高架を敷設します.
・平坦化をスルーする条件: すでに高架で道路か線路が敷設されているところ
 せっかく坂道にすると決めたマスなので、残しておきます

... PythonXMLパーサが、今の所は肌に合いません ...
... Simutrans のセーブデータの仕様との相性もあるんでしょうが ...

↓サンプルコードです。
↓※絶対に元のデータを上書きしないでください。
↓※データや設定が破壊されても、責任は一切負えません。
↓version=0.120.4, 自作改造パック pak.japan_custom 用になっていますが ... 適宜正しい値になっていないと、面白いくらい開けないデータになります、多分
↓私はそれで苦戦しました
↓さらに、文字コードとかあれこれ読み込みが繊細です。

# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

import xml.etree.ElementTree as ET

filename_in = 'test_in.sve'
filename_out = 'test_out.sve'

f = open(filename_in)
data1 = f.read()  # ファイル終端まで全て読んだデータを返す
f.close()

data2 = data1.replace("<![CDATA[","<CDATA>")
data2 = data2.replace("]]>","</CDATA>")

root = ET.fromstring(unicode(data2.encode('utf-8')))

# マップのサイズを取得
N_x = int(root[0][0].text)
N_y = int(root[0][13].text)

count_p = 0
count_x = 0
count_y = 0
for planquadrat in root.iter('planquadrat_t'):
  count_x = count_p % N_x
  count_y = count_p / N_x
  if (count_x % 5 == 0) or (count_y % 5 == 0) \
    or (count_x % 40 == 19) or (count_x % 40 == 21) \
    or (count_y % 40 == 19) or (count_y % 40 == 21):
    N_strasse = 0 # 道路
    N_schiene = 0  # 線路
    planquadrat_iter = planquadrat.iter('grund_t')
    planquadrat_iter.next() # 地上(?)はスルー
    for grund in planquadrat_iter:
      for elem in grund.iter('strasse_t'):
        N_strasse = N_strasse + 1
      for elem in grund.iter('schiene_t'):
        N_schiene = N_schiene + 1
    for grund in planquadrat.iter('grund_t'):
      if (N_strasse == 0) and (N_schiene == 0):
        grund_iter = grund.iter()
        elemdist = 0
        for elem in grund_iter:
          if elemdist == 5:
            elem.text = "0" # 平坦化
          elemdist = elemdist + 1
  count_p = count_p + 1

data3 = ET.tostring(root, encoding="utf-8")

data4 = "<?xml version=\"1.0\"?>\n" + data3
data4 = data4.replace("<CDATA>","<![CDATA[")
data4 = data4.replace("</CDATA>","]]>")
data4 = data4.replace("<CDATA />","<![CDATA[]]>")
data4 = data4.replace("<Simutrans pak=\"pak.japan_custom\" version=\"0.120.4\">","<Simutrans version=\"0.120.4\" pak=\"pak.japan_custom\">")

f = open(filename_out,'w') # 書き込みモードで開く
f.write(data4)  # 引数の文字列をファイルに書き込む
f.close()

PythonでSimutransの全乗り物の待機を上書きしてみた

GUIでぽちぽち1000台以上のバスの情報を設定するのは苦しいので、Pythonでやってみました。
正しくは、とりあえずやれたのが、まずこれだけ。

最大の苦戦どころが、
xmlデータだって言っても、CDATA なるものに対応するのが辛い
 → いっそテキストで全読み込み -> 文字列変換でElement化、パースできるように
というところです。
荒技感が強いですが、数日ググってあれこれやってみて明快な対応しているパーサや設定方法が見当たりませんでした。

さらに、あれこれやるのに苦しそうなのが、xmlデータのElementの要素名が最小限以外ほぼ無意味っぽいってことです。
convoi, haltestelle, fabrik ... あとは i64 とかだらけで、
バイナリから最小限に引っ張り出してきた感満載です。


↓サンプルコードです。
↓※絶対に元のデータを上書きしないでください。
↓※データや設定が破壊されても、責任は一切負えません。
↓version=0.120.4, 自作改造パック pak.japan_custom 用になっていますが ... 適宜正しい値になっていないと、面白いくらい開けないデータになります、多分
↓私はそれで苦戦しました
↓さらに、文字コードとかあれこれ読み込みが繊細です。

# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

import xml.etree.ElementTree as ET

filename_in = 'test_in.sve'
filename_out = 'test_out.sve'

f = open(filename_in)
data1 = f.read()  # ファイル終端まで全て読んだデータを返す
f.close()

data2 = data1.replace("<![CDATA[","<CDATA>")
data2 = data2.replace("]]>","</CDATA>")

root = ET.fromstring(unicode(data2.encode('utf-8')))

for convoi in root.iter('convoi_t'):
  for fahrplan in convoi.iter('fahrplan_t'):
    fahrplan_iter = fahrplan.iter()
    elemdist = 0
    for elem in fahrplan_iter:
      if elem.tag == "koord3d":
        elemdist = 1
      elif elemdist == 5:
        # 積むまで待機(%)
        elem.text = "10"
      elif elemdist == 6:
        # 最大待ち時間(月)
        # elem.text = "6" # 1/1024
        # elem.text = "7" # 1/512
        elem.text = "8" # 1/256
      elemdist = elemdist + 1

data3 = ET.tostring(root, encoding="utf-8")

data4 = "<?xml version=\"1.0\"?>\n" + data3
data4 = data4.replace("<CDATA>","<![CDATA[")
data4 = data4.replace("</CDATA>","]]>")
data4 = data4.replace("<CDATA />","<![CDATA[]]>")
data4 = data4.replace("<Simutrans pak=\"pak.japan_custom\" version=\"0.120.4\">","<Simutrans version=\"0.120.4\" pak=\"pak.japan_custom\">")
# 元々のセーブデータのバージョンに合わせる

f = open(filename_out,'w') # 書き込みモードで開く
f.write(data4)  # 引数の文字列をファイルに書き込む
f.close()

Simutransで人工地盤してみる

素敵なフリーの輸送会社経営シミュレーションゲーム Simutrans で人工地盤っぽいことを初めてみました。

↓実験の光景↓

白黒の見切れているのが、役場です。
道路や鉄道を引くのに、役場の場所が邪魔になることがあります。
しかし、役場は撤去しないで済むならしたくありません。街もろとも消滅するからです。

そこで、高架橋。上を通すことができます、無理やり感がありますが。
これを発展させて、街を丸ごと上に上げてしまおうというのが、このプレイの発想です。
こういうのを、人工地盤って言ったと思う。

↓例えば、こんな感じ↓

 

さて、人工地盤するのに、追加で以下のアドインを用いました。
・道路関係セット [作者:wa様] アドオン/交通施設 2 - Simutrans日本語化・解説
バラスト・スラブ線路セット [作者:りむ様] アドオン/railtool - Simutrans日本語化・解説
+↑上述2つをベースに自分で改造したもの(柱、低コスト道路...)

ゲーム内ではコストの概念があります。
すると、高架橋は高規格が主なので、高コストな気がして気が引けました。
そこで、柱だけ〜低速用〜のものを自作(改造?)することにしました。
とりあえず、GIMPでガシガシ。

 

さあ、果たして本当に私好みなプレイができるんでしょうか。
高架を積みあげた分のコストが回収できるんでしょうか。。。