Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

A Starcraft Broodwar Script V1.3b


  • Please log in to reply
42 replies to this topic
Enter_User_Name_Here
  • Members
  • 35 posts
  • Last active: Jan 19 2012 07:40 PM
  • Joined: 18 Apr 2010

Well i havent tested it all totally, but I can tell you that the sends seem to be working on my system.

I was able to set zones and recall them, so the mouse movements are working..

cycling of the units seems to be working.

sounds like a vista issue..
perhaps a different sendmode setting ? possibly different timing settings? or if there is an alternative to sleep?


I got most of the code working on Win7. To do so, I had to compile it and run it as admin. However, it is not picking up the right pixel colors (my guess) because it is not detecting which race the player is or how many units in a selected group. ShowMessage() is also spotty. It was working early in the game, but I paused the game in order to adjust the brightness to see if it had any effect and I lost it.

Perhaps the output could be better achieved with GPF v1.1b:
http://www.autohotke...topic45689.html

000sixzeros000
  • Members
  • 43 posts
  • Last active: Apr 23 2011 08:11 AM
  • Joined: 17 Oct 2009
I ran into some pixel issues with gamma settings.. I realized afterwards that the colors could be anything given gamma. There's a note above about setting gamma manually.

I believe this version had a custom pixel check function that looked at a range of pixel colors..If it doesnt then we might need to cook one up.

Im thinking we could look for the RGB values at a particular location that should never change.. perhaps part of the user interface while in the lobby.. and then use this value to calculate a gamma offset. We would want to examine the R G B component separately, perhaps average them, and then compare them to an average of what we expect them to be. (This would probably work best on a middle gray pixel of about #808080). That would give us say a gamma delta.. and we could auto correct our pixel color checks.

In a prior version I had some troubles with send message getting out of sync.. so I set a key that would reset the flags. Im not sure if its the same issue.. but maybe manually clearing the flags would allow you to debug the issue.

to work out those pixel values, I took a screenshot, and then loaded the images in a image processing app.. (photoshop) then zoomed in on the pixel and read off the pixel RGB color value. You might want to look up the exact pixel locations being used to identify the races.. and then check that the values match.. this would tell you if you need to dial the gamma up or down.

000sixzeros000
  • Members
  • 43 posts
  • Last active: Apr 23 2011 08:11 AM
  • Joined: 17 Oct 2009
ahh, so the checkpixel functions for isTerran(),isZerg(),isProtoss() were hardcoded and are not using the ranged checkPixelColor Function.. the ,10) below allows you to set a range to compensate for gamma variance. the comparison is done within the checkColor function.. Here are modified versions of the race check.. the ,10)) tells it to allow +/- 10 decimal for each R G or B component. you could dial this up and down if you wanted.


;------------------------------------------
isTerran(){
;------------------------------------------
  if (checkPixelColor(0x36363f, 5, 317,10)){
    return 1
  } else {
    return 0
  }
}
;------------------------------------------
isZerg(){
;------------------------------------------
  if (checkPixelColor(0x797979, 5, 317,10)){
    return 1
  } else {
    return 0
  }
}
;------------------------------------------
isProtoss(){
;------------------------------------------
  if (checkPixelColor(0x3bbfa2, 5, 317,10)){
	return 1
   }else{
     return 0
   }
}

