alchemistarkの日記

やったことのメモ

PropSpawnプラグイン

クソプラグイン作った
指定したPropを出現させて位置を設定、設置した位置を保存できる
ラウンド開始時に自動でロードする機能付き
マップをカスタムしたりしてどうぞ

以下ソースコード全文

#include <sourcemod>
#include <sdktools>

new Handle:v_enable = INVALID_HANDLE;
new Handle:v_map = INVALID_HANDLE;

new g_ent[MAXPLAYERS+1];	//(プレイヤーごとに保存)操作対象のProp
new float:g_point[MAXPLAYERS+1];	//(プレイヤーごとに保存)Propの操作単位

public Plugin:myinfo =
{
	//プラグイン情報を記述
	name = "[ANY]Prop Spawn",
	author = "AMG",
	description = "Propを出したり消したり",
	version = "",
	url = ""
}

//プラグイン起動時
public OnPluginStart(){

	v_enable = CreateConVar("sm_prop_enable", "0", "Prop操作モードのオンオフ");
	v_map = CreateConVar("sm_prop_autoload", "0", "自動的にPropをロードする");

	RegAdminCmd("sm_spawn", Cmd_prop_spawn, 0, "Propを出現させる");
	RegAdminCmd("sm_skin", Cmd_prop_skin, 0, "Propのスキンを変更する");
	RegAdminCmd("sm_tele", Cmd_prop_tele, 0, "Propを移動させる");
	RegAdminCmd("sm_ang", Cmd_prop_ang, 0, "Propの向きを変える");
	RegAdminCmd("sm_kill", Cmd_prop_kill, 0, "Propを消去する");
	RegAdminCmd("sm_save", Cmd_prop_save, 0, "Propの位置を保存する");
	RegAdminCmd("sm_load", Cmd_prop_load, 0, "Propの位置を再現する");
	RegAdminCmd("sm_target", Cmd_prop_target, 0, "Propの位置の微調整");
	RegAdminCmd("sm_point", Cmd_prop_point, 0, "Propの移動距離を設定");
	RegAdminCmd("sm_hp", Cmd_prop_hp, 0, "Propのヘルスを設定する");
	//RegAdminCmd("sm_move", Cmd_prop_move, 0, "Propを持ち運ぶ");
	
	//Propのダメージイベントをフックする
	HookEntityOutput("prop_physics","OnTakeDamage",EntityHook);
	HookEntityOutput("prop_dynamic","OnTakeDamage",EntityHook);
	//HookEvent("break_prop", OnBreakProp);
	
	HookEvent("teamplay_round_active", OnRoundStart);	
}

//HookEvent ラウンド開始時
public OnRoundStart(Handle:event, const String:name[], bool:dontBroadcast){

	if(GetConVarInt(v_map) == 0){
		return;
	}
	
	Func_PropLoad();
}

//Propにダメージが与えられた時に実行される OnTakeDamage
public Action:EntityHook(const char[] output, int caller, int activator, float delay){

	if(GetConVarInt(v_enable) == 0){
		return Plugin_Continue;
	}
	
	//PrintToChatAll("Entity:%d OutPut:%s Health:%d Activator:%d",caller,output,GetEntProp(caller, Prop_Data, "m_iHealth"),activator);
	
	new String:strName[10];
	
	GetEntityClassname(activator,strName,sizeof(strName));
	
	if(StrEqual(strName,"player") == true){
		PrintToChat(activator,"TargetEntity:%d",caller);
		g_ent[activator] = caller;
	}
}

//チャットトリガ Propの移動距離を設定
public Action:Cmd_prop_point(client,args){

	if(GetConVarInt(v_enable) == 0){
		return Plugin_Continue;
	}
	
	new String:strCmd[10];
	new float:flTemp;
	
	GetCmdArg(1,strCmd,sizeof(strCmd));
	flTemp = StringToFloat(strCmd);
	
	if(FloatCompare(flTemp,0.0) < 1){
		PrintToChat(client,"0.1以上の値を指定してください。");
		return Plugin_Continue;
	}
	
	g_point[client] = flTemp;
	PrintToChat(client,"移動距離を%fに設定しました。",g_point[client]);
	
	return Plugin_Continue;
}

