Coverage for topdownengine/controls.py: 68%

40 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-24 02:27 +0000

1# Copyright (c) 2026 Shaurya Sharma 

2# SPDX-License-Identifier: MIT 

3 

4import pygame as pg 

5from pygame._sdl2.controller import Controller 

6 

7class KeyboardInputManager: 

8 """Acts as a keyboard and mouse input reciever. 

9  

10 Attributes: 

11 keybinds (dict[str,int|str]): Stores all keybind data. 

12 non_hold_inputs (list[str]): List of which inputs can't be held. 

13 keys (pg.key.ScancodeWrapper): Keys currently pressed. 

14 just_pressed_keys (pg.key.ScancodeWrapper): Keys pressed in the current frame. 

15 """ 

16 

17 def __init__(self) -> None: 

18 "Initialize KeyboardInputManager." 

19 self.keybinds = { 

20 "Move Right": pg.K_d, 

21 "Move Left": pg.K_a, 

22 "Move Up": pg.K_w, 

23 "Move Down": pg.K_s, 

24 "Jump": pg.K_SPACE 

25 } 

26 self.non_hold_inputs = [] 

27 

28 def serialize(self) -> dict: 

29 "Serialize the keybinds." 

30 return { 

31 k: pg.key.name(v) if type(v) == int else v 

32 for k, v in self.keybinds.items() 

33 } 

34 

35 def deserialize(self, data: dict) -> None: 

36 "Deserialize the keybinds into this object." 

37 for k, v in data.items(): 

38 try: 

39 self.keybinds[k] = pg.key.key_code(v) 

40 except ValueError: 

41 self.keybinds[k] = v 

42 

43 def get_input(self) -> list[str]: 

44 """Returns all inputs to execute logic for. MUST be called after pg.event.get(). 

45  

46 Returns: 

47 list[str]: The inputs to execute logic for. 

48 """ 

49 self.keys = pg.key.get_pressed() 

50 self.just_pressed_keys = pg.key.get_just_pressed() 

51 inputs = [] 

52 

53 for keybind in self.keybinds: 

54 if self.keybinds[keybind] == "Button 1": 

55 if pg.mouse.get_just_pressed()[0]: 

56 inputs.append(keybind) 

57 elif self.keybinds[keybind] == "Button 3": 

58 if pg.mouse.get_just_pressed()[2]: 

59 inputs.append(keybind) 

60 else: 

61 if self.keys[self.keybinds[keybind]]: 

62 if keybind in self.non_hold_inputs: 

63 if self.just_pressed_keys[self.keybinds[keybind]]: 

64 inputs.append(keybind) 

65 else: 

66 inputs.append(keybind) 

67 

68 return inputs 

69 

70# TODO: Add controller support 

71# class ControllerInputManager: 

72# def __init__(self): 

73# self.controller = Controller(0) 

74 

75# self.keybinds = { 

76# # "Interact": pg.K_e, 

77# # "Inventory": pg.K_i, 

78# "Use Item": pg.CONTROLLER_BUTTON_RIGHTSHOULDER, 

79# "Use Item Special": 5, 

80# # "Use Ability 1": pg.K_v, 

81# # "Use Ability 2": pg.K_b, 

82# } 

83# self.non_hold_inputs = ["Interact", "Inventory"] 

84 

85# def get_input(self) -> list[str]: 

86# inputs = [] 

87 

88# for keybind in self.keybinds: 

89# print(self.controller.get_button(self.keybinds[keybind])) 

90# if self.controller.get_button(self.keybinds[keybind]):  

91# if keybind in self.non_hold_inputs and False: 

92# if self.just_pressed_keys[self.keybinds[keybind]]: 

93# inputs.append(keybind) 

94# else: 

95# inputs.append(keybind)  

96# print("hello") 

97# return inputs 

98 

99class NoKeysPressed: 

100 "Emulates a pygame ScancodeWrapper where no keys are pressed." 

101 def __getitem__(self, key: int) -> bool: 

102 return False 

103 

104class MoreKeysPressed: 

105 """Emulates a pygame ScancodeWrapper where given keys are always pressed. 

106  

107 Attributes: 

108 wrapper: The base wrapper that keys were added to. 

109 pressed_keys: The keys added to the wrapper. 

110 """ 

111 

112 def __init__(self, wrapper: pg.key.ScancodeWrapper, pressed_keys: set[int]) -> None: 

113 """Initialize the MoreKeysPressed object. 

114  

115 Args: 

116 wrapper: The wrapper to add keys to. 

117 pressed_keys: The keys to add to the wrapper. 

118 """ 

119 self.wrapper = wrapper 

120 self.pressed_keys = pressed_keys 

121 

122 def __getitem__(self, key: int) -> bool: 

123 return self.wrapper[key] or key in self.pressed_keys