Jump to content

Mining AHK goodies


Recommended Posts

Yes, I know there's an autohotkey (AHK) thread already. Maybe I'm just an attention whore. I want to share what I've got because I find it super useful (at least to me). As some may know, I'm working on a mapping tool that will (eventually) let you see where all the mobs and roids in the game are. Right now I'm working on roids. To make this less masochistic, I fly with 5 TTs. 4 of the TTs get a 2-level band of ore because *usually* that fits into a single TT hold (Lagarto is an exception, there are probably others). So one char gets lvl 1-2 ores, 3-4, 5-6, 7-8, and the last one gets hulk contents, pop rock mob contents, and lvl 9 ores. Except for Lagarto, this works out where I can mine out an entire sector without needing to dock and dump.

 

It was getting tedious having to sort the ore to my alts. So, I sat down and made autohotkey do it for me. Now, this requires GDIP, which is ULTRA finicky. Like, upgrading AHK minor versions can completely break it. But, when it works, it's glorious. For the maximum chance at success: I am using the Unicode 64-bit version of AHK 1.1.37.01. I have attached a .zip containing both the images (needles) and libraries (Gdip_all/Imagesearch) needed. (code at bottom of this post for length reasons)

 

I plan to update this thread with additional goodies as I create them.

 

Assumptions:

  • Your windows are titled by your accounts. So, for example, the window where DoctorJE lives is always titled 'enb-61'. Because it's on my 6th game account. Whereas my PW/my ore TT for lvl 1-2 ore live on enb-51.
    • Check outif you want to see the code for how I do this.
  • You are playing at 1280x720
    • It will work with other resolutions, you just need to update the pixel coordinates at the top of the file.
    • Note: for things like INVENTORY_SCREEN_PIXELS, [61,320] are the coordinates of the first inventory slot (top-left) and 77 is the X offset (so slot 5 is 77px right of slot 1) and 55 is the Y offset (slot 2 is 55px below slot 1). I use this convention in many places.

 

"Why not use ImageSearch instead of Gdip?!"

ImageSearch is MUCH slower. I can get the ilvl of every ore in the hold in less than a second with Gdip. I need around 15 seconds to do the same with ImageSearch. I'm sure to a normal person that isn't a problem, but to me, it's nails on a chalkboard.

 

GDIP is also needed if I ever end up OCRing stuff. I tried to use Tesseract OCR to get the itemlvl for me, but it turned out to be way too much of a PITA, and not reliable. The reason why is that GDIP makes it easy to take a screenshot of the whole screen, then crop it. Normal AHK can't really do that.

 

"What's the deal with isOre()?"

So, the game client doesn't consistently render the icons for things in your hold. Take hydrocarbon results (the blue and white cylinder). If you have a lvl 5 and a lvl 8 in your hold, and you look at them in photoshop/gimp, you'll see that they are not identical and mis-aligned by 1/2 a pixel. That isn't something I can correct for in Gdip/AHK. To really classify those properly, I'd need to use a tool like OpenCV, or train a model using fast.ai as an image classifier. That's a level of headache I'm just not interested in right now. But I'll probably do it. Maybe. The only purpose of the isOre() function was to find hulk /mob contents and move those to one character (since, without that, Debris goes to my lvl 1-2 char, instead of to the one I want to hold hulk-stuff). Hulks are rare enough I just handle this special case by hand atm.

 

On a higher level, I suspect that once I start using openCV, I'll use it for a lot of things (if it has any sort of performance at all) because being able to visually classify things sounds HUGELY more time saving than what I have to do right now. Or possibly training some sort of neural network. I've never seen either of those perform particularly fast, but...meh. I think you can see why I didn't keep going down this rabbit hole.

 

The code:

;Scaled for 1280 window
;startx, starty, xoffset, yoffset
INVENTORY_SCREEN_PIXELS:=[61,320,77,55]
TRADE_SCREEN_PIXELS:=[493,291,95,75]
TRADE_BUTTON:=[1135,500]
CONFIRM_BUTTON:=[699,433]
VAULT_SCREEN_PIXELS:=[440,268,75,54]

FORMATION_BUTTON:=[1178, 269]
FORMATION_MENU_OPTION:=[1181, 228]
FORMATION_PIXEL_CHECK:=[1175,266]
ASSIST_LEADER_BUTTON:=[1225, 444]

ROID_LEVEL_LOCATION:=[1165,650,1180,690]

#Include gdip_imageSearch\Gdip_All.ahk
#Include gdip_imageSearch\Gdip_ImageSearch.ahk

If !pToken := Gdip_Startup()
{
   MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
   ExitApp
}

OnExit, EXIT_LABEL

;CTRL + SHIFT + y - For Ore mining: Move ores to alts.
^+y::
KeyWait CTRL
KeyWait Shift