//チャットトリガ Prop位置微調整
public Action:Cmd_prop_target(client, args){

	if(GetConVarInt(v_enable) == 0){
		return Plugin_Continue;
	}

	//移動量が不正ならば1を代入する
	if(FloatCompare(g_point[client],0.0) <= 0){
		g_point[client] = 1.0;
	}
	
	new String:strCmd[10];
	new float:vec[3];
	new float:ang[3];
	
	GetCmdArgString(strCmd,sizeof(strCmd));
	
	//数値が来たら対象のEntityIndexを更新する
	if(StringToInt(strCmd) != 0){
		g_ent[client] = StringToInt(strCmd);
		if(IsValidEntity(g_ent[client]) == false){
			PrintToChat(client,"ValidEntity %d",g_ent[client]);
		}
		return Plugin_Continue;
	}

	//Entityの存在確認
	if(IsValidEntity(g_ent[client]) == false){
		PrintToChat(client,"ValidEntity %d",g_ent[client]);
		return Plugin_Continue;
	}
	
	//座標を確保
	GetEntPropVector(g_ent[client], Prop_Data, "m_vecOrigin", vec);
	GetEntPropVector(g_ent[client], Prop_Data, "m_angRotation", ang);

	//x座標
	if(StrEqual(strCmd,"+x") == true){
		vec[0] = FloatAdd(vec[0],g_point[client]);
	}
	else if(StrEqual(strCmd,"-x") == true){
		vec[0] = FloatAdd(vec[0],FloatMul(g_point[client],-1.0));
	}
	//y座標
	else if(StrEqual(strCmd,"+y") == true){
		vec[1] = FloatAdd(vec[1],g_point[client]);
	}
	else if(StrEqual(strCmd,"-y") == true){
		vec[1] = FloatAdd(vec[1],FloatMul(g_point[client],-1.0));
	}
	//z座標
	else if(StrEqual(strCmd,"u") == true){
		vec[2] = FloatAdd(vec[2],g_point[client]);
	}
	else if(StrEqual(strCmd,"d") == true){
		vec[2] = FloatAdd(vec[2],FloatMul(g_point[client],-1.0));
	}
	//ピッチ(前後回転)
	else if(StrEqual(strCmd,"+p") == true){
		ang[0] = FloatAdd(ang[0],g_point[client]);
	}
	else if(StrEqual(strCmd,"-p") == true){
		ang[0] = FloatAdd(ang[0],FloatMul(g_point[client],-1.0));
	}
	//ヨー(水平左右回転)
	else if(StrEqual(strCmd,"l") == true){
		ang[1] = FloatAdd(ang[1],g_point[client]);
	}
	else if(StrEqual(strCmd,"r") == true){
		ang[1] = FloatAdd(ang[1],FloatMul(g_point[client],-1.0));
	}
	//ロール(垂直左右回転)
	else if(StrEqual(strCmd,"+r") == true){
		ang[2] = FloatAdd(ang[2],g_point[client]);
	}
	else if(StrEqual(strCmd,"-r") == true){
		ang[2] = FloatAdd(ang[2],FloatMul(g_point[client],-1.0));
	}

	//指定の位置に移動
	TeleportEntity(g_ent[client], vec, ang, NULL_VECTOR);

	return Plugin_Continue;
}

//チャットトリガ Propヘルス設定
public Action:Cmd_prop_hp(client, args){

	if(GetConVarInt(v_enable) == 0){
		return Plugin_Continue;
	}
	
	//Entityの存在確認
	if(IsValidEntity(g_ent[client]) == false){
		PrintToChat(client,"ValidEntity %d",g_ent[client]);
		return Plugin_Continue;
	}

	new String:strArg[30];
	new iHealth;
	new iDamage;
	
	if(args != 1){
		PrintToChat(client,"need arg");
	}
	
	GetCmdArg(1,strArg,sizeof(strArg));
	
	iHealth = StringToInt(strArg);
	
	//Propのヘルスを設定する
	SetEntProp(g_ent[client], Prop_Data, "m_iHealth", iHealth);
	
	if(iHealth < 1){
		iDamage = 1;
	}
	else{
		iDamage = 2;
	}
	
	//ヘルスが0以外ならばダメージを受ける 0なら無敵
	SetEntProp(g_ent[client], Prop_Data, "m_takedamage", iDamage);
	
}

