mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-05 20:23:20 +00:00
Rewrite/cleanup code, split selection from spawning
This commit is contained in:
parent
d278f582cc
commit
c51aa6533c
6 changed files with 1053 additions and 840 deletions
|
@ -85,8 +85,8 @@ enum struct PortalData {
|
|||
float portalOffsets[3];
|
||||
}
|
||||
static AnyMap portals;
|
||||
|
||||
stock int CreatePortal(PortalType type, const char model[64], const float pos[3], const float offset[3] = { 40.0, 40.0, 0.0 }, const float scale[3] = { 5.0, 5.0, 5.0 }) {
|
||||
#pragma unused model
|
||||
int entity = CreateEntityByName("trigger_multiple");
|
||||
if(entity == -1) return -1;
|
||||
DispatchKeyValue(entity, "spawnflags", "513");
|
||||
|
|
348
scripting/include/randomizer/defs.sp
Normal file
348
scripting/include/randomizer/defs.sp
Normal file
|
@ -0,0 +1,348 @@
|
|||
#define MAX_SCENE_NAME_LENGTH 32
|
||||
#define MAX_INPUTS_CLASSNAME_LENGTH 64
|
||||
|
||||
int DEFAULT_COLOR[4] = { 255, 255, 255, 255 };
|
||||
|
||||
MapData g_MapData; // The global map data
|
||||
SceneSelection g_selection; // The selected scenes from the global map data
|
||||
BuilderData g_builder; // The global instance of the builder
|
||||
StringMap g_mapTraverseSelections; // For maps that traverse backwards, holds record of the selected scenes so they can be re-applied
|
||||
|
||||
int g_ropeIndex; // Unique id for ropes on spawn, is reset to 0 for every new spawn attempt
|
||||
ArrayList g_gascanRespawnQueue; // Queue that gascan respawns pop from to respawn to
|
||||
AnyMap g_gascanSpawners; // Mapping of <entity index, GascanSpawnerData>, for when a can is destroyed it can be respawned in position
|
||||
|
||||
int g_iLaserIndex;
|
||||
|
||||
public void InitGlobals() {
|
||||
g_gascanSpawners = new AnyMap();
|
||||
g_mapTraverseSelections = new StringMap();
|
||||
}
|
||||
|
||||
enum struct SelectedSceneData {
|
||||
char name[MAX_SCENE_NAME_LENGTH];
|
||||
ArrayList selectedVariantIndexes;
|
||||
}
|
||||
|
||||
enum struct GascanSpawnerData {
|
||||
float origin[3];
|
||||
float angles[3];
|
||||
}
|
||||
|
||||
enum struct MapData {
|
||||
StringMap scenesKv;
|
||||
ArrayList scenes;
|
||||
ArrayList lumpEdits;
|
||||
ArrayList activeScenes;
|
||||
ArrayList gascanSpawners;
|
||||
StringMap groups;
|
||||
|
||||
void Cleanup() {
|
||||
SceneData scene;
|
||||
for(int i = 0; i < this.scenes.Length; i++) {
|
||||
this.scenes.GetArray(i, scene);
|
||||
scene.Cleanup();
|
||||
}
|
||||
delete this.scenes;
|
||||
delete this.scenesKv;
|
||||
delete this.lumpEdits;
|
||||
delete this.activeScenes;
|
||||
delete this.gascanSpawners;
|
||||
delete this.groups;
|
||||
}
|
||||
|
||||
SceneSelection GenerateSelection(int flags) {
|
||||
return selectScenes(this, flags);
|
||||
}
|
||||
|
||||
bool ApplySelection(SceneSelection selection, int flags) {
|
||||
Profiler profiler = new Profiler();
|
||||
|
||||
profiler.Start();
|
||||
selection.Activate(this, flags);
|
||||
spawnGascans(this);
|
||||
profiler.Stop();
|
||||
|
||||
// _ropeIndex = 0;
|
||||
|
||||
Log("Done applying selection in %.4f seconds", profiler.Time);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsLoaded() {
|
||||
return this.scenes != null;
|
||||
}
|
||||
}
|
||||
|
||||
enum loadFlags {
|
||||
FLAG_NONE = 0,
|
||||
FLAG_ALL_SCENES = 1, // Pick all scenes, no random chance
|
||||
FLAG_ALL_VARIANTS = 2, // Pick all variants (for debug purposes),
|
||||
FLAG_REFRESH = 4, // Load data bypassing cache
|
||||
FLAG_FORCE_ACTIVE = 8, // Similar to ALL_SCENES, bypasses % chance
|
||||
FLAG_IGNORE_TRAVERSE_STORE = 16 // Do not load stored selection from the g_mapTraverseSelections
|
||||
}
|
||||
|
||||
enum struct BuilderData {
|
||||
JSONObject mapData;
|
||||
|
||||
JSONObject selectedSceneData;
|
||||
char selectedSceneId[64];
|
||||
|
||||
JSONObject selectedVariantData;
|
||||
int selectedVariantIndex;
|
||||
|
||||
bool IsLoaded() {
|
||||
return this.mapData != null;
|
||||
}
|
||||
|
||||
void Cleanup() {
|
||||
this.selectedSceneData = null;
|
||||
this.selectedVariantData = null;
|
||||
this.selectedVariantIndex = -1;
|
||||
this.selectedSceneId[0] = '\0';
|
||||
if(this.mapData != null)
|
||||
delete this.mapData;
|
||||
// JSONcleanup_and_delete(this.mapData);
|
||||
}
|
||||
|
||||
bool SelectScene(const char[] group) {
|
||||
if(!g_builder.mapData.HasKey(group)) return false;
|
||||
this.selectedSceneData = view_as<JSONObject>(g_builder.mapData.Get(group));
|
||||
strcopy(this.selectedSceneId, sizeof(this.selectedSceneId), group);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a variant, enter -1 to not select any (scene's entities)
|
||||
*/
|
||||
bool SelectVariant(int index = -1) {
|
||||
if(this.selectedSceneData == null) LogError("SelectVariant called, but no group selected");
|
||||
JSONArray variants = view_as<JSONArray>(this.selectedSceneData.Get("variants"));
|
||||
if(index >= variants.Length) return false;
|
||||
else if(index < -1) return false;
|
||||
else if(index > -1) {
|
||||
this.selectedVariantData = view_as<JSONObject>(variants.Get(index));
|
||||
} else {
|
||||
this.selectedVariantData = null;
|
||||
}
|
||||
this.selectedVariantIndex = index;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddEntity(int entity, ExportType exportType = Export_Model) {
|
||||
JSONObject entityData = ExportEntity(entity, exportType);
|
||||
this.AddEntityData(entityData);
|
||||
}
|
||||
|
||||
void AddEntityData(JSONObject entityData) {
|
||||
JSONArray entities;
|
||||
if(g_builder.selectedVariantData == null) {
|
||||
// Create <scene>.entities if doesn't exist:
|
||||
if(!g_builder.selectedSceneData.HasKey("entities")) {
|
||||
g_builder.selectedSceneData.Set("entities", new JSONArray());
|
||||
}
|
||||
entities = view_as<JSONArray>(g_builder.selectedSceneData.Get("entities"));
|
||||
} else {
|
||||
entities = view_as<JSONArray>(g_builder.selectedVariantData.Get("entities"));
|
||||
}
|
||||
entities.Push(entityData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
enum struct SceneData {
|
||||
char name[MAX_SCENE_NAME_LENGTH];
|
||||
float chance;
|
||||
char group[MAX_SCENE_NAME_LENGTH];
|
||||
ArrayList variants;
|
||||
|
||||
void Cleanup() {
|
||||
SceneVariantData choice;
|
||||
for(int i = 0; i < this.variants.Length; i++) {
|
||||
this.variants.GetArray(i, choice);
|
||||
choice.Cleanup();
|
||||
}
|
||||
delete this.variants;
|
||||
}
|
||||
}
|
||||
|
||||
enum struct SceneVariantData {
|
||||
int weight;
|
||||
ArrayList inputsList;
|
||||
ArrayList entities;
|
||||
ArrayList forcedScenes;
|
||||
|
||||
void Cleanup() {
|
||||
delete this.inputsList;
|
||||
delete this.entities;
|
||||
delete this.forcedScenes;
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
Input_Classname,
|
||||
Input_Targetname,
|
||||
Input_HammerId
|
||||
}
|
||||
enum struct VariantInputData {
|
||||
char name[MAX_INPUTS_CLASSNAME_LENGTH];
|
||||
InputType type;
|
||||
char input[64];
|
||||
|
||||
void Trigger() {
|
||||
int entity = -1;
|
||||
switch(this.type) {
|
||||
case Input_Classname: {
|
||||
while((entity = FindEntityByClassname(entity, this.name)) != INVALID_ENT_REFERENCE) {
|
||||
this._trigger(entity);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _trigger(int entity) {
|
||||
if(entity > 0 && IsValidEntity(entity)) {
|
||||
if(StrEqual(this.input, "_allow_ladder")) {
|
||||
if(HasEntProp(entity, Prop_Send, "m_iTeamNum")) {
|
||||
SetEntProp(entity, Prop_Send, "m_iTeamNum", 0);
|
||||
} else {
|
||||
Log("Warn: Entity (%d) with id \"%s\" has no teamnum for \"_allow_ladder\"", entity, this.name);
|
||||
}
|
||||
} else if(StrEqual(this.input, "_lock")) {
|
||||
AcceptEntityInput(entity, "Close");
|
||||
AcceptEntityInput(entity, "Lock");
|
||||
} else if(StrEqual(this.input, "_lock_nobreak")) {
|
||||
AcceptEntityInput(entity, "Close");
|
||||
AcceptEntityInput(entity, "Lock");
|
||||
AcceptEntityInput(entity, "SetUnbreakable");
|
||||
} else {
|
||||
char cmd[32];
|
||||
// Split input "a b" to a with variant "b"
|
||||
int len = SplitString(this.input, " ", cmd, sizeof(cmd));
|
||||
if(len > -1) {
|
||||
SetVariantString(this.input[len]);
|
||||
AcceptEntityInput(entity, cmd);
|
||||
Debug("_trigger(%d): %s (v=%s)", entity, cmd, this.input[len]);
|
||||
} else {
|
||||
Debug("_trigger(%d): %s", entity, this.input);
|
||||
AcceptEntityInput(entity, this.input);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum struct LumpEditData {
|
||||
char name[MAX_INPUTS_CLASSNAME_LENGTH];
|
||||
InputType type;
|
||||
char action[32];
|
||||
char value[64];
|
||||
|
||||
int _findLumpIndex(int startIndex = 0, EntityLumpEntry entry) {
|
||||
int length = EntityLump.Length();
|
||||
char val[64];
|
||||
Debug("Scanning for \"%s\" (type=%d)", this.name, this.type);
|
||||
for(int i = startIndex; i < length; i++) {
|
||||
entry = EntityLump.Get(i);
|
||||
int index = entry.FindKey("hammerid");
|
||||
if(index != -1) {
|
||||
entry.Get(index, "", 0, val, sizeof(val));
|
||||
if(StrEqual(val, this.name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
index = entry.FindKey("classname");
|
||||
if(index != -1) {
|
||||
entry.Get(index, "", 0, val, sizeof(val));
|
||||
Debug("%s vs %s", val, this.name);
|
||||
if(StrEqual(val, this.name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
index = entry.FindKey("targetname");
|
||||
if(index != -1) {
|
||||
entry.Get(index, "", 0, val, sizeof(val));
|
||||
if(StrEqual(val, this.name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
delete entry;
|
||||
}
|
||||
Log("Warn: Could not find any matching lump for \"%s\" (type=%d)", this.name, this.type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Trigger() {
|
||||
int index = 0;
|
||||
EntityLumpEntry entry;
|
||||
while((index = this._findLumpIndex(index, entry) != -1)) {
|
||||
// for(int i = 0; i < entry.Length; i++) {
|
||||
// entry.Get(i, a, sizeof(a), v, sizeof(v));
|
||||
// Debug("%s=%s", a, v);
|
||||
// }
|
||||
this._trigger(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void _updateKey(EntityLumpEntry entry, const char[] key, const char[] value) {
|
||||
int index = entry.FindKey(key);
|
||||
if(index != -1) {
|
||||
Debug("update key %s = %s", key, value);
|
||||
entry.Update(index, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
void _trigger(EntityLumpEntry entry) {
|
||||
if(StrEqual(this.action, "setclassname")) {
|
||||
this._updateKey(entry, "classname", this.value);
|
||||
}
|
||||
|
||||
delete entry;
|
||||
}
|
||||
}
|
261
scripting/include/randomizer/loader_functions.sp
Normal file
261
scripting/include/randomizer/loader_functions.sp
Normal file
|
@ -0,0 +1,261 @@
|
|||
public bool LoadGlobalMapData(const char[] map, int flags) {
|
||||
Cleanup();
|
||||
return ParseMapData(g_MapData, map, flags);
|
||||
}
|
||||
|
||||
public JSONObject LoadMapJson(const char[] map) {
|
||||
Debug("Loading config for %s", map);
|
||||
char filePath[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, filePath, sizeof(filePath), "data/randomizer/%s.json", map);
|
||||
if(!FileExists(filePath)) {
|
||||
Log("No map config file (data/randomizer/%s.json), not loading", map);
|
||||
return null;
|
||||
}
|
||||
|
||||
JSONObject data = JSONObject.FromFile(filePath);
|
||||
if(data == null) {
|
||||
LogError("Could not parse map config file (data/randomizer/%s.json)", map);
|
||||
return null;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
public void SaveMapJson(const char[] map, JSONObject json) {
|
||||
Debug("Saving config for %s", map);
|
||||
char filePath[PLATFORM_MAX_PATH], filePathTemp[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, filePathTemp, sizeof(filePath), "data/randomizer/%s.json.tmp", map);
|
||||
BuildPath(Path_SM, filePath, sizeof(filePath), "data/randomizer/%s.json", map);
|
||||
|
||||
json.ToFile(filePathTemp, JSON_INDENT(4));
|
||||
RenameFile(filePath, filePathTemp);
|
||||
SetFilePermissions(filePath, FPERM_U_WRITE | FPERM_U_READ | FPERM_G_WRITE | FPERM_G_READ | FPERM_O_READ);
|
||||
}
|
||||
|
||||
/// Parses map data into first parameter, bool for success
|
||||
public bool ParseMapData(MapData data, const char[] map, int flags) {
|
||||
JSONObject json = LoadMapJson(map);
|
||||
if(json == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug("Starting parsing json data");
|
||||
|
||||
data.scenes = new ArrayList(sizeof(SceneData));
|
||||
data.scenesKv = new StringMap();
|
||||
data.lumpEdits = new ArrayList(sizeof(LumpEditData));
|
||||
|
||||
Profiler profiler = new Profiler();
|
||||
profiler.Start();
|
||||
|
||||
JSONObjectKeys iterator = json.Keys();
|
||||
char key[32];
|
||||
while(iterator.ReadKey(key, sizeof(key))) {
|
||||
if(key[0] == '_') {
|
||||
if(StrEqual(key, "_lumps")) {
|
||||
JSONArray lumpsList = view_as<JSONArray>(json.Get(key));
|
||||
if(lumpsList != null) {
|
||||
for(int l = 0; l < lumpsList.Length; l++) {
|
||||
loadLumpData(data.lumpEdits, view_as<JSONObject>(lumpsList.Get(l)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Debug("Unknown special entry \"%s\", skipping", key);
|
||||
}
|
||||
} else {
|
||||
// if(data.GetType(key) != JSONType_Object) {
|
||||
// Debug("Invalid normal entry \"%s\" (not an object), skipping", key);
|
||||
// continue;
|
||||
// }
|
||||
JSONObject scene = view_as<JSONObject>(json.Get(key));
|
||||
// Parses scene data and inserts to scenes
|
||||
loadScene(data, key, scene);
|
||||
}
|
||||
}
|
||||
delete json;
|
||||
|
||||
data.groups = new StringMap();
|
||||
getSceneGroups(data, data.groups);
|
||||
|
||||
profiler.Stop();
|
||||
Log("Parsed map %s(%d) in %.4f seconds (%d scenes)", map, flags, profiler.Time, data.scenes.Length);
|
||||
delete profiler;
|
||||
return true;
|
||||
}
|
||||
|
||||
void getSceneGroups(MapData data, StringMap groups) {
|
||||
ArrayList groupList;
|
||||
SceneData scene;
|
||||
for(int i = 0; i < data.scenes.Length; i++) {
|
||||
data.scenes.GetArray(i, scene);
|
||||
if(scene.group[0] != '\0') {
|
||||
// Load it into group list
|
||||
if(!groups.GetValue(scene.group, groupList)) {
|
||||
groupList = new ArrayList();
|
||||
}
|
||||
groupList.Push(i);
|
||||
groups.SetValue(scene.group, groupList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loadScene(MapData data, const char key[MAX_SCENE_NAME_LENGTH], JSONObject sceneData) {
|
||||
SceneData scene;
|
||||
scene.name = key;
|
||||
scene.chance = sceneData.GetFloat("chance");
|
||||
if(scene.chance < 0.0 || scene.chance > 1.0) {
|
||||
LogError("Scene \"%s\" has invalid chance (%f)", scene.name, scene.chance);
|
||||
return;
|
||||
} else if(!sceneData.HasKey("variants")) {
|
||||
ThrowError("Failed to load: Scene \"%s\" has missing \"variants\" array", scene.name);
|
||||
return;
|
||||
}
|
||||
// TODO: load "entities", merge with choice.entities
|
||||
sceneData.GetString("group", scene.group, sizeof(scene.group));
|
||||
scene.variants = new ArrayList(sizeof(SceneVariantData));
|
||||
|
||||
JSONArray entities;
|
||||
if(sceneData.HasKey("entities")) {
|
||||
entities = view_as<JSONArray>(sceneData.Get("entities"));
|
||||
}
|
||||
|
||||
// Load all variants
|
||||
JSONArray variants = view_as<JSONArray>(sceneData.Get("variants"));
|
||||
for(int i = 0; i < variants.Length; i++) {
|
||||
// Parses choice and loads to scene.choices
|
||||
loadChoice(scene, view_as<JSONObject>(variants.Get(i)), entities);
|
||||
}
|
||||
|
||||
data.scenes.PushArray(scene);
|
||||
data.scenesKv.SetArray(scene.name, scene, sizeof(scene));
|
||||
}
|
||||
|
||||
void loadChoice(SceneData scene, JSONObject choiceData, JSONArray extraEntities) {
|
||||
SceneVariantData choice;
|
||||
choice.weight = choiceData.HasKey("weight") ? choiceData.GetInt("weight") : 1;
|
||||
choice.entities = new ArrayList(sizeof(VariantEntityData));
|
||||
choice.inputsList = new ArrayList(sizeof(VariantInputData));
|
||||
choice.forcedScenes = new ArrayList(ByteCountToCells(MAX_SCENE_NAME_LENGTH));
|
||||
// Load in any variant-based entities
|
||||
if(choiceData.HasKey("entities")) {
|
||||
JSONArray entities = view_as<JSONArray>(choiceData.Get("entities"));
|
||||
for(int i = 0; i < entities.Length; i++) {
|
||||
// Parses entities and loads to choice.entities
|
||||
loadChoiceEntity(choice.entities, view_as<JSONObject>(entities.Get(i)));
|
||||
}
|
||||
delete entities;
|
||||
}
|
||||
// Load in any entities that the scene has
|
||||
if(extraEntities != null) {
|
||||
for(int i = 0; i < extraEntities.Length; i++) {
|
||||
// Parses entities and loads to choice.entities
|
||||
loadChoiceEntity(choice.entities, view_as<JSONObject>(extraEntities.Get(i)));
|
||||
}
|
||||
// delete extraEntities;
|
||||
}
|
||||
// Load all inputs
|
||||
if(choiceData.HasKey("inputs")) {
|
||||
JSONArray inputsList = view_as<JSONArray>(choiceData.Get("inputs"));
|
||||
for(int i = 0; i < inputsList.Length; i++) {
|
||||
loadChoiceInput(choice.inputsList, view_as<JSONObject>(inputsList.Get(i)));
|
||||
}
|
||||
delete inputsList;
|
||||
}
|
||||
if(choiceData.HasKey("force_scenes")) {
|
||||
JSONArray scenes = view_as<JSONArray>(choiceData.Get("force_scenes"));
|
||||
char sceneId[32];
|
||||
for(int i = 0; i < scenes.Length; i++) {
|
||||
scenes.GetString(i, sceneId, sizeof(sceneId));
|
||||
choice.forcedScenes.PushString(sceneId);
|
||||
Debug("scene %s: require %s", scene.name, sceneId);
|
||||
}
|
||||
delete scenes;
|
||||
}
|
||||
scene.variants.PushArray(choice);
|
||||
}
|
||||
|
||||
void loadChoiceInput(ArrayList list, JSONObject inputData) {
|
||||
VariantInputData input;
|
||||
input.type = Input_Classname;
|
||||
// Check classname -> targetname -> hammerid
|
||||
if(!inputData.GetString("classname", input.name, sizeof(input.name))) {
|
||||
if(inputData.GetString("targetname", input.name, sizeof(input.name))) {
|
||||
input.type = Input_Targetname;
|
||||
} else {
|
||||
if(inputData.GetString("hammerid", input.name, sizeof(input.name))) {
|
||||
input.type = Input_HammerId;
|
||||
} else {
|
||||
int id = inputData.GetInt("hammerid");
|
||||
if(id > 0) {
|
||||
input.type = Input_HammerId;
|
||||
IntToString(id, input.name, sizeof(input.name));
|
||||
} else {
|
||||
LogError("Missing valid input specification (hammerid, classname, targetname)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
inputData.GetString("input", input.input, sizeof(input.input));
|
||||
list.PushArray(input);
|
||||
}
|
||||
|
||||
void loadLumpData(ArrayList list, JSONObject inputData) {
|
||||
LumpEditData input;
|
||||
// Check classname -> targetname -> hammerid
|
||||
if(!inputData.GetString("classname", input.name, sizeof(input.name))) {
|
||||
if(inputData.GetString("targetname", input.name, sizeof(input.name))) {
|
||||
input.type = Input_Targetname;
|
||||
} else {
|
||||
if(inputData.GetString("hammerid", input.name, sizeof(input.name))) {
|
||||
input.type = Input_HammerId;
|
||||
} else {
|
||||
int id = inputData.GetInt("hammerid");
|
||||
if(id > 0) {
|
||||
input.type = Input_HammerId;
|
||||
IntToString(id, input.name, sizeof(input.name));
|
||||
} else {
|
||||
LogError("Missing valid input specification (hammerid, classname, targetname)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
inputData.GetString("action", input.action, sizeof(input.action));
|
||||
inputData.GetString("value", input.value, sizeof(input.value));
|
||||
list.PushArray(input);
|
||||
}
|
||||
|
||||
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<JSONArray>(entityData.Get("keyframes"));
|
||||
float vec[3];
|
||||
for(int i = 0 ; i < keyframesData.Length; i++) {
|
||||
JSONArray vecArray = view_as<JSONArray>(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);
|
||||
GetColor(entityData, "color", entity.color, DEFAULT_COLOR);
|
||||
list.PushArray(entity);
|
||||
}
|
285
scripting/include/randomizer/select_functions.sp
Normal file
285
scripting/include/randomizer/select_functions.sp
Normal file
|
@ -0,0 +1,285 @@
|
|||
public bool LoadRunGlobalMap(const char[] map, int flags) {
|
||||
// Unless FLAG_IGNORE_TRAVERSE_STORE, if the map is the _b variant, then load the stored _a value
|
||||
SceneSelection selection;
|
||||
// Only load map data if we don't already have it
|
||||
if(g_MapData.scenes == null || g_selection == null || flags & view_as<int>(FLAG_REFRESH)) {
|
||||
if(~flags & view_as<int>(FLAG_IGNORE_TRAVERSE_STORE) && String_EndsWith(map, "_b")) {
|
||||
// Switch _b to _a
|
||||
char buffer[64];
|
||||
int len = strcopy(buffer, sizeof(buffer), map);
|
||||
buffer[len-1] = 'a';
|
||||
|
||||
// Load the A variant
|
||||
if(!LoadGlobalMapData(buffer, flags)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load selection from the traverse store, if it exists
|
||||
ArrayList list;
|
||||
if(g_mapTraverseSelections.GetValue(buffer, list)) {
|
||||
Log("Loaded previously traversed map selection (c:%s p:%s)", map, buffer);
|
||||
selection = view_as<SceneSelection>(list);
|
||||
} else {
|
||||
Log("Tried to load previously traversed map selection, but nothing stored (c:%s p:%s)", map, buffer);
|
||||
}
|
||||
} else if(selection == null) {
|
||||
// This is called if not traverse map or previous data not found
|
||||
if(!LoadGlobalMapData(map, flags)) {
|
||||
return false;
|
||||
}
|
||||
selection = selectScenes(g_MapData, flags);
|
||||
}
|
||||
}
|
||||
if(selection == null) {
|
||||
LogError("LoadRunGlobalMap: No selection was loaded");
|
||||
}
|
||||
|
||||
g_selection = selection;
|
||||
|
||||
return g_MapData.ApplySelection(selection, flags);
|
||||
}
|
||||
|
||||
void trySelectScene(SceneSelection selection, SceneData scene, int flags) {
|
||||
// Use the .chance field unless FLAG_ALL_SCENES or FLAG_FORCE_ACTIVE is set
|
||||
if(~flags & view_as<int>(FLAG_ALL_SCENES) && ~flags & view_as<int>(FLAG_FORCE_ACTIVE) && GetURandomFloat() > scene.chance) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(scene.variants.Length == 0) {
|
||||
LogError("Warn: No variants were found for scene \"%s\"", scene.name);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: select variant...
|
||||
SelectedSceneData aScene;
|
||||
aScene.name = scene.name;
|
||||
aScene.selectedVariantIndexes = new ArrayList();
|
||||
|
||||
ArrayList choices = new ArrayList();
|
||||
SceneVariantData choice;
|
||||
int chosenIndex;
|
||||
Debug("Scene %s has %d variants", scene.name, scene.variants.Length);
|
||||
// Weighted random: Push N times dependent on weight
|
||||
for(int i = 0; i < scene.variants.Length; i++) {
|
||||
scene.variants.GetArray(i, choice);
|
||||
if(flags & view_as<int>(FLAG_ALL_VARIANTS)) {
|
||||
aScene.selectedVariantIndexes.Push(i);
|
||||
} else {
|
||||
if(choice.weight <= 0) {
|
||||
PrintToServer("Warn: Variant %d in scene %s has invalid weight", i, scene.name);
|
||||
continue;
|
||||
}
|
||||
for(int c = 0; c < choice.weight; c++) {
|
||||
choices.Push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(flags & view_as<int>(FLAG_ALL_VARIANTS)) {
|
||||
|
||||
} else if(choices.Length > 0) {
|
||||
// Pick a random variant from list
|
||||
chosenIndex = GetURandomInt() % choices.Length;
|
||||
chosenIndex = choices.Get(chosenIndex);
|
||||
Log("Chosen scene \"%s\" with variant #%d", scene.name, chosenIndex);
|
||||
|
||||
aScene.selectedVariantIndexes.Push(chosenIndex);
|
||||
}
|
||||
delete choices;
|
||||
|
||||
selection.AddScene(aScene);
|
||||
}
|
||||
|
||||
void selectGroups(SceneSelection selection, MapData data, int flags) {
|
||||
StringMapSnapshot snapshot = data.groups.Snapshot();
|
||||
char key[MAX_SCENE_NAME_LENGTH];
|
||||
ArrayList groupList;
|
||||
SceneData scene;
|
||||
for(int i = 0; i < snapshot.Length; i++) {
|
||||
snapshot.GetKey(i, key, sizeof(key));
|
||||
data.groups.GetValue(key, groupList);
|
||||
|
||||
// Select a random scene from the group:
|
||||
int index = GetURandomInt() % groupList.Length;
|
||||
index = groupList.Get(index);
|
||||
data.scenes.GetArray(index, scene);
|
||||
|
||||
Debug("Selected scene \"%s\" for group %s (%d members)", scene.name, key, groupList.Length);
|
||||
trySelectScene(selection, scene, flags);
|
||||
delete groupList;
|
||||
}
|
||||
delete snapshot;
|
||||
}
|
||||
|
||||
void selectForcedScenes(SceneSelection selection, MapData data, int flags) {
|
||||
// Traverse active scenes, loading any other scene it requires (via .force_scenes)
|
||||
SelectedSceneData aScene;
|
||||
SceneVariantData choice;
|
||||
SceneData scene;
|
||||
// list of scenes that will need to be forced if not already:
|
||||
ArrayList forcedScenes = new ArrayList(ByteCountToCells(MAX_SCENE_NAME_LENGTH));
|
||||
char key[MAX_SCENE_NAME_LENGTH];
|
||||
for(int i = 0; i < selection.Count; i++) {
|
||||
selection.Get(i, aScene);
|
||||
// Load scene from active scene entry
|
||||
if(!data.scenesKv.GetArray(aScene.name, scene, sizeof(scene))) {
|
||||
// this shouldn't happen
|
||||
Log("WARN: scene \"%s\" not found in scene selection", aScene.name);
|
||||
// can't find scene, ignore
|
||||
continue;
|
||||
}
|
||||
for(int v = 0; v < aScene.selectedVariantIndexes.Length; v++) {
|
||||
aScene.selectedVariantIndexes.GetArray(v, choice);
|
||||
// If the choice has forced scenes
|
||||
if(choice.forcedScenes != null) {
|
||||
// Add each scene to the list to be added
|
||||
for(int j = 0; j < choice.forcedScenes.Length; j++) {
|
||||
choice.forcedScenes.GetString(j, key, sizeof(key));
|
||||
forcedScenes.PushString(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(forcedScenes.Length > 0) {
|
||||
Debug("Loading %d forced scenes", forcedScenes.Length);
|
||||
}
|
||||
// Iterate and activate any forced scenes
|
||||
for(int i = 0; i < forcedScenes.Length; i++) {
|
||||
forcedScenes.GetString(i, key, sizeof(key));
|
||||
// Check if scene was already loaded
|
||||
bool isSceneAlreadyLoaded = false;
|
||||
for(int j = 0; j < data.activeScenes.Length; j++) {
|
||||
data.activeScenes.GetArray(j, aScene);
|
||||
if(StrEqual(aScene.name, key)) {
|
||||
isSceneAlreadyLoaded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isSceneAlreadyLoaded) continue;
|
||||
data.scenesKv.GetArray(key, scene, sizeof(scene));
|
||||
trySelectScene(selection, scene, flags | view_as<int>(FLAG_FORCE_ACTIVE));
|
||||
}
|
||||
delete forcedScenes;
|
||||
}
|
||||
|
||||
// TODO: the scenes that are selected need variant index set
|
||||
methodmap SceneSelection < ArrayList {
|
||||
public SceneSelection() {
|
||||
ArrayList selectedScenes = new ArrayList(sizeof(SelectedSceneData));
|
||||
return view_as<SceneSelection>(selectedScenes);
|
||||
}
|
||||
|
||||
property int Count {
|
||||
public get() {
|
||||
return (view_as<ArrayList>(this)).Length;
|
||||
}
|
||||
}
|
||||
|
||||
public void Cleanup() {
|
||||
delete this;
|
||||
}
|
||||
|
||||
public void Activate(MapData data, int flags = 0) {
|
||||
g_ropeIndex = 0;
|
||||
SelectedSceneData aScene;
|
||||
SceneData scene;
|
||||
SceneVariantData choice;
|
||||
ArrayList list = view_as<ArrayList>(this);
|
||||
for(int i = 0; i < list.Length; i++) {
|
||||
list.GetArray(i, aScene);
|
||||
Log("Activating scene \"%s\" with %d variants", aScene.name, aScene.selectedVariantIndexes.Length);
|
||||
|
||||
// Fetch the scene that aScene marks
|
||||
if(!data.scenesKv.GetArray(aScene.name, scene, sizeof(scene))) {
|
||||
Log("WARN: Selected scene \"%s\" not found, skipping", aScene.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
for(int v = 0; v < aScene.selectedVariantIndexes.Length; v++) {
|
||||
int variantIndex = aScene.selectedVariantIndexes.Get(v);
|
||||
|
||||
|
||||
scene.variants.GetArray(variantIndex, choice);
|
||||
activateVariant(choice, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Get(int sceneIndex, SelectedSceneData scene) {
|
||||
(view_as<ArrayList>(this)).GetArray(sceneIndex, scene);
|
||||
}
|
||||
|
||||
public ArrayList AsList() {
|
||||
return view_as<ArrayList>(this);
|
||||
}
|
||||
|
||||
public void AddScene(SelectedSceneData aScene) {
|
||||
view_as<ArrayList>(this).PushArray(aScene);
|
||||
}
|
||||
}
|
||||
|
||||
// Selects what scenes and its variants to apply and returns list - does not activate
|
||||
SceneSelection selectScenes(MapData data, int flags = 0) {
|
||||
SceneData scene;
|
||||
SceneSelection selection = new SceneSelection();
|
||||
|
||||
Profiler profiler = new Profiler();
|
||||
profiler.Start();
|
||||
|
||||
for(int i = 0; i < data.scenes.Length; i++) {
|
||||
data.scenes.GetArray(i, scene);
|
||||
if(scene.group[0] == '\0') {
|
||||
trySelectScene(selection, scene, flags);
|
||||
}
|
||||
}
|
||||
selectGroups(selection, data, flags);
|
||||
selectForcedScenes(selection, data, flags);
|
||||
|
||||
profiler.Stop();
|
||||
Log("Done generating selection in %.4f seconds", profiler.Time);
|
||||
return selection;
|
||||
}
|
||||
|
||||
void spawnGascans(MapData data) {
|
||||
if(data.gascanSpawners != null && data.gascanSpawners.Length > 0) {
|
||||
// Iterate through every gascan until we run out - picking a random spawner each time
|
||||
int entity = -1;
|
||||
char targetname[9];
|
||||
GascanSpawnerData spawner;
|
||||
int spawnerCount = data.gascanSpawners.Length;
|
||||
int count;
|
||||
while((entity = FindEntityByClassname(entity, "weapon_gascan")) != INVALID_ENT_REFERENCE) {
|
||||
GetEntPropString(entity, Prop_Data, "m_iName", targetname, sizeof(targetname));
|
||||
int hammerid = GetEntProp(entity, Prop_Data, "m_iHammerID");
|
||||
int glowColor = GetEntProp(entity, Prop_Send, "m_glowColorOverride"); // check if white
|
||||
if(hammerid == 0 && glowColor == 16777215 && targetname[0] == '\0' && !g_gascanSpawners.ContainsKey(entity)) {
|
||||
// Found a valid gascan, apply a random spawner
|
||||
int spawnerIndex = GetRandomInt(0, data.gascanSpawners.Length - 1);
|
||||
data.gascanSpawners.GetArray(spawnerIndex, spawner);
|
||||
data.gascanSpawners.Erase(spawnerIndex); // only want one can to use this spawner
|
||||
|
||||
AssignGascan(entity, spawner);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
Debug("Assigned %d gascans to %d spawners", count, spawnerCount);
|
||||
}
|
||||
}
|
||||
|
||||
void activateVariant(SceneVariantData choice, int flags) {
|
||||
#pragma unused flags
|
||||
VariantEntityData entity;
|
||||
for(int i = 0; i < choice.entities.Length; i++) {
|
||||
choice.entities.GetArray(i, entity);
|
||||
spawnEntity(entity);
|
||||
}
|
||||
|
||||
if(choice.inputsList.Length > 0) {
|
||||
VariantInputData input;
|
||||
for(int i = 0; i < choice.inputsList.Length; i++) {
|
||||
choice.inputsList.GetArray(i, input);
|
||||
input.Trigger();
|
||||
}
|
||||
}
|
||||
}
|
40
scripting/include/randomizer/util.sp
Normal file
40
scripting/include/randomizer/util.sp
Normal file
|
@ -0,0 +1,40 @@
|
|||
public bool GetVector(JSONObject obj, const char[] key, float out[3]) {
|
||||
if(!obj.HasKey(key)) return false;
|
||||
JSONArray vecArray = view_as<JSONArray>(obj.Get(key));
|
||||
if(vecArray != null) {
|
||||
out[0] = vecArray.GetFloat(0);
|
||||
out[1] = vecArray.GetFloat(1);
|
||||
out[2] = vecArray.GetFloat(2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void GetColor(JSONObject obj, const char[] key, int out[4], int defaultColor[4]) {
|
||||
if(obj.HasKey(key)) {
|
||||
JSONArray vecArray = view_as<JSONArray>(obj.Get(key));
|
||||
out[0] = vecArray.GetInt(0);
|
||||
out[1] = vecArray.GetInt(1);
|
||||
out[2] = vecArray.GetInt(2);
|
||||
if(vecArray.Length == 4)
|
||||
out[3] = vecArray.GetInt(3);
|
||||
else
|
||||
out[3] = 255;
|
||||
} else {
|
||||
out = defaultColor;
|
||||
}
|
||||
}
|
||||
|
||||
stock JSONArray FromFloatArray(float[] vec, int count) {
|
||||
JSONArray arr = new JSONArray();
|
||||
for(int i = 0 ; i < count; i++) {
|
||||
arr.PushFloat(vec[i]);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
stock JSONArray FromIntArray(int[] vec, int count) {
|
||||
JSONArray arr = new JSONArray();
|
||||
for(int i = 0 ; i < count; i++) {
|
||||
arr.PushInt(vec[i]);
|
||||
}
|
||||
return arr;
|
||||
}
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue