I did a little work on this tonight. I got down to MSI_Cast. I'd like to basically rewrite from MSI_Cast and down. Split things up, utilize the new type, and just generally clean it up.
const
SLOT_ALCH_LOW = 6;
SLOT_ALCH_HIGH = 16;
MAX_RUNES = 1000000000; // A billion
ANIM_COL_ALCH = 500; // Minimum pixel shift
CASTS_BEFORE_REPORT = 50; // Will perform this many casts before printing the report
SPELL_ARRAY_LENGTH = 27;
type
TMagicSpell = record
Name: string;
StatsName: string;
Level: Integer;
Experience: Integer;
Staffs: TIntegerArray;
ClickBox: TBox;
AutoCast: Boolean;
RequiredRunes: array[0..(RUNE_COUNT - 1)] of Integer;
end;
var
MSI_Spells: array[0..SPELL_ARRAY_LENGTH] of TMagicSpell;
procedure MSI_LoadSpell(Spell: Integer);
begin
case Spell of
SPELL_ALCH_LOW:
with MSI_Spells[Spell] do
begin
Name := 'Low Level Alchemy';
StatsName := 'Low Alchs';
Level := 21;
Experience := 10;
ClickBox := IntToBox();
AutoCast := False;
RequiredRunes[RUNE_FIRE] := 5;
RequiredRunes[RUNE_NATURE] := 1;
end;
SPELL_ALCH_HIGH:
with MSI_Spells[Spell] do
begin
Name := 'High Level Alchemy';
StatsName := 'High Alchs';
Level := 55;
Experience := 10;
ClickBox := IntToBox();
AutoCast := False;
RequiredRunes[RUNE_FIRE] := 5;
RequiredRunes[RUNE_NATURE] := 1;
end;
end;
end;
procedure MSI_LoadAllSpells();
var
i: Integer;
begin
for i := 0 to High(MSI_Spells) do
MSI_LoadSpell(i);
end;
function MSI_GetPossibleStaffs(Spells: TIntegerArray): TIntegerArray;
var
i: Integer;
begin
for i := 0 to High(Spells) do
Result := CombineIntArray(Result, MSI_Spells[Spells[i]].Staffs);
ClearSameIntegers(Result);
MSI_SubDebug('Possible Staffs: ' + ToStr(Result));
end;
{******************************************************************************}
{ function MSI_FindStaffs(): TIntegerArray; }
{ By: Coh3n }
{ Date: 14 November 2010 }
{ Description: Returns any staffs the player may have that would be useful for }
{ their spells }
{******************************************************************************}
function MSI_FindStaffs(Spells: TIntegerArray): TIntegerArray;
var
i, j, t, x, y: Integer;
tabs, possStaffs: TIntegerArray;
begin
MSI_AddHeader('MSI_FindStaffs');
if (GetCurrentTab = TAB_INV) then
tabs := [TAB_INV, TAB_EQUIP]
else
tabs := [TAB_EQUIP, TAB_INV];
possStaffs := MSI_GetPossibleStaffs(Spells);
for i := 0 to High(tabs) do
begin
for j := 0 to High(possStaffs) do
if (MSI_FindTool(possStaffs[j], tabs[i], x, y)) then
begin
if (tabs[i] = TAB_INV) then
MSI_Items[possStaffs[j]].InvSlot := CoordsToItem(x, y); // Set tool's inventory slot
SetLength(Result, (Length(Result) + 1));
Result[High(Result)] := possStaffs[j];
end else
Inc(t);
// If all staffs are found, it will break, otherwise check the other gametab
if (t <= 0) then
Break;
end;
MSI_CloseHeader('MSI_FindStaffs: '+ToStr(Result));
end;
function MSI_LeastRunesStaffs(): TIntegerArray;
var
a, i, j, spellMode, spellIndex: Integer;
theStaffs, runes, staffsFound: TIntegerArray;
begin
staffsFound := MSI_FindStaffs();
runes := [ RUNE_AIR, RUNE_EARTH, RUNE_FIRE, RUNE_WATER ];
theStaffs := [ STAFF_AIR, STAFF_EARTH, STAFF_FIRE, STAFF_WATER ];
if (Length(staffsFound) <= 0) then
begin
MSI_SubDebug('MSI_LeastRunesStaffs: No staffs were found');
Exit;
end;
with MSI_Players[CurrentPlayer].Scripts[CurrentScript] do
begin
SetLength(Result, Length(Spells));
for i := 0 to High(Spells) do
begin
with MSI_Spells[Spells[i]] do
begin
QuickSort(RequiredRunes);
InvertTIA(RequiredRunes);
for j := 0 to High(RequiredRunes) do
begin
if (RequiredRunes[j] <= 0) then
Break;
if (InIntArrayEx(runes, a, RequiredRunes[j])) then
if (InIntArray(staffsFound, theStaffs[a])) then // If the staff was found on the player
begin
Result[i] := theStaffs[a];
MSI_SubDebug('Using '+MSI_Items[Result[i]].Name+' for '+Capitalize(MSI_SpellToStr(Spells[i])));
Staffs[i] := Result[i]; // Set script attribute to the staff chosen
Break;
end else
Continue
else
Continue;
end;
end;
end;
end;
end;
{******************************************************************************}
{ function MSI_ChooseStaffs(): TIntegerArray; }
{ By: Coh3n }
{ Date: 14 November 2010 }
{ Description: Returns the staffs chosen for each spell for the current player }
{ Also sets the scripts 'Staffs' attributes }
{******************************************************************************}
function MSI_ChooseStaffs(): TIntegerArray;
begin
with MSI_Players[CurrentPlayer].Scripts[CurrentScript] do
begin
// Exit if the user has specified the staffs to use
if (Staffs[Low(Staffs)] > 0) then
begin
MSI_SubDebug('Staff types already set in DeclarePlayers');
Result := Staffs;
Exit;
end;
SetLength(Result, Length(Spells));
case StaffType of
MostCasts:
begin
end;
Cheapest:
begin
end;
else // By default, uses the staff that uses the most runes
Result := MSI_LeastRunesStaffs();
end;
end;
end;
{******************************************************************************}
{ function MSI_GetStaffRunes(): TIntegerArray; }
{ By: Coh3n }
{ Date: 14 November 2010 }
{ Description: Returns the runes associated with the staffs found }
{******************************************************************************}
function MSI_GetStaffRunes(): TIntegerArray;
var
i, t: Integer;
runeStaffs, possStaffs, chosenStaffs: TIntegerArray;
begin
runeStaffs := [ RUNE_AIR, RUNE_EARTH, RUNE_FIRE, RUNE_WATER ];
possStaffs := [ STAFF_AIR, STAFF_EARTH, STAFF_FIRE, STAFF_WATER ];
chosenStaffs := MSI_ChooseStaffs();
for i := 0 to High(chosenStaffs) do
if (InIntArrayEx(possStaffs, t, chosenStaffs[i])) then
begin
SetLength(Result, Length(Result) + 1);
Result[High(Result)] := runeStaffs[t];
MSI_SubDebug(MSI_Items[runeStaffs[t]+MSI_RUNE_AIR].Name+' not needed, we have a staff');
end;
end;
{******************************************************************************}
{ function MSI_GetRunesFound: TIntegerArray; }
{ By: Coh3n }
{ Date: 09 November 2010 }
{ Description: Returns the amount of each rune found in the inventory }
{******************************************************************************}
function MSI_GetRunesFound: TIntegerArray;
var
i: Integer;
exceptions: TIntegerArray;
begin
SetLength(Result, (RUNE_COUNT - 1));
exceptions := MSI_GetStaffRunes();
for i := 0 to High(Result) do
begin
if (InIntArray(exceptions, i)) then
begin
Result[i] := MAX_RUNES;
Continue;
end;
Result[i] := ItemAmount('inv', 'dtm', MSI_Items[i + MSI_RUNE_AIR].DTM, []);
if (Result[i] <= 0) then // Don't debug the ones that aren't found
Continue;
MSI_SubDebug(Capitalize(MSI_Items[i + MSI_RUNE_AIR].Name)+' found: '+IntToStr(Result[i]));
end;
end;
{******************************************************************************}
{ function MSI_CalculateCasts: TIntegerArray; }
{ By: nickrules & Coh3n }
{ Date: 09 November 2010 }
{ Description: Returns the number of casts per spell the player can cast }
{******************************************************************************}
function MSI_CalculateCasts: TIntegerArray;
var
i, j, lo, spellMode, spellIndex: Integer;
runeAmounts, casts: TIntegerArray;
begin
MSI_AddHeader('MSI_CalculateCasts');
with MSI_Players[CurrentPlayer].Scripts[CurrentScript] do
begin
SetLength(Result, Length(Spells));
runeAmounts := MSI_GetRunesFound(); // All the runes found in the inventory
for i := 0 to High(Spells) do
begin
with MSI_Spells[Spells[i]] do
begin
SetLength(casts, Length(RequiredRunes));
// Debug number of casts per individual rune type
for j := 0 to High(RequiredRunes) do
begin
if (runeAmounts[RequiredRunes[j]] >= MAX_RUNES) then
begin
casts[j] := runeAmounts[RequiredRunes[j]];
Continue;
end;
casts[j] := Floor(runeAmounts[RequiredRunes[j]] div RequiredRuneAmount[j]); // Rounds down
MSI_Debug('Enough '+MSI_Items[RequiredRunes[j]+MSI_RUNE_AIR].Name+
's to cast '+ IntToStr(casts[j])+' '+Capitalize(Name)+'s');
end;
// Set the result to the maximum number of casts depending on the runes found
for j := 0 to High(casts) do
begin
// Get the lowest number of calculated casts
if (j = 0) then
lo := casts[j]
else
if (casts[j] < lo) then
lo := casts[j];
if (j = High(casts)) then
begin
Result[i] := lo;
MSI_Debug('Player can cast '+IntToStr(Result[i])+' '+Capitalize(Name)+'s');
end;
end;
// Decrease the amount of runes found because they've been 'used' for another spell
for j := 0 to High(RequiredRunes) do
begin
if (runeAmounts[RequiredRunes[j]] >= MAX_RUNES) then
Continue;
DecEx(runeAmounts[RequiredRunes[j]], (Result[i] * RequiredRuneAmount[j]));
MSI_Debug(MSI_Items[RequiredRunes[j]+MSI_RUNE_AIR].Name+'s left: '+IntToStr(runeAmounts[RequiredRunes[j]]));
end;
end;
end;
end;
MSI_CloseHeader('MSI_CalculateCasts: ' + ToStr(Result));
end;
{******************************************************************************}
{ function MSI_ItemToSpellSlot(Spell: Integer): boolean; }
{ By: Nickrules }
{ Date: 5 August 2010 }
{ Description: Moves the item to the most ideal slot }
{******************************************************************************}
function MSI_ItemToSpellSlot(Spell, ItemIndex: Integer): boolean;
begin
with MSI_Players[CurrentPlayer].Scripts[CurrentScript] do
case Spell of
SPELL_ALCH_LOW:
result := MSI_DragItem(AlchSlots[ItemIndex], SLOT_ALCH_LOW);
SPELL_ALCH_HIGH:
result := MSI_DragItem(AlchSlots[ItemIndex], SLOT_ALCH_HIGH);
else begin
MSI_Debug('MSI_ItemToSpellSlot: Item manipulation not necessary...');
Result := True;
end;
end;
end;
{******************************************************************************}
{ function MSI_SetupMagic(): Boolean; }
{ By: Coh3n & nickrules }
{ Date: 08 November 2010 }
{ Description: Sets the player up for autoing the magic skill }
{******************************************************************************}
function MSI_SetupMagic(): Boolean;
var
i: Integer;
begin
MSI_AddHeader('MSI_SetupMagic');
with MSI_Players[CurrentPlayer].Scripts[CurrentScript] do
begin
CurrentSpell := 0;
SetupSpells; // Set all the spell information (SRL's magic.scar)
SetLength(Staffs, Length(Spells));
MaxCasts := MSI_CalculateCasts();
// If can't cast spell, delete it from the necessary arrays
for i := 0 to High(MaxCasts) do
if (MaxCasts[i] <= 0) then
begin
DeleteValueInIntArray(MaxCasts, i);
DeleteValueInIntArray(Spells, i);
end;
// Equip the staff for the CurrentSpell
if (MSI_Items[Staffs[CurrentSpell]].InvSlot > 0) then
begin
MSI_Debug('Equipping '+Capitalize(MSI_Items[Staffs[CurrentSpell]].Name));
InvMouse(MSI_Items[Staffs[CurrentSpell]].InvSlot, 1); // 1 = LeftClick
Wait(RandomRange(200, 500));
end;
Result := ((Length(MaxCasts) > 0) and (Length(Spells) > 0));
end;
MSI_CloseHeader('MSI_SetupMagic: ' + BoolToStr(Result));
end;
{******************************************************************************}
{ function MSI_Cast(spell, which: integer; isObject: boolean): boolean; }
{ By: Nickrules & Coh3n & NCDS }
{ Date: 22 August 2010 }
{ Description: Casts 'spell' on item/object 'which' }
{******************************************************************************}
function MSI_Cast(spell, which: integer; isObject: boolean): boolean;
var
x, y, slot, i: Integer;
s, spellName: string;
spellInfo: TSpell;
didCast: boolean;
iBox: TBox;
begin
spellName := MSI_SpellToStr(spell);
spellInfo := GetSpellInfo(spellName);
if (not Cast(spellName, spellInfo.autoCast)) then
begin
MSI_SubDebug('Failed to cast spell: ' + Capitalize(spellName));
exit;
end;
if (isObject) then
begin
didCast := MSI_FindObject(which, ClickLeft);
s := MSI_Objects[which].Name;
end else
begin
if ((spell = SPELL_ALCH_LOW) or (spell = SPELL_ALCH_HIGH)) then
begin
GetMousePos(x, y);
slot := CoordsToItem(x, y);
if slot = 0 then
case spell of
SPELL_ALCH_LOW : slot := SLOT_ALCH_LOW;
SPELL_ALCH_HIGH : slot := SLOT_ALCH_HIGH;
end;
if (ExistsItem(slot)) then
begin
if (not (WaitUpText('3', 120))) then
begin
iBox := InvBox(slot);
for i := 0 to 2 do
begin
MMouse(RandomRange(iBox.x1 + 5, iBox.x2 - 5), RandomRange(iBox.y1 + 5, iBox.y2 - 5), 2, 2);
if WaitUpText('3', 120) then
Break;
if i = 2 then
Exit;
end;
end;
GetMousePos(x, y);
MSI_SubDebug('Successfully cast ' + Capitalize(spellName));
Mouse(x, y, 0, 0, True);
Result := True;
Exit;
end;
MSI_SubDebug('Failed to cast ' + Capitalize(spellName));
Exit;
end;
didCast := MSI_ClickItem(which, ['ast']);
s := MSI_Items[which].Name;
end;
if (didCast) then
begin
Result := True;
MSI_SubDebug('Cast ' + Capitalize(spellName) + ' on ' + s);
end else
MSI_SubDebug('Failed to cast ' + Capitalize(spellName) + ' on ' + s);
end;
{******************************************************************************}
{ function MSI_IsAlching(WaitTime: Integer): Boolean; }
{ By: Coh3n }
{ Date: 24 November 2010 }
{ Description: Returns true if the player is alching; should eventually be }
{ written to work with several different animations }
{******************************************************************************}
function MSI_IsAlching(WaitTime: Integer): Boolean;
var
t: Integer;
b: TBox;
begin
t := (GetSystemTime + WaitTime);
b := IntToBox(MSCX - 30, MSCY - 40, MSCX + 30, MSCY + 15);
SMART_DrawBoxEx(True, b, clRed);
while (GetSystemTime < t) do
begin
if (PixelShift(b, 200) > ANIM_COL_ALCH) then
begin
MSI_SubDebug('We are alching!');
Result := True;
if GetCurrentTab = tab_Magic then
Exit;
end;
Wait(RandomRange(10, 30));
end;
Result := False;
end;
{******************************************************************************}
{ function MSI_Alch(Spell: Integer): Boolean; }
{ By: Coh3n }
{ Date: 24 November 2010 }
{ Description: Alchs until done all it can do, times out or ran out of alch }
{ items; should eventually be written to work with all spells }
{******************************************************************************}
function MSI_Alch(Spell: Integer): Boolean;
var
a, i, t, idealSlot: Integer;
spellName: string;
spellInfo: TSpell;
begin
MSI_AddHeader('MSI_Alch');
spellName := MSI_SpellToStr(Spell);
spellInfo := GetSpellInfo(spellName);
t := GetSystemTime + (60000 * 3); // Will stop if doesn't cast in 5 minutes
case Spell of
SPELL_ALCH_LOW: idealSlot := SLOT_ALCH_LOW;
SPELL_ALCH_HIGH: idealSlot := SLOT_ALCH_HIGH;
end;
with MSI_Players[CurrentPlayer].Scripts[CurrentScript] do
begin
if (Length(AlchSlots) <= 0) then
begin
MSI_Debug('No AlchSlots specified in DeclarePlayers, please specify which inventory slots your items are in.');
MSI_CloseHeader('MSI_Alch: ' + BoolToStr(Result));
Exit;
end;
for i := 0 to High(AlchSlots) do
begin
if (ExistsItem(AlchSlots[i])) and (AlchSlots[i] <> idealSlot) then
begin
DragItem(AlchSlots[i], idealSlot); // Drag alch item to ideal inventory slot
Wait(RandomRange(100, 300));
end else
if (not ExistsItem(idealSlot)) then
begin
MSI_Debug('Item doesn''t exist in AlchSlot['+IntToStr(i)+'] or idealSlot');
Continue;
end;
repeat
if (not MSI_Cast(Spell, 0, False)) then
begin
if (not (LoggedIn)) then
Exit;
MSI_FindRandoms;
if ((not ExistsItem(idealSlot))) then
if (i = High(AlchSlots)) then
begin
MSI_Debug('Ran out of items to alch!');
MSI_Players[CurrentPlayer].ReportInfo.FalseReason := 'Ran out of items to alch';
Exit;
end else
Break;
end else begin
// Adds report information (exp and casts)
MSI_AddToProggy(PROG_ADD_CASTS, -1, -1, -1);
MSI_AddToProggy(PROG_ADD_EXP, -1, -1, Round(spellInfo.Exp));
Inc(MSI_Players[CurrentPlayer].ReportInfo.Casts[CurrentScript][CurrentSpell]);
case MSI_Players[CurrentPlayer].Scripts[CurrentScript].Spells[CurrentSpell] of
SPELL_ALCH_LOW:
stats_IncVariable('Low Level Alchs', 1);
SPELL_ALCH_HIGH:
stats_IncVariable('High Level Alchs', 1);
end;
t := GetSystemTime + (60000 * 5);
end;
if IsChatBoxTextBetween(ALCH_COIN_MESSAGE, 8, 8, 6) then
begin
MSI_Debug('Alching Coins!');
Exit;
end;
a := ((GetSystemTime + 2000) + Random(1500));
while (MSI_IsAlching(2000) and (GetSystemTime < a)) do // Wait while alching
Wait(RandomRange(10, 20));
// Result is true if we've cast the maximum
if (MSI_Players[CurrentPlayer].ReportInfo.Casts[CurrentSpell][CurrentScript] >= MaxCasts[CurrentSpell]) then
begin
MSI_Debug('Cast maximum number of '+Capitalize(MSI_SpellToStr(Spell)));
MSI_CloseHeader('MSI_Alch: ' + BoolToStr(Result));
MSI_Players[CurrentPlayer].ReportInfo.FalseReason := 'Cast maximum number of '+Capitalize(MSI_SpellToStr(Spell));
Exit;
end;
if ((MSI_Players[CurrentPlayer].ReportInfo.Casts[CurrentScript][CurrentSpell] mod CASTS_BEFORE_REPORT) = 0) then
begin
Result := True;
MSI_CloseHeader('MSI_Alch: ' + BoolToStr(Result));
Exit;
end;
until(GetSystemTime > t);
end;
end;
MSI_CloseHeader('MSI_Alch: ' + BoolToStr(Result));
end;
{******************************************************************************}
{ function MSI_DoMagic(): Boolean; }
{ By: Coh3n }
{ Date: 24 November 2010 }
{ Description: Casts the maximum spells for each spell in DeclarePlayers }
{******************************************************************************}
function MSI_DoMagic(): Boolean;
begin
with MSI_Players[CurrentPlayer].Scripts[CurrentScript] do
case Spells[CurrentSpell] of
SPELL_ALCH_HIGH, SPELL_ALCH_LOW:
Result := MSI_Alch(Spells[CurrentSpell]);
else
MSI_SubDebug('Invalid spell in DoMagic');
end;
end;
I was thinking about something like this for casting:
// Returns true when the spell is clicked
// Maybe check for the white outline after it's clicked?
function MSI_ClickSpell(Spell: Integer): Boolean;
begin
end;
// Returns true when the spell was cast on an item
function MSI_CastItem(Spell: Integer): Boolean;
begin
end;
// Returns true when the spell was cast on an object (not really needed now since we just have alching)
function MSI_CastObject(Spell: Integer): Boolean;
begin
end;
The idea is to keep it as simple as possible. I also think MSI_LeastRunesStaff can be strunk down to one or two lines, I just haven't looked into it a lot yet, but I'll figure it out.
I want to change IsAlching to IsAnimating and work with any animations. I think it would be a good time to think about
this also.
Anything I haven't considered please let me know, and if you can help out (even if it's a little bit) that would be great. :)
Thanks
E: I also don't like how I have the MAX_RUNE constant. I need to figure out a way to get rid of it. It's used when setting the rune amounts for a staff.