Initial Commit
This commit is contained in:
		
							
								
								
									
										337
									
								
								Plugin_Blocks&ItemsCounter.as
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										337
									
								
								Plugin_Blocks&ItemsCounter.as
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,337 @@ | ||||
| #name "Blocks & Items Counter" | ||||
| #author "Beu" | ||||
| #category "Map Editor" | ||||
| #version "1.0" | ||||
| #siteid 97 | ||||
|  | ||||
| #include "Icons.as" | ||||
|  | ||||
| class Objects { //Items or Blocks | ||||
| 	string name; | ||||
| 	string type; | ||||
| 	string source; | ||||
| 	int size; | ||||
| 	int count; | ||||
| 	bool icon; | ||||
| 	array<vec3> positions; | ||||
|  | ||||
| 	Objects(string name, bool icon, string type, string source, int size, vec3 pos ) { | ||||
| 		this.name = name; | ||||
| 		this.count = 1; | ||||
| 		this.type = type; | ||||
| 		this.icon = icon; | ||||
| 		this.source = source; | ||||
| 		this.size = size; | ||||
| 		this.positions = {pos}; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| enum ESortColumn { | ||||
| 	ItemName, | ||||
| 	Type, | ||||
| 	Source, | ||||
| 	Size, | ||||
| 	Count | ||||
| } | ||||
|  | ||||
|  | ||||
| bool menu_visibility = false; | ||||
| int camerafocusindex = 0; | ||||
| bool include_default_objects = false; | ||||
| bool refreshobject; | ||||
|  | ||||
| bool sort_reverse; | ||||
| bool forcesort; | ||||
| string infotext; | ||||
|  | ||||
| array<Objects@> objects = {}; | ||||
| array<Objects@> sortableobjects = {}; | ||||
| array<string> objectsindex = {}; | ||||
|  | ||||
|  | ||||
| ESortColumn sortCol = ESortColumn(-1); | ||||
|  | ||||
| void Main() { | ||||
| 	while (true) { | ||||
| 		if (refreshobject) { | ||||
| 			objects.Resize(0); | ||||
| 			objectsindex.Resize(0); | ||||
| 			sortableobjects.Resize(0); | ||||
| 			RefreshBlocks(); | ||||
| 			RefreshItems(); | ||||
| 			sortableobjects = objects; | ||||
| 			refreshobject = false; | ||||
| 		} | ||||
| 		yield(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Force to split the refresh functions to bypass the script execution delay on heavy maps | ||||
| void RefreshBlocks() { | ||||
| 	auto map = GetApp().RootMap; | ||||
|  | ||||
| 	if (map !is null) { | ||||
| 		int defaultheight = 0; | ||||
| 		if (map.DecorationName.SubStr(0, 5) == "48x48") { // Change the default height depending on the presence of the stadium or not | ||||
| 			defaultheight = 9; | ||||
| 		} | ||||
|  | ||||
| 		// Blocks | ||||
| 		auto blocks = map.Blocks; | ||||
| 		for(int i = 0; i < blocks.Length; i++) { | ||||
| 			int idifexist = -1; | ||||
| 			string blockname; | ||||
| 			blockname = blocks[i].BlockModel.IdName; | ||||
| 			if (blockname.ToLower().SubStr(blockname.Length - 22, 22) == ".block.gbx_customblock") blockname = blockname.SubStr(0, blockname.Length - 12); | ||||
| 			if (include_default_objects || blockname.ToLower().SubStr(blockname.Length - 10, 10) == ".block.gbx") { | ||||
| 				vec3 pos; | ||||
| 				pos.x = blocks[i].CoordX * 32 + 16; | ||||
| 				pos.y = (blocks[i].CoordY - defaultheight) * 8 + 4; | ||||
| 				pos.z = blocks[i].CoordZ * 32 + 16; | ||||
|  | ||||
| 				int index = objectsindex.Find(blockname); | ||||
| 				 | ||||
| 				if (index >= 0) { | ||||
| 					objects[index].count++; | ||||
| 					objects[index].positions.InsertLast(pos); | ||||
| 				} else { | ||||
| 					AddNewObject(blockname, "Block", pos ); | ||||
| 					objectsindex.InsertLast(blockname); | ||||
| 				} | ||||
| 			} | ||||
| 			if (i % 100 == 0) yield(); // to avoid timeout | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Force to split the refresh functions to bypass the script execution delay on heavy maps | ||||
| void RefreshItems() { | ||||
| 	auto map = GetApp().RootMap; | ||||
|  | ||||
| 	if (map !is null) { | ||||
| 		// Items | ||||
| 		auto items = map.AnchoredObjects; | ||||
| 		for(int i = 0; i < items.Length; i++) { | ||||
| 			int idifexist = -1; | ||||
| 			string itemname = items[i].ItemModel.IdName; | ||||
| 			if (include_default_objects || itemname.ToLower().SubStr(itemname.Length - 9, 9) == ".item.gbx") { | ||||
| 				int index = objectsindex.Find(itemname); | ||||
| 				if (index >= 0) { | ||||
| 					objects[index].count++; | ||||
| 					objects[index].positions.InsertLast(items[i].AbsolutePositionInMap); | ||||
| 				} else { | ||||
| 					AddNewObject(itemname, "Item", items[i].AbsolutePositionInMap); | ||||
| 					objectsindex.InsertLast(itemname); | ||||
| 				} | ||||
| 			} | ||||
| 			if (i % 100 == 0) yield(); // to avoid timeout | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void AddNewObject(string objectname, string type, vec3 pos) { | ||||
| 	bool icon = false; | ||||
| 	int size; | ||||
| 	string source; | ||||
| 	CSystemFidFile@ file; | ||||
| 	CGameCtnCollector@ collector; | ||||
| 	CSystemFidFile@ tempfile; | ||||
|  | ||||
| 	if (type == "Item" && objectname.SubStr(0,5) == "club:") {//  ItemCollections | ||||
| 		source = "Club"; | ||||
| 		@file = Fids::GetFake('MemoryTemp\\FavoriteClubItems\\' + objectname.SubStr(5,objectname.Length)); | ||||
| 		@collector = cast<CGameCtnCollector>(cast<CGameItemModel>(file.Nod)); | ||||
| 		if (collector is null || (collector.Icon !is null || file.ByteSize == 0)) { | ||||
| 			@tempfile = Fids::GetFake('MemoryTemp\\CurrentMap_EmbeddedFiles\\ContentLoaded\\ClubItems\\' + objectname.SubStr(5,objectname.Length)); | ||||
| 		} | ||||
| 	} else { // Blocks and Items | ||||
| 		source = "Local"; | ||||
| 		@file = Fids::GetUser(type + 's\\' + objectname); | ||||
| 		CGameItemModel@ model = cast<CGameItemModel>(file.Nod); | ||||
| 		@collector = cast<CGameCtnCollector>(model); | ||||
| 		if (collector is null || (collector.Icon !is null || file.ByteSize == 0)) { | ||||
| 			@tempfile = Fids::GetFake('MemoryTemp\\CurrentMap_EmbeddedFiles\\ContentLoaded\\' + type + 's\\' + objectname); | ||||
| 		} | ||||
| 	} | ||||
| 	if (tempfile !is null) { | ||||
| 		if (collector !is null && collector.Icon !is null && tempfile.ByteSize == 0) { | ||||
| 			icon = true; | ||||
| 			size = file.ByteSize; | ||||
| 		} else  { | ||||
| 			size = tempfile.ByteSize; | ||||
| 		} | ||||
| 		if (file.ByteSize == 0 && tempfile.ByteSize == 0) { | ||||
| 			source = "In-Game"; | ||||
| 		} else if (file.ByteSize == 0 && tempfile.ByteSize > 0) { | ||||
| 			source = "Embedded"; | ||||
| 		} | ||||
| 	} else { | ||||
| 		size = file.ByteSize; | ||||
| 	} | ||||
| 	objects.InsertLast(Objects(objectname, icon, type, source, size, pos)); | ||||
| } | ||||
|  | ||||
| bool FocusCam(string objectname) { | ||||
| 	auto editor = cast<CGameCtnEditorFree>(GetApp().Editor); | ||||
| 	auto camera = editor.OrbitalCameraControl; | ||||
| 	auto map = GetApp().RootMap; | ||||
|  | ||||
| 	// variables to workaround the non-existence of positions for freemapping placed blocks | ||||
| 	bool canfocus = false; | ||||
| 	int iterations; | ||||
|  | ||||
| 	if (camera !is null) { | ||||
| 		int index = objectsindex.Find(objectname); | ||||
| 		iterations = 0;	 | ||||
|  | ||||
| 		while (!canfocus) { | ||||
| 			iterations++; | ||||
| 			camerafocusindex++; | ||||
| 			if (camerafocusindex > objects[index].positions.get_Length() - 1 ) { | ||||
| 				camerafocusindex = 0; | ||||
| 			} | ||||
| 			if (objects[index].positions[camerafocusindex].x != 4294967295 && objects[index].positions[camerafocusindex].z != 4294967295) { | ||||
| 				canfocus = true; | ||||
| 			} | ||||
| 			if (iterations > objects[index].positions.get_Length()) { | ||||
| 				return false; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		camera.m_TargetedPosition = objects[index].positions[camerafocusindex]; | ||||
| 		// Workaround to update camera TargetedPosition | ||||
| 		editor.ButtonZoomInOnClick(); | ||||
| 		editor.ButtonZoomOutOnClick(); | ||||
| 		return true; | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| void GenerateRow(Objects@ object) { | ||||
| 	UI::TableNextRow(); | ||||
| 	UI::TableNextColumn(); | ||||
| 	if (UI::Button(Icons::Search + "###" + object.name)) { | ||||
| 		FocusCam(object.name); | ||||
| 	} | ||||
| 	if (UI::IsItemHovered() && object.type == "Block") infotext = "ATM, it's not possible to focus on blocks placed in free mapping."; | ||||
| 	UI::SameLine(); | ||||
| 	UI::Text(object.name); | ||||
| 	UI::TableNextColumn(); | ||||
| 	UI::Text(object.type); | ||||
| 	UI::TableNextColumn(); | ||||
| 	UI::Text(object.source); | ||||
| 	UI::TableNextColumn(); | ||||
| 	if (object.icon) { | ||||
| 		UI::Text("\\$fc0" + Text::Format("%lld",object.size)); | ||||
| 		if (UI::IsItemHovered()) infotext = "All items with size in orange contains the icon. You must re-open the map to have the real size."; | ||||
| 	} else { | ||||
| 		UI::Text(Text::Format("%lld",object.size)); | ||||
| 	} | ||||
| 	UI::TableNextColumn(); | ||||
| 	UI::Text(Text::Format("%lld",object.count)); | ||||
| } | ||||
|  | ||||
| void Render() { | ||||
| 	if (!menu_visibility) { | ||||
| 		return; | ||||
| 	} | ||||
| 	infotext = ""; | ||||
| 	auto editor = cast<CGameCtnEditorFree>(GetApp().Editor); | ||||
|  | ||||
| 	UI::SetNextWindowSize(600, 400); | ||||
| 	UI::SetNextWindowPos(200, 200, UI::Cond::Once); | ||||
| 	UI::Begin("\\$cf9" + Icons::Table + "\\$z Blocks & Items Counter###Blocks & Items Counter", menu_visibility); | ||||
| 	if (editor !is null) { | ||||
| 		if (refreshobject) { | ||||
| 			if (Time::get_Now() % 3000 > 2000) { | ||||
| 				UI::Button("Loading..."); | ||||
| 			} else if (Time::get_Now() % 3000 > 1000) { | ||||
| 				UI::Button("Loading.. "); | ||||
| 			} else { | ||||
| 				UI::Button("Loading.  "); | ||||
| 			} | ||||
| 			if (UI::IsItemHovered()) infotext = "Parsing all blocks and items to generate the table. Please wait..."; | ||||
| 		} else { | ||||
| 			if (UI::Button(Icons::SyncAlt + " Refresh")) { | ||||
| 				// Force to split the refresh functions to bypass the script execution delay on heavy maps | ||||
| 				refreshobject = true; | ||||
| 				forcesort = true; | ||||
| 			} | ||||
| 		} | ||||
| 		UI::SameLine(); | ||||
| 		include_default_objects = UI::Checkbox("Include In-Game Blocks and Items", include_default_objects); | ||||
| 		UI::Separator(); | ||||
| 		vec2 winsize = UI::GetWindowSize(); | ||||
| 		winsize.x = winsize.x-10; | ||||
| 		winsize.y = winsize.y-105; | ||||
| 		if (UI::BeginTable("ItemsTable", 5, UI::TableFlags(UI::TableFlags::Resizable | UI::TableFlags::Sortable | UI::TableFlags::NoSavedSettings | UI::TableFlags::BordersInnerV | UI::TableFlags::SizingStretchProp | UI::TableFlags::ScrollY),winsize )) { | ||||
| 			UI::TableSetupScrollFreeze(0, 1); | ||||
| 			UI::TableSetupColumn("Item Name", UI::TableColumnFlags::None, 55.f, ESortColumn::ItemName);	 | ||||
| 			UI::TableSetupColumn("Type", UI::TableColumnFlags::None, 7.f, ESortColumn::Type); | ||||
| 			UI::TableSetupColumn("Source", UI::TableColumnFlags::None, 13.f, ESortColumn::Source); | ||||
| 			UI::TableSetupColumn("Size", UI::TableColumnFlags::None, 15.f, ESortColumn::Size); | ||||
| 			UI::TableSetupColumn("Count", UI::TableColumnFlags::DefaultSort, 10.f, ESortColumn::Count); | ||||
| 			UI::TableHeadersRow(); | ||||
|  | ||||
| 			UI::TableSortSpecs@ sortSpecs = UI::TableGetSortSpecs(); | ||||
| 			if(sortSpecs !is null && sortSpecs.Specs.Length == 1 && sortableobjects.Length > 1) { | ||||
| 				if(sortSpecs.Dirty || (forcesort && !refreshobject)) { | ||||
| 					if(sortCol != ESortColumn(sortSpecs.Specs[0].ColumnUserID) || (forcesort && !refreshobject)) { | ||||
| 						sortCol = ESortColumn(sortSpecs.Specs[0].ColumnUserID); | ||||
| 						switch(sortCol) { | ||||
| 							case ESortColumn::ItemName: | ||||
| 								sortableobjects.Sort(function(a,b) { return a.name < b.name; }); | ||||
| 								break; | ||||
| 							case ESortColumn::Type: | ||||
| 								sortableobjects.Sort(function(a,b) { return a.type < b.type; }); | ||||
| 								break; | ||||
| 							case ESortColumn::Source: | ||||
| 								sortableobjects.Sort(function(a,b) { return a.source < b.source; }); | ||||
| 								break; | ||||
| 							case ESortColumn::Size: | ||||
| 								sortableobjects.Sort(function(a,b) { return a.size < b.size; }); | ||||
| 								break; | ||||
| 							case ESortColumn::Count: | ||||
| 								sortableobjects.Sort(function(a,b) { return a.count < b.count; }); | ||||
| 								break; | ||||
| 						} | ||||
| 						if (forcesort && sort_reverse) { | ||||
| 							sortableobjects.Reverse(); | ||||
| 						} else { | ||||
| 							sort_reverse = false; | ||||
| 						} | ||||
| 					} else if (sortCol == ESortColumn(sortSpecs.Specs[0].ColumnUserID)) { | ||||
| 						sortableobjects.Reverse(); | ||||
| 						sort_reverse = !sort_reverse; | ||||
| 					} | ||||
|  | ||||
| 					sortSpecs.Dirty = false; | ||||
| 					forcesort = false; | ||||
| 				} | ||||
| 			} | ||||
| 			if (sortableobjects.Length > 0 ) { | ||||
| 				for(int i = 0; i < sortableobjects.Length; i++) { | ||||
| 					GenerateRow(sortableobjects[i]); | ||||
| 				} | ||||
| 			} else { | ||||
| 				for(int i = 0; i < objects.Length; i++) { | ||||
| 					GenerateRow(objects[i]); | ||||
| 				} | ||||
| 			} | ||||
| 			UI::EndTable(); | ||||
| 			UI::Separator(); | ||||
| 			UI::Text(Icons::Info + " " + infotext); | ||||
| 		} | ||||
| 	} else { | ||||
| 		UI::Text("Open this plugin in the map editor"); | ||||
| 	} | ||||
| 	UI::End(); | ||||
| 	 | ||||
| } | ||||
| 	 | ||||
| void RenderMenu() { | ||||
| 	if(UI::MenuItem("\\$cf9" + Icons::Table + "\\$z Blocks & Items Counter", "", menu_visibility)) { | ||||
| 		menu_visibility = !menu_visibility; | ||||
| 		refreshobject = true; | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user