Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
BUTTON_COLOR = (50, 50, 50)
BUTTON_HOVER_COLOR = (150, 150, 150) # Dark Grey
BUTTON_TEXT_COLOR = (255, 255, 255) # White
MENU_HIT_THRESHOLD = 1000 # How hard to hit the button
MENU_HIT_THRESHOLD = 2000 # How hard to hit the button
GAMEOVER_COOLDOWN = 5.0 # Seconds to wait before button active

#== ADAPTIVE DIFFICULTY SETTINGS ==#
Expand Down
5 changes: 3 additions & 2 deletions debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def draw_vision_pipeline(self, main_frame, raw_frame, gray_frame, mask_frame, vi

# Loop through and draw each one with a nice green border and label
for y, pip, title in zip(y_positions, pips, titles):
# Overwrite pixel blocks to create tiny debug screen
main_frame[y:y+pip_h, x_offset:x_offset+pip_w] = pip
cv.rectangle(main_frame, (x_offset, y), (x_offset+pip_w, y+pip_h), (0, 255, 0), 2)

Expand All @@ -75,8 +76,8 @@ def draw_thermal_debug(self, main_frame, visual_motion):
if not config.DEBUG_MODE: return

# 1. Calculate numerical intensity (Before color mapping)
mean_val = cv.mean(visual_motion)[0]
_, max_val, _, _ = cv.minMaxLoc(visual_motion)
mean_val = cv.mean(visual_motion)[0] # Calculate average pixel intensity of entire matrix
_, max_val, _, _ = cv.minMaxLoc(visual_motion) # Find min and max pixel and their positions

# --- UI TWEAK: Master Y variable to slide the whole block ---
base_y = 150
Expand Down
99 changes: 99 additions & 0 deletions logs/test_results.csv
Original file line number Diff line number Diff line change
Expand Up @@ -508,3 +508,102 @@ Timestamp,Mode,Player,Target_Dir,Up_Votes,Left_Votes,Right_Votes,Total_Strong,Do
20:08:03,SINGLE,PLAYER 1,LEFT,0,1665,0,1665,LEFT,VALID,85.0,6.7,9.627,0.0077
20:08:06,SINGLE,PLAYER 1,RIGHT,0,0,407,407,RIGHT,VALID,106.0,5.1,4.197,0.0078
20:22:45,SINGLE,PLAYER 1,RIGHT,148,6,843,997,RIGHT,VALID,118.0,30.9,11.478,0.0062
20:29:31,SINGLE,PLAYER 1,UP,1596,0,812,2408,UP,VALID,129.0,33.0,26.846,0.0074
20:29:33,SINGLE,PLAYER 1,RIGHT,336,3702,0,4038,LEFT,WRONG DIRECTION!,109.0,10.7,9.928,0.0093
20:29:33,SINGLE,PLAYER 1,RIGHT,2428,4991,0,7419,LEFT,WRONG DIRECTION!,126.0,13.8,11.538,0.0077
20:29:34,SINGLE,PLAYER 1,RIGHT,268,658,19,945,LEFT,WRONG DIRECTION!,110.0,36.0,12.079,0.0085
20:29:47,SINGLE,PLAYER 1,RIGHT,10,1894,0,1904,LEFT,WRONG DIRECTION!,142.0,8.0,4.899,0.0059
20:29:50,SINGLE,PLAYER 1,RIGHT,216,3740,1216,5172,LEFT,WRONG DIRECTION!,130.0,17.9,28.991,0.0081
20:29:50,SINGLE,PLAYER 1,RIGHT,371,1772,313,2456,LEFT,WRONG DIRECTION!,165.0,31.2,20.637,0.0075
20:29:51,SINGLE,PLAYER 1,RIGHT,4,0,1638,1642,RIGHT,VALID,147.0,11.1,7.467,0.0066
20:29:53,SINGLE,PLAYER 1,LEFT,1913,2183,1402,5498,LEFT,WEAK LEFT PUNCH!,126.0,13.4,11.294,0.0091
20:30:04,SINGLE,PLAYER 1,LEFT,35,0,1560,1595,RIGHT,WRONG DIRECTION!,169.0,25.9,5.889,0.0067
20:30:04,SINGLE,PLAYER 1,LEFT,1023,2914,233,4170,LEFT,VALID,69.0,11.5,32.021,0.0071
00:15:54,SINGLE,PLAYER 1,UP,1213,16,0,1229,UP,VALID,60.0,9.7,6.66,0.0081
00:15:55,SINGLE,PLAYER 1,UP,775,0,59,834,UP,VALID,153.0,28.1,7.435,0.0073
00:16:01,SINGLE,PLAYER 1,RIGHT,635,2286,0,2921,LEFT,WRONG DIRECTION!,116.0,24.7,15.585,0.0065
00:16:02,SINGLE,PLAYER 1,RIGHT,0,3767,0,3767,LEFT,WRONG DIRECTION!,160.0,13.0,8.142,0.0052
00:16:03,SINGLE,PLAYER 1,RIGHT,136,2888,0,3024,LEFT,WRONG DIRECTION!,95.0,20.3,13.106,0.0055
00:16:06,SINGLE,PLAYER 1,RIGHT,362,0,652,1014,RIGHT,VALID,125.0,30.8,19.655,0.0065
00:16:07,SINGLE,PLAYER 1,RIGHT,0,317,102,419,LEFT,WRONG DIRECTION!,94.0,24.9,15.72,0.0074
00:16:08,SINGLE,PLAYER 1,RIGHT,0,0,1405,1405,RIGHT,VALID,140.0,9.1,2.857,0.0068
00:16:15,SINGLE,PLAYER 1,RIGHT,227,357,686,1270,RIGHT,VALID,109.0,4.6,10.728,0.0073
00:16:20,SINGLE,PLAYER 1,LEFT,1043,1,2545,3589,RIGHT,WRONG DIRECTION!,146.0,29.3,13.535,0.0083
00:16:21,SINGLE,PLAYER 1,LEFT,15,3324,473,3812,LEFT,VALID,137.0,19.7,11.802,0.0074
00:23:25,MULTI,PLAYER 1,UP,1057,19,1370,2446,RIGHT,WRONG DIRECTION!,139.0,29.1,8.144,0.0038
00:23:26,MULTI,PLAYER 1,UP,33,2061,1,2095,LEFT,WRONG DIRECTION!,89.0,9.8,4.461,0.0062
00:23:26,MULTI,PLAYER 2,RIGHT,1226,0,389,1615,UP,WRONG DIRECTION!,120.0,4.8,11.668,0.0068
00:23:27,MULTI,PLAYER 2,RIGHT,0,2970,0,2970,LEFT,WRONG DIRECTION!,135.0,7.0,5.402,0.0062
00:23:27,MULTI,PLAYER 1,UP,303,4766,0,5069,LEFT,WRONG DIRECTION!,127.0,24.6,10.319,0.0081
00:23:27,MULTI,PLAYER 2,RIGHT,0,1095,0,1095,LEFT,WRONG DIRECTION!,91.0,6.2,12.669,0.008
00:23:28,MULTI,PLAYER 2,RIGHT,129,0,727,856,RIGHT,VALID,173.0,9.3,7.232,0.0079
00:23:28,MULTI,PLAYER 1,UP,651,0,3139,3790,RIGHT,WRONG DIRECTION!,142.0,14.0,9.097,0.0076
00:23:29,MULTI,PLAYER 1,UP,956,169,780,1905,UP,VALID,108.0,28.7,10.364,0.0086
00:23:30,MULTI,PLAYER 1,LEFT,1173,364,0,1537,UP,WRONG DIRECTION!,111.0,24.2,6.871,0.0074
00:23:31,MULTI,PLAYER 1,LEFT,622,85,784,1491,RIGHT,WRONG DIRECTION!,121.0,17.6,9.4,0.0078
00:23:32,MULTI,PLAYER 1,LEFT,38,449,619,1106,RIGHT,WRONG DIRECTION!,125.0,13.5,2.614,0.0062
00:23:32,MULTI,PLAYER 1,LEFT,655,2157,1877,4689,LEFT,VALID,135.0,26.6,5.185,0.0082
00:23:34,MULTI,PLAYER 1,RIGHT,23,0,400,423,RIGHT,VALID,123.0,3.4,5.846,0.0074
00:23:34,MULTI,PLAYER 1,UP,664,2817,449,3930,LEFT,WRONG DIRECTION!,163.0,59.4,12.929,0.0066
00:23:35,MULTI,PLAYER 1,UP,7,0,518,525,RIGHT,WRONG DIRECTION!,146.0,7.8,12.181,0.0077
00:23:35,MULTI,PLAYER 1,UP,689,2,16,707,UP,VALID,123.0,19.9,2.676,0.0068
00:23:36,MULTI,PLAYER 1,UP,0,8,1301,1309,RIGHT,WRONG DIRECTION!,112.0,17.2,5.286,0.0065
00:23:36,MULTI,PLAYER 1,UP,281,53,304,638,RIGHT,WRONG DIRECTION!,145.0,61.2,5.61,0.0043
00:23:37,MULTI,PLAYER 1,UP,1137,169,1754,3060,RIGHT,WRONG DIRECTION!,145.0,28.0,2.463,0.0064
00:23:38,MULTI,PLAYER 1,UP,2088,2,201,2291,UP,VALID,95.0,22.9,1.225,0.0073
00:23:39,MULTI,PLAYER 1,UP,0,0,535,535,RIGHT,WRONG DIRECTION!,92.0,5.6,7.855,0.0066
00:23:40,MULTI,PLAYER 1,UP,16,466,303,785,LEFT,WRONG DIRECTION!,158.0,37.3,8.584,0.0082
00:23:42,MULTI,PLAYER 1,UP,1313,211,1989,3513,RIGHT,WRONG DIRECTION!,134.0,54.7,10.449,0.0069
00:23:43,MULTI,PLAYER 1,UP,1455,408,414,2277,UP,VALID,175.0,34.7,8.015,0.0075
00:23:44,MULTI,PLAYER 1,UP,908,0,337,1245,UP,VALID,101.0,30.2,1.802,0.006
00:23:45,MULTI,PLAYER 2,RIGHT,1502,348,55,1905,UP,WRONG DIRECTION!,111.0,12.2,4.152,0.0078
00:23:46,MULTI,PLAYER 1,LEFT,200,77,538,815,RIGHT,WRONG DIRECTION!,119.0,31.2,10.246,0.0065
00:23:47,MULTI,PLAYER 1,LEFT,0,894,0,894,LEFT,VALID,144.0,15.7,19.155,0.008
00:23:48,MULTI,PLAYER 1,UP,124,50,2056,2230,RIGHT,WRONG DIRECTION!,148.0,73.0,4.368,0.0057
00:23:51,MULTI,PLAYER 1,UP,494,4,1271,1769,RIGHT,WRONG DIRECTION!,128.0,46.4,3.258,0.005
00:23:51,MULTI,PLAYER 1,UP,211,2105,0,2316,LEFT,WRONG DIRECTION!,167.0,14.9,8.362,0.0083
00:23:52,MULTI,PLAYER 1,UP,0,623,0,623,LEFT,WRONG DIRECTION!,167.0,21.4,2.123,0.0075
00:23:54,MULTI,PLAYER 1,UP,666,171,379,1216,UP,VALID,148.0,47.3,3.127,0.0075
00:31:10,MULTI,PLAYER 1,UP,0,0,857,857,RIGHT,WRONG DIRECTION!,84.0,10.6,6.037,0.0062
00:31:12,MULTI,PLAYER 1,UP,0,457,0,457,LEFT,WRONG DIRECTION!,148.0,10.7,3.193,0.0068
00:31:12,MULTI,PLAYER 1,UP,406,35,13,454,UP,VALID,112.0,24.9,5.695,0.0064
00:31:15,MULTI,PLAYER 1,RIGHT,119,0,708,827,RIGHT,VALID,134.0,9.9,2.191,0.0062
00:31:19,MULTI,PLAYER 2,RIGHT,0,0,1175,1175,RIGHT,VALID,140.0,11.3,4.239,0.0058
00:31:19,MULTI,PLAYER 2,LEFT,0,42,525,567,RIGHT,WRONG DIRECTION!,114.0,25.0,10.502,0.0067
00:31:22,MULTI,PLAYER 2,LEFT,71,1453,0,1524,LEFT,VALID,103.0,23.1,4.786,0.0067
00:31:24,MULTI,PLAYER 1,UP,1450,543,289,2282,UP,VALID,159.0,30.5,10.016,0.006
00:31:25,MULTI,PLAYER 1,RIGHT,7,0,4022,4029,RIGHT,VALID,177.0,16.6,18.871,0.0072
00:31:27,MULTI,PLAYER 1,LEFT,103,805,260,1168,LEFT,VALID,76.0,9.0,42.075,0.0079
00:31:28,MULTI,PLAYER 2,LEFT,6604,326,0,6930,UP,WRONG DIRECTION!,113.0,16.3,41.288,0.0073
00:31:29,MULTI,PLAYER 1,RIGHT,0,92,3901,3993,RIGHT,VALID,122.0,12.6,10.429,0.0071
00:31:30,MULTI,PLAYER 2,LEFT,1716,264,0,1980,UP,WRONG DIRECTION!,121.0,6.6,8.296,0.0074
00:31:31,MULTI,PLAYER 2,LEFT,139,915,0,1054,LEFT,VALID,138.0,7.8,8.413,0.0072
00:31:32,MULTI,PLAYER 2,RIGHT,127,0,727,854,RIGHT,VALID,103.0,30.9,2.819,0.0084
00:31:34,MULTI,PLAYER 1,LEFT,236,244,647,1127,RIGHT,WRONG DIRECTION!,87.0,22.3,7.259,0.0077
00:31:35,MULTI,PLAYER 1,LEFT,212,197,1225,1634,RIGHT,WRONG DIRECTION!,134.0,35.2,2.443,0.0082
00:31:36,MULTI,PLAYER 1,LEFT,643,118,3,764,UP,WRONG DIRECTION!,115.0,33.3,8.565,0.0092
00:31:37,MULTI,PLAYER 1,LEFT,0,2372,0,2372,LEFT,VALID,106.0,22.6,1.844,0.0083
00:31:38,MULTI,PLAYER 1,LEFT,0,3360,60,3420,LEFT,VALID,165.0,17.0,2.806,0.0086
00:31:39,MULTI,PLAYER 1,RIGHT,0,0,3761,3761,RIGHT,VALID,182.0,17.0,5.139,0.0074
00:31:40,MULTI,PLAYER 1,UP,45,5182,0,5227,LEFT,WRONG DIRECTION!,110.0,13.4,8.873,0.0069
00:31:40,MULTI,PLAYER 2,LEFT,0,4035,0,4035,LEFT,VALID,152.0,10.4,10.591,0.0076
00:31:41,MULTI,PLAYER 1,UP,367,31,356,754,UP,VALID,71.0,12.7,4.297,0.0066
00:31:41,MULTI,PLAYER 1,RIGHT,65,275,195,535,LEFT,WRONG DIRECTION!,117.0,18.2,6.455,0.008
00:31:45,MULTI,PLAYER 1,RIGHT,59,267,2143,2469,RIGHT,VALID,157.0,18.7,11.856,0.0066
00:31:46,MULTI,PLAYER 2,LEFT,1609,68,0,1677,UP,WRONG DIRECTION!,125.0,19.0,2.022,0.008
00:31:46,MULTI,PLAYER 1,LEFT,163,0,373,536,RIGHT,WRONG DIRECTION!,137.0,23.2,1.58,0.0065
00:31:48,MULTI,PLAYER 2,LEFT,645,6,17,668,UP,WRONG DIRECTION!,118.0,11.7,6.568,0.0063
00:31:48,MULTI,PLAYER 1,LEFT,335,620,15,970,LEFT,VALID,151.0,25.1,2.2,0.0066
00:32:02,SINGLE,PLAYER 1,LEFT,95,1270,0,1365,LEFT,VALID,131.0,20.3,5.608,0.0063
00:32:03,SINGLE,PLAYER 1,UP,0,2177,0,2177,LEFT,WRONG DIRECTION!,179.0,17.1,5.017,0.0071
00:32:04,SINGLE,PLAYER 1,UP,670,104,40,814,UP,VALID,122.0,25.8,1.451,0.0058
00:32:08,SINGLE,PLAYER 1,LEFT,39,1711,0,1750,LEFT,VALID,140.0,11.6,5.923,0.0062
00:32:11,SINGLE,PLAYER 1,RIGHT,101,4795,1345,6241,LEFT,WRONG DIRECTION!,146.0,28.2,10.473,0.0067
00:32:12,SINGLE,PLAYER 1,RIGHT,2,0,1512,1514,RIGHT,VALID,140.0,5.6,4.246,0.005
00:32:15,SINGLE,PLAYER 1,RIGHT,492,436,5335,6263,RIGHT,VALID,156.0,52.9,9.512,0.0063
00:32:16,SINGLE,PLAYER 1,RIGHT,0,3700,0,3700,LEFT,WRONG DIRECTION!,193.0,25.2,1.985,0.0064
00:32:17,SINGLE,PLAYER 1,RIGHT,0,2000,70,2070,LEFT,WRONG DIRECTION!,131.0,19.8,6.652,0.007
00:32:18,SINGLE,PLAYER 1,RIGHT,0,183,1469,1652,RIGHT,VALID,179.0,36.6,5.824,0.0081
00:32:21,SINGLE,PLAYER 1,UP,0,0,639,639,RIGHT,WRONG DIRECTION!,141.0,28.8,1.657,0.0062
00:32:23,SINGLE,PLAYER 1,UP,0,2488,0,2488,LEFT,WRONG DIRECTION!,137.0,10.1,6.7,0.006
00:32:29,SINGLE,PLAYER 1,UP,72,1494,563,2129,LEFT,WRONG DIRECTION!,114.0,31.5,2.544,0.0052
72 changes: 37 additions & 35 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@

#== Initialization ==#
fullscreen = False
cv.namedWindow('Thermal Punch', cv.WINDOW_NORMAL)
cap = cv.VideoCapture(0)
time.sleep(1)
cv.namedWindow('Thermal Punch', cv.WINDOW_NORMAL) # Craate resizable window
cap = cv.VideoCapture(0) # Use primary webcam
time.sleep(1) # Give time for camera to adjust auto exposure and white balance before start capturing frames

# Create Audio System
sound = SoundManager()
sound.play_music('menu')
sound.play_music('menu') # Menu music starts immediatey

# Create UI Screens
menu_screen = MainMenuScreen()
Expand Down Expand Up @@ -45,9 +45,10 @@
last_p_press = 0
show_heatmap = True

ret, frame = cap.read()
if not ret: exit()
frame = cv.resize(frame, (config.WIDTH, config.HEIGHT))
# Capture base frame
ret, frame = cap.read() # Read the frame from video capture object. Ret is bool, to see if the frame was read successfully
if not ret: exit() # Safely shutdown program if webcam failed
frame = cv.resize(frame, (config.WIDTH, config.HEIGHT))
frame = cv.flip(frame, 1)
prev_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)

