alchemistarkの日記

やったことのメモ

SouceModプラグインの開発を始めるにあたって必要なもの

SourcePawnのWiki作ろうって瞬間だけ思って放置してるやつに書いてたやつ。

準備するもの

プラグインのアイディア、プラグインを作成して行いたいこと

これが無ければ始まりません。

MetaMod、SourceModが動作する環境

srcdsのインストールにて解説します。(機会があれば書きます)

SourcePawnのコンパイラ

SourceModがインストールされていれば、
addons/sourcemod/scripting/compile.exe
にあります。

また、ブラウザから利用できるコンパイラとしてSourceModの配布ページの
www.sourcemod.net

こちらか、

Spider - SourcePawn Compiler

こちらが利用できます。

下の方はincファイルのincludeができるのでこちらの方が少々多機能です。

テキストエディタ

文字コード UTF-8(BOMなし)で保存できるエディタ なら何でも良いです。
プラグイン自体は文字コードを問いませんが、KeyValueを使用する時に使うファイルは UTF-8(BOMなし)である必要があります。

お好みのエディタがあるならばそれを、特にこだわらないならNotepad++を推奨します。
「Notepad++」オープンソースで開発されているプログラマー向けのテキストエディタ
forest.watch.impress.co.jp


SourcePawn用のハイライト機能を使うことができます。

こちらで配布されています。
forums.alliedmods.net


プラグインになりますが、NppExecというプラグインを導入することによって保存してそのままコンパイラにファイルを送ることができるので、
こちらも導入を推奨します。

NppExecの使い方を解説しているページ
NppExec (コマンドの実行) | Notepad++のプラグイン解説




設定例

NPP_SAVE 
(インストールフォルダ)\addons\sourcemod\scripting\spcomp.exe "$(FULL_CURRENT_PATH)" 
powershell move $(NAME_PART).smx (インストールフォルダ)\addons\sourcemod\plugins\ -force

別にpowershellで無くても良いですが、サーバーパスがsmbの向こう側だったりすると必要です。

以上が揃えばプラグインを書く準備が整います。

Propの出現と破壊可能にする方法

ゴリゴリ書いてたのでとりあえず全部貼る。

public Action:Cmd_prop(client, args){

	new ent = CreateEntityByName("prop_dynamic");
	new filter;
	new String:temp[255];
	

	//フィルター関係///////////////////////////
	
	while ((filter = FindEntityByClassname(filter, "filter_activator_tfteam")) != -1){
		GetEntPropString(filter, Prop_Data, "m_iGlobalname", temp,255);
		if(StrEqual(temp,"prop_filter")){
			//既にフィルターが生成されていた場合、削除する
			AcceptEntityInput(filter, "Kill");
		}
	}
	
	filter = CreateEntityByName("filter_activator_tfteam");	//フィルターを作成する
	SetEntProp(filter, Prop_Data, "m_iTeamNum",TFTeam_Blue);	//フィルター対象チームを設定
	
	DispatchSpawn(filter);	//スポーンさせる	
	
	//フィルター名を設定する
	SetEntPropString(filter, Prop_Data, "m_iGlobalname", "prop_filter");

	//Propにフィルターを設定する
	//フィルター名とフィルターEntityが両方必要?
	SetEntPropString(ent, Prop_Data, "m_iszDamageFilterName","prop_filter");
	
	//PropにフィルターEntityを設定
	SetEntPropEnt(ent, Prop_Data, "m_hDamageFilter",filter);
	
	//Prop関係//////////////////////////////////////
	
	//Propのダメージイベントをフックする
	HookSingleEntityOutput(ent,"OnTakeDamage",EntityHook,false);
	
	//Propにモデルを設定する
	SetEntPropString(ent, Prop_Data, "m_ModelName", "models/props_badlands/barrel_flatbed01.mdl");
	//衝突判定を設定
	SetEntProp(ent, Prop_Data, "m_nSolidType",6);
	
	//Propをスポーンさせる
	DispatchSpawn(ent);
	
	//Propのヘルスを設定する
	SetEntProp(ent, Prop_Data, "m_iHealth", 200);
	
	//Propがダメージを受けるようにする
	SetEntProp(ent, Prop_Data, "m_takedamage", 2, 1);	//Mortal
	
	
	//適当な位置に出現させる
	new float:vec[3];
	GetClientAbsOrigin(client, vec);
	TeleportEntity(ent, vec, NULL_VECTOR, NULL_VECTOR);
	
}