ORE_SLOTS := 30
;Which inv slot is in the top-right?
first_slot_visible := 1
12_window := "enb-51"
12_moves :=[]
12_idx_and_len :=[1,0]
34_window := "enb-41"
34_moves := []
34_idx_and_len :=[1,0]
56_window := "enb-91"
56_moves := []
56_idx_and_len :=[1,0]
78_window := "enb-81"
78_moves := []
78_idx_and_len :=[1,0]
9_window := "enb-21"
9_moves := []
9_idx_and_len :=[1,0]
item_level_needles := []

IfWinActive enb-61
{
	;Step 1, build a cache of the needles we need.
	Loop, 9 {
		needle_name:="C:\YOUR_PATH_TO\item_level_" A_Index ".png"
		file_needle:=Gdip_CreateBitmapFromFile(needle_name)
		item_level_needles[A_Index] := file_needle
	}
	
	;Step 2, get a screenshot of the window.
	screenshot_haystack:=getGameScreen_gdip()

	;Step 3, classify inventory slots
	Loop, %ORE_SLOTS% {
		slot:=A_Index
		
		;Advance to next set of pages.
		if( slot > 11 && Mod(slot-1, 12) == 0) {
			;Scaled for 1280 window
			Mousemove, 116,256
			send {WheelDown 3}
			Sleep 50
			;Update Screenshot on change
			Gdip_DisposeImage(screenshot_haystack)
			screenshot_haystack:=getGameScreen_gdip()
		}
		
		;Non-ore items (Hulk/mob drops) go to the char that gets lvl 9 ores.
		;This method doesn't work, see function for details.
/* 		
		isOre:=getIsOre(slot, ORE_SLOTS)
		if(isOre == 0) {
			9_moves.Push(slot)
			continue
		} 
*/
		
		ilvl:=getInvItemLevel(screenshot_haystack, slot, ORE_SLOTS, item_level_needles)
		if(ilvl == -1) {
			;This happens on an empty slot
			continue
		} else if(ilvl == 1 || ilvl == 2) {
			12_moves.Push(slot)
			12_idx_and_len[2] := 12_idx_and_len[2] + 1
		} else if(ilvl == 3 || ilvl == 4) {
			34_moves.Push(slot)
			34_idx_and_len[2] := 34_idx_and_len[2] + 1
		} else if(ilvl == 5 || ilvl == 6) {
			56_moves.Push(slot)
			56_idx_and_len[2] := 56_idx_and_len[2] + 1
		} else if(ilvl == 7 || ilvl == 8) {
			78_moves.Push(slot)
			78_idx_and_len[2] := 78_idx_and_len[2] + 1
		} else if(ilvl == 9) {
			9_moves.Push(slot)
			9_idx_and_len[2] := 9_idx_and_len[2] + 1
		} else {
			Msgbox, Unknown item in %slot%, got %ilvl%
		}
	}
	
	;Rewind window to starting position.
	Mousemove, 116,256
	send {WheelUp 10}
	Sleep, 50
	
	;Step 4, Start moving trade items
	initial_target:=true
	while(12_idx_and_len[1] <= 12_idx_and_len[2]) {
		12_idx_and_len[1] := makeTrades(12_window, 12_idx_and_len, 12_moves, first_slot_visible, ORE_SLOTS, initial_target)
		initial_target:=false
	}
	initial_target:=true
	while(34_idx_and_len[1] <= 34_idx_and_len[2]) {
		34_idx_and_len[1] := makeTrades(34_window, 34_idx_and_len, 34_moves, first_slot_visible, ORE_SLOTS, initial_target)
		initial_target:=false
	}
	initial_target:=true
	while(56_idx_and_len[1] <= 56_idx_and_len[2]) {
		56_idx_and_len[1] := makeTrades(56_window, 56_idx_and_len, 56_moves, first_slot_visible, ORE_SLOTS, initial_target)
		initial_target:=false
	}
	initial_target:=true
	while(78_idx_and_len[1] <= 78_idx_and_len[2]) {
		78_idx_and_len[1] := makeTrades(78_window, 78_idx_and_len, 78_moves, first_slot_visible, ORE_SLOTS, initial_target)
		initial_target:=false
	}
	initial_target:=true
	while(9_idx_and_len[1] <= 9_idx_and_len[2]) {
		9_idx_and_len[1] := makeTrades(9_window, 9_idx_and_len, 9_moves, first_slot_visible, ORE_SLOTS, initial_target)
		initial_target:=false
	}
	
	for index in item_level_needles
		Gdip_DisposeImage(item_level_needles[index])
	Gdip_DisposeImage(screenshot_haystack)

	SoundBeep
}
return