Expand All @@ -59,20 +60,20 @@

key = cv.waitKey(1) & 0xFF

# 1. Prepare Frame
frame = cv.flip(frame, 1)
frame = cv.resize (frame, (config.WIDTH, config.HEIGHT))
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
# 1. Prepare First Frame
frame = cv.flip(frame, 1) # Convert frame horizontally so seems like a mirror
frame = cv.resize (frame, (config.WIDTH, config.HEIGHT)) # Resize to standard resolution to ensure math calculations take exact same amount of time on any computer
gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) # Converts BGR frame into greyscale, strips out uncessary data

# 2. Image Processing
mask = pipeline.process_frame(gray)
mask = pipeline.process_frame(gray) # Pass clean grayscale frame into vision pipeline. Returns a binary mask of pure black and white pixel, to isolate moving objects from background

# Visualization Base
visual_motion = cv.absdiff(gray, prev_gray) # Keep absdiff purely for the visual heatmap as it creates cool fading thermal effect
heatmap = cv.applyColorMap(visual_motion * 5, cv.COLORMAP_JET)
visual_motion = cv.absdiff(gray, prev_gray) # Calculate diff between current and prev frame. If it hasn't change, then black (0), else higher num (closer to255)
heatmap = cv.applyColorMap(visual_motion * 5, cv.COLORMAP_JET) # Apply grayscal color to JET color, visual_motion * 5 to amplify weak differences