;------------------------------------------
countUnits(){
;------------------------------------------
    xSrch:=169
    ySrch:=398
    XW:=36
    YH:=37
    pColor:=0x1459d4   
   ;}
  total:=0  
  
  if( checkPixelColor(pColor,XSrch,YSrch,10) ) {
    total++
  }
  XSrch+=XW
  if( checkPixelColor(pColor,XSrch,YSrch,10) ) {
    total++
  }
  XSrch+=XW
  if( checkPixelColor(pColor,XSrch,YSrch,10) ) {
    total++
  }
  XSrch+=XW
  if( checkPixelColor(pColor,XSrch,YSrch,10) ) {
    total++
  }
  XSrch+=XW
  if( checkPixelColor(pColor,XSrch,YSrch,10) ) {
    total++
  }
  XSrch+=XW
  if( checkPixelColor(pColor,XSrch,YSrch,10) ) {
    total++
  }
  XSrch:=169
  YSrch+=YH
  if( checkPixelColor(pColor,XSrch,YSrch,10) ) {
    total++
  }
  XSrch+=XW
  if( checkPixelColor(pColor,XSrch,YSrch,10) ) {
    total++
  }
  XSrch+=XW
  if( checkPixelColor(pColor,XSrch,YSrch,10) ) {
    total++
  }
  XSrch+=XW
  if( checkPixelColor(pColor,XSrch,YSrch,10) ) {
    total++
  }
  XSrch+=XW
  if( checkPixelColor(pColor,XSrch,YSrch,10) ) {
    total++
  }
  XSrch+=XW
  if( checkPixelColor(pColor,XSrch,YSrch,10) ) {
    total++
  }
 return total
}


We will want to make this change to all calls to pixelGetColor() and replace them with our custom Gamma adjusted checkPixelColor() function..

Also, keep in mind if you are needing to reset things manually when out of game.. that creating a pixel test for the lobby, and then activating any reset functions before going back into the game, works really well. And as an emergency backup you could create a hotkey to just reset the script while in game if things go nuts..

{YOUR_RESET_KEY}::
 Reload
 msgBox reloading
Return


000sixzeros000
  • Members
  • 43 posts
  • Last active: Apr 23 2011 08:11 AM
  • Joined: 17 Oct 2009
re: sendmessage ...

it uses the function.. isActive() to make sure that you are not in the game lobby... (otherwise it will spew messages and all sorts over the chat room as you type away..) This is also a good function to insert your defineGlobals reset .. so you dont need to do it manually with cntl Z.

isActive does a pixel check... if the gamma changes such that the pixel cant be detetected then you will have an isActive() failure, at which point the messages and everything will start to fail.

000sixzeros000
  • Members
  • 43 posts
  • Last active: Apr 23 2011 08:11 AM
  • Joined: 17 Oct 2009
Well this has bought to light some possible issues..

FIXING GAMMA SETTINGS...

So I added this function to allow you to tune it in.. It will show the actual gamma value detetected and how far off you are so you can dial it up and down.. you need to be within 10 decimal of the target for the pixel detections to start to work.

Grab the latest version.. it has this code below.., hit CNTL V and it will show a message telling you what your actual gamma setting is, and what it needs to be.

Hit F10 or bring up the options menu, go to video settings and your gamma up or down accordingly. If your 'actual' is lower than target then dial your gamma up, and if it is higher than target then bring it down..

Exit the menu and do a test for gamma. The setting required is very close to the 6th notch on the gamma slider.

Remember you need to be within 10 points of the target, if you are off by 1 or 2, then this should be okay.

^v::
   PixelGetColor, pixelColor, 436, 3,RGB
  ; Convert a decimal integer to hexadecimal:
   SetFormat, IntegerFast, hex
   pixelColor +=0
   pixelColor .= ""  ; Necessary due to the "fast" mode.
   Debug:=true
   sendMessage("Target= 0x2d23c2 Actual=" . pixelColor  )
   SetFormat, IntegerFast, d
   Debug:=false
Return


Enter_User_Name_Here
  • Members
  • 35 posts
  • Last active: Jan 19 2012 07:40 PM
  • Joined: 18 Apr 2010

Well this has bought to light some possible issues..

FIXING GAMMA SETTINGS...

So I added this function to allow you to tune it in.. It will show the actual gamma value detetected and how far off you are so you can dial it up and down.. you need to be within 10 decimal of the target for the pixel detections to start to work.

Grab the latest version.. it has this code below.., hit CNTL V and it will show a message telling you what your actual gamma setting is, and what it needs to be.

Hit F10 or bring up the options menu, go to video settings and your gamma up or down accordingly. If your 'actual' is lower than target then dial your gamma up, and if it is higher than target then bring it down..

Exit the menu and do a test for gamma. The setting required is very close to the 6th notch on the gamma slider.

Remember you need to be within 10 points of the target, if you are off by 1 or 2, then this should be okay.