cropImage(pBitmapOld, tl_x, tl_y, br_x, br_y, byref cropped, save_to="", should_save=false)
{
	NewWidth := br_x - tl_x
	NewHeight := br_y - tl_y
	xOffset:= tl_x
	yOffset := tl_y
	;BMP
	Format := 0x26200A
	;PNG
	;Format := 0x3147504E
	
	cropped := Gdip_CreateBitmap(NewWidth, NewHeight, Format)
	G := Gdip_GraphicsFromImage(cropped)
	Gdip_SetSmoothingMode(G, 4)
	Gdip_SetInterpolationMode(G, 7)
	Gdip_DrawImage(G, pBitmapOld, 0,0, Gdip_GetImageWidth(pBitmapOld), Gdip_GetImageHeight(pBitmapOld),xOffset,yOffset,Gdip_GetImageWidth(pBitmapOld), Gdip_GetImageHeight(pBitmapOld))
	if(should_save) {
		Gdip_SaveBitmapToFile(cropped,save_to)
	}
}

findImage(screenshot_haystack,file_needle,byref outx, byref outy, x1=0,y1=0,x2=0,y2=0,debug=false)
{
	variation:=0
	;1 = top->left->right->bottom
	search_dir:=1
	trans_color:=0xFFFFFF
	
	outx:=-1
	outy:=-1

	answer:=Gdip_ImageSearch(screenshot_haystack,file_needle, LIST,x1,y1,x2,y2,variation,trans_color,search_dir,1)
	Loop, Parse, LIST, `n
	{
		StringSplit, Coord, A_LoopField, `,
		Gdip_GetImageDimensions(file_needle,img_x,img_y)
		outx:=Coord1 + img_x/2
		outy:=Coord2 + img_y/2

		if(debug == true)
		{
			Msgbox, Hit %x% %y% o %x_offset% i %img_x%
		}
		;msgbox %Coord1%, %Coord2%, 0
	}
	if(debug == true)
	{
		MsgBox, % "Returned: " answer "`n`n" LIST "`n" outx
		;msgbox, %x1%,%y1%,%x2%,%y2%
		;Gdip_SaveBitmapToFile(screenshot_haystack, "area.png")  ;saves image to file
	}
}

getGameScreen_gdip() {
	WinGet,hwnd,ID,A
	;screenshot_haystack:=Gdip_BitmapFromHWND(hwnd)
	
	WinGetPos, wx, wy, ww, wh, ahk_id %hwnd%
	screen:=wx . "|" . wy . "|" . ww . "|" . wh
    screenshot_haystack := Gdip_BitmapFromScreen(screen)
	
	return screenshot_haystack
	;Don't forget to Gdip_DisposeImage(screenshot_haystack)
}

getInvItemLevel(screenshot_haystack, slot_num, inv_slots, needle_images) {
	results := invSlotIDToPixels(slot_num, inv_slots)
		
	;Search area for VIII (largest) is 20x8 px
	SEARCH_AREA_SIZE := [22,9]

	;The -10 and -21 offsets here are not related to SEARCH_AREA_SIZE, they are the offset between where INVENTORY_SCREEN_PIXELS points and where the item lvl is.
	src_x:=results[1] - 10
	src_y:=results[2] - 19
	
	end_x:=src_x+SEARCH_AREA_SIZE[1]
	end_y:=src_y+SEARCH_AREA_SIZE[2]
	
	cropped_img:=""
	cropImage(screenshot_haystack, src_x,src_y,end_x,end_y, cropped_img)

	for index,value in [9,8,7,6,4,5,3,2,1]
	{
		findImage(cropped_img,needle_images[value],matchX, matchY, 0,0,0,0)
		if(matchX != -1 && matchY != -1) {
			Gdip_DisposeImage(cropped_img)
			return value
		}
	}
	Gdip_DisposeImage(cropped_img)
	return -1
}

getScrollsNeeded(current_tl_slot, target_slot, max_slots) {
	if(current_column_index > max_column_index) {
		Msgbox, Programmer error: current_tl_slot is %current_tl_slot% which is larger than allowed.
	}
	
	if(target_slot <= current_tl_slot + 11 && target_slot >= current_tl_slot) {
		return 0
	}

	current_column_index := FLOOR((current_tl_slot - 1) / 4)
    target_column_index := FLOOR((target_slot - 1) / 4)
	max_column_index := FLOOR((max_slots-1) / 4)-2
	
	if(target_column_index > max_column_index) {
		return max_column_index - current_column_index
	}
	
	;If we are on the last (partial) page:
	cols_to_normalize:= 0
	if(current_tl_slot > max_column_index*4) {
		cols_to_normalize := -1 * MOD(max_column_index, 3)
	}
	
	current_page := FLOOR((current_tl_slot - 1) / 12)
    target_page := FLOOR((target_slot - 1) / 12)
	scrolls := 3 * (target_page - current_page) + cols_to_normalize
	
	return scrolls
}

