Changeset 131
- Timestamp:
- 04/26/08 13:53:53 (2 years ago)
- Location:
- adventure
- Files:
-
- 10 added
- 2 removed
- 7 modified
-
README (modified) (1 diff)
-
adventuremuc.py (modified) (4 diffs)
-
adventuremuc.rb (deleted)
-
cave.xml (modified) (1 diff)
-
place.py (modified) (1 diff)
-
player.py (modified) (1 diff)
-
thing.py (modified) (1 diff)
-
tower (added)
-
tower/bag.png (added)
-
tower/bag.svg (added)
-
tower/cat.png (added)
-
tower/cat.svg (added)
-
tower/frog.png (added)
-
tower/rana01_architetto_france_01.svg (added)
-
tower/spider.png (added)
-
tower/spider.svg (added)
-
tower/tower.xml (added)
-
world.py (modified) (8 diffs)
-
world.rb (deleted)
Legend:
- Unmodified
- Added
- Removed
-
adventure/README
r94 r131 1 What is this? 1 Notes: 2 ------ 2 3 3 This is an example of what you can do with XMPP4R. It is a conferencing 4 component in which you can walk around, travel to various places, look 5 at things and talk to other visitors on the same places. If you like 6 Multi-User Dungeons (MUDs) this is for you! 4 tower/{bad,cat,spider}.svg are from Pierre-Marie de Rodat <pmdomine@orange.fr> (JID: pmdomine@im.apinc.org). His images are under GPLv3 (as the rest of this project). 7 5 8 --- 6 tower/rana01_architetto_france_01.svg is from openclipart.org and under Public Domain. 9 7 10 How does it work?11 12 The component loads a few worlds from a few XML files. Each world is a13 component. Once joined the chat will tell you what you can do. Remember14 that you can get a command listing anytime by saying '?'.15 16 Reading 'You can go north, west' you may say 'go north' to go in the17 northern direction and 'go west' to go in the western direction. You'll18 then find yourself at some other place but still in the same MUC19 conference. Your groupchat roster will change as you'll notice different20 people and things that are just in the same place, not other places.21 22 Before starting to hack the scripts you may want to take a look at23 tower.xml. Note that users are <thing/>s, too and the whole world could24 be serialized back to XML by just issuing "world.to_s".25 26 Please note that the code, especially the error handling, is of extreme27 poor quality and was mostly written in one afternoon. If I'm going to28 develop this further everything should be rewritten...29 30 ---31 32 How to try?33 34 Because this is a component you are going to need your own Jabber35 Daemon - which you'll need anyways if you're going to experiment with36 XMPP4R. ;-)37 38 Syntax:39 ./adventure.rb <JID> <Password> <Host>40 41 Example:42 ./adventure.rb mud.example.com geheimnis localhost43 44 ---45 46 Messages seem to have random order?47 48 I don't know any solution for this. One may add short delays between49 messages, but that would only be a very dirty hack.50 51 RFC3920:52 53 "10. Server Rules for Handling XML Stanzas54 55 Compliant server implementations MUST ensure in-order processing of56 XML stanzas between any two entities."57 -
adventure/adventuremuc.py
r121 r131 4 4 from sys import argv 5 5 from world import World 6 from xmpp import Component, JID, NS_COMPONENT_ACCEPT 6 from xmpp import (Component, JID, NS_COMPONENT_ACCEPT, NS_DATA, NS_VCARD, Node, 7 NodeProcessed) 7 8 from xmpp_error import Error 8 9 … … 43 44 self.send(stanza) 44 45 45 def handle_iq(self, _, iq _):46 def handle_iq(self, _, iq): 46 47 """ 47 48 Handle iq stanza 48 49 """ 49 #print "iq: from #{iq.from} type #{iq.type} to #{iq.to}: #{iq.queryns}" 50 if not iq.getQueryNS(): 51 # If not a query, is it a vCard? 52 vcard = iq.getTag('vCard') 50 53 51 #if iq.query.kind_of?(Jabber::Discovery::IqQueryDiscoInfo): 52 # handle_disco_info(iq) 53 # return True 54 #elif iq.query.kind_of?(Jabber::Discovery::IqQueryDiscoItems): 55 # handle_disco_items(iq) 56 # return True 57 #else: 58 # return False 59 pass 54 if vcard: 55 # Resolve world and player involved 56 world_name = iq.getTo().getNode() 57 player_name = iq.getTo().getResource() 58 world = self.worlds[world_name] 59 player = None 60 for thing in (world.players + world.npcs): 61 if thing.name == player_name: 62 player = thing 63 if player_name == 'Bag': 64 player = world.bag 65 # Reply 66 answer = iq.buildReply('result') 67 if iq.getType() == 'get' and player is not None: 68 if player.vcard is not None: 69 answer.addChild(node=player.vcard) 70 else: 71 answer.setTag(NS_VCARD + ' vCard') 72 self.send(answer) 73 raise NodeProcessed 74 75 elif (iq.getQueryNS().endswith('#info') or 76 iq.getQueryNS().endswith('#items')): 77 self.handle_disco_info(iq) 78 raise NodeProcessed 79 80 def handle_disco_info(self, iq): 81 if iq.getType() != 'get' : 82 #answer = iq.buildReply('error') 83 answer = Error(iq, 'bad-request') 84 self.send(answer) 85 return 86 answer = iq.buildReply('result') 87 if not iq.getTo().getNode(): 88 query = answer.getTag('query') 89 if not query: 90 query = answer.setTag('query') 91 query.setTag('identity', attrs = {'category': 'conference', 92 'type': 'text', 93 'name': 'MUD-MUC'}) 94 query.setTag('feature', 95 attrs = {'var': 'http://jabber.org/protocol/muc' }) 96 elif self.worlds.has_key(iq.getTo().getNode()): 97 world_name = iq.getTo().getNode() 98 query = answer.getTag('query') 99 if not query: 100 query = answer.setTag('query') 101 query.setTag('identity', attrs = {'category': 'conference', 102 'type': 'text', 103 'name': world_name}) 104 query.setTag('feature', attrs = 105 {'var': 'http://jabber.org/protocol/muc' }) 106 query.setTag('feature', attrs = {'var': 'muc_public' }) 107 query.setTag('feature', attrs = {'var': 'muc_persistent' }) 108 query.setTag('feature', attrs = {'var': 'muc_open' }) 109 query.setTag('feature', attrs = {'var': 'muc_semianonymous' }) 110 query.setTag('feature', attrs = {'var': 'muc_unmoderated' }) 111 query.setTag('feature', attrs = {'var': 'muc_unsecured' }) 112 data = query.setTag(NS_DATA + ' x', attrs = {'type': 'result'}) 113 field = data.setTag('field', attrs = {'type': 'hidden', 114 'var': 'FORM_TYPE'}) 115 field.addChild(node=Node('value', {}, 116 'http://jabber.org/protocol/muc#roominfo')) 117 118 field = data.setTag('field', 119 attrs = {'label': 'Description', 120 'var': 'muc#roominfo_description'}) 121 field.addChild(node=Node('value', {}, self.worlds[world_name].name)) 122 123 field = data.setTag('field', 124 attrs = {'label': 'Number of occupants', 125 'var': 'muc#roominfo_occupants'}) 126 field.addChild(node=Node('value', {}, 127 str(len(self.worlds[world_name].players)))) 128 self.send(answer) 60 129 61 #def handle_disco_info(iq): 62 # if iq.type != :get : 63 # answer = iq.answer 64 # answer.type = :error 65 # answer.add(Jabber::Error.new('bad-request')) 66 # self.send(answer) if iq.type != :error 67 # return 68 # answer = iq.answer 69 # answer.type = :result 70 # if iq.to.node == nil: 71 # answer.query.add(Jabber::Discovery::Identity.new('conference', 'Adventure component', 'text')) 72 # answer.query.add(Jabber::Discovery::Feature.new(Jabber::Discovery::IqQueryDiscoInfo.new.namespace)) 73 # answer.query.add(Jabber::Discovery::Feature.new(Jabber::Discovery::IqQueryDiscoItems.new.namespace)) 74 # else: 75 # world = self.worlds[iq.to.node] 76 # if world.nil? : 77 # answer.type = :error 78 # answer.query.add(Jabber::Error.new('item-not-found', 'The world you are trying to reach is currently unavailable.')) 79 # else: 80 # answer.query.add(Jabber::Discovery::Identity.new('conference', world.iname, 'text')) 81 # answer.query.add(Jabber::Discovery::Feature.new(Jabber::Discovery::IqQueryDiscoInfo.new.namespace)) 82 # answer.query.add(Jabber::Discovery::Feature.new(Jabber::Discovery::IqQueryDiscoItems.new.namespace)) 83 # answer.query.add(Jabber::Discovery::Feature.new(Jabber::MUC::XMUC.new.namespace)) 84 # answer.query.add(Jabber::Discovery::Feature.new(Jabber::MUC::XMUCUser.new.namespace)) 85 # self.send(answer) 86 87 #def handle_disco_items(iq): 88 # if iq.type != :get : 89 # answer = iq.answer 90 # answer.add(Jabber::Error.new('bad-request')) 91 # self.send(answer) 92 # return 93 # answer = iq.answer 94 # answer.type = :result 95 # if iq.to.node == nil: 96 # self.worlds.each { |node,world| 97 # answer.query.add(Jabber::Discovery::Item.new(Jabber::JID::new(String::new(node), self.component.jid.domain), world.iname)) 98 # } 99 # self.send(answer) 130 def handle_disco_items(self, iq): 131 if iq.getType() != 'get': 132 self.send(answer) 133 return 134 answer = iq.buildReply('result') 135 if not iq.getTo().getNode(): 136 query = answer.getTag('query') 137 if not query: 138 query = answer.setTag('query') 139 for name, i in self.worlds.items(): 140 query.setTag('item', 141 attrs = {'jid': JID(node=i.node, 142 domain=self.jid.getDomain()), 143 'name': name}) 144 self.send(answer) 100 145 101 146 def handle_presence(self, _, pres): … … 103 148 Handle presence stanza 104 149 """ 105 print 'presence: from %s type %s to %s' % ( pres.getFrom(),106 pres.getType(),107 pres.getTo())150 print 'presence: from %s type %s to %s' % (unicode(pres.getFrom()), 151 unicode(pres.getType()), 152 unicode(pres.getTo())) 108 153 109 154 world = pres.getTo().getNode() … … 144 189 MUD = AdventureMUC(argv[1], argv[2], argv[3], int(argv[4])) 145 190 MUD.add_world('cave.xml') 191 MUD.add_world('tower/tower.xml') 146 192 while 1: 147 193 MUD.Process(10) -
adventure/cave.xml
r121 r131 1 <world node='cave' name='A mysterious dark cave' start='cave _1'>1 <world node='cave' name='A mysterious dark cave' start='cave (1)'> 2 2 3 <place name='cave _1'>3 <place name='cave (1)'> 4 4 <description>You are in the dark, but you can hear a little sound from the East, and the wind from the West.</description> 5 <go spec="east" place="cave_2" /> 6 <go spec="west" place="cave_3" /> 5 <go spec="east" place="cave (2)" /> 6 <go spec="west" place="cave (3)" /> 7 8 <npc name="Wizard"> 9 <on-enter> 10 <narration>%self% looks at %actor%...</narration> 11 <say>Hello %actor%! I'm the greatest Wizard in the World! 12 Welcome to this strange place! 13 If you need help, talk to me!</say> 14 </on-enter> 15 16 <presence> 17 <show>available</show> 18 <status>A strange man</status> 19 </presence> 20 21 <commands> 22 <command name="talk to %self%" aliases="I need help"><say>So, you want some help? Type "?"! 23 It will list all the available commands!</say></command> 24 </commands> 25 </npc> 7 26 </place> 8 27 9 <place name="cave _2">28 <place name="cave (2)"> 10 29 <description>There is a strange man, lying on the floor, with a happy face.</description> 11 <go spec="west" place="cave_1" /> 30 <go spec="west" place="cave (1)" /> 31 32 <npc name="Drunk man" aliases='the man,man'> 33 <on-enter> 34 <say>...!</say> 35 </on-enter> 36 37 <commands> 38 <command name="look at %self%"><narration> A drunk man with a bottle </narration></command> 39 <command name="talk to %self%" signal="Bottle_taken"><say> ...! </say></command> 40 <command name="hit %self%"><narration> He didn't move... Don't drink as much as him...</narration></command> 41 </commands> 42 43 <presence> 44 <show>dnd</show> 45 <status>A man lying on the floor</status> 46 </presence> 47 </npc> 48 49 <thing name="bottle" aliases='the bottle'> 50 <commands> 51 <command name="pick up %self%"> 52 <narration> %actor% take the bottle </narration> 53 <signal>Bottle_taken</signal> 54 <give>bottle</give> 55 </command> 56 </commands> 57 </thing> 12 58 </place> 13 59 14 <place name="cave _3">60 <place name="cave (3)"> 15 61 <description>You can see a light ray from the North.</description> 16 <go spec="east" place="cave _1" />17 <go spec="north" place="cave _4" />62 <go spec="east" place="cave (1)" /> 63 <go spec="north" place="cave (4)" /> 18 64 </place> 19 65 20 <place name="cave _4">66 <place name="cave (4)"> 21 67 <description>There is a fire, and a stupid rat... 22 68 No exit, make what you want, but please don't hurt any animal.</description> 23 <go spec="south" place="cave_3" /> 24 </place> 25 26 <thing name="fire" place="cave_4"> 27 <commands> 28 <command name="look at"><narration>A wood fire...</narration></command> 29 </commands> 30 </thing> 31 32 <npc name="Rat" aliases="stupid rat, ugly rat" place="cave_4" respawn="60"> 33 34 <presence> 35 <show>chat</show> 36 <status>Happy Rat</status> 37 </presence> 69 <go spec="south" place="cave (3)" /> 38 70 39 <commands> 40 <command name="look at"><narration>An ugly rat</narration></command> 41 <command name="use %s on" objects="bottle"> 42 <use target="bottle" /> 43 <narration>%actor% use alcool bottle on %self%</narration> 44 <say>Gniii!</say> 45 <narration>%self% run away, and go through the fire. 46 The rat whas ugly, its uglier now, don't watch.</narration> 47 <destroy /> 48 </command> 49 </commands> 50 </npc> 51 52 <npc name="Drunk man" aliases='the man, man' place='cave_2'> 53 <on-enter> 54 <say>...!</say> 55 </on-enter> 56 57 <commands> 58 <command name="look at"><narration> A drunk man with a bottle </narration></command> 59 <command name="talk to"><say> ...! </say></command> 60 <command name="hit"><narration> He didn't move... Don't drink as much as him...</narration></command> 61 </commands> 62 63 <presence> 64 <show>dnd</show> 65 <status>A man lying on the floor</status> 66 </presence> 67 68 <thing inherit="bottle" respawn='20'> 71 <thing name="fire"> 69 72 <commands> 70 <command name="take"> 71 <narration> %actor% take the bottle </narration> 72 <say target="Drunk man"> ...! </say> 73 <give /> 74 </command> 75 <inherit command="look at"/> 73 <command name="look at %self%"><narration>A wood fire...</narration></command> 76 74 </commands> 77 75 </thing> 78 79 </npc> 80 81 <thing name='bottle'/> 76 77 <npc name="Rat" aliases="stupid rat,ugly rat" respawn="60"> 78 79 <presence> 80 <show>chat</show> 81 <status>Happy Rat</status> 82 </presence> 83 84 <commands> 85 <command name="look at %self%"><narration>An ugly rat</narration></command> 86 <command name="use %s on %self%" objects="bottle"> 87 <use target="actor">bottle</use> 88 <narration>%actor% use alcool bottle on %self%</narration> 89 <say>Gniii!</say> 90 <narration>%self% run away, and go through the fire. 91 The rat was ugly, its uglier now, don't watch.</narration> 92 <destroy /> 93 </command> 94 </commands> 95 </npc> 96 </place> 82 97 83 98 </world> -
adventure/place.py
r118 r131 1 1 # -*- coding: utf-8 -*- 2 2 3 # MUDMUC 4 # place.py 5 # Copyright (c) 2008 Thibaut Girka, Anaël Verrier 6 7 # This program is free software; you can redistribute it and/or modify 8 # it under the terms of the GNU General Public License as published by 9 # the Free Software Foundation; version 3 only. 10 11 # This program is distributed in the hope that it will be useful, 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 # GNU General Public License for more details. 15 16 # You should have received a copy of the GNU General Public License 17 # along with this program; if not, write to the Free Software 18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 20 from xmpp import Message, Presence, NS_MUC_USER 21 3 22 class Place: 4 def __init__(self, name, description): 23 def __init__(self, world, name, description): 24 self.world = world 5 25 self.name = name 6 26 self.description = description 7 self.visitors = list() 8 self.participants = list() 9 self.objects = list() 10 self.respawn = dict() # {'object name': timestamp_to_respawn, ...} 11 self.exits = dict() # {'name': 'place_name', ...} 27 28 self.visitors = list() # Players ; Role: visitors 29 self.npcs = list() # Role: Participants 30 31 self.things = list() # List of Things 32 self.exits = dict() # { spec: place_name } 33 34 def player_leaved(self, player): 35 """ 36 Broadcast unavaibility of Player or NonPlayableCharacter, 37 Trigger on-leave events and remove 38 Player/NonPlayableCharacter from the lists. 39 """ 40 41 if player.place: 42 message = '/me leaves %s going to %s' % (self.name, 43 player.place.name) 44 else: 45 message = '/me disintegrates' 46 47 if player.is_npc: 48 player.trigger_event('on-self-leave') 49 self.npcs.remove(player) 50 else: 51 self.visitors.remove(player) 52 53 # Send players and npcs unaivaibility to player 54 for npc in self.npcs: 55 pres = Presence(node=npc.presence) 56 pres.setType('unavailable') 57 player.send_presence(npc.name, pres) 58 for visitor in self.visitors: 59 if visitor is not player: 60 pres = visitor.fix_presence() 61 pres.setType('unavailable') 62 player.send_presence(visitor.name, pres) 63 64 # Broadcast player's unavailibility to all players in the room 65 self.broadcast_message(player.name, message) 66 self.broadcast_status(player, 'unavailable') 67 68 # Trigger on-leave events 69 self.trigger_event('on-leave', player) 70 71 def player_entered(self, player, old_place=None): 72 """ 73 Broadcast avaibility of Player or NonPlayableCharacter, 74 Trigger on-enter events, append 75 Player/NonPlayableCharacter to the lists and send description 76 of the room to the Player. 77 """ 78 79 if old_place: 80 message = '/me enters in %s coming from %s' % (self.name, 81 old_place.name) 82 else: 83 message = '/me spawns' 84 85 # Broadcast player's availibility to all players in the room 86 self.broadcast_status(player) 87 self.broadcast_message(player.name, message) 88 89 if player.is_npc: 90 self.npcs.append(player) 91 player.trigger_event('on-self-enter') 92 else: 93 self.visitors.append(player) 94 # Send description of the room 95 player.send_message(None, 96 '\n******\n\n\n\n**********\n' 97 'Entering %s\n**********' 98 '\n\n%s\n' % (self.name.capitalize(), 99 self.description), 100 '%s: %s' % (self.world.name, 101 self.name.capitalize())) 102 103 # Send players and npcs avaibility to player 104 for npc in self.npcs: 105 player.send_presence(npc.name, npc.presence) 106 for visitor in self.visitors: 107 pres = visitor.fix_presence() 108 player.send_presence(visitor.name, pres) 109 110 # Trigger on-enter events 111 self.trigger_event('on-enter', player) 112 113 def trigger_event(self, event_type, actor): 114 """ Trigger event of type event_type for all things and npcs """ 115 for npc in self.npcs: 116 if actor is not npc: 117 npc.trigger_event(event_type, actor) 118 for thing in self.things: 119 thing.trigger_event(event_type, actor) 120 121 def broadcast_status(self, player, status=None): 122 """ Broadcast a presence stanza in the room """ 123 presence = player.fix_presence() 124 if status is not None: 125 presence.setType(status) 126 for visitor in self.visitors: 127 visitor.send_presence(player.name, presence) 128 129 def broadcast_message(self, from_, message): 130 """ Broadcast a message in all visitors in the room """ 131 for visitor in self.visitors: 132 visitor.send_message(from_, message) 133 -
adventure/player.py
r121 r131 1 1 # -*- coding: utf-8 -*- 2 2 3 # MUDMUC 4 # player.py 5 # Copyright (c) 2008 Thibaut GIRKA, Anaël Verrier 6 7 # This program is free software; you can redistribute it and/or modify 8 # it under the terms of the GNU General Public License as published by 9 # the Free Software Foundation; version 3 only. 10 11 # This program is distributed in the hope that it will be useful, 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 # GNU General Public License for more details. 15 16 # You should have received a copy of the GNU General Public License 17 # along with this program; if not, write to the Free Software 18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 20 from os.path import isfile, isabs, join as path_join 21 from base64 import encodestring 22 from mimetypes import guess_type as guess_mimetype 23 from sha import sha 24 3 25 from thing import Thing 4 from xmpp import Message 26 from xmpp import Message, Presence, NS_MUC_USER, NS_VCARD, Node 5 27 6 class Player( Thing):28 class Player(object): 7 29 def __init__(self, world, name, jid): 8 Thing.__init__(self, world, name, None) 30 self.world = world 31 self.name = name 9 32 self.jid = unicode(jid) 10 33 self.place = None 11 12 def see(self, place): 13 if not place: 14 return 34 self.presence = None 35 36 self.vcard = None 37 38 self.is_npc = False 39 40 self.inventory = list() 41 42 def set_vcard(self, image=None, birthday=None): 43 vcard = Node(NS_VCARD + ' vCard') 44 vcard.addChild(node=Node('NICKNAME', {}, self.name)) 45 if image: 46 if not isabs(image): 47 image = path_join(self.world.data_dir, image) 48 if isfile(image): 49 file = open(image, 'rb') 50 data = file.read() 51 encoded_data = encodestring(data) 52 mime = guess_mimetype(image)[0] 53 file.close() 54 photo = vcard.setTag('PHOTO') 55 photo.addChild(node=Node('TYPE', {}, mime)) 56 photo.addChild(node=Node('BINVAL', {}, encoded_data)) 57 58 vcard_pres = self.presence.getTag(NS_VCARD + ':x:update x') 59 if not vcard_pres: 60 vcard_pres = self.presence.setTag(NS_VCARD + ':x:update x') 61 vcard_pres.addChild(node=Node('photo', {}, 62 sha(data).hexdigest())) 63 64 if birthday: 65 vcard.addChild(node=Node('BDAY', {}, birthday)) 66 #TODO: DESC 67 self.vcard = vcard 15 68 16 for line in place.description.split('\n'): 17 self.send_message(None, line.strip()) 18 19 self.send_message(None, ' ') 20 self.send_message(None, 'You can go %s' % ', '.join(place.exits.keys())) 21 22 def send_message(self, fromresource, text, subject=None): 69 def send_message(self, fromresource, text, subject=None, 70 msg_type='groupchat'): 71 """ Send message to the player """ 72 # New message stanza to the player 23 73 msg = Message(self.jid, text) 24 msg.setType( 'groupchat')74 msg.setType(msg_type) 25 75 if subject: 26 76 msg.setSubject(subject) 27 77 self.world.send(fromresource, msg) 78 79 def send_presence(self, fromresource, presence): 80 """ Send presence stanza to the player """ 81 pres = Presence(node=presence) 82 pres.setTo(self.jid) 83 self.world.send(fromresource, pres) 84 85 def say(self, text, target=None): 86 """ Make the player say something. If target is None, broadcast """ 87 if target is None: 88 self.place.broadcast_message(self.name, text) 89 else: 90 target.send_message(self.name, text) 91 92 def move_to(self, to_, warning=True): 93 """ Move the player to its new place """ 94 oldplace = self.place 95 self.place = to_ 96 if self.place: 97 self.place.player_entered(self, oldplace) 98 elif warning: 99 self.send_message(None, 100 'Sorry! The room isn\'t available!\n' 101 'It\'s a bug for sure ' 102 '(either in the data ' 103 'either in the engine) :/') 104 if oldplace is not None: 105 oldplace.player_leaved(self) 106 107 def fix_presence(self, role=None, affiliation=None): 108 """ 109 Fix presence: Remove double presence tag and set 110 the specified role and affiliation 111 """ 112 if role: 113 if self.is_npc: 114 role = 'participant' 115 affiliation = 'member' 116 else: 117 role = 'visitor' 118 affiliation = 'none' 119 pres = Presence(node=self.presence) 120 tag_x = pres.getTag(NS_MUC_USER + ' x') 121 if not tag_x: 122 tag_x = pres.setTag(NS_MUC_USER + ' x') 123 tag_item = tag_x.getTag(NS_MUC_USER + ' item') 124 if not tag_item: 125 tag_item = tag_x.setTag(NS_MUC_USER + ' item') 126 tag_item.setAttr('affiliation', affiliation) 127 tag_item.setAttr('role', role) 128 return pres 28 129 29 def on_enter(self, thing, from_):30 if thing != self:31 if from_:32 self.send_message(None, '%s enters %s coming from %s' %33 (thing.name, self.place, from_))34 else:35 self.send_message(None, '%s spawns' % thing.name)36 130 37 def on_leave(self, thing, to_): 38 if thing != self: 39 if to_: 40 self.send_message(None, '%s leaves %s going to %s' % 41 (thing.name, self.place, to_)) 42 else: 43 self.send_message(None, '%s disintegrates' % thing.name) 131 class NonPlayableCharacter(Player, Thing): 132 def __init__(self, world, name, jid, place): 133 Player.__init__(self, world, name, jid) 134 Thing.__init__(self, world, name, place) 135 136 self.on_self_enter = list() 137 self.on_self_leave = list() 138 139 self.is_npc = True 140 44 141 -
adventure/thing.py
r121 r131 1 1 # -*- coding: utf-8 -*- 2 2 3 #from player import Player 4 from xmpp import Presence 3 # MUDMUC 4 # thing.py 5 # Copyright (c) 2008 Thibaut Girka, Anaël Verrier 6 7 # This program is free software; you can redistribute it and/or modify 8 # it under the terms of the GNU General Public License as published by 9 # the Free Software Foundation; version 3 only. 10 11 # This program is distributed in the hope that it will be useful, 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 # GNU General Public License for more details. 15 16 # You should have received a copy of the GNU General Public License 17 # along with this program; if not, write to the Free Software 18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 5 19 6 20 class Thing: 7 def __init__(self, world, name, p arent):21 def __init__(self, world, name, place): 8 22 self.world = world 9 23 self.name = name 10 self.parent = parent 11 self.aliases = (name,) # Noms alternatifs 12 self.actions = list() 13 self.presences = None 14 self.uid = 0 15 self.place = None 16 self.enters = list() 17 self.leaves = list() 18 self.jid = None # useful ? 19 20 def presence(self): 21 pres = None 22 #for pres in each_element('presence'): 23 # pres = Presence(node=pres) 24 #if isinstance(self, Player): 25 # pres.add(Jabber::MUC::XMUCUser.new).add(Jabber::MUC::XMUCUserItem.new('none', 'participant')) 26 #else: 27 # pres.add(Jabber::MUC::XMUCUser.new).add(Jabber::MUC::XMUCUserItem.new('owner', 'moderator')) 28 return pres 29 30 def see(self, place): 31 pass 32 33 def send_message(self, fromresource, text, subject=None): 34 pass 35 36 def send_message_to_place(self, fromresource, text): 37 for thing in self.world.each_element('thing'): 38 if thing.place == self.place: 39 thing.send_message(fromresource, text) 40 41 def on_enter(self, thing, from_): 42 for command in self.enters: 43 self.command(thing, command, [from_]) 44 45 def on_leave(self, thing, to_): 46 for command in self.leaves: 47 self.command(thing, command, [to_]) 48 49 def command(self, source, command, arguments): 50 if command.action[1]: 51 text = command.action[1] 52 else: 53 text = '' 54 target = None 55 if command.action[2]: 56 for thing in self.world.each_thing_by_place(self.place): 57 if thing.name == command.action[2]: 58 target = thing 24 self.place = place 25 self.aliases = [name,] # Alternative names 26 self.commands = list() # List of dicts : name, actions : list() 27 self.on_enters = list() 28 self.on_leaves = list() 29 self.uid = 0 # Useful? 30 31 def add_aliases(self, aliases_obj, alias_str): 32 """ Safely add aliases """ 33 if not (isinstance(alias_str, str) or isinstance(alias_str, unicode)): 34 return False 35 if alias_str: 36 aliases = alias_str.split(',') 37 for alias in aliases: 38 if alias: 39 aliases_obj.append(alias.strip()) 40 41 def parse_commands(self, command_elements): 42 """ Parse commands and store them in self.commands """ 43 for command in command_elements: 44 if command.getAttribute('objects'): 45 objects = command.getAttribute('objects').split(',') 46 required_objects = tuple(objects) 47 else: 48 required_objects = tuple() 49 command_obj = dict() 50 command_obj['name'] = command.getAttribute('name').replace('%self%', '<<self>>') 51 command_obj['name'] = command_obj['name'] % required_objects 52 command_obj['aliases'] = list() 53 command_obj['aliases'].append(command_obj['name']) 54 command_obj['actions'] = list() 55 command_obj['signal'] = command.getAttribute('signal').strip() 56 command_obj['objects'] = list(required_objects) 57 58 aliases = command.getAttribute('aliases') 59 self.add_aliases(command_obj['aliases'], aliases) 60 61 action_elements = command.getElementsByTagName('*') 62 for i in action_elements: 63 self.parse_action(command_obj['actions'], i) 64 self.commands.append(command_obj) 65 66 def parse_action(self, action_list, action_element): 67 """ 68 Parse action_element (xml.dom.minidom.Element) 69 and append each action in action_list 70 """ 71 action_obj = dict() 72 action_obj['name'] = action_element.localName 73 action_obj['target'] = action_element.getAttribute('target') 74 if action_element.firstChild and action_element.firstChild.data: 75 action_obj['params'] = action_element.firstChild.data.strip() 76 action_list.append(action_obj) 77 78 def do_action(self, action, player=None): 79 """ Execute a stored action """ 80 # Resolve target: None, self, player, or a Player instance 81 target = False 82 if action['target'] == 'actor': 83 target = player 84 elif action['target'] == 'self': 85 target = self 86 elif action['target']: 87 target = None 88 for i in (self.place.visitors + self.place.npcs): 89 if action['target'] == i.name: 90 target = i 59 91 break 60 #else: 61 if not target: 62 target = self 63 text.replace('%self%', target.name) 64 text.replace('%actor%', source.name) 65 text.replace('%place%', self.place) 66 if command.action[0] == 'say' or command.action[0] == 'narration': 67 sender = None 68 if command.action[0] == 'say': 69 sender = self.name 70 if command.action[3] == 'all': 71 self.send_message_to_place(sender, text) 92 93 # Parse parameters, replace %self% and %actor% 94 params = '' 95 if action.has_key('params'): 96 params = action['params'] 97 params = params.replace('%self%', self.name) 98 if player: 99 params = params.replace('%actor%', player.name) 100 101 # Execute actions 102 # <say [target="actor|self|name"]>text</say> 103 if action['name'] == 'say': 104 if target: 105 self.say(params, target) 72 106 else: 73 source.send_message(sender, text) 74 75 76 77 78 class Action: 79 def __init__(self, name, parent): 80 self.name = name 81 self.parent = parent 82 self.objects = list() # objets requis 83 # Expressions... Un élément si pas de "%" dans la chaîne "name", sinon : 84 # un élément par alias ( ex "use bottle on", "use whiskey on" ) 85 self.expressions = list() 86 87 # Élément XML à traiter si les objets sont dans l'inventaire 88 #self.xmlelement = None 89 90 # (("say","coucou",None,None),("destroy",None)) par exemple 91 # à respecter : premier élément : nom de la commande, 92 # deuxième : texte, troisième : target 93 self.action = list() 107 self.say(params) 108 109 # <narration>text</narration> 110 elif action['name'] == 'narration': 111 self.place.broadcast_message(None, params) 112 113 # <follow [target="actor|self|name"] /> 114 elif action['name'] == 'follow': 115 if target.place is not None and not target.is_npc: 116 forbidden_places = params.split(',') 117 for i in xrange(len(forbidden_places)): 118 forbidden_places[i] = forbidden_places[i].strip() 119 if not target.place.name in forbidden_places: 120 self.say('/me follows ' + target.name) 121 self.move_to(target.place) 122 123 # <signal [target="name"]>signal</signal> 124 elif action['name'] == 'signal': 125 if target is False: 126 for i in (self.place.npcs + self.place.things): 127 i.trigger_signal(params, player) 128 elif target: 129 target.trigger_signal(params, player) 130 elif ':Failed' not in params: 131 for i in (self.place.npcs + self.place.things): 132 i.trigger_signal(params+':Failed', player) 133 134 # <move [target="actor|self|name"]>place</move> 135 elif action['name'] == 'move': 136 if not target: 137 target = self 138 if not params or params == target.place.name: 139 for name, place in target.place.exits.items(): 140 target.move_to(self.world.place(place)) 141 break 142 else: 143 target.move_to(target.world.place(params)) 144 145 # <add-exit>spec: place</add-exit> 146 elif action['name'] == 'add-exit': 147 (spec, place) = params.split(':') 148 spec = spec.strip() 149 place = place.strip() 150 self.place.exits[spec] = place 151 152 # <del-exit>spec</del-exit> 153 elif action['name'] == 'del-exit': 154 if self.place.exits.has_key(params): 155 del self.place.exits[params] 156 # <give target="target">object</give> 157 elif action['name'] == 'give': 158 if not target: 159 target = player 160 if target: 161 target.inventory.append(params) 162 # <use target="target">object</give> 163 elif action['name'] == 'use': 164 if not target: 165 target = player 166 if target: 167 target.inventory.remove(params) 168 169 def trigger_event(self, event, actor=None): 170 """ Trigger events if handled by the NPC""" 171 if event == 'on-enter': 172 for on_enter in self.on_enters: 173 if (not on_enter['target'] or 174 on_enter['target'].lower() == actor.name.lower()): 175 for action in on_enter['actions']: 176 self.do_action(action, actor) 177 178 if event == 'on-leave': 179 for on_leave in self.on_leaves: 180 if (not on_leave['target'] or 181 on_leave['target'].lower() == actor.name.lower()): 182 for action in on_leave['actions']: 183 self.do_action(action, actor) 184 185 if event == 'on-self-enter': 186 for i in self.on_self_enter: 187 self.do_action(i, actor) 188 189 if event == 'on-self-leave': 190 for i in self.on_self_leave: 191 self.do_action(i, actor) 192 193 def trigger_signal(self, signal, actor=None): 194 """ Trigger a signal if handled by the NPC """ 195 for command in self.commands: 196 if signal.strip() == command['signal'].strip(): 197 for action in command['actions']: 198 self.do_action(action, actor) 199 200 def handle_command(self, text, player): 201 """ 202 Test all combinations with all aliasies. 203 If command is handled, execute actions and return True. 204 Else, return False. 205 """ 206 for alias in self.aliases: 207 for command_obj in self.commands: 208 for command in command_obj['aliases']: 209 command_txt = command 210 command_txt = command_txt.replace('<<self>>', alias) 211 command_txt = command_txt.lower() 212 if command_txt and command_txt in text.lower().strip(): 213 has_required = True 214 for i in command_obj['objects']: 215 if i not in player.inventory: 216 player.send_message('Help!', 217 'You don\'t have a %s' % i) 218 has_required = False 219 break 220 if has_required: 221 player.say('/me %s' % command_obj['name'].replace( 222 '<<self>>', self.name)) 223 for action in command_obj['actions']: 224 self.do_action(action, player) 225 return True 226 return False -
adventure/world.py
r121 r131 1 1 # -*- coding: utf-8 -*- 2 2 3 # MUDMUC 4 # world.py 5 # Copyright (c) 2008 Thibaut Girka, Anaël Verrier 6 7 # This program is free software; you can redistribute it and/or modify 8 # it under the terms of the GNU General Public License as published by 9 # the Free Software Foundation; version 3 only. 10 11 # This program is distributed in the hope that it will be useful, 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 # GNU General Public License for more details. 15 16 # You should have received a copy of the GNU General Public License 17 # along with this program; if not, write to the Free Software 18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 20 from os.path import dirname, abspath 21 from xml.dom.minidom import parse as xml_parse 22 23 from xmpp import Message, Presence, NS_MUC_USER, JID 24 from xmpp_error import Error 25 3 26 from place import Place 4 from player import Player 5 from xml.dom.minidom import parse as xml_parse 6 from xmpp import Message, Presence 7 from xmpp_error import Error 27 from player import Player, NonPlayableCharacter 28 from thing import Thing 8 29 9 30 class World: 31 """ World class. 32 Worlds have their own JID and correspond to an xml file """ 10 33 def __init__(self, muc, filename): 34 """ Init a world. 35 muc is the MUC component handling messages and presences. 36 filename is the world's filename. """ 11 37 self.muc = muc 12 self.places = dict() # {'place_name': place, ...} 13 self.things = dict() 38 self.places = dict() # { place_name: Place() } 39 self.players = list() 40 self.things = list() 41 self.npcs = list() 42 43 self.data_dir = dirname(abspath(filename)) 44 45 self.bag = Player(self, 'Bag', 'Bag') 46 self.bag.presence = Presence() 47 self.bag.presence.setShow('available') 48 self.bag.presence.setStatus('A bag (your inventory)') 49 self.bag.presence = self.bag.fix_presence('moderator', 'owner') 50 self.bag.set_vcard('bag.png') 14 51 15 52 doc = xml_parse(filename) … … 20 57 get_elements = doc.documentElement.getElementsByTagName 21 58 places = get_elements('place') 59 60 # Parsing places 22 61 for place in places: 23 place_obj = Place( 62 place_obj = Place(self, 24 63 place.getAttribute('name'), 25 64 place.getElementsByTagName('description')[0].firstChild.data) 26 65 self.places[place_obj.name] = place_obj 66 67 # Parsing exits of the place 27 68 gos = place.getElementsByTagName('go') 28 69 for go in gos: 29 70 place_obj.exits[ 30 71 go.getAttribute('spec')] = go.getAttribute('place') 31 #things = get_elements('thing') 32 #for thing in things: 72 73 # Parsing NPCs in the place 74 npcs = place.getElementsByTagName('npc') 75 for npc in npcs: 76 npc_obj = NonPlayableCharacter(self, 77 npc.getAttribute('name'), 78 npc.getAttribute('name'), 79 place_obj) 80 npc_obj.add_aliases(npc_obj.aliases, 81 npc.getAttribute('aliases')) 82 83 # Parsing NPC presence 84 pres = Presence() 85 presences = npc.getElementsByTagName('presence') 86 image = None 87 if len(presences): 88 # Set up NPC status 89 shows = presences[0].getElementsByTagName('show') 90 if len(shows): 91 pres.setShow(shows[0].firstChild.data) 92 status = presences[0].getElementsByTagName('status') 93 if len(status): 94 pres.setStatus(status[0].firstChild.data) 95 images = presences[0].getElementsByTagName('image') 96 if len(images): 97 image = images[0].firstChild.data 98 npc_obj.presence = pres 99 npc_obj.presence = npc_obj.fix_presence() 100 npc_obj.set_vcard(image) 101 102 # Add NPC to the World and to the Place 103 place_obj.npcs.append(npc_obj) 104 self.npcs.append(npc_obj) 105 106 # Parse NPC's commands 107 commands = npc.getElementsByTagName('commands') 108 if len(commands): 109 commands = commands[0].getElementsByTagName('command') 110 npc_obj.parse_commands(commands) 111 112 # Set up NPC's on-enter event 113 on_enters = npc.getElementsByTagName('on-enter') 114 for on_enter in on_enters: 115 actions = on_enter.getElementsByTagName('*') 116 on_enter_obj = dict() 117 on_enter_obj['target'] = on_enter.getAttribute('target').strip() 118 on_enter_obj['actions'] = list() 119 npc_obj.on_enters.append(on_enter_obj) 120 for action in actions: 121 npc_obj.parse_action(on_enter_obj['actions'], action) 122 123 # Set up NPC's on-enter event 124 on_leaves = npc.getElementsByTagName('on-leave') 125 for on_leave in on_leaves: 126 actions = on_leave.getElementsByTagName('*') 127 on_leave_obj = dict() 128 on_leave_obj['target'] = on_leave.getAttribute('target').strip() 129 on_leave_obj['actions'] = list() 130 npc_obj.on_leaves.append(on_leave_obj) 131 for action in actions: 132 npc_obj.parse_action(on_leave_obj['actions'], action) 133 134 # Set up NPC's on-self-enter event 135 on_self_enters = npc.getElementsByTagName('on-self-enter') 136 if len(on_self_enters): 137 actions = on_self_enters[0].getElementsByTagName('*') 138 for action in actions: 139 npc_obj.parse_action(npc_obj.on_self_enter, action) 140 141 # Set up NPC's on-self-leave event 142 on_self_leave = npc.getElementsByTagName('on-self-leave') 143 if len(on_self_leave): 144 actions = on_self_leave[0].getElementsByTagName('*') 145 for action in actions: 146 npc_obj.parse_action(npc_obj.on_self_leave, action) 147 148 # Parsing Things in the place 149 things = place.getElementsByTagName('thing') 150 for thing in things: 151 thing_obj = Thing(self, thing.getAttribute('name'), place_obj) 152 thing_obj.add_aliases(thing_obj.aliases, thing.getAttribute('aliases')) 153 154 # Add Thing to the World and to the Place 155 place_obj.things.append(thing_obj) 156 self.things.append(thing_obj) 157 158 # Parse Thing's commands 159 commands = thing.getElementsByTagName('commands') 160 if len(commands): 161 commands = commands[0].getElementsByTagName('command') 162 thing_obj.parse_commands(commands) 163 164 # Set up Thing's on-enter event 165 on_enters = thing.getElementsByTagName('on-enter') 166 for on_enter in on_enters: 167 actions = on_enter.getElementsByTagName('*') 168 on_enter_obj = dict() 169 on_enter_obj['target'] = on_enter.getAttribute('target').strip() 170 on_enter_obj['actions'] = list() 171 thing_obj.on_enters.append(on_enter_obj) 172 for action in actions: 173 thing_obj.parse_action(on_enter_obj['actions'], action) 174 175 # Set up Thing's on-enter event 176 on_leaves = thing.getElementsByTagName('on-leave') 177 for on_leave in on_leaves: 178 actions = on_leave.getElementsByTagName('*') 179 on_leave_obj = dict() 180 on_leave_obj['target'] = on_leave.getAttribute('target').strip() 181 on_leave_obj['actions'] = list() 182 thing_obj.on_leaves.append(on_leave_obj) 183 for action in actions: 184 thing_obj.parse_action(on_leave_obj['actions'], action) 33 185 34 186 35 187 36 188 def send(self, resource, stanza): 189 """ Send stanza through the room """ 37 190 # Avoid sending to things without JID 38 191 if stanza.getTo(): … … 40 193 41 194 def place(self, placename): 195 """ Return the Place whose name is placename """ 42 196 if self.places.has_key(placename): 43 197 return self.places[placename] 44 198 return None 45 199 46 def each_thing_by_place(self, placename): 47 if placename: 48 for thing in self.things.values(): 49 if thing.place == placename: 50 yield(thing) 51 52 def move_thing(self, thing, newplace): 53 for t in self.each_thing_by_place(thing.place): 54 # Call leave hooks 55 t.on_leave(thing, newplace) 56 57 # Broadcast unavailability presence to leaver 58 if t.presence: 59 pres = Presence(node=t.presence) 60 pres.setType('unavailable') 61 pres.setTo(thing.jid) 62 if t.jid != thing.jid: 63 self.send(t.name, pres) 64 65 # Broadcast unavailability presence to all who are here 66 if thing.presence: 67 pres = Presence(node=thing.presence) 68 pres.setType('unavailable') 69 pres.setTo(t.jid) 70 if thing.jid != t.jid: 71 self.send(thing.name, pres) 72 73 # Enter new place 74 oldplace = thing.place 75 thing.place = newplace 76 77 for t in self.each_thing_by_place(thing.place): 78 # Broadcast availability presence to enterer 79 if t.presence: 80 pres = Presence(node=t.presence) 81 pres.setTo(thing.jid) 82 self.send(t.name, pres) 83 84 # Broadcast availability presence to all who are here 85 if thing.presence: 86 pres = Presence(node=thing.presence) 87 pres.setTo(t.jid) 88 self.send(thing.name, pres) 89 90 thing.send_message(None, ' ') 91 if newplace: 92 subject = newplace.capitalize() 93 else: 94 subject = ' ' 95 thing.send_message(None, 'Entering %s' % newplace, subject) 96 thing.send_message(None, ' ') 97 thing.see(self.place(newplace)) 98 99 for t in self.each_thing_by_place(thing.place): 100 # Call enter hooks 101 t.on_enter(thing, oldplace) 200 def move_thing(self, thing, newplace, warning=True): 201 """ Move thing thing to its new place """ 202 thing.move_to(self.place(newplace), warning) 102 203 103 204 def handle_presence(self, pres): 205 """ Handle presence stanzas coming to the room """ 104 206 # A help for the irritated first: 105 207 if pres.getType() == 'subscribe': … … 115 217 # Look if player is already known 116 218 player = None 117 for thing in self.things.values(): 118 if (isinstance(thing, Player) and 119 pres.getTo().getResource() == thing.name): 219 for thing in self.players: 220 if pres.getTo().getResource() == thing.name: 120 221 player = thing 121 222 122 223 # Disallow nick changes 123 if (isinstance(thing, Player) and pres.getFrom() == thing.jid and 124 player != thing): 224 if (pres.getFrom() == thing.jid and player != thing): 125 225 answer = Error(pres, 'not-acceptable', 126 'Nickchange not allowed') #False226 'Nickchange not allowed') 127 227 self.send(thing.name, answer) 128 228 return True 129 229 130 230 # Either nick-collission or empty nick 131 231 if (player and pres.getFrom() != player.jid or … … 138 238 self.send(None, answer) 139 239 return True 240 241 # Disallow nick "Bag", since it's used internally by the MUD 242 if pres.getTo().getResource() == 'Bag': 243 answer = Error(pres, 'not-acceptable', 'Nickname already used by a non-playbable character') 244 self.send(None, answer) 245 return True 246 247 # Nick-collision with a NPC 248 for npc in self.npcs: 249 if npc.name == pres.getTo().getResource(): 250 answer = Error(pres, 'not-acceptable', 'Nickname already used by a non-playbable character') 251 self.send(None, answer) 252 return True 140 253 141 254 # Add the valid player … … 143 256 player = Player(self, pres.getTo().getResource(), pres.getFrom()) 144 257 player.presence = pres 145 self. things[player.name] = player258 self.players.append(player) 146 259 self.move_thing(player, self.start) 147 player.send_message('Help!', 'Send "?" to get a list of available ' 148 'commands any time.') 260 261 # Send the bag presence to the player: 262 player.send_presence('Bag', self.bag.presence) 149 263 # Or broadcast updated presence 150 264 else: 151 265 player.presence = pres 152 153 for t in self.each_thing_by_place(player.place): 154 # Broadcast presence to all who are here 155 if isinstance(t, Player): 156 pres = Presence(node=player.presence) 157 pres.setTo(t.jid) 158 self.send(player.name, pres) 266 267 player.place.broadcast_status(player) 159 268 160 269 # Remove the player instantly 161 270 if pres.getType() in ('error', 'unavailable'): 162 self.move_thing(player, None )163 del self.things[player.name]271 self.move_thing(player, None, warning=False) 272 self.players.remove(player) 164 273 165 274 def handle_message(self, msg): 275 """ Handle message stanzas coming to the room """ 166 276 player = None 167 for thing in self.things.values(): 168 if (isinstance(thing, Player) and not msg.getTo().getResource() and 169 msg.getFrom() == thing.jid): 277 278 # Search the player by its JID 279 for thing in self.players: 280 if msg.getFrom() == thing.jid: 170 281 player = thing 171 282 283 # If the player were not found, send an answer to the message 172 284 if not player: 173 285 answer = Error(msg, 'forbidden') 174 286 self.send(msg.getTo().getResource(), answer) 175 287 return True 176 288 elif msg.getTo() == JID(node=self.node, 289 domain=self.muc.jid.getDomain()): 290 tag_x = msg.getTag('x') 291 if tag_x: 292 tag_invite = tag_x.getTag('invite') 293 # Is the message an invitation? 294 if tag_invite: 295 self.send_invitation(msg.getTo(), 296 tag_invite.getAttr('to')) 297 elif msg.getTo().getResource() == 'Bag': 298 #TODO: Invetory handling 299 self.bag_command(player, msg.getBody()) 300 return True 301 302 if msg.getTo().getResource(): 303 for visitor in player.place.visitors: 304 if visitor.name == msg.getTo().getResource(): 305 visitor.send_message(player.name, msg.getBody(), 306 msg_type='normal') 307 return True 308 for npc in player.place.npcs: 309 if npc.name == msg.getTo().getResource(): 310 player.send_message(npc.name, 311 'Sorry, I\'m a NPC, you can\'t send private messages to me.', 312 msg_type='normal') 313 return True 314 # Parse the content. If not handled, broadcast it. 177 315 if not self.command(player, msg.getBody()): 178 for thing in self.each_thing_by_place(player.place): 179 thing.send_message(player.name, msg.getBody()) 316 player.say(msg.getBody()) 317 318 def send_invitation(self, from_, to_): 319 """ Send an invitation to jid to_ from jid from_ """ 320 msg = Message(to_) 321 msg.setFrom(JID(node=self.node, domain=self.muc.jid.getDomain())) 322 msg_x = msg.setTag(NS_MUC_USER + ' x') 323 msg_invite = msg_x.setTag(NS_MUC_USER + ' invite') 324 msg_invite.setAttr('from', from_) 325 msg_reason = msg_invite.setTag(NS_MUC_USER + ' reason') 326 msg_reason.setData('Join the super MUDMUC!(World %s)' % self.name) 327 self.send(None, msg) 328 329 def bag_command(self, player, text): 330 """ Try to parse commands or return False if not handled """ 331 if text in ('list', 'ls'): 332 333 temp_items = list() 334 temp_numbers = list() 335 for item in player.inventory: 336 if item in temp_items: 337 temp_numbers[temp_items.index(item)] += 1 338 else: 339 temp_items.append(item) 340 temp_numbers.append(1) 341 if not len(temp_items): 342 player.send_message('Bag', 'Your bag is empty!', 343 msg_type='normal') 344 elif len(temp_items) == 1: 345 player.send_message('Bag', 346 'Your bag contains: %s (x%d)' 347 % (temp_items[0], temp_numbers[0]), 348 msg_type='normal') 349 else: 350 answer_text = 'Your bag contains: ' 351 for item_i in xrange(len(temp_items)-1): 352 answer_text += '%s (x%d), ' % (temp_items[item_i], 353 temp_numbers[item_i]) 354 answer_text += 'and %s (x%d)' % (temp_items[len(temp_items)-1], temp_numbers[len(temp_numbers)-1]) 355 player.send_message('Bag', answer_text, msg_type='normal') 356 else: 357 player.send_message('Bag', 'I don\'t want to chat. If you want to use me, type ls or list', msg_type='normal') 180 358 181 359 def command(self, player, text): 182 if text == '?': 360 """ Try to parse commands or return False if not handled """ 361 if text in ('?', '!help'): 183 362 player.send_message('Help!', '(Command) who') 184 place = self.place(player.place)363 place = player.place 185 364 if place: 186 365 for exit_name in place.exits.keys(): 187 366 player.send_message('Help!', '(Command) go %s' % exit_name) 188 for thing in self.each_thing_by_place(player.place): 189 for c in thing.actions: 190 player.send_message('Help!', 191 '(Command) %s %s' % (c.expressions[0], 192 thing.name)) 193 return True 194 else: 367 for thing in (player.place.npcs + player.place.things): 368 for c in thing.commands: 369 if len(c['name']) > 3: 370 has_required = True 371 for i in c['objects']: 372 if i not in player.inventory: 373 has_required = False 374 break 375 if has_required: 376 player.send_message('Help!', 377 '(Command) %s' 378 % (c['name'].replace('<<self>>', 379 thing.name))) 380 return True 381 elif text: 195 382 words = text.split(' ') 196 383 cmd = words.pop(0) … … 200 387 what = '' 201 388 if cmd == 'go': 202 print what 203 oldplace = self.place(player.place) 389 oldplace = player.place 204 390 newplace = None 205 391 206 392 for name, place in oldplace.exits.items(): 207 if name == what:393 if name.lower() == what.lower(): 208 394 newplace = place 209 395 … … 213 399 player.send_message(None, 'You cannot go there') 214 400 return True 401 elif cmd == 'inventory': 402 self.bag_command(player, 'ls') 403 return True 215 404 elif cmd == 'who': 216 405 player.send_message(None, 'Players in "%s":' % self.name) 217 for thing in self.things.values(): 218 if isinstance(thing, Player): 219 player.send_message(None, 220 '%s is at/in %s' % (thing.name, 221 thing.place)) 406 for thing in self.players: 407 player.send_message(None, 408 ' - %s is at/in %s' % (thing.name, 409 thing.place.name)) 222 410 return True 223 411 else: 224 412 handled = False 225 for thing in self.each_thing_by_place(player.place): 226 if what.lower() in thing.aliases: 227 for action in thing.actions: 228 for c in action.expressions: 229 if c == cmd: 230 thing.command(player, action, words) 231 handled = True 413 for thing in (player.place.npcs + player.place.things): 414 if thing.handle_command(text, player): 415 handled = True 232 416 if handled: 233 417 return True 234 418 return False 235
