#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
ASCII Minecraft-Inspired Survival Game – Improved Prototype
Added:
- Mouse clicking to break/place blocks.
- An improved inventory system with scrollable display,
stacking limits (max 64 per item), and item selection for placement.
For Windows users, if curses is missing, run: pip install windows-curses
"""
try:
import curses
except ImportError:
print("On Windows, please run: pip install windows-curses")
exit(1)
import curses
import time
import random
import math
import pickle # For save/load
# ==============================
# GLOBAL SETTINGS
# ==============================
WORLD_WIDTH = 150
WORLD_HEIGHT = 50
SCREEN_WIDTH = 40
SCREEN_HEIGHT = 20
DAY_LENGTH = 120 # seconds (stub for day/night)
global_time = 0
# Block symbols
AIR = ' '
GRASS = 'G'
DIRT = 'D'
STONE = 'S'
WOOD = 'W'
LEAF = 'L'
OBSIDIAN = 'O'
FURNACE_BLK = 'F'
# Mouse left-click flag constant (curses uses these bit masks)
LEFT_CLICK = curses.BUTTON1_CLICKED
# ==============================
# HELPER FUNCTIONS
# ==============================
def get_direction_symbol(angle):
"""Return an ASCII symbol for the player's facing given an angle (degrees)."""
angle = angle % 360
if angle < 45 or angle >= 315:
return ">" # facing right
elif angle < 135:
return "^" # facing up
elif angle < 225:
return "<" # facing left
else:
return "v" # facing down
def flash_effect(stdscr, duration=0.1):
"""A flash effect (e.g. on damage) using a temporary color change."""
stdscr.bkgd(' ', curses.color_pair(2))
stdscr.refresh()
time.sleep(duration)
stdscr.bkgd(' ', curses.color_pair(1))
stdscr.refresh()
# ==============================
# CLASSES
# ==============================
class Player:
def __init__(self, x, y):
self.x = x
self.y = y
self.health = 100
self.hunger = 100
self.inventory = {
"wood": 0,
"wood_plank": 0,
"cobblestone": 0,
"stone": 0,
"wooden_pickaxe": 0,
"furnace": 0,
"iron_ore": 0,
"iron_ingot": 0,
"coal": 0,
"diamond": 0,
"gold_ore": 0,
"gold_ingot": 0,
"obsidian": 0,
"food": 0,
# Additional items can be added here.
}
self.tool = None
self.orientation = 0 # In degrees
self.active_item = None # Currently selected block for placement
player = Player(WORLD_WIDTH // 2, WORLD_HEIGHT // 2)
enemy_types = [
{"type": "zombie", "symbol": "Z", "health": 20},
{"type": "skeleton", "symbol": "S", "health": 15},
{"type": "creeper", "symbol": "C", "health": 10},
{"type": "enderman", "symbol": "E", "health": 25},
]
enemies = []
boss = None # Placeholder for future boss integration
# ==============================
# WORLD GENERATION
# ==============================
def generate_world():
world = [[AIR for _ in range(WORLD_WIDTH)] for _ in range(WORLD_HEIGHT)]
for y in range(WORLD_HEIGHT):
for x in range(WORLD_WIDTH):
ground_level = WORLD_HEIGHT - 10 + int(3 * math.sin(x / 10.0))
if y < ground_level - 2:
world[y][x] = AIR
elif y < ground_level:
world[y][x] = DIRT
elif y == ground_level:
world[y][x] = GRASS
else:
world[y][x] = STONE
# Add trees as before (only in part of the world)
for _ in range(20):
tx = random.randint(5, WORLD_WIDTH // 2)
for ty in range(WORLD_HEIGHT):
if world[ty][tx] == GRASS:
height = random.randint(3, 5)
for h in range(height):
if ty - h >= 0:
world[ty - h][tx] = WOOD
for dx in range(-1, 2):
for dy in range(-2, 1):
if 0 <= tx + dx < WORLD_WIDTH and 0 <= ty - height + dy < WORLD_HEIGHT:
if random.random() < 0.7:
world[ty - height + dy][tx + dx] = LEAF
break
# Add some caves (random holes)
for _ in range(50):
cx = random.randint(0, WORLD_WIDTH - 1)
cy = random.randint(WORLD_HEIGHT // 2, WORLD_HEIGHT - 1)
if world[cy][cx] == STONE:
world[cy][cx] = AIR
return world
world = generate_world()
# ==============================
# CRAFTING SYSTEM (Stub)
# ==============================
crafting_recipes = {
"wood_plank": {"wood": 1},
"wooden_pickaxe": {"wood_plank": 3},
"furnace": {"cobblestone": 8},
}
def crafting_menu(stdscr):
stdscr.clear()
stdscr.addstr(0, 0, "----- Crafting Menu -----")
available = []
line = 1
for item, req in crafting_recipes.items():
can_craft = all(player.inventory.get(ing, 0) >= amt