ed-game 1.0.0__tar.gz

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.
ed_game-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Your Name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
ed_game-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: ed-game
3
+ Version: 1.0.0
4
+ Summary: Earth Defense Game
5
+ Author-email: Your Name <your@email.com>
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: pygame-ce>=2.4
11
+ Dynamic: license-file
12
+
13
+ # 地球防御 · 星辰守卫者
14
+
15
+ ## 安装
16
+
17
+ ```bash
18
+ pip install earth-defense
@@ -0,0 +1,6 @@
1
+ # 地球防御 · 星辰守卫者
2
+
3
+ ## 安装
4
+
5
+ ```bash
6
+ pip install earth-defense
@@ -0,0 +1,18 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ed-game"
7
+ version = "1.0.0"
8
+ description = "Earth Defense Game"
9
+ authors = [{name = "Your Name", email = "your@email.com"}]
10
+ readme = "README.md"
11
+ requires-python = ">=3.10"
12
+ license = {text = "MIT"}
13
+ dependencies = ["pygame-ce>=2.4"]
14
+
15
+ [tool.setuptools.packages.find]
16
+ where = ["src"]
17
+ [project.scripts]
18
+ ed-game = "ed_game.game_1:run"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,29 @@
1
+ """地球防御 · 星辰守卫者射击游戏"""
2
+
3
+ import sys
4
+
5
+ def _check_pygame():
6
+ """检查 pygame-ce 是否已安装"""
7
+ try:
8
+ import pygame
9
+ return True
10
+ except ImportError:
11
+ print("=" * 50)
12
+ print("❌ 错误:未找到 pygame-ce!")
13
+ print("=" * 50)
14
+ print("请先安装 pygame-ce:")
15
+ print(" pip install pygame-ce")
16
+ print("")
17
+ print("或者使用国内镜像源:")
18
+ print(" pip install pygame-ce -i https://pypi.tuna.tsinghua.edu.cn/simple")
19
+ print("=" * 50)
20
+ return False
21
+
22
+ # 模块加载时自动检查
23
+ if not _check_pygame():
24
+ sys.exit(1)
25
+
26
+ from .game_1 import run
27
+
28
+ __version__ = "1.0.0"
29
+ __all__ = ["run"]
@@ -0,0 +1,637 @@
1
+ import pygame
2
+ import random
3
+ import math
4
+ import sys
5
+ import time
6
+
7
+ # 初始化
8
+ pygame.init()
9
+
10
+ # 游戏配置
11
+ WIDTH = 1800
12
+ HEIGHT = 1050
13
+ FPS = 60
14
+
15
+ # 颜色定义
16
+ COLOR_BG = (2, 6, 23)
17
+ COLOR_ENEMY = (255, 58, 111)
18
+ COLOR_ENEMY_ELITE = (255, 136, 68)
19
+ COLOR_ENEMY_BOSS = (180, 50, 255) # 紫色巨型敌人
20
+ COLOR_BULLET = (255, 255, 128)
21
+ COLOR_BULLET_CORE = (255, 204, 68)
22
+ COLOR_PLAYER_MAIN = (62, 255, 250)
23
+ COLOR_PLAYER_CORE = (0, 204, 255)
24
+ COLOR_PLAYER_FIRE = (255, 102, 0)
25
+ COLOR_TEXT_SCORE = (0, 255, 255)
26
+ COLOR_TEXT_LIVES = (255, 51, 102)
27
+ COLOR_TEXT_STATUS = (255, 215, 0)
28
+ COLOR_TEXT_GAMEOVER = (255, 68, 102)
29
+ COLOR_WHITE = (255, 255, 255)
30
+ COLOR_GOLD = (255, 215, 0)
31
+ COLOR_HEART = (255, 80, 120)
32
+
33
+ # 速度参数
34
+ BASE_ENEMY_SPEED = 0.31
35
+ MAX_ENEMY_SPEED = 0.65
36
+ ELITE_SPEED_FACTOR = 0.84
37
+ BOSS_SPEED_FACTOR = 0.2 # 慢5倍 = 0.2倍速
38
+
39
+ BULLET_SPEED = -7.2
40
+ PLAYER_SPEED = 6.2
41
+
42
+ PLAYER_RADIUS = 15
43
+ PLAYER_START_POS = (WIDTH // 2, HEIGHT - 60)
44
+
45
+ INVINCIBLE_DURATION = 55
46
+ SHOOT_COOLDOWN_FRAMES = 9
47
+
48
+ BASE_SPAWN_DELAY = 48
49
+ STAR_COUNT = 220
50
+
51
+ # 巨型敌人参数
52
+ BOSS_SIZE_MULTIPLIER = 4 # 大4倍
53
+ BOSS_HP_MULTIPLIER = 16 # 血量16倍
54
+ BOSS_SPEED_MULTIPLIER = 0.2 # 速度慢5倍
55
+ BOSS_SPAWN_INTERVAL = 20 * 1000
56
+
57
+ # 全局状态
58
+ game_active = True
59
+ score = 0
60
+ lives = 3
61
+ invincible = 0
62
+ wave_level = 1
63
+ enemy_speed_current = BASE_ENEMY_SPEED
64
+ spawn_delay_current = BASE_SPAWN_DELAY
65
+
66
+ player_x, player_y = PLAYER_START_POS
67
+ left_pressed = False
68
+ right_pressed = False
69
+ shoot_pressed = False
70
+ shoot_cooldown = 0
71
+
72
+ bullets = []
73
+ enemies = []
74
+ powerups = []
75
+ explosions = []
76
+ particles = []
77
+
78
+ stars = []
79
+ status_message = "极慢模式 | 敌人每秒仅移动5毫米"
80
+ status_msg_timer = 0
81
+ enemy_spawn_timer = 20
82
+
83
+ # 巨型敌人计时器
84
+ last_boss_spawn_time = 0
85
+ boss_spawn_timer = 0
86
+
87
+ # Pygame 相关
88
+ screen = pygame.display.set_mode((WIDTH, HEIGHT))
89
+ pygame.display.set_caption("星辰守卫者 · 极慢漂移版")
90
+ clock = pygame.time.Clock()
91
+
92
+ # 加载字体(支持中文)
93
+ try:
94
+ font_large = pygame.font.Font("C:/Windows/Fonts/simhei.ttf", 48)
95
+ font_medium = pygame.font.Font("C:/Windows/Fonts/simhei.ttf", 28)
96
+ font_small = pygame.font.Font("C:/Windows/Fonts/simhei.ttf", 20)
97
+ font_tiny = pygame.font.Font("C:/Windows/Fonts/simhei.ttf", 14)
98
+ except:
99
+ font_large = pygame.font.SysFont("simhei", 48)
100
+ font_medium = pygame.font.SysFont("simhei", 28)
101
+ font_small = pygame.font.SysFont("simhei", 20)
102
+ font_tiny = pygame.font.SysFont("simhei", 14)
103
+
104
+
105
+ def draw_text(text, x, y, color, center=False, font=None):
106
+ if font is None:
107
+ font = font_medium
108
+ surface = font.render(text, True, color)
109
+ rect = surface.get_rect()
110
+ if center:
111
+ rect.center = (x, y)
112
+ else:
113
+ rect.topleft = (x, y)
114
+ screen.blit(surface, rect)
115
+
116
+
117
+ def draw_circle(x, y, radius, color, width=0):
118
+ pygame.draw.circle(screen, color, (int(x), int(y)), int(radius), width)
119
+
120
+
121
+ def draw_star(cx, cy, size):
122
+ """画五角星"""
123
+ points = []
124
+ outer_r = size
125
+ inner_r = size * 0.4
126
+ for i in range(5):
127
+ angle = math.radians(90 - i * 72)
128
+ x = cx + outer_r * math.cos(angle)
129
+ y = cy - outer_r * math.sin(angle)
130
+ points.append((x, y))
131
+ angle = math.radians(90 - (i + 0.5) * 72)
132
+ x = cx + inner_r * math.cos(angle)
133
+ y = cy - inner_r * math.sin(angle)
134
+ points.append((x, y))
135
+ pygame.draw.polygon(screen, COLOR_GOLD, points)
136
+
137
+
138
+ def draw_heart(cx, cy, size):
139
+ """画心形"""
140
+ points = []
141
+ scale = size / 15
142
+ for t in range(0, 360, 10):
143
+ rad = math.radians(t)
144
+ x = 16 * math.sin(rad) ** 3
145
+ y = 13 * math.cos(rad) - 5 * math.cos(2*rad) - 2 * math.cos(3*rad) - math.cos(4*rad)
146
+ points.append((cx + x * scale, cy - y * scale))
147
+ if len(points) > 2:
148
+ pygame.draw.polygon(screen, COLOR_HEART, points)
149
+
150
+
151
+ def draw_crown(cx, cy, size):
152
+ """画王冠(巨型敌人标记)"""
153
+ points = [
154
+ (cx - size, cy + size),
155
+ (cx - size*0.5, cy - size),
156
+ (cx, cy - size*0.5),
157
+ (cx + size*0.5, cy - size),
158
+ (cx + size, cy + size),
159
+ (cx + size*0.6, cy + size*0.3),
160
+ (cx, cy),
161
+ (cx - size*0.6, cy + size*0.3),
162
+ ]
163
+ pygame.draw.polygon(screen, COLOR_GOLD, points)
164
+
165
+
166
+ def reset_game():
167
+ global game_active, score, lives, invincible, wave_level
168
+ global enemy_speed_current, spawn_delay_current
169
+ global player_x, player_y, bullets, enemies, powerups, explosions, particles
170
+ global shoot_cooldown, status_message, status_msg_timer, enemy_spawn_timer
171
+ global last_boss_spawn_time, boss_spawn_timer
172
+
173
+ game_active = True
174
+ score = 0
175
+ lives = 3
176
+ invincible = 0
177
+ wave_level = 1
178
+ enemy_speed_current = BASE_ENEMY_SPEED
179
+ spawn_delay_current = BASE_SPAWN_DELAY
180
+
181
+ player_x, player_y = PLAYER_START_POS
182
+
183
+ bullets.clear()
184
+ enemies.clear()
185
+ powerups.clear()
186
+ explosions.clear()
187
+ particles.clear()
188
+
189
+ shoot_cooldown = 0
190
+ enemy_spawn_timer = 20
191
+
192
+ last_boss_spawn_time = pygame.time.get_ticks()
193
+ boss_spawn_timer = 0
194
+
195
+ status_message = "重启极慢领域 | 巨型BOSS每分钟降临一次"
196
+ status_msg_timer = 120
197
+
198
+ for _ in range(3):
199
+ spawn_enemy()
200
+
201
+
202
+ def add_explosion(px, py, is_boss=False):
203
+ explosions.append({
204
+ 'x': px, 'y': py,
205
+ 'life': 6 if not is_boss else 20,
206
+ 'max_life': 6 if not is_boss else 20,
207
+ 'radius': 12 if not is_boss else 30
208
+ })
209
+ particle_count = 8 if not is_boss else 30
210
+ for _ in range(particle_count):
211
+ particles.append({
212
+ 'x': px, 'y': py,
213
+ 'vx': (random.random() - 0.5) * 3.5,
214
+ 'vy': (random.random() - 0.5) * 3 - 1,
215
+ 'life': 6 if not is_boss else 20,
216
+ 'size': random.random() * 3 + 1.5,
217
+ 'color': (random.randint(200, 255), random.randint(100, 180), random.randint(50, 150))
218
+ })
219
+
220
+
221
+ def spawn_powerup(x, y, is_boss=False):
222
+ # BOSS 必掉多个道具
223
+ if is_boss:
224
+ for _ in range(3):
225
+ if random.random() < 0.8:
226
+ powerups.append({
227
+ 'x': x + random.randint(-20, 20),
228
+ 'y': y,
229
+ 'radius': 9,
230
+ 'type': 'heart',
231
+ 'speed_y': 1.2
232
+ })
233
+ else:
234
+ if random.random() < 0.12:
235
+ powerups.append({
236
+ 'x': x, 'y': y,
237
+ 'radius': 9,
238
+ 'type': 'heart',
239
+ 'speed_y': 1.2
240
+ })
241
+
242
+
243
+ def spawn_enemy(is_boss=False):
244
+ if is_boss:
245
+ radius = 12 * BOSS_SIZE_MULTIPLIER # 48
246
+ x = random.randint(radius, WIDTH - radius)
247
+ speed = enemy_speed_current * BOSS_SPEED_MULTIPLIER
248
+ hp = 50 # 100倍血量
249
+ score_val = 200
250
+ color = COLOR_ENEMY_BOSS
251
+
252
+ enemies.append({
253
+ 'x': x, 'y': -radius,
254
+ 'radius': radius,
255
+ 'speed_y': speed,
256
+ 'color': color,
257
+ 'hp': hp,
258
+ 'max_hp': hp,
259
+ 'score_value': score_val,
260
+ 'elite': False,
261
+ 'is_boss': True
262
+ })
263
+ status_message = "⚠️ 巨型主宰者降临!⚠️"
264
+ status_msg_timer = 90
265
+ else:
266
+ radius = 12
267
+ x = random.randint(radius, WIDTH - radius)
268
+ speed = enemy_speed_current
269
+ hp = 1
270
+ score_val = 10
271
+ is_elite = (wave_level >= 2 and random.random() < 0.12)
272
+ color = COLOR_ENEMY_ELITE if is_elite else COLOR_ENEMY
273
+
274
+ if is_elite:
275
+ radius = 16
276
+ hp = 2
277
+ score_val = 30
278
+ speed = speed * ELITE_SPEED_FACTOR
279
+ if speed < 0.22:
280
+ speed = 0.22
281
+
282
+ enemies.append({
283
+ 'x': x, 'y': -radius,
284
+ 'radius': radius,
285
+ 'speed_y': speed,
286
+ 'color': color,
287
+ 'hp': hp,
288
+ 'max_hp': hp,
289
+ 'score_value': score_val,
290
+ 'elite': is_elite,
291
+ 'is_boss': False
292
+ })
293
+
294
+
295
+ def update_difficulty():
296
+ global wave_level, enemy_speed_current, spawn_delay_current, status_message, status_msg_timer
297
+ raw_wave = 1 + score // 550
298
+ raw_wave = min(raw_wave, 9)
299
+ if raw_wave > wave_level:
300
+ wave_level = raw_wave
301
+ new_speed = BASE_ENEMY_SPEED + (wave_level - 1) * 0.035
302
+ if new_speed > MAX_ENEMY_SPEED:
303
+ new_speed = MAX_ENEMY_SPEED
304
+ enemy_speed_current = new_speed
305
+ spawn_delay_current = max(32, 52 - int(wave_level * 1.5))
306
+ speed_mm = enemy_speed_current * 60 * 0.2646
307
+ status_message = f"烈度提升 Lv.{wave_level} | 速度 ≈ {speed_mm:.1f} mm/秒"
308
+ status_msg_timer = 120
309
+
310
+
311
+ def shoot_bullet():
312
+ global shoot_cooldown
313
+ if not game_active:
314
+ return
315
+ bullets.append({
316
+ 'x': player_x, 'y': player_y - 12,
317
+ 'radius': 5,
318
+ 'speed_y': BULLET_SPEED,
319
+ 'damage': 1
320
+ })
321
+ if score >= 600 and random.random() < 0.45:
322
+ bullets.append({
323
+ 'x': player_x - 7, 'y': player_y - 8,
324
+ 'radius': 4.2, 'speed_y': BULLET_SPEED, 'damage': 1
325
+ })
326
+ bullets.append({
327
+ 'x': player_x + 7, 'y': player_y - 8,
328
+ 'radius': 4.2, 'speed_y': BULLET_SPEED, 'damage': 1
329
+ })
330
+ shoot_cooldown = SHOOT_COOLDOWN_FRAMES
331
+
332
+
333
+ def update():
334
+ global player_x, player_y, invincible, shoot_cooldown
335
+ global game_active, lives, score, status_message, status_msg_timer, enemy_spawn_timer
336
+ global last_boss_spawn_time, boss_spawn_timer
337
+
338
+ if not game_active:
339
+ return
340
+
341
+ if invincible > 0:
342
+ invincible -= 1
343
+ if shoot_cooldown > 0:
344
+ shoot_cooldown -= 1
345
+
346
+ if left_pressed:
347
+ player_x -= PLAYER_SPEED
348
+ if right_pressed:
349
+ player_x += PLAYER_SPEED
350
+ player_x = max(PLAYER_RADIUS + 5, min(WIDTH - PLAYER_RADIUS - 5, player_x))
351
+
352
+ if shoot_pressed and shoot_cooldown == 0:
353
+ shoot_bullet()
354
+
355
+ # 检查巨型敌人生成(1分钟一次)
356
+ current_time = pygame.time.get_ticks()
357
+ if game_active and current_time - last_boss_spawn_time >= BOSS_SPAWN_INTERVAL:
358
+ spawn_enemy(is_boss=True)
359
+ last_boss_spawn_time = current_time
360
+ status_message = "👑 巨型主宰者出现! 血量100倍,速度极慢! 👑"
361
+ status_msg_timer = 120
362
+
363
+ # 子弹更新
364
+ for b in bullets[:]:
365
+ b['y'] += b['speed_y']
366
+ if b['y'] + b['radius'] < 0 or b['y'] - b['radius'] > HEIGHT:
367
+ bullets.remove(b)
368
+
369
+ # 敌人移动
370
+ for e in enemies[:]:
371
+ e['y'] += e['speed_y']
372
+ if e['y'] + e['radius'] > HEIGHT + 40:
373
+ enemies.remove(e)
374
+ if game_active and invincible <= 0 and not e.get('is_boss', False):
375
+ lives -= 1
376
+ if lives <= 0:
377
+ lives = 0
378
+ game_active = False
379
+ status_message = "战舰沉没 | 极慢世界也需谨慎"
380
+ return
381
+ else:
382
+ invincible = INVINCIBLE_DURATION
383
+ add_explosion(player_x, player_y - 8)
384
+ status_message = "护盾紧急启动!"
385
+ status_msg_timer = 60
386
+
387
+ # 子弹碰撞
388
+ for b in bullets[:]:
389
+ hit = None
390
+ for e in enemies:
391
+ if math.hypot(b['x'] - e['x'], b['y'] - e['y']) < b['radius'] + e['radius']:
392
+ hit = e
393
+ break
394
+ if hit:
395
+ bullets.remove(b)
396
+ hit['hp'] -= b['damage']
397
+ if hit['hp'] <= 0:
398
+ score += hit['score_value']
399
+ update_difficulty()
400
+ add_explosion(hit['x'], hit['y'], is_boss=hit.get('is_boss', False))
401
+ spawn_powerup(hit['x'], hit['y'], is_boss=hit.get('is_boss', False))
402
+ enemies.remove(hit)
403
+ else:
404
+ particle_mult = 4 if not hit.get('is_boss', False) else 12
405
+ for _ in range(particle_mult):
406
+ particles.append({
407
+ 'x': hit['x'], 'y': hit['y'],
408
+ 'vx': (random.random()-0.5)*2.5,
409
+ 'vy': (random.random()-0.5)*2.5,
410
+ 'life': 8,
411
+ 'size': 2 if not hit.get('is_boss', False) else 4,
412
+ 'color': (255, 170, 51)
413
+ })
414
+
415
+ # 玩家碰撞
416
+ for e in enemies[:]:
417
+ if math.hypot(player_x - e['x'], player_y - e['y']) < PLAYER_RADIUS + e['radius']:
418
+ if invincible <= 0 and game_active:
419
+ lives -= 1
420
+ if lives <= 0:
421
+ lives = 0
422
+ game_active = False
423
+ status_message = "极慢败北 | 按 R 重启"
424
+ add_explosion(player_x, player_y)
425
+ return
426
+ else:
427
+ invincible = INVINCIBLE_DURATION
428
+ add_explosion(player_x, player_y - 6)
429
+ status_message = "撞击伤害!短暂无敌"
430
+ status_msg_timer = 60
431
+ add_explosion(e['x'], e['y'])
432
+ enemies.remove(e)
433
+ # 道具拾取
434
+ for p in powerups[:]:
435
+ p['y'] += p['speed_y']
436
+
437
+ # 碰到下边界 → 自动拾取
438
+ if p['y'] + p['radius'] > HEIGHT + 30:
439
+ if p['type'] == 'heart':
440
+ if lives < 5:
441
+ lives += 1
442
+ status_message = "自动接住 +1 耐久"
443
+ else:
444
+ score += 25
445
+ status_message = "自动接住 +25 积分"
446
+ status_msg_timer = 60
447
+ add_explosion(p['x'], p['y'])
448
+ powerups.remove(p)
449
+ continue
450
+
451
+ # 玩家主动拾取
452
+ if math.hypot(player_x - p['x'], player_y - p['y']) < PLAYER_RADIUS + p['radius']:
453
+ if p['type'] == 'heart':
454
+ if lives < 5:
455
+ lives += 1
456
+ status_message = "维修核心 +1 耐久"
457
+ status_msg_timer = 60
458
+ else:
459
+ score += 25
460
+ status_message = "能量满载 +25 积分"
461
+ status_msg_timer = 60
462
+ add_explosion(p['x'], p['y'])
463
+ powerups.remove(p)
464
+
465
+ # 敌人生成
466
+ if enemy_spawn_timer <= 0:
467
+ count = 2 if (wave_level >= 4 and random.random() < 0.3) else 1
468
+ for _ in range(count):
469
+ spawn_enemy()
470
+ enemy_spawn_timer = max(34, spawn_delay_current - int(wave_level * 1.2))
471
+ else:
472
+ enemy_spawn_timer -= 1
473
+ # 额外精英
474
+ if wave_level >= 2 and random.random() < 0.008 and len(enemies) < 20:
475
+ spawn_enemy()
476
+
477
+ # 粒子/爆炸更新
478
+ for p in particles[:]:
479
+ p['x'] += p['vx']
480
+ p['y'] += p['vy']
481
+ p['life'] -= 1
482
+ if p['life'] <= 0:
483
+ particles.remove(p)
484
+ for ex in explosions[:]:
485
+ ex['life'] -= 1
486
+ if ex['life'] <= 0:
487
+ explosions.remove(ex)
488
+
489
+ if status_msg_timer > 0:
490
+ status_msg_timer -= 1
491
+ if status_msg_timer == 0 and game_active:
492
+ status_message = "极慢星河 · 悠闲射击"
493
+
494
+ if lives <= 0:
495
+ game_active = False
496
+ status_message = "极慢世界终结 | 按 R 继续"
497
+
498
+
499
+ def draw():
500
+ screen.fill(COLOR_BG)
501
+
502
+ # 星星
503
+ for s in stars:
504
+ s['y'] += s['speed'] * 0.3
505
+ if s['y'] > HEIGHT:
506
+ s['y'] = 0
507
+ draw_circle(s['x'], s['y'], s['radius'], s['color'])
508
+
509
+ # 敌人
510
+ for e in enemies:
511
+ draw_circle(e['x'], e['y'], e['radius']-1, e['color'])
512
+ draw_circle(e['x'], e['y'], e['radius']-4, (170, 34, 34))
513
+
514
+ if e.get('is_boss', False):
515
+ # BOSS 画王冠
516
+ draw_crown(e['x'], e['y'] - e['radius']//2, 12)
517
+ # BOSS 血条
518
+ bar_width = 80 * (e['hp'] / e['max_hp'])
519
+ pygame.draw.rect(screen, (255, 50, 50), (e['x']-40, e['y']-e['radius']-8, 80, 6))
520
+ pygame.draw.rect(screen, (0, 255, 0), (e['x']-40, e['y']-e['radius']-8, bar_width, 6))
521
+ elif e['elite']:
522
+ # 精英画星星
523
+ draw_star(e['x'], e['y'] - 8, 8)
524
+ if e['hp'] > 1:
525
+ bar_width = 20 * (e['hp'] / 2)
526
+ pygame.draw.rect(screen, (255, 170, 85), (e['x']-10, e['y']-e['radius']-4, bar_width, 4))
527
+
528
+ # 道具 - 画心形
529
+ for p in powerups:
530
+ draw_circle(p['x'], p['y'], p['radius']-2, (255, 94, 126))
531
+ draw_heart(p['x'], p['y'] + 2, 10)
532
+
533
+ # 子弹
534
+ for b in bullets:
535
+ draw_circle(b['x'], b['y'], b['radius']-1, COLOR_BULLET)
536
+ draw_circle(b['x'], b['y'], b['radius']-2, COLOR_BULLET_CORE)
537
+
538
+ # 玩家飞船
539
+ flash = (invincible > 0 and (pygame.time.get_ticks() // 100 % 2 == 0))
540
+ if not flash or not game_active:
541
+ draw_circle(player_x, player_y, 16, COLOR_PLAYER_MAIN)
542
+ draw_circle(player_x, player_y-4, 6, COLOR_PLAYER_CORE)
543
+ draw_circle(player_x-3, player_y-6, 2, COLOR_WHITE)
544
+ draw_circle(player_x+3, player_y-6, 2, COLOR_WHITE)
545
+ draw_circle(player_x, player_y+18, 6, COLOR_PLAYER_FIRE)
546
+ draw_circle(player_x-3, player_y+16, 4, COLOR_PLAYER_FIRE)
547
+ draw_circle(player_x+3, player_y+16, 4, COLOR_PLAYER_FIRE)
548
+ draw_circle(player_x, player_y, 19, (0, 255, 255, 100), width=1)
549
+
550
+ # 爆炸与粒子
551
+ for ex in explosions:
552
+ ratio = ex['life'] / ex['max_life']
553
+ r = ex['radius'] * (0.5 + ratio * 0.5)
554
+ draw_circle(ex['x'], ex['y'], r, (255, 100, 30))
555
+ draw_circle(ex['x'], ex['y'], r*0.6, (255, 200, 50))
556
+ for p in particles:
557
+ draw_circle(p['x'], p['y'], p['size'], p['color'])
558
+
559
+ # UI 文字
560
+ draw_text(f"积分: {score}", 30, 20, COLOR_TEXT_SCORE, font=font_medium)
561
+ draw_text(f"战舰耐久: {lives}", WIDTH-220, 20, COLOR_TEXT_LIVES, font=font_medium)
562
+ draw_text(f"烈度: {min(10, wave_level)}", WIDTH//2-50, 20, COLOR_TEXT_SCORE, font=font_medium)
563
+ draw_text(status_message, WIDTH//2, HEIGHT-30, COLOR_TEXT_STATUS, center=True, font=font_small)
564
+
565
+ # 显示BOSS倒计时
566
+ current_time = pygame.time.get_ticks()
567
+ time_since_boss = current_time - last_boss_spawn_time
568
+ time_to_next_boss = max(0, BOSS_SPAWN_INTERVAL - time_since_boss) // 1000
569
+ draw_text(f"下一巨型BOSS: {time_to_next_boss}秒", WIDTH//2, HEIGHT-55, (200, 200, 100), center=True, font=font_tiny)
570
+
571
+ draw_text("A/D ←→ | 射击 空格/K/↑ | R 重启", WIDTH//2, HEIGHT-10, (127, 140, 141), center=True, font=font_tiny)
572
+
573
+ if not game_active:
574
+ draw_text("GAME OVER", WIDTH//2, HEIGHT//2-30, COLOR_TEXT_GAMEOVER, center=True, font=font_large)
575
+ draw_text("按下 [ R ] 重新开始", WIDTH//2, HEIGHT//2+20, (187, 221, 255), center=True, font=font_small)
576
+
577
+ pygame.display.flip()
578
+
579
+
580
+ def handle_events():
581
+ global left_pressed, right_pressed, shoot_pressed, game_active
582
+
583
+ for event in pygame.event.get():
584
+ if event.type == pygame.QUIT:
585
+ return False
586
+
587
+ elif event.type == pygame.KEYDOWN:
588
+ if event.key == pygame.K_LEFT or event.key == pygame.K_a:
589
+ left_pressed = True
590
+ elif event.key == pygame.K_RIGHT or event.key == pygame.K_d:
591
+ right_pressed = True
592
+ elif event.key == pygame.K_SPACE or event.key == pygame.K_UP or event.key == pygame.K_k:
593
+ shoot_pressed = True
594
+ elif event.key == pygame.K_r:
595
+ reset_game()
596
+ elif event.key == pygame.K_ESCAPE:
597
+ return False
598
+
599
+ elif event.type == pygame.KEYUP:
600
+ if event.key == pygame.K_LEFT or event.key == pygame.K_a:
601
+ left_pressed = False
602
+ elif event.key == pygame.K_RIGHT or event.key == pygame.K_d:
603
+ right_pressed = False
604
+ elif event.key == pygame.K_SPACE or event.key == pygame.K_UP or event.key == pygame.K_k:
605
+ shoot_pressed = False
606
+
607
+ return True
608
+
609
+
610
+ # 初始化星星
611
+ for _ in range(STAR_COUNT):
612
+ stars.append({
613
+ 'x': random.randint(0, WIDTH),
614
+ 'y': random.randint(0, HEIGHT),
615
+ 'radius': random.uniform(1, 3.2),
616
+ 'speed': random.uniform(0.2, 0.6),
617
+ 'color': (255, 240, 200)
618
+ })
619
+
620
+ reset_game()
621
+ last_boss_spawn_time = pygame.time.get_ticks()
622
+
623
+ # 主循环
624
+ def run():
625
+ global running
626
+ running = True
627
+ while running:
628
+ running = handle_events()
629
+ update()
630
+ draw()
631
+ clock.tick(FPS)
632
+ pygame.quit()
633
+ sys.exit()
634
+
635
+ if __name__ == "__main__":
636
+ run()
637
+
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: ed-game
3
+ Version: 1.0.0
4
+ Summary: Earth Defense Game
5
+ Author-email: Your Name <your@email.com>
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: pygame-ce>=2.4
11
+ Dynamic: license-file
12
+
13
+ # 地球防御 · 星辰守卫者
14
+
15
+ ## 安装
16
+
17
+ ```bash
18
+ pip install earth-defense
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/ed_game/__init__.py
5
+ src/ed_game/game_1.py
6
+ src/ed_game.egg-info/PKG-INFO
7
+ src/ed_game.egg-info/SOURCES.txt
8
+ src/ed_game.egg-info/dependency_links.txt
9
+ src/ed_game.egg-info/entry_points.txt
10
+ src/ed_game.egg-info/requires.txt
11
+ src/ed_game.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ed-game = ed_game.game_1:run
@@ -0,0 +1 @@
1
+ pygame-ce>=2.4
@@ -0,0 +1 @@
1
+ ed_game