invSlotIDToPixels(slot_num, max_slots) {
	global INVENTORY_SCREEN_PIXELS
	
	inv_row_id:=MOD(slot_num-1, 4)
	inv_col_id:=FLOOR((slot_num-1)/4)
	relative_col_id:=MOD(inv_col_id, 3)
	max_cols := CEIL(max_slots / 4)
	
	last_full_page_slot := FLOOR(max_slots/12)*12
	
	if(slot_num > last_full_page_slot) {
		cols_to_add := 3-MOD(max_cols, 3)
		relative_col_id := relative_col_id + cols_to_add
	}
	
	src_x:=INVENTORY_SCREEN_PIXELS[1] + INVENTORY_SCREEN_PIXELS[3]*relative_col_id
	src_y:=INVENTORY_SCREEN_PIXELS[2] + INVENTORY_SCREEN_PIXELS[4]*inv_row_id
	
	return [src_x, src_y]
}

makeTrades(window_title, idx_and_len, movelist, byref first_slot_visible, max_slots, initial_targeting) {
	global ASSIST_LEADER_BUTTON, TRADE_BUTTON, CONFIRM_BUTTON
	
	moves_to_make := idx_and_len[2]+1 - idx_and_len[1]
	if(moves_to_make < 1) {
		return [idx_and_len[1], first_slot_visible]
	}
	if(moves_to_make > 6) {
		moves_to_make:=6
	}
	
	if WinExist(window_title) {
		WinActivate, %window_title%
	} else {
		return [idx_and_len[1], first_slot_visible]
	}
	
	if(initial_targeting) {
		MouseClick, left, ASSIST_LEADER_BUTTON[1]-100, ASSIST_LEADER_BUTTON[2]
		Sleep, 1500
	}
	MouseClick, left, TRADE_BUTTON[1], TRADE_BUTTON[2]
	Sleep, 250
	
	WinActivate, enb-61
	Sleep, 50

	Loop, %moves_to_make% {
		inv_slot_id := movelist[idx_and_len[1]]
		inv_xy := invSlotIDToPixels(inv_slot_id, max_slots)
		trade_xy := tradeSlotIDToPixels(A_Index)
		
		scrolls_needed := getScrollsNeeded(first_slot_visible, inv_slot_id, max_slots)
		if( scrolls_needed != 0 ) {
			fixed_scrolls := ABS(scrolls_needed)
			Mousemove, 116,256
			if( scrolls_needed > 0 ) {
				send {WheelDown %fixed_scrolls%}
			} else {
				send {WheelUp %fixed_scrolls%}
			}
			Sleep 50
			;debugval := first_slot_visible + (4 * scrolls_needed)
			;Msgbox, Updating FSV from %first_slot_visible% to %debugval% with %scrolls_needed% called with %inv_slot_id% and %max_slots%
			first_slot_visible := first_slot_visible + (4 * scrolls_needed)
		}
		
		;for debugging
		;x1:=inv_xy[1]
		;y1:=inv_xy[2]
		;x2:=trade_xy[1]
		;y2:=trade_xy[2]
		;Msgbox, Moving inv slot %inv_slot_id% at %x1% %y1% to trade slot %A_Index% at %x2% %y2%
		
		idx_and_len[1] := idx_and_len[1] + 1
		;Move the mouse to the clickdrag start BEFORE you push shift!
		MouseMove, inv_xy[1], inv_xy[2]
		Send, {Shift down}
		MouseClickDrag, left, inv_xy[1], inv_xy[2], trade_xy[1], trade_xy[2]
		Send, {Shift up}
		Sleep, 50
	}
	MouseClick, left, CONFIRM_BUTTON[1], CONFIRM_BUTTON[2]
	
	;Important! Function must re-focus the character with ore to move.
	WinActivate, %window_title%
	Sleep, 50
	;Msgbox, Debug
	MouseClick, left, CONFIRM_BUTTON[1], CONFIRM_BUTTON[2]
	
	WinActivate, enb-61
	;Trade window hangs around after a trade finishes. Wait for it to go away.
	Sleep, 3000
	
	return idx_and_len[1]
}

tradeSlotIDToPixels(slot_num) {
	global TRADE_SCREEN_PIXELS
	
	trade_row_id:=MOD(slot_num-1, 2)
	trade_col_id:=FLOOR((slot_num-1)/2)
	
	dest_x:=TRADE_SCREEN_PIXELS[1] + TRADE_SCREEN_PIXELS[3]*trade_col_id
	dest_y:=TRADE_SCREEN_PIXELS[2] + TRADE_SCREEN_PIXELS[4]*trade_row_id
	
	return [dest_x, dest_y]
}

EXIT_LABEL: ; be really sure the script will shutdown GDIP
Gdip_Shutdown(pToken)
EXITAPP

 

ore_mover_ahk.7z

 

