Lukeetah commited on
Commit
5b387b8
·
verified ·
1 Parent(s): 56f5e02

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +434 -228
app.py CHANGED
@@ -1,301 +1,507 @@
1
- # app.py - VERSIÓN ÉPICA SIN ERRORES DE NUMPY
2
  import gradio as gr
3
  import time
4
  import math
5
- from PIL import Image, ImageDraw
6
  import random
 
7
  from game_engine import GauchoCharacter, PhysicsEngine, WorldGenerator, Vector2D
8
 
9
- class EpicGauchoRenderer:
10
- """Renderer épico para un gaucho que se respete"""
11
  def __init__(self):
12
- self.particle_systems = []
13
- self.background_stars = self._generate_stars(200)
14
- self.world_tiles = self._create_epic_tiles()
15
-
16
- def _generate_stars(self, count):
17
- """Genera estrellas sin usar numpy.choice con tuplas"""
18
- star_colors = [(255, 255, 255), (200, 200, 255), (255, 255, 200), (255, 200, 255)]
19
- return [(
20
- random.randint(0, 800),
21
- random.randint(0, 600),
22
- random.choice([1, 2, 3]),
23
- random.choice(star_colors) # Usamos random de Python, no numpy
24
- ) for _ in range(count)]
25
-
26
- def _create_epic_tiles(self):
27
- """Crea texturas épicas para cada tipo de terreno"""
28
- tiles = {}
29
 
30
- # Pampa cósmica con gradientes épicos
31
- pampa_base = Image.new('RGB', (100, 100), (20, 40, 80))
32
- draw = ImageDraw.Draw(pampa_base)
 
 
 
 
 
33
 
