Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,507 +1,592 @@
|
|
| 1 |
-
#
|
| 2 |
import gradio as gr
|
| 3 |
import time
|
| 4 |
import math
|
| 5 |
import random
|
| 6 |
-
from PIL import Image, ImageDraw
|
| 7 |
-
from
|
| 8 |
|
| 9 |
-
class
|
| 10 |
def __init__(self):
|
| 11 |
-
|
| 12 |
-
self.height = 600
|
| 13 |
-
self.player =
|
| 14 |
-
self.
|
| 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 |
-
#
|
| 115 |
-
self.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
|
| 117 |
-
#
|
| 118 |
-
self.
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 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
|
| 153 |
-
"""
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
|
| 164 |
-
def
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
}
|
| 172 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
|
| 174 |
-
def
|
| 175 |
-
"""
|
| 176 |
-
img = Image.new('RGB', (
|
| 177 |
draw = ImageDraw.Draw(img)
|
| 178 |
|
| 179 |
-
#
|
| 180 |
-
self.
|
| 181 |
|
| 182 |
-
#
|
| 183 |
-
self.
|
| 184 |
|
| 185 |
-
#
|
| 186 |
-
self.
|
| 187 |
|
| 188 |
-
#
|
| 189 |
-
self.
|
| 190 |
|
| 191 |
-
#
|
| 192 |
-
self.
|
| 193 |
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
|
|
|
| 199 |
|
| 200 |
-
#
|
| 201 |
-
|
|
|
|
|
|
|
|
|
|
| 202 |
|
| 203 |
-
#
|
| 204 |
-
|
|
|
|
|
|
|
|
|
|
| 205 |
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 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
|
| 218 |
-
"""
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
|
| 228 |
-
def
|
| 229 |
-
"""
|
| 230 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
height = 450 + 30 * math.sin(world_x / 80) + 20 * math.cos(world_x / 120)
|
| 235 |
-
terrain_points.append((i, height))
|
| 236 |
|
| 237 |
-
|
| 238 |
-
draw.
|
|
|
|
| 239 |
|
| 240 |
-
def
|
| 241 |
-
"""
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 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 |
-
#
|
| 256 |
-
|
| 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 |
-
#
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 272 |
|
| 273 |
-
#
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
|
| 280 |
-
#
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
draw.
|
| 284 |
-
fill=(150, 75, 0), outline=(255, mate_glow, 0), width=2)
|
| 285 |
|
| 286 |
-
#
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
|
|
|
|
|
|
| 290 |
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 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
|
| 377 |
-
"""
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 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
|
| 392 |
def __init__(self):
|
| 393 |
-
self.engine =
|
| 394 |
-
self.
|
| 395 |
-
self.
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 400 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 401 |
|
| 402 |
-
def
|
| 403 |
-
"""Procesa
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 408 |
|
| 409 |
-
def
|
| 410 |
-
"""
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 415 |
|
| 416 |
-
def
|
| 417 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 418 |
return {
|
| 419 |
-
"
|
| 420 |
-
"
|
| 421 |
-
"
|
| 422 |
-
"
|
| 423 |
-
"
|
| 424 |
-
"
|
|
|
|
|
|
|
|
|
|
| 425 |
}
|
| 426 |
|
| 427 |
-
def
|
| 428 |
-
game =
|
| 429 |
|
| 430 |
with gr.Blocks(
|
| 431 |
-
title="
|
| 432 |
-
theme=gr.themes.
|
| 433 |
) as interface:
|
| 434 |
|
| 435 |
gr.HTML("""
|
| 436 |
-
<div style="
|
| 437 |
-
|
| 438 |
-
<h1 style="font-size: 3em;
|
| 439 |
-
|
| 440 |
</h1>
|
| 441 |
-
<
|
| 442 |
-
|
|
|
|
|
|
|
|
|
|
| 443 |
</p>
|
| 444 |
</div>
|
| 445 |
""")
|
| 446 |
|
| 447 |
with gr.Row():
|
| 448 |
-
with gr.Column(scale=
|
| 449 |
game_display = gr.Image(
|
| 450 |
-
label="
|
| 451 |
-
|
| 452 |
-
width=800,
|
| 453 |
height=600
|
| 454 |
)
|
| 455 |
|
| 456 |
-
gr.Markdown("### 🎮
|
| 457 |
with gr.Row():
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
with gr.Row():
|
| 463 |
-
|
| 464 |
-
|
|
|
|
| 465 |
|
| 466 |
with gr.Column(scale=1):
|
| 467 |
-
gr.Markdown("### 📊 ESTADO
|
| 468 |
status_display = gr.JSON(
|
| 469 |
-
label="🎯
|
| 470 |
-
value=game.
|
| 471 |
)
|
| 472 |
|
| 473 |
-
gr.Markdown("###
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 474 |
gr.HTML("""
|
| 475 |
-
<div style="background: #
|
| 476 |
-
<p>
|
| 477 |
-
<
|
| 478 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 491 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 496 |
|
| 497 |
# Inicialización
|
| 498 |
interface.load(
|
| 499 |
-
fn=lambda: (game.
|
| 500 |
-
|
|
|
|
|
|
|
| 501 |
)
|
| 502 |
|
| 503 |
return interface
|
| 504 |
|
| 505 |
if __name__ == "__main__":
|
| 506 |
-
interface =
|
| 507 |
interface.launch(share=True, show_error=True)
|
|
|
|
| 1 |
+
# main_game.py - CRISIS Y CORAJE: EL JUEGO ARGENTINO DEFINITIVO
|
| 2 |
import gradio as gr
|
| 3 |
import time
|
| 4 |
import math
|
| 5 |
import random
|
| 6 |
+
from PIL import Image, ImageDraw
|
| 7 |
+
from typing import Dict, List, Tuple
|
| 8 |
|
| 9 |
+
class ArgentinaGameEngine:
|
| 10 |
def __init__(self):
|
| 11 |
+
# Core del juego
|
| 12 |
+
self.width, self.height = 900, 600
|
| 13 |
+
self.player = ArgentineCharacter()
|
| 14 |
+
self.world = ArgentineWorld()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
+
# Sistema de supervivencia argentino
|
| 17 |
+
self.resources = {
|
| 18 |
+
'pesos': 1000,
|
| 19 |
+
'dolares': 50,
|
| 20 |
+
'comida': 80,
|
| 21 |
+
'esperanza': 100,
|
| 22 |
+
'solidaridad': 90,
|
| 23 |
+
'trabajo': 70
|
| 24 |
+
}
|
| 25 |
|
| 26 |
+
# Mecánicas únicas argentinas
|
| 27 |
+
self.inflation_rate = 1.0
|
| 28 |
+
self.social_network = []
|
| 29 |
+
self.cultural_events = []
|
| 30 |
+
self.crisis_level = 1
|
| 31 |
+
|
| 32 |
+
# Estados del juego dinámicos
|
| 33 |
+
self.day = 1
|
| 34 |
+
self.season = "verano"
|
| 35 |
+
self.current_crisis = "corralito_2001"
|
| 36 |
+
self.neighborhood_mood = "esperanzado"
|
| 37 |
+
|
| 38 |
+
self.initialize_argentina()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
+
def initialize_argentina(self):
|
| 41 |
+
"""Inicializa el mundo argentino auténtico"""
|
| 42 |
+
self.create_neighborhoods()
|
| 43 |
+
self.spawn_cultural_events()
|
| 44 |
+
self.setup_crisis_timeline()
|
| 45 |
+
|
| 46 |
+
class ArgentineCharacter:
|
| 47 |
+
def __init__(self):
|
| 48 |
+
self.name = "Carlos"
|
| 49 |
+
self.profession = random.choice(["albañil", "docente", "comerciante", "empleado_publico"])
|
| 50 |
+
self.position = [450, 300] # Centro de CABA
|
| 51 |
+
self.family = {
|
| 52 |
+
'spouse': True,
|
| 53 |
+
'children': random.randint(1, 3),
|
| 54 |
+
'elderly_parents': random.choice([True, False])
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
# Skills argentinos únicos
|
| 58 |
+
self.skills = {
|
| 59 |
+
'rebusque': 70,
|
| 60 |
+
'mate_sharing': 90,
|
| 61 |
+
'asado_expertise': 80,
|
| 62 |
+
'crisis_survival': 60,
|
| 63 |
+
'soccer_knowledge': 95,
|
| 64 |
+
'political_debate': 75
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
self.current_strategy = "supervivencia_diaria"
|
| 68 |
+
self.stress_level = 30
|
| 69 |
+
self.community_reputation = 50
|
| 70 |
+
|
| 71 |
+
class CrisisManager:
|
| 72 |
+
"""Sistema que simula las crisis argentinas reales"""
|
| 73 |
+
def __init__(self):
|
| 74 |
+
self.crisis_timeline = {
|
| 75 |
+
2001: {
|
| 76 |
+
'name': 'Corralito',
|
| 77 |
+
'effects': {'peso_devaluation': 300, 'unemployment': 25, 'social_unrest': 80},
|
| 78 |
+
'opportunities': ['trueque', 'economia_popular', 'solidaridad_vecinal']
|
| 79 |
+
},
|
| 80 |
+
2008: {
|
| 81 |
+
'name': 'Crisis Mundial',
|
| 82 |
+
'effects': {'export_drop': 40, 'inflation': 25},
|
| 83 |
+
'opportunities': ['mercado_interno', 'cooperativas']
|
| 84 |
+
},
|
| 85 |
+
2018: {
|
| 86 |
+
'name': 'Crisis Cambiaria',
|
| 87 |
+
'effects': {'peso_devaluation': 100, 'inflation': 50},
|
| 88 |
+
'opportunities': ['dolarizacion_popular', 'emprendedurismo']
|
| 89 |
+
},
|
| 90 |
+
2020: {
|
| 91 |
+
'name': 'Pandemia',
|
| 92 |
+
'effects': {'lockdown': 90, 'unemployment': 15, 'solidarity_boost': 120},
|
| 93 |
+
'opportunities': ['delivery', 'remote_work', 'community_support']
|
| 94 |
+
}
|
| 95 |
+
}
|
| 96 |
|
| 97 |
+
def apply_crisis_effects(self, game_state, current_year):
|
| 98 |
+
"""Aplica efectos realistas de las crisis"""
|
| 99 |
+
if current_year in self.crisis_timeline:
|
| 100 |
+
crisis = self.crisis_timeline[current_year]
|
| 101 |
+
return self.modify_game_state(game_state, crisis)
|
| 102 |
+
return game_state
|
| 103 |
+
|
| 104 |
+
class NeighborhoodSystem:
|
| 105 |
+
"""Sistema de barrios argentinos dinámicos"""
|
| 106 |
+
def __init__(self):
|
| 107 |
+
self.neighborhoods = {
|
| 108 |
+
'villa_31': {
|
| 109 |
+
'solidarity': 95, 'resources': 30, 'creativity': 90,
|
| 110 |
+
'challenges': ['infraestructura', 'estigma_social'],
|
| 111 |
+
'strengths': ['union_vecinal', 'cultura_popular']
|
| 112 |
+
},
|
| 113 |
+
'palermo': {
|
| 114 |
+
'solidarity': 60, 'resources': 85, 'creativity': 75,
|
| 115 |
+
'challenges': ['gentrificacion', 'burbuja_social'],
|
| 116 |
+
'strengths': ['startups', 'vida_nocturna']
|
| 117 |
+
},
|
| 118 |
+
'san_telmo': {
|
| 119 |
+
'solidarity': 80, 'resources': 70, 'creativity': 95,
|
| 120 |
+
'challenges': ['turismo_masivo'],
|
| 121 |
+
'strengths': ['tango', 'arte', 'historia']
|
| 122 |
+
},
|
| 123 |
+
'mataderos': {
|
| 124 |
+
'solidarity': 90, 'resources': 50, 'creativity': 85,
|
| 125 |
+
'challenges': ['infraestructura'],
|
| 126 |
+
'strengths': ['folklore', 'tradicion_gaucha']
|
| 127 |
+
}
|
| 128 |
}
|
| 129 |
+
|
| 130 |
+
class GameRenderer:
|
| 131 |
+
def __init__(self):
|
| 132 |
+
self.current_view = "neighborhood"
|
| 133 |
+
self.animation_frame = 0
|
| 134 |
|
| 135 |
+
def render_argentina_frame(self, game_state) -> Image.Image:
|
| 136 |
+
"""Renderiza Argentina de manera épica y auténtica"""
|
| 137 |
+
img = Image.new('RGB', (900, 600), (20, 30, 60))
|
| 138 |
draw = ImageDraw.Draw(img)
|
| 139 |
|
| 140 |
+
# Fondo: Skyline de Buenos Aires
|
| 141 |
+
self.draw_buenos_aires_skyline(draw, game_state)
|
| 142 |
|
| 143 |
+
# Primer plano: Vecindario actual
|
| 144 |
+
self.draw_neighborhood(draw, game_state)
|
| 145 |
|
| 146 |
+
# Personajes: Vecinos interactuando
|
| 147 |
+
self.draw_community_life(draw, game_state)
|
| 148 |
|
| 149 |
+
# UI: Información de supervivencia
|
| 150 |
+
self.draw_survival_ui(draw, game_state)
|
| 151 |
|
| 152 |
+
# Efectos especiales: Crisis o celebraciones
|
| 153 |
+
self.draw_dynamic_effects(draw, game_state)
|
| 154 |
|
| 155 |
+
return img
|
| 156 |
+
|
| 157 |
+
def draw_buenos_aires_skyline(self, draw, game_state):
|
| 158 |
+
"""Dibuja el skyline icónico de Buenos Aires"""
|
| 159 |
+
# Río de la Plata
|
| 160 |
+
draw.rectangle([0, 400, 900, 600], fill=(30, 60, 120))
|
| 161 |
|
| 162 |
+
# Puerto Madero
|
| 163 |
+
for i in range(5):
|
| 164 |
+
building_height = 250 + random.randint(-50, 50)
|
| 165 |
+
draw.rectangle([700 + i*30, 600-building_height, 720 + i*30, 600],
|
| 166 |
+
fill=(40, 40, 60), outline=(80, 80, 100))
|
| 167 |
|
| 168 |
+
# Microcentro
|
| 169 |
+
for i in range(8):
|
| 170 |
+
building_height = 200 + random.randint(-100, 100)
|
| 171 |
+
draw.rectangle([200 + i*40, 600-building_height, 230 + i*40, 600],
|
| 172 |
+
fill=(60, 60, 80), outline=(100, 100, 120))
|
| 173 |
|
| 174 |
+
# Obelisco (si estamos en el centro)
|
| 175 |
+
if game_state.get('location') == 'microcentro':
|
| 176 |
+
draw.polygon([(450, 150), (445, 400), (455, 400)],
|
| 177 |
+
fill=(200, 200, 200), outline=(255, 255, 255))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
|
| 179 |
+
def draw_neighborhood(self, draw, game_state):
|
| 180 |
+
"""Dibuja el barrio actual con detalles auténticos"""
|
| 181 |
+
neighborhood = game_state.get('current_neighborhood', 'villa_31')
|
| 182 |
+
|
| 183 |
+
if neighborhood == 'villa_31':
|
| 184 |
+
self.draw_villa_scene(draw, game_state)
|
| 185 |
+
elif neighborhood == 'palermo':
|
| 186 |
+
self.draw_palermo_scene(draw, game_state)
|
| 187 |
+
elif neighborhood == 'san_telmo':
|
| 188 |
+
self.draw_san_telmo_scene(draw, game_state)
|
| 189 |
|
| 190 |
+
def draw_villa_scene(self, draw, game_state):
|
| 191 |
+
"""Villa 31: Casas coloridas, solidaridad, creatividad"""
|
| 192 |
+
# Casas de chapa coloridas
|
| 193 |
+
colors = [(255, 100, 100), (100, 255, 100), (100, 100, 255), (255, 255, 100)]
|
| 194 |
+
for i in range(6):
|
| 195 |
+
x = 50 + i * 120
|
| 196 |
+
y = 350 + random.randint(-30, 30)
|
| 197 |
+
color = random.choice(colors)
|
| 198 |
+
|
| 199 |
+
# Casa principal
|
| 200 |
+
draw.rectangle([x, y, x+80, y+80], fill=color, outline=(0, 0, 0), width=2)
|
| 201 |
+
|
| 202 |
+
# Techo de chapa
|
| 203 |
+
draw.polygon([(x-5, y), (x+40, y-20), (x+85, y)],
|
| 204 |
+
fill=(150, 150, 150), outline=(100, 100, 100))
|
| 205 |
+
|
| 206 |
+
# Antena parabólica
|
| 207 |
+
draw.ellipse([x+60, y-30, x+75, y-15], outline=(200, 200, 200), width=2)
|
| 208 |
|
| 209 |
+
# Cancha de fútbol improvisada
|
| 210 |
+
draw.rectangle([300, 450, 600, 550], fill=(100, 150, 50), outline=(255, 255, 255), width=3)
|
|
|
|
|
|
|
| 211 |
|
| 212 |
+
# Arcos de fútbol
|
| 213 |
+
draw.rectangle([300, 480, 310, 520], outline=(255, 255, 255), width=3)
|
| 214 |
+
draw.rectangle([590, 480, 600, 520], outline=(255, 255, 255), width=3)
|
| 215 |
|
| 216 |
+
def draw_community_life(self, draw, game_state):
|
| 217 |
+
"""Dibuja la vida comunitaria argentina"""
|
| 218 |
+
# Ronda de mate
|
| 219 |
+
mate_circle_x, mate_circle_y = 200, 500
|
| 220 |
+
for i in range(5):
|
| 221 |
+
angle = i * (math.pi * 2 / 5)
|
| 222 |
+
x = mate_circle_x + 40 * math.cos(angle)
|
| 223 |
+
y = mate_circle_y + 40 * math.sin(angle)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
|
| 225 |
+
# Persona
|
| 226 |
+
draw.ellipse([x-8, y-15, x+8, y+10], fill=(150, 120, 90), outline=(100, 80, 60))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
|
| 228 |
+
# Mate (persona central)
|
| 229 |
+
if i == 0:
|
| 230 |
+
draw.ellipse([x-3, y-5, x+3, y+2], fill=(100, 50, 0), outline=(150, 100, 50))
|
| 231 |
+
|
| 232 |
+
# Parrilla/asado
|
| 233 |
+
if game_state.get('day_time') == 'tarde':
|
| 234 |
+
asado_x, asado_y = 700, 500
|
| 235 |
+
draw.rectangle([asado_x, asado_y, asado_x+60, asado_y+30],
|
| 236 |
+
fill=(50, 50, 50), outline=(100, 100, 100))
|
| 237 |
|
| 238 |
+
# Humo del asado
|
| 239 |
+
for i in range(5):
|
| 240 |
+
smoke_y = asado_y - 20 - i*15
|
| 241 |
+
smoke_x = asado_x + 30 + random.randint(-10, 10)
|
| 242 |
+
draw.ellipse([smoke_x-5, smoke_y-5, smoke_x+5, smoke_y+5],
|
| 243 |
+
fill=(200, 200, 200, 100))
|
| 244 |
+
|
| 245 |
+
# Chicos jugando
|
| 246 |
+
for i in range(3):
|
| 247 |
+
child_x = 400 + i*50 + random.randint(-20, 20)
|
| 248 |
+
child_y = 470 + random.randint(-10, 10)
|
| 249 |
+
draw.ellipse([child_x-5, child_y-10, child_x+5, child_y+5],
|
| 250 |
+
fill=(180, 140, 100), outline=(120, 100, 80))
|
| 251 |
+
|
| 252 |
+
# Pelota de fútbol
|
| 253 |
+
ball_x = 450 + 20 * math.sin(time.time() * 2)
|
| 254 |
+
draw.ellipse([ball_x-8, 485, ball_x+8, 501],
|
| 255 |
+
fill=(255, 255, 255), outline=(0, 0, 0), width=2)
|
| 256 |
+
|
| 257 |
+
def draw_survival_ui(self, draw, game_state):
|
| 258 |
+
"""UI de supervivencia argentina"""
|
| 259 |
+
# Panel principal
|
| 260 |
+
draw.rectangle([10, 10, 300, 150], fill=(0, 0, 0, 180), outline=(150, 150, 150))
|
| 261 |
+
|
| 262 |
+
# Recursos
|
| 263 |
+
resources = game_state.get('resources', {})
|
| 264 |
+
y_offset = 25
|
| 265 |
+
|
| 266 |
+
for resource, value in resources.items():
|
| 267 |
+
color = self.get_resource_color(resource, value)
|
| 268 |
|
| 269 |
+
# Barra de recurso
|
| 270 |
+
draw.rectangle([20, y_offset, 200, y_offset+15], fill=(50, 50, 50))
|
| 271 |
+
bar_width = int((value / 100) * 180)
|
| 272 |
+
draw.rectangle([20, y_offset, 20 + bar_width, y_offset+15], fill=color)
|
|
|
|
| 273 |
|
| 274 |
+
# Texto del recurso
|
| 275 |
+
resource_names = {
|
| 276 |
+
'pesos': '💰 Pesos', 'dolares': '💵 Dólares', 'comida': '🍖 Comida',
|
| 277 |
+
'esperanza': '💪 Esperanza', 'solidaridad': '🤝 Solidaridad', 'trabajo': '🔨 Trabajo'
|
| 278 |
+
}
|
| 279 |
+
# draw.text([210, y_offset], f"{resource_names.get(resource, resource)}: {value}", fill=(255, 255, 255))
|
| 280 |
|
| 281 |
+
y_offset += 20
|
| 282 |
+
|
| 283 |
+
# Indicador de crisis
|
| 284 |
+
crisis_intensity = game_state.get('crisis_level', 1)
|
| 285 |
+
crisis_color = (255, 255 - crisis_intensity * 50, 255 - crisis_intensity * 50)
|
| 286 |
+
draw.rectangle([320, 10, 500, 40], fill=crisis_color, outline=(255, 255, 255))
|
| 287 |
+
|
| 288 |
+
# Día y situación
|
| 289 |
+
day = game_state.get('day', 1)
|
| 290 |
+
draw.rectangle([320, 50, 500, 80], fill=(20, 20, 20), outline=(100, 100, 100))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 291 |
|
| 292 |
+
def get_resource_color(self, resource, value):
|
| 293 |
+
"""Colores dinámicos según el estado del recurso"""
|
| 294 |
+
if value > 70:
|
| 295 |
+
return (100, 255, 100) # Verde
|
| 296 |
+
elif value > 30:
|
| 297 |
+
return (255, 255, 100) # Amarillo
|
| 298 |
+
else:
|
| 299 |
+
return (255, 100, 100) # Rojo
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 300 |
|
| 301 |
+
class CrisisYCorajeGame:
|
| 302 |
def __init__(self):
|
| 303 |
+
self.engine = ArgentinaGameEngine()
|
| 304 |
+
self.renderer = GameRenderer()
|
| 305 |
+
self.crisis_manager = CrisisManager()
|
| 306 |
+
self.neighborhood_system = NeighborhoodSystem()
|
| 307 |
+
|
| 308 |
+
# Estado del juego
|
| 309 |
+
self.game_state = {
|
| 310 |
+
'resources': self.engine.resources.copy(),
|
| 311 |
+
'day': 1,
|
| 312 |
+
'current_neighborhood': 'villa_31',
|
| 313 |
+
'crisis_level': 1,
|
| 314 |
+
'location': 'villa_31',
|
| 315 |
+
'day_time': 'mañana'
|
| 316 |
}
|
| 317 |
+
|
| 318 |
+
# Eventos dinámicos
|
| 319 |
+
self.active_events = []
|
| 320 |
+
self.decisions_made = []
|
| 321 |
|
| 322 |
+
def process_action(self, action):
|
| 323 |
+
"""Procesa las acciones del jugador con consecuencias reales"""
|
| 324 |
+
if action == "trabajar":
|
| 325 |
+
return self.work_day()
|
| 326 |
+
elif action == "ayudar_vecinos":
|
| 327 |
+
return self.help_neighbors()
|
| 328 |
+
elif action == "buscar_rebusque":
|
| 329 |
+
return self.find_rebusque()
|
| 330 |
+
elif action == "organizar_comunitario":
|
| 331 |
+
return self.organize_community()
|
| 332 |
+
elif action == "ahorrar_dolares":
|
| 333 |
+
return self.save_dollars()
|
| 334 |
+
elif action == "hacer_asado":
|
| 335 |
+
return self.make_asado()
|
| 336 |
+
|
| 337 |
+
return self.advance_day()
|
| 338 |
|
| 339 |
+
def work_day(self):
|
| 340 |
+
"""Simula un día de trabajo con variables argentinas"""
|
| 341 |
+
profession = self.engine.player.profession
|
| 342 |
+
base_income = {
|
| 343 |
+
'albañil': random.randint(2000, 4000),
|
| 344 |
+
'docente': random.randint(1500, 2500),
|
| 345 |
+
'comerciante': random.randint(1000, 8000),
|
| 346 |
+
'empleado_publico': random.randint(1800, 2200)
|
| 347 |
+
}
|
| 348 |
+
|
| 349 |
+
income = base_income.get(profession, 2000)
|
| 350 |
+
|
| 351 |
+
# Efectos de la inflación
|
| 352 |
+
real_income = income / self.engine.inflation_rate
|
| 353 |
+
|
| 354 |
+
self.game_state['resources']['pesos'] += real_income
|
| 355 |
+
self.game_state['resources']['trabajo'] = min(100, self.game_state['resources']['trabajo'] + 10)
|
| 356 |
+
|
| 357 |
+
# Stress por la crisis
|
| 358 |
+
if self.game_state['crisis_level'] > 3:
|
| 359 |
+
self.engine.player.stress_level += 5
|
| 360 |
+
self.game_state['resources']['esperanza'] -= 5
|
| 361 |
+
|
| 362 |
+
return self.advance_day(), f"Trabajaste como {profession}. Ganaste ${real_income:.0f} pesos (inflación: {self.engine.inflation_rate:.1f}x)"
|
| 363 |
+
|
| 364 |
+
def help_neighbors(self):
|
| 365 |
+
"""Ayudar a los vecinos - mecánica clave argentina"""
|
| 366 |
+
solidarity_gain = random.randint(10, 20)
|
| 367 |
+
self.game_state['resources']['solidaridad'] += solidarity_gain
|
| 368 |
+
self.game_state['resources']['esperanza'] += 5
|
| 369 |
+
|
| 370 |
+
# Costo en recursos pero ganancia en red social
|
| 371 |
+
self.game_state['resources']['comida'] -= 10
|
| 372 |
+
self.game_state['resources']['pesos'] -= 200
|
| 373 |
+
|
| 374 |
+
# Chance de reciprocidad
|
| 375 |
+
if random.random() < 0.3:
|
| 376 |
+
favor_returned = random.choice([
|
| 377 |
+
"Te devolvieron un favor con trabajo",
|
| 378 |
+
"Te invitaron a un asado",
|
| 379 |
+
"Te prestaron herramientas",
|
| 380 |
+
"Te cuidaron a los chicos"
|
| 381 |
+
])
|
| 382 |
+
self.game_state['resources']['trabajo'] += 15
|
| 383 |
+
return self.advance_day(), f"Ayudaste a tus vecinos. {favor_returned}."
|
| 384 |
+
|
| 385 |
+
return self.advance_day(), f"Ayudaste a tus vecinos. Ganaste {solidarity_gain} puntos de solidaridad."
|
| 386 |
+
|
| 387 |
+
def find_rebusque(self):
|
| 388 |
+
"""Rebusque - supervivencia argentina pura"""
|
| 389 |
+
rebusque_skill = self.engine.player.skills['rebusque']
|
| 390 |
+
success_chance = rebusque_skill / 100
|
| 391 |
+
|
| 392 |
+
if random.random() < success_chance:
|
| 393 |
+
rebusque_options = [
|
| 394 |
+
"Vendiste empanadas caseras",
|
| 395 |
+
"Arreglaste celulares",
|
| 396 |
+
"Hiciste changas de albañilería",
|
| 397 |
+
"Vendiste en la feria",
|
| 398 |
+
"Diste clases particulares"
|
| 399 |
+
]
|
| 400 |
+
|
| 401 |
+
chosen_rebusque = random.choice(rebusque_options)
|
| 402 |
+
income = random.randint(500, 2000)
|
| 403 |
+
self.game_state['resources']['pesos'] += income
|
| 404 |
+
self.engine.player.skills['rebusque'] += 2
|
| 405 |
+
|
| 406 |
+
return self.advance_day(), f"{chosen_rebusque}. Ganaste ${income} pesos!"
|
| 407 |
+
else:
|
| 408 |
+
self.game_state['resources']['esperanza'] -= 10
|
| 409 |
+
return self.advance_day(), "El rebusque no funcionó hoy. Seguí intentando."
|
| 410 |
+
|
| 411 |
+
def make_asado(self):
|
| 412 |
+
"""Hacer asado - tradición argentina que mejora la moral"""
|
| 413 |
+
if self.game_state['resources']['pesos'] >= 1500:
|
| 414 |
+
self.game_state['resources']['pesos'] -= 1500
|
| 415 |
+
self.game_state['resources']['solidaridad'] += 25
|
| 416 |
+
self.game_state['resources']['esperanza'] += 20
|
| 417 |
+
|
| 418 |
+
# Bonus de comunidad
|
| 419 |
+
self.engine.player.community_reputation += 10
|
| 420 |
+
|
| 421 |
+
return self.advance_day(), "Hiciste un asado bárbaro. Todo el barrio vino, mejoró la moral general!"
|
| 422 |
+
else:
|
| 423 |
+
return self.advance_day(), "No tenés suficiente plata para el asado. Seguí juntando."
|
| 424 |
|
| 425 |
+
def advance_day(self):
|
| 426 |
+
"""Avanza el día con eventos dinámicos"""
|
| 427 |
+
self.game_state['day'] += 1
|
| 428 |
+
|
| 429 |
+
# Inflación diaria
|
| 430 |
+
self.engine.inflation_rate += random.uniform(0.01, 0.05)
|
| 431 |
+
|
| 432 |
+
# Consumo diario
|
| 433 |
+
daily_costs = {
|
| 434 |
+
'comida': random.randint(5, 15),
|
| 435 |
+
'pesos': random.randint(100, 500)
|
| 436 |
+
}
|
| 437 |
+
|
| 438 |
+
for resource, cost in daily_costs.items():
|
| 439 |
+
self.game_state['resources'][resource] -= cost
|
| 440 |
+
self.game_state['resources'][resource] = max(0, self.game_state['resources'][resource])
|
| 441 |
+
|
| 442 |
+
# Eventos random
|
| 443 |
+
if random.random() < 0.2:
|
| 444 |
+
self.trigger_random_event()
|
| 445 |
+
|
| 446 |
+
# Cambio de estación
|
| 447 |
+
if self.game_state['day'] % 90 == 0:
|
| 448 |
+
self.change_season()
|
| 449 |
+
|
| 450 |
+
return self.renderer.render_argentina_frame(self.game_state)
|
| 451 |
+
|
| 452 |
+
def trigger_random_event(self):
|
| 453 |
+
"""Eventos aleatorios argentinos"""
|
| 454 |
+
events = [
|
| 455 |
+
"Piquete en 9 de Julio - no pudiste trabajar",
|
| 456 |
+
"Encontraste $500 en el subte",
|
| 457 |
+
"El almacenero te fió hasta fin de mes",
|
| 458 |
+
"Ganaste en la quiniela",
|
| 459 |
+
"Se rompió el colectivo - llegaste tarde",
|
| 460 |
+
"Apareció laburo extra en el barrio"
|
| 461 |
+
]
|
| 462 |
+
|
| 463 |
+
event = random.choice(events)
|
| 464 |
+
self.active_events.append(event)
|
| 465 |
+
|
| 466 |
+
# Aplicar efectos del evento
|
| 467 |
+
if "Piquete" in event:
|
| 468 |
+
self.game_state['resources']['trabajo'] -= 20
|
| 469 |
+
elif "Encontraste $500" in event:
|
| 470 |
+
self.game_state['resources']['pesos'] += 500
|
| 471 |
+
elif "quiniela" in event:
|
| 472 |
+
self.game_state['resources']['pesos'] += random.randint(1000, 5000)
|
| 473 |
+
self.game_state['resources']['esperanza'] += 30
|
| 474 |
+
|
| 475 |
+
def get_game_status(self):
|
| 476 |
+
"""Estado completo del juego"""
|
| 477 |
return {
|
| 478 |
+
"📅 Día": self.game_state['day'],
|
| 479 |
+
"🏠 Barrio": self.game_state['current_neighborhood'].replace('_', ' ').title(),
|
| 480 |
+
"💰 Pesos": f"${self.game_state['resources']['pesos']:.0f}",
|
| 481 |
+
"💵 Dólares": f"US${self.game_state['resources']['dolares']:.0f}",
|
| 482 |
+
"🍖 Comida": f"{self.game_state['resources']['comida']:.0f}%",
|
| 483 |
+
"💪 Esperanza": f"{self.game_state['resources']['esperanza']:.0f}%",
|
| 484 |
+
"🤝 Solidaridad": f"{self.game_state['resources']['solidaridad']:.0f}%",
|
| 485 |
+
"📈 Inflación": f"{self.engine.inflation_rate:.2f}x",
|
| 486 |
+
"🎯 Crisis": f"Nivel {self.game_state['crisis_level']}/5"
|
| 487 |
}
|
| 488 |
|
| 489 |
+
def create_revolutionary_interface():
|
| 490 |
+
game = CrisisYCorajeGame()
|
| 491 |
|
| 492 |
with gr.Blocks(
|
| 493 |
+
title="🇦🇷 CRISIS Y CORAJE - El Juego Argentino Definitivo",
|
| 494 |
+
theme=gr.themes.Soft()
|
| 495 |
) as interface:
|
| 496 |
|
| 497 |
gr.HTML("""
|
| 498 |
+
<div style="background: linear-gradient(135deg, #74b9ff, #0984e3, #fdcb6e);
|
| 499 |
+
padding: 25px; text-align: center; border-radius: 15px; margin: 15px;">
|
| 500 |
+
<h1 style="color: white; font-size: 3em; text-shadow: 2px 2px 4px rgba(0,0,0,0.7); margin: 0;">
|
| 501 |
+
🇦🇷 CRISIS Y CORAJE
|
| 502 |
</h1>
|
| 503 |
+
<h2 style="color: #ffffff; font-size: 1.3em; margin: 10px 0;">
|
| 504 |
+
Supervivencia, Solidaridad y Cultura en la Argentina Real
|
| 505 |
+
</h2>
|
| 506 |
+
<p style="color: #f1f2f6; font-size: 1.1em; margin: 5px 0;">
|
| 507 |
+
Navegá las crisis económicas mientras preservás lo que nos hace argentinos
|
| 508 |
</p>
|
| 509 |
</div>
|
| 510 |
""")
|
| 511 |
|
| 512 |
with gr.Row():
|
| 513 |
+
with gr.Column(scale=2):
|
| 514 |
game_display = gr.Image(
|
| 515 |
+
label="🌆 Tu Barrio en Argentina",
|
| 516 |
+
width=900,
|
|
|
|
| 517 |
height=600
|
| 518 |
)
|
| 519 |
|
| 520 |
+
gr.Markdown("### 🎮 ACCIONES ARGENTINAS")
|
| 521 |
with gr.Row():
|
| 522 |
+
work_btn = gr.Button("💼 TRABAJAR", variant="primary", size="lg")
|
| 523 |
+
help_btn = gr.Button("🤝 AYUDAR VECINOS", variant="secondary", size="lg")
|
| 524 |
+
rebusque_btn = gr.Button("💡 REBUSQUE", variant="stop", size="lg")
|
| 525 |
+
|
| 526 |
with gr.Row():
|
| 527 |
+
organize_btn = gr.Button("📢 ORGANIZAR", variant="secondary", size="lg")
|
| 528 |
+
save_btn = gr.Button("💵 AHORRAR", variant="primary", size="lg")
|
| 529 |
+
asado_btn = gr.Button("🔥 HACER ASADO", variant="stop", size="lg")
|
| 530 |
|
| 531 |
with gr.Column(scale=1):
|
| 532 |
+
gr.Markdown("### 📊 ESTADO DE SUPERVIVENCIA")
|
| 533 |
status_display = gr.JSON(
|
| 534 |
+
label="🎯 Tu Situación",
|
| 535 |
+
value=game.get_game_status()
|
| 536 |
)
|
| 537 |
|
| 538 |
+
gr.Markdown("### 📰 ÚLTIMAS NOTICIAS")
|
| 539 |
+
news_display = gr.Textbox(
|
| 540 |
+
value="Bienvenido a tu barrio. La crisis está, pero la solidaridad también. ¿Cómo vas a sobrevivir hoy?",
|
| 541 |
+
label="📺 Canal de Noticias",
|
| 542 |
+
lines=4,
|
| 543 |
+
interactive=False
|
| 544 |
+
)
|
| 545 |
+
|
| 546 |
+
gr.Markdown("### 🎯 TUTORIAL RÁPIDO")
|
| 547 |
gr.HTML("""
|
| 548 |
+
<div style="background: #2d3436; color: white; padding: 15px; border-radius: 10px;">
|
| 549 |
+
<p><strong>🎮 Cómo Jugar:</strong></p>
|
| 550 |
+
<ul style="margin: 10px 0; padding-left: 20px;">
|
| 551 |
+
<li>💼 <strong>Trabajá</strong> para ganar pesos</li>
|
| 552 |
+
<li>🤝 <strong>Ayudá</strong> para aumentar solidaridad</li>
|
| 553 |
+
<li>💡 <strong>Rebuscátelas</strong> en crisis</li>
|
| 554 |
+
<li>🔥 <strong>Hacé asados</strong> para la moral</li>
|
| 555 |
+
<li>💵 <strong>Ahorrá dólares</strong> contra inflación</li>
|
| 556 |
+
</ul>
|
| 557 |
+
<p><em>¡Sobreviví con creatividad argentina!</em></p>
|
| 558 |
</div>
|
| 559 |
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 560 |
|
| 561 |
+
# Conectar acciones
|
| 562 |
+
def handle_action(action_name):
|
| 563 |
+
frame, message = game.process_action(action_name)
|
| 564 |
+
status = game.get_game_status()
|
| 565 |
+
return frame, status, message
|
| 566 |
+
|
| 567 |
+
work_btn.click(fn=lambda: handle_action("trabajar"),
|
| 568 |
+
outputs=[game_display, status_display, news_display])
|
| 569 |
+
help_btn.click(fn=lambda: handle_action("ayudar_vecinos"),
|
| 570 |
+
outputs=[game_display, status_display, news_display])
|
| 571 |
+
rebusque_btn.click(fn=lambda: handle_action("buscar_rebusque"),
|
| 572 |
+
outputs=[game_display, status_display, news_display])
|
| 573 |
+
organize_btn.click(fn=lambda: handle_action("organizar_comunitario"),
|
| 574 |
+
outputs=[game_display, status_display, news_display])
|
| 575 |
+
save_btn.click(fn=lambda: handle_action("ahorrar_dolares"),
|
| 576 |
+
outputs=[game_display, status_display, news_display])
|
| 577 |
+
asado_btn.click(fn=lambda: handle_action("hacer_asado"),
|
| 578 |
+
outputs=[game_display, status_display, news_display])
|
| 579 |
|
| 580 |
# Inicialización
|
| 581 |
interface.load(
|
| 582 |
+
fn=lambda: (game.renderer.render_argentina_frame(game.game_state),
|
| 583 |
+
game.get_game_status(),
|
| 584 |
+
"¡Arrancó el juego! Tomate tu tiempo para pensar cada decisión."),
|
| 585 |
+
outputs=[game_display, status_display, news_display]
|
| 586 |
)
|
| 587 |
|
| 588 |
return interface
|
| 589 |
|
| 590 |
if __name__ == "__main__":
|
| 591 |
+
interface = create_revolutionary_interface()
|
| 592 |
interface.launch(share=True, show_error=True)
|