SimbaScripts/degrime.simba
2025-03-18 13:16:58 +01:00

667 lines
22 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{$DEFINE SCRIPT_ID := 'degrime-002'}
{$DEFINE SCRIPT_REVISION := '1'}
{$IFDEF WINDOWS}{$DEFINE SCRIPT_GUI}{$ENDIF}
{$I SRL-T/osr.simba}
{$I WaspLib/osr.simba}
{-------------------------------------------------------------------------
Constants for timing and wait values used throughout the script.
-------------------------------------------------------------------------}
const
// General wait constants
cInterfaceCloseDelay = 300; // Delay after closing interfaces
cWaitUntilDelay = 65; // Poll delay for WaitUntil calls
cRandomAdjustment = 1000; // Maximum random adjustment
// Magic interface retry and castable wait times
cMagicInterfaceRetryDelay = 500; // Delay to retry opening magic interface
cCastableWaitMin = 400; // Minimum wait time for checking castability
cCastableWaitExpected = 500; // Expected wait time for checking castability
cCastableWaitMax = 600; // Maximum wait time for checking castability
cSkewParam = 3; // Skew parameter for all srl.SkewedRand calls
// Wait times in the cast animation (WaitCast procedure)
cUnfocusedWaitMin = 5000; // Minimum wait time when unfocused
cUnfocusedWaitExpected = 7000; // Expected wait time when unfocused
cUnfocusedWaitMax = 15000; // Maximum wait time when unfocused
cFocusedWaitMin = 2400; // Minimum wait time when focused
cFocusedWaitExpected = 2600; // Expected wait time when focused
cFocusedWaitMax = 2700; // Maximum wait time when focused
// Wait times for bank deposit and withdrawal actions
cDepositWaitMin = 450; // Minimum wait time for deposit
cDepositWaitExpected = 550; // Expected wait time for deposit
cDepositWaitMax = 650; // Maximum wait time for deposit
cWithdrawWaitMin = 300; // Minimum wait time for withdrawal
cWithdrawWaitExpected = 400; // Expected wait time for withdrawal
cWithdrawWaitMax = 550; // Maximum wait time for withdrawal
cBankOpenWaitMin = 300; // Minimum wait time for bank open
cBankOpenWaitExpected = 400; // Expected wait time for bank open
cBankOpenWaitMax = 550; // Maximum wait time for bank open
cPostBankCloseDelay = 500; // Delay after closing bank
// Delay for main loop iterations in Run procedure
cLoopDelayMin = 300; // Minimum loop delay
cLoopDelayExpected = 400; // Expected loop delay
cLoopDelayMax = 500; // Maximum loop delay
// Focus duration constants (for focus-loss simulation)
cFocusSkewMin = 2; // Minimum skew for focus duration calculation
cFocusSkewMax = 4; // Maximum skew for focus duration calculation
cFocusSwitchChance = 1; // Early switch chance threshold (in 0..cFocusRandomRange scale)
cFocusRandomRange = 1000; // Random range used for early focus switch check
cFocusedDurationExpected = 15 * 60 * 1000; // Expected focused duration (15 minutes in ms)
cFocusedDurationMin = 0; // Minimum focused duration for srl.SkewedRand call
cFocusedDurationMax = 20 * 60 * 1000; // Maximum focused duration (20 minutes in ms)
cUnfocusedDurationExpected = 4 * 60 * 1000; // Expected unfocused duration (4 minutes in ms)
cUnfocusedDurationMin = 0; // Minimum unfocused duration for srl.SkewedRand call
cUnfocusedDurationMax = 8 * 60 * 1000; // Maximum unfocused duration (8 minutes in ms)
// Time conversion constant
cMsPerHour = 3600000; // Milliseconds per hour
{-------------------------------------------------------------------------
End of constants
-------------------------------------------------------------------------}
{-------------------------------------------------------------------------
Shared Helper Functions
-------------------------------------------------------------------------}
{ Returns a wait time using the skewed random function with given expected, min, and max values. }
function GetWaitTime(expected, minVal, maxVal: Integer): Integer;
begin
Result := srl.SkewedRand(expected, minVal, maxVal, cSkewParam);
end;
{ Waits until the bank is open.
If the bank is not open, it walks to the bank and waits until open. }
procedure WaitForBankOpen();
begin
if not Bank.IsOpen() then
begin
Bank.WalkOpen();
WaitUntil(Bank.IsOpen(), cWaitUntilDelay, GetWaitTime(cBankOpenWaitExpected, cBankOpenWaitMin, cBankOpenWaitMax));
end;
end;
{ Deposits a bank item and waits until the specified item is no longer in the inventory. }
procedure DepositItemWithWait(bankItem: TRSBankItem; itemName: string);
begin
Bank.DepositItem(bankItem, True);
WaitUntil(not Inventory.ContainsItem(itemName), cWaitUntilDelay,
GetWaitTime(cDepositWaitExpected, cDepositWaitMin, cDepositWaitMax));
end;
{ Withdraws a bank item and waits until the specified item is present in the inventory. }
procedure WithdrawItemWithWait(bankItem: TRSBankItem; itemName: string);
begin
Bank.WithdrawItem(bankItem, True);
WaitUntil(Inventory.ContainsItem(itemName), cWaitUntilDelay,
GetWaitTime(cWithdrawWaitExpected, cWithdrawWaitMin, cWithdrawWaitMax));
end;
{ Checks if the required runes (or rune pouches) are available.}
function HasRequiredRunes(): Boolean;
begin
Result := ((Inventory.CountItem('Earth rune') >= 4) and (Inventory.CountItem('Nature rune') >= 2))
or Inventory.ContainsItem('Rune pouch')
or Inventory.ContainsItem('Divine rune pouch');
end;
{-------------------------------------------------------------------------
End of shared helpers
-------------------------------------------------------------------------}
type
// State machine reflecting in-game flow.
TDegrimeState = (
DS_INVENTORY_CHECK,
DS_CHECK_CASTABLE,
DS_CAST_WAIT,
DS_BANK,
DS_TERMINATE
);
// Enumeration for the herbs.
THerbs = (
herbHuasca,
herbTarromin,
herbHarralander,
herbMarrentill,
herbGuamLeaf,
herbIritLeaf,
herbAvantoe,
herbDwarfWeed,
herbLantadyme,
herbToadflax,
herbCadantine,
herbKwuarm,
herbTorstol,
herbRanarrWeed,
herbSnapdragon
);
const
// Mapping from enum to the full "Grimy ..." herb name.
HerbNames: array[THerbs] of string = [
'Grimy Huasca',
'Grimy Tarromin',
'Grimy Harralander',
'Grimy Marrentill',
'Grimy Guam Leaf',
'Grimy Irit Leaf',
'Grimy Avantoe',
'Grimy Dwarf Weed',
'Grimy Lantadyme',
'Grimy Toadflax',
'Grimy Cadantine',
'Grimy Kwuarm',
'Grimy Torstol',
'Grimy Ranarr Weed',
'Grimy Snapdragon'
];
// Mapping from enum to the clean herb name.
CleanHerbNames: array[THerbs] of string = [
'Huasca',
'Tarromin',
'Harralander',
'Marrentill',
'Guam Leaf',
'Irit Leaf',
'Avantoe',
'Dwarf Weed',
'Lantadyme',
'Toadflax',
'Cadantine',
'Kwuarm',
'Torstol',
'Ranarr Weed',
'Snapdragon'
];
var
GlobalCurrentHerb: THerbs = herbIritLeaf;
SimulateFocusLoss: Boolean = False; // Global flag to enable focus-loss simulation
type
// Type for focus state.
TFocusState = (fsFocused, fsUnfocused);
// Main script record inherits from TBaseBankScript.
TDegrimeScript = record(TBaseBankScript)
State: TDegrimeState;
CastCount: UInt32;
CastRetries: Integer;
CurrentHerb: THerbs;
CleanHerbBank: TRSBankItem;
GrimyHerbBank: TRSBankItem;
StartXP: Integer;
FocusState: TFocusState;
FocusTimer: Int64;
end;
{-------------------------------------------------------------------------
Setup: Configure antiban settings.
-------------------------------------------------------------------------}
procedure TAntiban.Setup(); override;
begin
Antiban.Skills := [ERSSkill.TOTAL, ERSSKILL.MAGIC];
Antiban.MinZoom := 63;
Antiban.MaxZoom := 83;
inherited;
end;
{-------------------------------------------------------------------------
Helper Methods for Script Flow
-------------------------------------------------------------------------}
{ DS_INIT: Initialization close interfaces, set bank region, and initialize focus timer. }
procedure TDegrimeScript.Init(maxActions: UInt32; maxTime: UInt64); override;
begin
inherited;
RSInterface.Close();
Wait(cInterfaceCloseDelay);
Self.RSW.SetupNamedRegion();
// Initialize herb selection and bank items.
Self.CurrentHerb := GlobalCurrentHerb;
Self.CleanHerbBank := TRSBankItem.Setup(CleanHerbNames[Self.CurrentHerb], Bank.QUANTITY_ALL, True);
Self.GrimyHerbBank := TRSBankItem.Setup(HerbNames[Self.CurrentHerb], Bank.QUANTITY_ALL, False);
Self.CastCount := 0;
Self.CastRetries := 0;
// Initialize XP reporting and focus simulation if enabled.
Self.StartXP := XPBar.Read();
if SimulateFocusLoss then
begin
Self.FocusState := fsFocused;
Self.FocusTimer := GetTimeRunning() +
GetWaitTime(cFocusedDurationExpected, cFocusedDurationMin, cFocusedDurationMax)
+ Random(-cRandomAdjustment, cRandomAdjustment);
end
else
begin
Self.FocusState := fsFocused;
Self.FocusTimer := 0;
end;
Self.State := DS_INVENTORY_CHECK;
end;
{ DS_INVENTORY_CHECK: Verify that the inventory contains the grimy herb and required runes. }
procedure TDegrimeScript.InventoryCheck();
begin
Self.SetAction('Checking inventory for ' + HerbNames[Self.CurrentHerb] + ' and required runes.');
if Inventory.Count() = 0 then
begin
Self.SetAction('Inventory is empty. Terminating.');
Self.State := DS_TERMINATE;
Exit;
end;
if not Inventory.ContainsItem(HerbNames[Self.CurrentHerb]) then
begin
Self.SetAction('No grimy herb in inventory. Switching to bank.');
Self.State := DS_BANK;
Exit;
end;
if not HasRequiredRunes() then
begin
Self.SetAction('Missing required runes/pouch in inventory. Switching to bank.');
Self.State := DS_BANK;
end
else
Self.State := DS_CHECK_CASTABLE;
end;
{ DS_CHECK_CASTABLE: Open magic interface and verify the Degrime spell is castable. }
procedure TDegrimeScript.CheckCastable();
var
waitTime: Integer;
begin
if not Magic.Open() then
begin
Self.SetAction('Magic interface failed to open. Retrying...');
Wait(cMagicInterfaceRetryDelay);
if not Magic.Open() then
begin
Self.SetAction('Unable to open magic interface. Terminating.');
Self.State := DS_TERMINATE;
Exit;
end;
end;
waitTime := GetWaitTime(cCastableWaitExpected, cCastableWaitMin, cCastableWaitMax);
Wait(waitTime);
if Magic.CanActivate(ERSSpell.Degrime) then
begin
Self.SetAction('Spell is castable.');
Self.State := DS_CAST_WAIT;
end
else
begin
Self.SetAction('Spell unavailable. Terminating.');
Self.State := DS_TERMINATE;
end;
end;
{ DS_CAST_WAIT: Attempts to cast the spell and waits the appropriate amount of time to take the next action}
procedure TDegrimeScript.CastAndWait();
var
waitTime: Integer;
begin
Self.CheckCastable();
if Self.State = DS_TERMINATE then Exit;
if Magic.CanActivate(ERSSpell.Degrime) then
begin
Self.SetAction('Casting Degrime spell.');
Inc(Self.CastCount);
Magic.CastSpell(ERSSpell.Degrime);
end
else
begin
Self.State := DS_TERMINATE;
Exit;
end;
if SimulateFocusLoss then
Self.SwitchFocusState();
if SimulateFocusLoss and (Self.FocusState = fsUnfocused) then
begin
waitTime := GetWaitTime(cUnfocusedWaitExpected, cUnfocusedWaitMin, cUnfocusedWaitMax)
+ Random(-cRandomAdjustment, cRandomAdjustment);
Self.SetAction('Unfocused: waiting for cast animation (' + ToStr(waitTime) + ' ms).');
end
else
begin
waitTime := GetWaitTime(cFocusedWaitExpected, cFocusedWaitMin, cFocusedWaitMax);
Self.SetAction('Waiting for cast animation (' + ToStr(waitTime) + ' ms).');
end;
Bank.Hover();
Wait(waitTime);
Self.Report();
Self.State := DS_BANK;
end;
{ Deposits clean herbs using the shared helper function. }
procedure TDegrimeScript.DepositCleanHerbs();
begin
Self.SetAction('Depositing clean herbs.');
DepositItemWithWait(Self.CleanHerbBank, CleanHerbNames[Self.CurrentHerb]);
end;
{ Withdraws grimy herbs using the shared helper function. }
procedure TDegrimeScript.WithdrawGrimyHerbs();
begin
if Inventory.ContainsItem(CleanHerbNames[Self.CurrentHerb]) then
begin
Self.SetAction('Deposit might have failed, retry');
DepositCleanHerbs();
end;
if Bank.ContainsItem(HerbNames[Self.CurrentHerb]) then
begin
Self.SetAction('Withdrawing grimy herbs.');
WithdrawItemWithWait(Self.GrimyHerbBank, HerbNames[Self.CurrentHerb]);
end
else
begin
if (not Inventory.ContainsItem(CleanHerbNames[Self.CurrentHerb])) then
begin
Self.SetAction('No grimy herbs left in bank. Terminating.');
Self.State := DS_TERMINATE;
end
else
begin
Self.SetAction('Deposit might have failed, retry');
DepositCleanHerbs();
end
end;
end;
{ DS_BANK: Handle bank interactions (deposit/withdraw). }
procedure TDegrimeScript.BankInteraction();
var
runesOk: Boolean;
begin
Self.SetAction('Performing bank interaction.');
WaitForBankOpen();
runesOk := ((Inventory.CountItem('Earth rune') >= 4) and (Inventory.CountItem('Nature rune') >= 2))
or Inventory.ContainsItem('Rune pouch')
or Inventory.ContainsItem('Divine rune pouch');
{ Case 1: Inventory contains grimy herbs. }
if Inventory.ContainsItem(HerbNames[Self.CurrentHerb]) then
begin
if runesOk then
begin
Self.SetAction('Grimy herb present with required runes. Closing bank.');
Bank.Close();
Wait(cPostBankCloseDelay);
Self.State := DS_CHECK_CASTABLE;
end
else
begin
Self.SetAction('Grimy herb present but missing required runes. Terminating.');
Self.State := DS_TERMINATE;
end;
Exit;
end
{ Case 2: Inventory contains clean herbs. }
else if Inventory.ContainsItem(CleanHerbNames[Self.CurrentHerb]) then
begin
DepositCleanHerbs();
WithdrawGrimyHerbs();
if Self.State <> DS_TERMINATE then
Self.State := DS_CHECK_CASTABLE;
Exit;
end
{ Case 3: Inventory is empty. }
else if Inventory.Count() = 0 then
begin
Self.SetAction('Inventory is empty. Terminating.');
Self.State := DS_TERMINATE;
Exit;
end
{ Case 4: Fallback unexpected state. }
else
begin
if runesOk and Bank.ContainsItem(HerbNames[Self.CurrentHerb]) then
begin
WithdrawGrimyHerbs();
if Self.State <> DS_TERMINATE then
Self.State := DS_CHECK_CASTABLE;
end
else
begin
Self.SetAction('Missing required runes/pouch or herbs. Terminating.');
Self.State := DS_TERMINATE;
end;
end;
end;
{-------------------------------------------------------------------------
Focus-State Functionality
-------------------------------------------------------------------------}
{ SwitchFocusState:
Determines when to switch between focused and unfocused modes using
predefined durations and a chance for an early switch.
(inspired by BigAussie AIO Lunar Spells)
}
procedure TDegrimeScript.SwitchFocusState();
var
skewness: Integer;
focusedDuration, unfocusedDuration: Integer;
begin
skewness := Random(cFocusSkewMin, cFocusSkewMax);
focusedDuration := GetWaitTime(cFocusedDurationExpected, cFocusedDurationMin, cFocusedDurationMax)
+ Random(-cRandomAdjustment, cRandomAdjustment);
unfocusedDuration := GetWaitTime(cUnfocusedDurationExpected, cUnfocusedDurationMin, cUnfocusedDurationMax)
+ Random(-cRandomAdjustment, cRandomAdjustment);
if GetTimeRunning() >= Self.FocusTimer then
begin
if Self.FocusState = fsFocused then
begin
Self.FocusState := fsUnfocused;
Self.SetAction('Switched to Unfocused mode for ' + ToStr(unfocusedDuration) + ' ms.');
Self.FocusTimer := GetTimeRunning() + unfocusedDuration;
end
else
begin
Self.FocusState := fsFocused;
Self.SetAction('Switched to Focused mode for ' + ToStr(focusedDuration) + ' ms.');
Self.FocusTimer := GetTimeRunning() + focusedDuration;
end;
end
else
begin
if Random(cFocusRandomRange) < cFocusSwitchChance then
begin
if Self.FocusState = fsFocused then
begin
Self.FocusState := fsUnfocused;
Self.SetAction('Early switch to Unfocused mode.');
Self.FocusTimer := GetTimeRunning() + unfocusedDuration;
end
else
begin
Self.FocusState := fsFocused;
Self.SetAction('Early switch to Focused mode.');
Self.FocusTimer := GetTimeRunning() + focusedDuration;
end;
end;
end;
end;
{-------------------------------------------------------------------------
Reporting Functionality
-------------------------------------------------------------------------}
{ Report: Logs total casts, runtime, and XP splits for magic and herblore.}
procedure TDegrimeScript.Report();
const
ReportWidth = 60;
var
currentXP, totalGainedXP, magicExp, herbloreExp: Integer;
castsPerHour, magicExpPerHour, herbloreExpPerHour: Integer;
runtimeStr, focusStateStr, header, titleLine: string;
timeInHours, leftPadding: Double;
begin
currentXP := XPBar.Read();
totalGainedXP := currentXP - Self.StartXP;
magicExp := 83 * Self.CastCount;
herbloreExp := totalGainedXP - magicExp;
timeInHours := GetTimeRunning() / cMsPerHour;
castsPerHour := Round(Self.CastCount / timeInHours);
magicExpPerHour := Round(magicExp / timeInHours);
herbloreExpPerHour := Round(herbloreExp / timeInHours);
runtimeStr := SRL.MsToTime(GetTimeRunning, Time_Short);
if Self.FocusState = fsFocused then
focusStateStr := 'Focused'
else
focusStateStr := 'Unfocused';
header := StringOfChar('=', ReportWidth);
titleLine := 'Degrime Cast Report';
leftPadding := (ReportWidth - Length(titleLine)) / 2;
WriteLn(header);
WriteLn(StringOfChar(' ', Trunc(leftPadding)) + titleLine);
WriteLn(header);
WriteLn(Format('%-16s %10s %-18s %10s', ['Casts:', ToStr(Self.CastCount), 'Casts/Hour:', ToStr(castsPerHour)]));
WriteLn(Format('%-16s %10s %-18s %10s', ['Magic Exp:', ToStr(magicExp), 'Magic Exp/Hour:', ToStr(magicExpPerHour)]));
WriteLn(Format('%-16s %10s %-18s %10s', ['Herblore Exp:', ToStr(herbloreExp), 'Herblore Exp/Hour:', ToStr(herbloreExpPerHour)]));
WriteLn(Format('%-16s %10s %-18s %10s', ['Runtime:', runtimeStr, 'Focus State:', focusStateStr]));
WriteLn(header);
end;
{Override the standard PrintReport as I won't be using it}
procedure TDegrimeScript.PrintReport(); override;
begin
Self.PreviousAction := Self.Action;
end;
{-------------------------------------------------------------------------
Main Run Loop
-------------------------------------------------------------------------}
procedure TDegrimeScript.Run(maxActions: UInt32; maxTime: UInt64);
var
loopDelay: Integer;
begin
Self.Init(maxActions, maxTime);
while not Self.ShouldStop() do
begin
case Self.State of
DS_INVENTORY_CHECK: Self.InventoryCheck();
DS_CHECK_CASTABLE: Self.CheckCastable();
DS_CAST_WAIT: Self.CastAndWait();
DS_BANK: Self.BankInteraction();
DS_TERMINATE: Break;
end;
Self.DoAntiban();
loopDelay := GetWaitTime(cLoopDelayExpected, cLoopDelayMin, cLoopDelayMax);
Wait(loopDelay);
end;
Self.SetAction('Script terminated. Total casts: ' + ToStr(Self.CastCount));
end;
{$IFDEF SCRIPT_GUI}
type
TDegrimeConfig = record(TScriptForm)
HerbSelector: TLabeledCombobox;
BankCheckbox: TLabeledCheckbox;
FocusLossCheckbox: TLabeledCheckbox;
end;
procedure TDegrimeConfig.StartScript(sender: TObject); override;
begin
case Self.HerbSelector.GetItemIndex() of
0: GlobalCurrentHerb := herbHuasca;
1: GlobalCurrentHerb := herbTarromin;
2: GlobalCurrentHerb := herbHarralander;
3: GlobalCurrentHerb := herbMarrentill;
4: GlobalCurrentHerb := herbGuamLeaf;
5: GlobalCurrentHerb := herbIritLeaf;
6: GlobalCurrentHerb := herbAvantoe;
7: GlobalCurrentHerb := herbDwarfWeed;
8: GlobalCurrentHerb := herbLantadyme;
9: GlobalCurrentHerb := herbToadflax;
10: GlobalCurrentHerb := herbCadantine;
11: GlobalCurrentHerb := herbKwuarm;
12: GlobalCurrentHerb := herbTorstol;
13: GlobalCurrentHerb := herbRanarrWeed;
14: GlobalCurrentHerb := herbSnapdragon;
else
GlobalCurrentHerb := herbMarrentill;
end;
SimulateFocusLoss := Self.FocusLossCheckbox.IsChecked();
inherited;
end;
procedure TDegrimeConfig.Run(); override;
var
tab: TTabSheet;
begin
Self.Setup('Degrime Script Config');
Self.Start.SetOnClick(@Self.StartScript);
Self.AddTab('Script Settings');
tab := Self.Tabs[High(Self.Tabs)];
with Self.HerbSelector do
begin
Create(tab);
SetCaption('Select Herb:');
SetLeft(20);
SetTop(50);
Combobox.SetStyle(csDropDownList);
Combobox.GetItems.Add('Grimy Huasca');
Combobox.GetItems.Add('Grimy Tarromin');
Combobox.GetItems.Add('Grimy Harralander');
Combobox.GetItems.Add('Grimy Marrentill');
Combobox.GetItems.Add('Grimy Guam Leaf');
Combobox.GetItems.Add('Grimy Irit Leaf');
Combobox.GetItems.Add('Grimy Avantoe');
Combobox.GetItems.Add('Grimy Dwarf Weed');
Combobox.GetItems.Add('Grimy Lantadyme');
Combobox.GetItems.Add('Grimy Toadflax');
Combobox.GetItems.Add('Grimy Cadantine');
Combobox.GetItems.Add('Grimy Kwuarm');
Combobox.GetItems.Add('Grimy Torstol');
Combobox.GetItems.Add('Grimy Ranarr Weed');
Combobox.GetItems.Add('Grimy Snapdragon');
Combobox.SetItemIndex(5); // Default herb selection.
end;
with Self.FocusLossCheckbox do
begin
Create(tab);
SetCaption('Simulate Losing Focus');
SetLeft(20);
SetTop(Self.HerbSelector.GetBottom() + 20);
SetHint('Enable to occasionally switch focus state and cast with slower timers.');
end;
Self.CreateBankSettings();
Self.CreateAntibanManager();
Self.CreateWaspLibSettings();
Self.CreateAPISettings();
inherited;
end;
var
DegrimeConfig: TDegrimeConfig;
{$ENDIF}
var
DegrimeScript: TDegrimeScript;
{$IFNDEF SCRIPT_CHAIN}
begin
{$IFDEF SCRIPT_GUI}
DegrimeConfig.Run();
{$ENDIF}
DegrimeScript.Run(WLSettings.MaxActions, WLSettings.MaxTime);
end.
{$ENDIF}