public EntityHook(const char[] output, int caller, int activator, float delay){
	PrintToChatAll("%d",GetEntProp(caller, Prop_Data, "m_iHealth"));
}

大体コメントの通り
寝て良いよ

SetEntProp(ent, Prop_Data, "m_nSolidType",6);の値が6なのは何故か分かってない
一応プロパティのCollisionsの値がUse VPhysicsの時6になる。
KeyValueのキー名はsolid。

破壊可能にするだけならヘルスを設定してMortal化するだけでいいはず
ただし破壊された時何も表示せずただ消えるだけなので、破壊時にパーティクルを表示する等の配慮が必要。

フィルターは指定チームのみダメージを与えられるように設定してるので、誰でも壊せるならフィルター自体不要
なるべくフィルターには変な名前付けてマップ既存のフィルターと重複しないように注意した方がいいと思うけど大丈夫じゃないかな

あと言うまでもないけどTeleportEntityの座標がクライアントの位置なのでそのまま呼ぶと自分がPropにハマるから注意。

prop_dynamic - Valve Developer Community
filter_activator_tfteam - Valve Developer Community


追記
prop_physics_overrideを呼び出した場合、何故かHookSingleEntityOutputが上手く動作しないので
HookEntityOutputを使用した方がいいかも。この場合Entityの番号を保存しておくかユニークな名称を付けておく必要があるのでかなりめんどくさい。

prop_physics_override - Valve Developer Community

ていうかね!違いが全然分かってないんだよね!!!
prop_physics→モデル側で対応が必要
prop_physics_multiplayer→わからない モデルが動作中はプレイヤーがすり抜けてしまう?(モデルが静止するとCollisionが復活するのでプレイヤーが埋まる)
prop_physics_override→physics非対応モデルをphysics化?以降Entityのクラス名はprop_physicsになる
prop_physics_respawnable→知らない子です

パーティクルについて

完全に詰んだので書いておく。

まず基本的にこれ。
kimoto.hatenablog.com

これを使って決闘アイコンを表示したい。
んで表示したパーティクルをKill等で削除したいんだけど消えない。
Entityは消えている。決闘アイコンだけ宙に浮いたままになる。
なお1度も視認しなかったプレイヤーや消した後にjoinしたプレイヤーからは見えない。
クライアント側の問題?

解決案?
追従解除してマップのどっかの座標にふっ飛ばしてから消すとか


追記
アンユエフェクト出してみたら消えた
決闘アイコンもエフェクトアニメーションだけ消えるので、どうやらアニメーション設定されていないパーツが有る場合消えなくなる模様。

すごくどうでもいいことに3日ぐらい使った

NPP_SAVE
powershell "$0 = cat \"$(FULL_CURRENT_PATH)\" -Encoding UTF8;$1 = $0 -match \"#define BUILD\";$1 = $1.GetValue(0);$2 = $1.IndexOf(\"(\")+1;$3 = $1.LastIndexOf(\")\")-$2;$2 = $1.substring($2,$3);$3 = [int]$2 + 1;$0 -replace (\"build\($2\)\"),(\"build($3)\") | Set-Content \"$(FULL_CURRENT_PATH)\" -Encoding UTF8"
spcomp.exe "$(FULL_CURRENT_PATH)"
powershell copy $(NAME_PART).smx addons\sourcemod\plugins\ -force
powershell move $(NAME_PART).smx addons\sourcemod\plugins\ -force
powershell copy $(FULL_CURRENT_PATH) addons\sourcemod\scripting\ -force

これなーんだ

元々Notepad++でコンパイラにsp叩き込むようにしてたんだけど、
それに加えてspとsmxをしかるべき場所にコピーするようにしておいた

