ローカル変数の制限突破

Last-modified: 2018-12-14 (金) 02:57:42

こちらは古い情報です。
使用できる変数を増やすにある方法の方が扱いやすいので、そちらの使用を推奨します。

 

.csで自由に使えるローカル変数は0@~31@の32個しか存在せず、これ以上の数のオブジェクトなどを操作することは出来ません。
グローバル変数を使えば解決されますが、他のスレッドで使っていると不具合を起こします。
そこで、あらかじめスレッド内に領域を設け、そこへ直接メモリ書き込みを行うことで変数を節約します。
タイトルでは変数制限突破とありますが、変数とは扱いが違います。
また、操作も難しいものとなります。
しかし、スレッドがメモリに収まる限りいくらでも領域を大きくすることができるためとても便利です。

 

例:

for 0@ = 0 to 9
    0093: 1@ = integer 0@ to_float
    04C4: store_coords_to 5@ 6@ 7@ from_actor $PLAYER_ACTOR with_offset 0.0 1@ 0.0
    0107: 2@ = create_object #KNIFECUR at 5@ 6@ 7@
    gosub @get_pointer
    0085: 4@ = 0@ // (int)
    0012: 4@ *= 4
    005A: 30@ += 4@ // (int)
    0A8C: write_memory 30@ size 4 value 2@ virtual_protect 0
    04C4: store_coords_to 5@ 6@ 7@ from_actor $PLAYER_ACTOR with_offset 0.0 0.0 1@
    0107: 3@ = create_object #KATANA at 5@ 6@ 7@
    gosub @get_pointer
    0085: 4@ = 0@ // (int)
    0012: 4@ *= 4
    005A: 30@ += 4@ // (int)
    000A: 30@ += 40
    0A8C: write_memory 30@ size 4 value 3@ virtual_protect 0
end
wait 2000
for 0@ = 0 to 9
    gosub @get_pointer
    0085: 4@ = 0@ // (int)
    0012: 4@ *= 4
    005A: 30@ += 4@ // (int)
    0A8D: 2@ = read_memory 30@ size 4 virtual_protect 0
    if
    03CA:   object 2@ exists
    then
        0108: destroy_object 2@
    end
    gosub @get_pointer
    0085: 4@ = 0@ // (int)
    0012: 4@ *= 4
    005A: 30@ += 4@ // (int)
    000A: 30@ += 40
    0A8D: 3@ = read_memory 30@ size 4 virtual_protect 0
    if
    03CA:   object 3@ exists
    then
        0108: destroy_object 3@
    end
end
                     :
                     :
:get_pointer
0A9F: 30@ = current_thread_pointer
000A: 30@ += 0x10
0A8D: 30@ = read_memory 30@ size 4 virtual_protect 0
000E: 30@ -= @pool
return
:pool
hex
//2@ - knife pool - 40byte
00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000
//3@ - katana pool - 40byte
00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000
end

ナイフを前方に1m間隔で10個 刀を上方に1m間隔で10個出し、2秒後全て消すコードです。
使用変数は8個。
説明の為簡単な物を載せていますので、この場合は変数24個で作ることが出来ます。


1マス1byte

  • スレッドの領域を作る。
    1. 使いたい変数の数を数える。
    2. 変数一つにつき0を8個hex-endで記述する
    3. 使用サイズを覚えておく 変数*4が使用サイズ
    4. この場合は2タイプ扱うので2タイプ目までのサイズを数える(40byte)
    :pool
    hex
    //2@ - knife pool - 40byte
    00000000 00000000 00000000 00000000 00000000
    00000000 00000000 00000000 00000000 00000000
//3@ - katana pool - 40byte
00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000
end
  • 最初のfor-endループの上半分
    オブジェクトを作る部分は省略
    gosub @get_pointer
    0085: 4@ = 0@ // (int)
    0012: 4@ *= 4
    005A: 30@ += 4@ // (int)
    0A8C: write_memory 30@ size 4 value 2@ virtual_protect 0
    この部分で2@のHandleをスレッドに用意した領域に書き込みます。
    • 一行目
      :get_pointer
      0A9F: 30@ = current_thread_pointer
      000A: 30@ += 0x10
      0A8D: 30@ = read_memory 30@ size 4 virtual_protect 0
      000E: 30@ -= @pool
      return
      このスレッドのポインタを取得し、スレッド構造体+0x10 = スクリプトの先頭アドレスへポインタを移動させます。
      ラベルはスクリプトの先頭からのオフセットの符号を反転させたものなので先頭アドレスからラベルを引きます。
      最終的に30@は:poolのポインタとなります。
    • 二行目
      for-endで使用するカウンターを利用します。
    • 三、四行目
      カウンター*4 4byteずつ使用するので、4byteポインタを進めます
    • 五行目
      ポインタ30@に2@(Handle)を書き込みます。
      変数は型を問わずすべて4byteのサイズを持っているので、sizeは4を指定します。
  • 最初のfor-endループの下半分
    形は上のとほぼ同じです。違いは
    000A: 30@ += 40
    が追加されただけです。
    同じ計算式でならば当然同じ場所に書き込まれ、2@が書き込まれた分が消えてしまいます。
    ですので、そこから2タイプ目までのサイズを足します。
    このループを抜けると、設定した領域に値が書き込まれた状態になります。

二番目のfor-endのループで一番目で書き込んだ値を読み込みます。
読み込んだ値がそのままオブジェクトのHandleなので、いつものようにオブジェクトを操作することが出来ます。