/* * This file contains functions to reduce the number of items from NPC inventories, containers and from the world. * This script was kindly provided by Gothic Free Aim, free for use outside of its license. * * Gothic Free Aim - Free aiming for the video games Gothic 1 and Gothic 2 by Piranha Bytes * Copyright (C) 2016-2017 mud-freak (@szapp) * * * For elaborate documentation of this script, see * */ /* Fraction of remaining projectiles, e.g 0.2 to remove 80% of the projectiles */ var float _projectileReduceToFraction; /* * Broadcast function to reduce number of arrows and bolts from all (N)PCs */ func void reduceItemsInInventory(var C_Npc slf) { var string instName; instName = MEM_ReadString(MEM_GetSymbolByIndex(Hlp_GetInstanceID(slf))); // Get arrow and bolt instances depending on G1 or G2 var int arrowInst; var int boltInst; if (GOTHIC_BASE_VERSION == 1) { arrowInst = MEM_GetSymbolIndex("ItAmArrow"); boltInst = MEM_GetSymbolIndex("ItAmBolt"); } else { arrowInst = MEM_GetSymbolIndex("ItRw_Arrow"); boltInst = MEM_GetSymbolIndex("ItRw_Bolt"); }; // Number of projectiles present in the inventory var int numArrows; numArrows = Npc_HasItems(slf, arrowInst); var int numBolts; numBolts = Npc_HasItems(slf, boltInst); var int reduceTo; var int s; // Reduce for player only once ever! var int gotPlayer; if (Npc_IsPlayer(slf)) { if (gotPlayer) { return; }; gotPlayer = TRUE; }; // Reduce number of arrows if (numArrows) { reduceTo = roundf(mulf(mkf(numArrows), castToIntf(_projectileReduceToFraction))); // Amount * x // Keep at least two instances if (reduceTo < 2) { reduceTo = 2; }; Npc_RemoveInvItems(slf, arrowInst, numArrows-reduceTo); s = SB_New(); SB(" Removed "); SBi(numArrows-reduceTo); SB(" of "); SBi(numArrows); SB(" arrows from "); SB(instName); SB(", kept "); SBi(reduceTo); MEM_Info(SB_ToString()); SB_Destroy(); }; // Reduce number of bolts if (numBolts) { reduceTo = roundf(mulf(mkf(numBolts), castToIntf(_projectileReduceToFraction))); // Amount * x // Keep at least two instances if (reduceTo < 2) { reduceTo = 2; }; Npc_RemoveInvItems(slf, boltInst, numBolts-reduceTo); s = SB_New(); SB(" Removed "); SBi(numBolts-reduceTo); SB(" of "); SBi(numBolts); SB(" bolts from "); SB(instName); SB(", kept "); SBi(reduceTo); MEM_Info(SB_ToString()); SB_Destroy(); }; }; /* * Remove instances from containers (chests) */ func void reduceItemsInContainers(var int itemInst, var int minimumAmount, var float keepFraction) { // Gothic 1 const int zCWorld__SearchVobListByBaseClass_G1 = 6250016; //0x5F5E20 const int oCMobContainer__classDef_G1 = 9285504; //0x8DAF80 const int oCMobContainer__Remove_G1 = 6831792; //0x683EB0 // Gothic 2 const int zCWorld__SearchVobListByBaseClass_G2 = 6439712; //0x624320 const int oCMobContainer__classDef_G2 = 11212976; //0xAB18B0 const int oCMobContainer__Remove_G2 = 7495664; //0x725FF0 // Item instance name var string itemStr; itemStr = MEM_ReadString(MEM_GetSymbolByIndex(itemInst)); // Create array that will contain all containers in the current world var int vobListPtr; vobListPtr = MEM_ArrayCreate(); var zCArray vobList; vobList = _^(vobListPtr); // Search containers and fill the array var int vobTreePtr; vobTreePtr = _@(MEM_Vobtree); var int worldPtr; worldPtr = _@(MEM_World); var int classDef; classDef = MEMINT_SwitchG1G2(oCMobContainer__classDef_G1, oCMobContainer__classDef_G2); const int call = 0; if (CALL_Begin(call)) { CALL_PtrParam(_@(vobTreePtr)); CALL_PtrParam(_@(vobListPtr)); CALL_PtrParam(_@(classDef)); CALL__thiscall(_@(worldPtr), MEMINT_SwitchG1G2(zCWorld__SearchVobListByBaseClass_G1, zCWorld__SearchVobListByBaseClass_G2)); call = CALL_End(); }; // Print some info to the zSpy var int s; s = SB_New(); SB("Number of chests in this world: "); SBi(vobList.numInArray); SB(", keep "); SBf(mulf(castToIntf(keepFraction), mkf(100))); SB("% of all contained "); SB(itemStr); SB(" instances."); MEM_Info(SB_ToString()); SB_Clear(); // Reset counters var int totalInstances; totalInstances = 0; var int totalRemoved; totalRemoved = 0; // Iterate over all containers repeat(i, vobList.numInArray); var int i; var int containerPtr; containerPtr = MEM_ReadIntArray(vobList.array, i); var oCMobContainer container; container = _^(containerPtr); var int containerListPtr; containerListPtr = container.containList_next; // Reset counters var int keep; keep = 0; var int removed; removed = 0; var int origAmount; origAmount = 0; // Iterate over contents of this container while(containerListPtr); var zCListSort containerList; containerList = _^(containerListPtr); containerListPtr = containerList.next; // Get next item var int itemPtr; itemPtr = containerList.data; var oCItem item; item = _^(itemPtr); // Compare item instance if (item.instanz == itemInst) { origAmount = item.amount; keep = roundf(mulf(mkf(item.amount), castToIntf(keepFraction))); // Keep at least minimumAmount instances if (keep < minimumAmount) { keep = minimumAmount; }; item.amount = keep; // If none are left, remove all if (item.amount < 1) { const int call2 = 0; if (CALL_Begin(call2)) { CALL_PtrParam(_@(itemPtr)); CALL__thiscall(_@(containerPtr), MEMINT_SwitchG1G2(oCMobContainer__Remove_G1, oCMobContainer__Remove_G2)); call2 = CALL_End(); }; // Update the number of removed instances removed = origAmount; } else { removed = origAmount - keep; }; break; }; end; // Update counters if (origAmount) { totalInstances += origAmount; totalRemoved += removed; }; end; // Free container array MEM_ArrayFree(vobListPtr); // Output summary SB("Removed "); SBi(totalRemoved); SB(" out of "); SBi(totalInstances); SB(" "); SB(itemStr); SB(" instances, kept "); SBi(totalInstances-totalRemoved); SB("."); MEM_Info(SB_ToString()); SB_Destroy(); }; /* * Remove instances from world (all but one per way point) */ func void reduceItemsInWorld(var int itemInst) { // Addresses const int zCWayNet__GetNearestWaypoint_G1 = 7354960; //0x703A50 const int zCWayNet__GetNearestWaypoint_G2 = 8050272; //0x7AD660 // Item instance name var string itemStr; itemStr = MEM_ReadString(MEM_GetSymbolByIndex(itemInst)); // Get array of instance that are lying in the world var int itemArrayPtr; itemArrayPtr = MEM_SearchAllVobsByName(itemStr); var zCArray itemArray; itemArray = _^(itemArrayPtr); // Print some info to the zSpy var int s; s = SB_New(); SB("Found "); SBi(itemArray.numInArray); SB(" "); SB(itemStr); SB(" instances."); MEM_Info(SB_ToString()); SB_Clear(); // Create array to remember number of instances near each way point var int wpArrayPtr; wpArrayPtr = MEM_ArrayCreate(); var zCArray wpArray; wpArray = _^(wpArrayPtr); wpArray.numAlloc = itemArray.numInArray; // Worst case scenario wpArray.array = MEM_Alloc(wpArray.numAlloc * sizeof_zString); // Iterate over found instances repeat(i, itemArray.numInArray); var int i; var int vobPtr; vobPtr = MEM_ReadIntArray(itemArray.array, i); if (!vobPtr) { continue; }; var zCVob vob; vob = _^(vobPtr); // Get position of instance var int pos[3]; pos[0] = vob.trafoObjToWorld[3]; pos[1] = vob.trafoObjToWorld[7]; pos[2] = vob.trafoObjToWorld[11]; // Retrieve nearest way point var int posPtr; posPtr = _@(pos); var int wayNetPtr; wayNetPtr = MEM_World.wayNet; const int call = 0; if (CALL_Begin(call)) { CALL_PutRetValTo(_@(wpPtr)); CALL__fastcall(_@(wayNetPtr), _@(posPtr), MEMINT_SwitchG1G2(zCWayNet__GetNearestWaypoint_G1, zCWayNet__GetNearestWaypoint_G2)); call = CALL_End(); }; // Get way point name var int wpPtr; var string nearestWp; if (wpPtr) { var zCWaypoint wp; wp = _^(wpPtr); nearestWp = wp.name; } else { nearestWp = ""; }; // Decide which items to delete var int matchedWp; matchedWp = FALSE; // Iterate over all previously collected way points repeat(j, wpArray.numInArray); var int j; // If there is already an instance near that way point, remove it if (Hlp_StrCmp(MEM_ReadStringArray(wpArray.array, j), nearestWp)) { matchedWp = TRUE; Wld_RemoveItem(vob); break; }; end; // If this is the first instance near that way point, remember the way point if (!matchedWp) { MEM_WriteStringArray(wpArray.array, wpArray.numInArray, nearestWp); wpArray.numInArray += 1; // Output some info SB(" ("); SBi(wpArray.numInArray); SB(") Remove all but one instance in proximity of "); SB(nearestWp); SB("."); MEM_Info(SB_ToString()); SB_Clear(); }; end; // Output summary SB("Removed "); SBi(itemArray.numInArray-wpArray.numInArray); SB(" out of "); SBi(itemArray.numInArray); SB(" "); SB(itemStr); SB(" instances, kept "); SBi(wpArray.numInArray); SB("."); MEM_Info(SB_ToString()); SB_Destroy(); // Free item arrays MEM_ArrayFree(wpArrayPtr); MEM_ArrayFree(itemArrayPtr); };