File size: 11,838 Bytes
f63b467
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
import gradio as gr
import os
import json
import random
from huggingface_hub import InferenceClient

# --- CONFIGURATION ---
# Connects to Hugging Face's Free Inference API
# You must set 'HF_TOKEN' in your Space Settings (Variables and secrets)
HF_TOKEN = os.getenv("HF_TOKEN")

# --- BRANDING & ASSETS ---
COMPANY_NAME = "SpeedoDude"
APP_TITLE = f"🌊 {COMPANY_NAME} AI Game Architect"
DONATION_HTML = """
<div align="center">
  <p><b>Support {company} Development</b><br>
  OmniMod is free and open source. If this tool helped you, please consider donating!</p>
  <a href="https://www.patreon.com/YOUR_USERNAME" target="_blank">
    <img src="https://img.shields.io/badge/Support_on-Patreon-f96854?style=for-the-badge&logo=patreon&logoColor=white" alt="Support on Patreon"/>
  </a>
  <a href="https://ko-fi.com/YOUR_USERNAME" target="_blank">
    <img src="https://img.shields.io/badge/Buy_me_a-Coffee-ff5f5f?style=for-the-badge&logo=ko-fi&logoColor=white" alt="Buy me a Coffee"/>
  </a>
</div>
""".format(company=COMPANY_NAME)

LEGAL_TEXT = """
### ⚠️ Legal Disclaimer & Terms
1. **Single-Player Only:** This tool is strictly for offline, creative use. Do not use in multiplayer environments.
2. **Liability:** SpeedoDude is not responsible for account bans or data loss. Always backup your save files.
3. **Ownership:** Assets generated via the SpeedoDude Generator belong to you, the user.
"""

# ==============================================================================
# MODULE 1: THE GENERATOR (Assets & Textures)
# ==============================================================================
def generate_asset(prompt, style, asset_type):
    """
    Generates game textures/concepts using Stable Diffusion.
    """
    if not HF_TOKEN:
        return None, "⚠️ Error: HF_TOKEN secret is missing in Space Settings."
    
    # Model Selection: Stable Diffusion 2.1 is reliable and free on HF
    model_id = "stabilityai/stable-diffusion-2-1"
    client = InferenceClient(model_id, token=HF_TOKEN)
    
    # Optimized Prompt Engineering
    full_prompt = f"{prompt}, {style} style, {asset_type} game asset, high quality, 4k, flat lighting, seamless"
    
    try:
        image = client.text_to_image(full_prompt)
        output_path = "/tmp/speedodude_asset.png"
        image.save(output_path)
        return output_path, f"βœ… Success! Generated: '{full_prompt}'"
    except Exception as e:
        return None, f"❌ Generation Error: {str(e)}"

# ==============================================================================
# MODULE 2: THE FABRICATOR (Save File Editor)
# ==============================================================================
def analyze_save_file(file_obj):
    """
    Reads a .json or .xml file and shows the structure.
    """
    if file_obj is None: return "Please upload a file first."
    
    try:
        with open(file_obj.name, 'r') as f:
            content = f.read()
            
        # Try parsing JSON
        try:
            data = json.loads(content)
            keys = list(data.keys())[:50] # Preview first 50 keys
            return f"πŸ“‚ File Valid (JSON).\nFound Top-Level Keys:\n{keys}..."
        except json.JSONDecodeError:
            # Fallback for XML/Text (Basic preview)
            return f"πŸ“„ File Valid (Text/XML).\nPreview:\n{content[:500]}..."
            
    except Exception as e:
        return f"❌ Error reading file: {str(e)}"