@Codemonkeyx Credit where it's due. You were right, logging via data files was a better choice. This script was another thing that needed to happen. I just mined out the big PITA field in Rag for the second time...I beat the respawn easily now. Partly because I have 5 TTs instead of 2, and partly because I can cloak, push 3 buttons, wait about 1 minute, and have a completely clean hold, ready to keep mining. My god...if I could have started with this...

Edited by Doctor
Credit to codemonkey
  • Like 1
Link to comment
Share on other sites

Wow, this is absolutely mad, I love it, lol.  I don't know if 1) I'd be able to get this working on Linux, and 2) I would really have any use for it... but impressive. You should have called it "strip miner" or something because that's about the only use case where this starts to make sense.

 

One has to wonder what you're going to do with all this ore after you're done with the map tool.  :D  You will never need to mine anything ever again!

Link to comment
Share on other sites

Posted (edited)

Unfortunately that's not true. Grail Water seems to be one of the rarest minerals in the game. I still haven't managed to mine out the 300 boronite for the chavez faction mission. No joke, I might actually hit 300 aesirium before I get 300 boronite. The main thing I'm looking forward to is finally having a list of "Ok, you want to find this ore? Here are the places to look." and have that be based on something approaching sane data. At least then I'll know I'm not crazy for thinking some minerals are super rare. To give you some sense, I have around 1/2 the sectors in the game mapped right now, and the size of the DB DOUBLED just from all the roid data. And my roid data storage is very efficient; only ints. So...yeesh.

 

I will be rich beyond the dreams of avarice though. All of my TTs have at least 25m to their names just from selling stacks of refined lvl 8 ores. And that's *just* the extra stacks. I keep 1 (or more) stacks in reserve not just of lvl 8, but everything. The exceptions are the 'literally useless' ores like Firerock, Icy Pearl, Vaneon, etc. Things that have no uses either raw or refined.

 

But yeah, my lvl 8 mule is full of ore, so it's time to sit down and write that script I had mentioned as making more sense instead of caching character IDs on the account vault page. By the time I sell off all the extra stacks I've gotten from just this most recent pass, I'd be shocked if I wasn't at 50m or better on all my TTs. at least I won't have to worry about money for a while. 😛 Fun note: the easiest way to move cash from one char to another is actually stacks of refined lvl 8 ore. Each stack is worth ~2m after negotiate is factored in.

 

As for its utility to normal people, depends. I got the idea to mine with TT alts from another JE (before my JE was 150) and he had those TTs partially to sig-tank, and partially to hold ore. Back then I assumed the TS was going to be the best miner. Thankfully, he enlightened me to the utility of using the Martyr's Heart and a JE. The TS is a powerhouse, but it's kneecapped by it's lvl 8 reactor. Unless you intend to mine with a JS giving you every recharge and shunt buff there is...it just can't keep up with what a JE can do solo. Anyway, I do believe this will be useful to others. They probably won't need all 5 characters, but it is not hard to limit the source to only use 1/2/3/etc alts. I think the main problem is most people don't read the forums. In game, people are shocked to know there's a new mapping tool.

 

Also, at a higher level, I think the code itself has merit. If one wants to learn autohotkey, what I'm doing is pretty clean, clear, and makes sense. GDIP is a PITA to use, but once you figure out what to do it's super powerful. autohotkey + GDIP helped me solve a $25 million problem IRL a few years ago. So...yeah. Part of sharing is also showing problem solving, solution options, and whatever else. I don't know why you feel the need to continually pretend my contributions have little to no value, but it is unappreciated and wrong.

Edited by Doctor
More logic
Link to comment
Share on other sites

5 hours ago, Doctor said:

I don't know why you feel the need to continually pretend my contributions have little to no value, but it is unappreciated and wrong.

 

Hey, whoa, if that's what you're taking from any of my comments then please, don't misunderstand me.

 

Just because *I, personally* might not:

  • have a use for everything you create
  • be willing to go through the trouble to get it working in my (very different) environment (which was mostly what I was saying here)

OR

  • even if it doesn't generally fit a common use case (i.e. multi-boxing in general, completely mining out entire sectors of the game, etc.)

that doesn't mean it has "little to no value" *in general* and I certainly wasn't trying to imply that. I'm talking about specific use cases and largely giving opinions about my own personal use.  I use your TamperMonkey script for the vault transfer page on a daily basis. I use a slightly modified version (so it works at any resolution as I have multiple systems with different configurations) of your vault stacking AHK script almost as often. This map tool you're working on is looking crazy awesome and I'm certain EVERYONE would find value in that.

 

