shingoushori's dialy

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

PythonでSimutransの道路の下を掘って、ペデストリアンデッキを設置してみた

フリーの交通?シミュレーション
Simutrans(http://japanese.simutrans.com)が, 素晴らしい.

それでもって私は人工地盤っぽいことをやっています。
Simutransで人工地盤してみる - shingoushori's dialy
大規模な都市開発をさっさとやるべく、Pythonでセーブデータをいじっています

... 人工地盤プレイに限らない、Simutransの話題に
「市道化防止パッチ」というのがあります
twitter.com
市街地が拡がった時、せっかく設置した高規格道路が市道化されてバス大迷惑
それも歯抜けで拡がるから見た目にも苦しい、加減速を繰り返すバス可哀想
という苦しみです

色々議論がありますが ...
私なりの苦しい回避策が 「道路の下を掘って、高架化する」です

↓こんな感じです↓

できるだけ本体改造せず、セーブデータも独自拡張せず、
やりたい私だけがちょっとした苦労だけに抑えるべく、
セーブデータの改造でできるだけ済ませたいのです
喧嘩、怖い 門前払い、心と体が保たない いじめ、よくない

やってみると、色々ありそうなのですけども
とりあえず、2点気づきました
・掘ったところに設置するなら、箱積み駅舎のペデストリアンデッキがオススメ
 → 掘ったところに何かを設置しないと、そこに家がたってしまいます
  どうせなら、後々地下鉄...的な何か 開削工法だと言い張ってみる!を設置したいですよね
  そうすると、消しやすい何かがいいのです
  となると、箱積み駅舎のペデストリアンデッキならば、実態は運河なのでちょうどいい はずです
↓こんな感じです↓

・AIプレイヤーの道路は割り込んでくる
 →どういう処理なのかは未確認ですが、勝手に接続して来ちゃいます
  市道は接続しなかった気がするのですが ...
↓こんな感じです↓

なお、サンプルコードでは、とりあえず次のようにしています
・特定のプレイヤー target_player_num の道路についてのみ処理
・掘ったところに敷く道路/フェンス/ペデストリアンデッキの接続方向は 0

↓サンプルコードです。
↓※絶対に元のデータを上書きしないでください。
↓※データや設定が破壊されても、責任は一切負えません。
↓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 = 'testout_elv_road.sve'
filename_out = 'testresult.sve'


# 特定プレイヤーの道路の下を掘って、何やらを設置
def dig_gut_under_way(root, target_player_num, target_gut_way, target_gut_way_type):
  
  # 検索用
  l_planquadrat = list(root.findall('planquadrat_t'))
  
  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 len(planquadrat[1].getchildren()) < 6 :
        print str(count_x) + " " + str(count_y)
        count_p = count_p + 1
        continue
      if (planquadrat[1][5].text == '1'):
        # 道路あり
        player_num = int(planquadrat[1][6][0][0].text)
        if (player_num != target_player_num):
          count_p = count_p + 1
          continue
        
        #接続方向
        #print str(count_x) + " " + str(count_y) + " " + planquadrat[1][6][0][1].text
        connect_arg = int(planquadrat[1][6][0][1].text)
        # 1:toN, 2:toE, 4:toS, 8:toW
        
        # 掘らないタイプの道路
        target_way = planquadrat[1][6][1].text
        if (target_way == target_gut_way):
          count_p = count_p + 1
          continue
        
        # #### 処理
        
        # 高度 海面 計画
        target_h = int(planquadrat[1][0].text) # 高度
        target_hs = int(planquadrat[1][1].text) # 海面?
        target_hs2 = planquadrat[1][2].text # 
        
        target_hd = target_h - 1 # 高度 掘
        if (target_hs >= target_hd) :
          target_hs = target_hd - 1
        target_h = str(target_h)
        target_hs = str(target_hs)
        target_hd = str(target_hd)
        
        target_grad = planquadrat[1][4].text
        #  0 : 平地
        #  4 : 南北 ud b00000100
        #  8 : 南北 ud b00001000 h
        # 12 : 東西 ud b00000110
        # 28 : 東西 du b00010110
        # 36 : 南北 du b00100100
        
        # 高架追加
        count_ch = len(planquadrat.getchildren())
        
        elem_1 = ET.Element('id')
        elem_1.text = '6'
        elem_2 = ET.Element('grund_t')
        subelem_1 = ET.SubElement(elem_2, 'i8')
        subelem_1.text = str(target_h) # 高度
        subelem_1 = ET.SubElement(elem_2, 'i8')
        subelem_1.text = target_hs # 海面
        subelem_1 = ET.SubElement(elem_2, 'i8')
        subelem_1.text = target_hs2 
        subelem_1 = ET.SubElement(elem_2, 'CDATA')
        subelem_1.text = '' # 
        subelem_1 = ET.SubElement(elem_2, 'i8')
        subelem_1.text = target_grad # 勾配
        subelem_1 = ET.SubElement(elem_2, 'id')
        subelem_1.text = '1' # 
        subelem_1 = ET.SubElement(elem_2, 'strasse_t')
        subelem_2 = ET.SubElement(subelem_1, 'weg_t')
        subelem_3 = ET.SubElement(subelem_2, 'i8')
        subelem_3.text = str(target_player_num) # owner?
        subelem_3 = ET.SubElement(subelem_2, 'i8')
        subelem_3.text = str(connect_arg) # 接続方向
        subelem_3 = ET.SubElement(subelem_2, 'i16')
        subelem_3.text = '0' # 速度 (ロード時 pakの情報に上書き)
        subelem_3 = ET.SubElement(subelem_2, 'i8')
        subelem_3.text = '0' # 
        subelem_3 = ET.SubElement(subelem_2, 'i32')
        subelem_3.text = '0' # 
        subelem_3 = ET.SubElement(subelem_2, 'i32')
        subelem_3.text = '0' # 
        subelem_3 = ET.SubElement(subelem_2, 'i32')
        subelem_3.text = '0' # 
        subelem_3 = ET.SubElement(subelem_2, 'i32')
        subelem_3.text = '0' # 
        subelem_2 = ET.SubElement(subelem_1, 'CDATA')
        subelem_2.text = target_way #'city_road'
        subelem_1 = ET.SubElement(elem_2, 'id')
        subelem_1.text = '-1' # 
        subelem_1 = ET.SubElement(elem_2, 'i8')
        subelem_1.text = '0' # 
        planquadrat.insert(count_ch-1, elem_1)
        planquadrat.insert(count_ch, elem_2)
        
        # 沈め
        planquadrat[1][0].text = target_hd
        planquadrat[1][1].text = target_hs
        # 接続方向
        planquadrat[1][6][0][1].text = str(0)
        # 置き換え
        planquadrat[1][6][1].text = target_gut_way
        
        if (target_gut_way_type == 2) :
          planquadrat[1][5].text = '2'
          planquadrat[1][6].tag = 'schiene_t'
        
        if (target_gut_way_type == 3) :
          planquadrat[1][5].text = '3'
          planquadrat[1].insert(7, planquadrat[1][6][1])
          planquadrat[1].insert(7, planquadrat[1][6][0])
          planquadrat[1].remove(planquadrat[1][6])
        
      count_p = count_p + 1







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)



target_player_num = 1
target_gut_way = 'city_road'
target_gut_way_type = 1 # 道路は、1 ※ただし、市道化される
target_gut_way = 'green_fence2'
target_gut_way_type = 2 # フェンスは、2 ※ただし、一括で消せない
target_gut_way = 'wa-cs-pedway-ground'
target_gut_way_type = 3 # ペデストリアンデッキは、3
dig_gut_under_way(root, target_player_num, target_gut_way, target_gut_way_type)

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()