def apply_hack(file_obj, key_to_find, new_value):
    """
    Recursively searches a JSON file for a specific key and updates it.
    """
    if file_obj is None: return None, "Upload a file first."
    
    try:
        with open(file_obj.name, 'r') as f:
            data = json.load(f)
        
        matches = 0
        
        def update_recursive(d, target_k, target_v):
            nonlocal matches
            for k in d:
                if k == target_k:
                    # Smart Type Conversion (Maintain Int/Float/String)
                    if isinstance(d[k], int):
                        d[k] = int(target_v)
                    elif isinstance(d[k], float):
                        d[k] = float(target_v)
                    else:
                        d[k] = str(target_v)
                    matches += 1
                elif isinstance(d[k], dict):
                    update_recursive(d[k], target_k, target_v)
                elif isinstance(d[k], list):
                    for item in d[k]:
                        if isinstance(item, dict):
                            update_recursive(item, target_k, target_v)

        update_recursive(data, key_to_find, new_value)
        
        if matches == 0:
            return None, f"⚠️ Key '{key_to_find}' not found in file."
            
        output_path = "/tmp/speedodude_modded.json"
        with open(output_path, 'w') as f:
            json.dump(data, f, indent=4)
            
        return output_path, f"βœ… Success! Updated {matches} instance(s) of '{key_to_find}'."
    except Exception as e:
        return None, f"❌ Error: {str(e)}"

# ==============================================================================
# MODULE 3: THE ARCHITECT (Alchemist, Dojo, Director, Genesis)
# ==============================================================================
def generate_blueprint(blueprint_type, name, details):
    """
    Generates code snippets and configuration files for various game systems.
    Combines Alchemist (Items), Dojo (Combat), Director (Scenes), Genesis (Project).
    """
    name_clean = name.replace(" ", "")
    
    # --- ALCHEMIST (Custom Items) ---
    if blueprint_type == "Item Script (Unity C#)":
        return f"""// Generated by SpeedoDude Alchemist
using UnityEngine;

[CreateAssetMenu(fileName = "{name}", menuName = "SpeedoDude/Item")]
public class {name_clean} : ItemObject {{
    public string itemName = "{name}";
    [TextArea] public string description = "{details}";
    
    public override void OnUse(Character user) {{
        Debug.Log("Using {name}...");
        // Effect Logic: {details}
        // TODO: Implement specific stat changes here.
    }}
}}
"""

    # --- DOJO (Combat Logic) ---
    elif blueprint_type == "Combat Style (JSON)":
        data = {
            "style_name": name,
            "description": details,
            "creator": COMPANY_NAME,
            "moves": [
                {"name": "Light Attack", "damage": 10, "anim": f"{name}_Light.anim", "frame_data": 5},
                {"name": "Heavy Attack", "damage": 25, "anim": f"{name}_Heavy.anim", "frame_data": 15},
                {"name": "Special", "damage": 50, "effect": details}
            ]
        }
        return json.dumps(data, indent=4)

    # --- DIRECTOR (Scene Layout) ---
    elif blueprint_type == "Scene Layout (JSON)":
        return json.dumps({
            "scene_name": name,
            "theme": details,
            "lighting": {"ambient": "#1a1a1a", "sun_intensity": 0.8},
            "objects": [
                {"id": "player_start", "pos": [0, 1, 0]},
                {"id": "enemy_spawn", "pos": [10, 0, 5]},
                {"id": "prop_chest", "pos": [-5, 0, 2], "loot": "random"}
            ]
        }, indent=4)

    # --- GENESIS (Unreal Project Config) ---
    elif blueprint_type == "Unreal Project File (.uproject)":
        return json.dumps({
            "FileVersion": 3,
            "EngineAssociation": "5.3",
            "Category": "",
            "Description": f"{details} - Generated by SpeedoDude Genesis",
            "Modules": [
                {"Name": name_clean, "Type": "Runtime", "LoadingPhase": "Default"}
            ]
        }, indent=4)
        
    return "Error: Unknown Blueprint Type"