//チャットトリガ Prop消去
public Action:Cmd_prop_kill(client, args){

	if(GetConVarInt(v_enable) == 0){
		return Plugin_Continue;
	}
	
	//Entityの存在確認
	if(IsValidEntity(g_ent[client]) == false){
		PrintToChat(client,"ValidEntity %d",g_ent[client]);
		return Plugin_Continue;
	}
	
	AcceptEntityInput(g_ent[client],"Kill");
}

//チャットトリガ Prop出現
public Action:Cmd_prop_spawn(client, args){

	if(GetConVarInt(v_enable) == 0){
		return Plugin_Continue;
	}
	
	new String:Model[PLATFORM_MAX_PATH];
	new String:Prop[30];

	if(args < 1){
		PrintToChat(client,"!prop_spawn 'ModelName' 'PropType'");
		return Plugin_Continue;
	}
	GetCmdArg(1,Model,sizeof(Model));
	if(args >= 2){
		GetCmdArg(2,Prop,sizeof(Prop));
	}
	else{
		//Propタイプが指定されていなければphysics_overrideとする
		Prop = "prop_physics_override";
	}
	
	//Entity作成
	new ent = Func_PropSpawn(Model,Prop);
	
	if(ent == -1){
		PrintToChat(client,"Propの生成に失敗しました");
		return Plugin_Continue;
	}

	//Ent番号を表示させる
	PrintToChat(client,"Spawn Entity:%d target set",ent);
	g_ent[client] = ent;
	
	new float:vec[3];
	new float:ang[] = {0,0,0};
	
	GetClientAbsOrigin(client, vec);
	TeleportEntity(ent, vec, ang, NULL_VECTOR);
	
	return Plugin_Continue;
}

//関数 Prop出現処理
public Func_PropSpawn(String:Model[], String:Prop[]){

	new ent = CreateEntityByName(Prop);
	
	if(ent == -1){
		return ent;
	}
	
	new String:strName[PLATFORM_MAX_PATH];
	
	//Propにモデルを設定する
	SetEntPropString(ent, Prop_Data, "m_ModelName", Model);
	//衝突判定を設定
	SetEntProp(ent, Prop_Data, "m_nSolidType",6);
	
	//チェックのために名前をつける 同じ名前は付けられない(破壊されていても)
	Format(strName,sizeof(strName),"prop_test_%d",GetGameTickCount());
	SetEntPropString(ent, Prop_Data, "m_iName", strName);
	
	//Propをスポーンさせる
	DispatchSpawn(ent)
	
	return ent;
}

//チャットトリガ Prop移動
public Action:Cmd_prop_tele(client, args){

	if(GetConVarInt(v_enable) == 0){
		return Plugin_Continue;
	}

	//Entityの存在確認
	if(IsValidEntity(g_ent[client]) == false){
		PrintToChat(client,"ValidEntity %d",g_ent[client]);
		return Plugin_Continue;
	}
	
	new float:vec[3];
	new String:temp[25];
	
	GetCmdArg(1,temp,sizeof(temp));
	vec[0] = StringToFloat(temp);
	GetCmdArg(2,temp,sizeof(temp));
	vec[1] = StringToFloat(temp);
	GetCmdArg(3,temp,sizeof(temp));
	vec[2] = StringToFloat(temp);
	
	TeleportEntity(g_ent[client], vec, NULL_VECTOR, NULL_VECTOR);
}

/*チャットトリガ Prop持ち運び
public Action:Cmd_prop_move(client, args){

	if(GetConVarInt(v_enable) == 0){
		return Plugin_Continue;
	}
	
	//Entityの存在確認
	if(IsValidEntity(g_ent[client]) == false){
		PrintToChat(client,"ValidEntity %d",g_ent[client]);
		return Plugin_Continue;
	}
	
	new String:strName[10];
	
	IntToString(client,strName,sizeof(strName));
	
	if(GetEntPropEnt(g_ent[client], Prop_Data, "m_hMoveParent") <= 0){
	
		//クライアント番号で名前をつける
		SetEntPropString(client, Prop_Data, "m_iName", strName);
		//親子化処理
		SetVariantString(strName);
		AcceptEntityInput(g_ent[client], "SetParent");
	}
	else{
		//親子化解除処理
		AcceptEntityInput(g_ent[client], "ClearParent");
	}
	
}
*/

