ysn-rfd/text-dataset-tiny-code-script-py-format
USED of tahamajs/medicine_ds_persian for .parquet file USED of Alijafarixcs2/persian-it-llama2-2k for .parquet file USED of Abirate/english_quotes for .jsonl file NEW FILES (05/12/2025) NEW FILES (12/26/2025) NEW FILES (02/15/2026)
31.6k
1#!/usr/bin/env python3
2# TextWorld Adventure - A Complete Text-Based RPG Game
3# Author: YSNRFD
4# Version: 1.0.0
5
6import os
7import sys
8import json
9import random
10import time
11import pickle
12import math
13from enum import Enum
14from typing import Dict, List, Tuple, Optional, Any, Callable
15from dataclasses import dataclass, asdict
16import colorama
17from colorama import Fore, Back, Style
18
19# Initialize colorama for cross-platform colored text
20colorama.init()
21
22# Game Constants
23GAME_VERSION = "1.0.0"
24GAME_TITLE = "TextWorld Adventure"
25SAVE_FILE = "textworld_save.pkl"
26CONFIG_FILE = "textworld_config.json"
27
28# Game Enums
29class ItemType(Enum):
30 WEAPON = "weapon"
31 ARMOR = "armor"
32 CONSUMABLE = "consumable"
33 QUEST = "quest"
34 MISC = "misc"
35
36class SkillType(Enum):
37 COMBAT = "combat"
38 MAGIC = "magic"
39 STEALTH = "stealth"
40 SOCIAL = "social"
41
42class DamageType(Enum):
43 PHYSICAL = "physical"
44 FIRE = "fire"
45 ICE = "ice"
46 LIGHTNING = "lightning"
47 POISON = "poison"
48
49class QuestStatus(Enum):
50 NOT_STARTED = "not_started"
51 IN_PROGRESS = "in_progress"
52 COMPLETED = "completed"
53 FAILED = "failed"
54
55# Game Data Classes
56@dataclass
57class Item:
58 id: str
59 name: str
60 description: str
61 item_type: ItemType
62 value: int
63 rarity: str
64 stats: Dict[str, int]
65 consumable: bool = False
66 stackable: bool = False
67 max_stack: int = 1
68
69 def use(self, player):
70 if self.consumable:
71 if "heal" in self.stats:
72 player.health = min(player.max_health, player.health + self.stats["heal"])
73 return f"You used {self.name} and restored {self.stats['heal']} health."
74 elif "mana" in self.stats:
75 player.mana = min(player.max_mana, player.mana + self.stats["mana"])
76 return f"You used {self.name} and restored {self.stats['mana']} mana."
77 return f"You can't use {self.name}."
78
79@dataclass
80class Skill:
81 id: str
82 name: str
83 description: str
84 skill_type: SkillType
85 level: int = 1
86 experience: int = 0
87 max_experience: int = 100
88
89 def add_experience(self, amount):
90 self.experience += amount
91 leveled_up = False
92 while self.experience >= self.max_experience:
93 self.experience -= self.max_experience
94 self.level += 1
95 self.max_experience = int(self.max_experience * 1.5)
96 leveled_up = True
97 return leveled_up
98
99@dataclass
100class Quest:
101 id: str
102 name: str
103 description: str
104 objectives: List[str]
105 rewards: Dict[str, Any]
106 status: QuestStatus = QuestStatus.NOT_STARTED
107 progress: Dict[str, int] = None
108
109 def __post_init__(self):
110 if self.progress is None:
111 self.progress = {obj: 0 for obj in self.objectives}
112
113@dataclass
114class NPC:
115 id: str
116 name: str
117 description: str
118 dialogue: Dict[str, List[str]]
119 quests: List[str]
120 friendly: bool = True
121 shop_items: List[Item] = None
122
123 def get_dialogue(self, topic="default"):
124 return self.dialogue.get(topic, self.dialogue.get("default", ["I have nothing to say."]))
125
126@dataclass
127class Location:
128 id: str
129 name: str
130 description: str
131 exits: Dict[str, str]
132 npcs: List[str]
133 items: List[str]
134 enemies: List[str]
135 visited: bool = False
136
137 def get_description(self, game):
138 desc = f"{self.name}\n{'='*len(self.name)}\n{self.description}\n\n"
139
140 if self.exits:
141 desc += "Exits: " + ", ".join(self.exits.keys()) + "\n"
142
143 if self.npcs:
144 npcs = [game.npcs[npc_id] for npc_id in self.npcs]
145 desc += "People here: " + ", ".join([npc.name for npc in npcs]) + "\n"
146
147 if self.items:
148 items = [game.items[item_id] for item_id in self.items]
149 desc += "Items here: " + ", ".join([item.name for item in items]) + "\n"
150
151 if self.enemies:
152 enemies = [game.enemies[enemy_id] for enemy_id in self.enemies]
153 desc += "Enemies here: " + ", ".join([enemy.name for enemy in enemies]) + "\n"
154
155 return desc
156
157@dataclass
158class Enemy:
159 id: str
160 name: str
161 description: str
162 level: int
163 health: int
164 max_health: int
165 attack: int
166 defense: int
167 experience: int
168 gold: int
169 loot: List[str]
170 abilities: List[str]
171
172 def take_damage(self, damage):
173 actual_damage = max(1, damage - self.defense)
174 self.health -= actual_damage
175 return actual_damage
176
177 def is_alive(self):
178 return self.health > 0
179
180@dataclass
181class Player:
182 name: str
183 level: int = 1
184 experience: int = 0
185 max_experience: int = 100
186 health: int = 100
187 max_health: int = 100
188 mana: int = 50
189 max_mana: int = 50
190 attack: int = 10
191 defense: int = 5
192 gold: int = 50
193 inventory: List[Dict[str, Any]] = None
194 equipment: Dict[str, str] = None
195 skills: Dict[str, Skill] = None
196 quests: Dict[str, Quest] = None
197 current_location: str = "town_square"
198
199 def __post_init__(self):
200 if self.inventory is None:
201 self.inventory = []
202 if self.equipment is None:
203 self.equipment = {"weapon": None, "armor": None, "accessory": None}
204 if self.skills is None:
205 self.skills = {
206 "combat": Skill("combat", "Combat", "Your skill in fighting", SkillType.COMBAT),
207 "magic": Skill("magic", "Magic", "Your skill in casting spells", SkillType.MAGIC),
208 "stealth": Skill("stealth", "Stealth", "Your skill in moving unseen", SkillType.STEALTH),
209 "social": Skill("social", "Social", "Your skill in interacting with others", SkillType.SOCIAL)
210 }
211 if self.quests is None:
212 self.quests = {}
213
214 def add_experience(self, amount):
215 self.experience += amount
216 leveled_up = False
217 while self.experience >= self.max_experience:
218 self.experience -= self.max_experience
219 self.level += 1
220 self.max_experience = int(self.max_experience * 1.5)
221 self.max_health += 10
222 self.health = self.max_health
223 self.max_mana += 5
224 self.mana = self.max_mana
225 self.attack += 2
226 self.defense += 1
227 leveled_up = True
228 return leveled_up
229
230 def add_item(self, item, quantity=1):
231 for inv_item in self.inventory:
232 if inv_item["item"].id == item.id and item.stackable:
233 inv_item["quantity"] += quantity
234 return True
235
236 self.inventory.append({"item": item, "quantity": quantity})
237 return True
238
239 def remove_item(self, item_id, quantity=1):
240 for i, inv_item in enumerate(self.inventory):
241 if inv_item["item"].id == item_id:
242 if inv_item["quantity"] <= quantity:
243 self.inventory.pop(i)
244 return True
245 else:
246 inv_item["quantity"] -= quantity
247 return True
248 return False
249
250 def get_item(self, item_id):
251 for inv_item in self.inventory:
252 if inv_item["item"].id == item_id:
253 return inv_item["item"]
254 return None
255
256 def equip_item(self, item_id):
257 item = self.get_item(item_id)
258 if not item:
259 return False, "You don't have that item."
260
261 if item.item_type == ItemType.WEAPON:
262 if self.equipment["weapon"]:
263 self.add_item(self.get_item(self.equipment["weapon"]))
264 self.equipment["weapon"] = item_id
265 self.attack += item.stats.get("attack", 0)
266 return True, f"You equipped {item.name}."
267
268 elif item.item_type == ItemType.ARMOR:
269 if self.equipment["armor"]:
270 self.add_item(self.get_item(self.equipment["armor"]))
271 self.equipment["armor"] = item_id
272 self.defense += item.stats.get("defense", 0)
273 return True, f"You equipped {item.name}."
274
275 return False, "You can't equip that item."
276
277 def get_total_attack(self):
278 total = self.attack
279 if self.equipment["weapon"]:
280 weapon = self.get_item(self.equipment["weapon"])
281 if weapon:
282 total += weapon.stats.get("attack", 0)
283 return total
284
285 def get_total_defense(self):
286 total = self.defense
287 if self.equipment["armor"]:
288 armor = self.get_item(self.equipment["armor"])
289 if armor:
290 total += armor.stats.get("defense", 0)
291 return total
292
293# Game Configuration
294class GameConfig:
295 def __init__(self):
296 self.settings = {
297 "text_speed": 0.03,
298 "auto_save": True,
299 "auto_save_interval": 300, # seconds
300 "color_scheme": "default",
301 "difficulty": "normal",
302 "debug_mode": False
303 }
304 self.load_config()
305
306 def load_config(self):
307 try:
308 if os.path.exists(CONFIG_FILE):
309 with open(CONFIG_FILE, 'r') as f:
310 self.settings.update(json.load(f))
311 except Exception as e:
312 print(f"Error loading config: {e}")
313
314 def save_config(self):
315 try:
316 with open(CONFIG_FILE, 'w') as f:
317 json.dump(self.settings, f, indent=2)
318 except Exception as e:
319 print(f"Error saving config: {e}")
320
321 def get_setting(self, key, default=None):
322 return self.settings.get(key, default)
323
324 def set_setting(self, key, value):
325 self.settings[key] = value
326 self.save_config()
327
328# Plugin System
329class PluginManager:
330 def __init__(self, game):
331 self.game = game
332 self.plugins = {}
333 self.hooks = {
334 "on_command": [],
335 "on_move": [],
336 "on_combat": [],
337 "on_level_up": [],
338 "on_quest_complete": [],
339 "on_item_pickup": []
340 }
341
342 def load_plugin(self, plugin_name):
343 try:
344 # This is a simplified plugin loader
345 # In a real implementation, you'd load from files
346 if plugin_name == "debug":
347 self.plugins[plugin_name] = DebugPlugin(self.game)
348 elif plugin_name == "hardcore":
349 self.plugins[plugin_name] = HardcorePlugin(self.game)
350 return True
351 except Exception as e:
352 print(f"Error loading plugin {plugin_name}: {e}")
353 return False
354
355 def register_hook(self, hook_name, callback):
356 if hook_name in self.hooks:
357 self.hooks[hook_name].append(callback)
358
359 def trigger_hook(self, hook_name, *args, **kwargs):
360 if hook_name in self.hooks:
361 for callback in self.hooks[hook_name]:
362 try:
363 callback(*args, **kwargs)
364 except Exception as e:
365 print(f"Error in hook {hook_name}: {e}")
366
367# Base Plugin Class
368class BasePlugin:
369 def __init__(self, game):
370 self.game = game
371 self.name = "Base Plugin"
372 self.version = "1.0.0"
373 self.description = "Base plugin class"
374 self.enabled = True
375
376 def initialize(self):
377 pass
378
379 def on_command(self, command, args):
380 pass
381
382 def on_move(self, from_location, to_location):
383 pass
384
385 def on_combat(self, player, enemy):
386 pass
387
388 def on_level_up(self, player):
389 pass
390
391 def on_quest_complete(self, quest):
392 pass
393
394 def on_item_pickup(self, item):
395 pass
396
397# Example Plugins
398class DebugPlugin(BasePlugin):
399 def __init__(self, game):
400 super().__init__(game)
401 self.name = "Debug Plugin"
402 self.description = "Adds debug commands to the game"
403
404 def initialize(self):
405 self.game.register_command("debug", self.debug_command)
406
407 def debug_command(self, args):
408 if len(args) < 1:
409 return "Usage: debug <command>"
410
411 cmd = args[0].lower()
412 if cmd == "teleport":
413 if len(args) < 2:
414 return "Usage: debug teleport <location_id>"
415 location_id = args[1]
416 if location_id in self.game.locations:
417 self.game.player.current_location = location_id
418 return f"Teleported to {self.game.locations[location_id].name}"
419 return "Location not found"
420
421 elif cmd == "give":
422 if len(args) < 2:
423 return "Usage: debug give <item_id>"
424 item_id = args[1]
425 if item_id in self.game.items:
426 self.game.player.add_item(self.game.items[item_id])
427 return f"Gave {self.game.items[item_id].name}"
428 return "Item not found"
429
430 elif cmd == "level":
431 if len(args) < 2:
432 return "Usage: debug level <amount>"
433 try:
434 amount = int(args[1])
435 if self.game.player.add_experience(amount):
436 return f"Gained {amount} experience and leveled up!"
437 return f"Gained {amount} experience"
438 except ValueError:
439 return "Invalid amount"
440
441 elif cmd == "heal":
442 self.game.player.health = self.game.player.max_health
443 self.game.player.mana = self.game.player.max_mana
444 return "Fully healed"
445
446 return "Unknown debug command"
447
448class HardcorePlugin(BasePlugin):
449 def __init__(self, game):
450 super().__init__(game)
451 self.name = "Hardcore Mode"
452 self.description = "Makes the game more challenging"
453
454 def initialize(self):
455 self.game.register_hook("on_combat", self.on_combat)
456 self.game.register_hook("on_level_up", self.on_level_up)
457
458 def on_combat(self, player, enemy):
459 enemy.attack = int(enemy.attack * 1.5)
460 enemy.health = int(enemy.health * 1.5)
461 enemy.max_health = enemy.health
462
463 def on_level_up(self, player):
464 player.max_health = int(player.max_health * 0.8)
465 player.health = player.max_health
466
467# Main Game Class
468class TextWorldGame:
469 def __init__(self):
470 self.config = GameConfig()
471 self.plugin_manager = PluginManager(self)
472 self.player = None
473 self.locations = {}
474 self.items = {}
475 self.npcs = {}
476 self.enemies = {}
477 self.quests = {}
478 self.running = True
479 self.commands = {}
480 self.combat_active = False
481 self.current_enemy = None
482
483 self.initialize_game_data()
484 self.register_commands()
485 self.load_plugins()
486
487 def initialize_game_data(self):
488 # Initialize Items
489 self.items = {
490 "sword": Item("sword", "Iron Sword", "A simple iron sword", ItemType.WEAPON, 50, "common", {"attack": 5}),
491 "shield": Item("shield", "Wooden Shield", "A basic wooden shield", ItemType.ARMOR, 30, "common", {"defense": 3}),
492 "potion": Item("potion", "Health Potion", "A red potion that restores health", ItemType.CONSUMABLE, 10, "common", {"heal": 25}, consumable=True, stackable=True, max_stack=10),
493 "mana_potion": Item("mana_potion", "Mana Potion", "A blue potion that restores mana", ItemType.CONSUMABLE, 15, "common", {"mana": 15}, consumable=True, stackable=True, max_stack=10),
494 "leather_armor": Item("leather_armor", "Leather Armor", "Basic leather armor", ItemType.ARMOR, 40, "common", {"defense": 5}),
495 "magic_scroll": Item("magic_scroll", "Magic Scroll", "A scroll containing a magic spell", ItemType.MISC, 100, "rare", {"magic_power": 10}),
496 "gold_coin": Item("gold_coin", "Gold Coin", "A shiny gold coin", ItemType.MISC, 1, "common", {}, stackable=True, max_stack=999),
497 "dragon_sword": Item("dragon_sword", "Dragon Sword", "A legendary sword forged from dragon scales", ItemType.WEAPON, 1000, "legendary", {"attack": 20, "fire_damage": 10}),
498 "dragon_armor": Item("dragon_armor", "Dragon Armor", "Legendary armor made from dragon scales", ItemType.ARMOR, 1500, "legendary", {"defense": 15, "fire_resistance": 20}),
499 }
500
501 # Initialize Enemies
502 self.enemies = {
503 "goblin": Enemy("goblin", "Goblin", "A small green creature with sharp teeth", 1, 30, 30, 8, 2, 20, 10, ["potion"], ["bite"]),
504 "orc": Enemy("orc", "Orc", "A large green brute with a club", 3, 60, 60, 15, 5, 50, 25, ["sword", "potion"], ["smash"]),
505 "dragon": Enemy("dragon", "Dragon", "A massive fire-breathing dragon", 10, 200, 200, 30, 15, 200, 500, ["dragon_sword", "dragon_armor"], ["fire_breath", "tail_swipe"]),
506 "skeleton": Enemy("skeleton", "Skeleton", "An undead warrior with rusty armor", 2, 40, 40, 12, 4, 30, 15, ["gold_coin"], ["bone_throw"]),
507 "dark_wizard": Enemy("dark_wizard", "Dark Wizard", "A powerful spellcaster in dark robes", 5, 80, 80, 20, 8, 100, 100, ["magic_scroll", "mana_potion"], ["fireball", "lightning"]),
508 }
509
510 # Initialize NPCs
511 self.npcs = {
512 "merchant": NPC("merchant", "Merchant", "A friendly merchant selling various items", {
513 "default": ["Welcome to my shop! What can I get for you?", "I have the finest items in town!"],
514 "buy": ["What would you like to buy?"],
515 "sell": ["What would you like to sell?"],
516 "quest": ["I have a quest for you if you're interested."]
517 }, ["merchant_quest"], True, [self.items["potion"], self.items["sword"], self.items["shield"]]),
518
519 "guard": NPC("guard", "Town Guard", "A brave guard protecting the town", {
520 "default": ["Halt! State your business.", "The town is safe under my watch."],
521 "dragon": ["A dragon has been spotted in the mountains! Be careful if you go there."],
522 "quest": ["We need help dealing with the goblins in the forest."]
523 }, ["goblin_quest"], True),
524
525 "wizard": NPC("wizard", "Wizard", "A mysterious wizard with powerful magic", {
526 "default": ["Greetings, traveler. I sense great potential in you.", "Magic flows through the world like a river."],
527 "magic": ["Magic is the art of manipulating reality itself.", "Would you like to learn some spells?"],
528 "quest": ["I need someone to retrieve a rare artifact for me."]
529 }, ["wizard_quest"], True),
530 }
531
532 # Initialize Locations
533 self.locations = {
534 "town_square": Location("town_square", "Town Square", "The central square of the town. People are bustling about.", {
535 "north": "town_gate",
536 "east": "market",
537 "west": "tavern",
538 "south": "temple"
539 }, ["guard"], [], []),
540
541 "town_gate": Location("town_gate", "Town Gate", "The main entrance to the town. A large wooden gate stands open.", {
542 "south": "town_square",
543 "north": "forest_entrance"
544 }, [], [], []),
545
546 "market": Location("market", "Market", "A busy market with merchants selling their wares.", {
547 "west": "town_square"
548 }, ["merchant"], [], []),
549
550 "tavern": Location("tavern", "Tavern", "A cozy tavern with a warm fireplace and friendly patrons.", {
551 "east": "town_square"
552 }, [], ["potion"], []),
553
554 "temple": Location("temple", "Temple", "A peaceful temple where people come to pray and heal.", {
555 "north": "town_square"
556 }, [], [], []),
557
558 "forest_entrance": Location("forest_entrance", "Forest Entrance", "The edge of a dark forest. You can hear strange noises from within.", {
559 "south": "town_gate",
560 "north": "deep_forest",
561 "east": "river",
562 "west": "cave_entrance"
563 }, [], [], ["goblin"]),
564
565 "deep_forest": Location("deep_forest", "Deep Forest", "A dense forest with tall trees and little light.", {
566 "south": "forest_entrance",
567 "north": "mountain_base"
568 }, [], [], ["goblin", "skeleton"]),
569
570 "river": Location("river", "River", "A wide river flowing from the mountains.", {
571 "west": "forest_entrance",
572 "east": "bridge"
573 }, [], [], []),
574
575 "bridge": Location("bridge", "Bridge", "An old stone bridge crossing the river.", {
576 "west": "river",
577 "east": "abandoned_tower"
578 }, [], [], ["orc"]),
579
580 "cave_entrance": Location("cave_entrance", "Cave Entrance", "The entrance to a dark cave. You can feel cold air coming from inside.", {
581 "east": "forest_entrance",
582 "west": "cave_depths"
583 }, [], [], ["skeleton"]),
584
585 "cave_depths": Location("cave_depths", "Cave Depths", "The deepest part of the cave. It's dark and damp here.", {
586 "east": "cave_entrance"
587 }, [], ["magic_scroll"], ["dark_wizard"]),
588
589 "mountain_base": Location("mountain_base", "Mountain Base", "The base of a tall mountain. A path leads up.", {
590 "south": "deep_forest",
591 "north": "mountain_path"
592 }, [], [], []),
593
594 "mountain_path": Location("mountain_path", "Mountain Path", "A narrow path winding up the mountain.", {
595 "south": "mountain_base",
596 "north": "dragon_lair"
597 }, [], [], ["orc"]),
598
599 "dragon_lair": Location("dragon_lair", "Dragon Lair", "A massive cave where a dragon lives. You can feel the heat.", {
600 "south": "mountain_path"
601 }, [], [], ["dragon"]),
602
603 "abandoned_tower": Location("abandoned_tower", "Abandoned Tower", "An old tower that was once home to a powerful wizard.", {
604 "west": "bridge"
605 }, ["wizard"], [], []),
606 }
607
608 # Initialize Quests
609 self.quests = {
610 "merchant_quest": Quest("merchant_quest", "Merchant's Request", "The merchant needs you to deliver a package to the wizard.",
611 ["Deliver package to wizard"], {"experience": 100, "gold": 50}),
612
613 "goblin_quest": Quest("goblin_quest", "Goblin Problem", "The town guard needs help dealing with goblins in the forest.",
614 ["Defeat 3 goblins"], {"experience": 150, "gold": 75}),
615
616 "wizard_quest": Quest("wizard_quest", "Wizard's Artifact", "The wizard needs you to retrieve a magic scroll from the cave depths.",
617 ["Find magic scroll in cave depths"], {"experience": 200, "gold": 100, "item": "mana_potion"}),
618
619 "dragon_quest": Quest("dragon_quest", "Dragon Slayer", "A dragon terrorizes the mountains. Someone needs to defeat it.",
620 ["Defeat the dragon"], {"experience": 500, "gold": 500, "item": "dragon_sword"}),
621 }
622
623 def register_commands(self):
624 self.commands = {
625 "help": self.cmd_help,
626 "look": self.cmd_look,
627 "go": self.cmd_go,
628 "move": self.cmd_go,
629 "inventory": self.cmd_inventory,
630 "inv": self.cmd_inventory,
631 "equip": self.cmd_equip,
632 "unequip": self.cmd_unequip,
633 "use": self.cmd_use,
634 "take": self.cmd_take,
635 "get": self.cmd_take,
636 "drop": self.cmd_drop,
637 "talk": self.cmd_talk,
638 "attack": self.cmd_attack,
639 "fight": self.cmd_attack,
640 "quests": self.cmd_quests,
641 "status": self.cmd_status,
642 "save": self.cmd_save,
643 "load": self.cmd_load,
644 "quit": self.cmd_quit,
645 "exit": self.cmd_quit,
646 "rest": self.cmd_rest,
647 "skills": self.cmd_skills,
648 "shop": self.cmd_shop,
649 "buy": self.cmd_buy,
650 "sell": self.cmd_sell,
651 "map": self.cmd_map,
652 "clear": self.cmd_clear,
653 "version": self.cmd_version,
654 "config": self.cmd_config,
655 }
656
657 def load_plugins(self):
658 # Load default plugins
659 if self.config.get_setting("debug_mode", False):
660 self.plugin_manager.load_plugin("debug")
661
662 # Initialize all loaded plugins
663 for plugin in self.plugin_manager.plugins.values():
664 plugin.initialize()
665
666 def register_command(self, command, callback):
667 self.commands[command] = callback
668
669 def register_hook(self, hook_name, callback):
670 self.plugin_manager.register_hook(hook_name, callback)
671
672 def start(self):
673 self.clear_screen()
674 self.print_title()
675
676 # Check for save file
677 if os.path.exists(SAVE_FILE):
678 choice = self.get_input("A save file exists. Do you want to load it? (y/n): ").lower()
679 if choice == 'y':
680 self.load_game()
681 self.print_slow("Game loaded successfully!")
682 self.game_loop()
683 return
684
685 # Character creation
686 self.print_slow("Welcome to TextWorld Adventure!")
687 self.print_slow("Let's create your character...")
688
689 name = self.get_input("Enter your character's name: ")
690 while not name:
691 name = self.get_input("Please enter a valid name: ")
692
693 self.player = Player(name)
694 self.player.add_item(self.items["potion"], 3)
695 self.player.add_item(self.items["gold_coin"], 50)
696
697 self.print_slow(f"\nWelcome, {self.player.name}!")
698 self.print_slow("Your adventure begins in the Town Square.")
699 self.print_slow("Type 'help' for a list of commands.")
700
701 self.game_loop()
702
703 def game_loop(self):
704 last_auto_save = time.time()
705
706 while self.running:
707 # Auto-save check
708 if self.config.get_setting("auto_save", True):
709 if time.time() - last_auto_save > self.config.get_setting("auto_save_interval", 300):
710 self.save_game(silent=True)
711 last_auto_save = time.time()
712
713 # Get player input
714 if self.combat_active:
715 prompt = f"{Fore.RED}[COMBAT]{Style.RESET_ALL} What do you do? > "
716 else:
717 location = self.locations[self.player.current_location]
718 prompt = f"{Fore.GREEN}[{location.name}]{Style.RESET_ALL} What do you do? > "
719
720 command_input = self.get_input(prompt).strip().lower()
721
722 # Parse command
723 if not command_input:
724 continue
725
726 parts = command_input.split()
727 command = parts[0]
728 args = parts[1:] if len(parts) > 1 else []
729
730 # Trigger command hook
731 self.plugin_manager.trigger_hook("on_command", command, args)
732
733 # Execute command
734 if command in self.commands:
735 try:
736 result = self.commands[command](args)
737 if result:
738 self.print_slow(result)
739 except Exception as e:
740 self.print_slow(f"Error executing command: {e}")
741 else:
742 self.print_slow(f"Unknown command: {command}. Type 'help' for available commands.")
743
744 def cmd_help(self, args):
745 help_text = f"""
746{Fore.CYAN}=== TextWorld Adventure Commands ==={Style.RESET_ALL}
747
748{Fore.YELLOW}Movement:{Style.RESET_ALL}
749 go/move <direction> - Move in a direction (north, south, east, west)
750 look - Look around the current location
751 map - Show a simple map of the area
752
753{Fore.YELLOW}Inventory:{Style.RESET_ALL}
754 inventory/inv - Show your inventory
755 equip <item> - Equip an item
756 unequip <item> - Unequip an item
757 use <item> - Use an item
758 take/get <item> - Pick up an item
759 drop <item> - Drop an item
760
761{Fore.YELLOW}Combat:{Style.RESET_ALL}
762 attack/fight <enemy> - Attack an enemy
763 rest - Rest to restore health and mana
764
765{Fore.YELLOW}NPCs:{Style.RESET_ALL}
766 talk <npc> - Talk to an NPC
767 shop - Open shop interface (if available)
768 buy <item> - Buy an item from shop
769 sell <item> - Sell an item to shop
770
771{Fore.YELLOW}Character:{Style.RESET_ALL}
772 status - Show your character status
773 skills - Show your skills
774 quests - Show your active quests
775
776{Fore.YELLOW}System:{Style.RESET_ALL}
777 save - Save the game
778 load - Load a saved game
779 config <setting> <value> - Change game settings
780 clear - Clear the screen
781 version - Show game version
782 quit/exit - Quit the game
783"""
784 return help_text
785
786 def cmd_look(self, args):
787 location = self.locations[self.player.current_location]
788 location.visited = True
789 return location.get_description(self)
790
791 def cmd_go(self, args):
792 if not args:
793 return "Go where? Specify a direction (north, south, east, west)."
794
795 direction = args[0].lower()
796 location = self.locations[self.player.current_location]
797
798 if direction not in location.exits:
799 return f"You can't go {direction} from here."
800
801 new_location_id = location.exits[direction]
802 new_location = self.locations[new_location_id]
803
804 # Trigger move hook
805 self.plugin_manager.trigger_hook("on_move", self.player.current_location, new_location_id)
806
807 self.player.current_location = new_location_id
808
809 # Check for enemies in new location
810 if new_location.enemies and random.random() < 0.5:
811 enemy_id = random.choice(new_location.enemies)
812 enemy = Enemy(**asdict(self.enemies[enemy_id]))
813 self.start_combat(enemy)
814
815 return new_location.get_description(self)
816
817 def cmd_inventory(self, args):
818 if not self.player.inventory:
819 return "Your inventory is empty."
820
821 inv_text = f"{Fore.CYAN}=== Inventory ==={Style.RESET_ALL}\n"
822
823 for inv_item in self.player.inventory:
824 item = inv_item["item"]
825 quantity = inv_item["quantity"]
826 if quantity > 1:
827 inv_text += f"{item.name} x{quantity} - {item.description}\n"
828 else:
829 inv_text += f"{item.name} - {item.description}\n"
830
831 # Show equipped items
832 inv_text += f"\n{Fore.YELLOW}=== Equipped Items ==={Style.RESET_ALL}\n"
833 for slot, item_id in self.player.equipment.items():
834 if item_id:
835 item = self.player.get_item(item_id)
836 if item:
837 inv_text += f"{slot.capitalize()}: {item.name}\n"
838
839 return inv_text
840
841 def cmd_equip(self, args):
842 if not args:
843 return "Equip what? Specify an item name."
844
845 item_name = " ".join(args).lower()
846 item = None
847
848 for inv_item in self.player.inventory:
849 if inv_item["item"].name.lower() == item_name:
850 item = inv_item["item"]
851 break
852
853 if not item:
854 return f"You don't have a {item_name}."
855
856 success, message = self.player.equip_item(item.id)
857 return message
858
859 def cmd_unequip(self, args):
860 if not args:
861 return "Unequip what? Specify 'weapon' or 'armor'."
862
863 slot = args[0].lower()
864 if slot not in self.player.equipment:
865 return "You can only unequip 'weapon' or 'armor'."
866
867 if not self.player.equipment[slot]:
868 return f"You don't have anything equipped in your {slot} slot."
869
870 item_id = self.player.equipment[slot]
871 item = self.player.get_item(item_id)
872
873 if item:
874 self.player.add_item(item)
875 if slot == "weapon":
876 self.player.attack -= item.stats.get("attack", 0)
877 elif slot == "armor":
878 self.player.defense -= item.stats.get("defense", 0)
879
880 self.player.equipment[slot] = None
881 return f"You unequipped {item.name}."
882
883 def cmd_use(self, args):
884 if not args:
885 return "Use what? Specify an item name."
886
887 item_name = " ".join(args).lower()
888 item = None
889
890 for inv_item in self.player.inventory:
891 if inv_item["item"].name.lower() == item_name:
892 item = inv_item["item"]
893 break
894
895 if not item:
896 return f"You don't have a {item_name}."
897
898 if not item.consumable:
899 return f"You can't use {item.name}."
900
901 result = item.use(self.player)
902
903 # Trigger item pickup hook (for use as well)
904 self.plugin_manager.trigger_hook("on_item_pickup", item)
905
906 if item.consumable:
907 self.player.remove_item(item.id)
908
909 return result
910
911 def cmd_take(self, args):
912 if not args:
913 return "Take what? Specify an item name."
914
915 item_name = " ".join(args).lower()
916 location = self.locations[self.player.current_location]
917
918 for item_id in location.items[:]:
919 item = self.items[item_id]
920 if item.name.lower() == item_name:
921 self.player.add_item(item)
922 location.items.remove(item_id)
923
924 # Trigger item pickup hook
925 self.plugin_manager.trigger_hook("on_item_pickup", item)
926
927 return f"You took {item.name}."
928
929 return f"There's no {item_name} here."
930
931 def cmd_drop(self, args):
932 if not args:
933 return "Drop what? Specify an item name."
934
935 item_name = " ".join(args).lower()
936 location = self.locations[self.player.current_location]
937
938 for inv_item in self.player.inventory[:]:
939 item = inv_item["item"]
940 if item.name.lower() == item_name:
941 self.player.remove_item(item.id)
942 location.items.append(item.id)
943 return f"You dropped {item.name}."
944
945 return f"You don't have a {item_name}."
946
947 def cmd_talk(self, args):
948 if not args:
949 return "Talk to whom? Specify an NPC name."
950
951 npc_name = " ".join(args).lower()
952 location = self.locations[self.player.current_location]
953
954 for npc_id in location.npcs:
955 npc = self.npcs[npc_id]
956 if npc.name.lower() == npc_name:
957 dialogue = npc.get_dialogue()
958 return f"{npc.name}: {random.choice(dialogue)}"
959
960 return f"There's no one named {npc_name} here."
961
962 def cmd_attack(self, args):
963 if not self.combat_active:
964 return "There's nothing to attack here."
965
966 if not self.current_enemy:
967 self.combat_active = False
968 return "The enemy has disappeared."
969
970 # Player attacks
971 player_damage = self.player.get_total_attack()
972 actual_damage = self.current_enemy.take_damage(player_damage)
973
974 result = f"You attack {self.current_enemy.name} for {actual_damage} damage!\n"
975
976 # Trigger combat hook
977 self.plugin_manager.trigger_hook("on_combat", self.player, self.current_enemy)
978
979 if not self.current_enemy.is_alive():
980 result += f"You defeated {self.current_enemy.name}!\n"
981 result += f"You gained {self.current_enemy.experience} experience and {self.current_enemy.gold} gold!\n"
982
983 # Level up check
984 if self.player.add_experience(self.current_enemy.experience):
985 result += f"LEVEL UP! You are now level {self.player.level}!\n"
986 # Trigger level up hook
987 self.plugin_manager.trigger_hook("on_level_up", self.player)
988
989 self.player.gold += self.current_enemy.gold
990
991 # Loot
992 if self.current_enemy.loot:
993 loot_item = random.choice(self.current_enemy.loot)
994 if loot_item in self.items:
995 item = self.items[loot_item]
996 self.player.add_item(item)
997 result += f"You found {item.name}!\n"
998
999 self.combat_active = False
1000 self.current_enemy = None
1001
1002 # Update quest progress
1003 self.update_quest_progress("defeat", self.current_enemy.id if self.current_enemy else "unknown")
1004
1005 return result
1006
1007 # Enemy attacks
1008 enemy_damage = max(1, self.current_enemy.attack - self.player.get_total_defense())
1009 self.player.health -= enemy_damage
1010 result += f"{self.current_enemy.name} attacks you for {enemy_damage} damage!\n"
1011
1012 if self.player.health <= 0:
1013 result += f"{Fore.RED}You have been defeated! Game Over.{Style.RESET_ALL}\n"
1014 self.running = False
1015
1016 return result
1017
1018 def cmd_quests(self, args):
1019 if not self.player.quests:
1020 return "You don't have any active quests."
1021
1022 quest_text = f"{Fore.CYAN}=== Active Quests ==={Style.RESET_ALL}\n"
1023
1024 for quest_id, quest in self.player.quests.items():
1025 if quest.status == QuestStatus.IN_PROGRESS:
1026 quest_text += f"{Fore.YELLOW}{quest.name}{Style.RESET_ALL}\n"
1027 quest_text += f" {quest.description}\n"
1028 quest_text += " Objectives:\n"
1029 for obj, progress in quest.progress.items():
1030 quest_text += f" - {obj}: {progress}\n"
1031 quest_text += "\n"
1032
1033 return quest_text
1034
1035 def cmd_status(self, args):
1036 status_text = f"""
1037{Fore.CYAN}=== Character Status ==={Style.RESET_ALL}
1038Name: {self.player.name}
1039Level: {self.player.level} ({self.player.experience}/{self.player.max_experience} XP)
1040Health: {self.player.health}/{self.player.max_health}
1041Mana: {self.player.mana}/{self.player.max_mana}
1042Attack: {self.player.get_total_attack()} (Base: {self.player.attack})
1043Defense: {self.player.get_total_defense()} (Base: {self.player.defense})
1044Gold: {self.player.gold}
1045Location: {self.locations[self.player.current_location].name}
1046"""
1047 return status_text
1048
1049 def cmd_save(self, args):
1050 return self.save_game()
1051
1052 def cmd_load(self, args):
1053 return self.load_game()
1054
1055 def cmd_quit(self, args):
1056 choice = self.get_input("Are you sure you want to quit? (y/n): ").lower()
1057 if choice == 'y':
1058 self.save_game()
1059 self.running = False
1060 return "Thanks for playing TextWorld Adventure!"
1061 return "Continuing game..."
1062
1063 def cmd_rest(self, args):
1064 if self.combat_active:
1065 return "You can't rest during combat!"
1066
1067 location = self.locations[self.player.current_location]
1068
1069 # Can only rest in safe locations
1070 if location.id in ["town_square", "tavern", "temple"]:
1071 self.player.health = self.player.max_health
1072 self.player.mana = self.player.max_mana
1073 return "You rest and recover your health and mana."
1074
1075 # Resting in dangerous locations has a chance of encounter
1076 if random.random() < 0.3:
1077 if location.enemies:
1078 enemy_id = random.choice(location.enemies)
1079 enemy = Enemy(**asdict(self.enemies[enemy_id]))
1080 self.start_combat(enemy)
1081 return "While resting, you were ambushed!"
1082
1083 # Partial rest
1084 heal_amount = int(self.player.max_health * 0.3)
1085 mana_amount = int(self.player.max_mana * 0.3)
1086 self.player.health = min(self.player.max_health, self.player.health + heal_amount)
1087 self.player.mana = min(self.player.max_mana, self.player.mana + mana_amount)
1088 return f"You rest and recover {heal_amount} health and {mana_amount} mana."
1089
1090 def cmd_skills(self, args):
1091 skills_text = f"{Fore.CYAN}=== Skills ==={Style.RESET_ALL}\n"
1092
1093 for skill_id, skill in self.player.skills.items():
1094 skills_text += f"{Fore.YELLOW}{skill.name}{Style.RESET_ALL} (Level {skill.level})\n"
1095 skills_text += f" {skill.description}\n"
1096 skills_text += f" Experience: {skill.experience}/{skill.max_experience}\n\n"
1097
1098 return skills_text
1099
1100 def cmd_shop(self, args):
1101 location = self.locations[self.player.current_location]
1102
1103 # Find merchant in current location
1104 merchant = None
1105 for npc_id in location.npcs:
1106 npc = self.npcs[npc_id]
1107 if npc.shop_items:
1108 merchant = npc
1109 break
1110
1111 if not merchant:
1112 return "There's no shop here."
1113
1114 shop_text = f"{Fore.CYAN}=== {merchant.name}'s Shop ==={Style.RESET_ALL}\n"
1115 shop_text += "Items for sale:\n"
1116
1117 for item in merchant.shop_items:
1118 shop_text += f" {item.name} - {item.value} gold\n"
1119
1120 shop_text += "\nType 'buy <item>' to purchase or 'sell <item>' to sell."
1121 return shop_text
1122
1123 def cmd_buy(self, args):
1124 if not args:
1125 return "Buy what? Specify an item name."
1126
1127 item_name = " ".join(args).lower()
1128 location = self.locations[self.player.current_location]
1129
1130 # Find merchant in current location
1131 merchant = None
1132 for npc_id in location.npcs:
1133 npc = self.npcs[npc_id]
1134 if npc.shop_items:
1135 merchant = npc
1136 break
1137
1138 if not merchant:
1139 return "There's no shop here."
1140
1141 # Find item in shop
1142 for item in merchant.shop_items:
1143 if item.name.lower() == item_name:
1144 if self.player.gold >= item.value:
1145 self.player.gold -= item.value
1146 self.player.add_item(item)
1147 return f"You bought {item.name} for {item.value} gold."
1148 else:
1149 return f"You don't have enough gold to buy {item.name}."
1150
1151 return f"{merchant.name} doesn't have {item_name} for sale."
1152
1153 def cmd_sell(self, args):
1154 if not args:
1155 return "Sell what? Specify an item name."
1156
1157 item_name = " ".join(args).lower()
1158 location = self.locations[self.player.current_location]
1159
1160 # Find merchant in current location
1161 merchant = None
1162 for npc_id in location.npcs:
1163 npc = self.npcs[npc_id]
1164 if npc.shop_items:
1165 merchant = npc
1166 break
1167
1168 if not merchant:
1169 return "There's no shop here."
1170
1171 # Find item in inventory
1172 for inv_item in self.player.inventory[:]:
1173 item = inv_item["item"]
1174 if item.name.lower() == item_name:
1175 sell_price = int(item.value * 0.5) # Sell for half the value
1176 self.player.gold += sell_price
1177 self.player.remove_item(item.id)
1178 return f"You sold {item.name} for {sell_price} gold."
1179
1180 return f"You don't have a {item_name} to sell."
1181
1182 def cmd_map(self, args):
1183 location = self.locations[self.player.current_location]
1184
1185 # Simple ASCII map
1186 map_text = f"{Fore.CYAN}=== Map ==={Style.RESET_ALL}\n"
1187 map_text += "You are here: *\n\n"
1188
1189 # Show connected locations
1190 for direction, loc_id in location.exits.items():
1191 loc = self.locations[loc_id]
1192 map_text += f"{direction.capitalize()}: {loc.name}\n"
1193
1194 return map_text
1195
1196 def cmd_clear(self, args):
1197 self.clear_screen()
1198 return ""
1199
1200 def cmd_version(self, args):
