Advertisement
thewindmage420

Market

Feb 15th, 2025
451
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 90.30 KB | None | 0 0
  1. import random
  2. import sys
  3. import time
  4. from colorama import Fore, Style, init
  5.  
  6. # Initialize colorama with automatic reset and stripping for unsupported environments
  7. init(autoreset=True, strip=True)
  8.  
  9. # ASCII Art Title
  10. TITLE = f"""
  11. {Fore.CYAN}  __  __    _    ____  _  _______ _____
  12. |  \/  |  / \ |  _ \| |/ / ____|_   _|
  13. | |\/| | / _ \ | |_) | ' /|  _|   | |  
  14. | |  | |/ ___ \|  _ <| . \| |___  | |  
  15. |_|  |_/_/   \_\_| \_\_|\_\_____| |_|  
  16.                                        
  17. {Style.RESET_ALL}
  18. """
  19.  
  20. # Welcome Text without the '=' lines, with yellow coloring
  21. WELCOME_TEXT = f"""{Fore.YELLOW}Welcome to the Text-Based Trading Game!
  22. Your goal is to accumulate as much wealth as possible within a set number of turns.
  23. Explore a variety of goods, manage your cargo capacity, and navigate the dynamic market.
  24. Be mindful of your relationships with towns—they play a crucial role in your success.{Style.RESET_ALL}"""
  25.  
  26. # HELP Text with detailed rules and trading categories
  27. HELP_TEXT = f"""
  28. {Fore.GREEN}Game Rules and Instructions:{Style.RESET_ALL}
  29.  
  30. 1. {Fore.YELLOW}Objective:{Style.RESET_ALL}
  31.   Accumulate as much wealth as possible within the allotted number of turns.
  32.  
  33. 2. {Fore.YELLOW}Commands:{Style.RESET_ALL}
  34.   - {Fore.CYAN}BUY:{Style.RESET_ALL} Purchase goods from the market.
  35.   - {Fore.CYAN}SELL:{Style.RESET_ALL} Sell goods from your inventory.
  36.   - {Fore.CYAN}TRAVEL:{Style.RESET_ALL} Move to a different town.
  37.   - {Fore.CYAN}STATUS:{Style.RESET_ALL} View your current status, inventory, and relationships.
  38.   - {Fore.CYAN}UPGRADE:{Style.RESET_ALL} Upgrade your cargo capacity.
  39.   - {Fore.CYAN}NETWORK:{Style.RESET_ALL} Get rumors and information about other towns.
  40.   - {Fore.CYAN}HELP:{Style.RESET_ALL} View the game rules and trading categories.
  41.   - {Fore.CYAN}EXIT:{Style.RESET_ALL} Exit the game.
  42.  
  43. 3. {Fore.YELLOW}Goods Categories and Their Impact on Relationships:{Style.RESET_ALL}
  44.   - {Fore.CYAN}Strategic Goods:{Style.RESET_ALL} Gold, Gems, Iron
  45.     - Buying or selling affects {Fore.CYAN}Trade Relations{Style.RESET_ALL}.
  46.   - {Fore.CYAN}Supportive Goods:{Style.RESET_ALL} Food, Herbs, Cloth, Tea
  47.     - Buying or selling improves {Fore.CYAN}Reputation{Style.RESET_ALL}.
  48.   - {Fore.CYAN}Luxury Goods:{Style.RESET_ALL} Silk, Wine, Spices
  49.     - Buying or selling influences {Fore.CYAN}Favor{Style.RESET_ALL}.
  50.  
  51. 4. {Fore.YELLOW}Trading Tips:{Style.RESET_ALL}
  52.   - All trading actions significantly impact relationships.
  53.   - Relationships affect prices and trading conditions.
  54.   - Events can drastically alter your relationships.
  55.  
  56. 5. {Fore.YELLOW}Relationships with Towns:{Style.RESET_ALL}
  57.   - {Fore.CYAN}Trade:{Style.RESET_ALL} Reflects your trading history with the town.
  58.   - {Fore.CYAN}Reputation:{Style.RESET_ALL} Represents your overall standing and trustworthiness.
  59.   - {Fore.CYAN}Favor:{Style.RESET_ALL} Indicates personal favor and goodwill from the town.
  60.  
  61. 6. {Fore.YELLOW}Events:{Style.RESET_ALL}
  62.   - Random events may occur, impacting markets and relationships.
  63.   - Your global reputation can influence the likelihood of events.
  64.  
  65. 7. {Fore.YELLOW}Turn Mechanics:{Style.RESET_ALL}
  66.   - Actions like {Fore.CYAN}BUY, SELL, TRAVEL,{Style.RESET_ALL} and {Fore.CYAN}UPGRADE{Style.RESET_ALL} consume a turn.
  67.   - Viewing {Fore.CYAN}STATUS, NETWORK,{Style.RESET_ALL} or {Fore.CYAN}HELP{Style.RESET_ALL} does not consume a turn.
  68.   - {Fore.CYAN}Note:{Style.RESET_ALL} You can only use {Fore.CYAN}NETWORK{Style.RESET_ALL} once per turn.
  69.  
  70. {Style.RESET_ALL}
  71. """
  72.  
  73. # Goods and their base prices, weights, base supply, and base demand
  74. GOODS = {
  75.     "Spices": {"base_price": 100, "weight": 5, "base_supply": 300, "base_demand": 300},
  76.     "Gems": {"base_price": 500, "weight": 10, "base_supply": 150, "base_demand": 200},
  77.     "Food": {"base_price": 50, "weight": 3, "base_supply": 400, "base_demand": 400},
  78.     "Silk": {"base_price": 200, "weight": 4, "base_supply": 150, "base_demand": 200},
  79.     "Iron": {"base_price": 150, "weight": 15, "base_supply": 200, "base_demand": 250},
  80.     "Wine": {"base_price": 120, "weight": 6, "base_supply": 200, "base_demand": 250},
  81.     "Tea": {"base_price": 80, "weight": 2, "base_supply": 300, "base_demand": 300},
  82.     "Herbs": {"base_price": 60, "weight": 3, "base_supply": 350, "base_demand": 300},
  83.     "Cloth": {"base_price": 180, "weight": 8, "base_supply": 100, "base_demand": 150},
  84.     "Gold": {"base_price": 1000, "weight": 25, "base_supply": 80, "base_demand": 100},
  85. }
  86.  
  87. # Goods categories for relationship impact
  88. SUPPORTIVE_GOODS = ["Food", "Herbs", "Cloth", "Tea"]
  89. STRATEGIC_GOODS = ["Gold", "Gems", "Iron"]
  90. LUXURY_GOODS = ["Silk", "Wine", "Spices"]
  91.  
  92. # Combined list of all goods for inventory and selection
  93. ALL_GOODS = GOODS.copy()
  94.  
  95. # Expanded list of Towns
  96. TOWNS = [
  97.     "Onyxville", "Brightstone", "Crimsonburg", "Dusktown",
  98.     "Sunset City", "Northport", "Riverside", "Hilltop",
  99.     "Lakeside", "Oakwood", "Stormhaven", "Fairview"
  100. ]
  101.  
  102. # List of possible random events
  103. GLOBAL_EVENTS = [
  104.     # Existing negative global events
  105.     "Global trade embargo reducing supply worldwide.",
  106.     "Pandemic affecting demand for certain goods globally.",
  107.     "Global inflation increasing prices across the board.",
  108.     "Global recession decreasing purchasing power.",
  109.     # Positive global events
  110.     "A global trade agreement has been signed, reducing tariffs.",
  111.     "Technological advancements have improved trade efficiency worldwide.",
  112.     "A global festival increases demand for luxury goods.",
  113. ]
  114.  
  115. LOCAL_EVENTS = [
  116.     # Existing negative local events
  117.     "Thieves attacked your caravan and stole some goods!",
  118.     "A storm ruined your goods during travel.",
  119.     "Disease outbreak decreased the demand for food.",
  120.     "Pirates disrupted trade in the seas.",
  121.     "Government imposed a tax on trade.",
  122.     "Locusts devastated the food supplies in Dusktown.",
  123.     "Bandit attack in Oakwood stole some of your goods.",
  124.     "Natural disaster in Dusktown affecting multiple goods.",
  125.     # Positive local events
  126.     "You found a hidden stash of goods in the market!",
  127.     "A wealthy patron gifts you valuable items!",
  128.     "Your reputation has earned you a local tax exemption!",
  129.     "A local festival boosts demand and prices for certain goods.",
  130.     "Favorable winds reduce travel times temporarily.",
  131.     "Merchants admire your honesty, improving relationships.",
  132.     "An influential guild offers you a partnership.",
  133.     "An overstock in the market reduces prices for certain goods.",
  134.     "Local artisans gift you exquisite items to trade.",
  135.     "Successful mediation improves trade relations between towns.",
  136. ]
  137.  
  138. # Player relationships with towns
  139. RELATIONSHIP_BASE = 0  # Neutral
  140. RELATIONSHIP_MAX = 100
  141. RELATIONSHIP_MIN = -100
  142.  
  143. # Difficulty settings
  144. DIFFICULTIES = {
  145.     "EASY": {
  146.         "event_chance": 0.12,
  147.         "ai_aggressiveness": 0.7,
  148.         "starting_money": 4000,
  149.         "starting_cargo": 300,
  150.         "turns": 60
  151.     },
  152.     "MEDIUM": {
  153.         "event_chance": 0.2,
  154.         "ai_aggressiveness": 1.0,
  155.         "starting_money": 2500,
  156.         "starting_cargo": 250,
  157.         "turns": 55
  158.     },
  159.     "HARD": {
  160.         "event_chance": 0.3,
  161.         "ai_aggressiveness": 1.5,
  162.         "starting_money": 1800,
  163.         "starting_cargo": 200,
  164.         "turns": 50
  165.     }
  166. }
  167.  
  168. class AITrader:
  169.     """Represents an AI trader that interacts with the market each turn."""
  170.     def __init__(self, trader_id, aggressiveness=1.0):
  171.         self.trader_id = trader_id
  172.         self.name = f"Trader_{trader_id}"
  173.         self.aggressiveness = aggressiveness  # Determines transaction sizes and frequency
  174.  
  175.     def perform_action(self, market):
  176.         """AI trader performs buy or sell actions based on strategy."""
  177.         # Determine number of actions based on aggressiveness
  178.         num_actions = random.randint(1, 3) if self.aggressiveness > 1.0 else random.randint(1, 2)
  179.         for _ in range(num_actions):
  180.             # Decide action based on aggressiveness
  181.             action = random.choices(["BUY", "SELL"], weights=[self.aggressiveness, 1.0])[0]
  182.  
  183.             # Choose a good to buy or sell
  184.             goods_choices = list(ALL_GOODS.keys())
  185.             goods_prob = []
  186.             total_goods = len(goods_choices)
  187.             for good in goods_choices:
  188.                 if good == "Gold":
  189.                     # Further reduce the probability for Gold
  190.                     goods_prob.append(0.05)  # Very low probability
  191.                 elif good == "Gems":
  192.                     # Slightly reduce the probability for Gems
  193.                     goods_prob.append(0.1)
  194.                 else:
  195.                     # Distribute the remaining probability among other goods
  196.                     goods_prob.append(1.0)  # Assign higher weight to other goods
  197.  
  198.             # Normalize the weights
  199.             total_weight = sum(goods_prob)
  200.             normalized_weights = [w / total_weight for w in goods_prob]
  201.  
  202.             good = random.choices(goods_choices, weights=normalized_weights, k=1)[0]
  203.  
  204.             # Determine quantity based on aggressiveness
  205.             quantity = random.randint(1, max(1, int(10 * self.aggressiveness)))  # Reduced max quantity
  206.             if action == "BUY":
  207.                 # AI trader buys goods: decrease supply, increase demand
  208.                 if market.supply.get(good, 0) >= quantity:
  209.                     market.supply[good] -= quantity
  210.                     market.demand[good] += quantity * 0.5  # Reduced impact
  211.             elif action == "SELL":
  212.                 # AI trader sells goods: increase supply, decrease demand
  213.                 market.supply[good] = market.supply.get(good, 0) + quantity
  214.                 market.demand[good] = market.demand.get(good, 0) - quantity * 0.5  # Reduced impact
  215.                 if market.demand.get(good, 0) < 50:  # Prevent demand from going too low
  216.                     market.demand[good] = 50
  217.  
  218. class Player:
  219.     def __init__(self, starting_money, starting_cargo, game):
  220.         self.starting_money = starting_money
  221.         self.money = starting_money
  222.         self.inventory = {good: 0 for good in GOODS}
  223.         self.location = random.choice(TOWNS)
  224.         self.turns = 0
  225.         self.max_cargo = starting_cargo
  226.         self.current_cargo = 0
  227.         self.cargo_upgrades = 0
  228.         self.relationships = {town: {
  229.             "Trade": RELATIONSHIP_BASE,
  230.             "Reputation": RELATIONSHIP_BASE,
  231.             "Favor": RELATIONSHIP_BASE
  232.         } for town in TOWNS}
  233.         self.event_log = []
  234.         self.visited_towns = set()
  235.         self.visited_towns.add(self.location)  # Add this line to include the starting town
  236.         self.reputation = 0
  237.         self.game = game  # Reference to the game instance
  238.  
  239.     def modify_relationship(self, town, aspect, amount):
  240.         multiplier = self.game.relation_multiplier
  241.         adjusted_amount = int(amount * multiplier)
  242.         new_score = self.relationships[town][aspect] + adjusted_amount
  243.         new_score = int(max(RELATIONSHIP_MIN, min(new_score, RELATIONSHIP_MAX)))
  244.         change = new_score - self.relationships[town][aspect]
  245.         self.relationships[town][aspect] = new_score
  246.         # Notify the player about the change
  247.         if change > 0:
  248.             slow_print(f"\n[{town} - {aspect}] Improved by {change} points!")
  249.         elif change < 0:
  250.             slow_print(f"\n[{town} - {aspect}] Worsened by {-change} points!")
  251.  
  252.     def get_overall_relationship_status(self, town):
  253.         """Determines the overall relationship status based on individual aspects."""
  254.         aspects = self.relationships[town]
  255.         # Calculate average of all aspects
  256.         average = sum(aspects.values()) / len(aspects)
  257.         return get_relationship_status(average)
  258.  
  259.     def status(self):
  260.         """Displays the player's current status with detailed relationship aspects."""
  261.         slow_print(f"\n{Fore.GREEN}Player Status:{Style.RESET_ALL}")
  262.         fast_print(f"Location: {self.location}")
  263.         fast_print(f"Money: ${self.money}")
  264.         fast_print(f"Cargo: {self.current_cargo}/{self.max_cargo} cargo space used")
  265.         fast_print("Inventory:")
  266.         for good, qty in self.inventory.items():
  267.             if qty > 0:
  268.                 fast_print(f"  {good}: {qty}")
  269.        
  270.         # Display visited towns
  271.         fast_print("\nVisited Towns:")
  272.         for town in sorted(self.visited_towns):  # Sort for readability
  273.             fast_print(f"  - {town}")
  274.        
  275.         # Display relationships with detailed aspects
  276.         fast_print("\nRelationships:")
  277.         for town, aspects in self.relationships.items():
  278.             overall_status = self.get_overall_relationship_status(town)
  279.             relationship_info = ', '.join([
  280.                 f"{Fore.CYAN}{aspect}: {score}{Style.RESET_ALL}" for aspect, score in aspects.items()
  281.             ])
  282.             if overall_status in ["Ally", "Friendly"]:
  283.                 status_color = Fore.GREEN
  284.             elif overall_status in ["Hostile", "Enemy"]:
  285.                 status_color = Fore.RED
  286.             else:
  287.                 status_color = Fore.YELLOW
  288.             fast_print(f"  {town}: {relationship_info} | Overall: {status_color}{overall_status}{Style.RESET_ALL}")
  289.         print()
  290.  
  291.     def status_short(self):
  292.         """Displays the player's current status without relationship aspects."""
  293.         slow_print(f"\n{Fore.GREEN}Player Status:{Style.RESET_ALL}")
  294.         fast_print(f"Location: {self.location}")
  295.         fast_print(f"Money: ${self.money}")
  296.         fast_print(f"Cargo: {self.current_cargo}/{self.max_cargo} units used")
  297.         fast_print("Inventory:")
  298.         for good, qty in self.inventory.items():
  299.             if qty > 0:
  300.                 fast_print(f"  {good}: {qty}")  
  301.         print()
  302.  
  303.     def add_cargo(self, good, quantity):
  304.         """Attempts to add cargo to the player's inventory."""
  305.         # Determine weight based on whether the good is regular
  306.         if good in GOODS:
  307.             weight = GOODS[good]["weight"]
  308.         else:
  309.             weight = 5  # Default weight
  310.         total_weight = weight * quantity
  311.         if self.current_cargo + total_weight > self.max_cargo:
  312.             return False, total_weight
  313.         self.current_cargo += total_weight
  314.         if good not in self.inventory:
  315.             self.inventory[good] = 0
  316.         self.inventory[good] += quantity
  317.         return True, total_weight
  318.  
  319.     def remove_cargo(self, good, quantity):
  320.         """Removes cargo from the player's inventory."""
  321.         if good not in self.inventory:
  322.             return
  323.         # Determine weight based on whether the good is regular
  324.         if good in GOODS:
  325.             weight = GOODS[good]["weight"]
  326.         else:
  327.             weight = 5  # Default weight
  328.         total_weight = weight * quantity
  329.         self.inventory[good] -= quantity
  330.         self.current_cargo -= total_weight
  331.         if self.inventory[good] < 0:
  332.             self.inventory[good] = 0
  333.         if self.current_cargo < 0:
  334.             self.current_cargo = 0
  335.  
  336.     def upgrade_cargo(self):
  337.         """Allows the player to upgrade cargo capacity with a fixed cost of $500 per upgrade."""
  338.         upgrade_cost = 500  # Fixed cost for each upgrade
  339.         upgrade_amount = 50  # Fixed cargo increase per upgrade
  340.  
  341.         if self.money < upgrade_cost:
  342.             slow_print("Insufficient funds to upgrade cargo capacity.")
  343.             return False  # Indicate that no upgrade was made
  344.  
  345.         confirm = input(f"Upgrade cargo capacity by {upgrade_amount} for ${upgrade_cost}? (Y/N): ").strip().upper()
  346.         if confirm == 'Y':
  347.             self.money -= upgrade_cost
  348.             self.max_cargo += upgrade_amount
  349.             self.cargo_upgrades += 1  # Increment the number of upgrades for tracking purposes
  350.             slow_print(f"Cargo capacity increased by {upgrade_amount}. New capacity: {self.max_cargo}")
  351.             self.event_log.append(f"Upgraded cargo capacity by {upgrade_amount} for ${upgrade_cost}.")
  352.             return True  # Indicate that upgrade was successful
  353.         elif confirm == 'N':
  354.             slow_print("Upgrade canceled.")
  355.             return False  # Indicate that no upgrade was made
  356.         else:
  357.             slow_print("ERROR: Invalid Input")
  358.             return False  # Indicate that no upgrade was made
  359.  
  360. class Market:
  361.     """Represents the market of a specific town."""
  362.     PRICE_FLOOR = 10
  363.     PRICE_CEILING = 2000
  364.  
  365.     def __init__(self, town, player):
  366.         self.town = town
  367.         self.player = player
  368.         # Initialize supply and demand per good with increased random variations
  369.         self.supply = {}
  370.         self.demand = {}
  371.         for good, info in GOODS.items():
  372.             supply_variation = random.uniform(0.6, 1.4)  # Increased range from 0.6-1.4
  373.             demand_variation = random.uniform(0.6, 1.4)  # Increased range from 0.6-1.4
  374.             self.supply[good] = info["base_supply"] * supply_variation
  375.             self.demand[good] = info["base_demand"] * demand_variation
  376.         self.prices = {}
  377.         self.generate_prices()
  378.         # Initialize AI traders for the town
  379.         self.ai_traders = [AITrader(trader_id=i+1, aggressiveness=random.uniform(0.7, 1.5)) for i in range(10)]  # 10 AI traders per town
  380.  
  381.     def generate_prices(self):
  382.             """Calculates current prices based on supply, demand, and relationship with the player."""
  383.             relation_modifier = self.get_relationship_modifier(self.town)
  384.             for good, info in GOODS.items():
  385.                 # Calculate price based on demand and supply with increased elasticity
  386.                 if self.supply.get(good, 0) == 0:
  387.                     price_multiplier = 2  # High price if no supply
  388.                 else:
  389.                     ratio = self.demand.get(good, 0) / self.supply.get(good, 0)
  390.                     elasticity = random.uniform(0.7, 1.3)  # Adjusted elasticity range
  391.                     price_multiplier = ratio * elasticity
  392.                 new_price = int(GOODS[good]["base_price"] * price_multiplier * relation_modifier)
  393.                 new_price = max(self.PRICE_FLOOR, min(new_price, self.PRICE_CEILING))
  394.                 self.prices[good] = new_price
  395.  
  396.     def get_relationship_modifier(self, town):
  397.         """Determines price modifiers based on multiple relationship aspects."""
  398.         aspects = self.player.relationships[town]
  399.         trade = aspects["Trade"]
  400.         reputation = aspects["Reputation"]
  401.         favor = aspects["Favor"]
  402.  
  403.         # Calculate individual modifiers
  404.         if trade >= 80:
  405.             trade_modifier = 0.60  # Increased discount: 40% discount
  406.         elif trade >= 50:
  407.             trade_modifier = 0.80  # 20% discount
  408.         elif trade >= -20:
  409.             trade_modifier = 1.00  # Neutral
  410.         elif trade >= -60:
  411.             trade_modifier = 1.25  # 25% price increase
  412.         else:
  413.             trade_modifier = 1.50  # 50% price increase
  414.  
  415.         if reputation >= 70:
  416.             reputation_modifier = 0.85  # 15% discount
  417.         elif reputation >= 40:
  418.             reputation_modifier = 0.90  # 10% discount
  419.         else:
  420.             reputation_modifier = 1.00  # Neutral
  421.  
  422.         if favor >= 60:
  423.             favor_modifier = 0.90  # 10% discount
  424.         else:
  425.             favor_modifier = 1.00  # Neutral
  426.  
  427.         # Combine modifiers by averaging
  428.         overall_modifier = (trade_modifier + reputation_modifier + favor_modifier) / 3
  429.         return overall_modifier
  430.  
  431.     def display_prices(self):
  432.         """Displays the current prices, supply, demand, and weight for all goods in the market,
  433.        along with the player's relationship status with the current town."""
  434.         slow_print(f"\n{Fore.GREEN}Market Overview in {self.town}:{Style.RESET_ALL}")
  435.        
  436.         for good, price in self.prices.items():
  437.             supply = int(self.supply.get(good, 0))
  438.             demand = int(self.demand.get(good, 0))
  439.             weight = GOODS[good]["weight"]
  440.             available_cargo_space = self.player.max_cargo - self.player.current_cargo
  441.             max_fit = available_cargo_space // weight if weight > 0 else "N/A"
  442.            
  443.             # Determine color based on price comparison
  444.             base_price = GOODS[good]["base_price"]
  445.             price_ratio = price / base_price
  446.            
  447.             if price_ratio < 0.8:
  448.                 # Good buy
  449.                 price_color = Fore.GREEN
  450.                 good_color = Fore.GREEN
  451.                 price_indicator = ""
  452.             elif price_ratio > 1.2:
  453.                 # Bad buy
  454.                 price_color = Fore.RED
  455.                 good_color = Fore.RED
  456.                 price_indicator = ""
  457.             else:
  458.                 # Neutral
  459.                 price_color = Fore.YELLOW
  460.                 good_color = Fore.YELLOW
  461.                 price_indicator = ""
  462.            
  463.             # Display the good with appropriate colors
  464.             fast_print(f"{good_color}{good}{Style.RESET_ALL}: {price_color}${price}{Style.RESET_ALL} {price_indicator} "
  465.                        f"(Supply: {supply}, Demand: {demand}, Weight: {weight} units, Max Cargo: {max_fit})")
  466.        
  467.         # Display Relationship with the current town
  468.         slow_print(f"\n{Fore.GREEN}Relationship with {self.town}:{Style.RESET_ALL}")
  469.         aspects = self.player.relationships[self.town]
  470.        
  471.         # Display Overall Relationship Status
  472.         overall_status = self.player.get_overall_relationship_status(self.town)
  473.         if overall_status in ["Ally", "Friendly"]:
  474.             status_color = Fore.GREEN
  475.         elif overall_status in ["Hostile", "Enemy"]:
  476.             status_color = Fore.RED
  477.         else:
  478.             status_color = Fore.YELLOW
  479.         slow_print(f"  Overall Relationship: {status_color}{overall_status}{Style.RESET_ALL}")
  480.        
  481.         # Display individual aspects
  482.         for aspect, score in aspects.items():
  483.             if score >= 40:
  484.                 aspect_color = Fore.GREEN
  485.             elif score >= 20:
  486.                 aspect_color = Fore.BLUE
  487.             elif score >= 0:
  488.                 aspect_color = Fore.YELLOW
  489.             elif score <= -30:
  490.                 aspect_color = Fore.RED
  491.             elif score <= -20:
  492.                 aspect_color = Fore.MAGENTA
  493.             else:
  494.                 aspect_color = Fore.YELLOW
  495.             fast_print(f"  {Fore.CYAN}{aspect}:{Style.RESET_ALL} {aspect_color}{score}{Style.RESET_ALL}")
  496.        
  497.         print()
  498.  
  499.     def process_ai_traders(self):
  500.         """Processes all AI traders' actions for the market."""
  501.         for trader in self.ai_traders:
  502.             trader.perform_action(self)
  503.         # After AI traders have acted, regenerate prices
  504.         self.generate_prices()
  505.  
  506. class Game:
  507.     """Represents the main game loop and logic."""
  508.  
  509.     def __init__(self):
  510.         # Difficulty selection will be done in start()
  511.         self.player = None
  512.         self.difficulty = None
  513.         self.starting_money = None  # Will be set during game start
  514.         self.turns_total = 0
  515.         self.markets = {}
  516.         self.current_market = None
  517.         self.current_event = None
  518.         self.pandemic_applied = False  # Add this flag to track pandemic impact
  519.         # Define decay rates to return supply and demand towards base values
  520.         self.supply_decay_rate = 0.03  # Reduced decay rate for slower stabilization
  521.         self.demand_decay_rate = 0.03  # Reduced decay rate for slower stabilization
  522.         self.turn_counter = 1
  523.         self.relation_multiplier = 1.5  # Adjust this value as desired
  524.         self.game_over = False
  525.         self.key_events = []  # To track key events for summary
  526.         # New variables to track per-turn actions
  527.         self.network_used = False  # To track if NETWORK command has been used in the current turn
  528.         self.consecutive_travels = 0  # To track consecutive travels
  529.         self.last_command = None  # To track the last command issued by the player
  530.  
  531.     def choose_difficulty(self):
  532.         """Allows the player to choose a difficulty level."""
  533.         fast_print("\nChoose Difficulty Level:")
  534.         for idx, level in enumerate(DIFFICULTIES.keys(), 1):
  535.             fast_print(f"{idx}. {level.capitalize()}")
  536.         while True:
  537.             choice = input(f"Enter the number corresponding to your choice (1-{len(DIFFICULTIES)}): ").strip()
  538.             if choice.isdigit() and 1 <= int(choice) <= len(DIFFICULTIES):
  539.                 selected = list(DIFFICULTIES.keys())[int(choice)-1]
  540.                 fast_print(f"Selected {selected.capitalize()} difficulty.\n")
  541.                 return selected
  542.             else:
  543.                 fast_print("Invalid choice. Please try again.")
  544.  
  545.     def start(self):
  546.         """Starts the game by displaying the title and welcome message."""
  547.         fast_print(TITLE)
  548.         # Print the '=' lines separately in yellow without extra line breaks
  549.         fast_print(f"""{Fore.YELLOW}{'='*60}{Style.RESET_ALL}""")
  550.         medium_print(WELCOME_TEXT)
  551.         fast_print(f"""{Fore.YELLOW}{'='*60}{Style.RESET_ALL}""")
  552.         # Now choose difficulty
  553.         self.difficulty = self.choose_difficulty()
  554.         # Initialize player and markets after difficulty is chosen
  555.         self.starting_money = DIFFICULTIES[self.difficulty]["starting_money"]
  556.         self.player = Player(
  557.             starting_money=self.starting_money,
  558.             starting_cargo=DIFFICULTIES[self.difficulty]["starting_cargo"],
  559.             game=self  # Pass the game instance
  560.         )
  561.         self.turns_total = DIFFICULTIES[self.difficulty]["turns"]
  562.         self.markets = {town: Market(town, self.player) for town in TOWNS}
  563.         self.current_market = self.markets[self.player.location]
  564.         input("Press Enter to start your trading adventure...")
  565.         self.game_loop()
  566.  
  567.     def game_loop(self):
  568.         """Main game loop that runs until the player runs out of turns or exits."""
  569.         while self.turn_counter <= self.turns_total and not self.game_over:
  570.             # Start of turn
  571.             medium_print(f"\n--- Turn {self.turn_counter}/{self.turns_total} ---")
  572.             # Reset per-turn flags
  573.             self.network_used = False  # Reset NETWORK usage at the start of each turn
  574.  
  575.             # Handle consecutive traveling
  576.             if self.last_command != "TRAVEL":
  577.                 self.consecutive_travels = 0  # Reset counter if player did something else
  578.  
  579.             self.last_command = None  # Reset last command at the start of the turn
  580.  
  581.             # Display player status (excluding relationships)
  582.             self.player.status_short()
  583.             # Display prices for the current market
  584.             self.current_market.display_prices()
  585.  
  586.             turn_consumed = False
  587.             while not turn_consumed and not self.game_over:
  588.                 command = input("Enter command (BUY/SELL/TRAVEL/STATUS/UPGRADE/NETWORK/HELP/EXIT): ").strip().upper()
  589.                 while command not in ["BUY", "SELL", "TRAVEL", "STATUS", "UPGRADE", "NETWORK", "HELP", "EXIT"]:
  590.                     slow_print("ERROR: Invalid Input")
  591.                     command = input("Enter command (BUY/SELL/TRAVEL/STATUS/UPGRADE/NETWORK/HELP/EXIT): ").strip().upper()
  592.                 self.last_command = command  # Store the last command
  593.  
  594.                 if command == "STATUS":
  595.                     self.player.status()
  596.                 elif command == "NETWORK":
  597.                     if not self.network_used:
  598.                         self.check_network()
  599.                         self.network_used = True  # Mark as used for this turn
  600.                     else:
  601.                         slow_print("You have already used the NETWORK command this turn.")
  602.                 elif command == "HELP":
  603.                     print(f"{Fore.YELLOW}{'='*60}{Style.RESET_ALL}")
  604.                     fast_print(HELP_TEXT)
  605.                     print(f"{Fore.YELLOW}{'='*60}{Style.RESET_ALL}")
  606.                 elif command == "EXIT":
  607.                     slow_print("Thank you for playing!")
  608.                     self.end_game()
  609.                     return  # Exit the game loop
  610.                 elif command in ["BUY", "SELL", "TRAVEL", "UPGRADE"]:
  611.                     # Process commands that consume a turn
  612.                     if command == "BUY":
  613.                         action_successful = self.buy()
  614.                         turn_consumed = action_successful
  615.                     elif command == "SELL":
  616.                         action_successful = self.sell()
  617.                         turn_consumed = action_successful
  618.                     elif command == "TRAVEL":
  619.                         action_successful, turn_consumed = self.travel()
  620.                     elif command == "UPGRADE":
  621.                         action_successful = self.player.upgrade_cargo()
  622.                         turn_consumed = action_successful
  623.  
  624.                     if action_successful:
  625.                         if turn_consumed:
  626.                             # At this point, a turn has passed
  627.                             # Process AI traders and events
  628.                             self.apply_decay()
  629.                             # Process AI traders for all markets
  630.                             for market in self.markets.values():
  631.                                 market.process_ai_traders()
  632.                             # Trigger event before displaying prices
  633.                             self.trigger_event()
  634.                             # Regenerate prices after events
  635.                             self.current_market.generate_prices()
  636.                             self.turn_counter += 1
  637.  
  638.                             # Apply relationship decay every 5 turns
  639.                             if self.turn_counter % 5 == 0:
  640.                                 self.apply_relationship_decay()
  641.  
  642.                             # Check if the game should end after the turn
  643.                             if self.turn_counter > self.turns_total:
  644.                                 self.game_over = True  # Set game over to True
  645.                                 break  # Break out of the inner while loop
  646.                         else:
  647.                             # Action was successful, but turn was not consumed (e.g., free travel)
  648.                             # Display the player's status and market overview at the new location
  649.                             self.player.status_short()
  650.                             self.current_market.display_prices()
  651.                     else:
  652.                         # Action failed; inform player and allow them to try again
  653.                         slow_print("Action could not be completed. Please choose another action.")
  654.                         # Do not consume turn
  655.                 else:
  656.                     # Invalid command (shouldn't reach here due to earlier validation)
  657.                     slow_print("ERROR: Invalid Input")
  658.         # After the loop ends, call end_game
  659.         self.end_game()
  660.         return  # Ensure the method exits after ending the game
  661.        
  662.     def choose_good(self, action):
  663.         """Prompts the player to choose a good to buy or sell."""
  664.         fast_print("Available goods:")
  665.         all_goods = list(ALL_GOODS.keys())
  666.         while True:
  667.             for idx, good in enumerate(all_goods, 1):
  668.                 fast_print(f"{idx}. {good}")
  669.             choice = input(f"Enter the number of the good to {action}: ").strip()
  670.             if not choice.isdigit() or not (1 <= int(choice) <= len(all_goods)):
  671.                 slow_print("ERROR: Invalid Input. Please enter a valid number.")
  672.                 continue
  673.             return all_goods[int(choice) - 1]
  674.  
  675.     def check_network(self):
  676.         """Provides rumors about the status of other towns in the merchant network."""
  677.         medium_print(f"\n{Fore.GREEN}Merchant Network Rumors:{Style.RESET_ALL}")
  678.         for town, market in self.markets.items():
  679.             status = self.player.get_overall_relationship_status(town)
  680.             rumor = self.generate_rumor(town, market, status)
  681.             if status in ["Ally", "Friendly"]:
  682.                 color = Fore.CYAN
  683.             elif status in ["Hostile", "Enemy"]:
  684.                 color = Fore.RED
  685.             else:
  686.                 color = Fore.YELLOW
  687.             fast_print(f"{color}{town}:{Style.RESET_ALL}")
  688.             fast_print(f" {rumor}")
  689.         print()
  690.  
  691.     def generate_rumor(self, town, market, status):
  692.         """Generates a rumor based on the town's market and relationship status."""
  693.         # Base rumor based on relationship
  694.         if status == "Ally":
  695.             base_rumor = f"{Fore.BLUE}is thriving and their trade is booming.{Style.RESET_ALL}"
  696.         elif status == "Friendly":
  697.             base_rumor = f"{Fore.GREEN}is experiencing steady trade.{Style.RESET_ALL}"
  698.         elif status == "Neutral":
  699.             base_rumor = f"{Fore.YELLOW}has a balanced market.{Style.RESET_ALL}"
  700.         elif status == "Hostile":
  701.             base_rumor = f"{Fore.RED}is facing some challenges, causing minor disruptions in trade.{Style.RESET_ALL}"
  702.         else:  # Enemy
  703.             base_rumor = f"{Fore.RED}is in turmoil, significantly affecting their market and relations.{Style.RESET_ALL}"
  704.  
  705.         # Select multiple goods to include in the rumor
  706.         affected_goods = random.sample(list(ALL_GOODS.keys()), k=random.randint(1, 3))  # Affect 1 to 3 goods
  707.  
  708.         goods_rumors = []
  709.         for good in affected_goods:
  710.             supply = market.supply.get(good, 0)
  711.             demand = market.demand.get(good, 0)
  712.             good_colored = f"{Fore.CYAN}{good}{Style.RESET_ALL}"
  713.             if demand > supply * 1.5:
  714.                 # High demand pushing prices up
  715.                 rumor_text = f"high demand for {good_colored} is pushing prices up"
  716.                 rumor_colored = f"{Fore.RED}{rumor_text}{Style.RESET_ALL}"
  717.                 goods_rumors.append(rumor_colored)
  718.             elif demand < supply * 0.5:
  719.                 # Excess supply lowering prices
  720.                 rumor_text = f"excess supply of {good_colored} is lowering prices"
  721.                 rumor_colored = f"{Fore.GREEN}{rumor_text}{Style.RESET_ALL}"
  722.                 goods_rumors.append(rumor_colored)
  723.             else:
  724.                 # Stable demand and supply
  725.                 rumor_text = f"stable demand and supply for {good_colored}"
  726.                 rumor_colored = f"{Fore.YELLOW}{rumor_text}{Style.RESET_ALL}"
  727.                 goods_rumors.append(rumor_colored)
  728.  
  729.         goods_rumor = "; ".join(goods_rumors)
  730.         rumor = f"{base_rumor} Additionally, there's {goods_rumor}."
  731.         return rumor
  732.  
  733.     def buy(self):
  734.         """Handles the buying process for the player."""
  735.         while True:
  736.             good = self.choose_good("buy")
  737.             if not good:
  738.                 return False  # Indicate that no transaction occurred
  739.  
  740.             price = self.current_market.prices.get(good, GOODS.get(good, {}).get("base_price", 100))
  741.             # Calculate max affordable based on money
  742.             max_affordable_quantity = self.player.money // price
  743.             # Calculate max based on cargo
  744.             if good in GOODS:
  745.                 good_weight = GOODS[good]["weight"]
  746.             else:
  747.                 good_weight = 5  # Default weight
  748.             available_cargo_space = self.player.max_cargo - self.player.current_cargo
  749.             max_cargo_quantity = available_cargo_space // good_weight
  750.             # Calculate max based on supply
  751.             max_supply = int(self.current_market.supply.get(good, 0))
  752.  
  753.             max_quantity = min(max_affordable_quantity, max_cargo_quantity, max_supply)
  754.  
  755.             if max_quantity <= 0:
  756.                 slow_print("You cannot buy any units of this good due to insufficient funds, cargo space, or supply.")
  757.                 return False  # Indicate that no transaction occurred
  758.  
  759.             # Encourage bulk buying with discounts
  760.             discount = 0
  761.             discounted_price = price
  762.             if max_quantity >= 20:
  763.                 discount = 0.10  # 10% discount for buying 20 or more units
  764.                 discounted_price = int(price * (1 - discount))
  765.                 fast_print(f"Bulk purchase discount applied! Price per unit: ${discounted_price} (Original: ${price})")
  766.             elif max_quantity >= 10:
  767.                 discount = 0.05  # 5% discount for buying 10 or more units
  768.                 discounted_price = int(price * (1 - discount))
  769.                 fast_print(f"Bulk purchase discount applied! Price per unit: ${discounted_price} (Original: ${price})")
  770.  
  771.             fast_print(f"You can afford up to {max_quantity} units based on your constraints.")
  772.  
  773.             while True:
  774.                 quantity_input = input(f"Enter the quantity of {good} to buy (max {max_quantity}): ").strip()
  775.                 if not quantity_input.isdigit():
  776.                     slow_print("ERROR: Invalid input. Please enter a valid number.")
  777.                     continue  # Prompt again
  778.                 quantity = int(quantity_input)
  779.                 if quantity <= 0 or quantity > max_quantity:
  780.                     slow_print("ERROR: Quantity must be a positive number within your maximum limit.")
  781.                     continue  # Prompt again
  782.  
  783.                 cost = discounted_price * quantity
  784.  
  785.                 success, added_weight = self.player.add_cargo(good, quantity)
  786.                 if not success:
  787.                     slow_print(f"Cannot buy {quantity} {good}. Exceeds cargo capacity.")
  788.                     return False  # Indicate that no transaction occurred
  789.  
  790.                 # Update player and market
  791.                 self.player.money -= cost
  792.                 self.current_market.supply[good] -= quantity
  793.                 self.current_market.demand[good] += quantity * 0.4  # Increased impact
  794.                 slow_print(f"Bought {quantity} {good} for ${cost}.")
  795.                 fast_print(f"Cargo Updated: {self.player.current_cargo}/{self.player.max_cargo} units used.")
  796.                 self.player.event_log.append(f"Turn {self.turn_counter}: Bought {quantity} {good} for ${cost}.")
  797.                 self.key_events.append(f"Turn {self.turn_counter}: Bought {quantity} {good} for ${cost}.")
  798.  
  799.                 # Relationship modifications based on goods categories
  800.                 self.update_relationships_after_purchase(good, quantity)
  801.  
  802.                 return True  # Indicate that the transaction was successful
  803.  
  804.     def sell(self):
  805.         """Handles the selling process for the player."""
  806.         while True:
  807.             good = self.choose_good("sell")
  808.             if not good:
  809.                 return False  # Indicate that no transaction occurred
  810.             if self.player.inventory.get(good, 0) == 0:
  811.                 fast_print(f"You have no {good} to sell.")
  812.                 return False  # Indicate that no transaction occurred
  813.  
  814.             max_sell_quantity = int(self.player.inventory.get(good, 0))
  815.  
  816.             fast_print(f"You can sell up to {max_sell_quantity} units based on your inventory.")
  817.             while True:
  818.                 quantity_input = input(f"Enter the quantity of {good} to sell (max {max_sell_quantity}): ").strip()
  819.                 if not quantity_input.isdigit():
  820.                     slow_print("ERROR: Invalid input. Please enter a valid number.")
  821.                     continue  # Prompt again
  822.                 quantity = int(quantity_input)
  823.                 if quantity <= 0 or quantity > max_sell_quantity:
  824.                     slow_print("ERROR: Quantity must be a positive number within your maximum limit.")
  825.                     continue  # Prompt again
  826.  
  827.                 price = self.current_market.prices.get(good, GOODS.get(good, {}).get("base_price", 100))
  828.                 revenue = price * quantity
  829.  
  830.                 # Update player and market
  831.                 self.player.money += revenue
  832.                 self.player.remove_cargo(good, quantity)
  833.                 self.current_market.supply[good] += quantity
  834.                 self.current_market.demand[good] -= quantity * 0.4  # Increased impact
  835.                 slow_print(f"Sold {quantity} {good} for ${revenue}.")
  836.                 fast_print(f"Cargo Updated: {self.player.current_cargo}/{self.player.max_cargo} units used.")
  837.                 self.player.event_log.append(f"Turn {self.turn_counter}: Sold {quantity} {good} for ${revenue}.")
  838.                 self.key_events.append(f"Turn {self.turn_counter}: Sold {quantity} {good} for ${revenue}.")
  839.  
  840.                 # Relationship modifications based on goods categories
  841.                 self.update_relationships_after_sale(good, quantity)
  842.  
  843.                 return True  # Indicate that the transaction was successful
  844.            
  845.     def travel(self):
  846.         """Handles the travel process for the player to move between towns."""
  847.         available_towns = [town for town in TOWNS if town != self.player.location]
  848.         slow_print("\nAvailable towns to travel to:")
  849.         while True:
  850.             for idx, town in enumerate(available_towns, 1):
  851.                 fast_print(f"{idx}. {town}")
  852.             choice = input(f"Enter the number of the town you want to travel to (1-{len(available_towns)}): ").strip()
  853.             if not choice.isdigit() or not (1 <= int(choice) <= len(available_towns)):
  854.                 slow_print("ERROR: Invalid Input. Please enter a valid number.")
  855.                 continue
  856.             new_location = available_towns[int(choice) - 1]
  857.             break
  858.         self.player.location = new_location
  859.         self.player.visited_towns.add(new_location)  # Ensure the new town is marked as visited
  860.         self.current_market = self.markets[new_location]
  861.         slow_print(f"Traveled to {new_location}.")
  862.         self.player.event_log.append(f"Turn {self.turn_counter}: Traveled to {new_location}.")
  863.         self.key_events.append(f"Turn {self.turn_counter}: Traveled to {new_location}.")
  864.  
  865.         # Update consecutive travel counter
  866.         self.consecutive_travels += 1
  867.  
  868.         # Apply penalties only if player has traveled multiple turns in a row
  869.         if self.consecutive_travels >= 3:
  870.             penalty_trade = -7  # Increased penalty
  871.             penalty_reputation = -5
  872.             penalty_favor = -3
  873.             self.player.modify_relationship(new_location, "Trade", penalty_trade)
  874.             self.player.modify_relationship(new_location, "Reputation", penalty_reputation)
  875.             self.player.modify_relationship(new_location, "Favor", penalty_favor)
  876.             slow_print(f"Frequent traveling has significantly strained your relationships with {new_location}.")
  877.             self.player.event_log.append(f"Turn {self.turn_counter}: Frequent traveling strained relationships with {new_location}.")
  878.             self.key_events.append(f"Turn {self.turn_counter}: Frequent traveling strained relationships with {new_location}.")
  879.         else:
  880.             # No penalty applied
  881.             pass
  882.  
  883.         # Check for free travel
  884.         if hasattr(self.player, 'free_travel') and self.player.free_travel:
  885.             slow_print("Due to favorable winds, your travel was faster!")
  886.             self.player.free_travel = False  # Reset free travel
  887.             return (True, False)  # Action successful, turn not consumed
  888.  
  889.         return (True, True)  # Action successful, turn consumed
  890.  
  891.     def apply_event(self, event, global_event=False):
  892.         """Applies the effects of a triggered event to the game."""
  893.         slow_print(f"\nEvent: {event}")
  894.  
  895.         # Positive Global Events
  896.         if event == "A global trade agreement has been signed, reducing tariffs.":
  897.             slow_print("Tariffs reduced globally! Prices for goods decrease significantly.")
  898.             for market in self.markets.values():
  899.                 for good in GOODS:
  900.                     market.prices[good] = int(market.prices[good] * 0.90)
  901.             self.player.modify_relationship(self.player.location, "Trade", 15)  # Increased impact
  902.             self.player.reputation += 10
  903.             self.key_events.append(f"Turn {self.turn_counter}: Global trade agreement reduced tariffs.")
  904.             return
  905.  
  906.         if event == "Technological advancements have improved trade efficiency worldwide.":
  907.             slow_print("Trade efficiency improved globally! Increased supply of goods.")
  908.             for market in self.markets.values():
  909.                 for good in GOODS:
  910.                     market.supply[good] += 100
  911.             self.player.modify_relationship(self.player.location, "Reputation", 15)  # Increased impact
  912.             self.player.reputation += 10
  913.             self.key_events.append(f"Turn {self.turn_counter}: Technological advancements improved trade efficiency.")
  914.             return
  915.  
  916.         if event == "A global festival increases demand for luxury goods.":
  917.             slow_print("Global festival! Demand and prices for luxury goods increase.")
  918.             luxury_goods = ["Silk", "Wine", "Gold"]
  919.             for market in self.markets.values():
  920.                 for good in luxury_goods:
  921.                     market.demand[good] += 150
  922.                     market.prices[good] = int(market.prices[good] * 1.3)
  923.             self.player.modify_relationship(self.player.location, "Favor", 15)  # Increased impact
  924.             self.player.reputation += 10
  925.             self.key_events.append(f"Turn {self.turn_counter}: Global festival increased demand for luxury goods.")
  926.             return
  927.  
  928.         # Positive Local Events
  929.         if event == "You found a hidden stash of goods in the market!":
  930.             found_good = random.choice(list(GOODS.keys()))
  931.             found_quantity = random.randint(15, 25)
  932.             added_weight = GOODS[found_good]["weight"] * found_quantity
  933.             if self.player.current_cargo + added_weight <= self.player.max_cargo:
  934.                 self.player.add_cargo(found_good, found_quantity)
  935.                 slow_print(f"You found {found_quantity} units of {found_good} and added them to your inventory!")
  936.                 self.player.modify_relationship(self.player.location, "Favor", 15)  # Increased impact
  937.                 self.player.reputation += 10
  938.                 self.key_events.append(f"Turn {self.turn_counter}: Found {found_quantity} {found_good} in the market.")
  939.             else:
  940.                 slow_print(f"You found {found_quantity} units of {found_good}, but lack the cargo space to carry them.")
  941.             return
  942.  
  943.         if event == "A wealthy patron gifts you valuable items!":
  944.             gifted_good = random.choice(["Gems", "Gold", "Silk"])
  945.             gifted_quantity = random.randint(10, 15)
  946.             added_weight = GOODS[gifted_good]["weight"] * gifted_quantity
  947.             if self.player.current_cargo + added_weight <= self.player.max_cargo:
  948.                 self.player.add_cargo(gifted_good, gifted_quantity)
  949.                 slow_print(f"A wealthy patron gifted you {gifted_quantity} units of {gifted_good}!")
  950.                 self.player.modify_relationship(self.player.location, "Favor", 20)  # Increased impact
  951.                 self.player.reputation += 15
  952.                 self.key_events.append(f"Turn {self.turn_counter}: Received {gifted_quantity} {gifted_good} from a patron.")
  953.             else:
  954.                 slow_print(f"A wealthy patron wanted to gift you {gifted_quantity} units of {gifted_good}, but you lacked the cargo space.")
  955.             return
  956.  
  957.         if event == "Your reputation has earned you a local tax exemption!":
  958.             saved_amount = 300
  959.             self.player.money += saved_amount
  960.             slow_print(f"You received a local tax exemption and saved ${saved_amount}!")
  961.             self.player.modify_relationship(self.player.location, "Reputation", 20)  # Increased impact
  962.             self.player.reputation += 15
  963.             self.key_events.append(f"Turn {self.turn_counter}: Received tax exemption, saved ${saved_amount}.")
  964.             return
  965.  
  966.         if event == "A local festival boosts demand and prices for certain goods.":
  967.             festival_goods = random.sample(["Spices", "Wine", "Cloth", "Tea"], 2)
  968.             for good in festival_goods:
  969.                 self.current_market.demand[good] += 120
  970.                 self.current_market.prices[good] = int(self.current_market.prices[good] * 1.5)
  971.             slow_print(f"The local festival boosted demand and prices for {', '.join(festival_goods)}!")
  972.             self.player.modify_relationship(self.player.location, "Favor", 15)  # Increased impact
  973.             self.player.reputation += 10
  974.             self.key_events.append(f"Turn {self.turn_counter}: Festival boosted prices for {', '.join(festival_goods)}.")
  975.             return
  976.  
  977.         if event == "Favorable winds reduce travel times temporarily.":
  978.             slow_print("Favorable winds! You can travel without consuming a turn next time.")
  979.             self.player.free_travel = True  # Set attribute for free travel
  980.             self.player.reputation += 5
  981.             self.key_events.append(f"Turn {self.turn_counter}: Favorable winds will reduce next travel time.")
  982.             return
  983.  
  984.         if event == "Merchants admire your honesty, improving relationships.":
  985.             slow_print("Your honesty has impressed the merchants. Relationships have greatly improved.")
  986.             self.player.modify_relationship(self.player.location, "Trade", 20)  # Increased impact
  987.             self.player.modify_relationship(self.player.location, "Reputation", 20)
  988.             self.player.reputation += 15
  989.             self.key_events.append(f"Turn {self.turn_counter}: Honesty improved relationships in {self.player.location}.")
  990.             return
  991.  
  992.         if event == "An influential guild offers you a partnership.":
  993.             slow_print("An influential guild offers you a partnership, significantly increasing your cargo capacity.")
  994.             self.player.max_cargo += 100  # Increased impact
  995.             slow_print(f"Your cargo capacity increased to {self.player.max_cargo}.")
  996.             self.player.modify_relationship(self.player.location, "Trade", 20)  # Increased impact
  997.             self.player.reputation += 15
  998.             self.key_events.append(f"Turn {self.turn_counter}: Guild partnership increased cargo capacity.")
  999.             return
  1000.  
  1001.         if event == "An overstock in the market reduces prices for certain goods.":
  1002.             overstock_goods = random.sample(["Food", "Iron", "Herbs"], 2)
  1003.             for good in overstock_goods:
  1004.                 self.current_market.supply[good] += 150
  1005.                 self.current_market.prices[good] = int(self.current_market.prices[good] * 0.6)
  1006.             slow_print(f"Overstock in the market! Prices for {', '.join(overstock_goods)} have decreased.")
  1007.             self.player.modify_relationship(self.player.location, "Favor", 15)  # Increased impact
  1008.             self.player.reputation += 10
  1009.             self.key_events.append(f"Turn {self.turn_counter}: Overstock reduced prices for {', '.join(overstock_goods)}.")
  1010.             return
  1011.  
  1012.         if event == "Local artisans gift you exquisite items to trade.":
  1013.             gifted_good = random.choice(["Silk", "Cloth", "Wine"])
  1014.             gifted_quantity = random.randint(15, 25)
  1015.             added_weight = GOODS[gifted_good]["weight"] * gifted_quantity
  1016.             if self.player.current_cargo + added_weight <= self.player.max_cargo:
  1017.                 self.player.add_cargo(gifted_good, gifted_quantity)
  1018.                 slow_print(f"Local artisans gifted you {gifted_quantity} units of {gifted_good}!")
  1019.                 self.player.modify_relationship(self.player.location, "Favor", 20)  # Increased impact
  1020.                 self.player.reputation += 15
  1021.                 self.key_events.append(f"Turn {self.turn_counter}: Artisans gifted {gifted_quantity} {gifted_good}.")
  1022.             else:
  1023.                 slow_print(f"Local artisans wanted to gift you {gifted_quantity} units of {gifted_good}, but you lacked the cargo space.")
  1024.             return
  1025.  
  1026.         if event == "Successful mediation improves trade relations between towns.":
  1027.             slow_print("Your mediation greatly improved trade relations between towns.")
  1028.             for town in TOWNS:
  1029.                 self.player.modify_relationship(town, "Trade", 15)  # Increased impact
  1030.             self.player.reputation += 20
  1031.             self.key_events.append(f"Turn {self.turn_counter}: Mediation improved trade relations universally.")
  1032.             return
  1033.  
  1034.         # Negative Global Events
  1035.         if event == "Global trade embargo reducing supply worldwide.":
  1036.             slow_print("A global trade embargo has been enacted. Supply of goods has decreased significantly worldwide.")
  1037.             for market in self.markets.values():
  1038.                 for good in GOODS:
  1039.                     market.supply[good] = max(10, market.supply[good] - 100)
  1040.                     market.prices[good] = int(market.prices[good] * 1.2)
  1041.             self.player.modify_relationship(self.player.location, "Trade", -15)  # Increased impact
  1042.             self.player.reputation -= 10
  1043.             self.key_events.append(f"Turn {self.turn_counter}: Global trade embargo reduced supply.")
  1044.             return
  1045.  
  1046.         if event == "Pandemic affecting demand for certain goods globally.":
  1047.             slow_print("A pandemic has severely reduced global demand for certain goods.")
  1048.             affected_goods = ["Spices", "Wine", "Silk"]
  1049.             for market in self.markets.values():
  1050.                 for good in affected_goods:
  1051.                     market.demand[good] = max(50, market.demand.get(good, 0) - 150)
  1052.                     market.prices[good] = int(market.prices[good] * 0.7)
  1053.             # Move relationship modification outside the loop
  1054.             self.player.modify_relationship(self.player.location, "Reputation", -15)
  1055.             self.player.reputation -= 10
  1056.             self.key_events.append(f"Turn {self.turn_counter}: Pandemic reduced demand for certain goods.")
  1057.             return
  1058.            
  1059.         if event == "Global inflation increasing prices across the board.":
  1060.             slow_print("Global inflation is significantly increasing prices everywhere.")
  1061.             for market in self.markets.values():
  1062.                 for good in GOODS:
  1063.                     market.prices[good] = int(market.prices[good] * 1.3)
  1064.             self.player.modify_relationship(self.player.location, "Trade", -15)  # Increased impact
  1065.             self.player.reputation -= 10
  1066.             self.key_events.append(f"Turn {self.turn_counter}: Global inflation increased prices.")
  1067.             return
  1068.  
  1069.         if event == "Global recession decreasing purchasing power.":
  1070.             slow_print("A global recession has severely decreased purchasing power.")
  1071.             for market in self.markets.values():
  1072.                 for good in GOODS:
  1073.                     market.demand[good] = max(50, market.demand[good] - 120)
  1074.                     market.prices[good] = int(market.prices[good] * 0.8)
  1075.             self.player.modify_relationship(self.player.location, "Trade", -15)  # Increased impact
  1076.             self.player.reputation -= 10
  1077.             self.key_events.append(f"Turn {self.turn_counter}: Global recession decreased purchasing power.")
  1078.             return
  1079.  
  1080.         # Negative Local Events
  1081.         if event == "Thieves attacked your caravan and stole some goods!":
  1082.             affected_good = random.choice(list(ALL_GOODS.keys()))
  1083.             stolen_qty = random.randint(10, 20)
  1084.             actual_stolen = min(stolen_qty, self.player.inventory.get(affected_good, 0))
  1085.             self.player.remove_cargo(affected_good, actual_stolen)
  1086.             slow_print(f"Thieves stole {actual_stolen} units of {affected_good} from your caravan!")
  1087.             self.player.modify_relationship(self.player.location, "Trade", -20)  # Increased impact
  1088.             self.player.modify_relationship(self.player.location, "Reputation", -15)
  1089.             self.player.modify_relationship(self.player.location, "Favor", -10)
  1090.             self.player.reputation -= 25
  1091.             self.key_events.append(f"Turn {self.turn_counter}: Thieves stole {actual_stolen} {affected_good}.")
  1092.             return
  1093.  
  1094.         if event == "A storm ruined your goods during travel.":
  1095.             damaged_good = random.choice(list(ALL_GOODS.keys()))
  1096.             damaged_qty = random.randint(10, 20)
  1097.             actual_damaged = min(damaged_qty, self.player.inventory.get(damaged_good, 0))
  1098.             self.player.remove_cargo(damaged_good, actual_damaged)
  1099.             slow_print(f"A storm damaged {actual_damaged} units of {damaged_good} in your cargo.")
  1100.             self.player.modify_relationship(self.player.location, "Trade", -15)  # Increased impact
  1101.             self.player.modify_relationship(self.player.location, "Reputation", -10)
  1102.             self.player.modify_relationship(self.player.location, "Favor", -7)
  1103.             self.player.reputation -= 20
  1104.             self.key_events.append(f"Turn {self.turn_counter}: Storm damaged {actual_damaged} {damaged_good}.")
  1105.             return
  1106.  
  1107.         if event == "Disease outbreak decreased the demand for food.":
  1108.             affected_good = "Food"
  1109.             price_drop = int(GOODS[affected_good]["base_price"] * 0.35)
  1110.             self.current_market.demand[affected_good] = max(50, self.current_market.demand.get(affected_good, 0) - 150)
  1111.             self.current_market.prices[affected_good] = max(Market.PRICE_FLOOR, self.current_market.prices.get(affected_good, 0) - price_drop)
  1112.             slow_print(f"A disease outbreak severely decreased the demand for {affected_good}, lowering its price by ${price_drop}.")
  1113.             self.player.modify_relationship(self.player.location, "Trade", -18)  # Increased impact
  1114.             self.player.modify_relationship(self.player.location, "Reputation", -12)
  1115.             self.player.reputation -= 25
  1116.             self.key_events.append(f"Turn {self.turn_counter}: Disease outbreak decreased demand for {affected_good}.")
  1117.             return
  1118.  
  1119.         if event == "Pirates disrupted trade in the seas.":
  1120.             affected_good = random.choice(list(ALL_GOODS.keys()))
  1121.             price_increase = int(GOODS[affected_good]["base_price"] * 0.45)
  1122.             self.current_market.supply[affected_good] = max(10, self.current_market.supply.get(affected_good, 0) - 100)
  1123.             self.current_market.prices[affected_good] = min(Market.PRICE_CEILING, self.current_market.prices.get(affected_good, 0) + price_increase)
  1124.             slow_print(f"Pirates severely disrupted trade, decreasing supply and increasing price of {affected_good} by ${price_increase}.")
  1125.             self.player.modify_relationship(self.player.location, "Trade", -18)  # Increased impact
  1126.             self.player.modify_relationship(self.player.location, "Reputation", -12)
  1127.             self.player.modify_relationship(self.player.location, "Favor", -8)
  1128.             self.player.reputation -= 25
  1129.             self.key_events.append(f"Turn {self.turn_counter}: Pirates disrupted trade, affected {affected_good}.")
  1130.             return
  1131.  
  1132.         if event == "Government imposed a tax on trade.":
  1133.             tax_amount = 200
  1134.             self.player.money = max(0, self.player.money - tax_amount)
  1135.             slow_print(f"The government imposed a heavy tax on your trade. You lost ${tax_amount}.")
  1136.             self.player.modify_relationship(self.player.location, "Trade", -18)  # Increased impact
  1137.             self.player.modify_relationship(self.player.location, "Reputation", -12)
  1138.             self.player.reputation -= 20
  1139.             self.key_events.append(f"Turn {self.turn_counter}: Government taxed you ${tax_amount}.")
  1140.             return
  1141.  
  1142.         if event == "Locusts devastated the food supplies in Dusktown.":
  1143.             affected_good = "Food"
  1144.             damage = random.randint(15, 25)
  1145.             actual_damaged = min(damage, self.player.inventory.get(affected_good, 0))
  1146.             if self.player.location == "Dusktown":
  1147.                 self.player.remove_cargo(affected_good, actual_damaged)
  1148.                 slow_print(f"Locusts devastated food supplies in Dusktown, destroying {actual_damaged} units of your {affected_good}.")
  1149.                 self.player.modify_relationship(self.player.location, "Trade", -15)  # Increased impact
  1150.                 self.player.modify_relationship(self.player.location, "Reputation", -10)
  1151.                 self.player.modify_relationship(self.player.location, "Favor", -7)
  1152.                 self.player.reputation -= 20
  1153.                 self.key_events.append(f"Turn {self.turn_counter}: Locusts destroyed {actual_damaged} {affected_good} in Dusktown.")
  1154.             else:
  1155.                 slow_print("Locusts devastated food supplies in Dusktown.")
  1156.             return
  1157.  
  1158.         if event == "Bandit attack in Oakwood stole some of your goods.":
  1159.             affected_good = random.choice(list(ALL_GOODS.keys()))
  1160.             stolen_qty = random.randint(10, 20)
  1161.             actual_stolen = min(stolen_qty, self.player.inventory.get(affected_good, 0))
  1162.             if self.player.location == "Oakwood":
  1163.                 self.player.remove_cargo(affected_good, actual_stolen)
  1164.                 slow_print(f"Bandits in Oakwood stole {actual_stolen} units of your {affected_good}.")
  1165.                 self.player.modify_relationship(self.player.location, "Trade", -20)  # Increased impact
  1166.                 self.player.modify_relationship(self.player.location, "Reputation", -15)
  1167.                 self.player.modify_relationship(self.player.location, "Favor", -10)
  1168.                 self.player.reputation -= 25
  1169.                 self.key_events.append(f"Turn {self.turn_counter}: Bandits in Oakwood stole {actual_stolen} {affected_good}.")
  1170.             else:
  1171.                 slow_print("Bandits attacked traders in Oakwood.")
  1172.             return
  1173.  
  1174.         if event == "Natural disaster in Dusktown affecting multiple goods.":
  1175.             affected_goods = random.sample(list(ALL_GOODS.keys()), k=3)
  1176.             if self.player.location == "Dusktown":
  1177.                 for good in affected_goods:
  1178.                     damaged_qty = random.randint(10, 20)
  1179.                     actual_damaged = min(damaged_qty, self.player.inventory.get(good, 0))
  1180.                     self.player.remove_cargo(good, actual_damaged)
  1181.                 slow_print(f"A natural disaster in Dusktown damaged a significant amount of your goods.")
  1182.                 self.player.modify_relationship(self.player.location, "Trade", -25)  # Increased impact
  1183.                 self.player.modify_relationship(self.player.location, "Reputation", -18)
  1184.                 self.player.modify_relationship(self.player.location, "Favor", -12)
  1185.                 self.player.reputation -= 30
  1186.                 self.key_events.append(f"Turn {self.turn_counter}: Natural disaster damaged goods in Dusktown.")
  1187.             else:
  1188.                 slow_print("A natural disaster struck Dusktown, severely affecting trade.")
  1189.             return
  1190.  
  1191.         # Fallback for unhandled events
  1192.         slow_print("An unusual event occurred, but nothing notable happened.")
  1193.  
  1194.     def trigger_event(self):
  1195.         """Randomly triggers a global or local event based on difficulty settings."""
  1196.         event_chance = DIFFICULTIES[self.difficulty]["event_chance"]
  1197.         # Reduce event chance based on reputation
  1198.         reputation_factor = 1 - (self.player.reputation / 200)  # Adjust as needed
  1199.         reputation_factor = max(0.5, reputation_factor)  # Ensure at least 50% of base chance
  1200.         adjusted_event_chance = event_chance * reputation_factor
  1201.  
  1202.         if random.random() < adjusted_event_chance:
  1203.             event_type = random.choice(["GLOBAL", "LOCAL"])
  1204.             if event_type == "GLOBAL":
  1205.                 event = random.choice(GLOBAL_EVENTS)
  1206.                 slow_print(f"\n{Fore.RED}Global Event: {event}{Style.RESET_ALL}")
  1207.                 if "Pandemic" in event:
  1208.                     if not hasattr(self, "pandemic_applied") or not self.pandemic_applied:
  1209.                         self.apply_event(event, global_event=True)
  1210.                         self.pandemic_applied = True  # Set the flag to prevent multiple applications
  1211.                 else:
  1212.                     self.apply_event(event, global_event=True)
  1213.             else:
  1214.                 event = random.choice(LOCAL_EVENTS)
  1215.                 slow_print(f"\n{Fore.RED}Local Event: {event}{Style.RESET_ALL}")
  1216.                 self.apply_event(event, global_event=False)
  1217.  
  1218.         # Reset the pandemic flag if not used
  1219.         if not hasattr(self, "pandemic_applied"):
  1220.             self.pandemic_applied = False  # Initialize if it doesn't exist
  1221.  
  1222.     def apply_relationship_decay(self):
  1223.         """Gradually decays the relationships over time."""
  1224.         medium_print("\nRelationships have naturally changed over time:")
  1225.         for town in TOWNS:
  1226.             aspects = self.player.relationships[town]
  1227.             for aspect in aspects:
  1228.                 original_score = aspects[aspect]
  1229.                 if aspects[aspect] > 0:
  1230.                     aspects[aspect] -= 2  # Increased decay rate
  1231.                 elif aspects[aspect] < 0:
  1232.                     aspects[aspect] += 2  # Increased recovery rate
  1233.                 # Clamp values
  1234.                 aspects[aspect] = max(RELATIONSHIP_MIN, min(aspects[aspect], RELATIONSHIP_MAX))
  1235.                 change = aspects[aspect] - original_score
  1236.                 if change != 0:
  1237.                     # Notify the player about the change
  1238.                     if change > 0:
  1239.                         medium_print(f"[{town} - {aspect}] Improved by {change} point(s).")
  1240.                     else:
  1241.                         medium_print(f"[{town} - {aspect}] Decreased by {-change} point(s).")
  1242.  
  1243.     def update_relationships_after_purchase(self, good, quantity):
  1244.         town = self.player.location
  1245.         price = self.current_market.prices.get(good, GOODS[good]["base_price"])
  1246.  
  1247.         # Introduce balanced scaling factor
  1248.         scaling_factor = 1 + (price / 500)  # Scales relationship changes with price
  1249.         scaling_factor = min(2.0, scaling_factor)  # Cap scaling factor to avoid excessive influence
  1250.  
  1251.         max_increase = 15  # Cap on the maximum relationship improvement per transaction
  1252.         relationship_change = min(int(quantity * scaling_factor), max_increase)
  1253.  
  1254.         if good in SUPPORTIVE_GOODS:
  1255.             self.player.modify_relationship(town, "Reputation", relationship_change)
  1256.         elif good in STRATEGIC_GOODS:
  1257.             self.player.modify_relationship(town, "Trade", relationship_change)
  1258.         elif good in LUXURY_GOODS:
  1259.             self.player.modify_relationship(town, "Favor", relationship_change)
  1260.  
  1261.     def update_relationships_after_sale(self, good, quantity):
  1262.         town = self.player.location
  1263.         price = self.current_market.prices.get(good, GOODS[good]["base_price"])
  1264.  
  1265.         # Adjust scaling: more linear impact for sales
  1266.         scaling_factor = 0.8 + (price / 1000)  # Lower scaling for sales
  1267.         scaling_factor = min(1.5, scaling_factor)  # Cap scaling factor
  1268.  
  1269.         max_increase = 10  # Cap on the maximum relationship improvement per transaction
  1270.         relationship_change = min(int(quantity * scaling_factor), max_increase)
  1271.  
  1272.         if good in SUPPORTIVE_GOODS:
  1273.             self.player.modify_relationship(town, "Reputation", relationship_change)
  1274.         elif good in STRATEGIC_GOODS:
  1275.             self.player.modify_relationship(town, "Trade", relationship_change)
  1276.         elif good in LUXURY_GOODS:
  1277.             self.player.modify_relationship(town, "Favor", relationship_change)
  1278.        
  1279.     def apply_decay(self):
  1280.         """Applies decay to supply and demand in all markets to move towards base values."""
  1281.         for good, info in GOODS.items():
  1282.             for market in self.markets.values():
  1283.                 # Supply decay towards base_supply
  1284.                 if market.supply.get(good, 0) < info["base_supply"]:
  1285.                     market.supply[good] += info["base_supply"] * self.supply_decay_rate
  1286.                     if market.supply[good] > info["base_supply"]:
  1287.                         market.supply[good] = info["base_supply"]
  1288.                 elif market.supply.get(good, 0) > info["base_supply"]:
  1289.                     market.supply[good] -= info["base_supply"] * self.supply_decay_rate
  1290.                     if market.supply[good] < info["base_supply"]:
  1291.                         market.supply[good] = info["base_supply"]
  1292.  
  1293.                 # Demand decay towards base_demand
  1294.                 if market.demand.get(good, 0) < info["base_demand"]:
  1295.                     market.demand[good] += info["base_demand"] * self.demand_decay_rate
  1296.                     if market.demand[good] > info["base_demand"]:
  1297.                         market.demand[good] = info["base_demand"]
  1298.                 elif market.demand.get(good, 0) > info["base_demand"]:
  1299.                     market.demand[good] -= info["base_demand"] * self.demand_decay_rate
  1300.                     if market.demand[good] < info["base_demand"]:
  1301.                         market.demand[good] = info["base_demand"]
  1302.  
  1303.                 # Additional decay for Gold's value to prevent hoarding
  1304.                 if good == "Gold":
  1305.                     market.prices["Gold"] = max(Market.PRICE_FLOOR, int(market.prices["Gold"] * 0.98))  # Increased depreciation per turn
  1306.  
  1307.     def end_game(self):
  1308.         """Displays the final game results and concludes the game."""
  1309.         slow_print("\nGame Over!")
  1310.         fast_print(f"Final Money: ${self.player.money}")
  1311.         total_goods = sum(self.player.inventory.values())
  1312.         fast_print(f"Total Goods: {total_goods}")
  1313.         net_worth = self.player.money + sum(
  1314.             self.player.inventory.get(good, 0) * self.current_market.prices.get(good, 0) for good in ALL_GOODS
  1315.         )
  1316.         fast_print(f"Net Worth: ${net_worth}")
  1317.  
  1318.         # Calculate financial performance
  1319.         financial_performance = self.calculate_financial_performance(net_worth)
  1320.         # Calculate social performance
  1321.         social_performance = self.calculate_social_performance()
  1322.  
  1323.         # Display financial performance
  1324.         slow_print(f"\n{Fore.GREEN}Financial Performance:{Style.RESET_ALL}")
  1325.         fast_print(f" - Net Worth Score: {financial_performance['net_worth_score']} / 5")
  1326.         fast_print(f" - Liquidity Score: {financial_performance['liquidity_score']} / 5")
  1327.         financial_total = financial_performance['net_worth_score'] + financial_performance['liquidity_score']
  1328.         fast_print(f" - Total Financial Score: {financial_total} / 10")
  1329.  
  1330.         # Display social performance
  1331.         slow_print(f"\n{Fore.BLUE}Social Performance (Relationships):{Style.RESET_ALL}")
  1332.         fast_print(f" - Average Trade Relations Score: {social_performance['trade_score']} / 5")
  1333.         fast_print(f" - Average Reputation Score: {social_performance['reputation_score']} / 5")
  1334.         fast_print(f" - Average Favor Score: {social_performance['favor_score']} / 5")
  1335.         fast_print(f" - Relationship Status Influence Score: {social_performance['status_score']} / 5")  # New Line
  1336.         social_total = social_performance['trade_score'] + social_performance['reputation_score'] + social_performance['favor_score'] + social_performance['status_score']  # Updated
  1337.         fast_print(f" - Total Social Score: {social_total} / 20")  # Updated
  1338.  
  1339.         # Calculate total score
  1340.         total_score = financial_total + social_total
  1341.         max_total_score = 10 + 20  # Financial max + Social max
  1342.  
  1343.         # Determine final rating based on total_score
  1344.         rating = self.determine_rating(total_score)
  1345.         rating_message = self.generate_final_message(financial_total, social_total, rating)
  1346.  
  1347.         slow_print(f"\nFinal Rating: {Fore.CYAN}{rating}{Style.RESET_ALL}")
  1348.         slow_print(rating_message)
  1349.  
  1350.         # Display key events summary
  1351.         slow_print("\nSummary of Your Journey:")
  1352.         for event in self.key_events:
  1353.             fast_print(event)
  1354.         # Removed the truncation with "..."
  1355.         # if len(self.key_events) > 50:
  1356.         #     fast_print("...")
  1357.  
  1358.         # Display Relationship Status Summary
  1359.         slow_print("\nRelationship Status Summary:")
  1360.         status_counts = {
  1361.             "Ally": 0,
  1362.             "Friendly": 0,
  1363.             "Neutral": 0,
  1364.             "Hostile": 0,
  1365.             "Enemy": 0
  1366.         }
  1367.         for town in self.player.visited_towns:
  1368.             status = self.player.get_overall_relationship_status(town)
  1369.             if status in status_counts:
  1370.                 status_counts[status] += 1
  1371.         for status, count in status_counts.items():
  1372.             color = Fore.GREEN if status == "Ally" else Fore.BLUE if status == "Friendly" else Fore.YELLOW if status == "Neutral" else Fore.RED if status in ["Hostile", "Enemy"] else Fore.WHITE
  1373.             fast_print(f"{color}{status}: {count}{Style.RESET_ALL}")
  1374.        
  1375.         slow_print("\nThank you for playing the Text-Based Trading Game!")
  1376.         sys.exit()
  1377.  
  1378.     def generate_final_message(self, financial_total, social_total, rating):
  1379.         """Generates a final message based on financial and social performance."""
  1380.         messages = {
  1381.             "Legendary": "Incredible! You became an unmatched trading legend with stellar relationships and unmatched wealth.",
  1382.             "Excellent": "Congratulations! You became a legendary trader with excellent relationships and strong financials.",
  1383.             "Great": "Outstanding! You achieved impressive wealth and maintained excellent relationships.",
  1384.             "Good": "Well done! You made a substantial profit and maintained good relationships.",
  1385.             "Decent": "You made a respectable profit and kept decent relations.",
  1386.             "Poor": "You faced challenges in trading, with room to improve both financially and socially.",
  1387.             "Terrible": "Your trading journey was fraught with difficulties. Significant improvement is needed in both trading and relationships."
  1388.         }
  1389.  
  1390.         # Customize the message based on which performance was better
  1391.         if financial_total > social_total:
  1392.             messages["Good"] += " Your financial acumen outshined your social engagements."
  1393.             messages["Decent"] += " You did well financially but could improve your relationships."
  1394.             messages["Poor"] += " Financial setbacks affected your journey more than social aspects."
  1395.         elif social_total > financial_total:
  1396.             messages["Good"] += " Your strong relationships boosted your trading journey."
  1397.             messages["Decent"] += " Your social skills helped balance financial challenges."
  1398.             messages["Poor"] += " Despite good relations, financial challenges hindered progress."
  1399.  
  1400.         return messages.get(rating, "")
  1401.  
  1402.     def calculate_net_worth_score(self, net_worth):
  1403.         """Assigns a score based on net worth."""
  1404.         if net_worth >= 40000:
  1405.             return 5
  1406.         elif net_worth >= 30000:
  1407.             return 4
  1408.         elif net_worth >= 20000:
  1409.             return 3
  1410.         elif net_worth >= 15000:
  1411.             return 2
  1412.         elif net_worth >= 10000:
  1413.             return 1
  1414.         else:
  1415.             return 0
  1416.  
  1417.     def calculate_trade_score(self, average_trade):
  1418.         """Assigns a score based on average Trade Relations."""
  1419.         if average_trade >= 60:
  1420.             return 5
  1421.         elif average_trade >= 40:
  1422.             return 4
  1423.         elif average_trade >= 20:
  1424.             return 3
  1425.         elif average_trade >= 10:
  1426.             return 2
  1427.         elif average_trade >= 0:
  1428.             return 1
  1429.         else:
  1430.             return 0
  1431.  
  1432.     def calculate_reputation_score(self, average_reputation):
  1433.         """Assigns a score based on average Reputation."""
  1434.         if average_reputation >= 60:
  1435.             return 5
  1436.         elif average_reputation >= 40:
  1437.             return 4
  1438.         elif average_reputation >= 20:
  1439.             return 3
  1440.         elif average_reputation >= 1:
  1441.             return 2
  1442.         elif average_reputation >= 0:
  1443.             return 1
  1444.         else:
  1445.             return 0
  1446.  
  1447.     def calculate_favor_score(self, average_favor):
  1448.         """Assigns a score based on average Favor."""
  1449.         if average_favor >= 60:
  1450.             return 5
  1451.         elif average_favor >= 40:
  1452.             return 4
  1453.         elif average_favor >= 20:
  1454.             return 3
  1455.         elif average_favor >= 10:
  1456.             return 2
  1457.         elif average_favor >= 0:
  1458.             return 1
  1459.         else:
  1460.             return 0
  1461.  
  1462.     def calculate_social_performance(self):
  1463.         """Calculates social performance metrics, including relationship statuses."""
  1464.         visited_towns = self.player.visited_towns
  1465.         if not visited_towns:
  1466.             visited_towns = {self.player.location}  # Use a set for consistency
  1467.  
  1468.         # Calculate overall relationship scores for visited towns only
  1469.         total_trade = sum([self.player.relationships[town]["Trade"] for town in visited_towns])
  1470.         total_reputation = sum([self.player.relationships[town]["Reputation"] for town in visited_towns])
  1471.         total_favor = sum([self.player.relationships[town]["Favor"] for town in visited_towns])
  1472.  
  1473.         average_trade = total_trade / len(visited_towns)
  1474.         average_reputation = total_reputation / len(visited_towns)
  1475.         average_favor = total_favor / len(visited_towns)
  1476.  
  1477.         trade_score = self.calculate_trade_score(average_trade)
  1478.         reputation_score = self.calculate_reputation_score(average_reputation)
  1479.         favor_score = self.calculate_favor_score(average_favor)
  1480.  
  1481.         # Calculate relationship status influence score
  1482.         status_score = 0
  1483.         for town in visited_towns:
  1484.             status = self.player.get_overall_relationship_status(town)
  1485.             if status == "Ally":
  1486.                 status_score += 3
  1487.             elif status == "Friendly":
  1488.                 status_score += 2
  1489.             elif status == "Neutral":
  1490.                 status_score += 1
  1491.             elif status == "Hostile":
  1492.                 status_score -= 1
  1493.             elif status == "Enemy":
  1494.                 status_score -= 2
  1495.  
  1496.         # Normalize status_score to a maximum of 5
  1497.         # Assuming maximum positive score is 3 * number of towns
  1498.         max_positive = 3 * len(visited_towns)
  1499.         if max_positive == 0:
  1500.             normalized_status_score = 0
  1501.         else:
  1502.             normalized_status_score = min(int((status_score / max_positive) * 5), 5)
  1503.             normalized_status_score = max(-5, normalized_status_score)  # Clamp between -5 and 5
  1504.  
  1505.         # Convert negative scores to 0 for the scoring system
  1506.         if normalized_status_score < 0:
  1507.             normalized_status_score = 0
  1508.         elif normalized_status_score > 5:
  1509.             normalized_status_score = 5
  1510.  
  1511.         return {
  1512.             "trade_score": trade_score,
  1513.             "reputation_score": reputation_score,
  1514.             "favor_score": favor_score,
  1515.             "status_score": normalized_status_score  # New field
  1516.         }
  1517.    
  1518.     def calculate_financial_performance(self, net_worth):
  1519.         """Calculates financial performance metrics."""
  1520.         # New liquidity ratio calculation based on starting money
  1521.         liquidity_ratio = self.player.money / self.player.starting_money
  1522.         liquidity_score = self.calculate_liquidity_score(liquidity_ratio)
  1523.         net_worth_score = self.calculate_net_worth_score(net_worth)
  1524.  
  1525.         return {
  1526.             "net_worth_score": net_worth_score,
  1527.             "liquidity_score": liquidity_score
  1528.         }
  1529.  
  1530.     def calculate_liquidity_score(self, liquidity_ratio):
  1531.         """
  1532.        Assigns a liquidity score based on the player's ending wealth relative to their starting wealth.
  1533.        Scales down the score the closer the ending wealth is to the starting wealth.
  1534.        """
  1535.         if liquidity_ratio >= 2.0:  # Ending cash is at least double the starting cash
  1536.             return 5
  1537.         elif liquidity_ratio >= 1.8:
  1538.             return 4
  1539.         elif liquidity_ratio >= 1.5:
  1540.             return 3
  1541.         elif liquidity_ratio >= 1.2:
  1542.             return 2
  1543.         elif liquidity_ratio > 1.0:
  1544.             return 1  # Small improvement still earns a point
  1545.         else:
  1546.             return 0  # No score for failing to maintain starting wealth
  1547.  
  1548.     def determine_rating(self, total_score):
  1549.         """Determines the final rating based on total_score."""
  1550.         if total_score >= 30:
  1551.             return "Legendary"
  1552.         elif total_score >= 25:
  1553.             return "Excellent"
  1554.         elif total_score >= 20:
  1555.             return "Great"
  1556.         elif total_score >= 15:
  1557.             return "Good"
  1558.         elif total_score >= 10:
  1559.             return "Decent"
  1560.         elif total_score >= 5:
  1561.             return "Poor"
  1562.         else:
  1563.             return "Terrible"
  1564.  
  1565.     def generate_final_message(self, financial_total, social_total, rating):
  1566.         """Generates a final message based on financial and social performance."""
  1567.         messages = {
  1568.             "Legendary": "Incredible! You became an unmatched trading legend with stellar relationships and unmatched wealth.",
  1569.             "Excellent": "Congratulations! You became a legendary trader with excellent relationships and strong financials.",
  1570.             "Great": "Outstanding! You achieved impressive wealth or maintained excellent relationships.",
  1571.             "Good": "Well done! You made a substantial profit or maintained good relationships.",
  1572.             "Decent": "You made a respectable profit or kept decent relations.",
  1573.             "Poor": "You faced challenges in trading, with room to improve both financially and socially.",
  1574.             "Terrible": "Your trading journey was fraught with difficulties. Significant improvement is needed in both trading and relationships."
  1575.         }
  1576.  
  1577.         # Customize the message based on which performance was better
  1578.         if financial_total > social_total:
  1579.             messages["Good"] += " Your financial acumen outshined your social engagements."
  1580.             messages["Decent"] += " You did well financially but could improve your relationships."
  1581.             messages["Poor"] += " Financial setbacks affected your journey more than social aspects."
  1582.         elif social_total > financial_total:
  1583.             messages["Good"] += " Your strong relationships boosted your trading journey."
  1584.             messages["Decent"] += " Your social skills helped balance financial challenges."
  1585.             messages["Poor"] += " Despite good relations, financial challenges hindered progress."
  1586.  
  1587.         return messages.get(rating, "")
  1588.  
  1589.     def calculate_social_performance(self):
  1590.         """Calculates social performance metrics, including relationship statuses."""
  1591.         visited_towns = self.player.visited_towns
  1592.         if not visited_towns:
  1593.             visited_towns = {self.player.location}  # Use a set for consistency
  1594.  
  1595.         # Calculate overall relationship scores for visited towns only
  1596.         total_trade = sum([self.player.relationships[town]["Trade"] for town in visited_towns])
  1597.         total_reputation = sum([self.player.relationships[town]["Reputation"] for town in visited_towns])
  1598.         total_favor = sum([self.player.relationships[town]["Favor"] for town in visited_towns])
  1599.  
  1600.         average_trade = total_trade / len(visited_towns)
  1601.         average_reputation = total_reputation / len(visited_towns)
  1602.         average_favor = total_favor / len(visited_towns)
  1603.  
  1604.         trade_score = self.calculate_trade_score(average_trade)
  1605.         reputation_score = self.calculate_reputation_score(average_reputation)
  1606.         favor_score = self.calculate_favor_score(average_favor)
  1607.  
  1608.         # Calculate relationship status influence score
  1609.         status_score = 0
  1610.         for town in visited_towns:
  1611.             status = self.player.get_overall_relationship_status(town)
  1612.             if status == "Ally":
  1613.                 status_score += 3
  1614.             elif status == "Friendly":
  1615.                 status_score += 2
  1616.             elif status == "Neutral":
  1617.                 status_score += 1
  1618.             elif status == "Hostile":
  1619.                 status_score -= 1
  1620.             elif status == "Enemy":
  1621.                 status_score -= 2
  1622.  
  1623.         # Normalize status_score to a maximum of 5
  1624.         # Assuming maximum positive score is 3 * number of towns
  1625.         max_positive = 3 * len(visited_towns)
  1626.         if max_positive == 0:
  1627.             normalized_status_score = 0
  1628.         else:
  1629.             normalized_status_score = min(int((status_score / max_positive) * 5), 5)
  1630.             normalized_status_score = max(-5, normalized_status_score)  # Clamp between -5 and 5
  1631.  
  1632.         # Convert negative scores to 0 for the scoring system
  1633.         if normalized_status_score < 0:
  1634.             normalized_status_score = 0
  1635.         elif normalized_status_score > 5:
  1636.             normalized_status_score = 5
  1637.  
  1638.         return {
  1639.             "trade_score": trade_score,
  1640.             "reputation_score": reputation_score,
  1641.             "favor_score": favor_score,
  1642.             "status_score": normalized_status_score  # New field
  1643.         }
  1644.    
  1645.     def apply_decay(self):
  1646.         """Applies decay to supply and demand in all markets to move towards base values."""
  1647.         for good, info in GOODS.items():
  1648.             for market in self.markets.values():
  1649.                 # Supply decay towards base_supply
  1650.                 if market.supply.get(good, 0) < info["base_supply"]:
  1651.                     market.supply[good] += info["base_supply"] * self.supply_decay_rate
  1652.                     if market.supply[good] > info["base_supply"]:
  1653.                         market.supply[good] = info["base_supply"]
  1654.                 elif market.supply.get(good, 0) > info["base_supply"]:
  1655.                     market.supply[good] -= info["base_supply"] * self.supply_decay_rate
  1656.                     if market.supply[good] < info["base_supply"]:
  1657.                         market.supply[good] = info["base_supply"]
  1658.  
  1659.                 # Demand decay towards base_demand
  1660.                 if market.demand.get(good, 0) < info["base_demand"]:
  1661.                     market.demand[good] += info["base_demand"] * self.demand_decay_rate
  1662.                     if market.demand[good] > info["base_demand"]:
  1663.                         market.demand[good] = info["base_demand"]
  1664.                 elif market.demand.get(good, 0) > info["base_demand"]:
  1665.                     market.demand[good] -= info["base_demand"] * self.demand_decay_rate
  1666.                     if market.demand[good] < info["base_demand"]:
  1667.                         market.demand[good] = info["base_demand"]
  1668.  
  1669.                 # Additional decay for Gold's value to prevent hoarding
  1670.                 if good == "Gold":
  1671.                     market.prices["Gold"] = max(Market.PRICE_FLOOR, int(market.prices["Gold"] * 0.98))  # Increased depreciation per turn
  1672.  
  1673.     def end_game(self):
  1674.         """Displays the final game results and concludes the game."""
  1675.         # The end_game method remains the same
  1676.  
  1677.         slow_print("\nGame Over!")
  1678.         fast_print(f"Final Money: ${self.player.money}")
  1679.         total_goods = sum(self.player.inventory.values())
  1680.         fast_print(f"Total Goods: {total_goods}")
  1681.         net_worth = self.player.money + sum(
  1682.             self.player.inventory.get(good, 0) * self.current_market.prices.get(good, 0) for good in ALL_GOODS
  1683.         )
  1684.         fast_print(f"Net Worth: ${net_worth}")
  1685.  
  1686.         # Calculate financial performance
  1687.         financial_performance = self.calculate_financial_performance(net_worth)
  1688.         # Calculate social performance
  1689.         social_performance = self.calculate_social_performance()
  1690.  
  1691.         # Display financial performance
  1692.         slow_print(f"\n{Fore.GREEN}Financial Performance:{Style.RESET_ALL}")
  1693.         fast_print(f" - Net Worth Score: {financial_performance['net_worth_score']} / 5")
  1694.         fast_print(f" - Liquidity Score: {financial_performance['liquidity_score']} / 5")
  1695.         financial_total = financial_performance['net_worth_score'] + financial_performance['liquidity_score']
  1696.         fast_print(f" - Total Financial Score: {financial_total} / 10")
  1697.  
  1698.         # Display social performance
  1699.         slow_print(f"\n{Fore.BLUE}Social Performance (Relationships):{Style.RESET_ALL}")
  1700.         fast_print(f" - Average Trade Relations Score: {social_performance['trade_score']} / 5")
  1701.         fast_print(f" - Average Reputation Score: {social_performance['reputation_score']} / 5")
  1702.         fast_print(f" - Average Favor Score: {social_performance['favor_score']} / 5")
  1703.         fast_print(f" - Relationship Status Influence Score: {social_performance['status_score']} / 5")  # New Line
  1704.         social_total = social_performance['trade_score'] + social_performance['reputation_score'] + social_performance['favor_score'] + social_performance['status_score']  # Updated
  1705.         fast_print(f" - Total Social Score: {social_total} / 20")  # Updated
  1706.  
  1707.         # Calculate total score
  1708.         total_score = financial_total + social_total
  1709.         max_total_score = 10 + 20  # Financial max + Social max
  1710.  
  1711.         # Determine final rating based on total_score
  1712.         rating = self.determine_rating(total_score)
  1713.         rating_message = self.generate_final_message(financial_total, social_total, rating)
  1714.  
  1715.         slow_print(f"\nFinal Rating: {Fore.CYAN}{rating}{Style.RESET_ALL}")
  1716.         slow_print(rating_message)
  1717.  
  1718.         # Display key events summary
  1719.         slow_print("\nSummary of Your Journey:")
  1720.         for event in self.key_events[:50]:  # Limit to first 50 for readability
  1721.             fast_print(event)
  1722.         if len(self.key_events) > 50:
  1723.             fast_print("...")
  1724.  
  1725.         # Display Relationship Status Summary
  1726.         slow_print("\nRelationship Status Summary:")
  1727.         status_counts = {
  1728.             "Ally": 0,
  1729.             "Friendly": 0,
  1730.             "Neutral": 0,
  1731.             "Hostile": 0,
  1732.             "Enemy": 0
  1733.         }
  1734.         for town in self.player.visited_towns:
  1735.             status = self.player.get_overall_relationship_status(town)
  1736.             if status in status_counts:
  1737.                 status_counts[status] += 1
  1738.         for status, count in status_counts.items():
  1739.             color = Fore.GREEN if status == "Ally" else Fore.BLUE if status == "Friendly" else Fore.YELLOW if status == "Neutral" else Fore.RED if status in ["Hostile", "Enemy"] else Fore.WHITE
  1740.             fast_print(f"{color}{status}: {count}{Style.RESET_ALL}")
  1741.        
  1742.         slow_print("\nThank you for playing the Text-Based Trading Game!")
  1743.         sys.exit()
  1744.  
  1745. # Print methods for different speeds
  1746. def fast_print(text):
  1747.     for char in text:
  1748.         print(char, end="", flush=True)
  1749.         time.sleep(0.0005)
  1750.     print()
  1751.  
  1752. def medium_print(text):
  1753.     for char in text:
  1754.         print(char, end="", flush=True)
  1755.         time.sleep(0.005)  # Medium typing speed
  1756.     print()
  1757.  
  1758. def slow_print(text):
  1759.     for char in text:
  1760.         print(char, end="", flush=True)
  1761.         time.sleep(0.02)  # Slow typing speed
  1762.     print()
  1763.  
  1764. def get_relationship_status(score):
  1765.     """Converts a numerical relationship score into a descriptive status."""
  1766.     if score >= 20:
  1767.         return "Ally"
  1768.     elif score >= 10:
  1769.         return "Friendly"
  1770.     elif score >= 0:
  1771.         return "Neutral"
  1772.     elif score <= -20:
  1773.         return "Enemy"
  1774.     elif score <= -10:
  1775.         return "Hostile"
  1776.     else:
  1777.         return "Neutral"  # For scores between -19 and -1
  1778.  
  1779. # Main execution block with error handling
  1780. if __name__ == "__main__":
  1781.     try:
  1782.         game_instance = Game()
  1783.         game_instance.start()
  1784.     except Exception as e:
  1785.         print(f"\n{Fore.RED}An unexpected error occurred: {e}{Style.RESET_ALL}")
  1786.         sys.exit(1)
  1787.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement