Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
06593805a7
|
|||
|
53b0b4317d
|
|||
|
ad8310f143
|
|||
|
5ff5764fa2
|
|||
|
c320e8d993
|
|||
|
dd2d7a3abe
|
|||
|
9c86fe30f6
|
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
source/
|
||||
BIN
42/icon.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
9
42/mod.info
Normal file
@@ -0,0 +1,9 @@
|
||||
name=Opinionated Firearms
|
||||
id=hrsys_opinionated_firearms_testing
|
||||
author=Riggs0
|
||||
modversion=1.0.0
|
||||
versionMin=42.12.13
|
||||
require=\2788256295/ammomaker,\HBVCEFb42
|
||||
description=Opinionated Firearms casing and other changes to Guns of 93, Ammomaker and Hot Brass
|
||||
icon=icon.png
|
||||
poster=preview.png
|
||||
BIN
42/preview.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
557
app.js
@@ -1,557 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const state = {
|
||||
catalog: null,
|
||||
profileByItem: {},
|
||||
selectedItem: null,
|
||||
};
|
||||
|
||||
const dom = {
|
||||
statusText: document.getElementById("statusText"),
|
||||
catalogFile: document.getElementById("catalogFile"),
|
||||
profileFile: document.getElementById("profileFile"),
|
||||
exportProfile: document.getElementById("exportProfile"),
|
||||
searchInput: document.getElementById("searchInput"),
|
||||
categoryFilter: document.getElementById("categoryFilter"),
|
||||
spawnFilter: document.getElementById("spawnFilter"),
|
||||
itemTableBody: document.getElementById("itemTableBody"),
|
||||
selectedDetails: document.getElementById("selectedDetails"),
|
||||
resetSelected: document.getElementById("resetSelected"),
|
||||
};
|
||||
|
||||
function setStatus(text) {
|
||||
dom.statusText.textContent = text;
|
||||
}
|
||||
|
||||
function readFileText(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => resolve(String(reader.result || ""));
|
||||
reader.onerror = () => reject(new Error(`Failed to read ${file.name}`));
|
||||
reader.readAsText(file);
|
||||
});
|
||||
}
|
||||
|
||||
function safeNumber(value, fallback = 0) {
|
||||
const n = Number.parseFloat(value);
|
||||
return Number.isFinite(n) ? n : fallback;
|
||||
}
|
||||
|
||||
function normalizeItemType(item) {
|
||||
if (typeof item !== "string") {
|
||||
return null;
|
||||
}
|
||||
const trimmed = item.trim();
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
return trimmed.includes(".") ? trimmed : `Base.${trimmed}`;
|
||||
}
|
||||
|
||||
function normalizeCatalog(raw) {
|
||||
if (!raw || !Array.isArray(raw.items)) {
|
||||
throw new Error("Invalid catalog format. Missing items array.");
|
||||
}
|
||||
|
||||
const lists = new Set(Array.isArray(raw.lists) ? raw.lists : []);
|
||||
const items = [];
|
||||
|
||||
for (const rawItem of raw.items) {
|
||||
const itemType = normalizeItemType(rawItem.item);
|
||||
if (!itemType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const aggregated = Array.isArray(rawItem.aggregatedPlacements)
|
||||
? rawItem.aggregatedPlacements
|
||||
: [];
|
||||
const placements = {};
|
||||
for (const placement of aggregated) {
|
||||
const listName = placement && typeof placement.list === "string" ? placement.list.trim() : "";
|
||||
const weight = safeNumber(placement && placement.weight, 0);
|
||||
if (!listName || weight <= 0) {
|
||||
continue;
|
||||
}
|
||||
placements[listName] = Number(weight.toFixed(6));
|
||||
lists.add(listName);
|
||||
}
|
||||
|
||||
items.push({
|
||||
item: itemType,
|
||||
shortId: typeof rawItem.shortId === "string" ? rawItem.shortId : itemType.replace(/^Base\./, ""),
|
||||
category: typeof rawItem.category === "string" ? rawItem.category : "unknown",
|
||||
defaultEnabled: rawItem.defaultEnabled !== false,
|
||||
spawnControlKeys: Array.isArray(rawItem.spawnControlKeys) ? rawItem.spawnControlKeys : [],
|
||||
defaultPlacements: placements,
|
||||
});
|
||||
}
|
||||
|
||||
items.sort((a, b) => a.item.localeCompare(b.item));
|
||||
return {
|
||||
generatedAt: raw.generatedAt || null,
|
||||
source: raw.source || {},
|
||||
items,
|
||||
lists: Array.from(lists).sort(),
|
||||
};
|
||||
}
|
||||
|
||||
function initializeProfileFromCatalog() {
|
||||
const profileByItem = {};
|
||||
for (const item of state.catalog.items) {
|
||||
const placements = {};
|
||||
for (const [listName, weight] of Object.entries(item.defaultPlacements)) {
|
||||
placements[listName] = weight;
|
||||
}
|
||||
profileByItem[item.item] = {
|
||||
item: item.item,
|
||||
category: item.category,
|
||||
enabled: item.defaultEnabled,
|
||||
placements,
|
||||
};
|
||||
}
|
||||
state.profileByItem = profileByItem;
|
||||
}
|
||||
|
||||
function getProfileEntry(itemType) {
|
||||
return state.profileByItem[itemType] || null;
|
||||
}
|
||||
|
||||
function updateProfileEntry(itemType, updater) {
|
||||
const current = getProfileEntry(itemType);
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
updater(current);
|
||||
}
|
||||
|
||||
function getFilteredItems() {
|
||||
if (!state.catalog) {
|
||||
return [];
|
||||
}
|
||||
const search = dom.searchInput.value.trim().toLowerCase();
|
||||
const category = dom.categoryFilter.value;
|
||||
const spawnState = dom.spawnFilter.value;
|
||||
|
||||
return state.catalog.items.filter((item) => {
|
||||
const entry = getProfileEntry(item.item);
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
if (category !== "all" && item.category !== category) {
|
||||
return false;
|
||||
}
|
||||
if (spawnState === "enabled" && !entry.enabled) {
|
||||
return false;
|
||||
}
|
||||
if (spawnState === "disabled" && entry.enabled) {
|
||||
return false;
|
||||
}
|
||||
if (!search) {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
item.item.toLowerCase().includes(search) ||
|
||||
item.shortId.toLowerCase().includes(search)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function renderItemTable() {
|
||||
const filteredItems = getFilteredItems();
|
||||
dom.itemTableBody.innerHTML = "";
|
||||
|
||||
for (const item of filteredItems) {
|
||||
const entry = getProfileEntry(item.item);
|
||||
const row = document.createElement("tr");
|
||||
if (state.selectedItem === item.item) {
|
||||
row.classList.add("selected");
|
||||
}
|
||||
|
||||
const spawnTd = document.createElement("td");
|
||||
const spawnCheck = document.createElement("input");
|
||||
spawnCheck.type = "checkbox";
|
||||
spawnCheck.checked = entry.enabled;
|
||||
spawnCheck.addEventListener("click", (event) => event.stopPropagation());
|
||||
spawnCheck.addEventListener("change", () => {
|
||||
updateProfileEntry(item.item, (target) => {
|
||||
target.enabled = spawnCheck.checked;
|
||||
});
|
||||
renderItemTable();
|
||||
renderSelectedDetails();
|
||||
});
|
||||
spawnTd.appendChild(spawnCheck);
|
||||
|
||||
const itemTd = document.createElement("td");
|
||||
itemTd.textContent = item.shortId;
|
||||
|
||||
const categoryTd = document.createElement("td");
|
||||
categoryTd.textContent = item.category;
|
||||
|
||||
const listsTd = document.createElement("td");
|
||||
listsTd.textContent = String(Object.keys(entry.placements).length);
|
||||
|
||||
row.appendChild(spawnTd);
|
||||
row.appendChild(itemTd);
|
||||
row.appendChild(categoryTd);
|
||||
row.appendChild(listsTd);
|
||||
|
||||
row.addEventListener("click", () => {
|
||||
state.selectedItem = item.item;
|
||||
renderItemTable();
|
||||
renderSelectedDetails();
|
||||
});
|
||||
|
||||
dom.itemTableBody.appendChild(row);
|
||||
}
|
||||
}
|
||||
|
||||
function renderPlacementsTable(item, entry, container) {
|
||||
const table = document.createElement("table");
|
||||
table.className = "placements-table";
|
||||
const thead = document.createElement("thead");
|
||||
thead.innerHTML = "<tr><th>List</th><th>Weight</th><th></th></tr>";
|
||||
table.appendChild(thead);
|
||||
|
||||
const tbody = document.createElement("tbody");
|
||||
const placementNames = Object.keys(entry.placements).sort();
|
||||
|
||||
for (const listName of placementNames) {
|
||||
const weight = entry.placements[listName];
|
||||
const tr = document.createElement("tr");
|
||||
|
||||
const listTd = document.createElement("td");
|
||||
listTd.textContent = listName;
|
||||
|
||||
const weightTd = document.createElement("td");
|
||||
const weightInput = document.createElement("input");
|
||||
weightInput.type = "number";
|
||||
weightInput.min = "0";
|
||||
weightInput.step = "0.001";
|
||||
weightInput.value = String(weight);
|
||||
weightInput.addEventListener("change", () => {
|
||||
const next = safeNumber(weightInput.value, 0);
|
||||
if (next <= 0) {
|
||||
delete entry.placements[listName];
|
||||
} else {
|
||||
entry.placements[listName] = Number(next.toFixed(6));
|
||||
}
|
||||
renderItemTable();
|
||||
renderSelectedDetails();
|
||||
});
|
||||
weightTd.appendChild(weightInput);
|
||||
|
||||
const actionTd = document.createElement("td");
|
||||
const removeBtn = document.createElement("button");
|
||||
removeBtn.type = "button";
|
||||
removeBtn.className = "small-btn remove";
|
||||
removeBtn.textContent = "Remove";
|
||||
removeBtn.addEventListener("click", () => {
|
||||
delete entry.placements[listName];
|
||||
renderItemTable();
|
||||
renderSelectedDetails();
|
||||
});
|
||||
actionTd.appendChild(removeBtn);
|
||||
|
||||
tr.appendChild(listTd);
|
||||
tr.appendChild(weightTd);
|
||||
tr.appendChild(actionTd);
|
||||
tbody.appendChild(tr);
|
||||
}
|
||||
|
||||
table.appendChild(tbody);
|
||||
container.appendChild(table);
|
||||
}
|
||||
|
||||
function renderSelectedDetails() {
|
||||
const selected = state.selectedItem;
|
||||
dom.selectedDetails.innerHTML = "";
|
||||
|
||||
if (!state.catalog || !selected) {
|
||||
dom.selectedDetails.textContent = "Select an item to edit placements and spawn rate.";
|
||||
dom.selectedDetails.className = "details-empty";
|
||||
return;
|
||||
}
|
||||
|
||||
const item = state.catalog.items.find((it) => it.item === selected);
|
||||
const entry = getProfileEntry(selected);
|
||||
if (!item || !entry) {
|
||||
dom.selectedDetails.textContent = "Selected item not found.";
|
||||
dom.selectedDetails.className = "details-empty";
|
||||
return;
|
||||
}
|
||||
|
||||
dom.selectedDetails.className = "details-body";
|
||||
|
||||
const itemHeader = document.createElement("div");
|
||||
itemHeader.className = "item-header";
|
||||
itemHeader.innerHTML = `
|
||||
<div>
|
||||
<h3 class="item-title">${item.item}</h3>
|
||||
<span class="badge">${item.category}</span>
|
||||
</div>
|
||||
`;
|
||||
dom.selectedDetails.appendChild(itemHeader);
|
||||
|
||||
const enabledRow = document.createElement("div");
|
||||
enabledRow.className = "inline-row";
|
||||
const enabledInput = document.createElement("input");
|
||||
enabledInput.type = "checkbox";
|
||||
enabledInput.checked = entry.enabled;
|
||||
enabledInput.addEventListener("change", () => {
|
||||
entry.enabled = enabledInput.checked;
|
||||
renderItemTable();
|
||||
});
|
||||
|
||||
const enabledLabel = document.createElement("label");
|
||||
enabledLabel.textContent = "Spawn enabled";
|
||||
enabledLabel.prepend(enabledInput);
|
||||
enabledLabel.style.display = "inline-flex";
|
||||
enabledLabel.style.alignItems = "center";
|
||||
enabledLabel.style.gap = "0.35rem";
|
||||
enabledRow.appendChild(enabledLabel);
|
||||
dom.selectedDetails.appendChild(enabledRow);
|
||||
|
||||
const placementsLabel = document.createElement("p");
|
||||
placementsLabel.textContent = "Placements (distribution list + spawn rate weight):";
|
||||
placementsLabel.style.margin = "0 0 0.4rem";
|
||||
dom.selectedDetails.appendChild(placementsLabel);
|
||||
|
||||
renderPlacementsTable(item, entry, dom.selectedDetails);
|
||||
|
||||
const addRow = document.createElement("div");
|
||||
addRow.className = "inline-row";
|
||||
|
||||
const listSelect = document.createElement("select");
|
||||
const usedLists = new Set(Object.keys(entry.placements));
|
||||
const availableLists = state.catalog.lists.filter((listName) => !usedLists.has(listName));
|
||||
for (const listName of availableLists) {
|
||||
const option = document.createElement("option");
|
||||
option.value = listName;
|
||||
option.textContent = listName;
|
||||
listSelect.appendChild(option);
|
||||
}
|
||||
|
||||
const customInput = document.createElement("input");
|
||||
customInput.type = "text";
|
||||
customInput.placeholder = "or custom list name";
|
||||
|
||||
const weightInput = document.createElement("input");
|
||||
weightInput.type = "number";
|
||||
weightInput.min = "0";
|
||||
weightInput.step = "0.001";
|
||||
weightInput.value = "1";
|
||||
|
||||
const addButton = document.createElement("button");
|
||||
addButton.type = "button";
|
||||
addButton.className = "small-btn";
|
||||
addButton.textContent = "Add Placement";
|
||||
addButton.addEventListener("click", () => {
|
||||
const custom = customInput.value.trim();
|
||||
const selectedList = custom || listSelect.value;
|
||||
const weight = safeNumber(weightInput.value, 0);
|
||||
if (!selectedList || weight <= 0) {
|
||||
return;
|
||||
}
|
||||
entry.placements[selectedList] = Number(weight.toFixed(6));
|
||||
if (!state.catalog.lists.includes(selectedList)) {
|
||||
state.catalog.lists.push(selectedList);
|
||||
state.catalog.lists.sort();
|
||||
}
|
||||
renderItemTable();
|
||||
renderSelectedDetails();
|
||||
});
|
||||
|
||||
addRow.appendChild(listSelect);
|
||||
addRow.appendChild(customInput);
|
||||
addRow.appendChild(weightInput);
|
||||
addRow.appendChild(addButton);
|
||||
dom.selectedDetails.appendChild(addRow);
|
||||
|
||||
const resetRow = document.createElement("div");
|
||||
resetRow.className = "inline-row";
|
||||
|
||||
const restoreButton = document.createElement("button");
|
||||
restoreButton.type = "button";
|
||||
restoreButton.className = "small-btn";
|
||||
restoreButton.textContent = "Restore Catalog Placements";
|
||||
restoreButton.addEventListener("click", () => {
|
||||
entry.enabled = item.defaultEnabled;
|
||||
entry.placements = { ...item.defaultPlacements };
|
||||
renderItemTable();
|
||||
renderSelectedDetails();
|
||||
});
|
||||
|
||||
const clearButton = document.createElement("button");
|
||||
clearButton.type = "button";
|
||||
clearButton.className = "small-btn remove";
|
||||
clearButton.textContent = "Clear Placements";
|
||||
clearButton.addEventListener("click", () => {
|
||||
entry.placements = {};
|
||||
renderItemTable();
|
||||
renderSelectedDetails();
|
||||
});
|
||||
|
||||
resetRow.appendChild(restoreButton);
|
||||
resetRow.appendChild(clearButton);
|
||||
dom.selectedDetails.appendChild(resetRow);
|
||||
|
||||
const meta = document.createElement("p");
|
||||
meta.className = "meta";
|
||||
meta.innerHTML = `
|
||||
Sandbox weight controls: ${item.spawnControlKeys.length ? item.spawnControlKeys.join(", ") : "none"}<br>
|
||||
Catalog list count: ${Object.keys(item.defaultPlacements).length}
|
||||
`;
|
||||
dom.selectedDetails.appendChild(meta);
|
||||
}
|
||||
|
||||
function buildExportProfile() {
|
||||
const entries = Object.keys(state.profileByItem)
|
||||
.sort()
|
||||
.map((itemType) => {
|
||||
const entry = state.profileByItem[itemType];
|
||||
const placements = Object.keys(entry.placements)
|
||||
.sort()
|
||||
.map((listName) => ({
|
||||
list: listName,
|
||||
weight: Number(entry.placements[listName]),
|
||||
}));
|
||||
return {
|
||||
item: itemType,
|
||||
category: entry.category,
|
||||
enabled: entry.enabled,
|
||||
placements,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
formatVersion: 1,
|
||||
generatedAt: new Date().toISOString(),
|
||||
sourceCatalog: {
|
||||
generatedAt: state.catalog ? state.catalog.generatedAt : null,
|
||||
source: state.catalog ? state.catalog.source : {},
|
||||
},
|
||||
entries,
|
||||
};
|
||||
}
|
||||
|
||||
function downloadTextFile(fileName, content) {
|
||||
const blob = new Blob([content], { type: "application/json" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const anchor = document.createElement("a");
|
||||
anchor.href = url;
|
||||
anchor.download = fileName;
|
||||
anchor.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
async function onCatalogFileSelected() {
|
||||
const file = dom.catalogFile.files[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const text = await readFileText(file);
|
||||
const rawCatalog = JSON.parse(text);
|
||||
state.catalog = normalizeCatalog(rawCatalog);
|
||||
initializeProfileFromCatalog();
|
||||
state.selectedItem = state.catalog.items.length ? state.catalog.items[0].item : null;
|
||||
setStatus(
|
||||
`Catalog loaded (${state.catalog.items.length} items, ${state.catalog.lists.length} lists).`
|
||||
);
|
||||
renderItemTable();
|
||||
renderSelectedDetails();
|
||||
} catch (error) {
|
||||
setStatus(`Catalog load failed: ${error.message}`);
|
||||
} finally {
|
||||
dom.catalogFile.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
async function onProfileFileSelected() {
|
||||
if (!state.catalog) {
|
||||
setStatus("Load a catalog first.");
|
||||
dom.profileFile.value = "";
|
||||
return;
|
||||
}
|
||||
|
||||
const file = dom.profileFile.files[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const text = await readFileText(file);
|
||||
const raw = JSON.parse(text);
|
||||
if (!Array.isArray(raw.entries)) {
|
||||
throw new Error("Profile must contain an entries array.");
|
||||
}
|
||||
|
||||
for (const row of raw.entries) {
|
||||
const itemType = normalizeItemType(row.item);
|
||||
if (!itemType || !state.profileByItem[itemType]) {
|
||||
continue;
|
||||
}
|
||||
const entry = state.profileByItem[itemType];
|
||||
entry.enabled = row.enabled !== false;
|
||||
entry.placements = {};
|
||||
if (Array.isArray(row.placements)) {
|
||||
for (const placement of row.placements) {
|
||||
const listName =
|
||||
placement && typeof placement.list === "string" ? placement.list.trim() : "";
|
||||
const weight = safeNumber(placement && placement.weight, 0);
|
||||
if (!listName || weight <= 0) {
|
||||
continue;
|
||||
}
|
||||
entry.placements[listName] = Number(weight.toFixed(6));
|
||||
if (!state.catalog.lists.includes(listName)) {
|
||||
state.catalog.lists.push(listName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.catalog.lists.sort();
|
||||
setStatus("Profile loaded and applied to current catalog.");
|
||||
renderItemTable();
|
||||
renderSelectedDetails();
|
||||
} catch (error) {
|
||||
setStatus(`Profile load failed: ${error.message}`);
|
||||
} finally {
|
||||
dom.profileFile.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
function onExportProfile() {
|
||||
if (!state.catalog) {
|
||||
setStatus("Load a catalog before exporting.");
|
||||
return;
|
||||
}
|
||||
const payload = buildExportProfile();
|
||||
downloadTextFile("of-spawn-profile.json", `${JSON.stringify(payload, null, 2)}\n`);
|
||||
setStatus("Profile exported.");
|
||||
}
|
||||
|
||||
function onResetSelected() {
|
||||
if (!state.catalog || !state.selectedItem) {
|
||||
return;
|
||||
}
|
||||
const item = state.catalog.items.find((it) => it.item === state.selectedItem);
|
||||
const entry = getProfileEntry(state.selectedItem);
|
||||
if (!item || !entry) {
|
||||
return;
|
||||
}
|
||||
entry.enabled = item.defaultEnabled;
|
||||
entry.placements = { ...item.defaultPlacements };
|
||||
renderItemTable();
|
||||
renderSelectedDetails();
|
||||
setStatus(`Reset ${item.shortId} to catalog defaults.`);
|
||||
}
|
||||
|
||||
dom.catalogFile.addEventListener("change", onCatalogFileSelected);
|
||||
dom.profileFile.addEventListener("change", onProfileFileSelected);
|
||||
dom.exportProfile.addEventListener("click", onExportProfile);
|
||||
dom.searchInput.addEventListener("input", renderItemTable);
|
||||
dom.categoryFilter.addEventListener("change", renderItemTable);
|
||||
dom.spawnFilter.addEventListener("change", renderItemTable);
|
||||
dom.resetSelected.addEventListener("click", onResetSelected);
|
||||
BIN
art/icon.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
art/icon.psd
Normal file
BIN
art/logo.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
art/logo.psd
Normal file
BIN
art/preview.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
art/preview.psd
Normal file
BIN
art/workshop-preview.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,368 @@
|
||||
return {
|
||||
|
||||
WepFire = {
|
||||
"Base.83Cheetah",
|
||||
"Base.AKM",
|
||||
"Base.AKSport",
|
||||
"Base.Anaconda",
|
||||
"Base.AR15",
|
||||
"Base.AR180",
|
||||
"Base.AssaultRifle",
|
||||
"Base.AssaultRifle2",
|
||||
"Base.Auto5",
|
||||
"Base.Auto5_Alt",
|
||||
"Base.BenelliM3",
|
||||
"Base.BenelliM3_Alt",
|
||||
"Base.Beretta682",
|
||||
"Base.Beretta682_Alt",
|
||||
"Base.Beretta92FS",
|
||||
"Base.BHP",
|
||||
"Base.Bobcat",
|
||||
"Base.Brown3006BAR",
|
||||
"Base.Brown308BAR",
|
||||
"Base.BrownCitori",
|
||||
"Base.BrownCitori_Alt",
|
||||
"Base.Buckmark",
|
||||
"Base.CalicoPistol",
|
||||
"Base.CalicoRifle",
|
||||
"Base.CAR15",
|
||||
"Base.Colt9mm",
|
||||
"Base.ColtArmy",
|
||||
"Base.ColtDet",
|
||||
"Base.Command1911",
|
||||
"Base.CZ75B",
|
||||
"Base.DE357",
|
||||
"Base.DeltaElite",
|
||||
"Base.Dissy",
|
||||
"Base.DoubleBarrelShotgun",
|
||||
"Base.DoubleBarrelShotgun_Alt",
|
||||
"Base.DoubleBarrelShotgunSawnoff",
|
||||
"Base.DoubleBarrelShotgunSawnoff_Alt",
|
||||
"Base.FAL",
|
||||
"Base.Glock17",
|
||||
"Base.Glock17L",
|
||||
"Base.Glock19",
|
||||
"Base.Glock20",
|
||||
"Base.Glock21",
|
||||
"Base.Glock22",
|
||||
"Base.Glock23",
|
||||
"Base.Glock24",
|
||||
"Base.Gov1911",
|
||||
"Base.GP100",
|
||||
"Base.GP100_Alt",
|
||||
"Base.GP101",
|
||||
"Base.GP101_Alt",
|
||||
"Base.Hammerless",
|
||||
"Base.HBAR",
|
||||
"Base.HK91",
|
||||
"Base.HuntingRifle",
|
||||
"Base.Ithaca37",
|
||||
"Base.Ithaca37_Alt",
|
||||
"Base.Ithaca37Riot",
|
||||
"Base.Ithaca37Riot_Alt",
|
||||
"Base.J22",
|
||||
"Base.Javelina",
|
||||
"Base.L395K",
|
||||
"Base.L395K_Alt",
|
||||
"Base.Longslide",
|
||||
"Base.M1903",
|
||||
"Base.M1917",
|
||||
"Base.M1A",
|
||||
"Base.M1ABush",
|
||||
"Base.M1Carbine",
|
||||
"Base.M1Garand",
|
||||
"Base.M24",
|
||||
"Base.M249",
|
||||
"Base.M3GreaseGun",
|
||||
"Base.M590",
|
||||
"Base.M590_Alt",
|
||||
"Base.M60",
|
||||
"Base.M635",
|
||||
"Base.M723",
|
||||
"Base.M727",
|
||||
"Base.M733",
|
||||
"Base.M870",
|
||||
"Base.M870_Alt",
|
||||
"Base.MAC10",
|
||||
"Base.Marlin3363030",
|
||||
"Base.Marlin33644",
|
||||
"Base.Marlin39A",
|
||||
"Base.Marlin45",
|
||||
"Base.Marlin60",
|
||||
"Base.Marlin9",
|
||||
"Base.Mauser98K",
|
||||
"Base.Mini14",
|
||||
"Base.Mini30",
|
||||
"Base.Moss500",
|
||||
"Base.Moss500_Alt",
|
||||
"Base.Moss590",
|
||||
"Base.Moss590_Alt",
|
||||
"Base.MP5",
|
||||
"Base.Officer1911",
|
||||
"Base.P226",
|
||||
"Base.P228",
|
||||
"Base.P380",
|
||||
"Base.P7M13",
|
||||
"Base.P7M8",
|
||||
"Base.Pistol",
|
||||
"Base.Pistol2",
|
||||
"Base.Pistol3",
|
||||
"Base.PPK",
|
||||
"Base.Python",
|
||||
"Base.Python_Alt",
|
||||
"Base.R223Rem788",
|
||||
"Base.R223RugM77",
|
||||
"Base.R3006Rem700",
|
||||
"Base.R3006RugM77",
|
||||
"Base.R3030Rem788",
|
||||
"Base.R308Rem700",
|
||||
"Base.R308RugM77",
|
||||
"Base.R44Rem788",
|
||||
"Base.Raven25",
|
||||
"Base.Rem1100",
|
||||
"Base.Rem1100_Alt",
|
||||
"Base.Rem1187P",
|
||||
"Base.Rem1187P_Alt",
|
||||
"Base.Rem223760",
|
||||
"Base.Rem3006760",
|
||||
"Base.Rem308760",
|
||||
"Base.Rem870P",
|
||||
"Base.Rem870P_Alt",
|
||||
"Base.Revolver",
|
||||
"Base.Revolver_Long",
|
||||
"Base.Revolver_Short",
|
||||
"Base.Rug1022",
|
||||
"Base.Rugmark",
|
||||
"Base.RugP89",
|
||||
"Base.RugP90",
|
||||
"Base.SavageFox",
|
||||
"Base.SavageFox_Alt",
|
||||
"Base.SecuritySix",
|
||||
"Base.SecuritySix_Alt",
|
||||
"Base.Shotgun",
|
||||
"Base.Shotgun_Alt",
|
||||
"Base.ShotgunSawnoff",
|
||||
"Base.ShotgunSawnoff_Alt",
|
||||
"Base.SKS",
|
||||
"Base.SOAuto5",
|
||||
"Base.SOAuto5_Alt",
|
||||
"Base.SOBeretta682",
|
||||
"Base.SOBeretta682_Alt",
|
||||
"Base.SOBrownCitori",
|
||||
"Base.SOBrownCitori_Alt",
|
||||
"Base.SOIthaca37",
|
||||
"Base.SOIthaca37_Alt",
|
||||
"Base.SOL395K",
|
||||
"Base.SOL395K_Alt",
|
||||
"Base.SOM24",
|
||||
"Base.SOMoss500",
|
||||
"Base.SOMoss500_Alt",
|
||||
"Base.SOR223Rem700",
|
||||
"Base.SOR223Rem788",
|
||||
"Base.SOR3006Rem700",
|
||||
"Base.SOR3030Rem788",
|
||||
"Base.SOR308Rem700",
|
||||
"Base.SOR308Rem788",
|
||||
"Base.SOR44Rem788",
|
||||
"Base.SORem1100",
|
||||
"Base.SORem1100_Alt",
|
||||
"Base.SOSavageFox",
|
||||
"Base.SOSavageFox_Alt",
|
||||
"Base.SOW223WinM70",
|
||||
"Base.SOW3006WinM70",
|
||||
"Base.SOW308WinM70",
|
||||
"Base.SOWin1200",
|
||||
"Base.SOWin1200_Alt",
|
||||
"Base.SOWin1400",
|
||||
"Base.SOWin1400_Alt",
|
||||
"Base.SOWin1912",
|
||||
"Base.SOWin1912_Alt",
|
||||
"Base.SOWin37",
|
||||
"Base.SOWin37_Alt",
|
||||
"Base.SPAS12",
|
||||
"Base.SPAS12_Alt",
|
||||
"Base.SW10",
|
||||
"Base.SW17",
|
||||
"Base.SW4006",
|
||||
"Base.SW4506",
|
||||
"Base.SW586",
|
||||
"Base.SW586_Alt",
|
||||
"Base.SW5906",
|
||||
"Base.SW65",
|
||||
"Base.SW65_Alt",
|
||||
"Base.T56",
|
||||
"Base.TEC9",
|
||||
"Base.USP40",
|
||||
"Base.USP9",
|
||||
"Base.Uzi",
|
||||
"Base.Valmet",
|
||||
"Base.VarmintRifle",
|
||||
"Base.W223WinM70",
|
||||
"Base.W3006WinM70",
|
||||
"Base.W308WinM70",
|
||||
"Base.Win1200",
|
||||
"Base.Win1200_Alt",
|
||||
"Base.Win1200Def",
|
||||
"Base.Win1200Def_Alt",
|
||||
"Base.Win1400",
|
||||
"Base.Win1400_Alt",
|
||||
"Base.Win1912",
|
||||
"Base.Win1912_Alt",
|
||||
"Base.Win30067400",
|
||||
"Base.Win3087400",
|
||||
"Base.Win37",
|
||||
"Base.Win37_Alt",
|
||||
"Base.Win61",
|
||||
"Base.Win69",
|
||||
"Base.Win943030",
|
||||
"Base.Win94357",
|
||||
"Base.Win94357_Alt",
|
||||
"Base.Win9445",
|
||||
},
|
||||
|
||||
WepAmmoMag = {
|
||||
"Base.100CalicoMag",
|
||||
"Base.1022Mag",
|
||||
"Base.10M14Mag",
|
||||
"Base.15BHPMag",
|
||||
"Base.17BHPMag",
|
||||
"Base.1908Mag",
|
||||
"Base.1911MagExtend",
|
||||
"Base.1911MagExtendSS",
|
||||
"Base.1911MagSS",
|
||||
"Base.20M16AR180Mag",
|
||||
"Base.20M16Mag",
|
||||
"Base.20M9Mag",
|
||||
"Base.20MP5Mag",
|
||||
"Base.223Rem760Mag",
|
||||
"Base.223RemMag",
|
||||
"Base.25Rug1022Mag",
|
||||
"Base.25UziColtSMGMag",
|
||||
"Base.25UziMag",
|
||||
"Base.3006BARMag",
|
||||
"Base.3006clip",
|
||||
"Base.3006Rem760Mag",
|
||||
"Base.3006WinMag",
|
||||
"Base.3030RemMag",
|
||||
"Base.308BARMag",
|
||||
"Base.308Clip",
|
||||
"Base.308Rem760Mag",
|
||||
"Base.308WinMag",
|
||||
"Base.30M14Mag",
|
||||
"Base.30M16AR180Mag",
|
||||
"Base.30M1CarMag",
|
||||
"Base.30Mini14Mag",
|
||||
"Base.4006Mag",
|
||||
"Base.40AKMag",
|
||||
"Base.40AR180Mag",
|
||||
"Base.40M16AR180Mag",
|
||||
"Base.44Clip",
|
||||
"Base.44RemMag",
|
||||
"Base.4506Mag",
|
||||
"Base.45Clip",
|
||||
"Base.45Moonclip",
|
||||
"Base.556Clip",
|
||||
"Base.5906Mag",
|
||||
"Base.83Mag",
|
||||
"Base.9mmClip",
|
||||
"Base.AK5Mag",
|
||||
"Base.AKBakeMag",
|
||||
"Base.AKDrumMag",
|
||||
"Base.AKMag",
|
||||
"Base.AR180Mag",
|
||||
"Base.BetaCMag",
|
||||
"Base.BHPMag",
|
||||
"Base.BHPMagSS",
|
||||
"Base.BobcatMag",
|
||||
"Base.BuckMag",
|
||||
"Base.CalicoMag",
|
||||
"Base.ColtSMGMag",
|
||||
"Base.CZ75BMag",
|
||||
"Base.DE357Mag",
|
||||
"Base.DeltaEliteMag",
|
||||
"Base.DeltaEliteMagBlue",
|
||||
"Base.DeltaEliteMagExtend",
|
||||
"Base.DeltaEliteMagExtendBlue",
|
||||
"Base.EblocClip",
|
||||
"Base.FALMag",
|
||||
"Base.G17ExtMag",
|
||||
"Base.G17Mag",
|
||||
"Base.G17StickMag",
|
||||
"Base.G19ExtMag",
|
||||
"Base.G19Mag",
|
||||
"Base.G20Mag",
|
||||
"Base.G21Mag",
|
||||
"Base.G22Mag",
|
||||
"Base.G23Mag",
|
||||
"Base.HK91Mag",
|
||||
"Base.HKS10A357",
|
||||
"Base.HKS10A38",
|
||||
"Base.HKS22K",
|
||||
"Base.HKS29M44",
|
||||
"Base.HKS36A357",
|
||||
"Base.HKS36A38",
|
||||
"Base.HKS586A357",
|
||||
"Base.HKS586A38",
|
||||
"Base.HKSDSA38",
|
||||
"Base.HKSMK3A357",
|
||||
"Base.HKSMK3A38",
|
||||
"Base.HKSPYA357",
|
||||
"Base.HKSPYA38",
|
||||
"Base.J22Mag",
|
||||
"Base.L395KMag",
|
||||
"Base.L395KMagSlugs",
|
||||
"Base.M14Clip",
|
||||
"Base.M1CarMag",
|
||||
"Base.M249Box",
|
||||
"Base.M3GreaseMag",
|
||||
"Base.M60Box",
|
||||
"Base.MAC10Mag",
|
||||
"Base.MACGreaseMag",
|
||||
"Base.MarkMag",
|
||||
"Base.Mauserclip",
|
||||
"Base.Mini14Mag",
|
||||
"Base.Mini30Mag",
|
||||
"Base.MP5Mag",
|
||||
"Base.OfficerMag",
|
||||
"Base.P226Mag",
|
||||
"Base.P228Mag",
|
||||
"Base.P380Mag",
|
||||
"Base.P7M13Mag",
|
||||
"Base.P7M8Mag",
|
||||
"Base.P89Mag",
|
||||
"Base.P90Mag",
|
||||
"Base.PPKMag",
|
||||
"Base.R25Mag",
|
||||
"Base.Size3CompII357",
|
||||
"Base.Size3CompII38",
|
||||
"Base.Size3CompIII357",
|
||||
"Base.Size3CompIII38",
|
||||
"Base.Size4CompII357",
|
||||
"Base.Size4CompII38",
|
||||
"Base.Size4CompIII357",
|
||||
"Base.Size4CompIII38",
|
||||
"Base.Size5CompII357",
|
||||
"Base.Size5CompII38",
|
||||
"Base.Size5CompIII357",
|
||||
"Base.Size5CompIII38",
|
||||
"Base.Size6CompII357",
|
||||
"Base.Size6CompII38",
|
||||
"Base.Size6CompIII357",
|
||||
"Base.Size6CompIII38",
|
||||
"Base.SKSclip",
|
||||
"Base.SpeedStrip357",
|
||||
"Base.SpeedStrip38",
|
||||
"Base.SpeedStrip44",
|
||||
"Base.TEC9Mag",
|
||||
"Base.TherARMag",
|
||||
"Base.TherMini14Mag",
|
||||
"Base.USP40Mag",
|
||||
"Base.USP9Mag",
|
||||
"Base.UziColtSMGMag",
|
||||
"Base.UziMag",
|
||||
"Base.ValmetMag",
|
||||
"Base.Win69AMag",
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
local OFBCGuns93ModPackInjector = {
|
||||
installed = false,
|
||||
tickHookAdded = false,
|
||||
}
|
||||
|
||||
local APPLY_MODS_MODULE = "BetterContainers/Categorize/Categories/_applyMods"
|
||||
local GUNS93_PACK_PATH = "mods/guns93_items"
|
||||
|
||||
local BC_MOD_IDS = {
|
||||
"EURY_CONTAINERS",
|
||||
"REORDER_CONTAINERS",
|
||||
"\\EURY_CONTAINERS",
|
||||
"\\REORDER_CONTAINERS",
|
||||
}
|
||||
|
||||
local GUNS93_MOD_IDS = {
|
||||
"guns93",
|
||||
"\\guns93",
|
||||
}
|
||||
|
||||
local function isSortingToggleEnabled()
|
||||
local vars = SandboxVars and SandboxVars.OpinionatedFirearms
|
||||
if vars and vars.AddSorting ~= nil then
|
||||
return vars.AddSorting == true
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function getActivatedModsList()
|
||||
if type(getActivatedMods) ~= "function" then
|
||||
return nil
|
||||
end
|
||||
|
||||
local mods = getActivatedMods()
|
||||
if not mods or type(mods.contains) ~= "function" then
|
||||
return nil
|
||||
end
|
||||
|
||||
return mods
|
||||
end
|
||||
|
||||
local function isBetterContainersActive()
|
||||
local mods = getActivatedModsList()
|
||||
if not mods then
|
||||
return nil
|
||||
end
|
||||
|
||||
for i = 1, #BC_MOD_IDS do
|
||||
if mods:contains(BC_MOD_IDS[i]) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function isGuns93Active()
|
||||
local mods = getActivatedModsList()
|
||||
if not mods then
|
||||
return false
|
||||
end
|
||||
|
||||
for i = 1, #GUNS93_MOD_IDS do
|
||||
if mods:contains(GUNS93_MOD_IDS[i]) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function installApplyModsWrapper()
|
||||
if OFBCGuns93ModPackInjector.installed then
|
||||
return true
|
||||
end
|
||||
|
||||
if not isSortingToggleEnabled() then
|
||||
OFBCGuns93ModPackInjector.installed = true
|
||||
return true
|
||||
end
|
||||
|
||||
local bcState = isBetterContainersActive()
|
||||
if bcState == false then
|
||||
OFBCGuns93ModPackInjector.installed = true
|
||||
return true
|
||||
end
|
||||
if bcState == nil then
|
||||
return false
|
||||
end
|
||||
|
||||
if _G.OF_GUNS93_BC_APPLYMODS_WRAPPED then
|
||||
OFBCGuns93ModPackInjector.installed = true
|
||||
return true
|
||||
end
|
||||
|
||||
local ok, applyMods = pcall(require, APPLY_MODS_MODULE)
|
||||
if not ok or type(applyMods) ~= "function" then
|
||||
return false
|
||||
end
|
||||
|
||||
if type(package) ~= "table" or type(package.loaded) ~= "table" then
|
||||
return false
|
||||
end
|
||||
|
||||
local function wrappedApplyMods(loadCategoryFile, dlog)
|
||||
applyMods(loadCategoryFile, dlog)
|
||||
|
||||
if not isGuns93Active() then
|
||||
return
|
||||
end
|
||||
|
||||
if type(loadCategoryFile) == "function" then
|
||||
loadCategoryFile(GUNS93_PACK_PATH)
|
||||
if dlog then
|
||||
dlog("Loaded category pack guns93_items")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
package.loaded[APPLY_MODS_MODULE] = wrappedApplyMods
|
||||
_G.OF_GUNS93_BC_APPLYMODS_WRAPPED = true
|
||||
OFBCGuns93ModPackInjector.installed = true
|
||||
return true
|
||||
end
|
||||
|
||||
local function tryInstallOnTick()
|
||||
if installApplyModsWrapper() then
|
||||
OFBCGuns93ModPackInjector.tickHookAdded = false
|
||||
Events.OnTick.Remove(tryInstallOnTick)
|
||||
end
|
||||
end
|
||||
|
||||
local function ensureTickHook()
|
||||
if OFBCGuns93ModPackInjector.tickHookAdded then
|
||||
return
|
||||
end
|
||||
|
||||
OFBCGuns93ModPackInjector.tickHookAdded = true
|
||||
Events.OnTick.Add(tryInstallOnTick)
|
||||
end
|
||||
|
||||
if not installApplyModsWrapper() then
|
||||
ensureTickHook()
|
||||
end
|
||||
|
||||
if Events.OnMainMenuEnter and type(Events.OnMainMenuEnter.Add) == "function" then
|
||||
Events.OnMainMenuEnter.Add(installApplyModsWrapper)
|
||||
end
|
||||
@@ -0,0 +1,97 @@
|
||||
local OFGGSCasingDisablePatch = {
|
||||
patched = false,
|
||||
hasDebugStackInfo = type(debug) == "table" and type(debug.getinfo) == "function",
|
||||
}
|
||||
|
||||
local BLOCKED_GGS_CASING_TYPES = {
|
||||
["Base.pistol_casing"] = true,
|
||||
["Base.revolver_casing"] = true,
|
||||
["Base.rifle_casing"] = true,
|
||||
["Base.shells_casing"] = true,
|
||||
}
|
||||
|
||||
local function isGgsShellEmitterCall()
|
||||
if not OFGGSCasingDisablePatch.hasDebugStackInfo then
|
||||
return false
|
||||
end
|
||||
|
||||
for stackLevel = 3, 10 do
|
||||
local info = debug.getinfo(stackLevel, "S")
|
||||
if not info then
|
||||
break
|
||||
end
|
||||
|
||||
local source = info.source
|
||||
if type(source) == "string" and string.find(source, "GGS_ShellCasingEmitter.lua", 1, true) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function isPatchToggleEnabled()
|
||||
local vars = SandboxVars and SandboxVars.OpinionatedFirearms
|
||||
if vars and vars.HandleHotBrassCasingSpawnUseAmmoMaker ~= nil then
|
||||
return vars.HandleHotBrassCasingSpawnUseAmmoMaker == true
|
||||
end
|
||||
|
||||
-- Backward compatibility for existing worlds.
|
||||
if vars and vars.HBVCEFAmmoMakerPatch ~= nil then
|
||||
return vars.HBVCEFAmmoMakerPatch == true
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function applyPatch()
|
||||
if OFGGSCasingDisablePatch.patched then
|
||||
return true
|
||||
end
|
||||
|
||||
if not isPatchToggleEnabled() then
|
||||
OFGGSCasingDisablePatch.patched = true
|
||||
return true
|
||||
end
|
||||
|
||||
if type(__classmetatables) ~= "table" then
|
||||
return false
|
||||
end
|
||||
|
||||
if not zombie or not zombie.iso or not zombie.iso.IsoGridSquare or not zombie.iso.IsoGridSquare.class then
|
||||
return false
|
||||
end
|
||||
|
||||
local squareMetatable = __classmetatables[zombie.iso.IsoGridSquare.class]
|
||||
if not squareMetatable or type(squareMetatable.__index) ~= "table" then
|
||||
return false
|
||||
end
|
||||
|
||||
local originalAddWorldInventoryItem = squareMetatable.__index.AddWorldInventoryItem
|
||||
if type(originalAddWorldInventoryItem) ~= "function" then
|
||||
return false
|
||||
end
|
||||
|
||||
squareMetatable.__index.AddWorldInventoryItem = function(square, itemType, ...)
|
||||
if BLOCKED_GGS_CASING_TYPES[itemType] and
|
||||
(not OFGGSCasingDisablePatch.hasDebugStackInfo or isGgsShellEmitterCall())
|
||||
then
|
||||
return nil
|
||||
end
|
||||
|
||||
return originalAddWorldInventoryItem(square, itemType, ...)
|
||||
end
|
||||
|
||||
OFGGSCasingDisablePatch.patched = true
|
||||
return true
|
||||
end
|
||||
|
||||
local function tryPatchOnTick()
|
||||
if applyPatch() then
|
||||
Events.OnTick.Remove(tryPatchOnTick)
|
||||
end
|
||||
end
|
||||
|
||||
if not applyPatch() then
|
||||
Events.OnTick.Add(tryPatchOnTick)
|
||||
end
|
||||
@@ -0,0 +1,221 @@
|
||||
local OFHotBrassPatch = {
|
||||
patched = false,
|
||||
}
|
||||
|
||||
local AMMO_TYPE_ALIAS_BY_ITEM = {
|
||||
["Base.9x39Bullets"] = "9x39",
|
||||
["Base.Bullets22LR"] = "22LR",
|
||||
["Base.Bullets50Magnum"] = "50AE",
|
||||
["Base.762x54rBullets"] = "762x54R",
|
||||
["Base.792x57Bullets"] = "792x57Maus",
|
||||
-- Common spelling variants seen in third-party weapon scripts.
|
||||
["Base.308Bulets"] = "308Win",
|
||||
["Base.762x54rBulets"] = "762x54R",
|
||||
}
|
||||
|
||||
local function isPatchToggleEnabled()
|
||||
local vars = SandboxVars and SandboxVars.OpinionatedFirearms
|
||||
if vars and vars.HandleHotBrassCasingSpawnUseAmmoMaker ~= nil then
|
||||
return vars.HandleHotBrassCasingSpawnUseAmmoMaker == true
|
||||
end
|
||||
|
||||
-- Backward compatibility for existing worlds.
|
||||
if vars and vars.HBVCEFAmmoMakerPatch ~= nil then
|
||||
return vars.HBVCEFAmmoMakerPatch == true
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function isSessionEligible()
|
||||
return isPatchToggleEnabled()
|
||||
end
|
||||
|
||||
local function getAmmoMakerFiredFromAmmoDataKey(ammoDataKey)
|
||||
if type(ammoDataKey) ~= "string" or ammoDataKey == "" then
|
||||
return nil
|
||||
end
|
||||
|
||||
if type(ammoMakerAmmoData) ~= "table" or type(ammoMakerAmmoParts) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
|
||||
local ammoData = ammoMakerAmmoData[ammoDataKey]
|
||||
if type(ammoData) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
|
||||
local casingType = ammoData.casingType
|
||||
if type(casingType) ~= "string" or casingType == "" then
|
||||
return nil
|
||||
end
|
||||
|
||||
local partData = ammoMakerAmmoParts[casingType]
|
||||
if type(partData) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
|
||||
local firedType = partData.partFired or partData.partOld
|
||||
if type(firedType) ~= "string" or firedType == "" then
|
||||
return nil
|
||||
end
|
||||
|
||||
return firedType
|
||||
end
|
||||
|
||||
local function getAmmoMakerFiredFromItemKey(ammoType)
|
||||
if type(ammoType) ~= "string" or ammoType == "" then
|
||||
return nil
|
||||
end
|
||||
|
||||
if type(ammoMakerAmmoTypes) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
|
||||
local typeData = ammoMakerAmmoTypes[ammoType]
|
||||
if type(typeData) ~= "table" or type(typeData.ammoTypes) ~= "table" or #typeData.ammoTypes == 0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
local activeIndex = 1
|
||||
if type(typeData.modIds) == "table" and type(ammoMakerCompatibleMods) == "table" then
|
||||
for i = 1, #typeData.modIds do
|
||||
local modId = typeData.modIds[i]
|
||||
if ammoMakerCompatibleMods[modId] == true then
|
||||
activeIndex = i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if activeIndex < 1 or activeIndex > #typeData.ammoTypes then
|
||||
activeIndex = 1
|
||||
end
|
||||
|
||||
local activeAmmoKey = typeData.ammoTypes[activeIndex]
|
||||
local activeFiredType = getAmmoMakerFiredFromAmmoDataKey(activeAmmoKey)
|
||||
if activeFiredType then
|
||||
return activeFiredType
|
||||
end
|
||||
|
||||
for i = 1, #typeData.ammoTypes do
|
||||
local fallbackFiredType = getAmmoMakerFiredFromAmmoDataKey(typeData.ammoTypes[i])
|
||||
if fallbackFiredType then
|
||||
return fallbackFiredType
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function getAmmoMakerFiredCasing(ammoType)
|
||||
if type(ammoType) ~= "string" or ammoType == "" then
|
||||
return nil
|
||||
end
|
||||
|
||||
local directFiredType = getAmmoMakerFiredFromItemKey(ammoType)
|
||||
if directFiredType then
|
||||
return directFiredType
|
||||
end
|
||||
|
||||
local aliasAmmoDataKey = AMMO_TYPE_ALIAS_BY_ITEM[ammoType]
|
||||
if aliasAmmoDataKey then
|
||||
local aliasFiredType = getAmmoMakerFiredFromAmmoDataKey(aliasAmmoDataKey)
|
||||
if aliasFiredType then
|
||||
return aliasFiredType
|
||||
end
|
||||
end
|
||||
|
||||
local lowerAmmoType = string.lower(ammoType)
|
||||
for aliasItemType, ammoDataKey in pairs(AMMO_TYPE_ALIAS_BY_ITEM) do
|
||||
if string.lower(aliasItemType) == lowerAmmoType then
|
||||
local aliasFiredType = getAmmoMakerFiredFromAmmoDataKey(ammoDataKey)
|
||||
if aliasFiredType then
|
||||
return aliasFiredType
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if type(ammoMakerAmmoTypes) == "table" then
|
||||
for itemType, typeData in pairs(ammoMakerAmmoTypes) do
|
||||
if type(itemType) == "string" and string.lower(itemType) == lowerAmmoType then
|
||||
if type(typeData) == "table" and type(typeData.ammoTypes) == "table" then
|
||||
for i = 1, #typeData.ammoTypes do
|
||||
local fallbackFiredType = getAmmoMakerFiredFromAmmoDataKey(typeData.ammoTypes[i])
|
||||
if fallbackFiredType then
|
||||
return fallbackFiredType
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function applyPatch()
|
||||
if OFHotBrassPatch.patched then
|
||||
return true
|
||||
end
|
||||
|
||||
if not isSessionEligible() then
|
||||
return false
|
||||
end
|
||||
|
||||
if type(SpentCasingPhysics) ~= "table" then
|
||||
return false
|
||||
end
|
||||
|
||||
if type(SpentCasingPhysics.getItemToEject) ~= "function" then
|
||||
return false
|
||||
end
|
||||
|
||||
if type(SpentCasingPhysics.doSpawnCasing) ~= "function" then
|
||||
return false
|
||||
end
|
||||
|
||||
local originalGetItemToEject = SpentCasingPhysics.getItemToEject
|
||||
local originalDoSpawnCasing = SpentCasingPhysics.doSpawnCasing
|
||||
|
||||
SpentCasingPhysics.getItemToEject = function(ammoType)
|
||||
local mappedType = getAmmoMakerFiredCasing(ammoType)
|
||||
if mappedType then
|
||||
return mappedType
|
||||
end
|
||||
|
||||
return originalGetItemToEject(ammoType)
|
||||
end
|
||||
|
||||
SpentCasingPhysics.doSpawnCasing = function(player, weapon, params, racking, optionalItem)
|
||||
if not optionalItem and weapon and weapon.getAmmoType then
|
||||
local ammoTypeObj = weapon:getAmmoType()
|
||||
local ammoType = ammoTypeObj and ammoTypeObj.getItemKey and ammoTypeObj:getItemKey() or ammoTypeObj
|
||||
if ammoType then
|
||||
local mappedType = getAmmoMakerFiredCasing(tostring(ammoType))
|
||||
if mappedType then
|
||||
optionalItem = mappedType
|
||||
if racking then
|
||||
-- Force rack ejects to stay as empties for this patch.
|
||||
racking = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return originalDoSpawnCasing(player, weapon, params, racking, optionalItem)
|
||||
end
|
||||
|
||||
OFHotBrassPatch.patched = true
|
||||
return true
|
||||
end
|
||||
|
||||
local function tryPatchOnTick()
|
||||
if OFHotBrassPatch.patched then
|
||||
Events.OnTick.Remove(tryPatchOnTick)
|
||||
return
|
||||
end
|
||||
|
||||
applyPatch()
|
||||
end
|
||||
|
||||
Events.OnTick.Add(tryPatchOnTick)
|
||||
@@ -0,0 +1,75 @@
|
||||
local BLOCKED_AMMOMAKER_COMMANDS = {
|
||||
returnCasingsOnAttack = true,
|
||||
incrementSpendtRoundCount = true,
|
||||
returnCasingsOnReload = true,
|
||||
}
|
||||
|
||||
local function isPatchToggleEnabled()
|
||||
local vars = SandboxVars and SandboxVars.OpinionatedFirearms
|
||||
if vars and vars.HandleHotBrassCasingSpawnUseAmmoMaker ~= nil then
|
||||
return vars.HandleHotBrassCasingSpawnUseAmmoMaker == true
|
||||
end
|
||||
|
||||
-- Backward compatibility for existing worlds.
|
||||
if vars and vars.HBVCEFAmmoMakerPatch ~= nil then
|
||||
return vars.HBVCEFAmmoMakerPatch == true
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function shouldBlockAmmoMakerCasingCommands()
|
||||
return isPatchToggleEnabled()
|
||||
end
|
||||
|
||||
local function getModuleAndCommand(...)
|
||||
local argc = select("#", ...)
|
||||
if argc < 2 then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local firstArg = select(1, ...)
|
||||
if type(firstArg) == "string" then
|
||||
return firstArg, select(2, ...)
|
||||
end
|
||||
|
||||
if argc >= 3 then
|
||||
return select(2, ...), select(3, ...)
|
||||
end
|
||||
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local function installCommandGate()
|
||||
if _G.OpinionatedFirearms_AmmoMakerCommandGateInstalled then
|
||||
return true
|
||||
end
|
||||
|
||||
if type(sendClientCommand) ~= "function" then
|
||||
return false
|
||||
end
|
||||
|
||||
_G.OpinionatedFirearms_AmmoMakerCommandGateInstalled = true
|
||||
_G.OpinionatedFirearms_OriginalSendClientCommand = sendClientCommand
|
||||
|
||||
sendClientCommand = function(...)
|
||||
local module, command = getModuleAndCommand(...)
|
||||
if module == "ammomaker" and BLOCKED_AMMOMAKER_COMMANDS[command] and shouldBlockAmmoMakerCasingCommands() then
|
||||
return
|
||||
end
|
||||
|
||||
return _G.OpinionatedFirearms_OriginalSendClientCommand(...)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function ensureGateOnTick()
|
||||
if installCommandGate() then
|
||||
Events.OnTick.Remove(ensureGateOnTick)
|
||||
end
|
||||
end
|
||||
|
||||
if not installCommandGate() then
|
||||
Events.OnTick.Add(ensureGateOnTick)
|
||||
end
|
||||
@@ -0,0 +1,141 @@
|
||||
local OFAmmoMakerDisplayCategoryPatch = {
|
||||
patched = false,
|
||||
tickHookAdded = false,
|
||||
}
|
||||
|
||||
local SPENT_CASINGS_CATEGORY = "OFSpentCasings"
|
||||
local AMMO_PART_CATEGORY = "OFAmmoPart"
|
||||
|
||||
local function isSortingToggleEnabled()
|
||||
local vars = SandboxVars and SandboxVars.OpinionatedFirearms
|
||||
if vars and vars.AddSorting ~= nil then
|
||||
return vars.AddSorting == true
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function addAmmoMakerType(typeSet, fullType)
|
||||
if type(fullType) ~= "string" or fullType == "" then
|
||||
return
|
||||
end
|
||||
|
||||
if not string.find(fullType, "^ammomaker%.") then
|
||||
return
|
||||
end
|
||||
|
||||
typeSet[fullType] = true
|
||||
end
|
||||
|
||||
local function setDisplayCategory(scriptManager, fullType, category)
|
||||
local scriptItem = scriptManager:FindItem(fullType)
|
||||
if not scriptItem then
|
||||
return false
|
||||
end
|
||||
|
||||
if type(scriptItem.DoParam) == "function" then
|
||||
scriptItem:DoParam("DisplayCategory = " .. category)
|
||||
return true
|
||||
end
|
||||
|
||||
if type(scriptItem.setDisplayCategory) == "function" then
|
||||
scriptItem:setDisplayCategory(category)
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function collectAmmoMakerTypes()
|
||||
if type(ammoMakerAmmoParts) ~= "table" then
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local spentCasingTypes = {}
|
||||
local ammoPartTypes = {}
|
||||
|
||||
for _, partData in pairs(ammoMakerAmmoParts) do
|
||||
if type(partData) == "table" then
|
||||
local partClass = partData.partClass
|
||||
if partClass == "Casing" or partClass == "Hull" then
|
||||
addAmmoMakerType(spentCasingTypes, partData.partOld)
|
||||
addAmmoMakerType(spentCasingTypes, partData.partFired)
|
||||
addAmmoMakerType(ammoPartTypes, partData.boxType)
|
||||
addAmmoMakerType(ammoPartTypes, partData.bagType)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return spentCasingTypes, ammoPartTypes
|
||||
end
|
||||
|
||||
local function applyCategory(scriptManager, itemTypes, category)
|
||||
local patchedAny = false
|
||||
|
||||
for fullType in pairs(itemTypes) do
|
||||
if setDisplayCategory(scriptManager, fullType, category) then
|
||||
patchedAny = true
|
||||
end
|
||||
end
|
||||
|
||||
return patchedAny
|
||||
end
|
||||
|
||||
local function applyPatch()
|
||||
if OFAmmoMakerDisplayCategoryPatch.patched then
|
||||
return true
|
||||
end
|
||||
|
||||
if not isSortingToggleEnabled() then
|
||||
OFAmmoMakerDisplayCategoryPatch.patched = true
|
||||
return true
|
||||
end
|
||||
|
||||
local scriptManager = ScriptManager and ScriptManager.instance
|
||||
if not scriptManager or type(scriptManager.FindItem) ~= "function" then
|
||||
return false
|
||||
end
|
||||
|
||||
local spentCasingTypes, ammoPartTypes = collectAmmoMakerTypes()
|
||||
if not spentCasingTypes or not ammoPartTypes then
|
||||
return false
|
||||
end
|
||||
|
||||
local patchedSpentCasings = applyCategory(scriptManager, spentCasingTypes, SPENT_CASINGS_CATEGORY)
|
||||
local patchedAmmoParts = applyCategory(scriptManager, ammoPartTypes, AMMO_PART_CATEGORY)
|
||||
if not patchedSpentCasings and not patchedAmmoParts then
|
||||
return false
|
||||
end
|
||||
|
||||
OFAmmoMakerDisplayCategoryPatch.patched = true
|
||||
return true
|
||||
end
|
||||
|
||||
local function tryPatchOnTick()
|
||||
if applyPatch() then
|
||||
OFAmmoMakerDisplayCategoryPatch.tickHookAdded = false
|
||||
Events.OnTick.Remove(tryPatchOnTick)
|
||||
end
|
||||
end
|
||||
|
||||
local function ensureTickHook()
|
||||
if OFAmmoMakerDisplayCategoryPatch.tickHookAdded then
|
||||
return
|
||||
end
|
||||
|
||||
OFAmmoMakerDisplayCategoryPatch.tickHookAdded = true
|
||||
Events.OnTick.Add(tryPatchOnTick)
|
||||
end
|
||||
|
||||
if not applyPatch() then
|
||||
ensureTickHook()
|
||||
end
|
||||
|
||||
if Events.OnMainMenuEnter and type(Events.OnMainMenuEnter.Add) == "function" then
|
||||
Events.OnMainMenuEnter.Add(function()
|
||||
OFAmmoMakerDisplayCategoryPatch.patched = false
|
||||
if not applyPatch() then
|
||||
ensureTickHook()
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,920 @@
|
||||
local OFGuns93DisplayCategoryPatch = {
|
||||
patched = false,
|
||||
tickHookAdded = false,
|
||||
repatchQueued = false,
|
||||
}
|
||||
|
||||
local BC_FIREARM_CATEGORY = "WepFire"
|
||||
local BC_MAGAZINE_CATEGORY = "WepAmmoMag"
|
||||
|
||||
local GUNS93_SENTINEL_FIREARM = "Base.83Cheetah"
|
||||
local GUNS93_SENTINEL_MAGAZINE = "Base.83Mag"
|
||||
local GAEL_GUNSTORE_SENTINEL_FIREARM = "Base.A91"
|
||||
local GAEL_GUNSTORE_SENTINEL_MAGAZINE = "Base.545x39Clip30"
|
||||
|
||||
local function resolveWeaponCategories()
|
||||
return BC_FIREARM_CATEGORY, BC_MAGAZINE_CATEGORY
|
||||
end
|
||||
|
||||
local function isSortingToggleEnabled()
|
||||
local vars = SandboxVars and SandboxVars.OpinionatedFirearms
|
||||
if vars and vars.AddSorting ~= nil then
|
||||
return vars.AddSorting == true
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local GUNS93_FIREARMS = {
|
||||
["Base.83Cheetah"] = true,
|
||||
["Base.AKM"] = true,
|
||||
["Base.AKSport"] = true,
|
||||
["Base.Anaconda"] = true,
|
||||
["Base.AR15"] = true,
|
||||
["Base.AR180"] = true,
|
||||
["Base.AssaultRifle"] = true,
|
||||
["Base.AssaultRifle2"] = true,
|
||||
["Base.Auto5"] = true,
|
||||
["Base.Auto5_Alt"] = true,
|
||||
["Base.BenelliM3"] = true,
|
||||
["Base.BenelliM3_Alt"] = true,
|
||||
["Base.Beretta682"] = true,
|
||||
["Base.Beretta682_Alt"] = true,
|
||||
["Base.Beretta92FS"] = true,
|
||||
["Base.BHP"] = true,
|
||||
["Base.Bobcat"] = true,
|
||||
["Base.Brown3006BAR"] = true,
|
||||
["Base.Brown308BAR"] = true,
|
||||
["Base.BrownCitori"] = true,
|
||||
["Base.BrownCitori_Alt"] = true,
|
||||
["Base.Buckmark"] = true,
|
||||
["Base.CalicoPistol"] = true,
|
||||
["Base.CalicoRifle"] = true,
|
||||
["Base.CAR15"] = true,
|
||||
["Base.Colt9mm"] = true,
|
||||
["Base.ColtArmy"] = true,
|
||||
["Base.ColtDet"] = true,
|
||||
["Base.Command1911"] = true,
|
||||
["Base.CZ75B"] = true,
|
||||
["Base.DE357"] = true,
|
||||
["Base.DeltaElite"] = true,
|
||||
["Base.Dissy"] = true,
|
||||
["Base.DoubleBarrelShotgun"] = true,
|
||||
["Base.DoubleBarrelShotgun_Alt"] = true,
|
||||
["Base.DoubleBarrelShotgunSawnoff"] = true,
|
||||
["Base.DoubleBarrelShotgunSawnoff_Alt"] = true,
|
||||
["Base.FAL"] = true,
|
||||
["Base.Glock17"] = true,
|
||||
["Base.Glock17L"] = true,
|
||||
["Base.Glock19"] = true,
|
||||
["Base.Glock20"] = true,
|
||||
["Base.Glock21"] = true,
|
||||
["Base.Glock22"] = true,
|
||||
["Base.Glock23"] = true,
|
||||
["Base.Glock24"] = true,
|
||||
["Base.Gov1911"] = true,
|
||||
["Base.GP100"] = true,
|
||||
["Base.GP100_Alt"] = true,
|
||||
["Base.GP101"] = true,
|
||||
["Base.GP101_Alt"] = true,
|
||||
["Base.Hammerless"] = true,
|
||||
["Base.HBAR"] = true,
|
||||
["Base.HK91"] = true,
|
||||
["Base.HuntingRifle"] = true,
|
||||
["Base.Ithaca37"] = true,
|
||||
["Base.Ithaca37_Alt"] = true,
|
||||
["Base.Ithaca37Riot"] = true,
|
||||
["Base.Ithaca37Riot_Alt"] = true,
|
||||
["Base.J22"] = true,
|
||||
["Base.Javelina"] = true,
|
||||
["Base.L395K"] = true,
|
||||
["Base.L395K_Alt"] = true,
|
||||
["Base.Longslide"] = true,
|
||||
["Base.M1903"] = true,
|
||||
["Base.M1917"] = true,
|
||||
["Base.M1A"] = true,
|
||||
["Base.M1ABush"] = true,
|
||||
["Base.M1Carbine"] = true,
|
||||
["Base.M1Garand"] = true,
|
||||
["Base.M24"] = true,
|
||||
["Base.M249"] = true,
|
||||
["Base.M3GreaseGun"] = true,
|
||||
["Base.M590"] = true,
|
||||
["Base.M590_Alt"] = true,
|
||||
["Base.M60"] = true,
|
||||
["Base.M635"] = true,
|
||||
["Base.M723"] = true,
|
||||
["Base.M727"] = true,
|
||||
["Base.M733"] = true,
|
||||
["Base.M870"] = true,
|
||||
["Base.M870_Alt"] = true,
|
||||
["Base.MAC10"] = true,
|
||||
["Base.Marlin3363030"] = true,
|
||||
["Base.Marlin33644"] = true,
|
||||
["Base.Marlin39A"] = true,
|
||||
["Base.Marlin45"] = true,
|
||||
["Base.Marlin60"] = true,
|
||||
["Base.Marlin9"] = true,
|
||||
["Base.Mauser98K"] = true,
|
||||
["Base.Mini14"] = true,
|
||||
["Base.Mini30"] = true,
|
||||
["Base.Moss500"] = true,
|
||||
["Base.Moss500_Alt"] = true,
|
||||
["Base.Moss590"] = true,
|
||||
["Base.Moss590_Alt"] = true,
|
||||
["Base.MP5"] = true,
|
||||
["Base.Officer1911"] = true,
|
||||
["Base.P226"] = true,
|
||||
["Base.P228"] = true,
|
||||
["Base.P380"] = true,
|
||||
["Base.P7M13"] = true,
|
||||
["Base.P7M8"] = true,
|
||||
["Base.Pistol"] = true,
|
||||
["Base.Pistol2"] = true,
|
||||
["Base.Pistol3"] = true,
|
||||
["Base.PPK"] = true,
|
||||
["Base.Python"] = true,
|
||||
["Base.Python_Alt"] = true,
|
||||
["Base.R223Rem788"] = true,
|
||||
["Base.R223RugM77"] = true,
|
||||
["Base.R3006Rem700"] = true,
|
||||
["Base.R3006RugM77"] = true,
|
||||
["Base.R3030Rem788"] = true,
|
||||
["Base.R308Rem700"] = true,
|
||||
["Base.R308RugM77"] = true,
|
||||
["Base.R44Rem788"] = true,
|
||||
["Base.Raven25"] = true,
|
||||
["Base.Rem1100"] = true,
|
||||
["Base.Rem1100_Alt"] = true,
|
||||
["Base.Rem1187P"] = true,
|
||||
["Base.Rem1187P_Alt"] = true,
|
||||
["Base.Rem223760"] = true,
|
||||
["Base.Rem3006760"] = true,
|
||||
["Base.Rem308760"] = true,
|
||||
["Base.Rem870P"] = true,
|
||||
["Base.Rem870P_Alt"] = true,
|
||||
["Base.Revolver"] = true,
|
||||
["Base.Revolver_Long"] = true,
|
||||
["Base.Revolver_Short"] = true,
|
||||
["Base.Rug1022"] = true,
|
||||
["Base.Rugmark"] = true,
|
||||
["Base.RugP89"] = true,
|
||||
["Base.RugP90"] = true,
|
||||
["Base.SavageFox"] = true,
|
||||
["Base.SavageFox_Alt"] = true,
|
||||
["Base.SecuritySix"] = true,
|
||||
["Base.SecuritySix_Alt"] = true,
|
||||
["Base.Shotgun"] = true,
|
||||
["Base.Shotgun_Alt"] = true,
|
||||
["Base.ShotgunSawnoff"] = true,
|
||||
["Base.ShotgunSawnoff_Alt"] = true,
|
||||
["Base.SKS"] = true,
|
||||
["Base.SOAuto5"] = true,
|
||||
["Base.SOAuto5_Alt"] = true,
|
||||
["Base.SOBeretta682"] = true,
|
||||
["Base.SOBeretta682_Alt"] = true,
|
||||
["Base.SOBrownCitori"] = true,
|
||||
["Base.SOBrownCitori_Alt"] = true,
|
||||
["Base.SOIthaca37"] = true,
|
||||
["Base.SOIthaca37_Alt"] = true,
|
||||
["Base.SOL395K"] = true,
|
||||
["Base.SOL395K_Alt"] = true,
|
||||
["Base.SOM24"] = true,
|
||||
["Base.SOMoss500"] = true,
|
||||
["Base.SOMoss500_Alt"] = true,
|
||||
["Base.SOR223Rem700"] = true,
|
||||
["Base.SOR223Rem788"] = true,
|
||||
["Base.SOR3006Rem700"] = true,
|
||||
["Base.SOR3030Rem788"] = true,
|
||||
["Base.SOR308Rem700"] = true,
|
||||
["Base.SOR308Rem788"] = true,
|
||||
["Base.SOR44Rem788"] = true,
|
||||
["Base.SORem1100"] = true,
|
||||
["Base.SORem1100_Alt"] = true,
|
||||
["Base.SOSavageFox"] = true,
|
||||
["Base.SOSavageFox_Alt"] = true,
|
||||
["Base.SOW223WinM70"] = true,
|
||||
["Base.SOW3006WinM70"] = true,
|
||||
["Base.SOW308WinM70"] = true,
|
||||
["Base.SOWin1200"] = true,
|
||||
["Base.SOWin1200_Alt"] = true,
|
||||
["Base.SOWin1400"] = true,
|
||||
["Base.SOWin1400_Alt"] = true,
|
||||
["Base.SOWin1912"] = true,
|
||||
["Base.SOWin1912_Alt"] = true,
|
||||
["Base.SOWin37"] = true,
|
||||
["Base.SOWin37_Alt"] = true,
|
||||
["Base.SPAS12"] = true,
|
||||
["Base.SPAS12_Alt"] = true,
|
||||
["Base.SW10"] = true,
|
||||
["Base.SW17"] = true,
|
||||
["Base.SW4006"] = true,
|
||||
["Base.SW4506"] = true,
|
||||
["Base.SW586"] = true,
|
||||
["Base.SW586_Alt"] = true,
|
||||
["Base.SW5906"] = true,
|
||||
["Base.SW65"] = true,
|
||||
["Base.SW65_Alt"] = true,
|
||||
["Base.T56"] = true,
|
||||
["Base.TEC9"] = true,
|
||||
["Base.USP40"] = true,
|
||||
["Base.USP9"] = true,
|
||||
["Base.Uzi"] = true,
|
||||
["Base.Valmet"] = true,
|
||||
["Base.VarmintRifle"] = true,
|
||||
["Base.W223WinM70"] = true,
|
||||
["Base.W3006WinM70"] = true,
|
||||
["Base.W308WinM70"] = true,
|
||||
["Base.Win1200"] = true,
|
||||
["Base.Win1200_Alt"] = true,
|
||||
["Base.Win1200Def"] = true,
|
||||
["Base.Win1200Def_Alt"] = true,
|
||||
["Base.Win1400"] = true,
|
||||
["Base.Win1400_Alt"] = true,
|
||||
["Base.Win1912"] = true,
|
||||
["Base.Win1912_Alt"] = true,
|
||||
["Base.Win30067400"] = true,
|
||||
["Base.Win3087400"] = true,
|
||||
["Base.Win37"] = true,
|
||||
["Base.Win37_Alt"] = true,
|
||||
["Base.Win61"] = true,
|
||||
["Base.Win69"] = true,
|
||||
["Base.Win943030"] = true,
|
||||
["Base.Win94357"] = true,
|
||||
["Base.Win94357_Alt"] = true,
|
||||
["Base.Win9445"] = true,
|
||||
}
|
||||
|
||||
local GUNS93_MAGAZINES = {
|
||||
["Base.100CalicoMag"] = true,
|
||||
["Base.1022Mag"] = true,
|
||||
["Base.10M14Mag"] = true,
|
||||
["Base.15BHPMag"] = true,
|
||||
["Base.17BHPMag"] = true,
|
||||
["Base.1908Mag"] = true,
|
||||
["Base.1911MagExtend"] = true,
|
||||
["Base.1911MagExtendSS"] = true,
|
||||
["Base.1911MagSS"] = true,
|
||||
["Base.20M16AR180Mag"] = true,
|
||||
["Base.20M16Mag"] = true,
|
||||
["Base.20M9Mag"] = true,
|
||||
["Base.20MP5Mag"] = true,
|
||||
["Base.223Rem760Mag"] = true,
|
||||
["Base.223RemMag"] = true,
|
||||
["Base.25Rug1022Mag"] = true,
|
||||
["Base.25UziColtSMGMag"] = true,
|
||||
["Base.25UziMag"] = true,
|
||||
["Base.3006BARMag"] = true,
|
||||
["Base.3006clip"] = true,
|
||||
["Base.3006Rem760Mag"] = true,
|
||||
["Base.3006WinMag"] = true,
|
||||
["Base.3030RemMag"] = true,
|
||||
["Base.308BARMag"] = true,
|
||||
["Base.308Clip"] = true,
|
||||
["Base.308Rem760Mag"] = true,
|
||||
["Base.308WinMag"] = true,
|
||||
["Base.30M14Mag"] = true,
|
||||
["Base.30M16AR180Mag"] = true,
|
||||
["Base.30M1CarMag"] = true,
|
||||
["Base.30Mini14Mag"] = true,
|
||||
["Base.4006Mag"] = true,
|
||||
["Base.40AKMag"] = true,
|
||||
["Base.40AR180Mag"] = true,
|
||||
["Base.40M16AR180Mag"] = true,
|
||||
["Base.44Clip"] = true,
|
||||
["Base.44RemMag"] = true,
|
||||
["Base.4506Mag"] = true,
|
||||
["Base.45Clip"] = true,
|
||||
["Base.45Moonclip"] = true,
|
||||
["Base.556Clip"] = true,
|
||||
["Base.5906Mag"] = true,
|
||||
["Base.83Mag"] = true,
|
||||
["Base.9mmClip"] = true,
|
||||
["Base.AK5Mag"] = true,
|
||||
["Base.AKBakeMag"] = true,
|
||||
["Base.AKDrumMag"] = true,
|
||||
["Base.AKMag"] = true,
|
||||
["Base.AR180Mag"] = true,
|
||||
["Base.BetaCMag"] = true,
|
||||
["Base.BHPMag"] = true,
|
||||
["Base.BHPMagSS"] = true,
|
||||
["Base.BobcatMag"] = true,
|
||||
["Base.BuckMag"] = true,
|
||||
["Base.CalicoMag"] = true,
|
||||
["Base.ColtSMGMag"] = true,
|
||||
["Base.CZ75BMag"] = true,
|
||||
["Base.DE357Mag"] = true,
|
||||
["Base.DeltaEliteMag"] = true,
|
||||
["Base.DeltaEliteMagBlue"] = true,
|
||||
["Base.DeltaEliteMagExtend"] = true,
|
||||
["Base.DeltaEliteMagExtendBlue"] = true,
|
||||
["Base.EblocClip"] = true,
|
||||
["Base.FALMag"] = true,
|
||||
["Base.G17ExtMag"] = true,
|
||||
["Base.G17Mag"] = true,
|
||||
["Base.G17StickMag"] = true,
|
||||
["Base.G19ExtMag"] = true,
|
||||
["Base.G19Mag"] = true,
|
||||
["Base.G20Mag"] = true,
|
||||
["Base.G21Mag"] = true,
|
||||
["Base.G22Mag"] = true,
|
||||
["Base.G23Mag"] = true,
|
||||
["Base.HK91Mag"] = true,
|
||||
["Base.HKS10A357"] = true,
|
||||
["Base.HKS10A38"] = true,
|
||||
["Base.HKS22K"] = true,
|
||||
["Base.HKS29M44"] = true,
|
||||
["Base.HKS36A357"] = true,
|
||||
["Base.HKS36A38"] = true,
|
||||
["Base.HKS586A357"] = true,
|
||||
["Base.HKS586A38"] = true,
|
||||
["Base.HKSDSA38"] = true,
|
||||
["Base.HKSMK3A357"] = true,
|
||||
["Base.HKSMK3A38"] = true,
|
||||
["Base.HKSPYA357"] = true,
|
||||
["Base.HKSPYA38"] = true,
|
||||
["Base.J22Mag"] = true,
|
||||
["Base.L395KMag"] = true,
|
||||
["Base.L395KMagSlugs"] = true,
|
||||
["Base.M14Clip"] = true,
|
||||
["Base.M1CarMag"] = true,
|
||||
["Base.M249Box"] = true,
|
||||
["Base.M3GreaseMag"] = true,
|
||||
["Base.M60Box"] = true,
|
||||
["Base.MAC10Mag"] = true,
|
||||
["Base.MACGreaseMag"] = true,
|
||||
["Base.MarkMag"] = true,
|
||||
["Base.Mauserclip"] = true,
|
||||
["Base.Mini14Mag"] = true,
|
||||
["Base.Mini30Mag"] = true,
|
||||
["Base.MP5Mag"] = true,
|
||||
["Base.OfficerMag"] = true,
|
||||
["Base.P226Mag"] = true,
|
||||
["Base.P228Mag"] = true,
|
||||
["Base.P380Mag"] = true,
|
||||
["Base.P7M13Mag"] = true,
|
||||
["Base.P7M8Mag"] = true,
|
||||
["Base.P89Mag"] = true,
|
||||
["Base.P90Mag"] = true,
|
||||
["Base.PPKMag"] = true,
|
||||
["Base.R25Mag"] = true,
|
||||
["Base.Size3CompII357"] = true,
|
||||
["Base.Size3CompII38"] = true,
|
||||
["Base.Size3CompIII357"] = true,
|
||||
["Base.Size3CompIII38"] = true,
|
||||
["Base.Size4CompII357"] = true,
|
||||
["Base.Size4CompII38"] = true,
|
||||
["Base.Size4CompIII357"] = true,
|
||||
["Base.Size4CompIII38"] = true,
|
||||
["Base.Size5CompII357"] = true,
|
||||
["Base.Size5CompII38"] = true,
|
||||
["Base.Size5CompIII357"] = true,
|
||||
["Base.Size5CompIII38"] = true,
|
||||
["Base.Size6CompII357"] = true,
|
||||
["Base.Size6CompII38"] = true,
|
||||
["Base.Size6CompIII357"] = true,
|
||||
["Base.Size6CompIII38"] = true,
|
||||
["Base.SKSclip"] = true,
|
||||
["Base.SpeedStrip357"] = true,
|
||||
["Base.SpeedStrip38"] = true,
|
||||
["Base.SpeedStrip44"] = true,
|
||||
["Base.TEC9Mag"] = true,
|
||||
["Base.TherARMag"] = true,
|
||||
["Base.TherMini14Mag"] = true,
|
||||
["Base.USP40Mag"] = true,
|
||||
["Base.USP9Mag"] = true,
|
||||
["Base.UziColtSMGMag"] = true,
|
||||
["Base.UziMag"] = true,
|
||||
["Base.ValmetMag"] = true,
|
||||
["Base.Win69AMag"] = true,
|
||||
}
|
||||
|
||||
local GAEL_GUNSTORE_FIREARMS = {
|
||||
["Base.A2000"] = true,
|
||||
["Base.A91"] = true,
|
||||
["Base.AA12"] = true,
|
||||
["Base.ACE21"] = true,
|
||||
["Base.ACE23"] = true,
|
||||
["Base.ACE52_CQB"] = true,
|
||||
["Base.ACE53"] = true,
|
||||
["Base.ACR"] = true,
|
||||
["Base.ADS"] = true,
|
||||
["Base.AEK"] = true,
|
||||
["Base.AEK919"] = true,
|
||||
["Base.AK_minidrako"] = true,
|
||||
["Base.AK101"] = true,
|
||||
["Base.AK103"] = true,
|
||||
["Base.AK12"] = true,
|
||||
["Base.AK19"] = true,
|
||||
["Base.AK47"] = true,
|
||||
["Base.AK5C"] = true,
|
||||
["Base.AK74"] = true,
|
||||
["Base.AK74u"] = true,
|
||||
["Base.AK74u_long"] = true,
|
||||
["Base.AK9"] = true,
|
||||
["Base.AKM"] = true,
|
||||
["Base.AKU12"] = true,
|
||||
["Base.AMD65"] = true,
|
||||
["Base.AN94"] = true,
|
||||
["Base.Anaconda"] = true,
|
||||
["Base.APC9K"] = true,
|
||||
["Base.AR10"] = true,
|
||||
["Base.AR15"] = true,
|
||||
["Base.AR160"] = true,
|
||||
["Base.AR6951"] = true,
|
||||
["Base.ASH_12"] = true,
|
||||
["Base.AssaultRifle"] = true,
|
||||
["Base.AssaultRifle2"] = true,
|
||||
["Base.AUG_9mm"] = true,
|
||||
["Base.AUG_A1"] = true,
|
||||
["Base.AUG_A2"] = true,
|
||||
["Base.Automag357"] = true,
|
||||
["Base.Automag44"] = true,
|
||||
["Base.Automag50AE"] = true,
|
||||
["Base.AWS"] = true,
|
||||
["Base.BAR"] = true,
|
||||
["Base.Becker_Shotgun"] = true,
|
||||
["Base.Becker_Shotgun_Short"] = true,
|
||||
["Base.BenelliM4"] = true,
|
||||
["Base.Beretta_A400"] = true,
|
||||
["Base.Beretta_A400_Short"] = true,
|
||||
["Base.Beretta_PX4"] = true,
|
||||
["Base.Browning_Auto"] = true,
|
||||
["Base.Browning_Auto_Short"] = true,
|
||||
["Base.BrowningHP"] = true,
|
||||
["Base.Carcano"] = true,
|
||||
["Base.CBJ"] = true,
|
||||
["Base.CETME"] = true,
|
||||
["Base.CircuitJudgeRifle"] = true,
|
||||
["Base.Colt9mm"] = true,
|
||||
["Base.ColtNavy1851"] = true,
|
||||
["Base.ColtNavyExorcist"] = true,
|
||||
["Base.ColtPeacemaker1873"] = true,
|
||||
["Base.Coonan357"] = true,
|
||||
["Base.CS5"] = true,
|
||||
["Base.CZ75"] = true,
|
||||
["Base.CZ805"] = true,
|
||||
["Base.CZScorpion"] = true,
|
||||
["Base.DB_Condor"] = true,
|
||||
["Base.DB_Condor_sawn"] = true,
|
||||
["Base.DDM4"] = true,
|
||||
["Base.Deagle357_gold"] = true,
|
||||
["Base.Deagle50AE"] = true,
|
||||
["Base.DeagleCar14"] = true,
|
||||
["Base.DeLisle"] = true,
|
||||
["Base.DoubleBarrelShotgun"] = true,
|
||||
["Base.DoubleBarrelShotgunSawnoff"] = true,
|
||||
["Base.DVB15"] = true,
|
||||
["Base.ENARM_Pentagun"] = true,
|
||||
["Base.Enfield"] = true,
|
||||
["Base.FAL"] = true,
|
||||
["Base.FAL_CQB"] = true,
|
||||
["Base.FAMAS"] = true,
|
||||
["Base.FiveSeven"] = true,
|
||||
["Base.FN2000"] = true,
|
||||
["Base.FN502_22LR"] = true,
|
||||
["Base.FNX45"] = true,
|
||||
["Base.G17"] = true,
|
||||
["Base.G18"] = true,
|
||||
["Base.G2"] = true,
|
||||
["Base.G27"] = true,
|
||||
["Base.G36C"] = true,
|
||||
["Base.G3A3"] = true,
|
||||
["Base.G43"] = true,
|
||||
["Base.Galil"] = true,
|
||||
["Base.Glock_tactical"] = true,
|
||||
["Base.Glock23"] = true,
|
||||
["Base.Glock43"] = true,
|
||||
["Base.GOL"] = true,
|
||||
["Base.Grizzly50AE"] = true,
|
||||
["Base.Groza"] = true,
|
||||
["Base.GSH18"] = true,
|
||||
["Base.HK_121"] = true,
|
||||
["Base.HK416"] = true,
|
||||
["Base.HKG28"] = true,
|
||||
["Base.HKMK23"] = true,
|
||||
["Base.HoneyBadger"] = true,
|
||||
["Base.HuntingRifle"] = true,
|
||||
["Base.IA2"] = true,
|
||||
["Base.IA2_308"] = true,
|
||||
["Base.Jackhammer"] = true,
|
||||
["Base.Jericho941"] = true,
|
||||
["Base.JNG90"] = true,
|
||||
["Base.K2"] = true,
|
||||
["Base.K7"] = true,
|
||||
["Base.KAC_PDW"] = true,
|
||||
["Base.Kark98"] = true,
|
||||
["Base.Kimber1911"] = true,
|
||||
["Base.Kriss9mm"] = true,
|
||||
["Base.KrissVector45"] = true,
|
||||
["Base.KS23"] = true,
|
||||
["Base.KSG"] = true,
|
||||
["Base.L115A"] = true,
|
||||
["Base.L85"] = true,
|
||||
["Base.L86"] = true,
|
||||
["Base.L96"] = true,
|
||||
["Base.LanchesterMK1"] = true,
|
||||
["Base.Lewis"] = true,
|
||||
["Base.LR300"] = true,
|
||||
["Base.LSAT"] = true,
|
||||
["Base.LVOA"] = true,
|
||||
["Base.M1"] = true,
|
||||
["Base.M110"] = true,
|
||||
["Base.M16A2"] = true,
|
||||
["Base.M1887"] = true,
|
||||
["Base.M1887_Short"] = true,
|
||||
["Base.M1A1"] = true,
|
||||
["Base.M200"] = true,
|
||||
["Base.M21"] = true,
|
||||
["Base.M24"] = true,
|
||||
["Base.M240B"] = true,
|
||||
["Base.M249"] = true,
|
||||
["Base.M39"] = true,
|
||||
["Base.M4"] = true,
|
||||
["Base.M40"] = true,
|
||||
["Base.M60E4"] = true,
|
||||
["Base.M620"] = true,
|
||||
["Base.M82A3"] = true,
|
||||
["Base.M9_Samurai"] = true,
|
||||
["Base.M93R"] = true,
|
||||
["Base.M98B"] = true,
|
||||
["Base.MAB38A"] = true,
|
||||
["Base.MAC10"] = true,
|
||||
["Base.MAS36"] = true,
|
||||
["Base.MAT49"] = true,
|
||||
["Base.MatebaGrifone"] = true,
|
||||
["Base.MG131"] = true,
|
||||
["Base.MG4"] = true,
|
||||
["Base.MG42"] = true,
|
||||
["Base.MG710"] = true,
|
||||
["Base.Micro_UZI"] = true,
|
||||
["Base.Mini_14"] = true,
|
||||
["Base.Minimi"] = true,
|
||||
["Base.MK18"] = true,
|
||||
["Base.Mosin"] = true,
|
||||
["Base.Mossber500"] = true,
|
||||
["Base.Mossber590"] = true,
|
||||
["Base.MP_R8"] = true,
|
||||
["Base.MP18"] = true,
|
||||
["Base.MP1911"] = true,
|
||||
["Base.MP40"] = true,
|
||||
["Base.MP5"] = true,
|
||||
["Base.MP5K"] = true,
|
||||
["Base.MP5SD"] = true,
|
||||
["Base.MP7"] = true,
|
||||
["Base.MP9"] = true,
|
||||
["Base.MPX"] = true,
|
||||
["Base.MSST"] = true,
|
||||
["Base.MTAR"] = true,
|
||||
["Base.MTS_255"] = true,
|
||||
["Base.MTS_255_Short"] = true,
|
||||
["Base.MX4"] = true,
|
||||
["Base.Nagant_M1895"] = true,
|
||||
["Base.Negev"] = true,
|
||||
["Base.OTS_33"] = true,
|
||||
["Base.P220"] = true,
|
||||
["Base.P220_Elite"] = true,
|
||||
["Base.P228"] = true,
|
||||
["Base.P38"] = true,
|
||||
["Base.P90"] = true,
|
||||
["Base.P99"] = true,
|
||||
["Base.P99_Kilin"] = true,
|
||||
["Base.PB6P9"] = true,
|
||||
["Base.Pistol"] = true,
|
||||
["Base.pistol_shotgun"] = true,
|
||||
["Base.Pistol2"] = true,
|
||||
["Base.Pistol3"] = true,
|
||||
["Base.PKP"] = true,
|
||||
["Base.PP_Bizon"] = true,
|
||||
["Base.PP2000"] = true,
|
||||
["Base.PP93"] = true,
|
||||
["Base.PPSH41"] = true,
|
||||
["Base.Python357"] = true,
|
||||
["Base.QBA"] = true,
|
||||
["Base.QBB95"] = true,
|
||||
["Base.QBS09"] = true,
|
||||
["Base.QBS09_Short"] = true,
|
||||
["Base.QBZ951"] = true,
|
||||
["Base.R5"] = true,
|
||||
["Base.Remington1100"] = true,
|
||||
["Base.Remington1100_Short"] = true,
|
||||
["Base.Remington121"] = true,
|
||||
["Base.Remington870"] = true,
|
||||
["Base.Remington870_Short"] = true,
|
||||
["Base.Revolver"] = true,
|
||||
["Base.Revolver_long"] = true,
|
||||
["Base.Revolver_short"] = true,
|
||||
["Base.Revolver38"] = true,
|
||||
["Base.Revolver666"] = true,
|
||||
["Base.Rhino20DS"] = true,
|
||||
["Base.RMB93"] = true,
|
||||
["Base.RPD"] = true,
|
||||
["Base.RPK"] = true,
|
||||
["Base.RPK12"] = true,
|
||||
["Base.RPK16"] = true,
|
||||
["Base.RSH12"] = true,
|
||||
["Base.Ruger10_22"] = true,
|
||||
["Base.Ruger357"] = true,
|
||||
["Base.RugerLC"] = true,
|
||||
["Base.SA58"] = true,
|
||||
["Base.Saiga12"] = true,
|
||||
["Base.Saiga9mm"] = true,
|
||||
["Base.Samurai_aw"] = true,
|
||||
["Base.Samurai_kendo"] = true,
|
||||
["Base.SAR21"] = true,
|
||||
["Base.ScarH"] = true,
|
||||
["Base.ScarL"] = true,
|
||||
["Base.Schofield1875"] = true,
|
||||
["Base.Scout_elite"] = true,
|
||||
["Base.ScrapRevolver"] = true,
|
||||
["Base.Shorty"] = true,
|
||||
["Base.Shotgun"] = true,
|
||||
["Base.ShotgunSawnoff"] = true,
|
||||
["Base.SIG_553"] = true,
|
||||
["Base.SIG516"] = true,
|
||||
["Base.Silenced_Sten"] = true,
|
||||
["Base.Sjorgen"] = true,
|
||||
["Base.Sjorgen_Short"] = true,
|
||||
["Base.SKS"] = true,
|
||||
["Base.SKS_carbine"] = true,
|
||||
["Base.SKS_carbine_short"] = true,
|
||||
["Base.Snub22LR"] = true,
|
||||
["Base.SPAS12"] = true,
|
||||
["Base.SPAS15"] = true,
|
||||
["Base.Springfield_sniper"] = true,
|
||||
["Base.SR1M"] = true,
|
||||
["Base.SR338"] = true,
|
||||
["Base.SR3M"] = true,
|
||||
["Base.SR47"] = true,
|
||||
["Base.SS2V5"] = true,
|
||||
["Base.Sten_MK5"] = true,
|
||||
["Base.Striker"] = true,
|
||||
["Base.SV98"] = true,
|
||||
["Base.SVD"] = true,
|
||||
["Base.SVD_short"] = true,
|
||||
["Base.SVD12"] = true,
|
||||
["Base.SVDK"] = true,
|
||||
["Base.SVDK_short"] = true,
|
||||
["Base.SVT_40"] = true,
|
||||
["Base.SVU"] = true,
|
||||
["Base.SW1905"] = true,
|
||||
["Base.SW1917"] = true,
|
||||
["Base.SW500"] = true,
|
||||
["Base.SW629"] = true,
|
||||
["Base.SWM1854"] = true,
|
||||
["Base.SWM1894"] = true,
|
||||
["Base.SWM3"] = true,
|
||||
["Base.SWM327"] = true,
|
||||
["Base.SWM629_Deluxe"] = true,
|
||||
["Base.SWMP_12"] = true,
|
||||
["Base.Taurus_raging_bull"] = true,
|
||||
["Base.Taurus_raging_bull460"] = true,
|
||||
["Base.Taurus_RT85"] = true,
|
||||
["Base.Taurus606"] = true,
|
||||
["Base.TEC9"] = true,
|
||||
["Base.Thompson"] = true,
|
||||
["Base.TMP"] = true,
|
||||
["Base.Type81"] = true,
|
||||
["Base.Type88"] = true,
|
||||
["Base.UMP45"] = true,
|
||||
["Base.UMP45_long"] = true,
|
||||
["Base.USAS12"] = true,
|
||||
["Base.USP45"] = true,
|
||||
["Base.UZI"] = true,
|
||||
["Base.V_M87"] = true,
|
||||
["Base.ValmetM82"] = true,
|
||||
["Base.VarmintRifle"] = true,
|
||||
["Base.VEPR"] = true,
|
||||
["Base.Veresk"] = true,
|
||||
["Base.VictorySW22"] = true,
|
||||
["Base.VP70"] = true,
|
||||
["Base.VR80"] = true,
|
||||
["Base.VSK"] = true,
|
||||
["Base.VSS"] = true,
|
||||
["Base.VSS_Tactical"] = true,
|
||||
["Base.VSSK"] = true,
|
||||
["Base.VZ58"] = true,
|
||||
["Base.VZ61"] = true,
|
||||
["Base.WA2000"] = true,
|
||||
["Base.Webley_MK_snub"] = true,
|
||||
["Base.Webley_Revolver"] = true,
|
||||
["Base.Wieger940"] = true,
|
||||
["Base.Wildey"] = true,
|
||||
["Base.Winchester1886"] = true,
|
||||
["Base.Winchester1895"] = true,
|
||||
["Base.Winchester1897"] = true,
|
||||
["Base.X86"] = true,
|
||||
["Base.XD"] = true,
|
||||
["Base.XM8"] = true,
|
||||
}
|
||||
|
||||
local GAEL_GUNSTORE_MAGAZINES = {
|
||||
["Base.12GClip"] = true,
|
||||
["Base.12GClip14"] = true,
|
||||
["Base.12GDrum24"] = true,
|
||||
["Base.22LRClip"] = true,
|
||||
["Base.22LRClip50"] = true,
|
||||
["Base.22LRDrum100"] = true,
|
||||
["Base.308Box150"] = true,
|
||||
["Base.308Clip"] = true,
|
||||
["Base.308Clip40"] = true,
|
||||
["Base.308Drum100"] = true,
|
||||
["Base.308Drum60"] = true,
|
||||
["Base.357Clip"] = true,
|
||||
["Base.357Drum45"] = true,
|
||||
["Base.44Clip"] = true,
|
||||
["Base.44Clip20"] = true,
|
||||
["Base.44Drum50"] = true,
|
||||
["Base.45Clip"] = true,
|
||||
["Base.45Clip25"] = true,
|
||||
["Base.45Drum100"] = true,
|
||||
["Base.45Drum50"] = true,
|
||||
["Base.50Clip"] = true,
|
||||
["Base.50Clip18"] = true,
|
||||
["Base.50MagnumClip"] = true,
|
||||
["Base.50MagnumClip18"] = true,
|
||||
["Base.50MagnumDrum40"] = true,
|
||||
["Base.545x39Clip30"] = true,
|
||||
["Base.545x39Clip60"] = true,
|
||||
["Base.545x39Drum100"] = true,
|
||||
["Base.556Box150"] = true,
|
||||
["Base.556Clip"] = true,
|
||||
["Base.556Drum_100rnd"] = true,
|
||||
["Base.556Drum_60rnd"] = true,
|
||||
["Base.762x39Clip"] = true,
|
||||
["Base.762x39Clip45"] = true,
|
||||
["Base.762x39Drum100"] = true,
|
||||
["Base.762x39Drum73"] = true,
|
||||
["Base.762x54rBox150"] = true,
|
||||
["Base.762x54rClip"] = true,
|
||||
["Base.762x54rClip40"] = true,
|
||||
["Base.792x57Box75"] = true,
|
||||
["Base.792x57Box97"] = true,
|
||||
["Base.792x57Clip"] = true,
|
||||
["Base.792x57Clip40"] = true,
|
||||
["Base.9mmClip"] = true,
|
||||
["Base.9mmClip30"] = true,
|
||||
["Base.9mmDrum100"] = true,
|
||||
["Base.9mmDrum50"] = true,
|
||||
["Base.9mmDrum75"] = true,
|
||||
["Base.9x39Clip"] = true,
|
||||
["Base.9x39Clip40"] = true,
|
||||
["Base.9x39Drum60"] = true,
|
||||
["Base.BizonClip64"] = true,
|
||||
}
|
||||
|
||||
|
||||
local function setDisplayCategory(scriptManager, fullType, category)
|
||||
local scriptItem = scriptManager:FindItem(fullType)
|
||||
if not scriptItem then
|
||||
return false
|
||||
end
|
||||
|
||||
if type(scriptItem.DoParam) == "function" then
|
||||
scriptItem:DoParam("DisplayCategory = " .. category)
|
||||
return true
|
||||
end
|
||||
|
||||
if type(scriptItem.setDisplayCategory) == "function" then
|
||||
scriptItem:setDisplayCategory(category)
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function areSupportedItemsReady(scriptManager)
|
||||
if not scriptManager then
|
||||
return false
|
||||
end
|
||||
|
||||
local guns93Firearm = scriptManager:FindItem(GUNS93_SENTINEL_FIREARM)
|
||||
local guns93Magazine = scriptManager:FindItem(GUNS93_SENTINEL_MAGAZINE)
|
||||
local guns93Ready = guns93Firearm ~= nil and guns93Magazine ~= nil
|
||||
|
||||
local gaelFirearm = scriptManager:FindItem(GAEL_GUNSTORE_SENTINEL_FIREARM)
|
||||
local gaelMagazine = scriptManager:FindItem(GAEL_GUNSTORE_SENTINEL_MAGAZINE)
|
||||
local gaelReady = gaelFirearm ~= nil and gaelMagazine ~= nil
|
||||
|
||||
return guns93Ready or gaelReady
|
||||
end
|
||||
|
||||
local function applyPatch()
|
||||
if OFGuns93DisplayCategoryPatch.patched then
|
||||
return true
|
||||
end
|
||||
|
||||
if not isSortingToggleEnabled() then
|
||||
OFGuns93DisplayCategoryPatch.patched = true
|
||||
return true
|
||||
end
|
||||
|
||||
local scriptManager = ScriptManager and ScriptManager.instance
|
||||
if not scriptManager or type(scriptManager.FindItem) ~= "function" then
|
||||
return false
|
||||
end
|
||||
|
||||
local firearmCategory, magazineCategory = resolveWeaponCategories()
|
||||
|
||||
if not areSupportedItemsReady(scriptManager) then
|
||||
return false
|
||||
end
|
||||
|
||||
local patchedCount = 0
|
||||
for fullType in pairs(GUNS93_FIREARMS) do
|
||||
if setDisplayCategory(scriptManager, fullType, firearmCategory) then
|
||||
patchedCount = patchedCount + 1
|
||||
end
|
||||
end
|
||||
|
||||
for fullType in pairs(GUNS93_MAGAZINES) do
|
||||
if setDisplayCategory(scriptManager, fullType, magazineCategory) then
|
||||
patchedCount = patchedCount + 1
|
||||
end
|
||||
end
|
||||
|
||||
for fullType in pairs(GAEL_GUNSTORE_FIREARMS) do
|
||||
if setDisplayCategory(scriptManager, fullType, firearmCategory) then
|
||||
patchedCount = patchedCount + 1
|
||||
end
|
||||
end
|
||||
|
||||
for fullType in pairs(GAEL_GUNSTORE_MAGAZINES) do
|
||||
if setDisplayCategory(scriptManager, fullType, magazineCategory) then
|
||||
patchedCount = patchedCount + 1
|
||||
end
|
||||
end
|
||||
|
||||
if patchedCount == 0 then
|
||||
return false
|
||||
end
|
||||
|
||||
OFGuns93DisplayCategoryPatch.patched = true
|
||||
return true
|
||||
end
|
||||
|
||||
local function tryPatchOnTick()
|
||||
if applyPatch() then
|
||||
OFGuns93DisplayCategoryPatch.tickHookAdded = false
|
||||
Events.OnTick.Remove(tryPatchOnTick)
|
||||
end
|
||||
end
|
||||
|
||||
local function ensureTickHook()
|
||||
if OFGuns93DisplayCategoryPatch.tickHookAdded then
|
||||
return
|
||||
end
|
||||
|
||||
OFGuns93DisplayCategoryPatch.tickHookAdded = true
|
||||
Events.OnTick.Add(tryPatchOnTick)
|
||||
end
|
||||
|
||||
local function queueRepatchNow()
|
||||
OFGuns93DisplayCategoryPatch.patched = false
|
||||
if not applyPatch() then
|
||||
ensureTickHook()
|
||||
end
|
||||
end
|
||||
|
||||
local function runQueuedRepatch()
|
||||
OFGuns93DisplayCategoryPatch.repatchQueued = false
|
||||
Events.OnTick.Remove(runQueuedRepatch)
|
||||
queueRepatchNow()
|
||||
end
|
||||
|
||||
local function queueRepatch()
|
||||
if OFGuns93DisplayCategoryPatch.repatchQueued then
|
||||
return
|
||||
end
|
||||
|
||||
OFGuns93DisplayCategoryPatch.repatchQueued = true
|
||||
Events.OnTick.Add(runQueuedRepatch)
|
||||
end
|
||||
|
||||
local function tryInstallBetterContainersRepatchHook()
|
||||
local ok, helpers = pcall(require, "BetterContainers/Helpers")
|
||||
if not ok or type(helpers) ~= "table" then
|
||||
return
|
||||
end
|
||||
|
||||
local eventName = helpers.OPTIONS_APPLIED
|
||||
if type(eventName) ~= "string" then
|
||||
return
|
||||
end
|
||||
|
||||
local event = Events[eventName]
|
||||
if event and type(event.Add) == "function" then
|
||||
event.Add(queueRepatch)
|
||||
end
|
||||
end
|
||||
|
||||
if not applyPatch() then
|
||||
ensureTickHook()
|
||||
end
|
||||
|
||||
if Events.OnMainMenuEnter and type(Events.OnMainMenuEnter.Add) == "function" then
|
||||
Events.OnMainMenuEnter.Add(queueRepatch)
|
||||
end
|
||||
|
||||
tryInstallBetterContainersRepatchHook()
|
||||
|
||||
7
common/media/lua/shared/Translate/EN/IG_UI_EN.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
IGUI_EN = {
|
||||
IGUI_ItemCat_WepFire = "Weapon - Firearm",
|
||||
IGUI_ItemCat_WepAmmoMag = "Weapon - Magazine",
|
||||
IGUI_ItemCat_Casings = "Spent Casings",
|
||||
IGUI_ItemCat_OFSpentCasings = "Spent Casings",
|
||||
IGUI_ItemCat_OFAmmoPart = "Ammo Part",
|
||||
}
|
||||
7
common/media/lua/shared/Translate/EN/Sandbox_EN.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Sandbox_EN = {
|
||||
Sandbox_OpinionatedFirearms = "Opinionated Firearms",
|
||||
Sandbox_HandleHotBrassCasingSpawnUseAmmoMaker = "Handle Hot Brass Casing Spawn - Use Ammomaker",
|
||||
Sandbox_HandleHotBrassCasingSpawnUseAmmoMaker_tooltip = "When enabled, Hot Brass ejects Ammo Maker empties for supported ammo (including Guns93 and Gael Gun Store) instead of default casings.",
|
||||
Sandbox_AddSorting = "Add Sorting",
|
||||
Sandbox_AddSorting_tooltip = "When enabled, apply Opinionated Firearms sorting/display categories for supported weapons, magazines, and ammo parts.",
|
||||
}
|
||||
14
common/media/sandbox-options.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
VERSION = 1,
|
||||
option OpinionatedFirearms.HandleHotBrassCasingSpawnUseAmmoMaker {
|
||||
type = boolean,
|
||||
default = true,
|
||||
page = OpinionatedFirearms,
|
||||
translation = HandleHotBrassCasingSpawnUseAmmoMaker,
|
||||
}
|
||||
|
||||
option OpinionatedFirearms.AddSorting {
|
||||
type = boolean,
|
||||
default = true,
|
||||
page = OpinionatedFirearms,
|
||||
translation = AddSorting,
|
||||
}
|
||||
77
index.html
@@ -1,77 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Opinionated Firearms Spawn List Builder</title>
|
||||
<link rel="stylesheet" href="./styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="topbar">
|
||||
<div>
|
||||
<h1>Opinionated Firearms Spawn List Builder</h1>
|
||||
<p>Edit firearm/attachment spawn enablement, placement lists, and spawn rates.</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<label class="file-button">
|
||||
Load Catalog JSON
|
||||
<input id="catalogFile" type="file" accept=".json,application/json">
|
||||
</label>
|
||||
<label class="file-button secondary">
|
||||
Load Profile JSON
|
||||
<input id="profileFile" type="file" accept=".json,application/json">
|
||||
</label>
|
||||
<button id="exportProfile" type="button">Export Profile JSON</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="status">
|
||||
<span id="statusText">Load an extracted catalog JSON to begin.</span>
|
||||
</section>
|
||||
|
||||
<main class="layout">
|
||||
<section class="panel">
|
||||
<div class="panel-head">
|
||||
<h2>Items</h2>
|
||||
<div class="filters">
|
||||
<input id="searchInput" type="search" placeholder="Search item ID...">
|
||||
<select id="categoryFilter">
|
||||
<option value="all">All categories</option>
|
||||
<option value="firearm">Firearms</option>
|
||||
<option value="attachment">Attachments</option>
|
||||
<option value="unknown">Unknown</option>
|
||||
</select>
|
||||
<select id="spawnFilter">
|
||||
<option value="all">All spawn states</option>
|
||||
<option value="enabled">Spawn enabled</option>
|
||||
<option value="disabled">Spawn disabled</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Spawn</th>
|
||||
<th>Item</th>
|
||||
<th>Category</th>
|
||||
<th>Spawn Loacation</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="itemTableBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel details-panel">
|
||||
<div class="panel-head">
|
||||
<h2>Selected Item</h2>
|
||||
<button id="resetSelected" type="button">Reset to Catalog</button>
|
||||
</div>
|
||||
<div id="selectedDetails" class="details-empty">Select an item to edit placements and spawn rate.</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script src="./app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
9
mod.info
Normal file
@@ -0,0 +1,9 @@
|
||||
name=Opinionated Firearms
|
||||
id=hrsys_opinionated_firearms
|
||||
author=Riggs0
|
||||
modversion=1.0.0
|
||||
versionMin=42.12.13
|
||||
require=\2788256295/ammomaker,\HBVCEFb42
|
||||
description=Opinionated Firearms casing and other changes to Guns of 93, Ammomaker and Hot Brass
|
||||
icon=icon.png
|
||||
poster=preview.png
|
||||
BIN
preview.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
263
styles.css
@@ -1,263 +0,0 @@
|
||||
:root {
|
||||
--bg: #f4efe6;
|
||||
--panel: #fffaf1;
|
||||
--panel-2: #f8f0e2;
|
||||
--line: #d8ccb8;
|
||||
--text: #2f2418;
|
||||
--muted: #7d6852;
|
||||
--accent: #9c3f1f;
|
||||
--accent-2: #4f6f52;
|
||||
--focus: #c77800;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Trebuchet MS", "Segoe UI", sans-serif;
|
||||
color: var(--text);
|
||||
background:
|
||||
radial-gradient(1200px 700px at 95% 0%, #e2c8a0 0%, transparent 60%),
|
||||
linear-gradient(145deg, #f8f2e7 0%, var(--bg) 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.topbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
gap: 1rem;
|
||||
padding: 1.1rem 1.3rem 1rem;
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: rgba(255, 250, 241, 0.8);
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
.topbar h1 {
|
||||
margin: 0;
|
||||
font-size: 1.3rem;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.topbar p {
|
||||
margin: 0.25rem 0 0;
|
||||
color: var(--muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.55rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.file-button,
|
||||
button {
|
||||
border: 1px solid var(--accent);
|
||||
color: #fff;
|
||||
background: var(--accent);
|
||||
padding: 0.48rem 0.75rem;
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.file-button.secondary {
|
||||
border-color: var(--accent-2);
|
||||
background: var(--accent-2);
|
||||
}
|
||||
|
||||
.file-button input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
.file-button:hover {
|
||||
filter: brightness(1.05);
|
||||
}
|
||||
|
||||
button:focus-visible,
|
||||
.file-button:focus-within {
|
||||
outline: 2px solid var(--focus);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 0.5rem 1.3rem;
|
||||
font-size: 0.88rem;
|
||||
color: var(--muted);
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(320px, 1.1fr) minmax(320px, 0.9fr);
|
||||
gap: 1rem;
|
||||
padding: 1rem 1.3rem 1.3rem;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 0.8rem;
|
||||
min-height: 66vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.panel-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 0.8rem;
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: var(--panel-2);
|
||||
}
|
||||
|
||||
.panel-head h2 {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.filters {
|
||||
display: flex;
|
||||
gap: 0.45rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
input[type="search"],
|
||||
select,
|
||||
input[type="number"],
|
||||
input[type="text"] {
|
||||
border: 1px solid var(--line);
|
||||
background: #fff;
|
||||
color: var(--text);
|
||||
border-radius: 0.45rem;
|
||||
padding: 0.35rem 0.45rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
input:focus-visible,
|
||||
select:focus-visible {
|
||||
outline: 2px solid var(--focus);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
.table-wrap {
|
||||
overflow: auto;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
text-align: left;
|
||||
padding: 0.45rem 0.55rem;
|
||||
border-bottom: 1px solid var(--line);
|
||||
font-size: 0.83rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
background: #fff2de;
|
||||
}
|
||||
|
||||
tbody tr.selected {
|
||||
background: #f9dfbc;
|
||||
}
|
||||
|
||||
.details-panel {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.details-body {
|
||||
padding: 0.8rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.details-empty {
|
||||
padding: 1.1rem;
|
||||
color: var(--muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.item-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
margin-bottom: 0.7rem;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.badge {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 999px;
|
||||
padding: 0.15rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.inline-row {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 0.65rem;
|
||||
}
|
||||
|
||||
.placements-table th,
|
||||
.placements-table td {
|
||||
font-size: 0.8rem;
|
||||
padding: 0.34rem 0.4rem;
|
||||
}
|
||||
|
||||
.small-btn {
|
||||
border: 1px solid var(--line);
|
||||
background: #fff;
|
||||
color: var(--text);
|
||||
border-radius: 0.35rem;
|
||||
padding: 0.2rem 0.45rem;
|
||||
font-size: 0.75rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.small-btn.remove {
|
||||
border-color: #b74a38;
|
||||
color: #8f2617;
|
||||
}
|
||||
|
||||
.meta {
|
||||
margin: 0.7rem 0 0;
|
||||
color: var(--muted);
|
||||
font-size: 0.78rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.panel {
|
||||
min-height: 45vh;
|
||||
}
|
||||
}
|
||||