Simon Volpert zdoomrl / master asmflow / AutoAssembly.acs
master

Tree @master (Download .tar.gz)

AutoAssembly.acs @masterraw · history · blame

// ZDoomRL - Gameplay Modifications for DoomRL Arsenal
// Author: Simon Volpert <simon@simonvolpert.com>
// Project page: https://simonvolpert.com/zdoomrl/
// This program is free software, released under the MIT license. See the LICENSE file for more information

#library "AssemblyFlow"
#include "zcommon.acs"
#include "Assemblies.acs"

#define CVAR_KNOWN_ONLY "DRLA_AssemblyLearning"

str Modpacks[8] = {"AgilityMod", "BulkMod", "TechnicalMod", "PowerMod", "FirestormMod", "SniperMod", "NanoMod", "ArtiMod"};
str ModName[7] = {"\cdA", "\cnB", "\ckT", "\cgP", "\ciF", "\ctS", "\ccN"};


// Utility: return the greater of two integers
function int max(int a, int b) {
	if (a > b) {
		return a;
	}
	return b;
}


// Return the number of required mods for an assembly
function int modreq(int AssemblyIndex, int ModType) {
	return GetChar(AssemblyDB[AssemblyIndex][ModType], 0) - 48;
}


// Watch for potential assemblies to auto-replace
script "AssemblyWatch" Enter {
	str UsedMod, HeldItem, ResultItem, BaseItem; // Set to empty string immediately before use
	int AMods, BMods, TMods, PMods, FMods, SMods, NMods, ModLimit, AsmSize;
	if (GetCvar("Debug")) {
		Log(s:"fluid assembly watch ready");
	}
	while (TRUE) {
		Delay(1);
		// Wait for a modpack activation
		UsedMod = "";
		for (int m = 0; m < 8; m++) {
			if (CheckInventory(StrParam(s:"RLUse", s:Modpacks[m]))) {
				UsedMod = StrParam(s:"RLUse", s:Modpacks[m]);
				if (GetCvar("Debug")) {
					Log(s:"used ", s:Modpacks[m]);
				}
				break;
			}
		}
		if (UsedMod == "") {
			continue;
		}
		// Wait for the modpack to deactibate
		while (CheckInventory(UsedMod)) {
			Delay(1);
		}
		HeldItem = "";
		BaseItem = "";
		// Detect the item being modified
		for (int i = 0; i < ASSEMBLIES; i++) {
			if (CheckInventory(StrParam(s:AssemblyDB[i][ASSEMBLY_BASE], s:"Selected"))) {
				HeldItem = AssemblyDB[i][ASSEMBLY_BASE];
				BaseItem = HeldItem;
				break;
			}
			else if (CheckInventory(StrParam(s:AssemblyDB[i][ASSEMBLY_RESULT], s:"Selected"))) {
				HeldItem = AssemblyDB[i][ASSEMBLY_RESULT];
				BaseItem = AssemblyDB[i][ASSEMBLY_BASE];
				break;
			}
		}
		if (HeldItem == "") {
			continue;
		}
		// Count applied modpacks
		if (GetCvar("Debug")) {
			Log(s:"checking modification of ", s:HeldItem);
		}
		AMods = CheckInventory(StrParam(s:HeldItem, s:"AgilityMod"));
		BMods = CheckInventory(StrParam(s:HeldItem, s:"BulkMod"));
		TMods = CheckInventory(StrParam(s:HeldItem, s:"TechnicalMod"));
		PMods = CheckInventory(StrParam(s:HeldItem, s:"PowerMod"));
		FMods = CheckInventory(StrParam(s:HeldItem, s:"FirestormMod"));
		SMods = CheckInventory(StrParam(s:HeldItem, s:"SniperMod"));
		NMods = CheckInventory(StrParam(s:HeldItem, s:"NanoMod"));
		ModLimit = AMods + BMods + TMods + PMods + FMods + SMods + NMods;
		// Don't allow the blaster to have more than one technical mod (upstream bug workaround)
		if (TMods == 2 && HeldItem == "RLBlaster") {
			Print(s:"This weapon cannot support further modification.");
			TakeInventory("RLBlasterTechnicalMod", 1);
			GiveInventory("RLTechnicalModItem", 1);
			StopSound(0, CHAN_ITEM);
			continue;
		}
		// Decompose assembly if applicable
		AsmSize = 0;
		if (HeldItem != BaseItem) {
			AMods += modreq(i, MOD_A);
			BMods += modreq(i, MOD_B);
			TMods += modreq(i, MOD_T);
			PMods += modreq(i, MOD_P);
			FMods += modreq(i, MOD_F);
			SMods += modreq(i, MOD_S);
			NMods += modreq(i, MOD_N);
			AsmSize = modreq(i, ASSEMBLY_SIZE);
			ModLimit += AsmSize;
		}
		if (GetCvar("Debug")) {
			Log(i:ModLimit, s:" mods: ", i:AMods, s:"A ", i:BMods, s:"B ", i:TMods, s:"T ", i:PMods, s:"P ", i:FMods, s:"F ", i:SMods, s:"S ", i:NMods, s:"N ");
		}
		// Check assembly eligibility
		ResultItem = "";
		for (int a = 0; a < ASSEMBLIES; a++) {
			if (BaseItem != AssemblyDB[a][ASSEMBLY_BASE]) {
				continue;
			}
			if (AMods < modreq(a, MOD_A) || BMods < modreq(a, MOD_B) || TMods < modreq(a, MOD_T) || PMods < modreq(a, MOD_P) ||
				FMods < modreq(a, MOD_F) || SMods < modreq(a, MOD_S) || NMods < modreq(a, MOD_N)) {
				continue;
			}
			// Ignore assemblies of the same or lower tier
			if (AsmSize >= modreq(a, ASSEMBLY_SIZE)) {
				continue;
			}
			ResultItem = AssemblyDB[a][ASSEMBLY_RESULT];
			// Ignore unknown assemblies if learning option is set
			if (GetCVar(CVAR_KNOWN_ONLY) && !CheckInventory(AssemblyDB[a][ASSEMBLY_RECIPE])) {
				if (GetCvar("Debug")) {
					Log(s:"recipe for ", s:ResultItem, s:" is unknown");
				}
				ResultItem = "";
				continue;
			}
			if (GetCvar("Debug")) {
				Log(s:"enough mods to complete ", s:ResultItem);
			}
			break;
		}
		if (ResultItem == "") {
			continue;
		}
		// Clear inventory of existing item and subtract assembly cost from total mod counts
		TakeInventory(HeldItem, 1);
		TakeInventory(StrParam(s:HeldItem, s:"AgilityMod"), 4);
		AMods -= modreq(a, MOD_A);
		TakeInventory(StrParam(s:HeldItem, s:"BulkMod"), 4);
		BMods -= modreq(a, MOD_B);
		TakeInventory(StrParam(s:HeldItem, s:"TechnicalMod"), 4);
		TMods -= modreq(a, MOD_T);
		TakeInventory(StrParam(s:HeldItem, s:"PowerMod"), 4);
		PMods -= modreq(a, MOD_P);
		TakeInventory(StrParam(s:HeldItem, s:"FirestormMod"), 4);
		FMods -= modreq(a, MOD_F);
		TakeInventory(StrParam(s:HeldItem, s:"SniperMod"), 4);
		SMods -= modreq(a, MOD_S);
		TakeInventory(StrParam(s:HeldItem, s:"NanoMod"), 4);
		NMods -= modreq(a, MOD_N);
		TakeInventory(StrParam(s:HeldItem, s:"ModLimit"), 4);
		// Give new assembled item, preserving any extra mods
		GiveInventory(ResultItem, 1);
		GiveInventory("RLDeselectionFunction", 1);
		GiveInventory("RLMisfireSpamPreventionCooldown", 1);
		GiveInventory("RLRarityTokenRemover", 1);
		// Clear assembly confirmation tokens
		for (int c = 0; c < ASSEMBLIES; c++) {
			TakeInventory(StrParam(s:AssemblyDB[c][ASSEMBLY_RESULT], s:"Confirm"), 1);
		}
		// Apply leftover mods to resulting assembly
		GiveInventory(StrParam(s:ResultItem, s:"AgilityMod"), AMods);
		GiveInventory(StrParam(s:ResultItem, s:"BulkMod"), BMods);
		GiveInventory(StrParam(s:ResultItem, s:"TechnicalMod"), TMods);
		GiveInventory(StrParam(s:ResultItem, s:"PowerMod"), PMods);
		GiveInventory(StrParam(s:ResultItem, s:"FirestormMod"), FMods);
		GiveInventory(StrParam(s:ResultItem, s:"SniperMod"), SMods);
		GiveInventory(StrParam(s:ResultItem, s:"NanoMod"), NMods);
		GiveInventory(StrParam(s:ResultItem, s:"ModLimit"), AMods + BMods + TMods + PMods + FMods + SMods + NMods);
		SetWeapon(ResultItem);
		// Report the result
		str strangemessage = "";
		if (UsedMod == "RLUseArtiMod") {
			strangemessage = "\n\nThe strange sentient mod pack seems satisfied with the\nbizarre addons it somehow put on your weapon.";
		}
		Print(s:"\cv", s:AssemblyDB[a][ASSEMBLY_NAME], s:"\c- assembled.", s:strangemessage);
		// Play a relevant sound effect
		if (modreq(a, ASSEMBLY_SIZE) == 4) {
			if (CheckInventory("RLScavengerPerk")) {
				PlaySound(0, "technician/laugh", CHAN_VOICE);
			}
			PlaySound(0, "weapons/masterassembly", CHAN_ITEM);
			PlaySound(0, "weapons/masterassemblyfanfare", CHAN_AUTO);
		}
		else {
			PlaySound(0, "weapons/assembly", CHAN_ITEM);
		}
	}
}