When it comes to this thing with GDIP or your NV<=>FN trade route deal which uses images and ImageSearch, etc. they are just a lot more complex and don't map directly to anything I'm trying to do. I completely understand how these purpose-built tools are the perfect solutions to the problems you created them for, they just aren't problems I have at the moment (i.e. transferring mountains of ore between dozens of characters across multiple accounts or running trade runs on six characters at once).  :)  I did try your NV <=> FN trade route and it was a cool idea, but it just wasn't very reliable for me which is hardly a surprise between linux and being at a different resolution (which I tried to account for by resizing the images, etc. but ultimately it just wasn't worth the hassle to get working reliably and a simpler approach / getting trade XP a different way made more sense for me personally at the time).  I've since found that linux/wine seems to have other issues with AHK's PixelGetColor (and in turn PixelSearch) so that was also probably a contributing factor to the problems I was having.

 

Anyway, I've probably beat this to death, but I just want to make it clear that I do really appreciate your contributions, even the wild ones that I don't (currently) see how or what I would use them for!  Hopefully in the end you're making all these things for you and ultimately don't really care what I or anyone else thinks!  ❤️

 

Some of us are probably just jealous we aren't retired and can only find time for stuff half this ambitious by not sleeping💤 😛

Link to comment
Share on other sites

I'm still working on the ore move script (like everything, the devil is in the details...and there's a LOT of details). But here's an example of why I'm writing it...

 

