{$UNDEF SCRIPT_ID}{$DEFINE SCRIPT_ID := '48c08e37-258c-49c4-88be-a0098fb14948'} {$DEFINE SCRIPT_REVISION := '12'} {$IFDEF WINDOWS}{$DEFINE SCRIPT_GUI}{$ENDIF} {$I SRL-B/osr.simba} {$I BashLib/osr.simba} {$I BashLib/optional/handlers/discord.simba} {$I BashLib/optional/handlers/aichat.simba} {$IFDEF SCRIPT_GUI} {$I BashLib/optional/handlers/bashgui.simba} {$ENDIF} { ---------------------------------------------------------------------------------- ~~~ Need Support? Contact Me ~~~ Website: https://bashscripts.shop/ Discord Server: https://discord.gg/qsmKs5uKfR Email: thebigaussie@proton.me Github: https://github.com/BigAussie/BASH ---------------------------------------------------------------------------------- } type ERootsState = ( CHOP_ROOT, WAIT_CHOPPING, DROP_LOGS, END_SCRIPT ); TInfectedRootsChopper = record(TBaseScript) State: ERootsState; InfectedRoots: array[0..5] of TRSObjectV2; CurrentRootIndex: Int32; RootSwitchTimer: TCountDown; IsChopping: Boolean; LastXPTime: UInt64; PrevXP: Int32; RootTimer: TCountDown; StartingTears: Int32; TearsCollected: Int32; LastImageClear: UInt64; StartXP: Int32; LastReportTime: UInt64; NextWorldHopTime: UInt64; WorldHopsCompleted: Int32; LastWorldHopTime: Int64; ConsecutiveFailures: Int32; MaxFailuresBeforeSwitch: Int32; end; function TRSLogout.ClickLogout(attempts: Int32 = 5; tryTime: Int32 = 20000): Boolean; override; var interval: UInt64; i: Int32; begin if (not Self.Open()) or (not Self.CloseWorldSwitcher()) then Exit; interval := tryTime div attempts; for i := 1 to attempts do begin Self.ClearEnter(); if Self.GetButton(ERSLogoutButton.CLICK_HERE).Click() and WaitUntil(not RSClient.IsLoggedIn(), 500, Interval + SRL.NormalRange(-2000, 2000)) then Exit(True); end; end; procedure TAntiban.Setup(); override; begin Self.Skills := [ERSSkill.WOODCUTTING, ERSSkill.TOTAL]; Self.MinZoom := 15; Self.MaxZoom := 60; inherited; if OVERRIDEBREAKS then begin WriteLn('[ANTIBAN] Custom break override enabled:'); WriteLn(' - Break Interval: ', CUSTOMBREAKINTERVAL, ' minutes'); WriteLn(' - Break Duration: ', CUSTOMBREAKDURATION, ' minutes'); Self.Breaks := []; Self.AddBreak(CUSTOMBREAKINTERVAL * ONE_MINUTE, CUSTOMBREAKDURATION * ONE_MINUTE, 0.33, 0.15); WriteLn('[ANTIBAN] Custom breaks initialized successfully'); end else WriteLn('[ANTIBAN] Using WaspLib break system'); end; function TBaseScript.DoAntiban(checkBreaks: Boolean = True; checkSleeps: Boolean = True): Boolean; override; begin Antiban.DismissRandom(); Self.TimeRunning.Pause(); Self.OnAntiban := True; checkBreaks := checkBreaks and (WLSettings.GetObject('antiban').getBoolean('breaks') or OVERRIDEBREAKS); checkSleeps := checkSleeps and WLSettings.GetObject('antiban').getJSONObject('sleep').getBoolean('enabled'); if WLSettings.GetObject('antiban').getJSONObject('tasks').getBoolean('enabled') or checkBreaks or checkSleeps then Result := Antiban.DoAntiban(checkBreaks, checkSleeps); Self.TimeRunning.Resume(); Self.OnAntiban := False; if not RSClient.IsLoggedIn() then Login.LoginPlayer(); end; procedure TInfectedRootsChopper.SetupObjects(); var I: Int32; RootCoords: TPointArray; begin WriteLn('Setting up infected root objects...'); RootCoords := [[14264, 12362], [14248, 12390], [14292, 12394], [14164, 12390], [14144, 12362], [14120, 12366]]; for I := 0 to 5 do begin with Self.InfectedRoots[I] do begin Walker := @Map.Walker; Finder.Colors := [CTS2(3444448, 18, 0.07, 1.30)]; SetupEx([3, 3, 2], [[RootCoords[I].X, RootCoords[I].Y]]); SetupUpText('Chop'); end; end; WriteLn('Objects setup complete'); end; procedure TInfectedRootsChopper.Init(MaxActions: UInt32; MaxTime: UInt64); override; begin inherited; if AIChatbot.Enabled then begin WriteLn('[AI Chatbot] Enabled - initializing'); AIChatbot.Init(); end else WriteLn('[AI Chatbot] Disabled'); Map.SetupChunkEx([19,149, 21,147], [1]); Self.SetupObjects(); Objects.Setup(Map.Objects(), @Map.Walker); Self.IsChopping := False; Self.LastXPTime := 0; Self.PrevXP := XPBar.Read(); Self.RootTimer.Setup(15000); Self.CurrentRootIndex := Random(6); Self.RootSwitchTimer.Setup(Random(2 * 60 * 60 * 1000, 4 * 60 * 60 * 1000)); WriteLn('[INFO] Starting with root index: ', Self.CurrentRootIndex); Self.StartingTears := Inventory.CountItemStack('Demon tear'); Self.TearsCollected := 0; Self.LastImageClear := GetTickCount(); WriteLn('[TEARS] Starting tears count: ', Self.StartingTears); Self.StartXP := XPBar.Read(); Self.LastReportTime := 0; Self.ConsecutiveFailures := 0; Self.MaxFailuresBeforeSwitch := 3; Self.SetupWorldHopping(); end; procedure TInfectedRootsChopper.SwitchToNewRoot(); var OldIndex, NewIndex: Int32; begin OldIndex := Self.CurrentRootIndex; repeat NewIndex := Random(6); until NewIndex <> OldIndex; Self.CurrentRootIndex := NewIndex; Self.RootSwitchTimer.Restart(Random(2 * 60 * 60 * 1000, 4 * 60 * 60 * 1000)); // 2-4 hours to pick a new rnadom root Self.ConsecutiveFailures := 0; WriteLn('[INFO] Switched from root ', OldIndex, ' to root ', NewIndex); end; function TInfectedRootsChopper.GetRandomWorldHopTime(): UInt64; var BaseInterval: UInt64; RandomVariation: Integer; begin BaseInterval := WORLDHOPINTERVAL * 60000; RandomVariation := Random(-10, 10); Result := BaseInterval + Round(BaseInterval * (RandomVariation / 100.0)); WriteLn('[WORLDHOP] Next world hop scheduled in: ' + SRL.MsToTime(Result, Time_Short) + ' (Base: ' + IntToStr(WORLDHOPINTERVAL) + 'min, Variation: ' + IntToStr(RandomVariation) + '%)'); end; procedure TInfectedRootsChopper.SetupWorldHopping(); begin WriteLn('[WORLDHOP] Setting up world hopping...'); if not ENABLEWORLDHOPPING then begin WriteLn('[WORLDHOP] World hopping is disabled.'); Exit; end; if Length(Login.GetPlayer().Worlds) < 2 then begin WriteLn('[WORLDHOP] ERROR: World hopping is enabled but you have less than 2 worlds configured!'); WriteLn('[WORLDHOP] Please configure at least 2 worlds in your player setup or disable world hopping.'); Logout.ClickLogout(); TerminateScript(); end; Self.NextWorldHopTime := GetTickCount() + Self.GetRandomWorldHopTime(); WriteLn('[WORLDHOP] World hopping enabled. Next hop in: ' + SRL.MsToTime(Self.NextWorldHopTime - GetTickCount(), Time_Short)); end; function TInfectedRootsChopper.ShouldHopWorld(): Boolean; begin if not ENABLEWORLDHOPPING then Exit(False); if GetTickCount() < Self.NextWorldHopTime then Exit(False); Result := True; end; procedure TInfectedRootsChopper.DoWorldHop(); var currentWorld: Int32; playerWorlds: TIntegerArray; targetWorld: Int32; availableWorlds: TIntegerArray; i: Int32; begin if not ENABLEWORLDHOPPING then Exit; WriteLn('[WORLDHOP] Time to hop worlds!'); playerWorlds := Login.GetPlayer().Worlds; if not Logout.Open() then begin WriteLn('[WORLDHOP] Failed to open logout menu'); Self.NextWorldHopTime := GetTickCount() + 120000; Exit; end; if not Logout.IsWorldSwitcherOpen() then Logout.GetButton(ERSLogoutButton.WORLD_SWITCHER).Click(MOUSE_LEFT); WaitUntil((currentWorld := WorldHopper.GetCurrentWorld()) <> 0, 65, 20000); WriteLn('[WORLDHOP] Current world: ' + ToStr(currentWorld)); WriteLn('[WORLDHOP] Available worlds: ' + ToStr(playerWorlds)); availableWorlds := []; for i := 0 to High(playerWorlds) do begin if playerWorlds[i] <> currentWorld then availableWorlds += playerWorlds[i]; end; if Length(availableWorlds) = 0 then begin WriteLn('[WORLDHOP] No other worlds available to hop to, disabling world hopping!'); Exit; end; targetWorld := availableWorlds[Random(Length(availableWorlds))]; WriteLn('[WORLDHOP] Randomly selected target world: ' + ToStr(targetWorld)); if WorldHopper.Hop([targetWorld]) then begin WriteLn('[WORLDHOP] Successfully hopped to world ' + ToStr(targetWorld) + '!'); Inc(Self.WorldHopsCompleted); Self.LastWorldHopTime := GetTickCount(); Self.NextWorldHopTime := GetTickCount() + Self.GetRandomWorldHopTime(); WriteLn('[WORLDHOP] Next world hop in: ' + SRL.MsToTime(Self.NextWorldHopTime - GetTickCount(), Time_Short)); end else begin WriteLn('[WORLDHOP] World hop to ' + ToStr(targetWorld) + ' failed, trying again in 2 minutes'); Self.NextWorldHopTime := GetTickCount() + 120000; end; end; function TInfectedRootsChopper.ChopRoot(): Boolean; var TargetRoot: TRSObjectV2; TargetCoord: TPoint; begin Result := False; if Self.RootSwitchTimer.IsFinished() then begin Self.SwitchToNewRoot(); end; if Self.ConsecutiveFailures >= Self.MaxFailuresBeforeSwitch then begin WriteLn('[FAILSAFE] Too many consecutive failures (', Self.ConsecutiveFailures, '), switching to different root'); Self.SwitchToNewRoot(); Self.ConsecutiveFailures := 0; end; WriteLn('[INFO] Attempting to chop infected root at index: ', Self.CurrentRootIndex); TargetRoot := Self.InfectedRoots[Self.CurrentRootIndex]; if TargetRoot.IsVisible() then begin if TargetRoot.Click() then begin WriteLn('[SUCCESS] Clicked targeted root #', Self.CurrentRootIndex, ', waiting for XP...'); if XPBar.WaitXP(12000) then begin WriteLn('[SUCCESS] Started chopping infected root!'); Self.IsChopping := True; Self.LastXPTime := GetTickCount(); Self.PrevXP := XPBar.Read(); Self.RootTimer.Restart(); Self.ConsecutiveFailures := 0; WL.Activity.Restart(); Result := True; end else begin WriteLn('[WARN] Clicked root but no XP gained within 12 seconds'); Inc(Self.ConsecutiveFailures); end; end else begin WriteLn('[WARN] Failed to click visible root'); Inc(Self.ConsecutiveFailures); TargetCoord := TargetRoot.Coordinates[0]; WriteLn('[FAILSAFE] Attempting to walk closer to root at [', TargetCoord.X, ', ', TargetCoord.Y, ']'); if Map.Walker.WalkBlind(TargetCoord, 20) then begin WriteLn('[FAILSAFE] Walked closer to root, waiting before retry...'); Wait(1000, 2000); end end; end else begin TargetCoord := TargetRoot.Coordinates[0]; WriteLn('[INFO] Target root #', Self.CurrentRootIndex, ' not visible, walking blind to [', TargetCoord.X, ', ', TargetCoord.Y, ']'); WL.Activity.Restart(); if Map.Walker.WalkBlind(TargetCoord, 30) then begin WriteLn('[SUCCESS] Walked blind to root location'); end else begin WriteLn('[WARN] Failed to walk blind to root'); Inc(Self.ConsecutiveFailures); end; end; end; function TInfectedRootsChopper.IsStillChopping(): Boolean; var CurrentXP: Int32; TimeSinceXP: UInt64; IsAnimating: Boolean; TimeoutMS: Int32; begin CurrentXP := XPBar.Read(); if CurrentXP > Self.PrevXP then begin Self.PrevXP := CurrentXP; Self.LastXPTime := GetTickCount(); Self.RootTimer.Restart(); WL.Activity.Restart(); Result := True; Exit; end; if Self.RootTimer.IsFinished() or Inventory.IsFull() then begin Self.IsChopping := False; Result := False; Exit; end; IsAnimating := SRL.IsAnimating(MainScreen.GetPlayerBox(), MainScreen.NormalizeDistance(100), 350, 3); if IsAnimating then TimeoutMS := 20000 else TimeoutMS := 4000; TimeSinceXP := GetTickCount() - Self.LastXPTime; if TimeSinceXP > TimeoutMS then begin WriteLn('[INFO] No XP for ', TimeoutMS div 1000, ' seconds (Animating: ', IsAnimating, ')'); Self.IsChopping := False; Result := False; Exit; end; Result := True; end; function TInfectedRootsChopper.WaitChopping(): Boolean; begin while Self.IsStillChopping() do begin if Minimap.EnableSpec(100) then exit; Self.DoAntiban(); Self.UpdateTearsCount(); if (GetTimeRunning() mod 1000) < 500 then Self.DrawStatusDisplay(); if (GetTimeRunning() mod 10000) < 1000 then Self.Report(); end; Result := True; end; function TInfectedRootsChopper.DropLogs(): Boolean; var Logs: TRSItem; begin Logs := 'Logs'; if not Inventory.ContainsItem(Logs) then begin WriteLn('[INFO] No logs to drop'); Exit(True); end; WriteLn('[INFO] Dropping logs...'); Result := Inventory.ShiftDrop([Logs], Inventory.RandomPattern()); Self.IsChopping := False; if Result then WriteLn('[SUCCESS] Dropped all logs') else WriteLn('[WARN] Failed to drop logs'); end; function TInfectedRootsChopper.ShouldDropLogs(): Boolean; begin if Inventory.IsFull() then Exit(True); if Inventory.ContainsItem('Logs') and (Random(5) = 0) then Exit(True); Result := False; end; function TInfectedRootsChopper.GetState(): ERootsState; begin if WL.Activity.IsFinished() then Exit(ERootsState.END_SCRIPT); // If we need to drop logs if Self.ShouldDropLogs() then begin Self.IsChopping := False; Exit(ERootsState.DROP_LOGS); end; // If we're currently chopping if Self.IsChopping then Exit(ERootsState.WAIT_CHOPPING); // chop root Exit(ERootsState.CHOP_ROOT); end; function TInfectedRootsChopper.GetCurrentActivity(): String; begin case Self.State of ERootsState.CHOP_ROOT: Result := 'Finding Root'; ERootsState.WAIT_CHOPPING: Result := 'Chopping Root'; ERootsState.DROP_LOGS: Result := 'Dropping Logs'; ERootsState.END_SCRIPT: Result := 'Ending'; else Result := 'Unknown'; end; end; procedure TInfectedRootsChopper.UpdateTearsCount(); var CurrentTotalTears: Int32; NewSessionTears: Int32; begin CurrentTotalTears := Inventory.CountItemStack('Demon tear'); NewSessionTears := CurrentTotalTears - Self.StartingTears; Self.TearsCollected := NewSessionTears; end; procedure TInfectedRootsChopper.DrawUIOverlay(drawBox: TBox; title, status, runtime, tears: String); var yPos: Int32; begin {$IFNDEF SRL_DISABLE_REMOTEINPUT} RSClient.Image().DrawBoxFilled(drawBox, False, $2D2D30); RSClient.Image().DrawBoxFilled(drawBox.Expand(1), False, $FFFFFF); RSClient.Image().DrawBoxFilled(drawBox, False, $2D2D30); RSClient.Image().SetFontName('Arial'); RSClient.Image().SetFontSize(10); yPos := drawBox.Y1 + 6; RSClient.Image().SetFontSize(13); RSClient.Image().DrawText(title, Point(drawBox.X1 + 3, yPos), $00D4FF); yPos += 14; RSClient.Image().SetFontSize(11); RSClient.Image().DrawText('Status: ' + status, Point(drawBox.X1 + 3, yPos), $FFFFFF); yPos += 12; RSClient.Image().DrawText('Runtime: ' + runtime, Point(drawBox.X1 + 3, yPos), $FFFFFF); yPos += 12; RSClient.Image().DrawText('Tears: ' + tears, Point(drawBox.X1 + 3, yPos), $FFFFFF); {$ENDIF} end; procedure TInfectedRootsChopper.DrawStatusDisplay(); var statusBox: TBox; currentActivity, runtime, tears: String; begin statusBox := Box(10, 260, 150, 320); currentActivity := Self.GetCurrentActivity(); runtime := SRL.MsToTime(GetTimeRunning(), Time_Short); tears := ToStr(Self.TearsCollected); Self.DrawUIOverlay(statusBox, 'Infected Roots Chopper', currentActivity, runtime, tears); end; procedure TInfectedRootsChopper.Report(); var Runtime: Int64; CurrentXP, XPGained: Int32; XPPerHour, TearsPerHour: Int32; CurrentTime: UInt64; begin CurrentTime := GetTickCount(); if (CurrentTime - Self.LastReportTime) < 10000 then Exit; Self.LastReportTime := CurrentTime; Runtime := GetTimeRunning(); CurrentXP := XPBar.Read(); XPGained := CurrentXP - Self.StartXP; if Runtime > 0 then begin XPPerHour := Round(XPGained / (Runtime / 3600000)); TearsPerHour := Round(Self.TearsCollected / (Runtime / 3600000)); end else begin XPPerHour := 0; TearsPerHour := 0; end; WriteLn('========================================'); WriteLn(' Infected Roots Chopper'); WriteLn('========================================'); WriteLn(' Runtime: ' + SRL.MsToTime(Runtime, Time_Short)); WriteLn(' XP Gained: ' + ToStr(XPGained)); WriteLn(' XP/Hour: ' + ToStr(XPPerHour)); WriteLn(' Tears Collected: ' + ToStr(Self.TearsCollected)); WriteLn(' Tears/Hour: ' + ToStr(TearsPerHour)); if ENABLEWORLDHOPPING then begin WriteLn(' World Hops: ' + ToStr(Self.WorldHopsCompleted)); WriteLn(' Next Hop: ' + SRL.MsToTime(Self.NextWorldHopTime - GetTickCount(), Time_Short)); end; WriteLn(' ', Antiban.BreakScheduleText()); WriteLn(' ', Antiban.SleepScheduleText()); WriteLn('========================================'); end; procedure TInfectedRootsChopper.Run(MaxActions: UInt32; MaxTime: UInt64); begin Self.Init(MaxActions, MaxTime); try if RSClient.RemoteInput.IsSetup() then RSClient.Image().Clear(); except end; Self.DrawStatusDisplay(); repeat if Self.ShouldHopWorld() then begin Self.DoWorldHop(); Wait(2000, 4000); end; Self.State := Self.GetState(); Self.SetAction(ToStr(Self.State)); case Self.State of ERootsState.CHOP_ROOT: Self.ChopRoot(); ERootsState.WAIT_CHOPPING: Self.WaitChopping(); ERootsState.DROP_LOGS: Self.DropLogs(); ERootsState.END_SCRIPT: Break; end; Self.UpdateTearsCount(); if (GetTickCount() - Self.LastImageClear) >= 30000 then begin try RSClient.Image.Clear; except end; Self.DrawStatusDisplay(); Self.LastImageClear := GetTickCount(); end; if (GetTimeRunning() mod 1000) < 500 then Self.DrawStatusDisplay(); if (GetTimeRunning() mod 10000) < 1000 then Self.Report(); until Self.ShouldStop(); Self.Report(); end; {$IFDEF SCRIPT_GUI} type TConfig = record(TBASHPremiumGUI) ActionHint: TLabel; end; TScriptGUI = TConfig; procedure TConfig.InitGUI(); override; begin Self.BrandTitle := 'Infected Roots'; Self.WindowTitle := 'BASH Scripts Infected Roots Chopper'; Self.WebhookTestPrefix := 'Test message from BASH Scripts Infected Roots Chopper'; Self.HasFarm := False; Self.HasAIChat := True; Self.HasWorldHopping := True; Self.HasMaxActions := True; Self.HasStopAtLevel := False; Self.LegacyAntibanSection := ' Infected Roots Antiban'; end; procedure TConfig.SetupAIChat(); override; begin inherited; AIChatbot.SetScriptDefaults('chopping infected roots'); end; procedure TConfig.LoadActionSettings(); override; var hop, hopInt: String; begin hop := ReadINI(Self.Username + ' Infected Roots Settings', 'EnableWorldHopping', BASH_GUI_SETTINGS_FILE); hopInt := ReadINI(Self.Username + ' Infected Roots Settings', 'WorldHopInterval', BASH_GUI_SETTINGS_FILE); if (Self.ReadAntibanINI('EnableWorldHopping', '') = '') and (hop <> '') then Self.WriteAntibanINI('EnableWorldHopping', hop); if (Self.ReadAntibanINI('WorldHopInterval', '') = '') and (hopInt <> '') then Self.WriteAntibanINI('WorldHopInterval', hopInt); end; procedure TConfig.BuildActionSection(); override; var hint: TLabel; begin Self.AddHeader(Self.ActionSection, 'Action', 'Chop infected roots in the Ruins of Mokhaiotl.'); hint := Self.MakeLabel(Self.ActionSection, 'Chops infected roots, drops logs, and rotates roots every 2-4 hours.', 24, 110, 10, BASH_GUI_MUTED, False); hint.SetWidth(TControl.AdjustToDPI(820)); hint := Self.MakeLabel(Self.ActionSection, 'Configure your account and antiban options in the other tabs, then press Start.', 24, 140, 10, BASH_GUI_MUTED, False); hint.SetWidth(TControl.AdjustToDPI(820)); end; procedure TConfig.ApplyActionSettings(); override; begin WriteINI(Self.Username + ' Infected Roots Settings', 'EnableWorldHopping', BoolToStr(ENABLEWORLDHOPPING, 'true', 'false'), BASH_GUI_SETTINGS_FILE); WriteINI(Self.Username + ' Infected Roots Settings', 'WorldHopInterval', IntToStr(WORLDHOPINTERVAL), BASH_GUI_SETTINGS_FILE); end; procedure TConfig.StartScript(Sender: TObject); override; begin if Sender = nil then; try Self._WarmBuildCache(); except end; inherited; end; {$IFDEF SCRIPT_GUI} {$I BashLib/optional/handlers/bashgui_host_scriptgui.simba} {$ENDIF} var Config: TConfig; {$ENDIF} var InfectedRootsBot: TInfectedRootsChopper; begin {$IFDEF SCRIPT_GUI} Sync(@Config.Run); {$ENDIF} InfectedRootsBot.Run(WLSettings.MaxActions, WLSettings.MaxTime); end.