^v::
   PixelGetColor, pixelColor, 436, 3,RGB
  ; Convert a decimal integer to hexadecimal:
   SetFormat, IntegerFast, hex
   pixelColor +=0
   pixelColor .= ""  ; Necessary due to the "fast" mode.
   Debug:=true
   sendMessage("Target= 0x2d23c2 Actual=" . pixelColor  )
   SetFormat, IntegerFast, d
   Debug:=false
Return


Cool. I'll have to check it out when I get home. Although, ideally I think we should be aiming for adjusting our pixel searches for what the user has set their brightness, not asking the user to adjust their brightness to a certain level. I think that this can be accomplished -- even if it means searching multiple pixels instead of just one to determine IfActive().

Also, I noticed an error within my messages for Units and Zones... I added the # symbol which is getting translated to pushing the Win key and getting out of game. I removed them from the above code, but anyone that has grabbed it so far should remove them or grab the newest code.

I did attempt briefly to no avail to get GPF to work. I would like to get this working so that a constant display of which Hotkeys groups and Zones are active/set may be displayed.


EDIT:

Ok. I looked at before getting home. Perhaps something along the lines of this would work?

;==================================
;==================================
CheckColor(RGBValue,RGBTarget,vRange)    ; make sure pixels are within a range to compensate for gamma
{
  vRed := 0
  vGreen := 0
  vBlue := 0
  tRed := 0
  tGreen := 0
  tBlue := 0
 
  NormalizeColor(RGBValue,vRed,vGreen,vBlue)
  NormalizeColor(RGBTarget,tRed,tGreen,tBlue)
  
  
  if ( (vRed > (tRed * (1 + vRange) )) OR ( vRed < (tRed * (1 - vRange) ) ) ) {
    return FALSE
  }
  if ( (vBlue > (tBlue * (1 + vRange) )) OR (vBlue < (tBlue * (1 - vRange) ) ) ) {
    return FALSE
  }
  if ( (vGreen > (tGreen * (1 + vRange) )) OR (vGreen < (tGreen * (1 - vRange) ) ) ) {
    return FALSE
  }
  return TRUE
}

;==================================
;==================================
NormalizeColor(RGBValue, ByRef Red, ByRef Green, ByRef Blue )
{
  SplitRGBColor(RGBValue, Red, Green, Blue)
  Normalizer := Red + Green + Blue
  if ( Normalizer <> 0) {
    Red := Red / Normalizer
    Green := Green / Normalizer
    Blue := Blue / Normalizer
  }
}
return

;==================================
;==================================
SplitRGBColor(RGBColor, ByRef Red, ByRef Green, ByRef Blue)
{
  Red := RGBColor >> 16 & 0xFF
  Green := RGBColor >> 8 & 0xFF
  Blue := RGBColor & 0xFF
}
return
  
;==================================
;==================================
CheckPixelColor(TargetPixelColor,xpos,ypos,vRange = 0.05)
{
  PixelGetColor, PixelColor, xpos, ypos,RGB
  return CheckColor(PixelColor,TargetPixelColor,vRange)
}



000sixzeros000
  • Members
  • 43 posts
  • Last active: Apr 23 2011 08:11 AM
  • Joined: 17 Oct 2009
I see where you're going.. Here's an idea..
Although that gamma adjustment.. doesnt look linear.. I had a bit of a play with it.. I wonder if luminence would yield more accuracy?

getLuminence(RGBValue, ByRef Red, ByRef Green, ByRef Blue ) 
{ 
  SplitRGBColor(RGBValue, Red, Green, Blue) 
  return  ((Red*.30) + (Green*.59) + (Blue*.11)) 
} 
return 

getGray(RGBValue, ByRef Red, ByRef Green, ByRef Blue ) 
{ 
  SplitRGBColor(RGBValue, Red, Green, Blue) 
  return  ((Red+Green+Blue)/3)
} 
return 


ALthough, upon closer inspection, I found that the gamma is not that sensitive, and that th existing method where we just do a +/- 10 on each color component, allows for a fairly broad range. If you are within a notch or two on the slider.. it still seems to work okay.

