Today, my goal was again to improve my POSIX shell scripting abilities.
In one MMO game called Goodgame Empire, there is possible to code yourself a coin (tax) collector. But that's of less importance than my intention above.
Sorry, I am not able to avoid arrays, well it could be done, but an elegant way? I will be happy to see any and all suggestions.
#!/bin/bash
print_usage_and_exit()
{
echo "Usage: $0 [-1]"
echo " -1: One-time coin collect."
echo "Default: Repeat coin collecting until CTRL+C is pressed."
exit 1
}
no_repeat=false
while getopts ":1h" option
do
case "${option}" in
1)
no_repeat=true
;;
h | *)
print_usage_and_exit
;;
esac
done
shift $((OPTIND - 1))
# ------------------------------------------------------------------------------
# global constants and variables - self-explanatory
declare -r window_name_chrome="Goodgame Empire - Google Chrome"
declare -r screen_resolution=$(xdpyinfo | awk '/dimensions:/ {print $2}')
# we need to keep track of these two variables used by mouse_click function
previous_rand=10
operation_add=true
# ------------------------------------------------------------------------------
print_error_and_exit()
{
# check if exactly two arguments have been passed
test "$#" -eq 2 || print_error_and_exit 2 "print_error_and_exit(): There have not been passed exactly two arguments!"
# check if the first argument is a number
is_number "$1" || print_error_and_exit 3 "print_error_and_exit(): The argument #1 is not a number!"
bold=$(tput bold)
red=$(tput setaf 1)
nocolor=$(tput sgr0)
echo "$bold$red$2 Exit code = $1.$nocolor" >&2
exit "$1"
}
# ------------------------------------------------------------------------------
is_number()
{
# check if exactly one argument has been passed
test "$#" -eq 1 || print_error_and_exit 4 "is_number(): There has not been passed exactly one argument!"
# check if the argument is an integer
test "$1" -eq "$1" 2>/dev/null
}
# ------------------------------------------------------------------------------
random_number()
{
# check if exactly two arguments have been passed
test "$#" -eq 2 || print_error_and_exit 5 "random_number(): There have not been passed exactly two arguments!"
# check if the arguments are both numbers
is_number "$1" || print_error_and_exit 6 "random_number(): The argument #1 is not a number!"
is_number "$2" || print_error_and_exit 7 "random_number(): The argument #2 is not a number!"
# generate one pseudo-random integer within the specified range
shuf -i "$1-$2" -n 1
}
# ------------------------------------------------------------------------------
activate_window_via_name() {
# check if exactly one argument has been passed
test "$#" -eq 1 || print_error_and_exit 8 "activate_window_via_name(): There has not been passed exactly one argument!"
xdotool search --name "$1" windowactivate --sync
}
# ------------------------------------------------------------------------------
maximize_active_window() {
# check if no argument has been passed
test "$#" -eq 0 || print_error_and_exit 9 "maximize_active_window(): There has been passed some argument, none expected!"
wmctrl -r :ACTIVE: -b add,maximized_vert,maximized_horz
}
# ------------------------------------------------------------------------------
mouse_click() {
# check if exactly two arguments have been passed
test "$#" -eq 2 || print_error_and_exit 10 "mouse_click(): There have not been passed exactly two arguments!"
# check if both of the arguments are numbers
is_number "$1" || print_error_and_exit 11 "mouse_click(): The argument #1 is not a number!"
is_number "$2" || print_error_and_exit 12 "mouse_click(): The argument #2 is not a number!"
# 1. invert the operation_add boolean value,
# it seems Bash does not have inbuilt command for that
# N: operation_add determines whether we will be adding or
# subtracting the random number later
test "$operation_add" = true && operation_add=false || operation_add=true
# 2. generate pseuso-random integer between 0 and 7, inclusive,
# if the generated number is the same as the previous_rand,
# generate until it is different
# N: rand will be later used as pixel offset from the given coordinates
# we define a constant for randomness
declare -r randomness=7
rand=$(random_number 0 "$randomness")
while [ "$rand" -eq "$previous_rand" ]
do
rand=$(random_number 0 "$randomness")
done
# 3. we don't want to repeat clicks right with the same offset,
# so we store information about the previous_rand here
previous_rand="$rand"
# 4. depending on the boolean value of operation_add,
# we either add the rand, or subtract it to/from the position x/y
if [ "$operation_add" = true ]
then
pos_x=$(($1 + rand))
pos_y=$(($2 + rand))
else
pos_x=$(($1 - rand))
pos_y=$(($2 - rand))
fi
# activate Goodgame Empire window and wait for sync,
# we need to do this before each click,
# because the user may have clicked on some other window
# during the 2 second delay
activate_window_via_name "$window_name_chrome"
maximize_active_window
# xdotool can move mouse and simulate button clicks and more
# ----------------------------------------------------------
# move the mouse cursor to the given position and wait for sync
# click the left mouse button
# restore the original mouse cursor position and wait for sync
# wait for 2 seconds
xdotool \
mousemove --sync "$pos_x" "$pos_y" \
click 1 \
mousemove --sync restore \
sleep 2
}
# ------------------------------------------------------------------------------
mouse_click_coords() {
# accept all parameters together as one array
local coords=("$@")
# self-explanatory, but non-memorizable
array_items_count="${#coords[*]}"
# check if there have been passed exactly ten arguments
test "$array_items_count" -eq 10 || print_error_and_exit 13 "mouse_click_coords(): There have not been passed exactly ten arguments!"
for (( i = 0; i < "$array_items_count"; i += 2 ))
do
mouse_click "${coords[$i]}" "${coords[$i + 1]}"
done
}
# ------------------------------------------------------------------------------
collect_coins_1920x1080() {
local coords=(
1895 955
1104 691
1131 660
1145 570
1199 381
)
mouse_click_coords "${coords[@]}"
}
# ------------------------------------------------------------------------------
collect_coins_3840x1080() {
local coords=(
3815 955
3024 691
3051 660
3065 570
3119 381
)
mouse_click_coords "${coords[@]}"
}
# ------------------------------------------------------------------------------
collect_coins() {
case "$screen_resolution" in
1920x1080) collect_coins_1920x1080
;;
3840x1080) collect_coins_3840x1080
;;
esac
}
# ------------------------------------------------------------------------------
if [ "$no_repeat" = false ]
then
echo "Repeating coin collecting until CTRL+C is pressed!"
while true
do
collect_coins
# wait for 10 minutes
sleep 600
done
else
echo "One-time coin collecting!"
collect_coins
fi