if config.WARMUP_FRAMES > 0:
config.WARMUP_FRAMES -= 1
config.WARMUP_FRAMES -= 1 # 60 sec for the camera to adjust auto exposure and white balance, also to let players see the calibration screen and stand still to let MOG2 learn the background better, reducing false positives in the actual game
# Show calibration text
overlay = heatmap.copy()
cv.rectangle(overlay, (0,0), (config.WIDTH, config.HEIGHT), (0,0,0), -1)
Expand All @@ -87,18 +88,19 @@
break
continue

# State architecture to switch between different behaviour in the game # A total of 5 states: Menu(0), Countdown(1), Playing(2), GameOver(3), Paused(4)
#== State 0: Menu ==#
if game_state == config.STATE_MENU:
msg_to_show = info_message if time.time() < info_timer else ""

# Draw Menu
menu_screen.draw(heatmap, msg_to_show)

if time.time() > menu_lock_time:
if time.time() > menu_lock_time: # Avoid direct triggering of menu after exiting a game
# Check Input
action = menu_screen.check_input(mask)

if key == ord('1'): action = "SINGLE"
if key == ord('1'): action = "SINGLE" # Two options for selecting menu, either punching the menu, or use shortcut key
elif key == ord('2'): action = "MULTI"
else:
action = None
Expand All @@ -117,7 +119,7 @@
start_time = time.time()