Enter_User_Name_Here
  • Members
  • 35 posts
  • Last active: Jan 19 2012 07:40 PM
  • Joined: 18 Apr 2010

I see where you're going.. Here's an idea..
Although that gamma adjustment.. doesnt look linear.. I had a bit of a play with it.. I wonder if luminence would yield more accuracy?

getLuminence(RGBValue, ByRef Red, ByRef Green, ByRef Blue ) 
{ 
  SplitRGBColor(RGBValue, Red, Green, Blue) 
  return  ((Red*.30) + (Green*.59) + (Blue*.11)) 
} 
return 

getGray(RGBValue, ByRef Red, ByRef Green, ByRef Blue ) 
{ 
  SplitRGBColor(RGBValue, Red, Green, Blue) 
  return  ((Red+Green+Blue)/3)
} 
return 


ALthough, upon closer inspection, I found that the gamma is not that sensitive, and that th existing method where we just do a +/- 10 on each color component, allows for a fairly broad range. If you are within a notch or two on the slider.. it still seems to work okay.


Looking at the Hue within a +-10 degree span looks like it will get the job done (at least for race determination).

;==================================
;==================================
CheckColor(RGBValue,RGBTarget,vRange)    ; make sure pixels are within a range to compensate for gamma
{
  vHue := DetermineHue(RGBValue)
  tHue := DetermineHue(RGBTarget)
  if ( (vHue > (tHue + vRange)) OR (vHue < (tHue - vRange)) ) {
    return FALSE
  }
  return TRUE
}

;==================================
;==================================
DetermineHue(RGBValue)
{
  Red := 0
  Green := 0
  Blue := 0
  SplitRGBColor(RGBValue, Red, Green, Blue)
  if ( (Red >= Green) AND (Green >= Blue ) ) {
    Hue := 60 * (Green - Blue) / (Red - Blue)
  } else if ((Green > Red) AND (Red >= Blue)) {
    Hue := 60 * ( 2 - (Red - Blue) / (Green - Blue) )
  } else if ((Green >= Blue) AND (Blue > Red)) {
    Hue := 60 * ( 2 + (Blue - Red) / (Green - Red) )
  } else if ((Blue > Green) AND (Green > Red)) {
    Hue := 60 * ( 4 - (Green - Red) / (Blue - Red) )
  } else if ((Blue > Red) AND (Red >= Green )) {
    Hue := 60 * ( 4 + (Red - Green) / (Blue - Green) )
  } else if ((Red >= Blue) AND (Blue > Green)) {
    Hue := 60 * ( 6 - (Blue - Green) / (Red - Green) )
  } else {
    Hue := ""
  }
  return Hue
}

I discovered another problem. The offsets for the mini-map center are only good for small 2-player boards. When you have a larger map, you have a smaller white rectangle in the mini-map. Any ideas for successfully finding the center? (Determine board size? Determine rectangle size? Imagesearch with transparent areas for each rectangle?)

Enter_User_Name_Here
  • Members
  • 35 posts
  • Last active: Jan 19 2012 07:40 PM
  • Joined: 18 Apr 2010
As far as the mini-map offset is concerned... I think running this once the first time IsActive() returns true for each game -- or something similar should solve the problem. I'll see if I can get this working and will post my most recent script.

FindCenter(ByRef x, ByRef y)
{
  color := 0xFFFFFF
  End_X := x
  End_Y := y
  while (color = 0xFFFFFF) {
    End_X += 1
    PixelGetColor, color, End_X, y, RGB
  }
  while (color = 0xFFFFFF) {
    End_Y += 1
    PixelGetColor, color, x, END_Y, RGB
  }
  x := (End_X - x) / 2
  y := (End_Y - y) / 2
}
return

EDIT: Didn't work. Perhaps the center of the rectangle is not the proper target? Looks to still be off to the upper left.

000sixzeros000
  • Members
  • 43 posts
  • Last active: Apr 23 2011 08:11 AM
  • Joined: 17 Oct 2009
Ah yes, as I recall the target was a pixel off or two from center.. but you could adjust for that with a +1 or somehting.. depending on the size of the map.. something I never considered though, that the map sizes would affect the bounding area of the white square...

