diff --git a/plugins/adminpanel.smx b/plugins/adminpanel.smx index 5e2f74b..447ed39 100644 Binary files a/plugins/adminpanel.smx and b/plugins/adminpanel.smx differ diff --git a/plugins/l4d2_crescendo_control.smx b/plugins/l4d2_crescendo_control.smx index 68d7167..af7f331 100644 Binary files a/plugins/l4d2_crescendo_control.smx and b/plugins/l4d2_crescendo_control.smx differ diff --git a/plugins/l4d2_editor.smx b/plugins/l4d2_editor.smx index 4b488c5..29b6751 100644 Binary files a/plugins/l4d2_editor.smx and b/plugins/l4d2_editor.smx differ diff --git a/plugins/l4d2_extraplayeritems.smx b/plugins/l4d2_extraplayeritems.smx index 5166d97..9890602 100644 Binary files a/plugins/l4d2_extraplayeritems.smx and b/plugins/l4d2_extraplayeritems.smx differ diff --git a/plugins/l4d2_population_control.smx b/plugins/l4d2_population_control.smx index 6b1d7d0..f8eb081 100644 Binary files a/plugins/l4d2_population_control.smx and b/plugins/l4d2_population_control.smx differ diff --git a/plugins/l4d2_randomizer.smx b/plugins/l4d2_randomizer.smx index 7b2dc06..c21bbe7 100644 Binary files a/plugins/l4d2_randomizer.smx and b/plugins/l4d2_randomizer.smx differ diff --git a/scripting/adminpanel.sp b/scripting/adminpanel.sp index bf4abe1..8b9a10f 100644 --- a/scripting/adminpanel.sp +++ b/scripting/adminpanel.sp @@ -1102,7 +1102,7 @@ void AddPlayerRecord(int client, ClientState state) { StartRecord(Live_Player); sendBuffer.WriteInt(GetClientUserId(client)); sendBuffer.WriteString(steamidCache[client]); - sendBuffer.WriteByte(state); + sendBuffer.WriteByte(view_as(state)); sendBuffer.WriteInt(state == Client_Disconnected ? GetTime() : playerJoinTime[client]); sendBuffer.WriteString(nameCache[client]); EndRecord(); diff --git a/scripting/include/.gitignore b/scripting/include/.gitignore new file mode 100644 index 0000000..38a3bf8 --- /dev/null +++ b/scripting/include/.gitignore @@ -0,0 +1,23 @@ +*.* +!.gitignore +!epi/** +!feedthetrolls/** +!gamemodes/** +!guesswho/** +!hats/** +!hideandseek/** +!randomizer/** +!editor/** +!editor/** +!ftt.inc +!hats_editor.inc +!jutils.inc +!l4d_anti_rush.inc +!l4d2_perms.inc +!l4d2_detections.inc +!l4d_survivor_identity_fix.inc +!l4d2_usermsg.inc +!overlay.inc +!player_notes.inc +!tkstopper.inc +!anymap.inc \ No newline at end of file diff --git a/scripting/include/editor/props/menu_handlers.sp b/scripting/include/editor/props/menu_handlers.sp index d87a3de..c9bccb4 100644 --- a/scripting/include/editor/props/menu_handlers.sp +++ b/scripting/include/editor/props/menu_handlers.sp @@ -444,6 +444,7 @@ int SpawnCategoryHandler(Menu menu, MenuAction action, int client, int param2) { ShowSpawnRoot(client); } } else { + PrintToServer("SpawnCategoryHandler: calling ClearItemBuffer, Menu cancelled"); g_PropData[client].CleanupBuffers(); } } else if (action == MenuAction_End) @@ -470,7 +471,6 @@ int SpawnItemHandler(Menu menu, MenuAction action, int client, int param2) { // Use same item menu again: ShowItemMenu(client); } else if(action == MenuAction_Cancel) { - g_PropData[client].ClearItemBuffer(); if(param2 == MenuCancel_ExitBack) { CategoryData category; if(g_PropData[client].PopCategory(category)) { @@ -480,7 +480,8 @@ int SpawnItemHandler(Menu menu, MenuAction action, int client, int param2) { // If there is no categories, it means we are in a temp menu (search / recents / favorites) ShowSpawnRoot(client); } - } else { + } else if(param2 != MenuCancel_Interrupted) { + PrintToServer("SpawnItemHandler: calling ClearItemBuffer, Menu cancelled. param2=%d", param2); g_PropData[client].CleanupBuffers(); } } else if (action == MenuAction_End) { diff --git a/scripting/include/editor/props/methods.sp b/scripting/include/editor/props/methods.sp index 975c6ca..bcdd9a7 100644 --- a/scripting/include/editor/props/methods.sp +++ b/scripting/include/editor/props/methods.sp @@ -426,6 +426,9 @@ ArrayList SearchItems(const char[] query) { ArrayList items = new ArrayList(sizeof(ItemData)); ItemData item; SearchData data; + if(results.Length > MAX_SEARCH_RESULTS) { + results.Resize(MAX_SEARCH_RESULTS); + } for(int i = 0; i < results.Length; i++) { results.GetArray(i, data); item.FromSearchData(data); @@ -471,7 +474,6 @@ bool _searchItems(ArrayList results, ArrayList items, const char[] query) { search.FromItemData(item); search.index = searchIndex; results.PushArray(search); - if(results.Length > MAX_SEARCH_RESULTS) return false; } } return true; diff --git a/scripting/include/epi/director.sp b/scripting/include/epi/director.sp index b95fb9a..2ea94f1 100644 --- a/scripting/include/epi/director.sp +++ b/scripting/include/epi/director.sp @@ -5,8 +5,8 @@ #define DIRECTOR_WITCH_CHECK_TIME 30.0 // How often to check if a witch should be spawned #define DIRECTOR_WITCH_MAX_WITCHES 5 // The maximum amount of extra witches to spawn #define DIRECTOR_WITCH_ROLLS 3 // The number of dice rolls, increase if you want to increase freq -#define DIRECTOR_MIN_SPAWN_TIME 13.0 // Possibly randomized, per-special -#define DIRECTOR_SPAWN_CHANCE 0.038 // The raw chance of a spawn +#define DIRECTOR_MIN_SPAWN_TIME 13.0 // Possibly randomized, per-special, in seconds +ConVar directorSpawnChance; // Base chance of a special spawning, changed by player stress #define DIRECTOR_CHANGE_LIMIT_CHANCE 0.05 // The chance that the maximum amount per-special is changed #define DIRECTOR_SPECIAL_TANK_CHANCE 0.05 // The chance that specials can spawn when a tank is active #define DIRECTOR_STRESS_CUTOFF 0.75 // The minimum chance a random cut off stress value is chosen [this, 1.0] @@ -41,7 +41,8 @@ enum specialType { enum directorState { DState_Normal, DState_NoPlayersOrNotCoop, - DState_PendingMinFlowOrDisabled, + DState_PendingMinFlow, + DState_Disabled, DState_MaxSpecialTime, DState_PlayerChance, DState_Resting, @@ -49,10 +50,11 @@ enum directorState { DState_HighStress, DState_MaxDirectorSpecials, } -char DIRECTOR_STATE[9][] = { +char DIRECTOR_STATE[10][] = { "normal", "no players / not coop", - "pending minflow OR disabled", + "pending minflow", + "disabled", "max special in window", "player scaled chance", "rest period", @@ -388,7 +390,8 @@ directorState Director_Think() { // C. Special spawning is enabled gd_maxSpecials = L4D2_GetScriptValueInt("MaxSpecials", 0); if(gd_maxSpecials <= 0) return DState_MaxDirectorSpecials; - if( ~cvEPISpecialSpawning.IntValue & 1 || !L4D_HasAnySurvivorLeftSafeArea() || g_highestFlowAchieved < g_minFlowSpawn) return DState_PendingMinFlowOrDisabled; + if(~cvEPISpecialSpawning.IntValue & 1 ) return DState_Disabled; + if(!L4D_HasAnySurvivorLeftSafeArea() || g_highestFlowAchieved < g_minFlowSpawn) return DState_PendingMinFlow; // Check if a rest period is given if(Director_ShouldRest()) { @@ -423,7 +426,7 @@ directorState Director_Think() { return DState_HighStress; } // Scale the chance where stress = 0.0, the chance is 50% more, and stress = 1.0, the chance is 50% less - float spawnChance = DIRECTOR_SPAWN_CHANCE + ((0.5 - curAvgStress) / 10.0); + float spawnChance = directorSpawnChance.FloatValue + ((0.5 - curAvgStress) / 10.0); for(int i = 0; i < NUM_SPECIALS; i++) { specialType special = view_as(i); // Skip if we hit our limit, or too soon: diff --git a/scripting/include/gamemodes/ents.inc b/scripting/include/gamemodes/ents.inc index 7ecbaa7..8d1975b 100644 --- a/scripting/include/gamemodes/ents.inc +++ b/scripting/include/gamemodes/ents.inc @@ -173,6 +173,7 @@ stock int StartPropCreate(const char[] entClass, const char[] model, const float stock int CreateProp(const char[] entClass, const char[] model, const float pos[3], const float ang[3] = NULL_VECTOR, const float vel[3] = NULL_VECTOR) { int entity = StartPropCreate(entClass, model, pos, ang, vel); + if(entity == -1) return -1; if(DispatchSpawn(entity)) { #if defined DEBUG_LOG_MAPSTART PrintToServer("spawn prop %.1f %.1f %.1f model %s", pos[0], pos[1], pos[2], model[7]); @@ -281,17 +282,24 @@ stock int CreateParticle(const char[] sParticle, const float vPos[3], const floa return 0; } -stock void CreateDecal(const char[] texture, const float vPos[3]) { - int index = PrecacheDecal("decals/checkpointarrow01_black.vmt"); +stock void CreateDecal(const char[] texture, const float origin[3]) { + int index = PrecacheDecal(texture); + if(index <= 0) { + LogError("CreateDecal: bad decal \"%s\", precache failed.", texture); + return; + } + PrintToServer("CreateDecal: %s -> %d", texture, index); TE_Start("World Decal"); - TE_WriteVector("m_vecOrigin", vPos); + TE_WriteVector("m_vecOrigin", origin); + // TE_WriteNum("m_nEntity", -1); TE_WriteNum("m_nIndex", index); TE_SendToAll(); - TE_Start("BSP Decal"); - TE_WriteVector("m_vecOrigin", vPos); - TE_WriteNum("m_nIndex", index); - TE_SendToAll(); + // TE_Start("World Decal"); + // TE_WriteVector("m_vecOrigin", origin); + // TE_WriteNum("m_nIndex", index); + // TE_SendToAll(); + // int entity = CreateEntityByName("infodecal"); // if( entity != -1 ) { // DispatchKeyValue(entity, "texture", "decals/checkpointarrow01_black.vmt"); @@ -299,6 +307,7 @@ stock void CreateDecal(const char[] texture, const float vPos[3]) { // TeleportEntity(entity, vPos, NULL_VECTOR, NULL_VECTOR); // DispatchSpawn(entity); // ActivateEntity(entity); + // AcceptEntityInput(entity, "Activate"); // } } diff --git a/scripting/include/randomizer/caralarm.sp b/scripting/include/randomizer/caralarm.sp index c2a9deb..2daf78e 100644 --- a/scripting/include/randomizer/caralarm.sp +++ b/scripting/include/randomizer/caralarm.sp @@ -12,7 +12,7 @@ int SpawnCar(VariantEntityData entity) { } char glassModel[64]; - strcopy(glassModel, sizeof(glassModel), entity.type); + strcopy(glassModel, sizeof(glassModel), entity.model); ReplaceString(glassModel, sizeof(glassModel), ".mdl", "_glass.mdl"); if(StrEqual(entity.type, "_car_physics")) { vehicle = CreateProp("prop_physics", entity.model, entity.origin, entity.angles); @@ -20,9 +20,11 @@ int SpawnCar(VariantEntityData entity) { vehicle = CreateProp("prop_dynamic", entity.model, entity.origin, entity.angles); } if(PrecacheModel(glassModel)) { - int glass = CreateProp(glassModel, entity.model, entity.origin, entity.angles); - SetVariantString("!activator"); - AcceptEntityInput(glass, "SetParent", vehicle); + int glass = CreateProp("prop_dynamic", glassModel, entity.origin, entity.angles); + if(glass != -1) { + SetVariantString("!activator"); + AcceptEntityInput(glass, "SetParent", vehicle); + } } SetEntityRenderColor(vehicle, GetRandomInt(0, 255), GetRandomInt(0, 255), GetRandomInt(0, 255)); return vehicle; diff --git a/scripting/include/randomizer/rbuild.sp b/scripting/include/randomizer/rbuild.sp index 2a352ce..2970678 100644 --- a/scripting/include/randomizer/rbuild.sp +++ b/scripting/include/randomizer/rbuild.sp @@ -43,7 +43,7 @@ void OpenVariantsMenu(int client) { JSONArray entities; for(int i = 0; i < variants.Length; i++) { varObj = view_as(variants.Get(i)); - entities = view_as(varObj.Get("entities")); + entities = varObj.HasKey("entities") ? view_as(varObj.Get("entities")) : new JSONArray(); if(i == g_builder.selectedVariantIndex) { Format(display, sizeof(display), "#%d - %d entities (✔)", i, entities.Length); } else { diff --git a/scripting/l4d2_extraplayeritems.sp b/scripting/l4d2_extraplayeritems.sp index bd19141..9d94302 100644 --- a/scripting/l4d2_extraplayeritems.sp +++ b/scripting/l4d2_extraplayeritems.sp @@ -325,6 +325,7 @@ public void OnPluginStart() { cvEPICommonCountScale = CreateConVar("epi_commons_scale_multiplier", "0", "This value is multiplied by the number of extra players playing. It's then added to z_common_limit. 5 players with value 5 would be z_common_limit + ", FCVAR_NONE, true, 0.0); cvEPICommonCountScaleMax = CreateConVar("epi_commons_scale_max", "60", "The maximum amount that z_common_limit can be scaled to.", FCVAR_NONE, true, 0.0); cvZCommonLimit = FindConVar("z_common_limit"); + directorSpawnChance = CreateConVar("epi_director_special_chance", "0.038", "The base chance a special spawns, scaled by the survivor's average stress.", FCVAR_NONE, true, 0.0, true, 1.0); cvEPICommonCountScale.AddChangeHook(Cvar_CommonScaleChange); cvEPICommonCountScaleMax.AddChangeHook(Cvar_CommonScaleChange); diff --git a/scripting/l4d2_randomizer.sp b/scripting/l4d2_randomizer.sp index 1181a45..b7b6ac6 100644 --- a/scripting/l4d2_randomizer.sp +++ b/scripting/l4d2_randomizer.sp @@ -39,6 +39,7 @@ enum struct ActiveSceneData { MapData g_MapData; BuilderData g_builder; char currentMap[64]; +static int _ropeIndex; enum struct BuilderData { JSONObject mapData; @@ -84,6 +85,11 @@ enum struct BuilderData { } void AddEntity(int entity, ExportType exportType = Export_Model) { + JSONObject entityData = ExportEntity(entity, Export_Model); + this.AddEntityData(entityData); + } + + void AddEntityData(JSONObject entityData) { JSONArray entities; if(g_builder.selectedVariantData == null) { // Create .entities if doesn't exist: @@ -94,7 +100,6 @@ enum struct BuilderData { } else { entities = view_as(g_builder.selectedVariantData.Get("entities")); } - JSONObject entityData = ExportEntity(entity, Export_Model); entities.Push(entityData); } } @@ -117,7 +122,7 @@ public void OnPluginStart() { SetFailState("This plugin is for L4D/L4D2 only."); } - HookEvent("round_start_post_nav", Event_RoundStartPosNav); + HookEvent("round_start_post_nav", Event_RoundStartPostNav); RegAdminCmd("sm_rcycle", Command_CycleRandom, ADMFLAG_CHEATS); RegAdminCmd("sm_expent", Command_ExportEnt, ADMFLAG_GENERIC); @@ -130,7 +135,13 @@ public void OnPluginStart() { g_MapData.activeScenes = new ArrayList(sizeof(ActiveSceneData)); } +bool randomizerRan = false; + void Event_PlayerFirstSpawn(Event event, const char[] name ,bool dontBroadcast) { + if(!randomizerRan) { + CreateTimer(0.1, Timer_Run); + randomizerRan = true; + } int client = GetClientOfUserId(event.GetInt("userid")); if(GetUserFlagBits(client) & ADMFLAG_CHAT) { // If enabled but no map loaded: @@ -139,9 +150,9 @@ void Event_PlayerFirstSpawn(Event event, const char[] name ,bool dontBroadcast) } } -void Event_RoundStartPosNav(Event event, const char[] name ,bool dontBroadcast) { - if(cvarEnabled.BoolValue) - CreateTimer(5.0, Timer_Run); +void Event_RoundStartPostNav(Event event, const char[] name ,bool dontBroadcast) { + // if(cvarEnabled.BoolValue) + // CreateTimer(15.0, Timer_Run); } // TODO: on round start @@ -152,6 +163,7 @@ public void OnMapStart() { public void OnMapEnd() { + randomizerRan = false; g_builder.Cleanup(); Cleanup(); } @@ -344,6 +356,10 @@ Action Command_RandomizerBuild(int client, int args) { ReplyToCommand(client, "No entity found"); } } else if(StrEqual(arg, "entityid")) { + if(g_builder.selectedVariantData == null) { + ReplyToCommand(client, "Please load map data, select a scene and a variant."); + return Plugin_Handled; + } char arg1[32]; int entity = GetCmdArgInt(2); GetCmdArg(3, arg1, sizeof(arg)); @@ -359,6 +375,20 @@ Action Command_RandomizerBuild(int client, int args) { } else { ReplyToCommand(client, "No entity found"); } + } else if(StrEqual(arg, "decal")) { + if(g_builder.selectedVariantData == null) { + ReplyToCommand(client, "Please load map data, select a scene and a variant."); + return Plugin_Handled; + } + float pos[3]; + GetLookingPosition(client, Filter_IgnorePlayer, pos); + Effect_DrawBeamBoxRotatableToAll(pos, { -5.0, -5.0, -5.0}, { 5.0, 5.0, 5.0}, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 40.0, 0.1, 0.1, 0, 0.0, {73, 0, 130, 255}, 0); + JSONObject obj = new JSONObject(); + obj.SetString("type", "infodecal"); + obj.Set("origin", VecToArray(pos)); + obj.SetString("model", "decals/checkpointarrow01_black.vmt"); + g_builder.AddEntityData(obj); + ReplyToCommand(client, "Added sprite to variant #%d", g_builder.selectedVariantIndex); } else { ReplyToCommand(client, "Unknown arg. Try: new, load, save, scenes, cursor"); } @@ -604,10 +634,13 @@ enum struct SceneVariantData { enum struct VariantEntityData { char type[32]; char model[128]; + char targetname[128]; float origin[3]; float angles[3]; float scale[3]; int color[4]; + + ArrayList keyframes; } enum InputType { @@ -630,22 +663,32 @@ enum struct VariantInputData { } case Input_Targetname: { char targetname[64]; + int count = 0; while((entity = FindEntityByClassname(entity, "*")) != INVALID_ENT_REFERENCE) { GetEntPropString(entity, Prop_Data, "m_iName", targetname, sizeof(targetname)); if(StrEqual(targetname, this.name)) { this._trigger(entity); + count++; } } + if(count == 0) { + PrintToServer("[Randomizer::WARN] Input TargetName=\"%s\" matched 0 entties", this.name); + } } case Input_HammerId: { int targetId = StringToInt(this.name); + int count = 0; while((entity = FindEntityByClassname(entity, "*")) != INVALID_ENT_REFERENCE) { int hammerId = GetEntProp(entity, Prop_Data, "m_iHammerID"); if(hammerId == targetId ) { this._trigger(entity); + count++; break; } } + if(count == 0) { + PrintToServer("[Randomizer::WARN] Input HammerId=%d matched 0 entties", targetId); + } } } } @@ -859,6 +902,8 @@ public bool RunMap(const char[] map, int flags) { selectScenes(flags); profiler.Stop(); + _ropeIndex = 0; + Log("Done processing in %.4f seconds", g_MapData.scenes.Length, profiler.Time); return true; } @@ -993,12 +1038,32 @@ void loadLumpData(ArrayList list, JSONObject inputData) { void loadChoiceEntity(ArrayList list, JSONObject entityData) { VariantEntityData entity; entityData.GetString("model", entity.model, sizeof(entity.model)); + if(entityData.GetString("targetname", entity.targetname, sizeof(entity.targetname))) { + Format(entity.targetname, sizeof(entity.targetname), "randomizer_%s", entity.targetname); + } if(!entityData.GetString("type", entity.type, sizeof(entity.type))) { entity.type = "prop_dynamic"; } /*else if(entity.type[0] == '_') { LogError("Invalid custom entity type \"%s\"", entity.type); return; }*/ + + if(StrEqual(entity.type, "move_rope")) { + if(!entityData.HasKey("keyframes")) { + LogError("move_rope entity is missing keyframes: Vec[] property"); + return; + } + entity.keyframes = new ArrayList(3); + JSONArray keyframesData = view_as(entityData.Get("keyframes")); + float vec[3]; + for(int i = 0 ; i < keyframesData.Length; i++) { + JSONArray vecArray = view_as(keyframesData.Get(i)); + vec[0] = vecArray.GetFloat(0); + vec[1] = vecArray.GetFloat(1); + vec[2] = vecArray.GetFloat(2); + entity.keyframes.PushArray(vec); + } + } GetVector(entityData, "origin", entity.origin); GetVector(entityData, "angles", entity.angles); GetVector(entityData, "scale", entity.scale); @@ -1206,13 +1271,16 @@ void spawnEntity(VariantEntityData entity) { } else if(StrEqual(entity.type, "env_physics_blocker") || StrEqual(entity.type, "env_player_blocker")) { CreateEnvBlockerScaled(entity.type, entity.origin, entity.scale); } else if(StrEqual(entity.type, "infodecal")) { + Effect_DrawBeamBoxRotatableToAll(entity.origin, { -1.0, -5.0, -5.0}, { 1.0, 5.0, 5.0}, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 40.0, 0.1, 0.1, 0, 0.0, {73, 0, 130, 255}, 0); CreateDecal(entity.model, entity.origin); } else if(StrContains(entity.type, "prop_") == 0 || StrEqual(entity.type, "prop_fuel_barrel")) { if(entity.model[0] == '\0') { LogError("Missing model for entity with type \"%s\"", entity.type); return; + } else if(!PrecacheModel(entity.model)) { + LogError("Precache of entity model \"%s\" with type \"%s\" failed", entity.model, entity.type); + return; } - PrecacheModel(entity.model); int prop = CreateProp(entity.type, entity.model, entity.origin, entity.angles); SetEntityRenderColor(prop, entity.color[0], entity.color[1], entity.color[2], entity.color[3]); } else if(StrEqual(entity.type, "hammerid")) { @@ -1256,11 +1324,67 @@ void spawnEntity(VariantEntityData entity) { Debug("Warn: Could not find entity (classname=%s)", entity.model); } else if(StrContains(entity.type, "_car") != -1) { SpawnCar(entity); + } else if(StrEqual(entity.type, "move_rope")) { + if(!PrecacheModel(entity.model)) { + LogError("Precache of entity model \"%s\" with type \"%s\" failed", entity.model, entity.type); + return; + } else if(entity.keyframes == null) { + // should not happen + LogError("rope entity has no keyframes", entity.keyframes); + return; + } + CreateRope(entity); } else { LogError("Unknown entity type \"%s\"", entity.type); } } +int CreateRope(VariantEntityData data) { + char targetName[32], nextKey[32]; + Format(targetName, sizeof(targetName), "randomizer_rope%d", _ropeIndex); + Format(nextKey, sizeof(nextKey), "randomizer_rope%d_0", _ropeIndex); + int entity = _CreateRope("move_rope", targetName, nextKey, data.model, data.origin); + float pos[3]; + for(int i = 0; i < data.keyframes.Length; i++) { + nextKey[0] = '\0'; + Format(targetName, sizeof(targetName), "randomizer_rope%d_%d", _ropeIndex, i); + if(i < data.keyframes.Length - 1) { + Format(nextKey, sizeof(nextKey), "randomizer_rope%d_%d", _ropeIndex, i + 1); + } + data.keyframes.GetArray(i, pos, sizeof(pos)); + _CreateRope("move_rope", targetName, nextKey, data.model, pos); + } + Debug("created rope #%d with %d keyframes. entid:%d", _ropeIndex, data.keyframes.Length, entity); + _ropeIndex++; + return entity; +} +int _CreateRope(const char[] type, const char[] targetname, const char[] nextKey, const char[] texture, const float origin[3]) { + int entity = CreateEntityByName(type); + if(entity == -1) return -1; + Debug("_createRope(\"%s\", \"%s\", \"%s\", \"%s\", %.0f %.0f %.0f", type, targetname, nextKey, texture, origin[0], origin[1], origin[2]); + DispatchKeyValue(entity, "targetname", targetname); + DispatchKeyValue(entity, "NextKey", nextKey); + DispatchKeyValue(entity, "RopeMaterial", texture); + DispatchKeyValueInt(entity, "Type", 0); + DispatchKeyValueFloat(entity, "Width", 2.0); + DispatchKeyValueInt(entity, "Breakable", 0); + DispatchKeyValueInt(entity, "Slack", 0); + DispatchKeyValueInt(entity, "Type", 0); + DispatchKeyValueInt(entity, "TextureScale", 2); + DispatchKeyValueInt(entity, "Subdiv", 2); + DispatchKeyValueInt(entity, "MoveSpeed", 0); + DispatchKeyValueInt(entity, "Dangling", 0); + DispatchKeyValueInt(entity, "Collide", 0); + DispatchKeyValueInt(entity, "Barbed", 0); + DispatchKeyValue(entity, "PositionInterpolator", "2"); + // DispatchKeyValueFloat( entity, "m_RopeLength", 10.0 ); + TeleportEntity(entity, origin, NULL_VECTOR, NULL_VECTOR); + if(!DispatchSpawn(entity)) { + return -1; + } + return entity; +} + void Debug(const char[] format, any ...) { #if defined DEBUG_SCENE_PARSE char buffer[192]; @@ -1296,8 +1420,9 @@ void Cleanup() { int entity = -1; char targetname[128]; while((entity = FindEntityByClassname(entity, "*")) != INVALID_ENT_REFERENCE) { + if(!IsValidEntity(entity)) return; GetEntPropString(entity, Prop_Data, "m_iName", targetname, sizeof(targetname)); - if(StrContains(targetname, "randomizer_car") != -1) { + if(StrContains(targetname, "randomizer_") != -1) { RemoveEntity(entity); } }