34
- # Crear patrón de campo energético
35
- for i in range(0, 100, 20):
36
- for j in range(0, 100, 20):
37
- intensity = int(50 * math.sin(i/10) * math.cos(j/10)) + 100
38
- color = (20, 40 + intensity//3, 80 + intensity//2)
39
- draw.rectangle([i, j, i+18, j+18], fill=color, outline=(0, 255, 150), width=1)
40
 
41
- tiles['pampa'] = pampa_base
42
- return tiles
 
 
 
 
 
 
 
 
 
43
 
44
- def draw_epic_gaucho(self, draw, x, y, velocity):
45
- """Dibuja al gaucho con toda la épica que se merece"""
46
- # Estela de movimiento
47
- trail_length = min(30, abs(velocity.x) + abs(velocity.y))
48
- if trail_length > 5:
49
- trail_x = int(x - velocity.x * 3)
50
- trail_y = int(y - velocity.y * 3)
51
- draw.ellipse([trail_x-10, trail_y-10, trail_x+10, trail_y+10],
52
- fill=(100, 200, 255))
 
 
 
 
 
 
 
 
53
 
54
- # Cuerpo del gaucho (más épico)
55
- x, y = int(x), int(y)
56
- draw.ellipse([x-25, y-25, x+25, y+25],
57
- fill=(139, 69, 19), outline=(101, 67, 33), width=3)
 
 
 
 
 
 
 
 
 
58
 
59
- # Poncho tecnológico
60
- poncho_points = [
61
- (x-30, y-10), (x-20, y-35), (x+20, y-35), (x+30, y-10),
62
- (x+25, y+15), (x-25, y+15)
63
- ]
64
- draw.polygon(poncho_points, fill=(0, 100, 200), outline=(0, 150, 255), width=2)
65
 
66
- # Patrón del poncho
67
- for i in range(-20, 25, 10):
68
- draw.line([(x+i, y-30), (x+i, y+10)], fill=(255, 255, 100), width=2)
 
 
 
69
 
70
- # Sombrero gaucho épico
71
- draw.ellipse([x-20, y-35, x+20, y-25], fill=(50, 25, 0), outline=(80, 40, 0), width=3)
72
- draw.ellipse([x-35, y-30, x+35, y-28], fill=(40, 20, 0), outline=(60, 30, 0), width=2)
 
 
 
 
73
 
74
- # Mate tecnológico
75
- mate_x, mate_y = x + 15, y - 5
76
- draw.ellipse([mate_x-8, mate_y-8, mate_x+8, mate_y+8],
77
- fill=(150, 75, 0), outline=(255, 200, 0), width=2)
78
- # Bombilla láser
79
- draw.line([(mate_x, mate_y-8), (mate_x, mate_y-15)], fill=(0, 255, 0), width=3)
 
 
 
 
 
 
 
 
 
80
 
81
- # Ojos determinados
82
- draw.ellipse([x-8, y-8, x-3, y-3], fill=(255, 255, 255))
83
- draw.ellipse([x+3, y-8, x+8, y-3], fill=(255, 255, 255))
84
- draw.ellipse([x-6, y-6, x-5, y-5], fill=(0, 0, 0))
85
- draw.ellipse([x+5, y-6, x+6, y-5], fill=(0, 0, 0))
86
-
87
- class CosmicGauchoEpic:
88
- def __init__(self):
89
- self.player = GauchoCharacter()
90
- self.player.position = Vector2D(400, 300) # Centro de la pantalla
91
- self.physics = PhysicsEngine()
92
- self.renderer = EpicGauchoRenderer()
93
- self.camera_offset = Vector2D(0, 0)
94
- self.crystals = self._spawn_crystals()
95
- self.enemies = []
96
- self.particles = []
97
- self.game_time = 0
98
 
99
- def _spawn_crystals(self):
100
- """Genera cristales de valores argentinos"""
101
- crystal_types = ['Solidaridad', 'Familia', 'Amistad', 'Respeto', 'Creatividad']
102
- crystals = []
103
- for i, crystal_type in enumerate(crystal_types):
104
- crystals.append({
105
- 'type': crystal_type,
106
- 'position': Vector2D(200 + i * 150, 200 + (i % 2) * 200),
107
- 'collected': False,
108
- 'pulse': i * math.pi / 3 # Desfase para que no pulsen todos juntos
 
 
 
 
 
 
 
 
 
 
 
109
  })
110
- return crystals
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
- def update_game_state(self, movement: str = "none"):
113
- self.game_time += 0.1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
- acceleration = Vector2D(0, 0)
116
- speed = 4.0
117
 
118
- if movement == "left":
119
- acceleration.x = -speed
120
- elif movement == "right":
121
- acceleration.x = speed
122
- elif movement == "up":
123
- acceleration.y = -speed * 0.8 # Gravedad simulada
124
- elif movement == "down":
125
- acceleration.y = speed
126
 
127
- # Momentum más fluido
128
- self.player.velocity = self.player.velocity * 0.95 # Fricción
129
- self.player.velocity = self.physics.apply_momentum(
130
- self.player.velocity, acceleration, dt=0.1
131
- )
132
 
133
- self.player.position += self.player.velocity * 0.1
 
134
 
135
- # Límites del mundo expandido
136
- self.player.position.x = max(50, min(self.player.position.x, 1500))
137
- self.player.position.y = max(50, min(self.player.position.y, 1200))
138
 
139
- # Cámara que sigue al jugador
140
- self.camera_offset.x = self.player.position.x - 400
141
- self.camera_offset.y = self.player.position.y - 300
142
 
143
- # Recolectar cristales
144
- self._check_crystal_collection()
 
 
 
 
 
145
 
146
- def _check_crystal_collection(self):
147
- for crystal in self.crystals:
148
- if not crystal['collected']:
149
- distance = ((self.player.position.x - crystal['position'].x) ** 2 +
150
- (self.player.position.y - crystal['position'].y) ** 2) ** 0.5
151
- if distance < 40:
152
- crystal['collected'] = True
153
- self.player.inventory['cristales'][crystal['type']] = True
154
 
155
- def render_epic_frame(self) -> Image.Image:
156
- img = Image.new('RGB', (800, 600), (5, 5, 20))
157
- draw = ImageDraw.Draw(img)
 
 
 
 
 
 
 
 
 
 
 
158
 
159
- # Fondo estrellado épico
160
- for star_x, star_y, size, color in self.renderer.background_stars:
161
- # Parallax scrolling
162
- scroll_x = int(star_x - self.camera_offset.x * 0.3)
163
- scroll_y = int(star_y - self.camera_offset.y * 0.3)
164
- if 0 <= scroll_x <= 800 and 0 <= scroll_y <= 600:
165
- draw.ellipse([scroll_x-size, scroll_y-size, scroll_x+size, scroll_y+size], fill=color)
166
 
167
- # Grid energético de la pampa cósmica
168
- grid_size = 100
169
- for x in range(-200, 1000, grid_size):
170
- for y in range(-200, 800, grid_size):
171
- screen_x = int(x - self.camera_offset.x)
172
- screen_y = int(y - self.camera_offset.y)
173
- if -100 <= screen_x <= 900 and -100 <= screen_y <= 700:
174
- # Campo energético
175
- alpha = int(50 + 30 * math.sin(self.game_time + x/100))
176
- color = (0, 50 + alpha//2, 100 + alpha)
177
- draw.rectangle([screen_x, screen_y, screen_x+90, screen_y+90],
178
- fill=color, outline=(0, 150, 255), width=1)
179
 
180
- # Cristales épicos
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  for crystal in self.crystals:
182
  if not crystal['collected']:
183
- crystal_x = int(crystal['position'].x - self.camera_offset.x)
184
- crystal_y = int(crystal['position'].y - self.camera_offset.y)
185
 
186
- if 0 <= crystal_x <= 800 and 0 <= crystal_y <= 600:
187
- # Efecto de pulso
188
- pulse = math.sin(self.game_time * 3 + crystal['pulse']) * 10
189
- size = int(20 + pulse)
 
 
 
 
190
 
191
- # Aura del cristal
192
- draw.ellipse([crystal_x-size-5, crystal_y-size-5,
193
- crystal_x+size+5, crystal_y+size+5],
194
- fill=(100, 100, 100))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
 
196
- # Cristal
197
- crystal_colors = {
198
- 'Solidaridad': (255, 100, 100),
199
- 'Familia': (100, 255, 100),
200
- 'Amistad': (100, 100, 255),
201
- 'Respeto': (255, 255, 100),
202
- 'Creatividad': (255, 100, 255)
203
- }
204
- crystal_color = crystal_colors.get(crystal['type'], (255, 255, 255))
205
 
206
- draw.polygon([
207
- (crystal_x, crystal_y-size),
208
- (crystal_x-int(size*0.7), crystal_y),
209
- (crystal_x, crystal_y+size),
210
- (crystal_x+int(size*0.7), crystal_y)
211
- ], fill=crystal_color, outline=(255, 255, 255), width=2)
212
-
213
- # Gaucho épico (siempre centrado)
214
- player_screen_x = 400
215
- player_screen_y = 300
216
- self.renderer.draw_epic_gaucho(draw, player_screen_x, player_screen_y, self.player.velocity)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
 
218
- return img
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
 
220
- def get_epic_status(self):
221
- cristales_obtenidos = sum(self.player.inventory['cristales'].values())
 
 
 
 
 
 
 
222
  return {
223
- "🗺️ Coordenadas": f"({self.player.position.x:.0f}, {self.player.position.y:.0f})",
224
- "⚡ Momentum": f"({self.player.velocity.x:.1f}, {self.player.velocity.y:.1f})",
225
- "💎 Cristales": f"{cristales_obtenidos}/5",
226
- "🎯 Misión": "Recuperar la identidad argentina" if cristales_obtenidos < 5 else "¡MISIÓN CUMPLIDA!",
227
- "🔥 Épica": "MÁXIMA"
 
228
  }
229
 
230
- def create_epic_interface():
231
- game = CosmicGauchoEpic()
232
 
233
  with gr.Blocks(
234
- title="🚀 EL GAUCHO CÓSMICO ÉPICO 🇦🇷",
235
- theme=gr.themes.Soft()
236
  ) as interface:
237
 
238
  gr.HTML("""
239
- <div style="text-align: center; color: #FFD700; font-size: 2.5em; font-weight: bold; margin: 20px;">
240
- 🌟 EL GAUCHO CÓSMICO 🌟<br>
241
- <span style="font-size: 0.6em; color: #87CEEB;">
242
- AVENTURA ÉPICA DE IDENTIDAD CULTURAL ARGENTINA
243
- </span>
 
 
 
244
  </div>
245
  """)
246
 
247
  with gr.Row():
248
- with gr.Column(scale=2):
249
  game_display = gr.Image(
250
- label="🌌 LA PAMPA CÓSMICA ÉPICA",
251
  interactive=False,
252
  width=800,
253
  height=600
254
  )
255
 
 
256
  with gr.Row():
257
- left_btn = gr.Button("⬅️ IZQUIERDA", variant="primary", size="lg")
258
- up_btn = gr.Button("⬆️ SALTAR", variant="secondary", size="lg")
259
- down_btn = gr.Button("⬇️ AGACHAR", variant="secondary", size="lg")
260
- right_btn = gr.Button("➡️ DERECHA", variant="primary", size="lg")
 
 
 
261
 
262
  with gr.Column(scale=1):
263
- gr.Markdown("### 🎯 ESTADO ÉPICO DEL GAUCHO")
264
- status_display = gr.JSON(label="📊 DATOS DE MARTÍN 'EL MATE'")
 
 
 
265
 
266
- gr.Markdown("### 🗣️ FILOSOFÍA GAUCHA CÓSMICA")
267
- wisdom_quotes = [
268
- "¡La tradición no se negocia, che!",
269
- "En esta pampa cósmica, somos resistencia pura",
270
- "Cada cristal recuperado fortalece nuestra alma",
271
- "El mate une dimensiones, hermano",
272
- "¡Vamos a mostrarles qué es ser argentino!"
273
- ]
274
 
275
- wisdom_display = gr.Textbox(
276
- value=random.choice(wisdom_quotes),
277
- label="💭 Sabiduría del Gaucho",
278
- interactive=False
 
 
279
  )
280
 
281
- def epic_move_and_update(direction):
282
- game.update_game_state(direction)
283
- return game.render_epic_frame(), game.get_epic_status()
284
-
285
- # Conectar controles épicos
286
- left_btn.click(fn=lambda: epic_move_and_update("left"), outputs=[game_display, status_display])
287
- right_btn.click(fn=lambda: epic_move_and_update("right"), outputs=[game_display, status_display])
288
- up_btn.click(fn=lambda: epic_move_and_update("up"), outputs=[game_display, status_display])
289
- down_btn.click(fn=lambda: epic_move_and_update("down"), outputs=[game_display, status_display])
290
 
291
- # Estado inicial épico
292
  interface.load(
293
- fn=lambda: (game.render_epic_frame(), game.get_epic_status()),
294
  outputs=[game_display, status_display]
295
  )
296
 
297
  return interface
298
 
299
  if __name__ == "__main__":
300
- interface = create_epic_interface()
301
- interface.launch(share=True, show_error=True, debug=True)
 
1
+ # app.py - EL GAUCHO CÓSMICO ÉPICO VERSION 2.0
2
  import gradio as gr
3
  import time
4
  import math
 
5
  import random
6
+ from PIL import Image, ImageDraw, ImageFont
7
  from game_engine import GauchoCharacter, PhysicsEngine, WorldGenerator, Vector2D
8
 
9
+ class EpicGameEngine:
 
10
  def __init__(self):
11
+ self.width = 800
12
+ self.height = 600
13
+ self.player = GauchoCharacter()
14
+ self.player.position = Vector2D(100, 400)
15
+ self.physics = PhysicsEngine()
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
+ # Sistema de juego épico
18
+ self.scroll_x = 0
19
+ self.enemies = []
20
+ self.projectiles = []
21
+ self.crystals = []
22
+ self.particles = []
23
+ self.combo_meter = 0
24
+ self.cultural_power = 100
25
 
26
+ # Estados de juego
27
+ self.game_state = "playing" # playing, dialogue, victory
28
+ self.current_level = 1
29
+ self.boss_fight = False
 
 
30
 
31
+ # Inicializar mundo épico
32
+ self._spawn_initial_world()
33
+ self._spawn_enemies()
34
+ self._spawn_crystals()
35
+
36
+ def _spawn_initial_world(self):
37
+ """Crea un mundo side-scrolling épico"""
38
+ self.terrain = []
39
+ for x in range(0, 2000, 50):
40
+ height = 450 + 50 * math.sin(x / 100)
41
+ self.terrain.append((x, int(height)))
42
 
43
+ def _spawn_enemies(self):
44
+ """Genera enemigos algorítmicos épicos"""
45
+ enemy_types = ['homogenizer', 'cultural_eraser', 'identity_fragmenter']
46
+ for i in range(5):
47
+ self.enemies.append({
48
+ 'type': random.choice(enemy_types),
49
+ 'position': Vector2D(300 + i * 200, 350),
50
+ 'health': 3,
51
+ 'pattern': 'patrol',
52
+ 'attack_timer': 0,
53
+ 'animation_frame': 0
54
+ })
55
+
56
+ def _spawn_crystals(self):
57
+ """Cristales de valores argentinos distribuidos épicamente"""
58
+ values = ['Solidaridad', 'Familia', 'Amistad', 'Respeto', 'Creatividad']
59
+ colors = [(255, 100, 100), (100, 255, 100), (100, 100, 255), (255, 255, 100), (255, 100, 255)]
60
 
61
+ for i, (value, color) in enumerate(zip(values, colors)):
62
+ self.crystals.append({
63
+ 'type': value,
64
+ 'position': Vector2D(200 + i * 300, 200 + random.randint(-100, 100)),
65
+ 'color': color,
66
+ 'collected': False,
67
+ 'pulse': 0,
68
+ 'floating_offset': random.random() * math.pi
69
+ })
70
+
71
+ def update_game(self, action):
72
+ """Motor de juego principal"""
73
+ dt = 0.016 # 60 FPS target
74
 
75
+ # Actualizar jugador
76
+ self._update_player(action, dt)
 
 
 
 
77
 
78
+ # Actualizar mundo
79
+ self._update_scroll()
80
+ self._update_enemies(dt)
81
+ self._update_projectiles(dt)
82
+ self._update_particles(dt)
83
+ self._check_collisions()
84
 
85
+ # Sistemas de juego
86
+ self._update_combo_system()
87
+ self._check_victory_condition()
88
+
89
+ def _update_player(self, action, dt):
90
+ """Sistema de movimiento fluido del gaucho"""
91
+ acceleration = Vector2D(0, 0.5) # Gravedad
92
 
93
+ if action == "left":
94
+ acceleration.x = -8
95
+ self.player.facing = "left"
96
+ elif action == "right":
97
+ acceleration.x = 8
98
+ self.player.facing = "right"
99
+ elif action == "jump":
100
+ if self.player.on_ground:
101
+ self.player.velocity.y = -12
102
+ self.player.on_ground = False
103
+ self._create_particles(self.player.position, "jump", 5)
104
+ elif action == "attack":
105
+ self._throw_boleadoras()
106
+ elif action == "special":
107
+ self._use_mate_power()
108
 
109
+ # Aplicar física fluida
110
+ self.player.velocity = self.physics.apply_momentum(
111
+ self.player.velocity, acceleration, dt
112
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
+ # Movimiento suave
115
+ self.player.position += self.player.velocity * dt * 60
116
+
117
+ # Colisión con terreno
118
+ self._handle_terrain_collision()
119
+
120
+ def _update_scroll(self):
121
+ """Cámara que sigue al jugador dinámicamente"""
122
+ target_scroll = self.player.position.x - 200
123
+ self.scroll_x += (target_scroll - self.scroll_x) * 0.1 # Smooth following
124
+
125
+ def _throw_boleadoras(self):
126
+ """Sistema de combate con boleadoras cuánticas"""
127
+ if len(self.projectiles) < 3: # Máximo 3 boleadoras activas
128
+ direction = 1 if self.player.facing == "right" else -1
129
+ self.projectiles.append({
130
+ 'position': Vector2D(self.player.position.x, self.player.position.y - 20),
131
+ 'velocity': Vector2D(direction * 15, -2),
132
+ 'type': 'boleadoras',
133
+ 'lifetime': 2.0,
134
+ 'trail': []
135
  })
136
+ self._create_particles(self.player.position, "boleadoras", 3)
137
+
138
+ def _use_mate_power(self):
139
+ """Poder especial del mate tecnológico"""
140
+ if self.cultural_power >= 50:
141
+ self.cultural_power -= 50
142
+ self.combo_meter += 20
143
+
144
+ # Efecto de área que afecta enemigos cercanos
145
+ for enemy in self.enemies:
146
+ distance = ((self.player.position.x - enemy['position'].x) ** 2 +
147
+ (self.player.position.y - enemy['position'].y) ** 2) ** 0.5
148
+ if distance < 150:
149
+ enemy['health'] -= 2
150
+ self._create_particles(enemy['position'], "mate_effect", 8)
151
 
152
+ def _create_particles(self, position, particle_type, count):
153
+ """Sistema de partículas épico"""
154
+ for _ in range(count):
155
+ self.particles.append({
156
+ 'position': Vector2D(position.x + random.randint(-10, 10),
157
+ position.y + random.randint(-10, 10)),
158
+ 'velocity': Vector2D(random.randint(-3, 3), random.randint(-5, 0)),
159
+ 'color': self._get_particle_color(particle_type),
160
+ 'lifetime': random.uniform(0.5, 1.5),
161
+ 'type': particle_type
162
+ })
163
+
164
+ def _get_particle_color(self, particle_type):
165
+ colors = {
166
+ 'jump': (100, 200, 255),
167
+ 'boleadoras': (255, 200, 100),
168
+ 'mate_effect': (0, 255, 100),
169
+ 'explosion': (255, 100, 100),
170
+ 'crystal': (255, 255, 255)
171
+ }
172
+ return colors.get(particle_type, (255, 255, 255))
173
+
174
+ def render_epic_frame(self) -> Image.Image:
175
+ """Renderizado épico con múltiples capas"""
176
+ img = Image.new('RGB', (self.width, self.height), (10, 15, 35))
177
+ draw = ImageDraw.Draw(img)
178
 
179
+ # Capa 1: Fondo estrellado con parallax
180
+ self._draw_starfield(draw)
181
 
182
+ # Capa 2: Montañas distantes
183
+ self._draw_mountains(draw)
 
 
 
 
 
 
184
 
185
+ # Capa 3: Terreno principal
186
+ self._draw_terrain(draw)
187
+
188
+ # Capa 4: Cristales con efectos
189
+ self._draw_crystals(draw)
190
 
191
+ # Capa 5: Enemigos animados
192
+ self._draw_enemies(draw)
193
 
194
+ # Capa 6: Proyectiles con trails
195
+ self._draw_projectiles(draw)
 
196
 
197
+ # Capa 7: Gaucho protagonista
198
+ self._draw_epic_gaucho(draw)
 
199
 
200
+ # Capa 8: Partículas
201
+ self._draw_particles(draw)
202
+
203
+ # Capa 9: UI épica
204
+ self._draw_ui(draw)
205
+
206
+ return img
207
 
208
+ def _draw_starfield(self, draw):
209
+ """Fondo estrellado con parallax scrolling"""
210
+ for i in range(100):
211
+ star_x = (i * 47 + self.scroll_x * 0.2) % self.width
212
+ star_y = (i * 31) % self.height
213
+ brightness = 100 + (i % 155)
214
+ draw.ellipse([star_x-1, star_y-1, star_x+1, star_y+1],
215
+ fill=(brightness, brightness, brightness))
216
 
217
+ def _draw_mountains(self, draw):
218
+ """Montañas de fondo con profundidad"""
219
+ for i in range(0, self.width + 100, 100):
220
+ mountain_x = i - (self.scroll_x * 0.5) % 100
221
+ mountain_height = 200 + 50 * math.sin((mountain_x + self.scroll_x) / 150)
222
+ draw.polygon([
223
+ (mountain_x, self.height),
224
+ (mountain_x + 50, mountain_height),
225
+ (mountain_x + 100, self.height)
226
+ ], fill=(30, 40, 80), outline=(50, 60, 120))
227
+
228
+ def _draw_terrain(self, draw):
229
+ """Terreno orgánico y fluido"""
230
+ terrain_points = [(0, self.height)]
231
 
232
+ for i in range(0, self.width, 20):
233
+ world_x = i + self.scroll_x
234
+ height = 450 + 30 * math.sin(world_x / 80) + 20 * math.cos(world_x / 120)
235
+ terrain_points.append((i, height))
 
 
 
236
 
237
+ terrain_points.append((self.width, self.height))
238
+ draw.polygon(terrain_points, fill=(20, 60, 40), outline=(40, 120, 80))
239
+
240
+ def _draw_epic_gaucho(self, draw):
241
+ """Gaucho protagonista con animaciones fluidas"""
242
+ screen_x = int(self.player.position.x - self.scroll_x)
243
+ screen_y = int(self.player.position.y)
 
 
 
 
 
244
 
245
+ if 0 <= screen_x <= self.width:
246
+ # Sombra dinámica
247
+ draw.ellipse([screen_x-15, screen_y+20, screen_x+15, screen_y+25],
248
+ fill=(0, 0, 0, 100))
249
+
250
+ # Cuerpo principal
251
+ body_color = (139, 69, 19)
252
+ draw.ellipse([screen_x-20, screen_y-30, screen_x+20, screen_y+10],
253
+ fill=body_color, outline=(101, 67, 33), width=2)
254
+
255
+ # Poncho tecnológico animado
256
+ poncho_wave = math.sin(time.time() * 5) * 3
257
+ poncho_points = [
258
+ (screen_x-25, screen_y-15 + poncho_wave),
259
+ (screen_x-15, screen_y-35),
260
+ (screen_x+15, screen_y-35),
261
+ (screen_x+25, screen_y-15 - poncho_wave),
262
+ (screen_x+20, screen_y+5),
263
+ (screen_x-20, screen_y+5)
264
+ ]
265
+ draw.polygon(poncho_points, fill=(0, 120, 200), outline=(0, 180, 255), width=2)
266
+
267
+ # Líneas energéticas del poncho
268
+ for i in range(-15, 20, 8):
269
+ line_alpha = int(255 * (0.5 + 0.5 * math.sin(time.time() * 3 + i)))
270
+ draw.line([(screen_x+i, screen_y-30), (screen_x+i, screen_y+5)],
271
+ fill=(255, 255, 100), width=1)
272
+
273
+ # Sombrero gaucho épico
274
+ hat_tilt = 2 if self.player.facing == "right" else -2
275
+ draw.ellipse([screen_x-18+hat_tilt, screen_y-45, screen_x+18+hat_tilt, screen_y-35],
276
+ fill=(50, 25, 0), outline=(100, 50, 0), width=2)
277
+ draw.ellipse([screen_x-30+hat_tilt, screen_y-40, screen_x+30+hat_tilt, screen_y-38],
278
+ fill=(40, 20, 0), outline=(80, 40, 0), width=1)
279
+
280
+ # Mate tecnológico brillante
281
+ mate_x = screen_x + (12 if self.player.facing == "right" else -12)
282
+ mate_glow = int(100 + 50 * math.sin(time.time() * 4))
283
+ draw.ellipse([mate_x-6, screen_y-10, mate_x+6, screen_y+2],
284
+ fill=(150, 75, 0), outline=(255, mate_glow, 0), width=2)
285
+
286
+ # Bombilla láser
287
+ laser_intensity = int(255 * (0.7 + 0.3 * math.sin(time.time() * 8)))
288
+ draw.line([(mate_x, screen_y-10), (mate_x, screen_y-18)],
289
+ fill=(0, laser_intensity, 0), width=3)
290
+
291
+ # Ojos expresivos
292
+ eye_offset = 2 if self.player.facing == "right" else -2
293
+ draw.ellipse([screen_x-8+eye_offset, screen_y-25, screen_x-3+eye_offset, screen_y-20],
294
+ fill=(255, 255, 255))
295
+ draw.ellipse([screen_x+3+eye_offset, screen_y-25, screen_x+8+eye_offset, screen_y-20],
296
+ fill=(255, 255, 255))
297
+ draw.ellipse([screen_x-6+eye_offset, screen_y-23, screen_x-5+eye_offset, screen_y-22],
298
+ fill=(0, 0, 0))
299
+ draw.ellipse([screen_x+5+eye_offset, screen_y-23, screen_x+6+eye_offset, screen_y-22],
300
+ fill=(0, 0, 0))
301
+
302
+ def _draw_crystals(self, draw):
303
+ """Cristales con efectos mágicos"""
304
  for crystal in self.crystals:
305
  if not crystal['collected']:
306
+ screen_x = int(crystal['position'].x - self.scroll_x)
307
+ screen_y = int(crystal['position'].y + 10 * math.sin(time.time() * 2 + crystal['floating_offset']))
308
 
309
+ if -50 <= screen_x <= self.width + 50:
310
+ # Aura brillante
311
+ aura_size = 25 + 8 * math.sin(time.time() * 3 + crystal['pulse'])
312
+ for i in range(3):
313
+ alpha = 50 - i * 15
314
+ draw.ellipse([screen_x-aura_size+i*5, screen_y-aura_size+i*5,
315
+ screen_x+aura_size-i*5, screen_y+aura_size-i*5],
316
+ outline=crystal['color'])
317
 
318
+ # Cristal central
319
+ crystal_size = 15 + 5 * math.sin(time.time() * 4 + crystal['pulse'])
320
+ draw.polygon([
321
+ (screen_x, screen_y-crystal_size),
322
+ (screen_x-crystal_size*0.7, screen_y-crystal_size*0.3),
323
+ (screen_x-crystal_size*0.7, screen_y+crystal_size*0.3),
324
+ (screen_x, screen_y+crystal_size),
325
+ (screen_x+crystal_size*0.7, screen_y+crystal_size*0.3),
326
+ (screen_x+crystal_size*0.7, screen_y-crystal_size*0.3)
327
+ ], fill=crystal['color'], outline=(255, 255, 255), width=2)
328
+
329
+ def _draw_enemies(self, draw):
330
+ """Enemigos algorítmicos con IA visual"""
331
+ for enemy in self.enemies:
332
+ if enemy['health'] > 0:
333
+ screen_x = int(enemy['position'].x - self.scroll_x)
334
+ screen_y = int(enemy['position'].y)
335
+
336
+ if -50 <= screen_x <= self.width + 50:
337
+ # Forma algorítmica hostil
338
+ if enemy['type'] == 'homogenizer':
339
+ # Cubo gris hostil
340
+ draw.rectangle([screen_x-15, screen_y-15, screen_x+15, screen_y+15],
341
+ fill=(80, 80, 80), outline=(120, 120, 120), width=2)
342
+ # Patrón de homogenización
343
+ for i in range(-10, 15, 5):
344
+ draw.line([(screen_x+i, screen_y-15), (screen_x+i, screen_y+15)],
345
+ fill=(150, 150, 150), width=1)
346
 
347
+ elif enemy['type'] == 'cultural_eraser':
348
+ # Forma que "borra" la cultura
349
+ draw.polygon([
350
+ (screen_x, screen_y-20),
351
+ (screen_x-15, screen_y),
352
+ (screen_x, screen_y+20),
353
+ (screen_x+15, screen_y)
354
+ ], fill=(100, 50, 50), outline=(150, 100, 100), width=2)
 
355
 
356
+ # Barras de vida
357
+ health_width = (enemy['health'] / 3) * 30
358
+ draw.rectangle([screen_x-15, screen_y-25, screen_x+15, screen_y-22],
359
+ fill=(50, 50, 50))
360
+ draw.rectangle([screen_x-15, screen_y-25, screen_x-15+health_width, screen_y-22],
361
+ fill=(255, 100, 100))
362
+
363
+ def _draw_particles(self, draw):
364
+ """Sistema de partículas dinámico"""
365
+ for particle in self.particles:
366
+ if particle['lifetime'] > 0:
367
+ screen_x = int(particle['position'].x - self.scroll_x)
368
+ screen_y = int(particle['position'].y)
369
+
370
+ if 0 <= screen_x <= self.width and 0 <= screen_y <= self.height:
371
+ alpha = int(255 * (particle['lifetime'] / 1.5))
372
+ size = max(1, int(3 * particle['lifetime']))
373
+ draw.ellipse([screen_x-size, screen_y-size, screen_x+size, screen_y+size],
374
+ fill=particle['color'])
375
+
376
+ def _draw_ui(self, draw):
377
+ """Interfaz épica superpuesta"""
378
+ # Barra de poder cultural
379
+ draw.rectangle([20, 20, 220, 40], fill=(50, 50, 50), outline=(150, 150, 150), width=2)
380
+ cultural_width = int((self.cultural_power / 100) * 200)
381
+ draw.rectangle([20, 20, 20 + cultural_width, 40], fill=(0, 200, 100))
382
 
383
+ # Combo meter
384
+ if self.combo_meter > 0:
385
+ combo_height = min(100, self.combo_meter * 2)
386
+ draw.rectangle([self.width - 50, self.height - 120, self.width - 30, self.height - 20],
387
+ fill=(50, 50, 50), outline=(200, 200, 200), width=2)
388
+ draw.rectangle([self.width - 50, self.height - 20 - combo_height,
389
+ self.width - 30, self.height - 20], fill=(255, 200, 0))
390
+
391
+ class GauchoCosmicGame:
392
+ def __init__(self):
393
+ self.engine = EpicGameEngine()
394
+ self.last_action = "none"
395
+ self.game_stats = {
396
+ 'crystals_collected': 0,
397
+ 'enemies_defeated': 0,
398
+ 'cultural_combos': 0,
399
+ 'epic_level': 'MÁXIMO'
400
+ }
401
+
402
+ def process_input(self, action):
403
+ """Procesa input del jugador"""
404
+ self.last_action = action
405
+ self.engine.update_game(action)
406
+ self._update_stats()
407
+ return self.engine.render_epic_frame(), self.get_status()
408
 
409
+ def _update_stats(self):
410
+ """Actualiza estadísticas del juego"""
411
+ self.game_stats['crystals_collected'] = sum(
412
+ 1 for crystal in self.engine.crystals if crystal['collected']
413
+ )
414
+ self.game_stats['cultural_combos'] = self.engine.combo_meter // 10
415
+
416
+ def get_status(self):
417
+ """Estado épico del juego"""
418
  return {
419
+ "🗺️ Posición": f"X: {self.engine.player.position.x:.0f}",
420
+ "⚡ Velocidad": f"{abs(self.engine.player.velocity.x):.1f} km/h",
421
+ "💎 Cristales": f"{self.game_stats['crystals_collected']}/5",
422
+ "🔥 Poder Cultural": f"{self.engine.cultural_power:.0f}%",
423
+ "🎯 Combos": self.game_stats['cultural_combos'],
424
+ "🚀 Épica": self.game_stats['epic_level']
425
  }
426
 
427
+ def create_ultimate_interface():
428
+ game = GauchoCosmicGame()
429
 
430
  with gr.Blocks(
431
+ title="🎮 EL GAUCHO CÓSMICO - ULTIMATE EDITION 🇦🇷",
432
+ theme=gr.themes.Glass()
433
  ) as interface:
434
 
435
  gr.HTML("""
436
+ <div style="text-align: center; background: linear-gradient(45deg, #FF6B6B, #4ECDC4);
437
+ color: white; padding: 20px; border-radius: 10px; margin: 10px;">
438
+ <h1 style="font-size: 3em; margin: 0; text-shadow: 2px 2px 4px rgba(0,0,0,0.5);">
439
+ 🌟 EL GAUCHO CÓSMICO 🌟
440
+ </h1>
441
+ <p style="font-size: 1.2em; margin: 10px 0;">
442
+ AVENTURA SIDE-SCROLLING DE IDENTIDAD CULTURAL ARGENTINA
443
+ </p>
444
  </div>
445
  """)
446
 
447
  with gr.Row():
448
+ with gr.Column(scale=3):
449
  game_display = gr.Image(
450
+ label="🌌 PAMPA CÓSMICA ÉPICA",
451
  interactive=False,
452
  width=800,
453
  height=600
454
  )
455
 
456
+ gr.Markdown("### 🎮 CONTROLES ÉPICOS")
457
  with gr.Row():
458
+ left_btn = gr.Button("⬅️ IZQUIERDA", variant="primary")
459
+ right_btn = gr.Button("➡️ DERECHA", variant="primary")
460
+ jump_btn = gr.Button("⬆️ SALTAR", variant="secondary")
461
+
462
+ with gr.Row():
463
+ attack_btn = gr.Button("🪃 BOLEADORAS", variant="stop")
464
+ special_btn = gr.Button("🧉 MATE POWER", variant="stop")
465
 
466
  with gr.Column(scale=1):
467
+ gr.Markdown("### 📊 ESTADO ÉPICO")
468
+ status_display = gr.JSON(
469
+ label="🎯 MÉTRICAS DE MARTÍN 'EL MATE'",
470
+ value=game.get_status()
471
+ )
472
 
473
+ gr.Markdown("### 🎵 SOUNDTRACK")
474
+ gr.HTML("""
475
+ <div style="background: #2C3E50; padding: 15px; border-radius: 8px; color: white;">
476
+ <p>🎼 <strong>Ahora sonando:</strong></p>
477
+ <p style="font-style: italic;">"Pampa Cósmica - Tema Principal"</p>
478
+ <p>🎸 Género: Folklectrónica Argentina</p>
479
+ </div>
480
+ """)
481
 
482
+ gr.Markdown("### 💬 DIÁLOGOS ÉPICOS")
483
+ dialogue_box = gr.Textbox(
484
+ value="¡Che! ¿Viste esos algoritmos tratando de borrar nuestra cultura? ¡Ni en pedo se la llevo!",
485
+ label="🗨️ Martín 'El Mate' habla",
486
+ interactive=False,
487
+ max_lines=3
488
  )
489
 
490
+ # Conectar controles
491
+ left_btn.click(fn=lambda: game.process_input("left"), outputs=[game_display, status_display])
492
+ right_btn.click(fn=lambda: game.process_input("right"), outputs=[game_display, status_display])
493
+ jump_btn.click(fn=lambda: game.process_input("jump"), outputs=[game_display, status_display])
494
+ attack_btn.click(fn=lambda: game.process_input("attack"), outputs=[game_display, status_display])
495
+ special_btn.click(fn=lambda: game.process_input("special"), outputs=[game_display, status_display])
 
 
 
496
 
497
+ # Inicialización
498
  interface.load(
499
+ fn=lambda: (game.engine.render_epic_frame(), game.get_status()),
500
  outputs=[game_display, status_display]
501
  )
502
 
503
  return interface
504
 
505
  if __name__ == "__main__":
506
+ interface = create_ultimate_interface()
507
+ interface.launch(share=True, show_error=True)