//チャットトリガ Prop回転
public Action:Cmd_prop_ang(client, args){

	if(GetConVarInt(v_enable) == 0){
		return Plugin_Continue;
	}
	
	//Entityの存在確認
	if(IsValidEntity(g_ent[client]) == false){
		PrintToChat(client,"ValidEntity %d",g_ent[client]);
		return Plugin_Continue;
	}
	
	new float:ang[3];
	new String:temp[25];
	
	GetCmdArg(1,temp,sizeof(temp));
	ang[0] = StringToFloat(temp);
	GetCmdArg(2,temp,sizeof(temp));
	ang[1] = StringToFloat(temp);
	GetCmdArg(3,temp,sizeof(temp));
	ang[2] = StringToFloat(temp);
	
	TeleportEntity(g_ent[client], NULL_VECTOR, ang, NULL_VECTOR);
}

//チャットトリガ Propスキン変更
public Action:Cmd_prop_skin(client, args){

	if(GetConVarInt(v_enable) == 0){
		return Plugin_Continue;
	}
	
	//Entityの存在確認
	if(IsValidEntity(g_ent[client]) == false){
		PrintToChat(client,"ValidEntity %d",g_ent[client]);
		return Plugin_Continue;
	}
	
	new String:temp[5];
	
	GetCmdArg(1,temp,sizeof(temp));
	
	SetEntProp(g_ent[client], Prop_Data, "m_nSkin", StringToInt(temp));
}

//チャットトリガ Prop一覧作成
public Action:Cmd_prop_save(client, args){

	if(GetConVarInt(v_enable) == 0){
		return Plugin_Continue;
	}

	Func_PropSearch(1);
	
}

//関数 Propを探す
public Func_PropSearch(int Mode){

	//Mode 1 : Propの位置を保存する
	//Mode 0 : Propを全て削除する

	new ent = -1;
	new String:strClassName[30];
	new String:strMap[PLATFORM_MAX_PATH];
	new Handle:hFile = INVALID_HANDLE;
	
	if(Mode){
		GetCurrentMap(strMap,sizeof(strMap));
		Format(strMap,sizeof(strMap),"cfg/%s_prop.csv",strMap);
		
		//ファイルを作成する(読み込みモード・既存破棄(w)・書き込み可(+))
		hFile = OpenFile(strMap,"w+");
		if(hFile == INVALID_HANDLE){
			PrintToServer("[PropSpawn]ファイルが読み取り専用もしくは開いたままになっていませんか?");
			return;
		}
	}
	
	strClassName = "prop_physics"
	while ((ent = FindEntityByClassname(ent, strClassName)) != -1)	//名前からエンティティを探す
	{
		if(Mode){
			//恐らく全てoverrideだと思うん
			Func_Prop_File(ent,hFile,"prop_physics_override");
		}
		else{
			GetEntPropString(ent, Prop_Data, "m_iName", strMap, sizeof(strMap));
			if(strncmp("prop_test",strMap,9) == 0){
				AcceptEntityInput(ent,"Kill");
			}
		}
	}
	ent = -1;
	strClassName = "prop_dynamic"
	while ((ent = FindEntityByClassname(ent, strClassName)) != -1)	//名前からエンティティを探す
	{
		if(Mode){
			Func_Prop_File(ent,hFile,strClassName);
		}
		else{
			GetEntPropString(ent, Prop_Data, "m_iName", strMap, sizeof(strMap));
			if(strncmp("prop_test",strMap,9) == 0){
				AcceptEntityInput(ent,"Kill");
			}
		}
	}
	if(Mode){
		CloseHandle(hFile);
	}
}

