Rewrite/cleanup code, split selection from spawning

This commit is contained in:
Jackzie 2025-01-21 16:36:17 -06:00
parent d278f582cc
commit c51aa6533c
6 changed files with 1053 additions and 840 deletions

View file

@ -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");

View 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;
}
}

View 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);
}

View 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();
}
}
}

View 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