For char Dolladolla slot1 - Moving 300 Abyssian Dust in slot 0 to Doctorts
For char Dolladolla slot2 - Moving 300 Celestial Ore in slot 7 to Doctorts
For char Dolladolla slot3 - Moving 300 Conorite in slot 10 to Doctorts
For char Dolladolla slot4 - Moving 300 Cupidite in slot 12 to Doctorts
For char Dolladolla slot5 - Moving 300 Duplium Ore in slot 16 to Doctorts
For char Dolladolla slot6 - Moving 300 Emperion in slot 19 to Doctorts
For char Dolladolla slot7 - Moving 300 Helvatha in slot 21 to Doctorts
For char Dolladolla slot8 - Moving 300 Idunium Ore in slot 25 to Doctorts
For char Dolladolla slot9 - Moving 300 Inderite in slot 27 to Doctorts
For char Dolladolla slot10 - Moving 300 Khnumium Ore in slot 32 to Doctorts
For char Dolladolla slot11 - Moving 300 Leonite in slot 34 to Doctorts
For char Dolladolla slot12 - Moving 300 Leonite in slot 36 to Doctorts
Actual sending disabled.
For char Dolladolla slot1 - Moving 300 Neutronium Ore in slot 41 to Doctorts
For char Dolladolla slot2 - Moving 300 Pyrrhotite Gneiss in slot 46 to Doctorts
For char Dolladolla slot3 - Moving 300 Raw Charybdis Voidstone in slot 50 to Doctorts
For char Dolladolla slot4 - Moving 300 Raw Diamond in slot 52 to Doctorts
For char Dolladolla slot5 - Moving 300 Raw Firerock in slot 56 to Doctorts
For char Dolladolla slot6 - Moving 300 Raw Firerock in slot 57 to Doctorts
For char Dolladolla slot7 - Moving 300 Raw Galactic Rimstone in slot 60 to Doctorts
For char Dolladolla slot8 - Moving 300 Raw Hadecite in slot 61 to Doctorts
For char Dolladolla slot9 - Moving 300 Raw Meteoric Diamond in slot 63 to Doctorts
For char Dolladolla slot10 - Moving 300 Raw Meteoric Diamond in slot 64 to Doctorts
For char Dolladolla slot11 - Moving 300 Raw Mica in slot 66 to Doctorts
For char Dolladolla slot12 - Moving 300 Raw Scyllan Diamond in slot 68 to Doctorts
Actual sending disabled.
For char Rejecca slot1 - Moving 300 Andromesite in slot 2 to Doctorts
For char Rejecca slot2 - Moving 300 Charon's Dust in slot 8 to Doctorts
For char Rejecca slot3 - Moving 300 Gold Ore in slot 15 to Doctorts
For char Rejecca slot4 - Moving 300 Hades Blood in slot 17 to Doctorts
For char Rejecca slot5 - Moving 300 Hades Blood in slot 18 to Doctorts
For char Rejecca slot6 - Moving 300 Hafnium Ore in slot 20 to Doctorts
For char Rejecca slot7 - Moving 300 Halon in slot 22 to Doctorts
For char Rejecca slot8 - Moving 300 Krypton in slot 26 to Doctorts
For char Rejecca slot9 - Moving 300 Radium Ore in slot 37 to Doctorts
For char Rejecca slot10 - Moving 300 Radon in slot 39 to Doctorts
For char Rejecca slot11 - Moving 300 Raw Anthenicite in slot 41 to Doctorts
For char Rejecca slot12 - Moving 300 Raw Charon Crystal in slot 45 to Doctorts
Actual sending disabled.
For char Shogunfive slot1 - Moving 300 Anubium Ore in slot 1 to Doctorts
For char Shogunfive slot2 - Moving 300 Argon in slot 3 to Doctorts
For char Shogunfive slot3 - Moving 300 Argon in slot 4 to Doctorts
For char Shogunfive slot4 - Moving 300 Cobalite in slot 14 to Doctorts
For char Shogunfive slot5 - Moving 300 Fluorine in slot 16 to Doctorts
For char Shogunfive slot6 - Moving 300 Fluorine in slot 17 to Doctorts
For char Shogunfive slot7 - Moving 300 Fluorine in slot 18 to Doctorts
For char Shogunfive slot8 - Moving 300 Neon in slot 30 to Doctorts
For char Shogunfive slot9 - Moving 300 Neon in slot 31 to Doctorts
For char Shogunfive slot10 - Moving 300 Raw Alexandrite in slot 36 to Doctorts
For char Shogunfive slot11 - Moving 300 Raw Lapis Lazuli in slot 46 to Doctorts
For char Shogunfive slot12 - Moving 300 Raw Malachite in slot 48 to Doctorts
Actual sending disabled.
For char Dolladolla slot1 - Moving 300 Raw Tincal in slot 69 to Doctorts
For char Dolladolla slot2 - Moving 300 Raw Voidgem in slot 71 to Doctorts
For char Dolladolla slot3 - Moving 300 Stojsavline in slot 74 to Doctorts
For char Dolladolla slot4 - Moving 300 Stygian Blacksand in slot 76 to Doctorts
For char Dolladolla slot5 - Moving 300 Stygian Blacksand in slot 77 to Doctorts
For char Dolladolla slot6 - Moving 300 Vaneon in slot 83 to Doctorts
For char Dolladolla slot7 - Moving 300 Vaneon in slot 84 to Doctorts
For char Dolladolla slot8 - Moving 300 Wexeon in slot 88 to Doctorts
For char Dolladolla slot9 - Moving 300 Yunieon Gas in slot 91 to Doctorts
Actual sending disabled.
For char Dolladolla slot1 - Moving 300 Ambrosia Crude in slot 95 to Eightnineore
For char Dolladolla slot2 - Moving 300 Boragon in slot 93 to Eightnineore
For char Dolladolla slot3 - Moving 300 Emperion in slot 18 to Eightnineore
For char Dolladolla slot4 - Moving 300 Minosium Ore in slot 38 to Eightnineore
For char Dolladolla slot5 - Moving 300 Persephonite in slot 44 to Eightnineore
For char Dolladolla slot6 - Moving 300 Vanirum Ore in slot 86 to Eightnineore
Actual sending disabled.
For char Dolladolla slot1 - Moving 300 Apollonite in slot 3 to Sixsevenore
For char Dolladolla slot2 - Moving 300 Demeter's Tears in slot 14 to Sixsevenore
For char Dolladolla slot3 - Moving 300 Horusium Ore in slot 24 to Sixsevenore
For char Dolladolla slot4 - Moving 300 Iridium Ore in slot 29 to Sixsevenore
For char Dolladolla slot5 - Moving 300 Iridium Ore in slot 30 to Sixsevenore
For char Dolladolla slot6 - Moving 300 Raw Eye Stone in slot 54 to Sixsevenore
For char Dolladolla slot7 - Moving 300 Tantalum Ore in slot 79 to Sixsevenore
Actual sending disabled.
For char Greedincarnate slot1 - Moving 300 Diridium Crystal in slot 9 to Doctorts
For char Greedincarnate slot2 - Moving 300 Diridium Crystal in slot 10 to Doctorts
For char Greedincarnate slot3 - Moving 300 Raw Amethyst in slot 31 to Doctorts
Actual sending disabled.
For char Greedincarnate slot1 - Moving 300 Raw Topaz in slot 41 to Lvloneore
For char Greedincarnate slot2 - Moving 300 Raw Topaz in slot 42 to Lvloneore
Actual sending disabled.
For char Rejecca slot1 - Moving 300 Adamantine Ore in slot 0 to Sixsevenore
For char Rejecca slot2 - Moving 300 Discordite in slot 13 to Sixsevenore
For char Rejecca slot3 - Moving 300 Osirium Ore in slot 31 to Sixsevenore
For char Rejecca slot4 - Moving 300 Raw Flawless Ruby in slot 49 to Sixsevenore
For char Rejecca slot5 - Moving 300 Raw Flawless Ruby in slot 50 to Sixsevenore
For char Rejecca slot6 - Moving 300 Stygian Blackwater in slot 66 to Sixsevenore
For char Rejecca slot7 - Moving 300 Stygian Blackwater in slot 67 to Sixsevenore
For char Rejecca slot8 - Moving 300 Stygian Blackwater in slot 68 to Sixsevenore
For char Rejecca slot9 - Moving 300 Stygian Blackwater in slot 69 to Sixsevenore
Actual sending disabled.
For char Rejecca slot1 - Moving 300 Raw Heartstone in slot 52 to Doctorts
For char Rejecca slot2 - Moving 300 Raw Icy Pearl in slot 54 to Doctorts
For char Rejecca slot3 - Moving 300 Xenon in slot 77 to Doctorts
For char Rejecca slot4 - Moving 300 Xenon in slot 78 to Doctorts
For char Rejecca slot5 - Moving 300 Zalmoxium Ore in slot 80 to Doctorts
Actual sending disabled.
For char Rejecca slot1 - Moving 300 Brood Oil in slot 6 to Fourfiveore
For char Rejecca slot2 - Moving 300 Hawkinsite in slot 24 to Fourfiveore
For char Rejecca slot3 - Moving 300 Raw Skystone in slot 57 to Fourfiveore
For char Rejecca slot4 - Moving 300 Rhodite in slot 63 to Fourfiveore
For char Rejecca slot5 - Moving 300 Titanium Ore in slot 73 to Fourfiveore
Actual sending disabled.
For char Shogunfive slot1 - Moving 300 Vanadium Ore in slot 64 to Doctorts
Actual sending disabled.
For char Shogunfive slot1 - Moving 300 Meteoric Sand in slot 28 to Fourfiveore
For char Shogunfive slot2 - Moving 300 Solar Sweet Oil in slot 53 to Fourfiveore
Actual sending disabled.
For char Shogunfive slot1 - Moving 300 Raw Citrine in slot 41 to Twothreeore
For char Shogunfive slot2 - Moving 300 Star Iron Ore in slot 55 to Twothreeore
Actual sending disabled.

 