//Propの一覧 ファイル書き込み処理
public Func_Prop_File(int ent,Handle:hFile, String:strClassName[]){

	/*
	Propの情報をカンマ区切りリCSV形式で保存する
	・モデル名
	・Prop種類
	・位置座標(3要素)
	・角度(3要素)
	・スキン
	・ヘルス
	・衝突判定
	・ダメージ有効かどうか
	*/

	new float:vec[3];
	new float:ang[3];
	new String:strName[PLATFORM_MAX_PATH];

	GetEntPropString(ent, Prop_Data, "m_iName", strName, sizeof(strName));
	if(strncmp("prop_test",strName,9) == 0){
		GetEntPropString(ent, Prop_Data, "m_ModelName", strName, sizeof(strName));
		GetEntPropVector(ent, Prop_Data, "m_vecOrigin", vec);
		GetEntPropVector(ent, Prop_Data, "m_angRotation", ang);
		WriteFileLine(hFile,"%s,%s,%f,%f,%f,%f,%f,%f,%d,%d,%d,%d",strName,strClassName,vec[0],vec[1],vec[2],ang[0],ang[1],ang[2],GetEntProp(ent, Prop_Data, "m_nSkin"),GetEntProp(ent, Prop_Data, "m_iHealth"),GetEntProp(ent, Prop_Data, "m_nSolidType"),GetEntProp(ent, Prop_Data, "m_takedamage"));
	}
	
}

//チャットトリガ Prop再現
public Action:Cmd_prop_load(client, args){

	if(GetConVarInt(v_enable) == 0){
		return Plugin_Continue;
	}
	
	Func_PropLoad();
}

public Func_PropLoad(){
	
	new String:strMap[PLATFORM_MAX_PATH];
	new String:strArg[12][PLATFORM_MAX_PATH];
	
	new float:vec[3];
	new float:ang[3];
	
	new ent;
	
	//現在存在するPropを全て削除する
	Func_PropSearch(0);
	
	GetCurrentMap(strMap,sizeof(strMap));
	Format(strMap,sizeof(strMap),"cfg/%s_prop.csv",strMap);
	
	new Handle:hFile = OpenFile(strMap,"r");
	if(hFile == INVALID_HANDLE){
		PrintToServer("[PropSpawn]ファイルが読み取り専用もしくは開いたままになっていませんか? それかファイルがありません。");
		return;
	}
	
	while(!IsEndOfFile(hFile)){
	
		//1行読み込む
		ReadFileLine(hFile,strMap,sizeof(strMap));
		
		//空行でなければ実行する
		if(StrEqual(strMap,"") == false){
		
			//csvをフィールドごとに配列に入れる
			if(ExplodeString(strMap, ",", strArg, 12, PLATFORM_MAX_PATH) == 12){
				
				ent = Func_PropSpawn(strArg[0],strArg[1]);
				if(ent == -1){
					PrintToServer("[PropSpawn]Propのロードに失敗しました。");
					PrintToServer("%s",strMap);
				}
				else{
					//Entityの情報を設定する
					vec[0] = StringToFloat(strArg[2]);
					vec[1] = StringToFloat(strArg[3]);
					vec[2] = StringToFloat(strArg[4]);
					ang[0] = StringToFloat(strArg[5]);
					ang[1] = StringToFloat(strArg[6]);
					ang[2] = StringToFloat(strArg[7]);
					
					SetEntProp(ent, Prop_Data, "m_nSkin", StringToInt(strArg[8]));
					SetEntProp(ent, Prop_Data, "m_iHealth", StringToInt(strArg[9]));
					SetEntProp(ent, Prop_Data, "m_nSolidType", StringToInt(strArg[10]));
					SetEntProp(ent, Prop_Data, "m_takedamage", StringToInt(strArg[11]));
					
					TeleportEntity(ent, vec, ang, NULL_VECTOR);
				}
			}
			else{
				PrintToServer("[PropSpawn]フィールド数がおかしいです。");
				PrintToServer("%s",strMap);
			}
		}
	}
}


Propの操作はbindする必要あり 例

//prop_spawn用
bind "UPARROW" "sm_target +y"
bind "LEFTARROW" "sm_target -x"
bind "DOWNARROW" "sm_target -y"
bind "RIGHTARROW" "sm_target +x"
bind "PGUP" "sm_target u"
bind "PGDN" "sm_target d"
bind "DEL" "sm_target l"
bind "END" "sm_target r"
bind "INS" "sm_target +p"
bind "HOME" "sm_target -p"
bind "KP_SLASH" "sm_target +r"
bind "KP_MULTIPLY" "sm_target -r"

Propを攻撃すると操作対象をそのPropに移す
後はコマンドのコメント見て

あーあと!moveは上手く動かないからコメントアウトした