File size: 5,615 Bytes
c02cd3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
from __future__ import annotations

import random
from dataclasses import dataclass
from typing import Dict, List, Tuple

import pygame

from config import constants

ItemType = str


@dataclass
class ItemSpec:
    color: Tuple[int, int, int]
    label: str


ITEM_SPECS: Dict[ItemType, ItemSpec] = {
    # Metals (asteroids)
    "steel_scrap": ItemSpec((160, 160, 160), "Stl"),
    "aluminum_sheet": ItemSpec((180, 200, 220), "Al"),
    "copper_wire": ItemSpec((200, 140, 90), "Cu"),
    # Enemy drops
    "lithium_battery": ItemSpec((140, 200, 255), "Li"),
    "circuit_board": ItemSpec((90, 200, 120), "PCB"),
    "rare_earth_element": ItemSpec((200, 160, 255), "REE"),
    "pet_plastic": ItemSpec((200, 200, 220), "PET"),
    "carbon_fiber": ItemSpec((80, 80, 90), "CF"),
    "titanium_alloy": ItemSpec((170, 170, 190), "Ti"),
    "power_core": ItemSpec((255, 100, 80), "Core"),
    "crystal_capacitor": ItemSpec((180, 220, 255), "Cap"),
    "exotic_isotope": ItemSpec((255, 180, 80), "Iso"),
    # Health
    "repair_nanites": ItemSpec((90, 240, 140), "Heal"),
    "bio_gel": ItemSpec((90, 200, 90), "Gel"),
    # Power-ups (enemy drops)
    "powerup_magnet": ItemSpec((90, 200, 255), "Mag"),
    "powerup_super_magnet": ItemSpec((110, 220, 255), "SMag"),
    "powerup_weapon_missile": ItemSpec((255, 190, 80), "MSL"),
    "powerup_weapon_plasma": ItemSpec((120, 200, 255), "PLS"),
    "powerup_weapon_ray": ItemSpec((255, 120, 200), "RAY"),
    "powerup_shieldgen": ItemSpec((140, 220, 255), "SHD"),
    "powerup_rapidfire": ItemSpec((255, 220, 90), "RF"),
    "powerup_damage": ItemSpec((255, 120, 120), "Dmg"),
    "powerup_shield": ItemSpec((120, 180, 255), "Shd"),
    "powerup_regen": ItemSpec((90, 240, 140), "Regen"),
    "powerup_slow": ItemSpec((120, 180, 255), "Slow"),
    "powerup_multishot": ItemSpec((255, 160, 255), "2x"),
}


def random_metal_drop() -> ItemType:
    r = random.random()
    cumulative = 0.0
    table = constants.ASTEROID_METAL_DROP_TABLE
    for name, prob in table:
        cumulative += prob
        if r <= cumulative:
            return name
    return table[-1][0]


def random_enemy_drop() -> ItemType:
    table = constants.ENEMY_DROP_TABLE
    total = sum(weight for _, weight in table)
    r = random.uniform(0, total)
    cumulative = 0.0
    for name, weight in table:
        cumulative += weight
        if r <= cumulative:
            return name
    return table[-1][0]


def random_powerup_drop() -> ItemType:
    table = constants.POWERUP_DROP_TABLE
    total = sum(weight for _, weight in table)
    r = random.uniform(0, total)
    cumulative = 0.0
    for name, weight in table:
        cumulative += weight
        if r <= cumulative:
            return name
    return table[-1][0]


class Item(pygame.sprite.Sprite):
    """Collectible item dropped from asteroids or enemies."""

    ICONS: Dict[str, pygame.Surface] = {}

    def __init__(self, item_type: ItemType, pos: Tuple[float, float]):
        super().__init__()
        spec = ITEM_SPECS.get(item_type, ItemSpec((200, 200, 200), item_type))
        self.item_type = item_type
        icon = self.ICONS.get(item_type)
        if icon:
            self.image = pygame.transform.scale(icon, (18, 18))
        else:
            self.image = pygame.Surface((14, 14), pygame.SRCALPHA)
            self.image.fill(spec.color)
            font = pygame.font.Font(None, 12)
            text = font.render(spec.label, True, (0, 0, 0))
            text_rect = text.get_rect(center=(7, 7))
            self.image.blit(text, text_rect)
        self.rect = self.image.get_rect(center=pos)
        self.pos = pygame.Vector2(pos)
        self.vel = pygame.Vector2(-80, random.uniform(-20, 20))  # drift left with slight variation

    def update(self, dt: float):
        self.pos += self.vel * dt
        self.rect.center = (int(self.pos.x), int(self.pos.y))


class Inventory:
    """Simple inventory with capacity counting total items."""

    def __init__(self, capacity: int = constants.INVENTORY_LIMIT):
        self.capacity = capacity
        self.counts: Dict[ItemType, int] = {}
        self.weapons: Dict[str, int] = {}
        self.upgrades: Dict[str, int] = {}
        self.owned_ships = {"salvager_one"}
        self.current_ship = "salvager_one"

    @property
    def total(self) -> int:
        return sum(self.counts.values())

    def can_pick(self, amount: int = 1) -> bool:
        return True  # unlimited inventory

    def add(self, item_type: ItemType, amount: int = 1) -> bool:
        if not self.can_pick(amount):
            return False
        self.counts[item_type] = self.counts.get(item_type, 0) + amount
        return True

    def get(self, item_type: ItemType) -> int:
        # Allow checking crafted weapons using snake_case keys for recipes
        weapon_key_map = {
            "laser_cannon": "Laser Cannon",
            "missile_launcher": "Missile Launcher",
            "shield_generator": "Shield Generator",
            "plasma_weapon": "Plasma Weapon",
            "destruction_ray": "Destruction Ray",
        }
        if item_type in weapon_key_map:
            return self.weapons.get(weapon_key_map[item_type], 0)
        return self.counts.get(item_type, 0)

    def add_weapon(self, weapon_name: str):
        self.weapons[weapon_name] = self.weapons.get(weapon_name, 0) + 1

    def remove_weapon(self, weapon_name: str):
        if weapon_name not in self.weapons:
            return
        self.weapons[weapon_name] = max(0, self.weapons.get(weapon_name, 0) - 1)
        if self.weapons[weapon_name] <= 0:
            del self.weapons[weapon_name]