pyscratch-pysc 1.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. assets/bullet_hell/enemy.py +130 -0
  2. assets/bullet_hell/enemy_bullets.py +230 -0
  3. assets/bullet_hell/main.py +11 -0
  4. assets/bullet_hell/old_verisons/bullet_hell.py +379 -0
  5. assets/bullet_hell/old_verisons/enemy.py +226 -0
  6. assets/bullet_hell/old_verisons/game_start.py +6 -0
  7. assets/bullet_hell/old_verisons/main.py +50 -0
  8. assets/bullet_hell/old_verisons/player.py +76 -0
  9. assets/bullet_hell/player.py +89 -0
  10. assets/bullet_hell/player_bullets.py +34 -0
  11. assets/bullet_hell/setting.py +33 -0
  12. examples/animated_sprite/main.py +7 -0
  13. examples/animated_sprite/my_sprite.py +79 -0
  14. examples/bullet_hell/enemy.py +152 -0
  15. examples/bullet_hell/enemy_bullet.py +88 -0
  16. examples/bullet_hell/main.py +17 -0
  17. examples/bullet_hell/player.py +39 -0
  18. examples/bullet_hell/player_bullet.py +31 -0
  19. examples/doodle_jump/main.py +9 -0
  20. examples/doodle_jump/platforms.py +51 -0
  21. examples/doodle_jump/player.py +52 -0
  22. examples/fish/assets/bullet_hell/enemy.py +130 -0
  23. examples/fish/assets/bullet_hell/enemy_bullets.py +230 -0
  24. examples/fish/assets/bullet_hell/main.py +11 -0
  25. examples/fish/assets/bullet_hell/old_verisons/bullet_hell.py +379 -0
  26. examples/fish/assets/bullet_hell/old_verisons/enemy.py +226 -0
  27. examples/fish/assets/bullet_hell/old_verisons/game_start.py +6 -0
  28. examples/fish/assets/bullet_hell/old_verisons/main.py +50 -0
  29. examples/fish/assets/bullet_hell/old_verisons/player.py +76 -0
  30. examples/fish/assets/bullet_hell/player.py +89 -0
  31. examples/fish/assets/bullet_hell/player_bullets.py +34 -0
  32. examples/fish/assets/bullet_hell/setting.py +33 -0
  33. examples/fish/fish.py +67 -0
  34. examples/fish/main.py +4 -0
  35. examples/getting-started/step 1 - create a sprite/main.py +11 -0
  36. examples/getting-started/step 1 - create a sprite/player.py +5 -0
  37. examples/getting-started/step 2 - control a sprite/main.py +11 -0
  38. examples/getting-started/step 2 - control a sprite/player.py +42 -0
  39. examples/getting-started/step 3 - backdrops/main.py +17 -0
  40. examples/getting-started/step 3 - backdrops/player.py +33 -0
  41. examples/getting-started/step 4 - clone a sprite/enemy.py +53 -0
  42. examples/getting-started/step 4 - clone a sprite/main.py +17 -0
  43. examples/getting-started/step 4 - clone a sprite/player.py +32 -0
  44. examples/getting-started/step 4 - clone a sprite (simple)/enemy.py +42 -0
  45. examples/getting-started/step 4 - clone a sprite (simple)/main.py +17 -0
  46. examples/getting-started/step 4 - clone a sprite (simple)/player.py +32 -0
  47. examples/getting-started/step 5 - local variables/enemy.py +52 -0
  48. examples/getting-started/step 5 - local variables/main.py +17 -0
  49. examples/getting-started/step 5 - local variables/player.py +49 -0
  50. examples/getting-started/step 6 - shared variables/enemy.py +64 -0
  51. examples/getting-started/step 6 - shared variables/main.py +17 -0
  52. examples/getting-started/step 6 - shared variables/player.py +80 -0
  53. examples/getting-started/step 7 - Referencing other sprites/enemy.py +83 -0
  54. examples/getting-started/step 7 - Referencing other sprites/hearts.py +39 -0
  55. examples/getting-started/step 7 - Referencing other sprites/main.py +23 -0
  56. examples/getting-started/step 7 - Referencing other sprites/player.py +59 -0
  57. examples/getting-started/step 8 - sprite variables/enemy.py +98 -0
  58. examples/getting-started/step 8 - sprite variables/hearts.py +39 -0
  59. examples/getting-started/step 8 - sprite variables/main.py +22 -0
  60. examples/getting-started/step 8 - sprite variables/player.py +63 -0
  61. examples/getting-started/step 9 - messages/enemy.py +98 -0
  62. examples/getting-started/step 9 - messages/hearts.py +39 -0
  63. examples/getting-started/step 9 - messages/main.py +23 -0
  64. examples/getting-started/step 9 - messages/player.py +78 -0
  65. examples/perspective_background/main.py +14 -0
  66. examples/perspective_background/player.py +52 -0
  67. examples/perspective_background/trees.py +39 -0
  68. examples/simple_pong/ball.py +72 -0
  69. examples/simple_pong/left_paddle.py +36 -0
  70. examples/simple_pong/main.py +21 -0
  71. examples/simple_pong/right_paddle.py +37 -0
  72. examples/simple_pong/score_display.py +54 -0
  73. pyscratch/__init__.py +48 -0
  74. pyscratch/event.py +263 -0
  75. pyscratch/game_module.py +1589 -0
  76. pyscratch/helper.py +561 -0
  77. pyscratch/sprite.py +1920 -0
  78. pyscratch/tools/sprite_preview/left_panel/frame_preview_card.py +238 -0
  79. pyscratch/tools/sprite_preview/left_panel/frame_preview_panel.py +42 -0
  80. pyscratch/tools/sprite_preview/main.py +18 -0
  81. pyscratch/tools/sprite_preview/main_panel/animation_display.py +77 -0
  82. pyscratch/tools/sprite_preview/main_panel/frame_bin.py +33 -0
  83. pyscratch/tools/sprite_preview/main_panel/play_edit_ui.py +64 -0
  84. pyscratch/tools/sprite_preview/main_panel/set_as_sprite_folder.py +22 -0
  85. pyscratch/tools/sprite_preview/main_panel/sprite_edit_ui.py +174 -0
  86. pyscratch/tools/sprite_preview/main_panel/warning_message.py +25 -0
  87. pyscratch/tools/sprite_preview/right_panel/back_button.py +35 -0
  88. pyscratch/tools/sprite_preview/right_panel/cut_button.py +32 -0
  89. pyscratch/tools/sprite_preview/right_panel/cut_parameter_fitting.py +152 -0
  90. pyscratch/tools/sprite_preview/right_panel/cut_parameters.py +42 -0
  91. pyscratch/tools/sprite_preview/right_panel/file_display.py +84 -0
  92. pyscratch/tools/sprite_preview/right_panel/file_display_area.py +57 -0
  93. pyscratch/tools/sprite_preview/right_panel/spritesheet_view.py +262 -0
  94. pyscratch/tools/sprite_preview/right_panel/ss_select_corner.py +208 -0
  95. pyscratch/tools/sprite_preview/settings.py +14 -0
  96. pyscratch/tools/sprite_preview/utils/input_box.py +235 -0
  97. pyscratch/tools/sprite_preview/utils/render_wrapped_file_name.py +86 -0
  98. pyscratch_pysc-1.0.3.dist-info/METADATA +37 -0
  99. pyscratch_pysc-1.0.3.dist-info/RECORD +101 -0
  100. pyscratch_pysc-1.0.3.dist-info/WHEEL +5 -0
  101. pyscratch_pysc-1.0.3.dist-info/top_level.txt +3 -0
@@ -0,0 +1,208 @@
1
+
2
+ import numpy as np
3
+ import pyscratch as pysc
4
+ game = pysc.game
5
+
6
+
7
+ colour = 0, 0, 0, 50
8
+
9
+ cir0 = pysc.create_circle_sprite(colour, 30)
10
+ game['c0'] = cir0
11
+
12
+ cir1 = pysc.create_circle_sprite(colour, 30)
13
+ game['c1'] = cir1
14
+
15
+ # event game start: initial setting
16
+ def on_start():
17
+
18
+ cir0.set_draggable(True)
19
+ cir1.set_draggable(True)
20
+
21
+ # pysc.game.bring_to_front(cir)
22
+ # pysc.game.bring_to_front(cir2)
23
+
24
+ cir0.x = game['ss_view_topleft'][0] + 1
25
+ cir0.y = game['ss_view_topleft'][1] + 1
26
+
27
+ cir1.x = game['ss_view_buttom_right'][0]-1
28
+ cir1.y = game['ss_view_buttom_right'][1]-1
29
+
30
+
31
+ cir0.oob_limit=np.inf
32
+ cir1.oob_limit=np.inf
33
+
34
+ game.when_game_start([cir0, cir1]).add_handler(on_start)
35
+
36
+
37
+
38
+ # useful functions
39
+ def value_if_none(v, default):
40
+ if v is None:
41
+ return default
42
+ return v
43
+
44
+ def value_if_not(v, default):
45
+ if not v:
46
+ return default
47
+ return v
48
+
49
+
50
+
51
+
52
+ # event game start: set the cutting offset and the frame size if the sprite is dragged
53
+ def on_start2():
54
+ while True:
55
+ yield 1/30
56
+ ss_sprite: pysc.Sprite = game['ss_sprite']
57
+ if not ss_sprite: continue
58
+ game.bring_to_front(cir0)
59
+ game.bring_to_front(cir1)
60
+
61
+ mouse_down = pysc.get_mouse_presses()[0]
62
+ if not mouse_down:
63
+ try:
64
+ cir0.x = game['offset_x']*ss_sprite.scale_factor + ss_sprite.rect.left
65
+ cir1.x = game['limit_x']*ss_sprite.scale_factor + ss_sprite.rect.left
66
+
67
+ cir0.y = game['offset_y']*ss_sprite.scale_factor + ss_sprite.rect.top
68
+ cir1.y = game['limit_y']*ss_sprite.scale_factor + ss_sprite.rect.top
69
+ except:
70
+ pass
71
+ continue
72
+ if not (cir0.is_touching_mouse() or cir1.is_touching_mouse()): continue
73
+
74
+
75
+
76
+ # top left of the selected rect
77
+ cx0 = min(cir0.x, cir1.x)
78
+ cy0 = min(cir0.y, cir1.y)
79
+
80
+ # bottom right of the selected rect
81
+ cx1 = max(cir0.x, cir1.x)
82
+ cy1 = max(cir0.y, cir1.y)
83
+
84
+ # top left of the sprite sheet
85
+ x0, y0 = ss_sprite.rect.topleft
86
+
87
+ img_w, img_h = ss_sprite['original_width'], ss_sprite['original_height']
88
+
89
+ # scale factor
90
+ scale_factor = ss_sprite.scale_factor
91
+
92
+ # RC
93
+ n_col = value_if_not(game['n_col'], 1)
94
+ n_row = value_if_not(game['n_row'], 1)
95
+
96
+ # O
97
+ game['offset_x'] = Ox = max(np.floor((cx0-x0)/scale_factor), 0)
98
+ game['offset_y'] = Oy = max(np.floor((cy0-y0)/scale_factor), 0)
99
+
100
+ # L
101
+ game['limit_x'] = Lx = min(np.ceil((cx1-x0)/scale_factor), img_w-1)
102
+ game['limit_y'] = Ly = min(np.ceil((cy1-y0)/scale_factor), img_h-1)
103
+
104
+ # update the size
105
+ game['size_x'] = Lx - Ox + 1
106
+ game['size_y'] = Ly - Oy + 1
107
+
108
+ # pix
109
+ game['pixel_x'] = np.floor(game['size_x']/n_col)
110
+ game['pixel_y'] = np.floor(game['size_y']/n_row)
111
+
112
+
113
+
114
+
115
+
116
+ pysc.game.when_game_start([cir0, cir1]).add_handler(on_start2)
117
+
118
+
119
+ def on_offset_x_change(data):
120
+
121
+ if not (size_x := game['size_x']): return
122
+ if not (offset_x := game['offset_x']):
123
+ game['offset_x'] = 0
124
+ offset_x = 0
125
+
126
+ ss_sprite: pysc.Sprite = game['ss_sprite']
127
+ if not ss_sprite: return
128
+
129
+ game['limit_x'] = min(size_x+offset_x-1, ss_sprite['original_width']-1)
130
+
131
+ game['size_x'] = game['limit_x'] - offset_x + 1
132
+
133
+ cir0.x = offset_x*ss_sprite.scale_factor + ss_sprite.rect.left
134
+ cir1.x = game['limit_x']*ss_sprite.scale_factor + ss_sprite.rect.left
135
+
136
+ game['n_col'] = np.floor(game['size_x']/game['pixel_x'])
137
+
138
+ game.when_receive_message('offset_x_change').add_handler(on_offset_x_change)
139
+
140
+ def on_offset_y_change(data):
141
+
142
+ if not (size_y := game['size_y']): return
143
+ if (offset_y := game['offset_y']) is None:
144
+ game['offset_y'] = 0
145
+ offset_y = 0
146
+
147
+ ss_sprite: pysc.Sprite = game['ss_sprite']
148
+ if not ss_sprite: return
149
+
150
+ game['limit_y'] = min(size_y+offset_y-1, ss_sprite['original_height']-1)
151
+
152
+ game['size_y'] = game['limit_y'] - offset_y + 1
153
+
154
+ cir0.y = offset_y*ss_sprite.scale_factor + ss_sprite.rect.top
155
+ cir1.y = game['limit_y']*ss_sprite.scale_factor + ss_sprite.rect.top
156
+
157
+ game['n_row'] = np.floor(game['size_y']/game['pixel_y'])
158
+
159
+ game.when_receive_message('offset_y_change').add_handler(on_offset_y_change)
160
+
161
+
162
+ def on_n_row_change(data):
163
+ if not game['size_y']: return
164
+ game['pixel_y'] = np.floor(game['size_y']/value_if_not(game['n_row'], 1))
165
+
166
+ game.when_receive_message('n_row_change').add_handler(on_n_row_change)
167
+
168
+
169
+ def on_n_col_change(data):
170
+ if not game['size_x']: return
171
+ game['pixel_x'] = np.floor(game['size_x']/value_if_not(game['n_col'], 1))
172
+
173
+ game.when_receive_message('n_col_change').add_handler(on_n_col_change)
174
+
175
+
176
+
177
+ def on_pix_x_change(data):
178
+ if not game['size_x']: return
179
+ game['n_col'] = np.floor(game['size_x']/value_if_not(game['pixel_x'], 1))
180
+
181
+
182
+ game.when_receive_message('pixel_x_change').add_handler(on_pix_x_change)
183
+
184
+ def on_pix_y_change(data):
185
+ if not game['size_y']: return
186
+ game['n_row'] = np.floor(game['size_y']/value_if_not(game['pixel_y'], 1))
187
+
188
+
189
+ game.when_receive_message('pixel_y_change').add_handler(on_pix_y_change)
190
+
191
+
192
+
193
+ def on_scale_change(data):
194
+ ss_sprite: pysc.Sprite = game['ss_sprite']
195
+ if game['offset_x'] is None: return
196
+ if game['limit_x'] is None: return
197
+ if game['offset_y'] is None: return
198
+ if game['limit_y'] is None: return
199
+
200
+
201
+ cir0.x = game['offset_x']*ss_sprite.scale_factor + ss_sprite.rect.left
202
+ cir1.x = game['limit_x']*ss_sprite.scale_factor + ss_sprite.rect.left
203
+
204
+ cir0.y = game['offset_y']*ss_sprite.scale_factor + ss_sprite.rect.top
205
+ cir1.y = game['limit_y']*ss_sprite.scale_factor + ss_sprite.rect.top
206
+
207
+
208
+ game.when_receive_message('ss_sprite_scale_change').add_handler(on_pix_y_change)
@@ -0,0 +1,14 @@
1
+ import pygame
2
+
3
+ SCREEN_HEIGHT = 720
4
+ SCREEN_WIDTH = 1280
5
+ DEFAULT_FONT24 = pygame.font.SysFont(None, 24)
6
+ DEFAULT_FONT48 = pygame.font.SysFont(None, 24)
7
+
8
+ LEFT_PANEL_WIDTH = 120
9
+ RIGHT_PANEL_WIDTH = 600
10
+
11
+ BOTTOM_RIGHT_PANEL_HEIGHT = 200
12
+ PANEL_MARGIN = 10
13
+
14
+
@@ -0,0 +1,235 @@
1
+ from calendar import Day
2
+ import pyscratch as pysc
3
+ from settings import *
4
+
5
+ w, h = 120, 40
6
+ selected_colour = (255, 255, 255)
7
+ deselected_colour = (127, 127, 127)
8
+
9
+
10
+ def FloatInputBoxVerticallyLabel(data_key, message_on_change="", default_value=""):
11
+ label = pysc.create_rect_sprite(deselected_colour, w, h)
12
+ label.write_text(data_key, DEFAULT_FONT24, offset=(w/2, h/2))
13
+
14
+ selected_sur = pysc.create_rect(selected_colour, w, h)
15
+ deselected_sur = pysc.create_rect(deselected_colour, w, h)
16
+ pysc.game.shared_data[data_key] = None
17
+ text_box = pysc.Sprite({'selected':[selected_sur], 'deselected':[deselected_sur]}, 'deselected')
18
+ label.lock_to(text_box, (0,0), reset_xy=True)
19
+ label.y = -h
20
+
21
+ # def on_click():
22
+ # text_box.set_animation('selected')
23
+ # text_box.sprite_data['selected'] = True
24
+
25
+ # text_box.when_this_sprite_clicked().add_handler(on_click)
26
+ text_box['selected'] = False
27
+ text_box['text'] = default_value
28
+ try:
29
+ pysc.game.shared_data[data_key] = float(text_box.sprite_data['text'])
30
+ except:
31
+ pysc.game.shared_data[data_key] = None
32
+
33
+ text_box.write_text(text_box['text'], DEFAULT_FONT24, colour=(255,255,255), offset=(w/2, h/2))
34
+ def on_any_key_press(key:str, updown):
35
+ if updown == 'up': return
36
+ if not text_box.sprite_data['selected']: return
37
+ if text_box['just_selected']:
38
+ text_box['just_selected'] = False
39
+ text_box['text'] = ''
40
+
41
+ if key.isdigit() or key == '.':
42
+ text_box.sprite_data['text'] += key
43
+
44
+ if key == 'backspace' and len(text_box.sprite_data['text']):
45
+ text_box.sprite_data['text'] = text_box.sprite_data['text'][:-1]
46
+
47
+ try:
48
+ pysc.game.shared_data[data_key] = float(text_box.sprite_data['text'])
49
+ except:
50
+ pysc.game.shared_data[data_key] = None
51
+
52
+ text_box.write_text(text_box.sprite_data['text'], DEFAULT_FONT24, colour=(0,0,0), offset=(w/2, h/2))
53
+
54
+ if message_on_change:
55
+ pysc.game.broadcast_message(message_on_change, pysc.game.shared_data[data_key])
56
+
57
+ text_box.when_any_key_pressed().add_handler(on_any_key_press)
58
+
59
+ def on_any_mouse_click(pos, button, updown):
60
+ if not text_box.is_touching_mouse():
61
+ #print(button)
62
+ #print(pos)
63
+ text_box.sprite_data['selected'] = False
64
+ text_box['just_selected'] = False
65
+ text_box.set_animation('deselected')
66
+ text_box.write_text(text_box.sprite_data['text'], DEFAULT_FONT24, colour=(255,255,255), offset=(w/2, h/2))
67
+
68
+ else:
69
+ text_box.sprite_data['selected'] = True
70
+ text_box['just_selected'] = True
71
+ text_box.set_animation('selected')
72
+ text_box.write_text(text_box['text'], DEFAULT_FONT24, colour=(0,0,0), offset=(w/2, h/2))
73
+ pysc.game.when_mouse_click([text_box]).add_handler(on_any_mouse_click)
74
+
75
+ return text_box
76
+
77
+
78
+ def FloatInputBoxHoriLabel(data_key, message_on_change="", default_value="", label_width=80):
79
+ wl = label_width
80
+ label = pysc.create_rect_sprite(deselected_colour, wl, h)
81
+ label.write_text(data_key, DEFAULT_FONT24, offset=(wl/2, h/2))
82
+
83
+ selected_sur = pysc.create_rect(selected_colour, w, h)
84
+ deselected_sur = pysc.create_rect(deselected_colour, w, h)
85
+ pysc.game.shared_data[data_key] = None
86
+ text_box = pysc.Sprite({'selected':[selected_sur], 'deselected':[deselected_sur]}, 'deselected')
87
+ label.lock_to(text_box, (0,0), reset_xy=True)
88
+ label.x = -w/2-wl/2
89
+
90
+
91
+ # def on_click():
92
+ # text_box.set_animation('selected')
93
+ # text_box.sprite_data['selected'] = True
94
+
95
+ # text_box.when_this_sprite_clicked().add_handler(on_click)
96
+ text_box['selected'] = False
97
+ text_box['text'] = default_value
98
+ try:
99
+ pysc.game.shared_data[data_key] = float(text_box.sprite_data['text'])
100
+ except:
101
+ pysc.game.shared_data[data_key] = None
102
+
103
+ text_box.write_text(text_box['text'], DEFAULT_FONT24, colour=(255,255,255), offset=(w/2, h/2))
104
+ def on_any_key_press(key:str, updown):
105
+ if updown == 'up': return
106
+ if not text_box.sprite_data['selected']: return
107
+ if text_box['just_selected']:
108
+ text_box['just_selected'] = False
109
+ text_box['text'] = ''
110
+
111
+ if key.isdigit() or key == '.':
112
+ text_box.sprite_data['text'] += key
113
+
114
+ if key == 'backspace' and len(text_box.sprite_data['text']):
115
+ text_box.sprite_data['text'] = text_box.sprite_data['text'][:-1]
116
+
117
+ try:
118
+ pysc.game.shared_data[data_key] = float(text_box.sprite_data['text'])
119
+ except:
120
+ pysc.game.shared_data[data_key] = None
121
+
122
+ text_box.write_text(text_box.sprite_data['text'], DEFAULT_FONT24, colour=(0,0,0), offset=(w/2, h/2))
123
+
124
+ if message_on_change:
125
+ pysc.game.broadcast_message(message_on_change, pysc.game.shared_data[data_key])
126
+
127
+ text_box.when_any_key_pressed().add_handler(on_any_key_press)
128
+
129
+ def on_any_mouse_click(pos, button, updown):
130
+ if not text_box.is_touching_mouse():
131
+ #print(button)
132
+ #print(pos)
133
+ text_box.sprite_data['selected'] = False
134
+ text_box['just_selected'] = False
135
+ text_box.set_animation('deselected')
136
+ text_box.write_text(text_box.sprite_data['text'], DEFAULT_FONT24, colour=(255,255,255), offset=(w/2, h/2))
137
+
138
+ else:
139
+ text_box.sprite_data['selected'] = True
140
+ text_box['just_selected'] = True
141
+ text_box.set_animation('selected')
142
+ text_box.write_text(text_box['text'], DEFAULT_FONT24, colour=(0,0,0), offset=(w/2, h/2))
143
+ pysc.game.when_mouse_click([text_box]).add_handler(on_any_mouse_click)
144
+
145
+ return text_box
146
+
147
+
148
+ def IntegerInputBox(data_key, message_on_change="", default_value="", label_width=60):
149
+ wl = label_width
150
+ label = pysc.create_rect_sprite(deselected_colour, wl, h)
151
+ label.write_text(data_key, DEFAULT_FONT24, offset=(wl/2, h/2))
152
+
153
+ selected_sur = pysc.create_rect(selected_colour, w, h)
154
+ deselected_sur = pysc.create_rect(deselected_colour, w, h)
155
+ pysc.game.shared_data[data_key] = None
156
+ text_box = pysc.Sprite({'selected':[selected_sur], 'deselected':[deselected_sur]}, 'deselected')
157
+ label.lock_to(text_box, (0,0), reset_xy=True)
158
+ label.x = -w/2-wl/2
159
+
160
+ # def on_click():
161
+ # text_box.set_animation('selected')
162
+ # text_box.sprite_data['selected'] = True
163
+
164
+ # text_box.when_this_sprite_clicked().add_handler(on_click)
165
+ text_box.sprite_data['selected'] = False
166
+ text_box.sprite_data['text'] = default_value
167
+ text_box.write_text(text_box['text'], DEFAULT_FONT24, colour=(255,255,255), offset=(w/2, h/2))
168
+ def on_any_key_press(key:str, updown):
169
+ if updown == 'up': return
170
+ if not text_box.sprite_data['selected']: return
171
+ if text_box['just_selected']:
172
+ text_box['just_selected'] = False
173
+ text_box['text'] = ''
174
+
175
+ if key.isdigit():
176
+ text_box.sprite_data['text'] += key
177
+
178
+ if key == 'backspace' and len(text_box.sprite_data['text']):
179
+ text_box.sprite_data['text'] = text_box.sprite_data['text'][:-1]
180
+
181
+ # try:
182
+ # pysc.game.shared_data[data_key] = int(text_box.sprite_data['text'])
183
+ # except:
184
+ # pysc.game.shared_data[data_key] = None
185
+
186
+ text_box.write_text(text_box.sprite_data['text'], DEFAULT_FONT24, colour=(0,0,0), offset=(w/2, h/2))
187
+
188
+
189
+
190
+ text_box.when_any_key_pressed().add_handler(on_any_key_press)
191
+
192
+ def on_any_mouse_click(pos, button, updown):
193
+ if not text_box.is_touching_mouse():
194
+ #print(button)
195
+ #print(pos)
196
+ if text_box['selected']:
197
+ try:
198
+ pysc.game.shared_data[data_key] = int(text_box.sprite_data['text'])
199
+ except:
200
+ pysc.game.shared_data[data_key] = None
201
+ if message_on_change:
202
+ pysc.game.broadcast_message(message_on_change, pysc.game.shared_data[data_key])
203
+ text_box['selected'] = False
204
+ text_box['just_selected'] = False
205
+ text_box.set_animation('deselected')
206
+ text_box.write_text(text_box['text'], DEFAULT_FONT24, colour=(255,255,255), offset=(w/2, h/2))
207
+
208
+ else:
209
+ text_box['selected'] = True
210
+ text_box['just_selected'] = True
211
+ text_box.set_animation('selected')
212
+ text_box.write_text(text_box['text'], DEFAULT_FONT24, colour=(0,0,0), offset=(w/2, h/2))
213
+ pysc.game.when_mouse_click([text_box]).add_handler(on_any_mouse_click)
214
+
215
+
216
+ def update_value_on_change():
217
+ while True:
218
+ yield 1/30
219
+
220
+
221
+
222
+ if text_box.animation_name == "selected":
223
+ continue
224
+
225
+ try:
226
+ text_box['text'] = str(round(pysc.game[data_key]))
227
+ except:
228
+ text_box['text'] = ""
229
+
230
+ text_box.write_text(text_box['text'], DEFAULT_FONT24, colour=(255,255,255), offset=(w/2, h/2))
231
+
232
+
233
+ text_box.when_game_start().add_handler(update_value_on_change)
234
+
235
+ return text_box
@@ -0,0 +1,86 @@
1
+
2
+
3
+ import pygame
4
+
5
+
6
+ def render_wrapped_file_name(text, max_chars, font, color=(255, 255, 255), bg_color=None, max_lines=None):
7
+ """
8
+ THIS FUNCTION IS WRITTEN BY AN AI
9
+
10
+ Renders a file name like a file explorer: wrapping intelligently while preserving the extension.
11
+
12
+ :param text: The file name string to render.
13
+ :param max_chars: Maximum number of characters per line (approximate width).
14
+ :param font: Pygame font object to render with.
15
+ :param color: Text color (default white).
16
+ :param bg_color: Background color (default None = transparent).
17
+ :param max_lines: Optional maximum number of lines to render. Extra text will be truncated with '...'.
18
+ :return: A Pygame surface with the rendered multiline text.
19
+ """
20
+ def split_file_name(text):
21
+ if '.' in text and not text.startswith('.') and text.rfind('.') > 0:
22
+ idx = text.rfind('.')
23
+ return text[:idx], text[idx+1:]
24
+ else:
25
+ return text, ''
26
+
27
+ def wrap_lines(base, ext, max_chars, max_lines=None):
28
+ words = base.split(' ')
29
+ lines = []
30
+ current_line = ''
31
+
32
+ for word in words:
33
+ test_line = (current_line + ' ' + word).strip()
34
+ if len(test_line) <= max_chars:
35
+ current_line = test_line
36
+ else:
37
+ if current_line:
38
+ lines.append(current_line)
39
+ if max_lines and len(lines) == max_lines:
40
+ return lines[:-1] + [truncate_with_ellipsis(current_line, max_chars)]
41
+ # Break long word
42
+ while len(word) > max_chars:
43
+ lines.append(word[:max_chars])
44
+ word = word[max_chars:]
45
+ if max_lines and len(lines) == max_lines:
46
+ return lines[:-1] + [truncate_with_ellipsis(lines[-1], max_chars)]
47
+ current_line = word
48
+
49
+ if current_line:
50
+ lines.append(current_line)
51
+ if max_lines and len(lines) > max_lines:
52
+ lines = lines[:max_lines]
53
+ lines[-1] = truncate_with_ellipsis(lines[-1], max_chars)
54
+
55
+ # Add extension
56
+ if ext:
57
+ if len(lines[-1] + '.' + ext) <= max_chars:
58
+ lines[-1] += '.' + ext
59
+ elif not max_lines or len(lines) < max_lines:
60
+ lines.append('.' + ext)
61
+ else:
62
+ lines[-1] = truncate_with_ellipsis(lines[-1], max_chars)
63
+
64
+ return lines
65
+
66
+ def truncate_with_ellipsis(line, max_chars):
67
+ return line[:max(0, max_chars - 3)] + '...'
68
+
69
+ # Prepare
70
+ base, ext = split_file_name(text)
71
+ lines = wrap_lines(base, ext, max_chars, max_lines)
72
+
73
+ # Render
74
+ line_height = font.get_linesize()
75
+ surface_height = line_height * len(lines)
76
+ surface_width = max(font.size(line)[0] for line in lines)
77
+
78
+ rendered_surface = pygame.Surface((surface_width, surface_height), pygame.SRCALPHA)
79
+ if bg_color:
80
+ rendered_surface.fill(bg_color)
81
+
82
+ for i, line in enumerate(lines):
83
+ text_surface = font.render(line, True, color)
84
+ rendered_surface.blit(text_surface, (0, i * line_height))
85
+
86
+ return rendered_surface
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyscratch-pysc
3
+ Version: 1.0.3
4
+ Summary: A Python game development framework designed to provide an easy transitioning from Scratch to Python
5
+ Author-email: Daniel Ka-Wa Chan <kwdaniel525@protonmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://kwdchan.github.io/pyscratch/
8
+ Project-URL: Repository, https://github.com/kwdChan/pyscratch
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: pymunk<7
15
+ Requires-Dist: pygame<3
16
+ Requires-Dist: numpy<3
17
+ Requires-Dist: typing_extensions
18
+ Requires-Dist: pillow<12
19
+
20
+ # PyScratch
21
+ For more information, see: https://kwdchan.github.io/pyscratch/
22
+
23
+
24
+
25
+
26
+
27
+
28
+ ## Change History
29
+
30
+ ### 07 Sep 2025
31
+ **v1.0.3**
32
+ Added undeclared dependency: Pillow
33
+ Specified the versions of the dependencies
34
+
35
+ ### Jul 2025
36
+ **v1.0.0**
37
+ The first version