The script runs in multiple phases.

  1. Moving partial stacks to the staging characters,
  2. Moving full stacks or raw ore on the staging characters to the vault chars, then any excess to the designated 'Refining' character (doctorTS in this case)
    1. This is what is shown above.
    2. If you look for Emperion you can see this balancing behavior in action. I want to keep 300 in reserve on my vault, and any extra gets refined and either sold or saved (see step 3). Despite the doctorTS line printing first, it actually did slate a stack for the vault character (Eightnineore) first, but so few stacks went to the vault vs needing to go to doctorTS that the doctorTS batch ended up 'sending' (this is in debug mode, so it didn't actually move it) first.
  3. Moving refined items to their vault(s) or the designated Sell Character (someone with Negotiate 7 :D)

In reality, it would never be able to do all 3 in one pass. You'd have to log in between phases 2 and 3 and actually refine the ore. Also, the only way to collapse stacks is in-game, so that remains a manual step. But still...if you sit and count, that's 66 stacks of ore for DoctorTS to refine. And to know that I needed to move those 66 stacks I would have to check *at least* 5 different inventories. This tool is SO much less mental work. yay.

 

Oh, and the fact that it's breaking up into little transfers of 5, 1, 2, etc, that's because it's honoring the portal rules and treating each source:destination pair as unique. So it will do batches of 12 when it can, and when it can't, it will do as many as it can. I haven't actually tested whether the API supports more than 12 items, having one send from a single source go to multiple dests, and various other things like that...I don't plan to. Or if I do it will be purely to sate my curiosity. I imagine the handler is robust against such non-typical uses. But I also admit to raw curiosity.

 

And, yes, it works with hulk contents too. In my use case, Bogeril stuff goes to one character, and then I have separate vaults for Prototype Weapons/shields/reactors/engines. I also have regional vaults for various turn-ins (so Terran space suits go to Earth, Progen ones go to Mars, as do all the Gene Maps, etc). Because of how it's designed, it would not be particularly difficult to change it to be able to move ore in bulk. So if you have a TT making 100s of stacks of ammo for alts, this could also automate moving that, with minor effort.

 

And yes, Stygian Blackwater is INCREDIBLY abundant.

 

P.S. Yes, I say 'automate'. It's semi-auto. You have to manually trigger it, and do other manual stuff around it. It's automatic like a semi-auto handgun is automatic. You still have to do manual stuff to make it go bang.

Link to comment
Share on other sites

Right, I should explain. So, because of how many slots of ore exist per level, I have 5 vault characters for raw ore, and 2 for refined (not all refined is useful, so you need less). Then I have 5 more 'staging characters'. Those are the 5 TTs that fly with my JE so I can use them as mules while mining. So, the flow of ore/hulk stuff is like this:

 

Roid -> JE -> Staging Character (until full stack) -> Vault character (until reserve quantity met) -> Refining character -> (2 possibles; selling character or refining vault).

 

I don't really like the idea of having 'staging characters', but I'm not about to level up 5 more TTs from lvl 0, so...meh.

 

Oh! And for the Hulk stuff, prototype gear specifically, I have a way to only save the (customizable) 120%+ quality stuff. In my case, weapons have to be at least 120% quality to get saved, and shields/reactors/engines can be 100% or better. But those are variables in the script and easy to change/customize.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...