I also had an opportunity to play around with Starcraft II beta this weekend.. the bounding area of the minmap box is going to be a little tricky because it is not a square, it changes shape with elevation. A general purpose function that runs the traverse covering Y,Y+1 and Y-1 is going to be needed. and then the bounds calculated, and an arbitrary center will need to be created and forced.. the code will need to also force a maximum elevation somehow. So our area location code, will need to begin by forcing a new center, the map might move slightly, but after that we will have a fixed return position and building location setting can orient from there. I could live with a slight bump.
Everything else though, looks like it can be adapted slightly to the game. There are some nice changes to the code though, it seems you can cntl click buildings, groupings can be much larger, perhaps 24 to 32 units or so at once.
The f2,f3,f4 function key locators dont seem work anymore, so being able to create our own as we do with the zone controls might be very handy. The minimap attack keys will still work fine, and the group attack keys will be totally evil when your forces are massed.
The hotkeys have changed slightly, like Pylon and probe is now an E instead of P, which is much easier to use than that stretch across the keyboard between A,V,B and P.

Enter_User_Name_Here
  • Members
  • 35 posts
  • Last active: Jan 19 2012 07:40 PM
  • Joined: 18 Apr 2010
Aha! This should work:
;==================================
;==================================
GetMiniMapPosition()
{ 
  global MiniMapX, MiniMapY
  BlockInput On
  GetMiniMapCorner()
  PreviousX := MiniMapX
  PreviousY := MiniMapY
  WaitGameCycle()
  MouseMove, %MiniMapX%, %MiniMapY%, 0
  send {click}
  WaitGameCycle()
  GetMiniMapCorner()
  MiniMapX := (2 * PreviousX) - MiniMapX
  MiniMapY := (2 * PreviousY) - MiniMapY
  WaitGameCycle()
  MouseMove, %MiniMapX%, %MiniMapY%, 0
  send {click}
  BlockInput Off
}
return

;==================================
;==================================
GetMiniMapCorner()
{
  global MiniMapX,MiniMapY
  PixelSearch MiniMapX,MiniMapY, 5,348,133,477,0xffffff,1
  if (ErrorLevel = 0) {
    return TRUE
  }
  return FALSE
}

The only downside is that this will jump every time the GetMiniMapPosition is called... however, I think this isn't such a bad thing, because I noticed slightly different Map positions use the same MiniMap position... and if we are basing our building positions on having the exact same map position, the map position should be recalled before setting the building positions.

000sixzeros000
  • Members
  • 43 posts
  • Last active: Apr 23 2011 08:11 AM
  • Joined: 17 Oct 2009
lol, nice trick..

Enter_User_Name_Here
  • Members
  • 35 posts
  • Last active: Jan 19 2012 07:40 PM
  • Joined: 18 Apr 2010
It takes too long to jump every time. Too annoying. So I think determining the offset by looking at the length of the mini-map rectangle. To that end, if I have time tonight I will use this to find the correct offsets to program in.

^t::
{
  BlockInput On
  SaveMousePos()
  GetMiniMapPosition()
  PreviousX := MiniMapX
  PreviousY := MiniMapY
  sleep 100
  MouseMove, %PreviousX%, %PreviousY%, 0
  send {click}
  sleep 100
  GetMiniMapPosition()
  offsetX := PreviousX - MiniMapX
  offsetY := PreviousY - MiniMapY

  ShowMessage("Offset should be: (" . offsetX . ", " . offsetY . ") For box length: " . GetLengthOfMiniMapBox(MiniMapX, MiniMapY))
  sleep 1000
  MouseMove, %offsetX%, %offsetY%, 0, R
  send {click}
  RestoreMousePos()
  BlockInput Off
}
return

;==================================
;==================================
GetLengthOfMiniMapBox(x, y)
{
  color := 0xFFFFFF
  End_X := x
  while (color = 0xFFFFFF) {
    End_X += 1
    PixelGetColor, color, End_X, y, RGB
  }
  return (End_X - x)
}

;==================================
;==================================
GetMiniMapPosition()
{
  global MiniMapX,MiniMapY
  PixelSearch MiniMapX,MiniMapY, 5,348,133,477,0xffffff,1
  if (ErrorLevel = 0) {
    MiniMapX += 0
    MiniMapY += 0
    return TRUE
  }
  return FALSE
}

;==================================
;==================================
ShowMessage(Message)
{
  Global MsgActive
  gosub ClearMessage
  if ( TRUE ) {
    setTimer ClearMessage, 3000
    MsgActive := TRUE
    send {enter}%Message%
  }
}
return

;==================================
;==================================
ClearMessage:
  setTimer ClearMessage,OFF
  if (MsgActive) {
    MsgActive := FALSE
    send {esc}
  }
return

;==================================
;==================================
RestoreMiniMapPos()
{
 Global Stored_MiniMap_X,Stored_MiniMap_Y
 SetMiniMapPosition(Stored_MiniMap_X,Stored_MiniMap_Y)
}
return

;==================================
;==================================
RestoreMousePos()
{
 Global Stored_Mouse_X,Stored_Mouse_Y
 MouseMove,%Stored_Mouse_X%, %Stored_Mouse_Y%, 0
}
return

;==================================
;==================================
SaveMiniMapPos()
{
  Global Stored_MiniMap_X,Stored_MiniMap_Y,MiniMapX,MiniMapY
  GetMiniMapPosition()
  Stored_MiniMap_X := MiniMapX
  Stored_MiniMap_Y := MiniMapY
}
return

;==================================
;==================================
SaveMousePos()
{
  Global Stored_Mouse_X,Stored_Mouse_Y
  MouseGetPos Stored_Mouse_X, Stored_Mouse_Y
}
return

I think this is the last hurdle to have a fully functional macro script. I still would like to add an overlay so that you can display constant information about the groups/zones etc... but that would just be icing.

000sixzeros000
  • Members
  • 43 posts
  • Last active: Apr 23 2011 08:11 AM
  • Joined: 17 Oct 2009
I see what you mean, should only need to calibrate it like that once.. record the offsets and then set them in stone for the duration of the sitting.
Or you could just hard code them for the window size found..

SC II is coming up though... beta just closed.. and I swear once you play it.. you're going to put BW away forever.. lol. so keep in the back of your mind how you're going to handle a bounding box that changes shape constantly.

another way.. might be to just record some kind of calibration click made in the mini map.. a SHIFT-ALT click in the mini map for example. and that becomes the zone location, maybe even the trigger for the zone building location record phase.

Enter_User_Name_Here
  • Members
  • 35 posts
  • Last active: Jan 19 2012 07:40 PM
  • Joined: 18 Apr 2010

I see what you mean, should only need to calibrate it like that once.. record the offsets and then set them in stone for the duration of the sitting.
Or you could just hard code them for the window size found..


I was planning on hard coding the offset based on the box size. I was just going to check the box size each time... I figured that it should just be 1 additional pixel check (unless there are 3 box sizes... but I think there are only 2). We just need to check 1 pixel passed the end of the shorter length to see if it is the longer length. So this check should be fast.

SC II is coming up though... beta just closed.. and I swear once you play it.. you're going to put BW away forever.. lol. so keep in the back of your mind how you're going to handle a bounding box that changes shape constantly.

another way.. might be to just record some kind of calibration click made in the mini map.. a SHIFT-ALT click in the mini map for example. and that becomes the zone location, maybe even the trigger for the zone building location record phase.


As far as SCII is concerned... is it a rectangle or is it some other shape? I suppose, either way, it should be easy enough to run a pixel check to see what size mini-map recticle you have. Perhaps, you could run a conversion of the user's mouse click at a particular elevation to another (X,Y) at a different elevation.

So the zone remembers the location as (X, Y, Z) and figures (X', Y') based on Z'. Only adjusting the elevation when (X', Y') is outside the currently viewable area.

I had already been toying around in the back of my head to do something similar with SCI... to figure out if zone buildings are already onscreen and only adjusting the mini-map when needed.

EDIT: Posted my most recent version on the first page.