ついでにコード上で

#define BUILD = "build(0)"

public Plugin:myinfo = 
{
	name = "",
	author = "",
	description = "",
	version = BUILD,
	url = ""
}

って書いとくとコンパイルする度にpowershellでビルド回数を増やしていくという…
うん何やってんだ僕は…
んでそれを1行で叩き込んでると言うね もうね アホかと

PowerShellさんがスクリプトファイルをデフォルトで実行してくれないからです
あとNppExecで動かしたいってのもありますん
以下改行したやつ

$0 = cat "$(FULL_CURRENT_PATH)" -Encoding UTF8
$1 = $0 -match "#define BUILD"
$1 = $1.GetValue(0)
$2 = $1.IndexOf("(")+1
$3 = $1.LastIndexOf(")")-$2
$2 = $1.substring($2,$3)
$3 = [int]$2 + 1
$0 -replace ("build\($2\)"),("build($3)") | Set-Content "$(FULL_CURRENT_PATH)" -Encoding UTF8

はい、何やってんだろうね
寝て良いよ

TF2 セントリーガンの所有者を確認する

多分これ。

Member: m_hBuilder (offset 2276) (type integer) (bits 21) (Unsigned)

他にも使えそうなのいくつか。実際に中身確認してないから本当にそうか分からないけど、名称から予想。
m_bPlayerControlled:ラングラーで操作中かどうか?
m_bBuilding:建造中かどうか?

他の建造物も多分同じプロパティ持ってるとは思う。


試してみないと分からないけど、設置してすぐ所有者を消したら
2つ目3つ目のセントリーを置ける…かもしれない?

TF2 死を回避する方法

無敵化ではなく、ヘルスが0になったけどそのまま戦い続けるようにする方法。

引っ掛けるイベントはplayer_hurt
player_deathはやっぱりダメだった。よってplayer_deathイベントの情報は持ってこれない…

HookEvent("player_hurt", OnPlayerHurt,EventHookMode_Pre);
new client = GetClientOfUserId(GetEventInt(event, "userid"));
if(GetEventInt(event, "health") == 0){
	SetEntityHealth(client,1);
}

"health"の値はマイナスにはならないので安心して0でチェックして良い
ヘルスを最大値にしようと思ったらクラスをチェックしてクラスの最大値を直接指定する必要がある

player_deathが発生しないので、スコアは入らないしキルもデスもカウントが増えない点に注意。
イベントをFireしてもダメなんかな?多分ダメだと思うけど。


customの値一覧。
player_hurtの"custom"とplayer_deathの"customkill"で同じ値出てるから多分一緒だと思う。

…ヘッドショットが1でバックスタブが2なのは確認したし、火炎放射器と延焼ダメージが3なのも間違いない
けどライフルのボディショットが種類にかかわらず11で、小便銃が14(追記:ヒットマンズ・ヒートメーカーは51が出る。TF_CUSTOM_HEADSHOT_DECAPITATION)
分からぬ。
下記のリストは参考までに。