#== State 1: Countdown ==#
elif game_state == config.STATE_COUNTDOWN:
elif game_state == config.STATE_COUNTDOWN: # Countdown before starting a fight
current_mode.draw_ui_only(heatmap)

# Calculate which text to show
Expand All @@ -142,12 +144,12 @@
elif game_state == config.STATE_PLAYING:
current_t = time.time() - total_paused_time

# Calculate what percentage of the screen is currently moving
mog_density = (cv.countNonZero(mask) / (config.WIDTH * config.HEIGHT)) * 100
# Calculate percentage of the screen is currently moving
mog_density = (cv.countNonZero(mask) / (config.WIDTH * config.HEIGHT)) * 100 # Use it to adjust boss difficulty in 1P (Adaptive Difficulty)

mode_name = "SINGLE" if isinstance(current_mode, SingleplayerMode) else "MULTI"

# 1. Update Match Logic
# 1. Update Match Logic, Organize variable to pass
vision = {'mask': mask,
'gray': gray,
'prev_gray': prev_gray,
Expand All @@ -170,7 +172,7 @@
sound.play_music('menu')

#== State 4: Paused ==#
elif game_state == config.STATE_PAUSED:
elif game_state == config.STATE_PAUSED: # When user pause a game
# Calculate exact time paused
frozen_t = pause_start_time - total_paused_time

Expand All @@ -179,7 +181,7 @@
# Draw Pause UI over it
pause_screen.draw(heatmap)

if time.time() - pause_start_time > 1.0:
if time.time() - pause_start_time > 1.0: # Give user some time to move away from the pause menu

# 1. Check physical Button Punches
action = pause_screen.check_input(mask)
Expand Down Expand Up @@ -263,41 +265,41 @@
debugger.draw_vision_pipeline(display_frame, frame, gray, mask, visual_motion)
debugger.draw_thermal_debug(display_frame, visual_motion)

cv.imshow('Thermal Punch', display_frame)
prev_gray = gray
cv.imshow('Thermal Punch', display_frame) # Show window
prev_gray = gray # Save current frame

# Key Controls
if key == ord('q'):
if key == ord('q'): # quit
break
elif key == ord('h'):
elif key == ord('h'): # heatmap
show_heatmap = not show_heatmap
elif key == ord('f'):
elif key == ord('f'): # fullscreen
fullscreen = not fullscreen
if fullscreen:
cv.setWindowProperty('Thermal Punch', cv.WND_PROP_FULLSCREEN, cv.WINDOW_FULLSCREEN)
else:
cv.setWindowProperty('Thermal Punch', cv.WND_PROP_FULLSCREEN, cv.WINDOW_NORMAL)
elif key == ord('d'):
elif key == ord('d'): # debug mode
config.DEBUG_MODE = not config.DEBUG_MODE
elif key == ord('s'):
elif key == ord('s'): # screenshot
if not os.path.exists("screenshots"):
os.makedirs("screenshots")
# Save Images
timestamp = time.strftime("%H%M%S")
heatmap_path = os.path.join("screenshots", f"report_heatmap_{timestamp}.png")
mask_path = os.path.join("screenshots", f"report_mask_{timestamp}.png")
heatmap_path = os.path.join("screenshots", f"report_heatmap_{timestamp}.png") # JET color image
mask_path = os.path.join("screenshots", f"report_mask_{timestamp}.png") # MOG image

cv.imwrite(heatmap_path, heatmap)
cv.imwrite(heatmap_path, heatmap) # Save image to file
cv.imwrite(mask_path, mask)

print(f"Screenshots successfully saved! ({heatmap_path})")
sound.play_sfx("screenshot")
elif key == ord('c'):
elif key == ord('c'): # calibrate, in case some bug happen or something
# Reset Camera Memory Instantly (MOG2 Background)
pipeline = VisionPipeline()
config.WARMUP_FRAMES = 30 # Trigger calibration screen to absorb the MOG2 white flash!
print("Camera background memory recalibrated!")
elif key == ord('p') or key == 27: # ESC key
elif key == ord('p') or key == 27: # ESC key # pause
if time.time() - last_p_press > 0.5: # Prevent rapid toggling if key is held down
last_p_press = time.time() # Reset Cooldown
if game_state == config.STATE_PLAYING:
Expand Down
3 changes: 3 additions & 0 deletions modes.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def update(self, vision, current_time):
self.p1.update_stamina(mask)
self.p2.update_stamina(mask)

# Calculate player total motion based on their side
p1_total_motion = cv.countNonZero(mask[:, 0:config.MID_X])
p2_total_motion = cv.countNonZero(mask[:, config.MID_X:config.WIDTH])

Expand Down Expand Up @@ -182,6 +183,7 @@ def update(self, mask, player, current_time, sound, floating_texts):
# Heat targeting
zone_w = config.WIDTH // 3
zone_motions = []
# Divide screen into 3 zones, calculate motion density of each
for i in range(3):
x_start = i * zone_w
zone_mask = mask[:, x_start:x_start + zone_w]
Expand Down Expand Up @@ -398,6 +400,7 @@ def update(self, vision, current_time):
elif total_motion > config.ADAPTIVE_MED_MOTION: speed_factor = config.ADAPTIVE_SPEED_MED
else: speed_factor = config.ADAPTIVE_SPEED_LOW

# Shrink timestampe user are schedule to attack
if hasattr(self.boss, 'action_timer'):
self.boss.action_timer -= (0.01 * speed_factor)

Expand Down
Loading
Loading