# ==============================================================================
# MAIN UI LAYOUT
# ==============================================================================
# Using 'Soft' theme with SpeedoDude colors
with gr.Blocks(theme=gr.themes.Soft(primary_hue="cyan", secondary_hue="indigo"), title=APP_TITLE) as app:
    
    # --- HEADER ---
    gr.Markdown(f"# {APP_TITLE}")
    gr.Markdown("The ultimate cloud-based suite for single-player game modding.")
    gr.HTML(DONATION_HTML) # Inject Donation Badges
    
    # --- TABS ---
    with gr.Tabs():
        
        # TAB 1: GENERATOR
        with gr.TabItem("🎨 Generator"):
            gr.Markdown("### Create Game Assets")
            with gr.Row():
                gen_prompt = gr.Textbox(label="Prompt", placeholder="A rusty cyber-sword with glowing green edges")
                gen_style = gr.Dropdown(["Realistic", "Pixel Art", "Sci-Fi", "Fantasy", "Cartoon"], label="Style", value="Realistic")
                gen_type = gr.Dropdown(["Icon", "Texture", "Concept Art", "UI Element"], label="Type", value="Texture")
            
            gen_btn = gr.Button("Generate Asset", variant="primary")
            
            with gr.Row():
                gen_image = gr.Image(label="Result")
                gen_log = gr.Textbox(label="System Log")
            
            gen_btn.click(generate_asset, inputs=[gen_prompt, gen_style, gen_type], outputs=[gen_image, gen_log])

        # TAB 2: FABRICATOR
        with gr.TabItem("πŸ’Ύ Fabricator"):
            gr.Markdown("### Save File Editor")
            with gr.Row():
                file_input = gr.File(label="Upload Save (.json)")
                analyze_btn = gr.Button("Analyze File")
            
            file_info = gr.Textbox(label="File Structure Preview", lines=4)
            analyze_btn.click(analyze_save_file, inputs=file_input, outputs=file_info)
            
            gr.Markdown("---")
            gr.Markdown("### Apply Modification")
            with gr.Row():
                hack_key = gr.Textbox(label="Key to Change", placeholder="e.g., gold, health, xp")
                hack_val = gr.Textbox(label="New Value", placeholder="999999")
            
            hack_btn = gr.Button("Apply Hack", variant="stop")
            
            with gr.Row():
                hack_download = gr.File(label="Download Modded Save")
                hack_log = gr.Textbox(label="System Log")
            
            hack_btn.click(apply_hack, inputs=[file_input, hack_key, hack_val], outputs=[hack_download, hack_log])

        # TAB 3: ARCHITECT (Alchemist/Dojo/Director/Genesis)
        with gr.TabItem("πŸ“œ Architect"):
            gr.Markdown("### Code & Blueprint Generator")
            gr.Markdown("Generate production-ready scripts for Unity/Unreal.")
            
            with gr.Row():
                arch_type = gr.Dropdown([
                    "Item Script (Unity C#)", 
                    "Combat Style (JSON)", 
                    "Scene Layout (JSON)", 
                    "Unreal Project File (.uproject)"
                ], label="Blueprint Type")
                
                arch_name = gr.Textbox(label="Name", placeholder="Excalibur / CyberArena")
            
            arch_details = gr.Textbox(label="Description / Effect / Theme", placeholder="High damage sword that heals on hit...")
            
            arch_btn = gr.Button("Generate Blueprint", variant="primary")
            arch_output = gr.Code(label="Generated Output", language="csharp")
            
            arch_btn.click(generate_blueprint, inputs=[arch_type, arch_name, arch_details], outputs=arch_output)

    # --- FOOTER ---
    gr.Markdown("---")
    gr.Markdown(LEGAL_TEXT)
    gr.Markdown(f"Β© 2025 {COMPANY_NAME}. All Rights Reserved.")

# ==============================================================================
# LAUNCHER (MCP ENABLED)
# ==============================================================================
if __name__ == "__main__":
    # mcp=True enables Claude/Cursor to control this app as a server
    app.launch(mcp=True)