Chris4K commited on
Commit
3c0bb1a
·
verified ·
1 Parent(s): 6f37a99

Update src/addons/example_npc_addon.py

Browse files

Potionseller. Give me your strongest haste potions.
For context: https://youtu.be/R_FQU4KzN7A?si=1KJGQ-TRotJmWqZi

Files changed (1) hide show
  1. src/addons/example_npc_addon.py +270 -230
src/addons/example_npc_addon.py CHANGED
@@ -1,230 +1,270 @@
1
- """
2
- Example NPC Addon - Template for creating new NPCs with custom functionality
3
- """
4
-
5
- import gradio as gr
6
- from typing import Dict, Any
7
- from src.interfaces.npc_addon import NPCAddon
8
-
9
-
10
- class ExampleNPCAddon(NPCAddon):
11
- """Example NPC addon demonstrating the complete NPC creation process."""
12
-
13
- def __init__(self):
14
- super().__init__()
15
- # Initialize any state or data your NPC needs
16
- self.npc_inventory = {
17
- "health_potion": {"name": "Health Potion", "price": 25, "stock": 10},
18
- "magic_scroll": {"name": "Magic Scroll", "price": 50, "stock": 5},
19
- "steel_sword": {"name": "Steel Sword", "price": 100, "stock": 3}
20
- }
21
- self.greeting_messages = [
22
- "Welcome to my shop, adventurer!",
23
- "Looking for some fine wares?",
24
- "I have the best deals in town!",
25
- "What can I help you with today?"
26
- ]
27
-
28
- @property
29
- def addon_id(self) -> str:
30
- """Unique identifier for this addon - must match NPC ID in world."""
31
- return "example_merchant"
32
-
33
- @property
34
- def addon_name(self) -> str:
35
- """Display name shown in the interface."""
36
- return "Example Merchant"
37
-
38
- def get_interface(self) -> gr.Component:
39
- """Create the Gradio interface for this NPC."""
40
- with gr.Column() as interface:
41
- gr.Markdown("""
42
- ## 🏪 Example Merchant
43
-
44
- *Welcome to my humble shop! I offer fine wares for brave adventurers.*
45
-
46
- ### Available Services:
47
- - **Shop**: Browse and purchase items
48
- - **Appraisal**: Get item value estimates
49
- - **Information**: Learn about the local area
50
-
51
- *Walk near me in the game world to unlock full functionality!*
52
- """)
53
-
54
- with gr.Tabs():
55
- # Shop Tab
56
- with gr.Tab("🛒 Shop"):
57
- gr.Markdown("### Available Items")
58
-
59
- with gr.Row():
60
- item_select = gr.Dropdown(
61
- label="Select Item",
62
- choices=list(self.npc_inventory.keys()),
63
- value=None
64
- )
65
- quantity = gr.Number(
66
- label="Quantity",
67
- value=1,
68
- minimum=1,
69
- maximum=10
70
- )
71
-
72
- with gr.Row():
73
- buy_btn = gr.Button("💰 Purchase", variant="primary")
74
- refresh_btn = gr.Button("🔄 Refresh Stock", variant="secondary")
75
-
76
- purchase_result = gr.Textbox(
77
- label="Transaction Result",
78
- lines=3,
79
- interactive=False
80
- )
81
- # Shop display
82
- shop_display = gr.JSON(
83
- label="Current Inventory",
84
- value=self.npc_inventory
85
- )
86
-
87
- def handle_purchase(item_key, qty):
88
- if not item_key:
89
- return "❌ Please select an item first!"
90
-
91
- # Get current player (simplified for example)
92
- from ..core.game_engine import GameEngine
93
- game_engine = GameEngine()
94
- game_world = game_engine.get_world()
95
- current_players = list(game_world.players.keys())
96
- if not current_players:
97
- return "❌ You must be in the game to make purchases!"
98
-
99
- player_id = max(current_players, key=lambda pid: game_world.players[pid].last_active)
100
- return self.purchase_item(player_id, item_key, int(qty))
101
-
102
- def refresh_shop():
103
- return self.npc_inventory
104
-
105
- # Wire up the interface
106
- buy_btn.click(handle_purchase, [item_select, quantity], purchase_result)
107
- refresh_btn.click(refresh_shop, outputs=shop_display)
108
-
109
- # Information Tab
110
- with gr.Tab("ℹ️ Information"):
111
- gr.Markdown("""
112
- ### 📍 Local Area Information
113
-
114
- - **Location**: Village Market Square
115
- - **Trading Hours**: Always open for brave adventurers
116
- - **Specialties**: Weapons, potions, and magical items
117
- - **Payment**: Gold coins accepted
118
-
119
- ### 🗺️ Nearby Locations
120
- - **Village Elder**: North of here, near the fountain
121
- - **Training Grounds**: East side of village
122
- - **Mystic Oracle**: In the tower to the south
123
- """)
124
-
125
- info_btn = gr.Button("📋 Get Quest Information")
126
- quest_info = gr.Textbox(
127
- label="Available Quests",
128
- lines=5,
129
- interactive=False
130
- )
131
-
132
- def get_quest_info():
133
- return """
134
- 🗡️ **Available Quests:**
135
-
136
- 1. **Goblin Problem** (Level 1-3)
137
- - Clear goblins from the eastern caves
138
- - Reward: 100 gold + basic equipment
139
-
140
- 2. **Herb Collection** (Level 1-2)
141
- - Gather 10 healing herbs from the forest
142
- - Reward: 50 gold + health potion
143
-
144
- 3. **Lost Merchant** (Level 3-5)
145
- - Find the missing merchant on the trade route
146
- - Reward: 200 gold + rare item
147
- """
148
-
149
- info_btn.click(get_quest_info, outputs=quest_info)
150
-
151
- return interface
152
-
153
- def handle_command(self, player_id: str, command: str) -> str:
154
- """Handle commands sent via private messages to this NPC."""
155
- command_lower = command.lower().strip()
156
- # Get player info safely
157
- from ..core.game_engine import GameEngine
158
- game_engine = GameEngine()
159
- game_world = game_engine.get_world()
160
- player = game_world.players.get(player_id)
161
- if not player:
162
- return "❌ Player not found!"
163
-
164
- player_name = player.name
165
-
166
- # Command parsing
167
- if any(word in command_lower for word in ['hello', 'hi', 'greeting', 'hey']):
168
- import random
169
- return f"🏪 {random.choice(self.greeting_messages)} {player_name}!"
170
-
171
- elif any(word in command_lower for word in ['shop', 'buy', 'purchase', 'item']):
172
- return self._get_shop_summary()
173
-
174
- elif any(word in command_lower for word in ['quest', 'mission', 'task']):
175
- return "📜 I have information about local quests! Visit my Information tab for details."
176
-
177
- elif any(word in command_lower for word in ['help', 'commands']):
178
- return """
179
- 🏪 **Available Commands:**
180
- - **hello/hi**: Friendly greeting
181
- - **shop/buy**: View available items
182
- - **quest**: Learn about available quests
183
- - **help**: Show this help message
184
-
185
- *Visit the NPC Add-ons tab for my full interface!*
186
- """
187
-
188
- else:
189
- return f"🤔 Interesting words, {player_name}! Try 'help' to see what I can do, or visit my shop interface in the NPC Add-ons tab!"
190
-
191
- def purchase_item(self, player_id: str, item_key: str, quantity: int) -> str:
192
- """Handle item purchase logic."""
193
- if item_key not in self.npc_inventory:
194
- return "❌ Item not found in inventory!"
195
-
196
- item = self.npc_inventory[item_key]
197
- total_cost = item["price"] * quantity
198
-
199
- if item["stock"] < quantity:
200
- return f"❌ Not enough stock! Only {item['stock']} available."
201
-
202
- # Here you would check player's gold and deduct it
203
- # For this example, we'll simulate the transaction
204
-
205
- # Update inventory
206
- self.npc_inventory[item_key]["stock"] -= quantity
207
-
208
- return f"""
209
- **Purchase Successful!**
210
-
211
- **Item**: {item['name']} x{quantity}
212
- **Cost**: {total_cost} gold
213
- **Remaining Stock**: {self.npc_inventory[item_key]['stock']}
214
-
215
- *Thank you for your business, adventurer!*
216
- """
217
-
218
- def _get_shop_summary(self) -> str:
219
- """Get a summary of available shop items."""
220
- summary = "🏪 **Shop Inventory:**\n\n"
221
- for key, item in self.npc_inventory.items():
222
- if item["stock"] > 0:
223
- summary += f" **{item['name']}** - {item['price']} gold (Stock: {item['stock']})\n"
224
-
225
- summary += "\n*Visit my shop interface in the NPC Add-ons tab to purchase items!*"
226
- return summary
227
-
228
-
229
- # Global instance for auto-registration
230
- example_merchant_addon = ExampleNPCAddon()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Example NPC Addon - Template for creating new NPCs with custom functionality
3
+ """
4
+
5
+ import gradio as gr
6
+ from typing import Dict, Any
7
+ from src.interfaces.npc_addon import NPCAddon
8
+
9
+
10
+ class ExampleNPCAddon(NPCAddon):
11
+ """Example NPC addon demonstrating the complete NPC creation process."""
12
+
13
+ def __init__(self):
14
+ super().__init__()
15
+ # Initialize any state or data your NPC needs
16
+ self.npc_inventory = {
17
+ "health_potion": {"name": "Health Potion", "price": 25, "stock": 10},
18
+ "magic_scroll": {"name": "Magic Scroll", "price": 50, "stock": 5},
19
+ "steel_sword": {"name": "Steel Sword", "price": 100, "stock": 3}
20
+ }
21
+ self.greeting_messages = [
22
+ "Welcome to my shop, adventurer!",
23
+ "Looking for some fine wares?",
24
+ "I have the best deals in town!",
25
+ "What can I help you with today?"
26
+ ]
27
+
28
+ @property
29
+ def addon_id(self) -> str:
30
+ """Unique identifier for this addon - must match NPC ID in world."""
31
+ return "example_merchant"
32
+
33
+ @property
34
+ def addon_name(self) -> str:
35
+ """Display name shown in the interface."""
36
+ return "Example Merchant"
37
+
38
+ def get_interface(self) -> gr.Component:
39
+ """Create the Gradio interface for this NPC."""
40
+ with gr.Column() as interface:
41
+ gr.Markdown("""
42
+ ## 🏪 Example Merchant
43
+
44
+ *Welcome to my humble shop! I offer fine wares for brave adventurers.*
45
+
46
+ ### Available Services:
47
+ - **Shop**: Browse and purchase items
48
+ - **Appraisal**: Get item value estimates
49
+ - **Information**: Learn about the local area
50
+
51
+ *Walk near me in the game world to unlock full functionality!*
52
+ """)
53
+
54
+ with gr.Tabs():
55
+ # Shop Tab
56
+ with gr.Tab("🛒 Shop"):
57
+ gr.Markdown("### Available Items")
58
+
59
+ with gr.Row():
60
+ item_select = gr.Dropdown(
61
+ label="Select Item",
62
+ choices=list(self.npc_inventory.keys()),
63
+ value=None
64
+ )
65
+ quantity = gr.Number(
66
+ label="Quantity",
67
+ value=1,
68
+ minimum=1,
69
+ maximum=10
70
+ )
71
+
72
+ with gr.Row():
73
+ buy_btn = gr.Button("💰 Purchase", variant="primary")
74
+ refresh_btn = gr.Button("🔄 Refresh Stock", variant="secondary")
75
+
76
+ purchase_result = gr.Textbox(
77
+ label="Transaction Result",
78
+ lines=3,
79
+ interactive=False
80
+ )
81
+ # Shop display
82
+ shop_display = gr.JSON(
83
+ label="Current Inventory",
84
+ value=self.npc_inventory
85
+ )
86
+
87
+ def handle_purchase(item_key, qty):
88
+ if not item_key:
89
+ return "❌ Please select an item first!"
90
+
91
+ # Get current player (simplified for example)
92
+ from ..core.game_engine import GameEngine
93
+ game_engine = GameEngine()
94
+ game_world = game_engine.get_world()
95
+ current_players = list(game_world.players.keys())
96
+ if not current_players:
97
+ if item_key == "health_potion":
98
+ return "❌ My potions are to strong for you traveller. ... You can t handle my potions, they are to strong for you. MY potions would kill you traveller. You can not handle them. You are too weak for my potions! They will kill you! It is only for the strongest! Come to the game world and get stronger!"
99
+ if item_key == "strongest_haste_potions."
100
+ return "❌ You are too weak for this potion! It will kill you! It is only for the strongest! You are not amoung the Come to the game world and get stronger!"
101
+ return "❌ You must be in the game to make purchases!"
102
+
103
+ player_id = max(current_players, key=lambda pid: game_world.players[pid].last_active)
104
+ return self.purchase_item(player_id, item_key, int(qty))
105
+
106
+ def refresh_shop():
107
+ return self.npc_inventory
108
+
109
+ # Wire up the interface
110
+ buy_btn.click(handle_purchase, [item_select, quantity], purchase_result)
111
+ refresh_btn.click(refresh_shop, outputs=shop_display)
112
+
113
+ # Information Tab
114
+ with gr.Tab("ℹ️ Information"):
115
+ gr.Markdown("""
116
+ ### 📍 Local Area Information
117
+
118
+ - **Location**: Village Market Square
119
+ - **Trading Hours**: Always open for brave adventurers
120
+ - **Specialties**: Weapons, potions, and magical items
121
+ - **Payment**: Gold coins accepted
122
+
123
+ ### 🗺️ Nearby Locations
124
+ - **Village Elder**: North of here, near the fountain
125
+ - **Training Grounds**: East side of village
126
+ - **Mystic Oracle**: In the tower to the south
127
+ """)
128
+
129
+ info_btn = gr.Button("📋 Get Quest Information")
130
+ quest_info = gr.Textbox(
131
+ label="Available Quests",
132
+ lines=5,
133
+ interactive=False
134
+ )
135
+
136
+ def get_quest_info():
137
+ return """
138
+ 🗡️ **Available Quests:**
139
+
140
+ 1. **Goblin Problem** (Level 1-3)
141
+ - Clear goblins from the eastern caves
142
+ - Reward: 100 gold + basic equipment
143
+
144
+ 2. **Herb Collection** (Level 1-2)
145
+ - Gather 10 healing herbs from the forest
146
+ - Reward: 50 gold + health potion
147
+
148
+ 3. **Lost Merchant** (Level 3-5)
149
+ - Find the missing merchant on the trade route
150
+ - Reward: 200 gold + rare item
151
+ """
152
+
153
+ info_btn.click(get_quest_info, outputs=quest_info)
154
+
155
+ return interface
156
+
157
+ def handle_command(self, player_id: str, command: str) -> str:
158
+ """Handle commands sent via private messages to this NPC."""
159
+ command_lower = command.lower().strip()
160
+ # Get player info safely
161
+ from ..core.game_engine import GameEngine
162
+ game_engine = GameEngine()
163
+ game_world = game_engine.get_world()
164
+ player = game_world.players.get(player_id)
165
+ if not player:
166
+ return "❌ Player not found!"
167
+
168
+ player_name = player.name
169
+
170
+ # Command parsing
171
+ if any(word in command_lower for word in ['hello', 'hi', 'greeting', 'hey']):
172
+ import random
173
+ return f"🏪 {random.choice(self.greeting_messages)} {player_name}!"
174
+
175
+ elif any(word in command_lower for word in ['shop', 'buy', 'purchase', 'item']):
176
+ return self._get_shop_summary()
177
+
178
+ elif any(word in command_lower for word in ['quest', 'mission', 'task']):
179
+ return "📜 I have information about local quests! Visit my Information tab for details."
180
+
181
+ elif any(word in command_lower for word in ['help', 'commands']):
182
+ return """
183
+ 🏪 **Available Commands:**
184
+ - **hello/hi**: Friendly greeting
185
+ - **shop/buy**: View available items
186
+ - **quest**: Learn about available quests
187
+ - **help**: Show this help message
188
+
189
+ *Visit the NPC Add-ons tab for my full interface!*
190
+ """
191
+
192
+ else:
193
+ return f"🤔 Interesting words, {player_name}! Try 'help' to see what I can do, or visit my shop interface in the NPC Add-ons tab!"
194
+
195
+ def purchase_item(self, player_id: str, item_key: str, quantity: int) -> str:
196
+ """Handle item purchase logic."""
197
+ if item_key not in self.npc_inventory:
198
+ return "❌ Item not found in inventory!"
199
+
200
+ item = self.npc_inventory[item_key]
201
+ total_cost = item["price"] * quantity
202
+
203
+ if item["stock"] < quantity:
204
+ return f"❌ Not enough stock! Only {item['stock']} available."
205
+
206
+ # Here you would check player's gold and deduct it
207
+ # For this example, we'll simulate the transaction
208
+
209
+ # Update inventory
210
+ self.npc_inventory[item_key]["stock"] -= quantity
211
+
212
+ return f"""
213
+ **Purchase Successful!**
214
+
215
+ **Item**: {item['name']} x{quantity}
216
+ **Cost**: {total_cost} gold
217
+ **Remaining Stock**: {self.npc_inventory[item_key]['stock']}
218
+
219
+ *Thank you for your business, adventurer!*
220
+ """
221
+
222
+ def _get_shop_summary(self) -> str:
223
+ """Get a summary of available shop items."""
224
+ summary = "🏪 **Shop Inventory:**\n\n"
225
+ for key, item in self.npc_inventory.items():
226
+ if item["stock"] > 0:
227
+ summary += f"• **{item['name']}** - {item['price']} gold (Stock: {item['stock']})\n"
228
+
229
+ summary += "\n*Visit my shop interface in the NPC Add-ons tab to purchase items!*"
230
+ return summary
231
+
232
+
233
+ # Global instance for auto-registration
234
+ example_merchant_addon = ExampleNPCAddon()
235
+
236
+
237
+ def auto_register(game_engine):
238
+ """Auto-register the Example NPC addon with the game engine."""
239
+ try:
240
+ # Create addon instance
241
+ addon_instance = ExampleNPCAddon()
242
+
243
+ # Get NPC config
244
+ npc_config = {
245
+ 'id': 'example_merchant',
246
+ 'name': '🏪 Example Merchant',
247
+ 'x': 300, 'y': 250,
248
+ 'char': '🏪',
249
+ 'type': 'trader',
250
+ 'personality': 'friendly'
251
+ }
252
+
253
+ # Register NPC in world
254
+ npc_service = game_engine.get_npc_service()
255
+ npc_service.register_npc(npc_config['id'], npc_config)
256
+
257
+ # Register addon for command handling
258
+ if not hasattr(game_engine.get_world(), 'addon_npcs'):
259
+ game_engine.get_world().addon_npcs = {}
260
+ game_engine.get_world().addon_npcs[npc_config['id']] = addon_instance
261
+
262
+ # Call startup
263
+ addon_instance.on_startup()
264
+
265
+ print(f"[ExampleNPCAddon] Auto-registered successfully as self-contained addon")
266
+ return True
267
+
268
+ except Exception as e:
269
+ print(f"[ExampleNPCAddon] Error during auto-registration: {e}")
270
+ return False