Jump n Bump - AI bots

Post gaming related scripts
elmoonlite
Posts: 2
Joined: 06 Sep 2018, 14:55

Jump n Bump - AI bots

19 Mar 2022, 17:02

I wrote an AutoHotkey script for the game Jump n Bump.
You can toggle each of the 4 bunny rabbit players to automatically jump around,
effectively making a pvp only game a single player game with bots.
Great fun for my 4 year old son.

Could be fairly easy to modify for other simple games.

Code: Select all

#SingleInstance Force
#Warn

; --------------------------------------------------------------------------------
; SETTINGS:
; --------------------------------------------------------------------------------
program_window_title := "Jump n Bump"

; SetTitleMatchMode : used for WinActive(...) and #IfWinActive directive.
; 1: A window's title must start with the specified WinTitle to be a match.
; 2: A window's title can contain WinTitle anywhere inside it to be a match.
; 3: A window's title must exactly match WinTitle to be a match.
SetTitleMatchMode, 3
; --------------------------------------------------------------------------------


; --------------------------------------------------------------------------------
; TODO
; --------------------------------------------------------------------------------
; Sometimes when moving focus outside of the window it still sends keypresses elsewhere.
; It does not happen often, but it does happen sometimes.
; e.g. alt tabbing to a text editor might put random characters there, which is bad.
; Not sure how to fix this yet. Might be say() related.
; --------------------------------------------------------------------------------


is_windowactive := false
players := []
players[1] := {n: 1, enabled: false, in_action: false, keys: {up: "Up"     , left: "Left"   , right: "Right"  } }
players[2] := {n: 2, enabled: false, in_action: false, keys: {up: "w"      , left: "a"      , right: "d"      } }
players[3] := {n: 3, enabled: false, in_action: false, keys: {up: "i"      , left: "j"      , right: "l"      } }
players[4] := {n: 4, enabled: false, in_action: false, keys: {up: "Numpad8", left: "Numpad4", right: "Numpad6"} }

say(str) {
	; NB: saying things blocks execution for a while, could be quite annoying. Use sparingly.
	voice := ComObjCreate("SAPI.SpVoice")
	voice.Rate := 3
	voice.Volume := 80
	voice.Speak(str)
}

player_enable_toggle(n) {
	global players
	player_enable_set(n, players[n].enabled ? false : true)
}
player_enable_set(n, newvalue) {
	global players
	newvalue := !! newvalue
	if (players[n].enabled != newvalue ) {
		players[n].enabled := newvalue
		player_reset(n)
		info()
;		say("player " n " : " (newvalue ? "enabled" : "disabled") . ".")
	}
}

player_reset(n) {
	global players
	players[n].in_action := false
	key_up    := players[n].keys.up
	key_left  := players[n].keys.left
	key_right := players[n].keys.right
	Send {%key_up% up}
	Send {%key_left% up}
	Send {%key_right% up}
}
player_reset_all() {
	player_reset(1)
	player_reset(2)
	player_reset(3)
	player_reset(4)
	global queue
	queue := []
}

info() {
	global players
	p1 := players[1].enabled ? "enabled" : "disabled"
	p2 := players[2].enabled ? "enabled" : "disabled"
	p3 := players[3].enabled ? "enabled" : "disabled"
	p4 := players[4].enabled ? "enabled" : "disabled"
	str = 
(
Player 1: %p1%
Player 2: %p2%
Player 3: %p3%
Player 4: %p4%

F1..F4 to toggle player.
~ to show this info.
)
;	MsgBox % str
	Tooltip % str, 20, 40
	SetTimer, removetooltip, -1000
}

removetooltip() {
	ToolTip
}

; show info once on startup:
; info()