// Print available assemblies for the held weapon
script "ShowPossibleAssemblies" (void) {
	int AMods, BMods, TMods, PMods, FMods, SMods, NMods, ModLimit, AsmSize, NeededMods;
	int ModQuota = 4;
	if (CheckInventory("RLSuperiorWeaponToken")) {
		ModQuota = 2;
	}
	str HeldItem = "";
	str BaseItem = "";
	str ResultTable = "Possible Assemblies:\n\cu-------------------------";
	bool Results = FALSE;
	// Detect the currently held item and its base
	for (int i = 0; i < ASSEMBLIES; i++) {
		if (CheckInventory(StrParam(s:AssemblyDB[i][ASSEMBLY_BASE], s:"Selected"))) {
			HeldItem = AssemblyDB[i][ASSEMBLY_BASE];
			BaseItem = HeldItem;
			break;
		}
		else if (CheckInventory(StrParam(s:AssemblyDB[i][ASSEMBLY_RESULT], s:"Selected"))) {
			HeldItem = AssemblyDB[i][ASSEMBLY_RESULT];
			BaseItem = AssemblyDB[i][ASSEMBLY_BASE];
			break;
		}
	}
	if (HeldItem != "") {
		// Count applied modpacks
		if (GetCvar("Debug")) {
			Log(s:"checking modification of ", s:HeldItem);
		}
		AMods = CheckInventory(StrParam(s:HeldItem, s:"AgilityMod"));
		BMods = CheckInventory(StrParam(s:HeldItem, s:"BulkMod"));
		TMods = CheckInventory(StrParam(s:HeldItem, s:"TechnicalMod"));
		PMods = CheckInventory(StrParam(s:HeldItem, s:"PowerMod"));
		FMods = CheckInventory(StrParam(s:HeldItem, s:"FirestormMod"));
		SMods = CheckInventory(StrParam(s:HeldItem, s:"SniperMod"));
		NMods = CheckInventory(StrParam(s:HeldItem, s:"NanoMod"));
		ModLimit = AMods + BMods + TMods + PMods + FMods + SMods + NMods;
		// Decompose assembly if applicable
		AsmSize = 0;
		if (HeldItem != BaseItem) {
			AMods += modreq(i, MOD_A);
			BMods += modreq(i, MOD_B);
			TMods += modreq(i, MOD_T);
			PMods += modreq(i, MOD_P);
			FMods += modreq(i, MOD_F);
			SMods += modreq(i, MOD_S);
			NMods += modreq(i, MOD_N);
			AsmSize = modreq(i, ASSEMBLY_SIZE);
			ModLimit += AsmSize;
		}
		if (GetCvar("Debug")) {
			Log(i:ModLimit, s:" mods: ", i:AMods, s:"A ", i:BMods, s:"B ", i:TMods, s:"T ", i:PMods, s:"P ", i:FMods, s:"F ", i:SMods, s:"S ", i:NMods, s:"N ");
		}
		// Look for matching assemblies
		for (int a = 0; a < ASSEMBLIES; a++) {
			if (BaseItem != AssemblyDB[a][ASSEMBLY_BASE]) {
				continue;
			}
			// Skip assemblies of the same or lower tier than the currently held
			if (AsmSize > 0 && AsmSize >= modreq(a, ASSEMBLY_SIZE)) {
				continue;
			}
			// Calculate remaining mods to complete assembly
			NeededMods = 0;
			NeededMods += max(0, modreq(a, MOD_A) - AMods);
			NeededMods += max(0, modreq(a, MOD_B) - BMods);
			NeededMods += max(0, modreq(a, MOD_T) - TMods);
			NeededMods += max(0, modreq(a, MOD_P) - PMods);
			NeededMods += max(0, modreq(a, MOD_S) - SMods);
			NeededMods += max(0, modreq(a, MOD_F) - FMods);
			NeededMods += max(0, modreq(a, MOD_N) - NMods);
			if (NeededMods > ModQuota - ModLimit) {
				continue;
			}
			// Ignore unknown assemblies if learning option is set
			if (GetCVar(CVAR_KNOWN_ONLY) && !CheckInventory(AssemblyDB[a][ASSEMBLY_RECIPE])) {
				if (GetCvar("Debug")) {
					Log(s:"recipe for ", s:AssemblyDB[a][ASSEMBLY_RESULT], s:" is unknown");
				}
				continue;
			}
			if (GetCvar("Debug")) {
				Log(s:"possible to complete ", s:AssemblyDB[a][ASSEMBLY_RESULT]);
			}
			Results = TRUE;
			// Build the result entry
			ResultTable = StrParam(s:ResultTable, s:"\n\cv", s:AssemblyDB[a][ASSEMBLY_NAME], s:"\cu:  ");
			for (int m = 0; m < 7; m++) {
				for (int r = 0; r < modreq(a, m + MOD_A); r++) {
					ResultTable = StrParam(s:ResultTable, s:ModName[m]);
				}
			}
		}
	}
	// Be explicit about no results
	if (!Results) {
		ResultTable = StrParam(s:ResultTable, s:"\n\ccNothing");
		PlaySound(0, "hud/error", CHAN_AUTO);
	}
	else {
		PlaySound(0, "UI/PDA/Offline", CHAN_ITEM);
	}
	// Print the results
	Print(s:ResultTable);
}