CVehicle+0x94C CVehicle+0x950
戦車、消防車などの主砲の向き。 上がヘディング、下がピッチ。 FLOAT、単位はラジアン。 ######################## チチチ音 018C: play_sound 1190 at 0.0 0.0 0.0 ######################## Player.SetClothes($PLAYER_CHAR, "Alex_blade_d", "AlexBlade", 0) Player.Build($PLAYER_CHAR) 着替えです。モデルをLoadしておく必要はありません。 (プレイヤーのハンドル, テクスチャ, モデル, 部位ID)の順に書きます。 服をIDEに追加する必要が無い上、しゃがみやジャンプ中でも実行できて便利です。 いつも撮影前に使ってます。
07C0: load_path 856 while 87C1: not path 856 available wait 0 end 05EB: assign_car 5@ to_path 856 06FD: set_car 5@ speed_on_path_to 0.95 CarPath基本形。 あの自転車ビル登りのタネはこれです。 友達をSAMPに集めるか、もしくはデフォのAIを上手に使えば、映画のようなカーチェイスも作れるはずです。 他にもPath関係のコードはあるので、"Car Path"で検索するといいです。
0A8D: 10@ = read_memory 0xB6F9CC size 4 virtual_protect 0 0A8D: 11@ = read_memory 0xB6F9D0 size 4 virtual_protect 0 0A8D: 12@ = read_memory 0xB6F9D4 size 4 virtual_protect 0 0A8D: 20@ = read_memory 0xB6F9AC size 4 virtual_protect 0 0A8D: 21@ = read_memory 0xB6F9B0 size 4 virtual_protect 0 0A8D: 22@ = read_memory 0xB6F9B4 size 4 virtual_protect 0 カメラ座標、カメラ先座標を求めます。 068D、068Eと違って、CamHackの座標も求められます。 FOVは分かりませんでしたとさ。
0A97: 2@ = car 19@ struct 000A: 2@ += 68 0A8C: write_memory 2@ size 4 value 0.0 virtual_protect 0 000A: 2@ += 4 0A8C: write_memory 2@ size 4 value 0.0 virtual_protect 0 000A: 2@ += 4 0A8C: write_memory 2@ size 4 value 0.0 virtual_protect 0 07D5: set_car 19@ velocity_in_direction_XYZ 4@ 5@ 6@ rotation_velocitiesXY 0.0 0.0 unk 0.0 毎フレーム07D5を実行するようなコードを書いた人はいるんじゃないでしょうか。 信じられない速さで飛んでゆくのは、このコードが速度を上書きするのではなく、”加算”するからです。 思ったとおりの速さにするには、メモリーから速度を0にしてやらねばなりません。
009A: 2@ = create_actor_pedtype 23 model #NULL at 7@ 8@ 9@ 02AB: set_actor 2@ immunities BP 1 FP 1 EP 1 CP 1 MP 1 02E2: set_actor 2@ weapon_accuracy_to 100 0337: toggle_actor 2@ visibility 0 0489: toggle_actor 2@ muted 1 0619: toggle_actor 2@ collision_detection 0 081A: set_actor 2@ weapon_skill_to 1 04D8: toggle_actor 2@ drowns_in_water 0 0350: set_actor 2@ maintain_position_when_attacked 1 060A: create_decision_maker_type 0 store_to 32@ // decision\allowed\m_.ped files 060B: set_actor 2@ decision_maker_to 32@ 「動じない人」です。 動画では、にとりの装甲車の上にこいつがTurretされてました。 特に最後の2行は重要で、これで近くで車が燃えても無視してくれます。
0A96: 30@ = actor $PLAYER_ACTOR struct 0A96: 31@ = actor 9@ struct 30@ += 0x14 31@ += 0x14 0A8D: 30@ = read_memory 30@ size 4 virtual_protect 0 0A8D: 31@ = read_memory 31@ size 4 virtual_protect 0 for 0@ = 0 to 11 0A8D: 20@ = read_memory 31@ size 4 virtual_protect 0 0A8C: write_memory 30@ size 4 value 20@ virtual_protect 0 30@ += 0x4 31@ += 0x4 end Actor:9@の回転Matrixをプレイヤーにも適用してやります。 座標、角度計算が面倒なときに使います。 ムーンウォークしたり、ビルだって垂直に駆け上がれるはずです。
0619: enable_actor $PLAYER_ACTOR collision_detection 0 04C4等 0A96: 30@ = actor $PLAYER_ACTOR struct 30@ += 0x14 0A8D: 30@ = read_memory 30@ size 4 virtual_protect 0 30@ += 0x30 0A8C: write_memory 30@ size 4 value 7@ virtual_protect 0 30@ += 0x4 0A8C: write_memory 30@ size 4 value 8@ virtual_protect 0 30@ += 0x4 0A8C: write_memory 30@ size 4 value 9@ virtual_protect 0 当たり判定の無いアクターの動かし方です。 083Cはとんでもない速度制限がありますが、こっちで回避できます。 また、3行目以降のメモリ操作はアクターの座標を操作してるものですが、 アニメーションを保持したまま座標操作が出来ます。 また、”指定した座標より少し上に出現する”という現象が起きません。
06BD: no_obstacles_between 18@ 19@ 20@ and 15@ 16@ 17@ solid 1 car 1 actor 1 object 1 particle 0 0339: anything_in_cube_cornerA 15@ 16@ 17@ cornerB 15@ 16@ 17@ solid 0 car 1 actor 0 object 0 particle 0 ~間に何かあるか?を調べるこーど。 06BDは超頻出コードです。06BDが”辺”なのに対して、0339は”直方体”です。 0339のほうが大雑把に計算します。 色んな動画で使用。
if Actor.Animation($PLAYER_ACTOR) == "Sprint_civi" then 0393: actor $PLAYER_ACTOR perform_animation "Sprint_civi" at 25.0 times_normal_rate end アニメーションの速度がいぢれる。 走るアニメーションだと、走る速度も上がります。 色んな動画で使用。
08C6: set_actor $PLAYER_ACTOR stay_on_bike 1 全速力のAlexさんと正面衝突しても、バイクから落ちなくなります。 水中も例外では無いです。 藤原もっこりで使用。
まずは訂正から。 前の記事のカメラ座標を求めるこーどですが、 カメラ先座標の方はローカル座標が出てきてしまいます。 その続きに 0013: 20@ *= 100.0 0013: 21@ *= 100.0 0013: 22@ *= 100.0 005B: 20@ += 10@ // (float) 005B: 21@ += 11@ // (float) 005B: 22@ += 12@ // (float) …と書けば、20-22@にワールド座標が入ります。 100.0をかけてるのは後で調整しやすいようにするためだったり。
092F: lock_camera_target_point 0 0930: lock_camera_position 0 0931: lock_camera_zoom 0 0936: set_camera 0.0 0.0 0.0 position_to 10.0 10.0 10.0 time 15000 drop_mode 1 0920: point_camera 10.0 10.0 10.0 transverse_to 20.0 20.0 20.0 time 15000 mode 1 0922: set_camera_zoom_in_factor 55.0 out_factor 85.0 timelimit 6000 mode 1 092F: lock_camera_target_point 1 0930: lock_camera_position 1 0931: lock_camera_zoom 1 Camera基本形。 timeをずらしたり、中継点を設けたりすると、良いカメラワークが生まれることも。 戻すときは 0925: restore_camera_to_user_defined Camera.Restore_WithJumpCut で。 必要に応じて 0373: set_camera_directly_behind_player or 03C8: set_camera_directly_before_player も。
067C: put_camera_on_actor 0@ with_offset -0.7 -1.0 0.7 rotation 0.0 2.0 0.0 0.0 2 0679: put_camera_on_car 5@ with_offset -3.0 6.0 0.0 rotation 0.3 0.0 1.0 0.0 2 カメラを人or車に設置。そのカメラは設置した本人を向きます。
067B: put_camera_on_car 0@ with_offset 0.0 0.0 0.0 point_to_actor 1@ tilt 0.0 2 067E: put_camera_on_actor 0@ with_offset 0.0 0.0 0.0 point_to_actor 1@ 0.0 mode 2 ↑のと似てますが、設置した人とは別の人を向きます。 また、何も無い定点を見るということは出来ないようなので、 そんなときは透明なダミーの人を使うことになるでしょう。
0003: shake_camera 100 099C: jiggle_camera type 1 timelimit 20000.0 intensity 3.0 052C: set_player $PLAYER_CHAR drunk_visuals 100 カメラが揺れる系 0003は爆発の時の揺れみたいな感じ。 099Cはもう少しグニャグニャと揺れます。typeの値を変えれば揺れ方も変わるようです。 052Cはトゥルースの農場焼きで使われてます。酒か薬で酔った感じに揺れます。
02A3: enable_widescreen 1 0A48: enable_menu_access_in_widescreen_mode 1 ワイドスクリーンです。ちょっと映画っぽくなります。 0A48を使うと、途中でメニューを出すことが出来るようになります。
015D: set_gamespeed 1.0 そのまんま。ゲームの速度です。 ゼロにしても止まるわけじゃあないです。
0615: define_AS_pack_begin 90@ 05D3: AS_actor -1 goto_point 219.1689 119.2266 1003.2188 mode 4 30000 ms // versionA 0639: AS_actor -1 rotate_to_actor $PLAYER_ACTOR 0616: define_AS_pack_end 90@ 0618: assign_actor $ACTOR_REMILIA to_AS_pack 90@ 0615と0616の間に挟まれたアクションを、上から順番に実行していくコードです。 変数操作などのコードは無視されます。実行されるコードには決まりがあるようですが、「AS_actor」が含まれるやつは必ず実行されると考えてもいいです。 ”-1”というのは、後で0618で設定される動作主です。 0615と0616はスレッドを跨いで働くこともあります。詳しくはmain.scm参照。
0792: disembark_instantly_actor $PLAYER_ACTOR 0687: clear_actor $PLAYER_ACTOR task 何かのアクションをしてる時にアニメーションをさせようとしても無視されます。 直前に、このどちらか、あるいは両方を使います。(なげやり
04ED: load_animation "POWER" while 04EE: animation "POWER" loaded wait 0 end 0812: AS_actor $ACTOR_ALEX perform_animation "ham_air_a" IFP_file "POWER" 1000.0 loopA 1 lockX 0 lockY 0 lockF 0 time -1 // versionB アニメーション基本形。 versionA、versionCもあるみたいですが、基本的にはこっちを使います。多分。 loopAの前の数値は早さでしょうか。数値が低いとゆっくりとアニメーションに移りますが、逆に高いと即座にアニメーションに移ります。 loopAはループするか否か、lockXlockYはよく分からないので例を挙げます。 X0Y0だと、アニメーション後に元居た座標に戻ってきます。 X1Y1だと、アニメーション終了時の座標に残ります。(人に当たり判定が無いと動かない。) X1Y0だと、……<うp主の表現力を恨んでください。> lockFを1にすると、アニメーションが終わったときにその体制のまま固まります。 timeはアニメーションする時間です。一回きりで終了させるときは-1と書きます。
0611: actor $ACTOR_ALEX performing_animation "ham_air_a" 0613: 1@ = actor $PLAYER_ACTOR animation 40@v time 0611はif文で使うやつ。 0613でアニメーションの進度(0.0~1.0)が求められ、条件分岐などに使われるような気がする。
0A92: create_custom_thread "Homurang_StruR.s" かすたむ☆すれっど 一つのcsで並列処理が不可能なとき、面倒なときに使います。 もっこり3夜で乱用してました。 DriveByRPGでも使ってるので、詳しい使い方はそっちをみるといいかも。 ##################################################################### csでvtol機のスラスターを下に向けるにはどうすればいいでしょうか 0A97: 0@ = car 0@ struct 000A: 0@ += 0x86C 0A8C: write_memory 0@ size 2 value 5000 virtual_protect 0 ##################################################################### 速度をもった銃弾の作成
0AA5: call 0x736010 num_params 8 pop 8 [速度Z] [速度Y] [速度X] [位置Z] [位置Y] [位置X] [武器ID] [射手(NULL, *CPed, *CVehicle)]
missile.csのミサイルの作成
0AAA: 30@ = thread 'MISSILE' pointer if 8039: not 30@ == 0 then 000A: 30@ += 0xB0 0A8D: 31@ = read_memory 30@ size 4 virtual_protect 0 0012: 31@ *= 0x0A 000E: 30@ -= 0x6C 0A8D: 30@ = read_memory 30@ size 4 virtual_protect 0 005A: 30@ += 31@ // (int) 000A: 30@ += 0x0F 0012: 30@ *= 0x04 0AAA: 31@ = thread 'MISSILE' pointer 005A: 30@ += 31@ // (int) 0A8C: write_memory 30@ size 4 value [射手(NULL, *CPed, *CVehicle)] virtual_protect 0 000A: 30@ += 0x04 0A8C: write_memory 30@ size 4 value 0 virtual_protect 0 000A: 30@ += 0x04 0A8C: write_memory 30@ size 4 value [種類] virtual_protect 0 000A: 30@ += 0x04 0A8C: write_memory 30@ size 4 value 0 virtual_protect 0 000A: 30@ += 0x04 0A8C: write_memory 30@ size 4 value [目標(NULL, *CPed, *CVehicle)] virtual_protect 0 000A: 30@ += 0x04 0A8C: write_memory 30@ size 4 value [位置X] virtual_protect 0 000A: 30@ += 0x04 0A8C: write_memory 30@ size 4 value [位置Y] virtual_protect 0 000A: 30@ += 0x04 0A8C: write_memory 30@ size 4 value [位置Z] virtual_protect 0 000A: 30@ += 0x04 0A8C: write_memory 30@ size 4 value [回転H] virtual_protect 0 000A: 30@ += 0x04 0A8C: write_memory 30@ size 4 value [回転P] virtual_protect 0 0AAA: 30@ = thread 'MISSILE' pointer 000A: 30@ += 0xB0 0A8D: 31@ = read_memory 30@ size 4 virtual_protect 0 000A: 31@ += 0x01 if 0039: 31@ == 0x40 then 0006: 31@ = 0x00 end 0A8C: write_memory 30@ size 4 value 31@ virtual_protect 0 end
mininuke.csの核爆発の作成
0AAA: 14@ = thread 'MININUKE' pointer if 8039: not 14@ == 0 then 000A: 14@ += 0x3C 0A8C: write_memory 14@ size 4 value 1 virtual_protect 0 000A: 14@ += 0x4 0A8C: write_memory 14@ size 4 value [位置X] virtual_protect 0 000A: 14@ += 0x4 0A8C: write_memory 14@ size 4 value [位置Y] virtual_protect 0 000A: 14@ += 0x4 0A8C: write_memory 14@ size 4 value [位置Z] virtual_protect 0 end ##################################################################### 記事#478の 005A: 2@ += 1@ // (int) の処理が終わった時点で2@にはボーンの姿勢を表す行列へのポインタが 入っているのでそれを使って、
:BONETEST wait 0 if 056D: actor $PLAYER_ACTOR defined jf @BONETEST 0AB1: call_scm_func @GET_BONE_OFFSET 5 actor $PLAYER_ACTOR bone 6 offset 0.0 0.0 0.0 store_to 0@ 1@ 2@ 04D5: create_corona_at 0@ 1@ 2@ radius 0.05 type 0 flare 0 RGB 255 255 255 0AB1: call_scm_func @GET_BONE_OFFSET 5 actor $PLAYER_ACTOR bone 6 offset 0.1 0.0 0.0 store_to 0@ 1@ 2@ 04D5: create_corona_at 0@ 1@ 2@ radius 0.05 type 0 flare 0 RGB 255 0 0 0AB1: call_scm_func @GET_BONE_OFFSET 5 actor $PLAYER_ACTOR bone 6 offset 0.0 0.1 0.0 store_to 0@ 1@ 2@ 04D5: create_corona_at 0@ 1@ 2@ radius 0.05 type 0 flare 0 RGB 0 255 0 0AB1: call_scm_func @GET_BONE_OFFSET 5 actor $PLAYER_ACTOR bone 6 offset 0.0 0.0 0.1 store_to 0@ 1@ 2@ 04D5: create_corona_at 0@ 1@ 2@ radius 0.05 type 0 flare 0 RGB 0 0 255 0A9F: 0@ = current_thread_pointer 000A: 0@ += 0x40 0AB1: call_scm_func @GET_BONE_OFFSET 5 actor $PLAYER_ACTOR bone 6 offset 0.8 -0.03 0.03 store_to 1@ 2@ 3@ 0A9F: 4@ = current_thread_pointer 000A: 4@ += 0x50 0AB1: call_scm_func @GET_BONE_OFFSET 5 actor $PLAYER_ACTOR bone 6 offset 80.0 -3.0 3.0 store_to 5@ 6@ 7@ 0A9F: 20@ = current_thread_pointer 000A: 20@ += 0x5C 0A9F: 21@ = current_thread_pointer 000A: 21@ += 0x88 0AA7: call_function 0x56BA00 num_params 12 pop 12 1 0 0 1 1 1 1 1 21@ 20@ 4@ 0@ 18@ if 8039: not 18@ == 0 jf @BONETEST 005B: 12@ += 8@ // (float) 005B: 13@ += 9@ // (float) 005B: 14@ += 10@ // (float) 04D5: create_corona_at 8@ 9@ 10@ radius 0.1 type 0 flare 0 RGB 255 0 255 04D5: create_corona_at 12@ 13@ 14@ radius 0.1 type 0 flare 0 RGB 0 255 0 jump @BONETEST
:GET_BONE_OFFSET 0A96: 0@ = actor 0@ struct 0085: 5@ = 0@ // (int) 000A: 5@ += 0x18 0A8D: 5@ = read_memory 5@ size 4 virtual_protect 0 0AA7: call_function 0x734A40 num_params 1 pop 1 5@ 5@ // _clumpGetFirstSkinAtomicHAnimHierarchy 0012: 1@ *= 0x4 000A: 1@ += 0x488 005A: 1@ += 0@ // (int) 0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0 000A: 1@ += 0x14 0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0 0AA7: call_function 0x7C51E0 num_params 2 pop 2 1@ 5@ 1@ // _RpHAnimIDGetIndex 0AA7: call_function 0x7C5160 num_params 1 pop 1 5@ 5@ // _RpHAnimHierarchyGetMatrixArray 0012: 1@ *= 0x40 005A: 1@ += 5@ // (int) 0A9F: 5@ = current_thread_pointer 000A: 5@ += 0x44 0AA5: call 0x54EEF0 num_params 4 pop 4 5@ 1@ 1 5@ // _transofrmPoints 0AB2: ret 3 2@ 3@ 4@
GET_BONE_OFFSETがその関数です。 敵がグレネードランチャー使ってくるスクリプトは作りませんが、一言。
0209: 12@ = random_int_in_ranges 0 100 ではフレームレートが高いほどグレネードランチャーを使ってくる確率が高くなります。 32@ > 5000 があるので違いはほとんど分からないかもしれませんが、 32@ > 5000 をとってやると確率はフレームレートに比例するようになります。 最大を100ではなく、前フレームからの経過時間の数倍にすると常に同じ確率になります。 ##################################################################### スクリプトでの保護(?)のようなもののフラグが分かりました。多分。 構造体の種類によってオフセットが違います。全部1バイト。
CPed+0x484 0x01 その辺の人 0x02 スクリプトで使われてる人
CVehicle+0x4A4 0x01 その辺の車 0x02 スクリプトで使われている車
CObject+0x13C 0x01 マップオブジェクト 0x02 スクリプトで使われている物 0x03 その辺の物 0x05 拾える物、他 この値を目安に削除するとか放っておくとか決めるといいでしょう。 ##################################################################### 例えば、オブジェクトを飛ばして衝突したら爆発するスクリプトを作ろうとしたら、 オブジェクトを作成し飛ばした後、 for .... if 04DA: (check) has_object 0@ collided then .. .. .. end end などとループ処理を使って管理していました。 しかし.sファイルを使えば、 0@ 1@ 2@ に発射速度やオブジェクトの作成座標を格納しておき、 0A92: create_custom_thread "~.s" 0@ 1@ 2@ でスレッドを作成、あとは.s側でオブジェクトの処理を 単独で行わせればいいのです。 ##################################################################### 0A8D: 10@ = read_memory 0xB6F9CC size 4 virtual_protect 0 0A8D: 11@ = read_memory 0xB6F9D0 size 4 virtual_protect 0 0A8D: 12@ = read_memory 0xB6F9D4 size 4 virtual_protect 0 0A8D: 20@ = read_memory 0xB6F9AC size 4 virtual_protect 0 0A8D: 21@ = read_memory 0xB6F9B0 size 4 virtual_protect 0 0A8D: 22@ = read_memory 0xB6F9B4 size 4 virtual_protect 0 カメラ座標、カメラ先座標を求めます。 068D、068Eと違って、CamHackの座標も求められます。 ##################################################################### 0922: set_camera_zoom_in_factor 50.0 out_factor 50.0 timelimit 100 mode 1 The vital activity was stopped, and it died. ##################################################################### 火炎瓶の炎は(実行時アドレスで)0x5E5F7Eと0x5E8D0Bの jnz(0F85)をnop+jmp(90E9)に書き換えてやると消えます。 0x5E5F7E 0x90 0x5E5F7F 0xE9 0x5E8D0B 0x90 0x5E8D0C 0xE9
投げられた火炎瓶の赤い軌跡は0x536633に同じことをすると消えます。 0x536633 0x90 0x536634 0xE9
BYTE[]ではなくWORDで置き換えるときはエンディアンに注意してください。 Intel系の実行コードはリトルエンディアンになっています。 ##################################################################### これはまた残酷ですね。 0xBA86F0からMarkerの構造体が始まり、一つのサイズは0x28バイト、要素数は0xB0。 +0x04 DWORD アタッチ先ハンドル +0x14 WORD マーカーの色 +0x26 BYTE アタッチ先の種類(車04 人08 物0C) これだけあれば十分でしょう。 06BCについては知りません。
:resetactor for 3@ = 0xBA86F0 to 0xBAA248 step 0x28 000A: 3@ += 0x14 0A8D: 4@ = read_memory 3@ size 2 virtual_protect 0 000E: 3@ -= 0x14 0012: 4@ *= 0x10000 0085: 5@ = 3@ // (int) 000E: 5@ -= 0xBA86F0 0016: 5@ /= 0x28 005A: 4@ += 5@ // (int) //mark handle 000A: 3@ += 0x26 0A8D: 5@ = read_memory 3@ size 1 virtual_protect 0 //handle kind 000E: 3@ -= 0x26 if and //0100=car 1000=actor 1100=object 88B7: not test 5@ bit 2 08B7: test 5@ bit 3 then 000A: 3@ += 0x04 0A8D: 5@ = read_memory 3@ size 4 virtual_protect 0 //actor handle 000E: 3@ -= 0x04 if 003B: (check) 5@ == 26@ // (int) then 0164: disable_marker 4@ end end end return ##################################################################### ボーンの位置を求める 2009年05月14日 (木) 13:09 | 編集 タイトルの通りです。 008B: 1@ = $PlAYER_ACTOR // (int) 0006: 2@ = 6 gosub @GETBONE 04D5: create_corona_at 0@ 1@ 2@ radius 0.1 type 0 flare 0 RGB 0 255 0
:GETBONE 0A96: 0@ = actor 1@ struct 0085: 1@ = 0@ // (int) 000A: 1@ += 0x18 0A8D: 1@ = read_memory 1@ size 4 virtual_protect 0 0AA7: call_function 0x734A40 num_params 1 pop 1 1@ 1@ // _clumpGetFirstSkinAtomicHAnimHierarchy 0012: 2@ *= 0x4 000A: 2@ += 0x488 005A: 2@ += 0@ // (int) 0A8D: 2@ = read_memory 2@ size 4 virtual_protect 0 000A: 2@ += 0x14 0A8D: 2@ = read_memory 2@ size 4 virtual_protect 0 0AA7: call_function 0x7C51E0 num_params 2 pop 2 2@ 1@ 2@ // _RpHAnimIDGetIndex 0AA7: call_function 0x7C5160 num_params 1 pop 1 1@ 1@ // _RpHAnimHierarchyGetMatrixArray 0012: 2@ *= 0x40 005A: 2@ += 1@ // (int) 000A: 2@ += 0x30 0A8D: 0@ = read_memory 2@ size 4 virtual_protect 0 000A: 2@ += 0x4 0A8D: 1@ = read_memory 2@ size 4 virtual_protect 0 000A: 2@ += 0x4 0A8D: 2@ = read_memory 2@ size 4 virtual_protect 0 return
{ 1 Spine1 2 Head 3 R UpperArm 4 L UpperArm 5 R Hand 6 L Hand 7 R Thigh 8 L Thigh 9 R Foot 10 L Foot 11 L Calf 12 R Calf 13 R ForeArm 14 L ForeArm 15 Bip01 R Clavicle 16 Bip01 L Clavicle 17 Neck 18 Jaw }
1@に人のハンドル、2@に070Aで使うボーン番号を格納して呼び出すと 座標が0@ 1@ 2@に格納されて帰ってきます。
15行目の処理後には2@にD3DXMATRIXへのポインタが格納されているので、 ベクトルを変換してかけたりすればボーンからの相対座標も変換できます。
また、6-11行目の処理を省けば2@にボーンIDを直接渡すことも可能です。
実行ファイルによっては0x7C51E0が0x7C51A0に、 0x7C5160が0x7C5120になっていることもあるようです。 関数の先頭アドレスの値を調べることで判別できそうです。 ##################################################################### 新機能他 2009年06月06日 (土) 16:21 ミサイルスクリプトの解説でも。
とりあえずやっていることといえば
バッファオーバーランと0埋めによる変数拡張 fgetsとsscanfによるテキスト形式の設定の読みこみ 機械語の埋め込みと実行 低級フック
--------------------------------------------------------------------------------
バッファオーバーランと0埋めによる変数拡張
バッファオーバーランを使ったアクセスはCLEO以前にSCM Opcodeだけで メモリの値を読み書きするのに使われていましたが、CLEOの拡張コードは バイト毎、仮想保護の操作も可能なためほとんど使われない存在となっていました。 この拡張コードはメモリの読み書きには適していますが、変数の拡張には適していません。
たとえば、スクリプトのコード内に0などで埋めたバッファを置いておき、 それを拡張変数として使用し、この拡張変数の値を1増やすとします。
まずは拡張コードを使用した場合。
0A9F: 0@ = current_thread_pointer 000A: 0@ += 0x10 0A8D: 0@ = read_memory 0@ size 4 virtual_protect 0 000E: 0@ -= @EXT_VAR
: 0A8D: 1@ = read_memory 0@ size 4 virtual_protect 0 000A: 1@ += 1 0A8C: write_memory 0@ size 4 value 1@ virtual_protect 0 :
:EXT_VAR hex 00000000 end
バッファオーバーランを使用した場合。
0A9F: 32@ = current_thread_pointer 000A: 1@ += 0x10 0A8D: 0@ = read_memory 1@ size 4 virtual_protect 0 000A: 0@ += 4 000A: 1@ += 0x2C 000E: 0@ -= @EXT_VAR 0062: 0@ -= 1@ // (int) 0016: 0@ /= 4
: 000A: 0@(0@,1i) += 1 :
:EXT_VAR hex 00000000 // padding 00000000 00000000 // padding end
前者では初期化時のコードとバッファが最小で済みますが、 値を変更する場合にはメモリの読み書きが伴い、さらに0@に加えて1@を使用します。
一方後者では初期化時のコードが長くなり、バッファの前後にコードのズレを考慮した詰め物を 置いておかないといけませんが、値を変更する場合は0@だけでアクセス可能です。
これは多数の変数を使用するコードではより顕著になります。
0A9F: 0@ = current_thread_pointer 000A: 0@ += 0x10 0A8D: 0@ = read_memory 0@ size 4 virtual_protect 0 000E: 0@ -= @EXT_VAR
: 0A8D: 1@ = read_memory 0@ size 4 virtual_protect 0 000A: 0@ += 0x4 0A8D: 2@ = read_memory 0@ size 4 virtual_protect 0 000A: 0@ += 0x4 0A8D: 3@ = read_memory 0@ size 4 virtual_protect 0 000A: 0@ += 0x4 0A8D: 4@ = read_memory 0@ size 4 virtual_protect 0 000A: 0@ += 0x4 0A8D: 5@ = read_memory 0@ size 4 virtual_protect 0 000A: 0@ += 0x4 0A8D: 6@ = read_memory 0@ size 4 virtual_protect 0 000A: 0@ += 0x4 0A8D: 7@ = read_memory 0@ size 4 virtual_protect 0 0400: store_coords_to 5@ 6@ 7@ from_object 1@ with_offset 2@ 3@ 4@ 0A8C: write_memory 0@ size 4 value 7@ virtual_protect 0 000E: 0@ -= 0x4 0A8C: write_memory 0@ size 4 value 6@ virtual_protect 0 000E: 0@ -= 0x4 0A8C: write_memory 0@ size 4 value 5@ virtual_protect 0 000E: 0@ -= 0x4 0A8C: write_memory 0@ size 4 value 4@ virtual_protect 0 000E: 0@ -= 0x4 0A8C: write_memory 0@ size 4 value 3@ virtual_protect 0 000E: 0@ -= 0x4 0A8C: write_memory 0@ size 4 value 2@ virtual_protect 0 000E: 0@ -= 0x4 0A8C: write_memory 0@ size 4 value 1@ virtual_protect 0 :
:EXT_VAR hex 00000000 00000000 00000000 00000000 00000000 00000000 00000000 end 0A9F: 32@ = current_thread_pointer 000A: 1@ += 0x10 0A8D: 0@ = read_memory 1@ size 4 virtual_protect 0 000A: 0@ += 4 000A: 1@ += 0x2C 000E: 0@ -= @EXT_VAR 0062: 0@ -= 1@ // (int) 0016: 0@ /= 4
: 0400: store_coords_to 4@(0@,1i) 5@(0@,1i) 6@(0@,1i) from_object 0@(0@,1i) with_offset 1@(0@,1i) 2@(0@,1i) 3@(0@,1i) :
:EXT_VAR hex 00000000 // padding 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 // padding end
--------------------------------------------------------------------------------
fgetsとsscanfによるテキスト形式の設定の読みこみ
0x536F80はfgets(BUFFER, BUFFER_MAX, fp) 3@(3@,1i)は:FUNC_03のアドレスです。
viod FUNC_03(void* pDest, const char* sFormat, const char* sBuffer) { sscanf(sBuffer, sFormat, pDest + 0x00, pDest + 0x04, pDest + 0x08, pDest + 0x0C, pDest + 0x10, pDest + 0x14, pDest + 0x18, pDest + 0x1C, pDest + 0x20, pDest + 0x24, pDest + 0x28, pDest + 0x2C, pDest + 0x30, pDest + 0x34, pDest + 0x38, pDest + 0x3C, pDest + 0x40, pDest + 0x44, pDest + 0x48, pDest + 0x4C, pDest + 0x50, pDest + 0x54, pDest + 0x58, pDest + 0x5C, pDest + 0x60, pDest + 0x64, pDest + 0x68, pDest + 0x6C, pDest + 0x70, pDest + 0x74, pDest + 0x78, pDest + 0x7C, pDest + 0x80, pDest + 0x84, pDest + 0x88, pDest + 0x8C, pDest + 0x90, pDest + 0x94, pDest + 0x98, pDest + 0x9C); }
コードは以下のようになっています。
0A9F: 32@ = current_thread_pointer 000A: 32@ += 0x3C 0085: 14@ = 0@ // (int) 0012: 14@ *= 4 005A: 14@ += 32@ // (int) 0A9F: 13@ = current_thread_pointer 000A: 13@ += 0x10 0A8D: 13@ = read_memory 13@ size 4 virtual_protect 0 000E: 13@ -= @SSCANF_FORMAT 0A99: chdir 0 0A9A: 32@ = openfile "cleo\missile.dat" mode 0x6272 // IF and SET for 33@ = 0 to 23 0AA7: call_function 0x536F80 num_params 1 pop 1 32@ 11@ 0A8D: 12@ = read_memory 11@ size 1 virtual_protect 0 if 0039: 12@ == 0 then break end if 0039: 12@ == 0x3B then 000E: 33@ -= 1 continue end 0AA5: call 3@(3@,1i) num_params 3 pop 3 11@ 13@ 14@ 000A: 14@ += 0xA0 end 0A9B: closefile 32@
GTA San Andreas\cleo\missile.datを開き、fgets()で1行ずつ読みこみ、 行頭がNULなら読みこみ中止、行頭がセミコロンならその行を無視します。 どちらでもない場合は書式にしたがって読み込みます。
--------------------------------------------------------------------------------
機械語の埋め込みと実行
関数ポインタの配列を上のローカル変数の拡張を使用して作成します。
0A9F: 0@ = current_thread_pointer 000A: 0@ += 0x10 0A8D: 0@ = read_memory 0@ size 4 virtual_protect 0 0085: 0@(3@,1i) = 0@ // (int) 000E: 0@(3@,1i) -= @FUNC_00
:FUNC_00には機械語を直接記述します。
:FUNC_00 hex 6A00 // push 0 6A00 // push 0 6800007041 // push 15.0 6A00 // push 0 68CDCC4C3E // push 0.2 6A00 // push 0 6A00 // push 0 6A00 // push 0 6A00 // push 0 6A01 // push 1 8B4C2430 // mov ecx, [esp+0x30] 51 // push ecx // Corona Flare 8B4C2438 // mov ecx, [esp+0x38] 51 // push ecx // Corona Type 6800803B45 // push 3000.0 8B4C2444 // mov ecx, [esp+0x44] 51 // push ecx // Corona Size 8D94244C000000 // lea edx, [esp+0x0000004C] 52 // push edx // Corona Position Pointer 68FF000000 // push 255 // Corona Alpha 6880000000 // push 128 // Corona Blue 68C0000000 // push 192 // Corona Green 68FF000000 // push 255 // Corona Red 6A00 // push 0 8B442454 // mov eax, [esp+0x54] 03C6 // add eax, esi 50 // push eax // Corona ID B880C56F00 // mov eax, 0x006FC580 FFD0 // call eax 83C454 // add esp, 0x54 C3 // ret end
これはコロナを表示するコードで、色は固定されています。 表示範囲は3000mまで延長され、IDを指定可能です。 0AA5: call 0@(3@,1i) num_params 7 pop 7 [Z] [Y] [X] [Size] [Type] [Flare] [ID] IDを変更すれば同じ場所から呼び出しても複数表示可能です。
他にも
オブジェクトへロケットの音を適用する関数 爆発を作成する関数 sscanfを間接的に呼び出す関数 HitTestを行う関数 アークタンジェントを求める関数 座標にオブジェクトの逆マトリクスをかける関数 があります。
最後の3つは既存のコード(非公開, #83, #264)と比べるとずっと速くなっています。
--------------------------------------------------------------------------------
低級フック 至極単純。 フックしたいコードの場所に
mov eax, [ADDRESS] jmp eax
を埋め込むだけです。
たとえば携行ロケットの発射をフックしたいなら
0A9F: 13@ = current_thread_pointer 000A: 13@ += 0x10 0A8D: 13@ = read_memory 13@ size 4 virtual_protect 0 000E: 13@ -= @PROX_RPG 0A8C: write_memory 0x741A53 size 1 value 0xB8 virtual_protect 1 0A8C: write_memory 0x741A54 size 4 value 13@ virtual_protect 1 0A8C: write_memory 0x741A58 size 2 value 0xE0FF virtual_protect 1 000A: 13@ += 1 0A9F: 14@ = current_thread_pointer 0A8C: write_memory 13@ size 4 value 14@ virtual_protect 1
:PROX_RPG hex 7407 // je +0x07 B8807C7300 // mov eax, 0x00737C80 // 普通の処理 FFD0 // call eax
83C42C // add esp, 0x20 // 処理をスキップ B85B1A7400 // mov eax, 0x00741A5B FFE0 // jmp eax #####################################################################