// Custom kill identifiers for the customkill property on the player_death event
enum {
	TF_CUSTOM_HEADSHOT = 1,
	TF_CUSTOM_BACKSTAB,
	TF_CUSTOM_BURNING,
	TF_CUSTOM_WRENCH_FIX,
	TF_CUSTOM_MINIGUN,
	TF_CUSTOM_SUICIDE,
	TF_CUSTOM_TAUNT_HADOUKEN,
	TF_CUSTOM_BURNING_FLARE,
	TF_CUSTOM_TAUNT_HIGH_NOON,
	TF_CUSTOM_TAUNT_GRAND_SLAM,
	TF_CUSTOM_PENETRATE_MY_TEAM,
	TF_CUSTOM_PENETRATE_ALL_PLAYERS,
	TF_CUSTOM_TAUNT_FENCING,
	TF_CUSTOM_PENETRATE_HEADSHOT,
	TF_CUSTOM_TAUNT_ARROW_STAB,
	TF_CUSTOM_TELEFRAG,
	TF_CUSTOM_BURNING_ARROW,
	TF_CUSTOM_FLYINGBURN,
	TF_CUSTOM_PUMPKIN_BOMB,
	TF_CUSTOM_DECAPITATION,
	TF_CUSTOM_TAUNT_GRENADE,
	TF_CUSTOM_BASEBALL,
	TF_CUSTOM_CHARGE_IMPACT,
	TF_CUSTOM_TAUNT_BARBARIAN_SWING,
	TF_CUSTOM_AIR_STICKY_BURST,
	TF_CUSTOM_DEFENSIVE_STICKY,
	TF_CUSTOM_PICKAXE,
	TF_CUSTOM_ROCKET_DIRECTHIT,
	TF_CUSTOM_TAUNT_UBERSLICE,
	TF_CUSTOM_PLAYER_SENTRY,
	TF_CUSTOM_STANDARD_STICKY,
	TF_CUSTOM_SHOTGUN_REVENGE_CRIT,
	TF_CUSTOM_TAUNT_ENGINEER_SMASH,
	TF_CUSTOM_BLEEDING,
	TF_CUSTOM_GOLD_WRENCH,
	TF_CUSTOM_CARRIED_BUILDING,
	TF_CUSTOM_COMBO_PUNCH,
	TF_CUSTOM_TAUNT_ENGINEER_ARM,
	TF_CUSTOM_FISH_KILL,
	TF_CUSTOM_TRIGGER_HURT,
	TF_CUSTOM_DECAPITATION_BOSS,
	TF_CUSTOM_STICKBOMB_EXPLOSION,
	TF_CUSTOM_AEGIS_ROUND,
	TF_CUSTOM_FLARE_EXPLOSION,
	TF_CUSTOM_BOOTS_STOMP,
	TF_CUSTOM_PLASMA,
	TF_CUSTOM_PLASMA_CHARGED,
	TF_CUSTOM_PLASMA_GIB,
	TF_CUSTOM_PRACTICE_STICKY,
	TF_CUSTOM_EYEBALL_ROCKET,
	TF_CUSTOM_HEADSHOT_DECAPITATION,
	TF_CUSTOM_TAUNT_ARMAGEDDON,
	TF_CUSTOM_FLARE_PELLET,
	TF_CUSTOM_CLEAVER,
	TF_CUSTOM_CLEAVER_CRIT,
	TF_CUSTOM_SAPPER_RECORDER_DEATH,
	TF_CUSTOM_MERASMUS_PLAYER_BOMB,
	TF_CUSTOM_MERASMUS_GRENADE,
	TF_CUSTOM_MERASMUS_ZAP,
	TF_CUSTOM_MERASMUS_DECAPITATION,
	TF_CUSTOM_CANNONBALL_PUSH,
	TF_CUSTOM_TAUNT_ALLCLASS_GUITAR_RIFF,
	TF_CUSTOM_THROWABLE,
	TF_CUSTOM_THROWABLE_KILL,
	TF_CUSTOM_SPELL_TELEPORT,
	TF_CUSTOM_SPELL_SKELETON,
	TF_CUSTOM_SPELL_MIRV,
	TF_CUSTOM_SPELL_METEOR,
	TF_CUSTOM_SPELL_LIGHTNING,
	TF_CUSTOM_SPELL_FIREBALL,
	TF_CUSTOM_SPELL_MONOCULUS,
	TF_CUSTOM_SPELL_BLASTJUMP,
	TF_CUSTOM_SPELL_BATS,
	TF_CUSTOM_SPELL_TINY,
	TF_CUSTOM_KART,
	TF_CUSTOM_GIANT_HAMMER,
	TF_CUSTOM_RUNE_REFLECT,
	TF_CUSTOM_DRAGONS_FURY_IGNITE,
	TF_CUSTOM_DRAGONS_FURY_BONUS_BURNING,
	TF_CUSTOM_SLAP_KILL,
	TF_CUSTOM_CROC,
	TF_CUSTOM_TAUNTATK_GASBLAST,
};