getTime_freq := 0
getTime() {
	; return amount of seconds sinds computer turn on.
	; precision seems to be 1 / 10,000,000 seconds.
	global getTime_freq
	if (!getTime_freq) {
		DllCall("QueryPerformanceFrequency", "Int64*", getTime_freq)
;		MsgBox % getTime_freq ; e.g. 10000000 (10,000,000)
	}
	microtime := 0
	DllCall("QueryPerformanceCounter", "Int64*", microtime)
;	MsgBox % microtime ; e.g. 25878136383
	return microtime / getTime_freq
}

; put tasks with a "when" (do after timestamp) value in here:
queue := []

player_loop() {
	global players
	global queue

	now = getTime()

	for k, q in queue {
		if (q && now > q.when) {
			queue.RemoveAt(k)
/*
			if (q.send_arr) {
				for k_, s in q.send_arr {
					send_key := s.key
					send_updown := s.updown
;					MsgBox  % "send_key:" . send_key . " send_updown:" . send_updown
;					ToolTip % "send_key:" . send_key . " send_updown:" . send_updown
					Send {%send_key% %send_updown%}
				}
			}
*/

			if (q.player_reset && q.n) {
				player_reset(q.n)
			}

			players[q.n].in_action := false
		}
	}


	n := 0
	while (n < 4) {
		n := n + 1
		if ( ! players[n].enabled ) {
			continue
		}
		if (players[n].in_action ) {
			continue
		}

		; action is just an arbitrary number for an action
		Random, action, 0, 2

		key_up := players[n].keys.up
		key_left := players[n].keys.left
		key_right := players[n].keys.right
		if (action = 0) {
			; do nothing for a while
			player_reset(n)
			Random, rndtime, 500, 3000
			queue.push({"n": n, "when": now + rndtime, player_reset: true})
			players[n].in_action := true
		}
		if (action = 1) {
			; jump left
			player_reset(n)
			Send {%key_up% down}
			Send {%key_left% down}
			Random, rndtime, 500, 1000
;			queue.push({"n": n, "when": now + rndtime, "send_arr": [{"key": key_up, "updown": "up"}, {"key": key_left, "updown": "up"}]})
			queue.push({"n": n, "when": now + rndtime, player_reset: true})
			players[n].in_action := true
		}
		if (action = 2) {
			; jump right
			player_reset(n)
			Send {%key_up% down}
			Send {%key_right% down}
			Random, rndtime, 500, 1000
;			queue.push({"n": n, "when": now + rndtime, "send_arr": [{"key": key_up, "updown": "up"}, {"key": key_right, "updown": "up"}]})
			queue.push({"n": n, "when": now + rndtime, player_reset: true})
			players[n].in_action := true
		}
	}
}



; ------------------------------------------------------------------------------------------------------------------------
; --- main loop ----------------------------------------------------------------------------------------------------------
; ------------------------------------------------------------------------------------------------------------------------


; Loop that checks if we have lost active window focus, and do a keyup on any keys still "down".
; This prevents odd behaviour outside active window when alt tabbing and some keys are still down.
Loop {
;	if WinActive("Jump n Bump") {
	if WinActive(program_window_title) {
		if not is_windowactive {
			is_windowactive := true
			; window used to be inactive and is now active (only say once):
			info()
;			say("active")
		}
		; call player_loop() here for less chance of sending keyup/keydowns outside of the active window:
		player_loop()
	} else {
		if is_windowactive {
			is_windowactive := false
			; window used to be active and is now inactive (only say once):
			player_reset_all()
			removetooltip()
;			say("inactive")
		}
	}
	Sleep 100
}






; Any keybindings below only active when in the correct window:

;#IfWinActive, Jump n Bump
;#IfWinActive, %program_window_title% ; no worky with variable
#If WinActive(program_window_title)

`::info()
F1::player_enable_toggle(1)
F2::player_enable_toggle(2)
F3::player_enable_toggle(3)
F4::player_enable_toggle(4)

t::Numpad8
f::Numpad4
g::Numpad2
h::Numpad6

#If


Return to “Gaming Scripts (v1)”

Who is online

Users browsing this forum: No registered users and 21 guests