import tkinter as tk
from tkinter import messagebox, font
import random
import time
import threading
import math
class Game:
def __init__(self, root):
self.root = root
self.root.title("彩虹六号:炸弹冲锋2D简化版")
self.root.geometry("1024x768")
self.root.resizable(False, False)
self.font_family = "SimHei"
self.init_game_states()
self.obstacles = self.load_obstacles()
self.bank_structure = self.load_bank_structure()
self.create_start_screen()
def init_game_states(self):
self.game_active = False
self.defender_turn = self.attacker_turn = False
self.bomb_planted = self.bomb_defused = False
self.defender_wins = self.attacker_wins = False
self.defense_timer = 60
self.game_timer = 300
self.timer_running = False
self.player_count, self.ai_count = 1, 4
self.defenders = self.attackers = []
self.defense_objects = []
self.bomb_site = {"x": 512, "y": 384, "radius": 50}
self.defuser = None
self.player_speed = 5
self.key_pressed = {"w": False, "a": False, "s": False, "d": False}
self.mouse_x = self.mouse_y = 512
self.player_direction = 0
def load_obstacles(self):
return [
(50, 50, 974, 100), (50, 618, 974, 668),
(50, 50, 100, 618), (924, 50, 974, 618),
(200, 200, 250, 250), (400, 200, 450, 250),
(600, 200, 650, 250), (800, 200, 850, 250),
(200, 400, 250, 450), (400, 400, 450, 450),
(600, 400, 650, 450), (800, 400, 850, 450),
(450, 300, 574, 350), (450, 424, 574, 474),
(450, 300, 500, 474), (524, 300, 574, 474),
(475, 325, 550, 450),
(300, 150, 350, 300), (650, 150, 700, 300),
(300, 400, 350, 550), (650, 400, 700, 550),
(350, 250, 674, 300),
(150, 150, 200, 200, "breakable"),
(824, 150, 874, 200, "breakable"),
(150, 450, 200, 500, "breakable"),
(824, 450, 874, 500, "breakable"),
]
def load_bank_structure(self):
return {
"floors": 2,
"elevators": [{"x": 200, "y": 300, "width": 50, "height": 50},
{"x": 800, "y": 300, "width": 50, "height": 50}],
"doors": [{"x": 100, "y": 300, "width": 50, "height": 50, "state": "closed"},
{"x": 924, "y": 300, "width": 50, "height": 50, "state": "closed"},
{"x": 500, "y": 300, "width": 24, "height": 50, "state": "locked"}]
}
def create_start_screen(self):
for widget in self.root.winfo_children(): widget.destroy()
start_frame = tk.Frame(self.root, bg="#1a1a2e")
start_frame.pack(fill=tk.BOTH, expand=True)
title_font = font.Font(family=self.font_family, size=36, weight="bold")
tk.Label(start_frame, text="彩虹六号:炸弹冲锋2D简化版", font=title_font, fg="#e94560", bg="#1a1a2e").pack(pady=50)
button_frame = tk.Frame(start_frame, bg="#1a1a2e")
button_frame.pack(pady=100)
tk.Button(button_frame, text="选择防守方", command=self.select_defender,
font=(self.font_family, 16), bg="#4CAF50", fg="white", width=15, height=2, cursor="hand2").pack(side=tk.LEFT, padx=20)
tk.Button(button_frame, text="选择进攻方", command=self.select_attacker,
font=(self.font_family, 16), bg="#F44336", fg="white", width=15, height=2, cursor="hand2").pack(side=tk.LEFT, padx=20)
def select_defender(self):
self.defender_turn = True
self.create_game_screen()
self.initialize_players("defender")
self.start_defense_prep_phase()
self.setup_controls()
def select_attacker(self):
self.attacker_turn = True
self.create_game_screen()
self.initialize_players("attacker")
self.start_attack_phase()
self.setup_controls()
def create_game_screen(self):
for widget in self.root.winfo_children(): widget.destroy()
self.game_frame = tk.Frame(self.root, bg="#0f3460")
self.game_frame.pack(fill=tk.BOTH, expand=True)
self.canvas = tk.Canvas(self.game_frame, bg="#16213e", width=1024, height=668)
self.canvas.pack()
self.draw_bank_map()
self.status_frame = tk.Frame(self.game_frame, bg="#0f3460", height=100)
self.status_frame.pack(fill=tk.X)
self.timer_label = tk.Label(self.status_frame, text="01:00", font=(self.font_family, 24), fg="white", bg="#0f3460")
self.timer_label.pack(side=tk.LEFT, padx=20)
def draw_bank_map(self):
self.canvas.create_rectangle(50, 50, 974, 618, fill="#2c497f", outline="#e94560", width=2)
self.canvas.create_rectangle(100, 100, 924, 568, fill="#16213e", outline="#2c497f", width=5)
for x in [200, 400, 600, 800] for y in [200, 400]:
self.canvas.create_oval(x-20, y-20, x+20, y+20, fill="#d0d0d0", outline="#a0a0a0")
self.canvas.create_rectangle(x-15, y-20, x+15, y+100, fill="#e0e0e0", outline="#c0c0c0")
self.canvas.create_oval(x-20, y+100, x+20, y+120, fill="#d0d0d0", outline="#a0a0a0")
self.canvas.create_text(512, 75, text="BANK OF RAINBOW", font=(self.font_family, 24, "bold"), fill="#e94560")
self.canvas.create_rectangle(*self.bomb_site_coords(), fill="#1a1a2e", outline="#e94560", width=3)
def bomb_site_coords(self):
return (self.bomb_site["x"] - self.bomb_site["radius"],
self.bomb_site["y"] - self.bomb_site["radius"],
self.bomb_site["x"] + self.bomb_site["radius"],
self.bomb_site["y"] + self.bomb_site["radius"])
def initialize_players(self, role):
self.defenders = self.attackers = []
if role == "defender":
self.add_player("defender", (512, 384))
for _ in range(self.ai_count): self.add_ai("defender")
else:
self.add_player("attacker", (100, 334))
for _ in range(self.ai_count): self.add_ai("attacker", is_left=random.choice([True, False]))
def add_player(self, role, pos):
unit = {
"id": 0, "x": pos[0], "y": pos[1], "health": 100, "is_player": True,
"role": role, "state": "idle", "path": [], "target": None,
"color": "blue" if role == "defender" else "red"
}
self.draw_unit(unit)
(self.defenders if role == "defender" else self.attackers).append(unit)
def add_ai(self, role, is_left=True):
x, y = (100, random.randint(284, 384)) if is_left else (924, random.randint(284, 384))
unit = {
"id": len(self.defenders)+len(self.attackers), "x": x, "y": y, "health": 100, "is_player": False,
"role": role, "state": "patrol", "path": [], "target": None,
"color": "blue" if role == "defender" else "red",
"difficulty": random.randint(1, 3), "behavior": random.choice(["aggressive", "cautious"])
}
self.draw_unit(unit)
(self.defenders if role == "defender" else self.attackers).append(unit)
def draw_unit(self, unit):
size = 15 if unit["is_player"] else 10
unit["canvas_obj"] = self.canvas.create_oval(
unit["x"] - size, unit["y"] - size,
unit["x"] + size, unit["y"] + size,
fill=unit["color"]
)
self.canvas.create_text(unit["x"], unit["y"], text=str(unit["id"]), fill="white", font=(self.font_family, 10))
def setup_controls(self):
self.root.bind("<KeyPress>", self.on_key_press)
self.root.bind("<KeyRelease>", self.on_key_release)
self.canvas.bind("<Motion>", self.on_mouse_move)
self.canvas.bind("<Button-1>", self.on_mouse_click)
def on_key_press(self, event):
if event.keysym.lower() in self.key_pressed:
self.key_pressed[event.keysym.lower()] = True
def on_key_release(self, event):
if event.keysym.lower() in self.key_pressed:
self.key_pressed[event.keysym.lower()] = False
def on_mouse_move(self, event):
self.mouse_x, self.mouse_y = event.x, event.y
self.update_player_direction()
def update_player_direction(self):
if not self.defenders and not self.attackers: return
player = self.defenders[0] if self.defender_turn else self.attackers[0]
dx = self.mouse_x - player["x"]
dy = self.mouse_y - player["y"]
player["direction"] = math.atan2(dy, dx)
self.update_aim_indicator(player)
def update_aim_indicator(self, player):
if "direction_line" in player:
self.canvas.delete(player["direction_line"])
player["direction_line"] = self.canvas.create_line(
player["x"], player["y"],
player["x"] + math.cos(player["direction"]) * 20,
player["y"] + math.sin(player["direction"]) * 20,
fill="yellow", width=2
)
def on_mouse_click(self, event):
if not self.game_active: return
player = self.defenders[0] if self.defender_turn else self.attackers[0]
self.shoot(player, event.x, event.y)
def shoot(self, shooter, mx, my):
angle = math.atan2(my - shooter["y"], mx - shooter["x"])
dx, dy = math.cos(angle) * 300, math.sin(angle) * 300
x1, y1, x2, y2 = shooter["x"], shooter["y"], shooter["x"]+dx, shooter["y"]+dy
for obs in self.obstacles:
if len(obs) >= 4 and self.line_intersects_rect(x1, y1, x2, y2, *obs[:4]):
x2, y2 = self.get_intersection(x1, y1, x2, y2, *obs[:4])
break
self.canvas.create_line(x1, y1, x2, y2, fill="yellow", width=2)
self.damage_nearby_units(shooter, x2, y2, angle)
def damage_nearby_units(self, shooter, x, y, angle):
targets = self.defenders if shooter["role"] == "attacker" else self.attackers
for target in targets:
if target["health"] <= 0: continue
dist = math.hypot(x - target["x"], y - target["y"])
if dist < 20 and self.has_line_of_sight(shooter, target):
target["health"] -= 25
if target["health"] <= 0: self.delete_unit(target)
def has_line_of_sight(self, unit1, unit2):
x1, y1, x2, y2 = unit1["x"], unit1["y"], unit2["x"], unit2["y"]
for obs in self.obstacles:
if len(obs) >= 4 and self.line_intersects_rect(x1, y1, x2, y2, *obs[:4]):
return False
return True
def line_intersects_rect(self, x1, y1, x2, y2, rx1, ry1, rx2, ry2):
dx = x2 - x1; dy = y2 - y1
t = [(rx1 - x1)/dx, (rx2 - x1)/dx] if dx != 0 else [0, 0]
u = [(ry1 - y1)/dy, (ry2 - y1)/dy] if dy != 0 else [0, 0]
tmin = max(min(t), min(u))
tmax = min(max(t), max(u))
return tmin <= tmax and tmin >= 0 and tmax <= 1
def get_intersection(self, x1, y1, x2, y2, rx1, ry1, rx2, ry2):
dx = x2 - x1; dy = y2 - y1
t = ( (rx1 - x1)*dy - (ry1 - y1)*dx, (rx2 - x1)*dy - (ry2 - y1)*dx )
if dx != 0: t = (t[0]/dx, t[1]/dx)
if dy != 0: t = (t[0]/dy, t[1]/dy)
t = max(min(t), 0) if dx != 0 or dy != 0 else 0
return x1 + dx*t, y1 + dy*t
def delete_unit(self, unit):
self.canvas.delete(unit["canvas_obj"])
if "direction_line"