Merge lp:~ashfall/divmod.org/1320678-remove-imaginary into lp:divmod.org

Proposed by Ashwini Oruganti
Status: Merged
Approved by: Tristan Seligmann
Approved revision: 2743
Merged at revision: 2747
Proposed branch: lp:~ashfall/divmod.org/1320678-remove-imaginary
Merge into: lp:divmod.org
Diff against target: 16870 lines (+1/-16267)
115 files modified
Divmod.pth (+1/-3)
Imaginary/COMPATIBILITY.txt (+0/-6)
Imaginary/DEPS.txt (+0/-4)
Imaginary/ExampleGame/examplegame/furniture.py (+0/-139)
Imaginary/ExampleGame/examplegame/glass.py (+0/-72)
Imaginary/ExampleGame/examplegame/japanese.py (+0/-43)
Imaginary/ExampleGame/examplegame/mice.py (+0/-264)
Imaginary/ExampleGame/examplegame/quiche.py (+0/-116)
Imaginary/ExampleGame/examplegame/squeaky.py (+0/-42)
Imaginary/ExampleGame/examplegame/test/test_furniture.py (+0/-104)
Imaginary/ExampleGame/examplegame/test/test_glass.py (+0/-95)
Imaginary/ExampleGame/examplegame/test/test_japanese.py (+0/-474)
Imaginary/ExampleGame/examplegame/test/test_mice.py (+0/-155)
Imaginary/ExampleGame/examplegame/test/test_quiche.py (+0/-93)
Imaginary/ExampleGame/examplegame/test/test_squeaky.py (+0/-51)
Imaginary/ExampleGame/examplegame/test/test_tether.py (+0/-96)
Imaginary/ExampleGame/examplegame/tether.py (+0/-121)
Imaginary/ExampleGame/imaginary/plugins/monsters.py (+0/-6)
Imaginary/ExampleGame/imaginary/plugins/quiche.py (+0/-9)
Imaginary/LICENSE (+0/-20)
Imaginary/MANIFEST.in (+0/-6)
Imaginary/NAME.txt (+0/-10)
Imaginary/NEWS.txt (+0/-36)
Imaginary/README.txt (+0/-98)
Imaginary/axiom/plugins/imaginaryversion.py (+0/-12)
Imaginary/imaginary/__init__.py (+0/-19)
Imaginary/imaginary/_version.py (+0/-3)
Imaginary/imaginary/action.py (+0/-1299)
Imaginary/imaginary/copyright.py (+0/-22)
Imaginary/imaginary/creation.py (+0/-179)
Imaginary/imaginary/eimaginary.py (+0/-90)
Imaginary/imaginary/enhancement.py (+0/-95)
Imaginary/imaginary/events.py (+0/-284)
Imaginary/imaginary/garments.py (+0/-370)
Imaginary/imaginary/idea.py (+0/-625)
Imaginary/imaginary/iimaginary.py (+0/-707)
Imaginary/imaginary/iterutils.py (+0/-15)
Imaginary/imaginary/language.py (+0/-389)
Imaginary/imaginary/manipulation.py (+0/-82)
Imaginary/imaginary/objects.py (+0/-1288)
Imaginary/imaginary/plugins/__init__.py (+0/-5)
Imaginary/imaginary/plugins/clothes.py (+0/-8)
Imaginary/imaginary/plugins/imaginary_basic.py (+0/-9)
Imaginary/imaginary/plugins/lighting.py (+0/-6)
Imaginary/imaginary/pyparsing.py (+0/-2641)
Imaginary/imaginary/resources/help/actions (+0/-5)
Imaginary/imaginary/resources/help/bury (+0/-6)
Imaginary/imaginary/resources/help/close (+0/-5)
Imaginary/imaginary/resources/help/commands (+0/-3)
Imaginary/imaginary/resources/help/create (+0/-12)
Imaginary/imaginary/resources/help/describe (+0/-5)
Imaginary/imaginary/resources/help/dig (+0/-9)
Imaginary/imaginary/resources/help/drop (+0/-6)
Imaginary/imaginary/resources/help/eat (+0/-5)
Imaginary/imaginary/resources/help/emote (+0/-9)
Imaginary/imaginary/resources/help/equipment (+0/-5)
Imaginary/imaginary/resources/help/find (+0/-5)
Imaginary/imaginary/resources/help/get (+0/-6)
Imaginary/imaginary/resources/help/go (+0/-6)
Imaginary/imaginary/resources/help/help (+0/-5)
Imaginary/imaginary/resources/help/hit (+0/-5)
Imaginary/imaginary/resources/help/illuminate (+0/-6)
Imaginary/imaginary/resources/help/inventory (+0/-5)
Imaginary/imaginary/resources/help/list (+0/-10)
Imaginary/imaginary/resources/help/list thing types (+0/-5)
Imaginary/imaginary/resources/help/look (+0/-6)
Imaginary/imaginary/resources/help/name (+0/-5)
Imaginary/imaginary/resources/help/open (+0/-5)
Imaginary/imaginary/resources/help/put (+0/-5)
Imaginary/imaginary/resources/help/quit (+0/-5)
Imaginary/imaginary/resources/help/rebuild (+0/-5)
Imaginary/imaginary/resources/help/remove (+0/-5)
Imaginary/imaginary/resources/help/restore (+0/-5)
Imaginary/imaginary/resources/help/say (+0/-5)
Imaginary/imaginary/resources/help/score (+0/-5)
Imaginary/imaginary/resources/help/scrutinize (+0/-5)
Imaginary/imaginary/resources/help/search (+0/-5)
Imaginary/imaginary/resources/help/set (+0/-7)
Imaginary/imaginary/resources/help/take (+0/-3)
Imaginary/imaginary/resources/help/wear (+0/-5)
Imaginary/imaginary/resources/help/who (+0/-5)
Imaginary/imaginary/resources/motd (+0/-5)
Imaginary/imaginary/test/__init__.py (+0/-1)
Imaginary/imaginary/test/commandutils.py (+0/-253)
Imaginary/imaginary/test/test_actions.py (+0/-842)
Imaginary/imaginary/test/test_actor.py (+0/-37)
Imaginary/imaginary/test/test_concept.py (+0/-245)
Imaginary/imaginary/test/test_container.py (+0/-286)
Imaginary/imaginary/test/test_create.py (+0/-269)
Imaginary/imaginary/test/test_drop.py (+0/-30)
Imaginary/imaginary/test/test_enhancement.py (+0/-194)
Imaginary/imaginary/test/test_garments.py (+0/-306)
Imaginary/imaginary/test/test_hit.py (+0/-63)
Imaginary/imaginary/test/test_idea.py (+0/-241)
Imaginary/imaginary/test/test_illumination.py (+0/-370)
Imaginary/imaginary/test/test_language.py (+0/-116)
Imaginary/imaginary/test/test_look.py (+0/-65)
Imaginary/imaginary/test/test_objects.py (+0/-439)
Imaginary/imaginary/test/test_player.py (+0/-69)
Imaginary/imaginary/test/test_put.py (+0/-114)
Imaginary/imaginary/test/test_set.py (+0/-123)
Imaginary/imaginary/test/test_text.py (+0/-478)
Imaginary/imaginary/test/test_util.py (+0/-24)
Imaginary/imaginary/test/test_who.py (+0/-53)
Imaginary/imaginary/test/test_wiring.py (+0/-79)
Imaginary/imaginary/test/test_world.py (+0/-58)
Imaginary/imaginary/text.py (+0/-277)
Imaginary/imaginary/unc.py (+0/-66)
Imaginary/imaginary/wiring/faucet.py (+0/-62)
Imaginary/imaginary/wiring/player.py (+0/-104)
Imaginary/imaginary/wiring/textserver.py (+0/-379)
Imaginary/imaginary/wiring/tuiserver.py (+0/-75)
Imaginary/imaginary/world.py (+0/-78)
Imaginary/setup.py (+0/-26)
Imaginary/xmantissa/plugins/imaginaryoff.py (+0/-25)
To merge this branch: bzr merge lp:~ashfall/divmod.org/1320678-remove-imaginary
Reviewer Review Type Date Requested Status
Tristan Seligmann Approve
Review via email: mp+219969@code.launchpad.net

Description of the change

Removes Imaginary from lp:divmod.org

To post a comment you must log in.
Revision history for this message
Tristan Seligmann (mithrandi) wrote :

Looks good, please merge (I'm assuming you have commit access to lp:divmod.org; let me know if this isn't the case).

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Divmod.pth'
--- Divmod.pth 2013-06-27 06:02:46 +0000
+++ Divmod.pth 2014-05-18 17:55:49 +0000
@@ -1,4 +1,4 @@
1# -*- test-case-name: axiom,combinator,epsilon,xmantissa,nevow,formless,xquotient,reverend,sine,hyperbola,imaginary,examplegame -*-1# -*- test-case-name: axiom,combinator,epsilon,xmantissa,nevow,formless,xquotient,reverend,sine,hyperbola -*-
2Axiom2Axiom
3Combinator3Combinator
4Epsilon4Epsilon
@@ -8,6 +8,4 @@
8Reverend8Reverend
9Sine9Sine
10Hyperbola10Hyperbola
11Imaginary
12Imaginary/ExampleGame
13Prime11Prime
1412
=== removed directory 'Imaginary'
=== removed file 'Imaginary/COMPATIBILITY.txt'
--- Imaginary/COMPATIBILITY.txt 2009-08-17 02:40:03 +0000
+++ Imaginary/COMPATIBILITY.txt 1970-01-01 00:00:00 +0000
@@ -1,6 +0,0 @@
1Imaginary provides _no_ source-level compatibility from one release to the next.
2
3Efforts will be made to provide database level compatibility (i.e. data from
4one release can be loaded with the next). However, although we will try to
5ensure that data will load, there is no guarantee that it will be meaningfully
6upgraded yet.
70
=== removed file 'Imaginary/DEPS.txt'
--- Imaginary/DEPS.txt 2013-07-10 00:22:10 +0000
+++ Imaginary/DEPS.txt 1970-01-01 00:00:00 +0000
@@ -1,4 +0,0 @@
1Python 2.7
2Twisted 13.1
3Epsilon SVN-Trunk
4Axiom SVN-Trunk
50
=== removed directory 'Imaginary/ExampleGame'
=== removed directory 'Imaginary/ExampleGame/examplegame'
=== removed file 'Imaginary/ExampleGame/examplegame/__init__.py'
=== removed file 'Imaginary/ExampleGame/examplegame/furniture.py'
--- Imaginary/ExampleGame/examplegame/furniture.py 2009-08-17 02:40:03 +0000
+++ Imaginary/ExampleGame/examplegame/furniture.py 1970-01-01 00:00:00 +0000
@@ -1,139 +0,0 @@
1# -*- test-case-name: examplegame.test.test_furniture -*-
2
3"""
4
5 Furniture is the mass noun for the movable objects which may support the
6 human body (seating furniture and beds), provide storage, or hold objects
7 on horizontal surfaces above the ground.
8
9 -- Wikipedia, http://en.wikipedia.org/wiki/Furniture
10
11L{imaginary.furniture} contains L{Action}s which allow players to interact with
12household objects such as chairs and tables, and L{Enhancement}s which allow
13L{Thing}s to behave as same.
14
15This has the same implementation weakness as L{examplegame.tether}, in that it
16needs to make some assumptions about who is moving what in its restrictions of
17movement; it should be moved into imaginary proper when that can be properly
18addressed. It's also a bit too draconian in terms of preventing the player
19from moving for any reason just because they're seated. However, it's a
20workable approximation for some uses, and thus useful as an example.
21"""
22
23from zope.interface import implements
24
25from axiom.item import Item
26from axiom.attributes import reference
27
28from imaginary.iimaginary import ISittable, IContainer, IMovementRestriction
29from imaginary.eimaginary import ActionFailure
30from imaginary.events import ThatDoesntWork
31from imaginary.language import Noun
32from imaginary.action import Action, TargetAction
33from imaginary.events import Success
34from imaginary.enhancement import Enhancement
35from imaginary.objects import Container
36from imaginary.pyparsing import Literal, Optional, restOfLine
37
38class Sit(TargetAction):
39 """
40 An action allowing a player to sit down in a chair.
41 """
42 expr = (Literal("sit") + Optional(Literal("on")) +
43 restOfLine.setResultsName("target"))
44
45 targetInterface = ISittable
46
47 def do(self, player, line, target):
48 """
49 Do the action; sit down.
50 """
51 target.seat(player)
52
53 actorMessage=["You sit in ",
54 Noun(target.thing).definiteNounPhrase(),"."]
55 otherMessage=[player.thing, " sits in ",
56 Noun(target.thing).definiteNounPhrase(),"."]
57 Success(actor=player.thing, location=player.thing.location,
58 actorMessage=actorMessage,
59 otherMessage=otherMessage).broadcast()
60
61
62class Stand(Action):
63 """
64 Stand up from a sitting position.
65 """
66 expr = (Literal("stand") + Optional(Literal("up")))
67
68 def do(self, player, line):
69 """
70 Do the action; stand up.
71 """
72 # XXX This is wrong. I should be issuing an obtain() query to find
73 # something that qualifies as "my location" or "the thing I'm already
74 # sitting in".
75 chair = ISittable(player.thing.location, None)
76 if chair is None:
77 raise ActionFailure(ThatDoesntWork(
78 actor=player.thing,
79 actorMessage=["You're already standing."]))
80 chair.unseat(player)
81 Success(actor=player.thing, location=player.thing.location,
82 actorMessage=["You stand up."],
83 otherMessage=[player.thing, " stands up."]).broadcast()
84
85
86
87class Chair(Enhancement, Item):
88 """
89 A chair is a thing you can sit in.
90 """
91
92 implements(ISittable, IMovementRestriction)
93
94 powerupInterfaces = [ISittable]
95
96 thing = reference()
97 container = reference()
98
99
100 def movementImminent(self, movee, destination):
101 """
102 A player tried to move while they were seated. Prevent them from doing
103 so, noting that they must stand first.
104
105 (Assume the player was trying to move themselves, although there's no
106 way to know currently.)
107 """
108 raise ActionFailure(ThatDoesntWork(
109 actor=movee,
110 actorMessage=u"You can't do that while sitting down."))
111
112
113 def applyEnhancement(self):
114 """
115 Apply this enhancement to this L{Chair}'s thing, creating a
116 L{Container} to hold the seated player, if necessary.
117 """
118 super(Chair, self).applyEnhancement()
119 container = IContainer(self.thing, None)
120 if container is None:
121 container = Container.createFor(self.thing, capacity=300)
122 self.container = container
123
124
125 def seat(self, player):
126 """
127 The player sat down on this chair; place them into it and prevent them
128 from moving elsewhere until they stand up.
129 """
130 player.thing.moveTo(self.container)
131 player.thing.powerUp(self, IMovementRestriction)
132
133
134 def unseat(self, player):
135 """
136 The player stood up; remove them from this chair.
137 """
138 player.thing.powerDown(self, IMovementRestriction)
139 player.thing.moveTo(self.container.thing.location)
1400
=== removed file 'Imaginary/ExampleGame/examplegame/glass.py'
--- Imaginary/ExampleGame/examplegame/glass.py 2009-08-17 02:40:03 +0000
+++ Imaginary/ExampleGame/examplegame/glass.py 1970-01-01 00:00:00 +0000
@@ -1,72 +0,0 @@
1# -*- test-case-name: examplegame.test.test_glass -*-
2"""
3This example implements a transparent container that you can see, but not
4reach, the contents of.
5"""
6from zope.interface import implements
7
8from axiom.item import Item
9from axiom.attributes import reference
10
11from imaginary.iimaginary import (
12 ILinkContributor, IWhyNot, IObstruction, IContainer)
13from imaginary.enhancement import Enhancement
14from imaginary.objects import ContainmentRelationship
15from imaginary.idea import Link
16
17class _CantReachThroughGlassBox(object):
18 """
19 This object provides an explanation for why the user cannot access a target
20 that is inside a L{imaginary.objects.Thing} enhanced with a L{GlassBox}.
21 """
22 implements(IWhyNot)
23
24 def tellMeWhyNot(self):
25 """
26 Return a simple message explaining that the user can't reach through
27 the glass box.
28 """
29 return "You can't reach through the glass box."
30
31
32
33class _ObstructedByGlass(object):
34 """
35 This is an annotation on a link between two objects which represents a
36 physical obstruction between them. It is used to annotate between a
37 L{GlassBox} and its contents, so you can see them without reaching them.
38 """
39 implements(IObstruction)
40
41 def whyNot(self):
42 """
43 @return: an object which explains why you can't reach through the glass
44 box.
45 """
46 return _CantReachThroughGlassBox()
47
48
49
50class GlassBox(Item, Enhancement):
51 """
52 L{GlassBox} is an L{Enhancement} which modifies a container such that it is
53 contained.
54 """
55
56 powerupInterfaces = (ILinkContributor,)
57
58 thing = reference()
59
60 def links(self):
61 """
62 If the container attached to this L{GlassBox}'s C{thing} is closed,
63 yield its list of contents with each link annotated with
64 L{_ObstructedByGlass}, indicating that the object cannot be reached.
65 """
66 container = IContainer(self.thing)
67 if container.closed:
68 for content in container.getContents():
69 link = Link(self.thing.idea, content.idea)
70 link.annotate([_ObstructedByGlass(),
71 ContainmentRelationship(container)])
72 yield link
730
=== removed file 'Imaginary/ExampleGame/examplegame/japanese.py'
--- Imaginary/ExampleGame/examplegame/japanese.py 2007-08-17 04:46:38 +0000
+++ Imaginary/ExampleGame/examplegame/japanese.py 1970-01-01 00:00:00 +0000
@@ -1,43 +0,0 @@
1"""
2Japanese language data.
3
4This module contains a dict named 'hiragana' which maps hiragana
5unicode characters to romaji pronunciations, as well as a
6'romajiToHiragana' dict which maps romaji pronunciation to *lists* of
7hiragana characters. There are multiple hiragana characters with the
8same pronunciation, thus the multiple values per romaji in the
9romajiToHiragana dict.
10
11"""
12
13
14# Hiragana.
15hiragana = {
16 u'\u3042': 'A', u'\u3044': 'I', u'\u3046': 'U', u'\u3048': 'E',
17 u'\u3081': 'ME', u'\u3080': 'MU', u'\u3082': 'MO', u'\u3084': 'YA',
18 u'\u3086': 'YU', u'\u3089': 'RA', u'\u3088': 'YO', u'\u308b': 'RU',
19 u'\u308a': 'RI', u'\u308d': 'RO', u'\u308c': 'RE', u'\u308f': 'WA',
20 u'\u3091': 'WE', u'\u3090': 'WI', u'\u3093': 'N', u'\u3092': 'WO',
21 u'\u304b': 'KA', u'\u304a': 'O', u'\u304d': 'KI', u'\u304c': 'GA',
22 u'\u304f': 'KU', u'\u304e': 'GI', u'\u3051': 'KE', u'\u3050': 'GU',
23 u'\u3053': 'KO', u'\u3052': 'GE', u'\u3055': 'SA', u'\u3054': 'GO',
24 u'\u3057': 'SHI',u'\u3056': 'ZA', u'\u3059': 'SU', u'\u3058': 'JI',
25 u'\u305b': 'SE', u'\u305a': 'ZU', u'\u305d': 'SO', u'\u305c': 'ZE',
26 u'\u305f': 'TA', u'\u305e': 'ZO', u'\u3061': 'CHI', u'\u3060': 'DA',
27 u'\u3062': 'JI', u'\u3065': 'ZU', u'\u3064': 'TSU', u'\u3067': 'DE',
28 u'\u3066': 'TE', u'\u3069': 'DO', u'\u3068': 'TO', u'\u306b': 'NI',
29 u'\u306a': 'NA', u'\u306d': 'NE', u'\u306c': 'NU', u'\u306f': 'HA',
30 u'\u306e': 'NO', u'\u3071': 'PA', u'\u3070': 'BA', u'\u3073': 'BI',
31 u'\u3072': 'HI', u'\u3075': 'FU', u'\u3074': 'PI', u'\u3077': 'PU',
32 u'\u3076': 'BU', u'\u3079': 'BE', u'\u3078': 'HE', u'\u307b': 'HO',
33 u'\u307a': 'PE', u'\u307d': 'PO', u'\u307c': 'BO', u'\u307f': 'MI',
34 u'\u307e': 'MA'}
35
36
37romajiToHiragana = {}
38for k, v in hiragana.iteritems():
39 romajiToHiragana.setdefault(v, []).append(k)
40
41# Katakana.
42# katakana = {
43# }
440
=== removed file 'Imaginary/ExampleGame/examplegame/mice.py'
--- Imaginary/ExampleGame/examplegame/mice.py 2009-08-17 02:40:03 +0000
+++ Imaginary/ExampleGame/examplegame/mice.py 1970-01-01 00:00:00 +0000
@@ -1,264 +0,0 @@
1# -*- test-case-name: examplegame.test.test_mice,examplegame.test.test_japanese -*-
2
3import random
4
5from zope.interface import implements
6
7from axiom import item, attributes
8
9from imaginary import iimaginary, events, objects, action, language
10from examplegame import japanese
11
12
13class Mouse(item.Item):
14 """
15 A silly mouse which squeaks when actors enter the room it is in.
16
17 @ivar _callLater: The scheduling function to use. Override in unit
18 tests only.
19 """
20
21 implements(iimaginary.IEventObserver)
22
23 squeakiness = attributes.integer(doc="""
24 How likely the mouse is to squeak when intruded upon (0 - 100).
25
26 This mouse is so angry that he will pretty much always squeak.
27 """, default=100)
28
29 _callLater = attributes.inmemory()
30
31 def activate(self):
32 from twisted.internet import reactor
33 self._callLater = reactor.callLater
34
35
36 def prepare(self, concept):
37 """
38 An event was received. Squeak if it represents the arrival of a dude.
39 """
40 if isinstance(concept, events.ArrivalEvent):
41 return lambda: self._callLater(0, self.squeak)
42 return lambda: None
43
44
45 def squeak(self):
46 actor = self.store.findUnique(
47 objects.Actor,
48 objects.Actor._enduringIntelligence == self)
49 evt = events.Success(
50 actor=actor.thing,
51 otherMessage=u"SQUEAK!")
52 evt.broadcast()
53
54
55
56class ChallengeCollision(Exception):
57 """
58 Raised when a L{HiraganaMouse} is asked to start issuing challenges when it
59 is already issuing challenges.
60 """
61
62
63
64class ChallengeVacuum(Exception):
65 """
66 Raised when a L{HiraganaMouse} is asked to stop issuing challenges when it
67 is already not issuing challenges.
68 """
69
70
71
72class HiraganaMouse(item.Item):
73 """
74 A mouse which occasionally challenges those in its location to
75 transliterate Hiragana.
76
77 @ivar _callLater: The scheduling function to use. Defaults to the
78 reactor's callLater method. This is parameterized for the sake of
79 unit tests.
80 """
81
82 implements(iimaginary.IEventObserver)
83
84 challenging = attributes.boolean(doc="""
85 Whether or not this mouse is currently creating random challenges.
86 """, default=False)
87
88 challengeInterval = attributes.integer(doc="""
89 Number of seconds between challenges.
90 """, default=15, allowNone=False)
91
92 _currentChallenge = attributes.text(doc="""
93 The Hiragana character which the mouse has most recently issued as a
94 challenge.
95 """, default=None)
96
97
98 _callLater = attributes.inmemory()
99 _currentChallengeCall = attributes.inmemory()
100
101 def activate(self):
102 from twisted.internet import reactor
103 self._callLater = reactor.callLater
104
105 def _actor(self):
106 """
107 Get the h-mouse's associated actor. PRIVATE. WHY DID I DOCUMENT THIS.
108 """
109 return self.store.findUnique(
110 objects.Actor,
111 objects.Actor._enduringIntelligence == self)
112
113
114 def _numDudes(self):
115 """
116 Get the number of actors (other than the h-mouse) in the
117 h-mouse's location. PRIVATE.
118 """
119 actor = self._actor()
120 numDudes = len([actor
121 for dude
122 in actor.thing.findProviders(iimaginary.IActor, 1)
123 if dude is not actor])
124 return numDudes
125
126
127 def maybeChallenge(self):
128 """
129 Start challenging if there is anyone around to challenge (and
130 this h-mouse isn't already challenging).
131 """
132 if not self.challenging and self._numDudes() >= 1:
133 self.startChallenging()
134
135
136 def prepare(self, concept):
137 """
138 An event was received. Start or stop challenging as
139 appropriate, based on whether there is anyone to challenge.
140 """
141 if isinstance(concept, events.ArrivalEvent):
142 self.maybeChallenge()
143 elif isinstance(concept, events.DepartureEvent) and self._numDudes() == 0:
144 self.stopChallenging()
145 elif isinstance(concept, events.SpeechEvent) and concept.speaker is not self._actor().thing:
146 self.responseReceived(concept.speaker, concept.text)
147 return lambda: None
148
149
150 def startChallenging(self):
151 """
152 Start shouting hiragana in the hope that someone knows what it means.
153
154 @raises ChallengeCollision: If this h-mouse is already challenging.
155 """
156 if self.challenging:
157 raise ChallengeCollision()
158
159 self.challenging = True
160 self._scheduleChallenge()
161
162
163 def _scheduleChallenge(self):
164 """
165 Schedule a challenge to happen in the number of seconds set in
166 the instance attribute 'challengeInterval'.
167 """
168 self._currentChallengeCall = self._callLater(self.challengeInterval,
169 self._challengeAndRepeat)
170
171
172 def stopChallenging(self):
173 """
174 Stop shouting hiragana.
175
176 @raises ChallengeVacuum: If this h-mouse is not currently challenging.
177 """
178 if not self.challenging:
179 raise ChallengeVacuum()
180
181 self.challenging = False
182
183 self._currentChallenge = None
184 self._currentChallengeCall.cancel()
185 self._currentChallengeCall = None
186
187
188 def _challengeAndRepeat(self):
189 """
190 Shout a challenge and then schedule another one.
191 """
192 self.challenge()
193 self._scheduleChallenge()
194
195
196 def getCurrentChallenge(self):
197 """
198 Return the Hiragana character which is this mouse's current challenge,
199 if it has one.
200
201 @rtype: C{unicode} or C{None}
202 """
203 return self._currentChallenge
204
205
206 def vetteChallengeResponse(self, romajiResponse):
207 """
208 Return True if the given response matches the current challenge, False
209 otherwise.
210 """
211 hiragana = japanese.romajiToHiragana.get(romajiResponse.upper(), None)
212 return hiragana is not None and self.getCurrentChallenge() in hiragana
213
214
215 def responseReceived(self, responder, romajiResponse):
216 """
217 Called when some speech is observed.
218 """
219 me = self._actor().thing
220 if self.vetteChallengeResponse(romajiResponse):
221 self._currentChallenge = None
222 verb = u"salute"
223 else:
224 verb = u"bite"
225 evt = events.Success(
226 actor=me,
227 target=responder,
228 actorMessage=language.Sentence(["You ", verb, " ", responder, "."]),
229 targetMessage=language.Sentence([language.Noun(me).shortName(), " ", verb, "s you!"]),
230 otherMessage=language.Sentence([me, " ", verb, "s ", responder, "."]))
231 # Fuck the reactor, Fuck scheduling, why does responseReceived
232 # need to be concerned with these stupid scheduling details
233 # when all it wants to do is respond basically-immediately.
234 self._callLater(0, evt.broadcast)
235
236
237 def challenge(self, character=None):
238 """
239 Say only a single random hiragana character.
240 """
241 if character is None:
242 character = random.choice(japanese.hiragana.keys())
243 self._currentChallenge = character
244 actor = self._actor()
245 action.Say().do(actor, None, character)
246
247
248
249def createMouseCreator(mouseIntelligenceFactory):
250 """
251 Create a createMouse function, which can be called to create a
252 mouse object. Used for the 'Create' command plugin system.
253 """
254 def createMouse(**kw):
255 store = kw['store']
256 mouse = objects.Thing(**kw)
257 mouseActor = objects.Actor.createFor(mouse)
258 mousehood = mouseIntelligenceFactory(store=store)
259 mouseActor.setEnduringIntelligence(mousehood)
260 return mouse
261 return createMouse
262
263createMouse = createMouseCreator(Mouse)
264createHiraganaMouse = createMouseCreator(HiraganaMouse)
2650
=== removed file 'Imaginary/ExampleGame/examplegame/quiche.py'
--- Imaginary/ExampleGame/examplegame/quiche.py 2009-06-29 04:03:17 +0000
+++ Imaginary/ExampleGame/examplegame/quiche.py 1970-01-01 00:00:00 +0000
@@ -1,116 +0,0 @@
1# -*- test-case-name: examplegame.test.test_quiche -*-
2
3"""
4This module is a mid-level proof of concept of various features in Imaginary.
5
6Currently its implementation is a bit messy and it assumes lots of things about
7the reader's knowledge, but we are working on more thoroughly documenting it
8and making it into a good example of how to build functionality that interacts
9with multiple systems (currency, containment, object creation) in Imaginary.
10
11"""
12
13from zope.interface import implements, Interface
14
15from axiom import item, attributes
16
17from imaginary import iimaginary, objects, events, language
18from imaginary.enhancement import Enhancement
19
20from imaginary.creation import createCreator
21
22
23class ICoin(Interface):
24 """
25 Something small and probably flat and round and which probably serves as
26 some form of currency.
27 """
28
29
30
31class Coinage(object):
32 implements(ICoin)
33 powerupInterfaces = (ICoin,)
34
35
36
37class Quarter(item.Item, Coinage, Enhancement):
38 thing = attributes.reference(doc="""
39 The object this coin powers up.
40 """)
41
42
43
44class VendingMachine(item.Item, objects.Containment, Enhancement):
45 implements(iimaginary.IContainer)
46
47 capacity = attributes.integer(doc="""
48 Units of weight which can be contained.
49 """, allowNone=False, default=1)
50
51 closed = attributes.boolean(doc="""
52 Indicates whether the container is currently closed or open.
53 """, allowNone=False, default=True)
54
55 thing = attributes.reference(doc="""
56 The object this container powers up.
57 """)
58
59 _currencyCounter = attributes.integer(doc="""
60 The number of coins which have been added to this vending machine since it
61 last ejected an item.
62 """, allowNone=False, default=0)
63
64 def coinAdded(self, coin):
65 """
66 Called when a coin is added to this thing.
67
68 @type coin: C{ICoin} provider
69 """
70 self._currencyCounter += 1
71 if self._currencyCounter >= 5 and self.getContents():
72 self._currencyCounter = 0
73 try:
74 obj = iter(self.getContents()).next()
75 except StopIteration:
76 evt = events.Success(
77 actor=self.thing,
78 target=obj,
79 otherMessage=language.Sentence([self.thing, " thumps loudly."]))
80 else:
81 evt = events.Success(
82 actor=self.thing,
83 target=obj,
84 otherMessage=language.Sentence([
85 language.Noun(self.thing).definiteNounPhrase(),
86 " thumps loudly and spits out ", obj,
87 " onto the ground."]))
88 state = self.closed
89 self.closed = False
90 try:
91 obj.moveTo(self.thing.location)
92 finally:
93 self.closed = state
94 evt.broadcast()
95
96
97 def add(self, obj):
98 coin = ICoin(obj, None)
99 if coin is not None:
100 self.coinAdded(coin)
101 else:
102 return super(VendingMachine, self).add(obj)
103
104
105
106def createVendingMachine(store, name, description=u""):
107 o = objects.Thing(store=store, name=name, description=description)
108 VendingMachine.createFor(o)
109 return o
110
111
112
113createCoin = createCreator((Quarter, {}))
114createVendingMachine = createCreator((VendingMachine, {}))
115createQuiche = createCreator()
116
1170
=== removed file 'Imaginary/ExampleGame/examplegame/squeaky.py'
--- Imaginary/ExampleGame/examplegame/squeaky.py 2009-08-17 02:40:03 +0000
+++ Imaginary/ExampleGame/examplegame/squeaky.py 1970-01-01 00:00:00 +0000
@@ -1,42 +0,0 @@
1# -*- test-case-name: examplegame.test.test_squeaky -*-
2
3"""
4This module implements an L{ILinkAnnotator} which causes an object to squeak
5when it is moved. It should serve as a simple example for overriding what
6happens when an action is executed (in this case, 'take' and 'drop').
7"""
8
9from zope.interface import implements
10
11from axiom.item import Item
12from axiom.attributes import reference
13
14from imaginary.iimaginary import IMovementRestriction, IConcept
15from imaginary.events import Success
16from imaginary.enhancement import Enhancement
17from imaginary.objects import Thing
18
19
20class Squeaker(Item, Enhancement):
21 """
22 This is an L{Enhancement} which, when installed on a L{Thing}, causes that
23 L{Thing} to squeak when you pick it up.
24 """
25
26 implements(IMovementRestriction)
27
28 powerupInterfaces = [IMovementRestriction]
29
30 thing = reference(allowNone=False,
31 whenDeleted=reference.CASCADE,
32 reftype=Thing)
33
34
35 def movementImminent(self, movee, destination):
36 """
37 The object enhanced by this L{Squeaker} is about to move - emit a
38 L{Success} event which describes its squeak.
39 """
40 Success(otherMessage=(IConcept(self.thing).capitalizeConcept(),
41 " emits a faint squeak."),
42 location=self.thing.location).broadcast()
430
=== removed directory 'Imaginary/ExampleGame/examplegame/test'
=== removed file 'Imaginary/ExampleGame/examplegame/test/__init__.py'
=== removed file 'Imaginary/ExampleGame/examplegame/test/test_furniture.py'
--- Imaginary/ExampleGame/examplegame/test/test_furniture.py 2013-07-03 23:55:37 +0000
+++ Imaginary/ExampleGame/examplegame/test/test_furniture.py 1970-01-01 00:00:00 +0000
@@ -1,104 +0,0 @@
1
2"""
3This module contains tests for the examplegame.furniture module.
4"""
5
6from twisted.trial.unittest import TestCase
7
8from imaginary.test.commandutils import CommandTestCaseMixin, E, createLocation
9
10from imaginary.objects import Thing, Exit
11from examplegame.furniture import Chair
12
13class SitAndStandTests(CommandTestCaseMixin, TestCase):
14 """
15 Tests for the 'sit' and 'stand' actions.
16 """
17
18 def setUp(self):
19 """
20 Create a room, with a dude in it, and a chair he can sit in.
21 """
22 CommandTestCaseMixin.setUp(self)
23 self.chairThing = Thing(store=self.store, name=u"chair")
24 self.chairThing.moveTo(self.location)
25 self.chair = Chair.createFor(self.chairThing)
26
27
28 def test_sitDown(self):
29 """
30 Sitting in a chair should move your location to that chair.
31 """
32 self.assertCommandOutput(
33 "sit chair",
34 ["You sit in the chair."],
35 ["Test Player sits in the chair."])
36 self.assertEquals(self.player.location, self.chair.thing)
37
38
39 def test_standWhenStanding(self):
40 """
41 You can't stand up - you're already standing up.
42 """
43 self.assertCommandOutput(
44 "stand up",
45 ["You're already standing."])
46
47
48 def test_standWhenSitting(self):
49 """
50 If a player stands up when sitting in a chair, they should be seen to
51 stand up, and they should be placed back into the room where the chair
52 is located.
53 """
54 self.test_sitDown()
55 self.assertCommandOutput(
56 "stand up",
57 ["You stand up."],
58 ["Test Player stands up."])
59 self.assertEquals(self.player.location, self.location)
60
61
62 def test_takeWhenSitting(self):
63 """
64 When a player is seated, they should still be able to take objects on
65 the floor around them.
66 """
67 self.test_sitDown()
68 self.ball = Thing(store=self.store, name=u'ball')
69 self.ball.moveTo(self.location)
70 self.assertCommandOutput(
71 "take ball",
72 ["You take a ball."],
73 ["Test Player takes a ball."])
74
75
76 def test_moveWhenSitting(self):
77 """
78 A player who is sitting shouldn't be able to move without standing up
79 first.
80 """
81 self.test_sitDown()
82 otherRoom = createLocation(self.store, u"elsewhere", None).thing
83 Exit.link(self.location, otherRoom, u'north')
84 self.assertCommandOutput(
85 "go north",
86 ["You can't do that while sitting down."])
87 self.assertCommandOutput(
88 "go south",
89 ["You can't go that way."])
90
91
92 def test_lookWhenSitting(self):
93 """
94 Looking around when sitting should display the description of the room.
95 """
96 self.test_sitDown()
97 self.assertCommandOutput(
98 "look",
99 # I'd like to add ', in the chair' to this test, but there's
100 # currently no way to modify the name of the object being looked
101 # at.
102 [E("[ Test Location ]"),
103 "Location for testing.",
104 "Here, you see Observer Player and a chair."])
1050
=== removed file 'Imaginary/ExampleGame/examplegame/test/test_glass.py'
--- Imaginary/ExampleGame/examplegame/test/test_glass.py 2013-07-03 23:55:37 +0000
+++ Imaginary/ExampleGame/examplegame/test/test_glass.py 1970-01-01 00:00:00 +0000
@@ -1,95 +0,0 @@
1
2"""
3Tests for L{examplegame.glass}
4"""
5
6from twisted.trial.unittest import TestCase
7
8from imaginary.test.commandutils import CommandTestCaseMixin, E
9
10from imaginary.objects import Thing, Container
11
12from examplegame.glass import GlassBox
13
14class GlassBoxTests(CommandTestCaseMixin, TestCase):
15 """
16 Tests for L{GlassBox}
17 """
18
19 def setUp(self):
20 """
21 Create a room with a L{GlassBox} in it, which itself contains a ball.
22 """
23 CommandTestCaseMixin.setUp(self)
24 self.box = Thing(store=self.store, name=u'box',
25 description=u'The system under test.')
26 self.ball = Thing(store=self.store, name=u'ball',
27 description=u'an interesting object')
28 self.container = Container.createFor(self.box)
29 GlassBox.createFor(self.box)
30 self.ball.moveTo(self.box)
31 self.box.moveTo(self.location)
32 self.container.closed = True
33
34
35 def test_lookThrough(self):
36 """
37 You can see items within a glass box by looking at them directly.
38 """
39 self.assertCommandOutput(
40 "look at ball",
41 [E("[ ball ]"),
42 "an interesting object"])
43
44
45 def test_lookAt(self):
46 """
47 You can see the contents within a glass box by looking at the box.
48 """
49 self.assertCommandOutput(
50 "look at box",
51 [E("[ box ]"),
52 "The system under test.",
53 "It contains a ball."])
54
55
56 def test_take(self):
57 """
58 You can't take items within a glass box.
59 """
60 self.assertCommandOutput(
61 "get ball",
62 ["You can't reach through the glass box."])
63
64
65 def test_openTake(self):
66 """
67 Taking items from a glass box should work if it's open.
68 """
69 self.container.closed = False
70 self.assertCommandOutput(
71 "get ball",
72 ["You take a ball."],
73 ["Test Player takes a ball."])
74
75
76 def test_put(self):
77 """
78 You can't put items into a glass box.
79 """
80 self.container.closed = False
81 self.ball.moveTo(self.location)
82 self.container.closed = True
83 self.assertCommandOutput(
84 "put ball in box",
85 ["The box is closed."])
86
87
88 def test_whyNot(self):
89 """
90 A regression test; there was a bug where glass boxes would interfere
91 with normal target-acquisition error reporting.
92 """
93 self.assertCommandOutput(
94 "get foobar",
95 ["Nothing like that around here."])
960
=== removed file 'Imaginary/ExampleGame/examplegame/test/test_japanese.py'
--- Imaginary/ExampleGame/examplegame/test/test_japanese.py 2013-07-03 23:55:37 +0000
+++ Imaginary/ExampleGame/examplegame/test/test_japanese.py 1970-01-01 00:00:00 +0000
@@ -1,474 +0,0 @@
1import weakref
2
3from twisted.internet import task
4from twisted.trial import unittest
5
6from axiom import store
7
8from imaginary import iimaginary, objects, events, action
9
10from imaginary.test import commandutils
11
12from examplegame import mice
13from examplegame import japanese
14
15class MouseChallengeMixin(object):
16 """
17 A mixin meant to be used in TestCases which want to assert things
18 about mouse challenges.
19
20 The subclass must be sure to provide a C{player} instance
21 attribute, which is the L{IThing<iimaginary.IThing>} provider of
22 the player which observes the mouse, and a C{mouseName} attribute
23 which should be the mouse's name.
24 """
25 def assertChallenge(self, concept):
26 """
27 Assert that the given concept is a challenge from the mouse
28 named self.mouseName, as observed by self.player.
29 """
30 said = commandutils.flatten(concept.plaintext(self.player))
31 self.failUnless(said.startswith(u"A %s says, '" % (self.mouseName,)), repr(said))
32 self.failUnlessIn(said[-3], japanese.hiragana)
33 self.failUnless(said.endswith("'\n"), repr(said))
34
35
36
37class HiraganaMouseTestCase(MouseChallengeMixin, unittest.TestCase):
38 """
39 Test that there is a mouse that says hiragana and stuff
40 """
41
42 def setUp(self):
43 self.store = store.Store()
44
45 self.clock = objects.Thing(store=self.store, name=u"Clock")
46 self.clockContainer = objects.Container.createFor(self.clock, capacity=10)
47
48 self.mouseName = u"\N{KATAKANA LETTER PI}\N{KATAKANA LETTER SMALL YU}"
49 self.mouse = mice.createHiraganaMouse(
50 store=self.store,
51 name=self.mouseName)
52 self.mouseActor = iimaginary.IActor(self.mouse)
53 self.mousehood = self.mouseActor.getIntelligence()
54 self.mouse.moveTo(self.clock)
55
56 (self.player,
57 self.playerActor,
58 self.playerIntelligence) = commandutils.createPlayer(self.store,
59 u"Mean Old Man")
60
61
62 self.player.moveTo(self.clock)
63
64 self.reactorTime = task.Clock()
65 self.mousehood._callLater = self.reactorTime.callLater
66
67
68 def test_mouseCanSqueak(self):
69 """
70 When explicitly told to challenge with a given romaji syllable, the
71 mouse should say a hiragana letter.
72 """
73 events.runEventTransaction(
74 self.store,
75 self.mousehood.challenge,
76 character=u"\N{HIRAGANA LETTER A}")
77
78 self.assertEquals(len(self.playerIntelligence.concepts), 1)
79 event = self.playerIntelligence.concepts[0]
80 self.assertEquals(
81 commandutils.flatten(event.otherMessage.plaintext(self.player)),
82 u"A %s says, '\N{HIRAGANA LETTER A}'" % (self.mouseName,))
83
84
85 def test_randomHiragana(self):
86 """
87 When explicitly told to challenge without specifying a syllable, the
88 mouse should say a random one.
89 """
90 events.runEventTransaction(self.store, self.mousehood.challenge)
91 self.assertEquals(len(self.playerIntelligence.concepts), 1)
92 event = self.playerIntelligence.concepts[0]
93 self.assertChallenge(event)
94
95
96 def test_ji(self):
97 """
98 Two hiragana characters map to the romaji 'ji'. Test that we do the
99 right thing for them.
100 """
101 self.mousehood.challenge(character=u"\N{HIRAGANA LETTER DI}")
102 self.failUnless(self.mousehood.vetteChallengeResponse(u"ji"))
103 self.mousehood.challenge(character=u"\N{HIRAGANA LETTER ZI}")
104 self.failUnless(self.mousehood.vetteChallengeResponse(u"ji"))
105
106
107 def test_zu(self):
108 """
109 Two hiragana characters map to the romaji 'zu'. Test that we do the
110 right thing for them.
111 """
112 self.mousehood.challenge(character=u"\N{HIRAGANA LETTER DU}")
113 self.failUnless(self.mousehood.vetteChallengeResponse(u"zu"))
114 self.mousehood.challenge(character=u"\N{HIRAGANA LETTER ZU}")
115 self.failUnless(self.mousehood.vetteChallengeResponse(u"zu"))
116
117
118 def test_mouseStartsChallengingWhenPlayersArrive(self):
119 """
120 When a player arrives, the mouse should go into the 'I am
121 challenging' state.
122 """
123 # Whitebox
124 self.assertEquals(self.mousehood.challenging, False)
125
126 evt = events.ArrivalEvent(actor=self.player)
127 self.mouseActor.send(evt)
128
129 self.assertEquals(self.mousehood.challenging, True)
130
131
132 def test_mouseSchedulesChallenges(self):
133 """
134 After telling a mouse to start challenging, it should schedule timed
135 events to say challenges.
136 """
137 self.mousehood.startChallenging()
138 self.reactorTime.advance(self.mousehood.challengeInterval)
139 concepts = self.playerIntelligence.concepts
140 self.assertEquals(len(concepts), 1)
141 self.assertChallenge(concepts[0])
142
143
144 def test_mouseStopsChallengingWhenPlayersLeave(self):
145 """
146 When the 'last' player leaves, the mouse stops challenging.
147 """
148 # Whitebox
149 self.mousehood.startChallenging()
150
151 evt = events.DepartureEvent(location=self.clock,
152 actor=self.player)
153 self.player.moveTo(None)
154 self.mouseActor.send(evt)
155
156 self.assertEquals(self.mousehood.challenging, False)
157
158
159 def test_mouseStopsSchedulingChallenges(self):
160 """
161 When a mouse is told to stop challenging, it should cancel any
162 challenges it had scheduled.
163 """
164 self.mousehood.startChallenging()
165 self.mousehood.stopChallenging()
166
167 self.reactorTime.advance(self.mousehood.challengeInterval)
168 self.assertEquals(self.playerIntelligence.concepts, [])
169
170
171 def test_stopChallengingWhenNotChallengingFails(self):
172 """
173 Don't stop challenging when you're not challenging.
174 """
175 self.assertRaises(mice.ChallengeVacuum, self.mousehood.stopChallenging)
176
177
178 def test_startChallengingTwiceFails(self):
179 """
180 Don't start challenging twice.
181 """
182 self.mousehood.startChallenging()
183 self.assertRaises(mice.ChallengeCollision, self.mousehood.startChallenging)
184
185
186 def test_challengeRecurrence(self):
187 """
188 After a challenge is issued another one should be issued later.
189 """
190 self.mousehood.startChallenging()
191 self.reactorTime.advance(self.mousehood.challengeInterval)
192
193 self.assertIn(self.mousehood.getCurrentChallenge(), japanese.hiragana)
194
195 self.mousehood._currentChallenge = None # Clear his challenge evilly
196
197 self.reactorTime.advance(self.mousehood.challengeInterval)
198
199 self.assertIn(self.mousehood.getCurrentChallenge(), japanese.hiragana)
200
201
202 def test_twoMenEnter(self):
203 """
204 Test that when *TWO* players join, the mouse doesn't schedule too many
205 challenges.
206 """
207 otherPlayer = commandutils.createPlayer(self.store,
208 u"Polite Young Man")[0]
209
210 # Send an arrival event because setUp doesn't
211 firstEvent = events.ArrivalEvent(actor=self.player)
212
213 self.mouseActor.send(firstEvent)
214 otherPlayer.moveTo(self.clock, arrivalEventFactory=events.MovementArrivalEvent)
215
216 self.playerIntelligence.concepts = []
217 self.reactorTime.advance(self.mousehood.challengeInterval)
218
219 self.assertEquals(len(self.playerIntelligence.concepts), 1)
220 self.assertChallenge(self.playerIntelligence.concepts[0])
221
222
223 def test_twoMenLeave(self):
224 """
225 Test that when two players are near the mouse, the mouse doesn't
226 unschedule its challenge until they both leave.
227 """
228 otherPlayer = commandutils.createPlayer(self.store,
229 u"Polite Young Man")[0]
230 otherPlayer.moveTo(self.clock)
231
232 self.mousehood.startChallenging()
233
234 firstEvent = events.DepartureEvent(location=self.clock,
235 actor=self.player)
236 secondEvent = events.DepartureEvent(location=self.clock,
237 actor=otherPlayer)
238
239 otherPlayer.moveTo(None)
240 self.mouseActor.send(secondEvent)
241
242 self.playerIntelligence.concepts = []
243
244 self.reactorTime.advance(self.mousehood.challengeInterval)
245
246 self.assertEquals(len(self.playerIntelligence.concepts), 1)
247 self.assertChallenge(self.playerIntelligence.concepts[0])
248
249 self.player.moveTo(None)
250 self.mouseActor.send(firstEvent)
251
252 self.failIf(self.mousehood.challenging)
253
254
255 def test_getCurrentChallenge(self):
256 """
257 Test that we can introspect the current challenge of a mouse.
258 """
259 self.mousehood.startChallenging()
260 self.reactorTime.advance(self.mousehood.challengeInterval)
261 self.failUnlessIn(self.mousehood.getCurrentChallenge(), japanese.hiragana)
262
263 self.mousehood.stopChallenging()
264 self.assertIdentical(self.mousehood.getCurrentChallenge(), None)
265
266
267 def test_vetteChallengeResponse(self):
268 """
269 Test that the correct response to the current challenge is accepted by
270 the mouse.
271 """
272 self.mousehood.startChallenging()
273 self.reactorTime.advance(self.mousehood.challengeInterval)
274
275 romaji = japanese.hiragana[self.mousehood.getCurrentChallenge()]
276 self.failUnless(self.mousehood.vetteChallengeResponse(romaji))
277
278 for romaji in japanese.hiragana.values():
279 if romaji != japanese.hiragana[self.mousehood.getCurrentChallenge()]:
280 self.failIf(self.mousehood.vetteChallengeResponse(romaji))
281
282
283 def test_respondToChallengeCorrectly(self):
284 """
285 Test that when a correct response is received, the current challenge is
286 expired and the mouse salutes you.
287 """
288 self.mousehood.startChallenging()
289 self.reactorTime.advance(self.mousehood.challengeInterval)
290
291 correctResponse = japanese.hiragana[
292 self.mousehood.getCurrentChallenge()]
293
294 self.mousehood.responseReceived(self.player, correctResponse)
295 self.reactorTime.advance(0)
296
297 self.assertIdentical(self.mousehood.getCurrentChallenge(), None)
298
299 self.assertEquals(len(self.playerIntelligence.concepts), 2)
300 c = self.playerIntelligence.concepts[1]
301 self.assertEquals(
302 commandutils.flatten(c.plaintext(self.player)),
303 u"%s salutes you!\n" % (self.mouseName,))
304
305
306 def test_respondToChallengeInorrectly(self):
307 """
308 Test that when an incorrect response is received, the current challenge
309 is not expired and the mouse bites you.
310 """
311 self.mousehood.startChallenging()
312 self.reactorTime.advance(self.mousehood.challengeInterval)
313
314 correctResponse = japanese.hiragana[
315 self.mousehood.getCurrentChallenge()]
316
317 for ch in japanese.hiragana.values():
318 if ch != correctResponse:
319 self.mousehood.responseReceived(self.player, ch)
320 break
321 else:
322 self.fail("Buggy test")
323
324 self.reactorTime.advance(0)
325
326 self.assertIn(self.mousehood.getCurrentChallenge(),
327 japanese.romajiToHiragana[correctResponse])
328
329 self.assertEquals(len(self.playerIntelligence.concepts), 2)
330 c = self.playerIntelligence.concepts[1]
331 self.assertEquals(
332 commandutils.flatten(c.plaintext(self.player)),
333 u"%s bites you!\n" % (self.mouseName,))
334
335
336 def test_playerSaysCorrectThing(self):
337 """
338 Test that when someone gives voice to the correct response to a mouse's
339 current challenge, the mouse acknowledges this with a salute.
340 """
341 self.mousehood.startChallenging()
342 self.reactorTime.advance(self.mousehood.challengeInterval)
343 action.Say().do(
344 # http://divmod.org/trac/ticket/2917
345 iimaginary.IActor(self.player),
346 None,
347 japanese.hiragana[self.mousehood.getCurrentChallenge()])
348
349 self.assertIdentical(self.mousehood.getCurrentChallenge(), None)
350 self.reactorTime.advance(0)
351
352 self.assertEquals(len(self.playerIntelligence.concepts), 3)
353 c = self.playerIntelligence.concepts[2]
354 self.assertEquals(
355 commandutils.flatten(c.plaintext(self.player)),
356 u"%s salutes you!\n" % (self.mouseName,))
357
358
359 def test_playerSaysIncorrectThing(self):
360 """
361 Test that when someone gives voice to the correct response to a mouse's
362 current challenge, the mouse acknowledges this with a salute.
363 """
364 self.mousehood.startChallenging()
365 self.reactorTime.advance(self.mousehood.challengeInterval)
366
367 action.Say().do(
368 # http://divmod.org/trac/ticket/2917
369 iimaginary.IActor(self.player), None, u"lolololo pew")
370
371 self.failIfIdentical(self.mousehood.getCurrentChallenge(), None)
372 self.reactorTime.advance(0)
373
374 self.assertEquals(len(self.playerIntelligence.concepts), 3)
375 c = self.playerIntelligence.concepts[2]
376 self.assertEquals(
377 commandutils.flatten(c.plaintext(self.player)),
378 u"%s bites you!\n" % (self.mouseName,))
379
380
381 def test_activationUsesReactorScheduling(self):
382 """
383 Test that the default scheduler of the mouse is the Twisted
384 reactor, since that is the scheduler that needs to be used
385 with the actual Imaginary server.
386 """
387 deletions = []
388 ref = weakref.ref(self.mousehood, deletions.append)
389 # This is a hack to reload the mouse since it gets its
390 # _callLater set in setUp.
391 del self.mouse
392 del self.mouseActor
393 del self.mousehood
394 self.assertEquals(deletions, [ref])
395 mousehood = self.store.findUnique(mice.HiraganaMouse)
396 from twisted.internet import reactor
397 self.assertEquals(mousehood._callLater, reactor.callLater)
398
399
400
401class HiraganaMouseCommandTestCase(commandutils.CommandTestCaseMixin, unittest.TestCase):
402 """
403 H-mouse tests which use the command system.
404 """
405
406 mouseName = u"\N{KATAKANA LETTER PI}\N{KATAKANA LETTER SMALL YU}"
407 hiraganaCharacterPattern = u"'[" + u''.join(japanese.hiragana.keys()) + u"]'"
408 speechPattern = mouseName + u" says, " + hiraganaCharacterPattern
409
410 def test_oneManEnters(self):
411 """
412 Test that when a fellow jaunts into a venue inhabited by a mouse of the
413 Nipponese persuasion, a hiragana allocution follows.
414 """
415 clock = task.Clock()
416
417 closetContainer = commandutils.createLocation(
418 self.store, u"Closet", None)
419 closet = closetContainer.thing
420
421 mouse = mice.createHiraganaMouse(
422 store=self.store,
423 name=self.mouseName,
424 proper=True)
425 mouseActor = iimaginary.IActor(mouse)
426 mousehood = mouseActor.getIntelligence()
427 mousehood._callLater = clock.callLater
428 mouse.moveTo(closet)
429
430 objects.Exit.link(self.location, closet, u"north")
431
432 self._test(
433 "north",
434 [commandutils.E("[ Closet ]"),
435 commandutils.E("( south )"),
436 commandutils.E(u"Here, you see " + self.mouseName + u".")],
437 ["Test Player leaves north."])
438
439 clock.advance(mousehood.challengeInterval)
440
441 self._test(None, [self.speechPattern])
442
443
444 def test_creation(self):
445 """
446 Test the creation of a hiragana-speaking mouse using the thing creation
447 plugin system.
448 """
449 self._test(
450 u"create the 'hiragana mouse' named " + self.mouseName,
451 [commandutils.E(u"You create " + self.mouseName + u".")],
452 [commandutils.E(u"Test Player creates %s." % (self.mouseName,))])
453
454 for thing in self.location.findProviders(iimaginary.IThing, 0):
455 if thing.name == self.mouseName:
456 break
457 else:
458 self.fail("Could not find the mouse! Test bug.")
459
460 clock = task.Clock()
461 jimhood = iimaginary.IActor(thing).getIntelligence()
462 jimhood._callLater = clock.callLater
463
464 self._test(
465 u"drop " + self.mouseName,
466 [commandutils.E(u"You drop %s." % (self.mouseName,))],
467 [commandutils.E(u"Test Player drops %s." % (self.mouseName,))])
468
469 clock.advance(jimhood.challengeInterval)
470
471 self._test(
472 None,
473 [self.speechPattern],
474 [self.speechPattern])
4750
=== removed file 'Imaginary/ExampleGame/examplegame/test/test_mice.py'
--- Imaginary/ExampleGame/examplegame/test/test_mice.py 2013-07-03 23:50:20 +0000
+++ Imaginary/ExampleGame/examplegame/test/test_mice.py 1970-01-01 00:00:00 +0000
@@ -1,155 +0,0 @@
1
2from twisted.trial import unittest
3from twisted.internet import task
4
5from axiom import store
6
7from imaginary import iimaginary, events, objects
8from imaginary.test import commandutils
9
10from examplegame import mice
11
12
13class IntelligenceTestCase(unittest.TestCase):
14 def setUp(self):
15 self.store = store.Store()
16
17 self.locationContainer = commandutils.createLocation(
18 self.store, u"Place", None)
19 self.location = self.locationContainer.thing
20
21 self.alice = objects.Thing(store=self.store, name=u"Alice")
22 self.actor = objects.Actor.createFor(self.alice)
23
24 self.alice.moveTo(self.location)
25
26 self.intelligence = commandutils.MockIntelligence(store=self.store)
27 self.actor.setEnduringIntelligence(self.intelligence)
28
29
30 def test_intelligenceReceivesEvent(self):
31 """
32 Enduring intelligences should receive events.
33 """
34 evt = events.Success(
35 location=self.location,
36 otherMessage=u"Hello, how are you?")
37
38 self.actor.send(evt)
39 self.assertEquals(self.intelligence.concepts, [evt])
40
41
42 def test_persistentIntelligence(self):
43 """
44 Whitebox test that enduring intelligencii are actually persistent.
45 """
46 # TB <---- THAT MEANS IT'S TRANSLUCENT
47 self.assertIdentical(
48 self.store.findUnique(
49 objects.Actor,
50 objects.Actor._enduringIntelligence == self.intelligence),
51 self.actor)
52
53
54
55class MouseTestCase(unittest.TestCase):
56 def setUp(self):
57 self.store = store.Store()
58
59 self.clock = objects.Thing(store=self.store, name=u"Clock")
60 self.clockContainer = objects.Container.createFor(self.clock, capacity=10)
61
62 self.mouse = mice.createMouse(store=self.store, name=u"Squeaker McSqueakenson")
63 self.mouseActor = iimaginary.IActor(self.mouse)
64 self.mousehood = self.mouseActor.getIntelligence()
65 self.mouse.moveTo(self.clock)
66
67 self.player = objects.Thing(store=self.store, name=u"Mean Old Man")
68 self.playerActor = objects.Actor.createFor(self.player)
69 self.playerIntelligence = commandutils.MockIntelligence(
70 store=self.store)
71 self.playerActor.setEnduringIntelligence(self.playerIntelligence)
72
73 self.player.moveTo(self.clock)
74
75
76 def test_mouseSqueaksAtIntruders(self):
77 """
78 When a mean old man walks into the mouse's clock, the mouse will squeak
79 ruthlessly.
80 """
81 clock = task.Clock()
82 self.mousehood._callLater = clock.callLater
83 evt = events.ArrivalEvent(actor=self.player)
84 self.mouseActor.send(evt)
85
86 self.assertEquals(len(self.playerIntelligence.concepts), 0)
87 clock.advance(0)
88
89 self.assertEquals(len(self.playerIntelligence.concepts), 1)
90 event = self.playerIntelligence.concepts[0]
91 self.assertEquals(
92 commandutils.flatten(event.otherMessage.plaintext(self.player)),
93 u"SQUEAK!")
94
95
96 def test_mouseCanSqueak(self):
97 events.runEventTransaction(self.store, self.mousehood.squeak)
98 self.assertEquals(len(self.playerIntelligence.concepts), 1)
99 event = self.playerIntelligence.concepts[0]
100 self.assertEquals(
101 commandutils.flatten(event.otherMessage.plaintext(self.player)),
102 u"SQUEAK!")
103
104
105 def test_mouseActivation(self):
106 """
107 Activating a mouse should set the scheduling mechanism to the
108 reactor's.
109 """
110 from twisted.internet import reactor
111 self.assertEquals(self.mousehood._callLater, reactor.callLater)
112
113
114
115class MouseReactionTestCase(commandutils.CommandTestCaseMixin,
116 unittest.TestCase):
117 def testCreation(self):
118 """
119 Test that a mouse can be created with the create command.
120 """
121 self._test(
122 "create the mouse named squeaker",
123 ['You create squeaker.'],
124 ['Test Player creates squeaker.'])
125
126 [mouse] = list(self.playerContainer.getContents())
127 self.failUnless(isinstance(iimaginary.IActor(mouse).getIntelligence(), mice.Mouse))
128
129
130 def testSqueak(self):
131 """
132 Test that when someone walks into a room with a mouse, the mouse
133 squeaks and the person who walked in hears it.
134 """
135 mouse = mice.createMouse(store=self.store, name=u"squeaker")
136 clock = task.Clock()
137 intelligence = iimaginary.IActor(mouse).getIntelligence()
138 intelligence._callLater = clock.callLater
139
140 elsewhere = commandutils.createLocation(
141 self.store, u"Mouse Hole", None).thing
142
143 objects.Exit.link(self.location, elsewhere, u"south")
144
145 mouse.moveTo(elsewhere)
146
147 self._test(
148 "south",
149 [commandutils.E("[ Mouse Hole ]"),
150 commandutils.E("( north )"),
151 commandutils.E("Here, you see a squeaker.")],
152 ['Test Player leaves south.'])
153
154 clock.advance(0)
155 self._test(None, ["SQUEAK!"])
1560
=== removed file 'Imaginary/ExampleGame/examplegame/test/test_quiche.py'
--- Imaginary/ExampleGame/examplegame/test/test_quiche.py 2009-06-29 04:03:17 +0000
+++ Imaginary/ExampleGame/examplegame/test/test_quiche.py 1970-01-01 00:00:00 +0000
@@ -1,93 +0,0 @@
1from twisted.trial import unittest
2
3from imaginary import objects, iimaginary
4from imaginary.test import commandutils
5
6from examplegame import quiche
7
8
9class VendingTest(commandutils.CommandTestCaseMixin, unittest.TestCase):
10 def testTheyExist(self):
11 self._test("create the 'vending machine' named vendy",
12 ["You create vendy."],
13 ["Test Player creates vendy."])
14
15
16 def testPopulateVendingMachine(self):
17 self._test("create the 'vending machine' named vendy",
18 ["You create vendy."],
19 ["Test Player creates vendy."])
20
21 self._test("create a quiche named quiche",
22 ["You create a quiche."],
23 ["Test Player creates a quiche."])
24
25 self._test("open vendy",
26 ["You open vendy."],
27 ["Test Player opens vendy."])
28
29 self._test("put quiche in vendy",
30 ["You put the quiche in vendy."],
31 ["Test Player puts a quiche in vendy."])
32
33
34 def testBuyingQuiche(self):
35 self._test("create the 'vending machine' named vendy",
36 ["You create vendy."],
37 ["Test Player creates vendy."])
38
39 self._test("drop vendy",
40 ["You drop vendy."],
41 ["Test Player drops vendy."])
42
43 self._test("create a quiche named quiche",
44 ["You create a quiche."],
45 ["Test Player creates a quiche."])
46
47 self._test("open vendy",
48 ["You open vendy."],
49 ["Test Player opens vendy."])
50
51 self._test("put quiche in vendy",
52 ["You put the quiche in vendy."],
53 ["Test Player puts a quiche in vendy."])
54
55 for i in range(5):
56 self._test("create the quarter named quarter%s " % i,
57 ["You create quarter%s." % i],
58 ["Test Player creates quarter%s." % i])
59
60 for i in range(4):
61 self._test("put quarter%i in vendy" % i,
62 ["You put quarter%s in vendy." % i],
63 ["Test Player puts quarter%s in vendy." % i])
64
65 self._test("put quarter4 in vendy",
66 ["You put quarter4 in vendy.",
67 "Vendy thumps loudly and spits out a quiche onto the ground."],
68 ["Test Player puts quarter4 in vendy.",
69 "Vendy thumps loudly and spits out a quiche onto the ground."])
70
71
72 def testProgrammaticQuichePurchase(self):
73 location = objects.Thing(store=self.store, name=u"room")
74 icloc = objects.Container.createFor(location, capacity=500)
75
76 vm = quiche.createVendingMachine(store=self.store, name=u"Vendy", description=u"VEEEENDYYYYY")
77 vm.moveTo(location)
78
79 icvm = iimaginary.IContainer(vm)
80 icvm.closed = False
81 theQuiche = quiche.createQuiche(store=self.store, name=u"quiche")
82 icvm.add(theQuiche)
83 icvm.closed = True
84
85 for i in range(4):
86 quarter = quiche.createCoin(store=self.store, name=u"quarter%s" % (i,))
87 icvm.add(quarter)
88
89 quarter = quiche.createCoin(store=self.store, name=u"quarter4")
90 icvm.add(quarter)
91
92 self.failUnless(icloc.contains(theQuiche))
93
940
=== removed file 'Imaginary/ExampleGame/examplegame/test/test_squeaky.py'
--- Imaginary/ExampleGame/examplegame/test/test_squeaky.py 2009-08-17 02:40:03 +0000
+++ Imaginary/ExampleGame/examplegame/test/test_squeaky.py 1970-01-01 00:00:00 +0000
@@ -1,51 +0,0 @@
1
2from twisted.trial.unittest import TestCase
3
4from imaginary.test.commandutils import CommandTestCaseMixin
5
6from imaginary.objects import Thing, Container
7
8from examplegame.squeaky import Squeaker
9
10class SqueakTest(CommandTestCaseMixin, TestCase):
11 """
12 Squeak Test.
13 """
14
15 def setUp(self):
16 """
17 Set Up.
18 """
19 CommandTestCaseMixin.setUp(self)
20 self.squeaker = Thing(store=self.store, name=u"squeaker")
21 self.squeaker.moveTo(self.location)
22 self.squeakification = Squeaker.createFor(self.squeaker)
23
24
25 def test_itSqueaks(self):
26 """
27 Picking up a squeaky thing makes it emit a squeak.
28 """
29 self.assertCommandOutput(
30 "take squeaker",
31 ["You take a squeaker.",
32 "A squeaker emits a faint squeak."],
33 ["Test Player takes a squeaker.",
34 "A squeaker emits a faint squeak."])
35
36
37 def test_squeakyContainer(self):
38 """
39 If a container is squeaky, that shouldn't interfere with its function
40 as a container. (i.e. let's make sure that links keep working even
41 though we're using an annotator here.)
42 """
43 cont = Container.createFor(self.squeaker)
44
45 mcguffin = Thing(store=self.store, name=u"mcguffin")
46 mcguffin.moveTo(cont)
47
48 self.assertCommandOutput(
49 "take mcguffin from squeaker",
50 ["You take a mcguffin from the squeaker."],
51 ["Test Player takes a mcguffin from the squeaker."])
520
=== removed file 'Imaginary/ExampleGame/examplegame/test/test_tether.py'
--- Imaginary/ExampleGame/examplegame/test/test_tether.py 2009-08-17 02:40:03 +0000
+++ Imaginary/ExampleGame/examplegame/test/test_tether.py 1970-01-01 00:00:00 +0000
@@ -1,96 +0,0 @@
1
2from twisted.trial.unittest import TestCase
3
4from imaginary.test.commandutils import CommandTestCaseMixin, E
5
6from imaginary.objects import Thing, Container, Exit
7from imaginary.garments import Garment
8
9from examplegame.furniture import Chair
10from examplegame.tether import Tether
11
12class TetherTest(CommandTestCaseMixin, TestCase):
13 """
14 A test for tethering an item to its location, such that a player who picks
15 it up can't leave until they drop it.
16 """
17
18 def setUp(self):
19 """
20 Tether a ball to the room.
21 """
22 CommandTestCaseMixin.setUp(self)
23 self.ball = Thing(store=self.store, name=u'ball')
24 self.ball.moveTo(self.location)
25 self.tether = Tether.createFor(self.ball, to=self.location)
26 self.otherPlace = Thing(store=self.store, name=u'elsewhere')
27 Container.createFor(self.otherPlace, capacity=1000)
28 Exit.link(self.location, self.otherPlace, u'north')
29
30
31 def test_takeAndLeave(self):
32 """
33 You can't leave the room if you're holding the ball that's tied to it.
34 """
35 self.assertCommandOutput(
36 "take ball",
37 ["You take a ball."],
38 ["Test Player takes a ball."])
39 self.assertCommandOutput(
40 "go north",
41 ["You can't move, you're still holding a ball."],
42 ["Test Player struggles with a ball."])
43 self.assertCommandOutput(
44 "drop ball",
45 ["You drop the ball."],
46 ["Test Player drops a ball."])
47 self.assertCommandOutput(
48 "go north",
49 [E("[ elsewhere ]"),
50 E("( south )"),
51 ""],
52 ["Test Player leaves north."])
53
54
55 def test_allTiedUp(self):
56 """
57 If you're tied to a chair, you can't leave.
58 """
59 chairThing = Thing(store=self.store, name=u'chair')
60 chairThing.moveTo(self.location)
61 chair = Chair.createFor(chairThing)
62 self.assertCommandOutput("sit chair",
63 ["You sit in the chair."],
64 ["Test Player sits in the chair."])
65 Tether.createFor(self.player, to=chairThing)
66 self.assertCommandOutput(
67 "stand up",
68 ["You can't move, you're tied to a chair."],
69 ["Test Player struggles."])
70
71
72 def test_tetheredClothing(self):
73 """
74 Clothing that is tethered will also prevent movement if you wear it.
75
76 This isn't just simply a test for clothing; it's an example of
77 integrating with a foreign system which doesn't know about tethering,
78 but can move objects itself.
79
80 Tethering should I{not} have any custom logic related to clothing to
81 make this test pass; if it does get custom clothing code for some
82 reason, more tests should be added to deal with other systems that do
83 not take tethering into account (and vice versa).
84 """
85 Garment.createFor(self.ball, garmentDescription=u"A lovely ball.",
86 garmentSlots=[u"head"])
87 self.assertCommandOutput(
88 "wear ball",
89 ["You put on the ball."],
90 ["Test Player puts on a ball."])
91 self.assertCommandOutput(
92 "go north",
93 ["You can't move, you're still holding a ball."],
94 ["Test Player struggles with a ball."])
95
96
970
=== removed file 'Imaginary/ExampleGame/examplegame/tether.py'
--- Imaginary/ExampleGame/examplegame/tether.py 2009-08-17 02:40:03 +0000
+++ Imaginary/ExampleGame/examplegame/tether.py 1970-01-01 00:00:00 +0000
@@ -1,121 +0,0 @@
1# -*- test-case-name: examplegame.test.test_tether -*-
2
3"""
4A simplistic implementation of tethering, which demonstrates how to prevent
5someone from moving around.
6
7This implementation is somewhat limited, as it assumes that tethered objects
8can only be located in players' inventories and on the ground. It also makes
9several assumptions about who is actually doing the moving in moveTo; in order
10to be really correct, the implementation of movement needs to relay more
11information about what is moving and how.
12"""
13
14from zope.interface import implements
15
16from axiom.item import Item
17from axiom.attributes import reference
18
19from imaginary.iimaginary import IMovementRestriction, IActor
20from imaginary.eimaginary import ActionFailure
21from imaginary.events import ThatDoesntWork
22from imaginary.enhancement import Enhancement
23from imaginary.objects import Thing
24
25
26class Tether(Item, Enhancement):
27 """
28 I am a force that binds two objects together.
29
30 Right now this force isn't symmetric; the idea is that the thing that we
31 are tethered 'to' is immovable for some other reason. This is why we're in
32 the example rather than a real robust piece of game-library functionality
33 in imaginary proper.
34
35 The C{thing} that we are installed on is prevented from moving more than a
36 certain distance away from the thing it is tethered C{to}.
37
38 This is accomplished by preventing movement of the object's container;
39 i.e. if you pick up a ball that is tied to the ground, you can't move until
40 you drop it.
41 """
42
43 thing = reference(reftype=Thing,
44 whenDeleted=reference.CASCADE,
45 allowNone=False)
46
47 # XXX 'thing' and 'to' should be treated more consistently, or at least the
48 # differences between them explained officially.
49 to = reference(reftype=Thing,
50 whenDeleted=reference.CASCADE,
51 allowNone=False)
52
53 implements(IMovementRestriction)
54
55 powerupInterfaces = [IMovementRestriction]
56
57 def movementImminent(self, movee, destination):
58 """
59 The object which is tethered is trying to move somewhere. If it has an
60 IActor, assume that it's a player trying to move on its own, and emit
61 an appropriate message.
62
63 Otherwise, assume that it is moving *to* an actor, and install a
64 L{MovementBlocker} on that actor.
65 """
66 # There isn't enough information provided to moveTo just yet; we need
67 # to know who is doing the moving. In the meanwhile, if you have an
68 # actor, we'll assume you're a player.
69 if IActor(movee, None) is not None:
70 raise ActionFailure(
71 ThatDoesntWork(
72 actor=self.thing,
73 actorMessage=[u"You can't move, you're tied to ",
74 self.to,
75 "."],
76 otherMessage=[self.thing, u' struggles.']))
77 MovementBlocker.destroyFor(self.thing.location)
78 if self.to != destination:
79 MovementBlocker.createFor(destination, tether=self)
80
81 return False
82
83
84class MovementBlocker(Item, Enhancement):
85 """
86 A L{MovementBlocker} is an L{Enhancement} which prevents the movement of a
87 player holding a tethered object.
88 """
89 implements(IMovementRestriction)
90
91 powerupInterfaces = [IMovementRestriction]
92
93 thing = reference(
94 doc="""
95 The L{Thing} whose movement is blocked.
96 """, reftype=Thing, allowNone=False,
97 whenDeleted=reference.CASCADE)
98
99 tether = reference(
100 doc="""
101 The L{Tether} ultimely responsible for blocking movement.
102 """,
103 reftype=Tether, allowNone=False,
104 whenDeleted=reference.CASCADE)
105
106
107 def movementImminent(self, movee, destination):
108 """
109 The player this blocker is installed on is trying to move. Assume that
110 they are trying to move themselves (via a 'go' action) and prevent it
111 by raising an L{ActionFailure} with an appropriate error message for
112 the player.
113 """
114 raise ActionFailure(
115 ThatDoesntWork(
116 actor=self.thing,
117 actorMessage=
118 [u"You can't move, you're still holding ",
119 self.tether.thing,u'.'],
120 otherMessage=
121 [self.thing, u' struggles with ', self.tether.thing,u'.']))
1220
=== removed directory 'Imaginary/ExampleGame/imaginary'
=== removed directory 'Imaginary/ExampleGame/imaginary/plugins'
=== removed file 'Imaginary/ExampleGame/imaginary/plugins/monsters.py'
--- Imaginary/ExampleGame/imaginary/plugins/monsters.py 2007-08-17 04:46:38 +0000
+++ Imaginary/ExampleGame/imaginary/plugins/monsters.py 1970-01-01 00:00:00 +0000
@@ -1,6 +0,0 @@
1
2from imaginary.creation import CreationPluginHelper
3from examplegame.mice import createMouse, createHiraganaMouse
4
5mouse = CreationPluginHelper(u'mouse', createMouse)
6hiraganaMouse = CreationPluginHelper(u'hiragana mouse', createHiraganaMouse)
70
=== removed file 'Imaginary/ExampleGame/imaginary/plugins/quiche.py'
--- Imaginary/ExampleGame/imaginary/plugins/quiche.py 2007-08-17 04:46:38 +0000
+++ Imaginary/ExampleGame/imaginary/plugins/quiche.py 1970-01-01 00:00:00 +0000
@@ -1,9 +0,0 @@
1# -*- test-case-name: examplegame.test.test_vending -*-
2
3from imaginary.creation import CreationPluginHelper
4from examplegame.quiche import createQuiche, createCoin, createVendingMachine
5
6quichePlugin = CreationPluginHelper('quiche', createQuiche)
7vendingPlugin = CreationPluginHelper('vending machine', createVendingMachine)
8quarterPlugin = CreationPluginHelper('quarter', createCoin)
9
100
=== removed file 'Imaginary/LICENSE'
--- Imaginary/LICENSE 2006-02-26 02:37:39 +0000
+++ Imaginary/LICENSE 1970-01-01 00:00:00 +0000
@@ -1,20 +0,0 @@
1Copyright (c) 2005 Divmod Inc.
2
3Permission is hereby granted, free of charge, to any person obtaining
4a copy of this software and associated documentation files (the
5"Software"), to deal in the Software without restriction, including
6without limitation the rights to use, copy, modify, merge, publish,
7distribute, sublicense, and/or sell copies of the Software, and to
8permit persons to whom the Software is furnished to do so, subject to
9the following conditions:
10
11The above copyright notice and this permission notice shall be
12included in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
210
=== removed file 'Imaginary/MANIFEST.in'
--- Imaginary/MANIFEST.in 2008-07-16 19:12:33 +0000
+++ Imaginary/MANIFEST.in 1970-01-01 00:00:00 +0000
@@ -1,6 +0,0 @@
1include NAME.txt
2include DEPS.txt
3include NEWS.txt
4include LICENSE
5graft imaginary
6graft axiom
70
=== removed file 'Imaginary/NAME.txt'
--- Imaginary/NAME.txt 2006-02-26 02:37:39 +0000
+++ Imaginary/NAME.txt 1970-01-01 00:00:00 +0000
@@ -1,10 +0,0 @@
1
2See: http://achewood.com/index.php?date=04272005
3
4Imaginary numbers are so named not because they are fictitious, but because
5they are along a different axis from real numbers.
6
7Divmod Imaginary is a simulationists take on the realm of role playing,
8interactive fiction, and multiplayer dungeons. It incorporates gameplay
9features from each area while attempting to provide a richer environment
10than is generally available from existing systems.
110
=== removed file 'Imaginary/NEWS.txt'
--- Imaginary/NEWS.txt 2009-11-30 01:08:55 +0000
+++ Imaginary/NEWS.txt 1970-01-01 00:00:00 +0000
@@ -1,36 +0,0 @@
10.0.5 (2009-11-25):
2 - Remove the dead imaginary.objects.Thing.locate method
3 - Introduce an interface intended to be provided by administrative actors
4 and require it for the "illuminate" command.
5 - Remove the custom SSH server and instead plug in to the Mantissa SSH
6 application server.
7 - Remove all uses of "axiom.dependency.installOn".
8 - Fix a bug which caused newly created avatars to not be announced when
9 they arrived at the starting location.
10 - When presenting an ambiguity error to an actor, enumerate the possible
11 resolutions.
12 - Fix certain terminal handling issues with east asian characters.
13
140.0.4 (2008-08-12):
15
16 - A "plain" Thing type has now been added and is available to the
17 "create" command.
18 - "scrutinize" now works when the target is not a container.
19 - a "list thing types" command has been added, to allow viewing
20 types of things that can be created.
21 - "create" now allows users to specify if a thing is considered a
22 common or proper noun.
23 - Improved grammar in action text for various verbs.
24 - Added a "set" action for changing thing attributes.
25 - Changed noun resolution to match substrings and to match
26 case-insensitively.
27
280.0.3 (2007-01-23):
29 - Work with Axiom dependency api
30
310.0.2 (2006-09-20):
32 - Some changes
33
340.0.1 (2006-06-16):
35 - Imported from Pottery repository
36 - Imported from Imagination repository
370
=== removed file 'Imaginary/README.txt'
--- Imaginary/README.txt 2009-06-26 20:38:23 +0000
+++ Imaginary/README.txt 1970-01-01 00:00:00 +0000
@@ -1,98 +0,0 @@
1
2Imaginary is an experimental simulation-construction toolkit.
3
4Be warned! We aren't kidding when we say "experimental". Many features are
5not implemented yet and documentation is incomplete. We think there are some
6pretty cool ideas here, but if you are intending to use this system, be
7prepared to participate heavily in its development.
8
9This document is mainly concerned with getting an Imaginary server up and
10running, to the point where you can edit the code and see things change. Some
11familiarity with Python, Twisted, Nevow, Axiom, and Mantissa are all helpful,
12but we will try to make sure they aren't really required just to get started.
13
14While we have tried to make it possible to get a taste of what is possible
15here, if you want to make any serious progress, you will want to join the IRC
16channel "#imagination" on chat.freenode.net and start asking questions. If you
17are curious about what needs to be done, have a look here:
18
19 http://tinyurl.com/2tuo9o
20
21The first step in configuring a new installation of Imaginary will normally be
22creating a new Mantissa database.
23
24(This is assuming you have already set up Combinator, installed Twisted, and
25all necessary Divmod dependencies. If not, see
26http://divmod.org/trac/wiki/CombinatorTutorial for more information.)
27
28First run the following command:
29
30 axiomatic mantissa
31
32And answer the prompts appropriately. Please take note of the new password you
33enter for the "admin" user, as you will need it in a few steps.
34
35This should create a directory called "mantissa.axiom", containing the server
36for a web interface that will allow you to install "offerings" (plugins for
37Mantissa, in this case, Imaginary), create users, and grant them
38privileges. You can start this webserver with the following command: (the -n
39option will run it in the foreground on the current terminal)
40
41 axiomatic start -n
42
43You should now be able to access this server at http://localhost:8080 in your
44web browser.
45
46Click the "Sign In" link in the upper right hand corner, and log in as "admin"
47with the password you chose previously while configuring Mantissa.
48
49If you logged in successfully, you should now be presented with a list of
50"offerings" that can be installed. Click on "Imaginary" to install it.
51
52Next, mouse over the "Admin" menu at the upper left of the screen and then
53click on the "Products" sub-menu.
54
55In the "Installable Powerups" section, check the box corresponding to
56"Imaginary Game". Then click the "Installable Powerups" button below. You
57will hopefully be presented with a green confirmation dialog to confirm your
58success. This creates a "product" which can be given to users.
59
60Mouse over the "Admin" menu again and click "Local Users". You should be
61presented with a page including a table of users - probably with only the
62"admin" user in it. Click the word "Endow" in the "Actions" column of that
63table, then select the product with "ImaginaryApp" in it (again, this is
64probably the only one presented) and click the "Installproducton
65admin@localhost" button. You should again be presented with a green
66confirmation dialog. Hooray!
67
68Ideally, you will now be able to ssh into your Imaginary server. In a new
69terminal, ssh to localhost on the Mantissa server's SSH port (by default, this
70is 8022). For example:
71
72 ssh -p 8022 admin@localhost@localhost
73
74Note the odd username/host specifier - "admin@localhost" is the user, and the
75last "localhost" specifies the host to connect to.
76
77Log in with the same password you used to log in to the web interface. You
78should be presented with a screen including several options, one of which is
79"imaginary". Use tab to highlight that option (the highlighted option should
80appear red), then hit enter to select it.
81
82At the Imaginary character prompt, choose to create a new character; enter a
83new username (e.g. not "admin"). You will then join the game as that new
84character.
85
86Once in the game, you should see a row of dashes along the bottom of the
87display. To confirm your new MUDness, try typing "look" and hit enter; You
88should see some indication of the generic place that you are in, the available
89exits, and other players in the area, though initially you won't see much more
90than "[The Place]".
91
92You can enter "actions" for a list of actions, and use "help" along with one of
93them ("help dig") for specific information on how to use them. You can even log
94in via other telnet windows, create additional accounts, and interact with your
95initial user (for example, beating them to death with "hit").
96
97When you've tired of self-abuse, you can stop your Imaginary server by hitting
98control-c in the terminal where you ran "axiomatic start -n".
990
=== removed directory 'Imaginary/axiom'
=== removed directory 'Imaginary/axiom/plugins'
=== removed file 'Imaginary/axiom/plugins/imaginaryversion.py'
--- Imaginary/axiom/plugins/imaginaryversion.py 2008-07-16 19:12:33 +0000
+++ Imaginary/axiom/plugins/imaginaryversion.py 1970-01-01 00:00:00 +0000
@@ -1,12 +0,0 @@
1# Copyright 2008 Divmod, Inc.
2# See LICENSE file for details
3
4"""
5Register an Axiom version plugin for Imaginary.
6"""
7
8from zope.interface import directlyProvides
9from twisted.plugin import IPlugin
10from axiom.iaxiom import IVersion
11from imaginary import version
12directlyProvides(version, IPlugin, IVersion)
130
=== removed directory 'Imaginary/imaginary'
=== removed file 'Imaginary/imaginary/__init__.py'
--- Imaginary/imaginary/__init__.py 2009-06-29 04:03:17 +0000
+++ Imaginary/imaginary/__init__.py 1970-01-01 00:00:00 +0000
@@ -1,19 +0,0 @@
1# -*- test-case-name: imaginary,examplegame -*-
2
3"""
4Virtual simulation framework.
5"""
6
7from imaginary._version import version
8version # exported
9
10# Verbs are only registered when they are imported, and important verbs are
11# found in the following modules:
12from imaginary import action, creation
13action # exported
14creation # exported
15
16
17# Ideally there would be a nice, passive way to register verbs which would only
18# load them as necessary rather than forcing the entire package to get
19# imported, but this will work okay for now.
200
=== removed file 'Imaginary/imaginary/_version.py'
--- Imaginary/imaginary/_version.py 2009-11-30 01:08:55 +0000
+++ Imaginary/imaginary/_version.py 1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
1# This is an auto-generated file. Use Epsilon/bin/release-divmod to update.
2from twisted.python import versions
3version = versions.Version(__name__[:__name__.rfind('.')], 0, 0, 5)
40
=== removed file 'Imaginary/imaginary/action.py'
--- Imaginary/imaginary/action.py 2013-09-22 10:01:56 +0000
+++ Imaginary/imaginary/action.py 1970-01-01 00:00:00 +0000
@@ -1,1299 +0,0 @@
1# -*- test-case-name: imaginary.test.test_actions -*-
2
3import time, random, operator
4import pprint
5
6from zope.interface import implements
7
8from twisted.python import log, filepath
9from twisted.internet import defer
10
11from axiom import iaxiom
12from axiom.attributes import AND
13
14import imaginary.plugins
15from imaginary import (iimaginary, eimaginary, iterutils, events,
16 objects, text as T, language, pyparsing)
17from imaginary.world import ImaginaryWorld
18from imaginary.idea import (
19 CanSee, Proximity, ProviderOf, Named, Traversability)
20
21## Hacks because pyparsing doesn't have fantastic unicode support
22_quoteRemovingQuotedString = pyparsing.quotedString.copy()
23_quoteRemovingQuotedString.setParseAction(pyparsing.removeQuotes)
24
25class UnicodeWord(pyparsing.Token):
26 def parseImpl(self, instring, loc, doActions=True):
27 maxLoc = len(instring)
28 while loc < maxLoc and instring[loc].isspace():
29 loc += 1
30 start = loc
31 while loc < maxLoc and not instring[loc].isspace():
32 loc += 1
33 end = loc
34 return end, instring[start:end]
35
36
37
38class _ActionType(type):
39 actions = []
40 def __new__(cls, name, bases, attrs):
41 infrastructure = attrs.pop('infrastructure', False)
42 t = super(_ActionType, cls).__new__(cls, name, bases, attrs)
43 if not infrastructure:
44 cls.actions.append(t)
45 return t
46
47
48 def parse(self, player, line):
49 """
50 Parse an action.
51 """
52 for eachActionType in self.actions:
53 try:
54 match = eachActionType.match(player, line)
55 except pyparsing.ParseException:
56 pass
57 else:
58 if match is not None:
59 match = dict(match)
60 for k,v in match.items():
61 if isinstance(v, pyparsing.ParseResults):
62 match[k] = v[0]
63
64 return eachActionType().runEventTransaction(player, line, match)
65 return defer.fail(eimaginary.NoSuchCommand(line))
66
67
68
69class Action(object):
70 """
71 An L{Action} represents an intention of a player to do something.
72 """
73 __metaclass__ = _ActionType
74 infrastructure = True
75
76 actorInterface = iimaginary.IActor
77
78 def runEventTransaction(self, player, line, match):
79 """
80 Take a player, input, and dictionary of parse results, resolve those
81 parse results into implementations of appropriate interfaces in the
82 game world, and execute the actual Action implementation (contained in
83 the 'do' method) in an event transaction.
84
85 This is the top level of action invocation.
86
87 @param player: A L{Thing} representing the actor's body.
88
89 @param line: A unicode string containing the original input
90
91 @param match: A dictionary containing some parse results to pass
92 through to this L{Action}'s C{do} method as keyword arguments.
93
94 @raise eimaginary.AmbiguousArgument: if multiple valid targets are
95 found for an argument.
96 """
97 def thunk():
98 begin = time.time()
99 try:
100 actor = self.actorInterface(player)
101 for (k, v) in match.items():
102 try:
103 objs = self.resolve(player, k, v)
104 except NotImplementedError:
105 pass
106 else:
107 if len(objs) == 1:
108 match[k] = objs[0]
109 elif len(objs) == 0:
110 self.cantFind(player, actor, k, v)
111 else:
112 raise eimaginary.AmbiguousArgument(self, k, v, objs)
113 return self.do(actor, line, **match)
114 finally:
115 end = time.time()
116 log.msg(interface=iaxiom.IStatEvent,
117 stat_actionDuration=end - begin,
118 stat_actionExecuted=1)
119 events.runEventTransaction(player.store, thunk)
120
121
122 def cantFind(self, player, actor, slot, name):
123 """
124 This hook is invoked when a target cannot be found.
125
126 This will delegate to a method like C{self.cantFind_<slot>(actor,
127 name)} if one exists, to determine the error message to show to the
128 actor. It will then raise L{eimaginary.ActionFailure} to stop
129 processing of this action.
130
131 @param player: The L{Thing} doing the searching.
132
133 @type player: L{IThing}
134
135 @param actor: The L{IActor} doing the searching.
136
137 @type actor: L{IActor}
138
139 @param slot: The slot in question.
140
141 @type slot: C{str}
142
143 @param name: The name of the object being searched for.
144
145 @type name: C{unicode}
146
147 @raise eimaginary.ActionFailure: always.
148 """
149 func = getattr(self, "cantFind_"+slot, None)
150 if func:
151 msg = func(actor, name)
152 else:
153 msg = "Who's that?"
154 raise eimaginary.ActionFailure(
155 events.ThatDoesntWork(
156 actorMessage=msg,
157 actor=player))
158
159
160 @classmethod
161 def match(cls, player, line):
162 """
163 Parse the given C{line} using this L{Action} type's pyparsing C{expr}
164 attribute. A C{pyparsing.LineEnd} is appended to C{expr} to avoid
165 accidentally matching a prefix instead of the whole line.
166
167 @return: a list of 2-tuples of all the results of parsing, or None if
168 the expression does not match the given line.
169
170 @param line: a line of user input to be interpreted as an action.
171
172 @see: L{imaginary.pyparsing}
173 """
174 return (cls.expr + pyparsing.LineEnd()).parseString(line)
175
176
177 def do(self, player, line, **slots):
178 """
179 Subclasses override this method to actually perform the action.
180
181 This method is performed in an event transaction, by 'run'.
182
183 NB: The suggested implementation strategy for a 'do' method is to do
184 action-specific setup but then delegate the bulk of the actual logic to
185 a method on a target/tool interface. The 'do' method's job is to
186 select the appropriate methods to invoke.
187
188 @param player: a provider of this L{Action}'s C{actorInterface}.
189
190 @param line: the input string that created this action.
191
192 @param slots: The results of calling C{self.resolve} on each parsing
193 result (described by a setResultsName in C{self.expr}).
194 """
195 raise NotImplementedError("'do' method not implemented")
196
197
198 def resolve(self, player, name, value):
199 """
200 Resolve a given parsed value to a valid action parameter by calling a
201 'resolve_<name>' method on this L{Action} with the given C{player} and
202 C{value}.
203
204 @param player: the L{Thing} attempting to perform this action.
205
206 @type player: L{Thing}
207
208 @param name: the name of the slot being filled. For example, 'target'.
209
210 @type name: L{str}
211
212 @param value: a string representing the value that was parsed. For
213 example, if the user typed 'get fish', this would be 'fish'.
214
215 @return: a value which will be passed as the 'name' parameter to this
216 L{Action}'s C{do} method.
217 """
218 resolver = getattr(self, 'resolve_%s' % (name,), None)
219 if resolver is None:
220 raise NotImplementedError(
221 "Don't know how to resolve %r (%r)" % (name, value))
222 return resolver(player, value)
223
224
225
226def targetString(name):
227 return (
228 _quoteRemovingQuotedString ^
229 UnicodeWord()).setResultsName(name)
230
231
232
233class TargetAction(Action):
234 """
235 Subclass L{TargetAction} to implement an action that acts on a target, like
236 'take foo' or 'eat foo' where 'foo' is the target.
237
238 @cvar targetInterface: the interface which the 'target' parameter to 'do'
239 must provide.
240 """
241
242 infrastructure = True
243
244 targetInterface = iimaginary.IThing
245
246 def targetRadius(self, player):
247 return 2
248
249 def resolve_target(self, player, targetName):
250 return _getIt(player, targetName,
251 self.targetInterface, self.targetRadius(player))
252
253
254
255class ToolAction(TargetAction):
256 """
257 Subclass L{ToolAction} to implement an action that acts on a target by
258 using a tool, like 'unlock door with key', where 'door' is the target and
259 'key' is the tool.
260
261 @cvar toolInterface: the L{zope.interface.Interface} which the 'tool'
262 parameter to 'do' must provide.
263 """
264 infrastructure = True
265
266 toolInterface = iimaginary.IThing
267
268 def toolRadius(self, player):
269 return 2
270
271 def resolve_tool(self, player, toolName):
272 return _getIt(player, toolName,
273 self.toolInterface, self.toolRadius(player))
274
275
276
277def _getIt(player, thingName, iface, radius):
278 return list(player.search(radius, iface, thingName))
279
280
281
282class LookAround(Action):
283 actionName = "look"
284 expr = pyparsing.Literal("look") + pyparsing.StringEnd()
285
286 def do(self, player, line):
287 ultimateLocation = player.thing.location
288 while ultimateLocation.location is not None:
289 ultimateLocation = ultimateLocation.location
290 for visible in player.thing.findProviders(iimaginary.IVisible, 1):
291 # XXX what if my location is furniture? I want to see '( Foo,
292 # sitting in the Bar )', not '( Bar )'.
293 if visible.isViewOf(ultimateLocation):
294 concept = visible.visualize()
295 break
296 else:
297 concept = u"You are floating in an empty, formless void."
298 events.Success(actor=player.thing,
299 actorMessage=concept).broadcast()
300
301
302
303class LookAt(TargetAction):
304 actionName = "look"
305 expr = (pyparsing.Literal("look") +
306 pyparsing.Optional(pyparsing.White() +
307 pyparsing.Literal("at")) +
308 pyparsing.White() +
309 pyparsing.restOfLine.setResultsName("target"))
310
311 targetInterface = iimaginary.IVisible
312
313 def resolve_target(self, player, targetName):
314 """
315 Resolve the target to look at by looking for a named, visible object in
316 a proximity of 3 meters from the player.
317
318 @param player: The player doing the looking.
319
320 @type player: L{IThing}
321
322 @param targetName: The name of the object we are looking for.
323
324 @type targetName: C{unicode}
325
326 @return: A list of visible objects.
327
328 @rtype: C{list} of L{IVisible}
329
330 @raise eimaginary.ActionFailure: with an appropriate message if the
331 target cannot be resolved for an identifiable reason. See
332 L{imaginary.objects.Thing.obtainOrReportWhyNot} for a description
333 of how such reasons may be identified.
334 """
335 return player.obtainOrReportWhyNot(
336 Proximity(3.0, Named(targetName,
337 CanSee(ProviderOf(iimaginary.IVisible)),
338 player)))
339
340
341 def cantFind_target(self, player, name):
342 return "You don't see that."
343
344 def targetRadius(self, player):
345 return 3
346
347 def do(self, player, line, target):
348 if player.thing is not target:
349 evt = events.Success(
350 actor=player.thing,
351 target=target,
352 actorMessage=target.visualize(),
353 targetMessage=(player.thing, " looks at you."))
354 else:
355 evt = events.Success(
356 actor=player.thing,
357 actorMessage=target.visualize())
358 evt.broadcast()
359
360
361
362class Illuminate(Action):
363 """
364 Change the ambient light level at the location of the actor. Since this is
365 an administrative action that directly manipulates the environment, the
366 actor must be a L{iimaginary.IManipulator}.
367
368 The argument taken by this action is an integer which specifies the light
369 level in U{candelas<http://en.wikipedia.org/wiki/Candela>}.
370 """
371
372 actorInterface = iimaginary.IManipulator
373
374 expr = (pyparsing.Literal("illuminate") +
375 pyparsing.White() +
376 pyparsing.Word("0123456789").setResultsName("candelas"))
377
378
379 def do(self, player, line, candelas):
380 """
381 Attempt to change the illumination of the player's surroundings.
382
383 @param player: a manipulator that can change the illumination of its
384 room.
385 @type player: L{IManipulator}
386
387 @param line: the text being parsed
388 @type line: L{str}
389
390 @param candelas: the number of candelas to change the ambient
391 illumination to.
392 @type candelas: L{str}
393 """
394 candelas = int(candelas)
395 oldCandelas = player.setIllumination(candelas)
396 otherMessage = None
397 if oldCandelas == candelas:
398 actorMessage = u"You do it. Swell."
399 elif candelas == 0:
400 actorMessage = (
401 u"Your environs fade to black due to Ineffable Spooky Magic.")
402 otherMessage = actorMessage
403 elif oldCandelas == 0:
404 actorMessage = u"Your environs are suddenly alight."
405 otherMessage = actorMessage
406 elif candelas < oldCandelas:
407 actorMessage = u"Your environs seem slightly dimmer."
408 otherMessage = actorMessage
409 elif candelas > oldCandelas:
410 actorMessage = u"Your environs seem slightly brighter."
411 otherMessage = actorMessage
412 events.Success(actor=player.thing,
413 actorMessage=actorMessage,
414 otherMessage=otherMessage).broadcast()
415
416
417
418class Describe(TargetAction):
419 expr = (pyparsing.Literal("describe") +
420 pyparsing.White() +
421 targetString("target") +
422 pyparsing.White() +
423 pyparsing.restOfLine.setResultsName("description"))
424
425 def targetRadius(self, player):
426 return 3
427
428 def do(self, player, line, target, description):
429 target.description = description
430 evt = events.Success(
431 actor=player.thing,
432 actorMessage=("You change ", target, "'s description."),
433 otherMessage=(player.thing, " changes ", target, "'s description."))
434 evt.broadcast()
435
436
437class Name(TargetAction):
438 expr = (pyparsing.Literal("name") +
439 pyparsing.White() +
440 targetString("target") +
441 pyparsing.White() +
442 pyparsing.restOfLine.setResultsName("name"))
443
444 def targetRadius(self, player):
445 return 3
446
447 def do(self, player, line, target, name):
448 evt = events.Success(
449 actor=player.thing,
450 actorMessage=("You change ", target, "'s name."),
451 otherMessage=language.Sentence([player.thing, " changes ", target, "'s name to ", name, "."]))
452 evt.broadcast()
453 target.name = name
454
455
456
457class Open(TargetAction):
458 expr = (pyparsing.Literal("open") +
459 pyparsing.White() +
460 targetString("target"))
461
462 targetInterface = iimaginary.IContainer
463
464 def do(self, player, line, target):
465 dnf = language.Noun(target.thing).definiteNounPhrase()
466 if not target.closed:
467 raise eimaginary.ActionFailure(events.ThatDoesntWork(
468 actor=player.thing,
469 target=target.thing,
470 actorMessage=language.Sentence([dnf, " is already open."])))
471
472 target.closed = False
473 evt = events.Success(
474 actor=player.thing,
475 target=target.thing,
476 actorMessage=("You open ", dnf, "."),
477 targetMessage=language.Sentence([player.thing, " opens you."]),
478 otherMessage=language.Sentence([player.thing, " opens ", target.thing, "."]))
479 evt.broadcast()
480
481
482
483class Close(TargetAction):
484 expr = (pyparsing.Literal("close") +
485 pyparsing.White() +
486 targetString("target"))
487
488 targetInterface = iimaginary.IContainer
489
490 def do(self, player, line, target):
491 dnf = language.Noun(target.thing).definiteNounPhrase()
492 if target.closed:
493 raise eimaginary.ActionFailure(events.ThatDoesntWork(
494 actor=player.thing,
495 target=target.thing,
496 actorMessage=language.Sentence([dnf, " is already closed."])))
497
498 target.closed = True
499 evt = events.Success(
500 actor=player.thing,
501 target=target.thing,
502 actorMessage=("You close ", dnf, "."),
503 targetMessage=language.Sentence([player.thing, " closes you."]),
504 otherMessage=language.Sentence([player.thing, " closes ", target.thing, "."]))
505 evt.broadcast()
506
507
508
509def tooHeavy(player, target):
510 return eimaginary.ActionFailure(events.ThatDoesntWork(
511 actor=player, target=target,
512 actorMessage=(target, " is too heavy to pick up."),
513 otherMessage=(player, " struggles to lift ", target, ", but fails."),
514 targetMessage=(player, " tries to pick you up, but fails.")))
515
516
517
518def targetTaken(player, target, container=None):
519 if container is None:
520 return events.Success(
521 actor=player, target=target,
522 actorMessage=("You take ", target, "."),
523 targetMessage=(player, " takes you."),
524 otherMessage=(player, " takes ", target, "."))
525 idop = language.Noun(container).definiteNounPhrase()
526 return events.Success(
527 actor=player,
528 target=target,
529 tool=container,
530 actorMessage=("You take ", target, " from ", idop, "."),
531 targetMessage=(player, " takes you from ", idop, "."),
532 toolMessage=(player, " takes ", target, " from you."),
533 otherMessage=(player, " takes ", target, " from ", idop, "."))
534
535
536
537class Remove(TargetAction):
538 expr = ((pyparsing.Literal("remove") |
539 pyparsing.Literal("take off")) +
540 pyparsing.White() +
541 targetString("target"))
542
543 targetInterface = iimaginary.IClothing
544 actorInterface = iimaginary.IClothingWearer
545
546 def do(self, player, line, target):
547 from imaginary import garments
548 try:
549 player.takeOff(target)
550 except garments.InaccessibleGarment, e:
551 raise eimaginary.ActionFailure(events.ThatDoesntWork(
552 actor=player.thing,
553 target=target.thing,
554 actorMessage=(u"You cannot take off ",
555 language.Noun(target.thing).definiteNounPhrase(),
556 u" because you are wearing ",
557 e.obscuringGarment.thing, u"."),
558 otherMessage=language.Sentence([
559 player.thing,
560 u" gets a dumb look on ",
561 language.Noun(player.thing).hisHer(),
562 u" face."])))
563
564 evt = events.Success(
565 actor=player.thing,
566 target=target.thing,
567 actorMessage=(u"You take off ",
568 language.Noun(target.thing).definiteNounPhrase(),
569 u"."),
570 otherMessage=language.Sentence([
571 player.thing, u" takes off ", target.thing, u"."]))
572 evt.broadcast()
573
574
575
576class Wear(TargetAction):
577 expr = (pyparsing.Literal("wear") +
578 pyparsing.White() +
579 targetString("target"))
580
581 targetInterface = iimaginary.IClothing
582 actorInterface = iimaginary.IClothingWearer
583
584 def do(self, player, line, target):
585 from imaginary import garments
586 try:
587 player.putOn(target)
588 except garments.TooBulky, e:
589 raise eimaginary.ActionFailure(events.ThatDoesntWork(
590 actor=player.thing,
591 target=target.thing,
592 actorMessage=language.Sentence([
593 language.Noun(e.wornGarment.thing).definiteNounPhrase(),
594 u" you are already wearing is too bulky for you to do"
595 u" that."]),
596 otherMessage=language.Sentence([
597 player.thing,
598 u" wrestles with basic personal problems."])))
599
600 evt = events.Success(
601 actor=player.thing,
602 target=target.thing,
603 actorMessage=(u"You put on ",
604 language.Noun(target.thing).definiteNounPhrase(),
605 "."),
606 otherMessage=language.Sentence([
607 player.thing, " puts on ", target.thing, "."]))
608 evt.broadcast()
609
610
611
612class Equipment(Action):
613 expr = pyparsing.Literal("equipment")
614
615 actorInterface = iimaginary.IClothingWearer
616
617 def do(self, player, line):
618 from imaginary import garments
619 equipment = list(player.store.query(
620 objects.Thing,
621 AND(
622 garments.Garment.thing == objects.Thing.storeID,
623 garments.Garment.wearer == player),
624 sort=objects.Thing.name.ascending))
625 if equipment:
626 evt = events.Success(
627 actor=player.thing,
628 actorMessage=[
629 u"You are wearing ",
630 language.ItemizedList(equipment),
631 u"."])
632 else:
633 evt = events.Success(
634 actor=player.thing,
635 actorMessage=language.ExpressString(
636 u"You aren't wearing any equipment."))
637 evt.broadcast()
638
639
640
641class TakeFrom(ToolAction):
642 actionName = "take"
643
644 expr = ((pyparsing.Literal("get") ^ pyparsing.Literal("take")) +
645 pyparsing.White() +
646 targetString("target") +
647 pyparsing.Optional(pyparsing.White() +
648 pyparsing.Literal("from")) +
649 pyparsing.White() +
650 targetString("tool"))
651
652 def cantFind_target(self, player, targetName):
653 return "Nothing like that around here."
654 cantFind_tool = cantFind_target
655
656 def do(self, player, line, target, tool):
657 # XXX Make sure target is in tool
658 targetTaken(player.thing, target, tool).broadcast()
659 try:
660 target.moveTo(player.thing)
661 except eimaginary.DoesntFit:
662 raise tooHeavy(player.thing, target)
663
664
665
666class PutIn(ToolAction):
667
668 toolInterface = iimaginary.IThing
669 targetInterface = iimaginary.IContainer
670
671 def cantFind_target(self, player, targetName):
672 return "That doesn't work."
673
674 expr = (pyparsing.Literal("put") +
675 pyparsing.White() +
676 targetString("tool") +
677 pyparsing.Optional(pyparsing.White() +
678 pyparsing.Literal("in")) +
679 pyparsing.White() +
680 targetString("target"))
681
682 def do(self, player, line, tool, target):
683 ctool = iimaginary.IContainer(tool, None)
684 targetObject = target.thing
685 if ctool is not None and (ctool.contains(targetObject) or ctool is target):
686 raise eimaginary.ActionFailure(
687 events.ThatDoesntWork(
688 actor=player.thing,
689 target=targetObject,
690 tool=tool,
691 actorMessage="A thing cannot contain itself in euclidean space."))
692
693 dnf = language.Noun(targetObject).definiteNounPhrase()
694 evt = events.Success(
695 actor=player.thing,
696 target=targetObject,
697 tool=tool,
698 actorMessage=("You put ",
699 language.Noun(tool).definiteNounPhrase(),
700 " in ", dnf, "."),
701 targetMessage=language.Sentence([player.thing, " puts ", " tool in you."]),
702 toolMessage=language.Sentence([player.thing, " puts you in ", targetObject, "."]),
703 otherMessage=language.Sentence([player.thing, " puts ", tool, " in ", targetObject, "."]))
704 evt.broadcast()
705
706 try:
707 tool.moveTo(target)
708 except eimaginary.DoesntFit:
709 # <allexpro> dash: put me in a tent and give it to moshez!
710 raise eimaginary.ActionFailure(
711 events.ThatDoesntWork(
712 actor=player.thing,
713 target=targetObject,
714 tool=tool,
715 actorMessage=language.Sentence([
716 language.Noun(tool).definiteNounPhrase(),
717 u" does not fit in ", dnf, u"."])))
718 except eimaginary.Closed:
719 raise eimaginary.ActionFailure(
720 events.ThatDoesntWork(
721 actor=player.thing,
722 target=targetObject,
723 tool=tool,
724 actorMessage=language.Sentence([dnf, " is closed."])))
725
726
727
728class Take(TargetAction):
729 expr = ((pyparsing.Literal("get") ^ pyparsing.Literal("take")) +
730 pyparsing.White() +
731 targetString("target"))
732
733 def cantFind_target(self, player, targetName):
734 return u"Nothing like that around here."
735
736 def targetRadius(self, player):
737 return 1
738
739 def do(self, player, line, target):
740 if target in (player.thing, player.thing.location) or target.location is player.thing:
741 raise eimaginary.ActionFailure(events.ThatDoesntMakeSense(
742 actor=player.thing,
743 actorMessage=("You cannot take ", target, ".")))
744
745 targetTaken(player.thing, target).broadcast()
746 try:
747 target.moveTo(player.thing)
748 except eimaginary.DoesntFit:
749 raise tooHeavy(player.thing, target)
750
751
752
753def insufficientSpace(player):
754 return eimaginary.ActionFailure(events.ThatDoesntWork(
755 actor=player,
756 actorMessage="There's not enough space for that."))
757
758
759
760class Drop(TargetAction):
761 expr = (pyparsing.Literal("drop") +
762 pyparsing.White() +
763 targetString("target"))
764
765 def cantFind_target(self, player, targetName):
766 return "Nothing like that around here."
767
768 def targetRadius(self, player):
769 return 1
770
771 def do(self, player, line, target):
772 if target.location is not player.thing:
773 raise eimaginary.ActionFailure(
774 events.ThatDoesntMakeSense(
775 actor=player.thing,
776 actorMessage="You can't drop that."))
777
778 try:
779 target.moveTo(
780 player.thing.location,
781 arrivalEventFactory=lambda target: events.ArrivalEvent(
782 actor=player.thing,
783 actorMessage=("You drop ",
784 language.Noun(target).definiteNounPhrase(),
785 "."),
786 target=target,
787 targetMessage=(player.thing, " drops you."),
788 otherMessage=(player.thing, " drops ", target, ".")))
789 except eimaginary.DoesntFit:
790 raise insufficientSpace(player.thing)
791
792
793
794_directionNames = objects.OPPOSITE_DIRECTIONS.keys()
795_directionNames.extend(objects.DIRECTION_ALIASES.keys())
796
797DIRECTION_LITERAL = reduce(
798 operator.xor, [
799 pyparsing.Literal(d)
800 for d in _directionNames]).setResultsName("direction")
801
802
803
804def expandDirection(direction):
805 """
806 Expand direction aliases into the names of the directions they refer to.
807 """
808 return objects.DIRECTION_ALIASES.get(direction, direction)
809
810
811
812class Dig(Action):
813 expr = (pyparsing.Literal("dig") +
814 pyparsing.White() +
815 DIRECTION_LITERAL +
816 pyparsing.White() +
817 pyparsing.restOfLine.setResultsName("name"))
818
819 def do(self, player, line, direction, name):
820 direction = expandDirection(direction)
821 if iimaginary.IContainer(player.thing.location).getExitNamed(direction, None) is not None:
822 raise eimaginary.ActionFailure(events.ThatDoesntMakeSense(
823 actor=player.thing,
824 actorMessage="There is already an exit in that direction."))
825
826 room = objects.Thing(store=player.store, name=name)
827 objects.Container.createFor(room, capacity=1000)
828 objects.Exit.link(player.thing.location, room, direction)
829
830 evt = events.Success(
831 actor=player.thing,
832 actorMessage="You create an exit.",
833 otherMessage=language.Sentence([player.thing, " created an exit to the ", direction, "."]))
834 evt.broadcast()
835
836 # XXX Right now there can't possibly be anyone in the
837 # destination room, but someday there could be. When there
838 # could be, broadcast this to them too.
839
840
841
842class Bury(Action):
843 expr = (pyparsing.Literal("bury") +
844 pyparsing.White() +
845 DIRECTION_LITERAL)
846
847 def do(self, player, line, direction):
848 direction = expandDirection(direction)
849 for exit in iimaginary.IContainer(player.thing.location).getExits():
850 if exit.name == direction:
851 if exit.sibling is not None:
852 evt = events.Success(
853 location=exit.toLocation,
854 otherMessage=language.Sentence([
855 exit.sibling, " crumbles and disappears."]))
856 evt.broadcast()
857
858 evt = events.Success(
859 actor=player.thing,
860 actorMessage="It's gone.",
861 otherMessage=language.Sentence([
862 language.Noun(player.thing).nounPhrase(),
863 " destroyed ", exit, "."]))
864 evt.broadcast()
865 exit.destroy()
866 return
867
868 raise eimaginary.ActionFailure(events.ThatDoesntMakeSense(
869 actor=player.thing,
870 actorMessage="There isn't an exit in that direction."))
871
872
873
874class Go(Action):
875 expr = (
876 (pyparsing.Literal("go") + pyparsing.White() +
877 targetString("direction")) |
878 (pyparsing.Literal("enter") + pyparsing.White() +
879 targetString("direction")) |
880 (pyparsing.Literal("exit") + pyparsing.White() +
881 targetString("direction")) |
882 DIRECTION_LITERAL)
883
884 actorInterface = iimaginary.IThing
885
886 def resolve_direction(self, player, directionName):
887 """
888 Identify a direction by having the player search for L{IExit}
889 providers that they can see and reach.
890 """
891 directionName = expandDirection(directionName)
892 return player.obtainOrReportWhyNot(
893 Proximity(
894 3.0,
895 Traversability(
896 Named(directionName,
897 CanSee(ProviderOf(iimaginary.IExit)), player))))
898
899
900 def cantFind_direction(self, actor, directionName):
901 """
902 Explain to the user that they can't go in a direction that they can't
903 locate.
904 """
905 return u"You can't go that way."
906
907
908 def do(self, player, line, direction):
909 location = player.location
910
911 evt = events.Success(
912 location=location,
913 actor=player,
914 otherMessage=(player, " leaves ", direction.name, "."))
915 evt.broadcast()
916
917 try:
918 direction.traverse(player)
919 except eimaginary.DoesntFit:
920 raise eimaginary.ActionFailure(events.ThatDoesntWork(
921 actor=player,
922 actorMessage=language.ExpressString(
923 u"There's no room for you there.")))
924
925 # This is subtly incorrect: see http://divmod.org/trac/ticket/2917
926 lookAroundActor = iimaginary.IActor(player)
927 LookAround().do(lookAroundActor, "look")
928
929
930
931class Restore(TargetAction):
932 expr = (pyparsing.Literal("restore") +
933 pyparsing.White() +
934 pyparsing.restOfLine.setResultsName("target"))
935
936 targetInterface = iimaginary.IActor
937
938 def cantFind_target(self, player, targetName):
939 for thing in player.thing.search(self.targetRadius(player),
940 iimaginary.IThing, targetName):
941 return (language.Noun(thing).nounPhrase().plaintext(player),
942 " cannot be restored.")
943 return "Who's that?"
944
945 def targetRadius(self, player):
946 return 3
947
948
949 def do(self, player, line, target):
950 target.hitpoints.current = target.hitpoints.max
951 target.stamina.current = target.stamina.max
952
953 if player is target:
954 evt = events.Success(
955 actor=player.thing,
956 actorMessage="You have fully restored yourself.")
957 evt.broadcast()
958 else:
959 evt = events.Success(
960 actor=player.thing,
961 actorMessage=("You have restored ", target.thing, " to full health."),
962 target=target.thing,
963 targetMessage=(player.thing, " has restored you to full health."),
964 otherMessage=(player.thing, " has restored ", target.thing, " to full health."))
965 evt.broadcast()
966
967
968
969class Hit(TargetAction):
970 expr = ((pyparsing.Literal("hit") ^
971 pyparsing.Literal("attack") ^
972 pyparsing.Literal("kill")) +
973 pyparsing.White() +
974 pyparsing.restOfLine.setResultsName("target"))
975
976 targetInterface = iimaginary.IActor
977
978 def targetRadius(self, player):
979 return 3
980
981 def do(self, player, line, target):
982 if target is player:
983 raise eimaginary.ActionFailure(
984 events.ThatDoesntMakeSense(u"Hit yourself? Stupid.",
985 actor=player.thing))
986
987 cost = random.randrange(1, 5)
988 if player.stamina < cost:
989 raise eimaginary.ActionFailure(
990 events.ThatDoesntWork(u"You're too tired!",
991 actor=player.thing))
992
993 damage = random.randrange(1, 5)
994 player.stamina.decrease(cost)
995 thp = target.hitpoints.decrease(damage)
996 events.Success(
997 actor=player.thing,
998 target=target.thing,
999 targetMessage=language.Sentence([player.thing, " hits you for ", damage, " hitpoints."]),
1000 actorMessage=language.Sentence(["You hit ", language.Noun(target.thing).definiteNounPhrase(), " for ", damage, " hitpoints."]),
1001 otherMessage=language.Sentence([player.thing, " hits ", target.thing, "."])).broadcast()
1002
1003 if thp <= 0:
1004 xp = target.experience / 2 + 1
1005 player.gainExperience(xp) # I LOVE IT
1006 targetIsDead = [target.thing, " is dead!", "\n"]
1007 events.Success(
1008 actor=player.thing, target=target.thing,
1009 actorMessage=["\n", targetIsDead, "You gain ", xp, " experience"],
1010 targetMessage=["You are dead!"],
1011 otherMessage=targetIsDead).broadcast()
1012 target.thing.destroy()
1013
1014
1015
1016class Say(Action):
1017 expr = (((pyparsing.Literal("say") + pyparsing.White()) ^
1018 pyparsing.Literal("'")) +
1019 pyparsing.restOfLine.setResultsName("text"))
1020
1021 def do(self, player, line, text):
1022 evt = events.SpeechEvent(speaker=player.thing, text=text)
1023 evt.broadcast()
1024
1025
1026
1027class Emote(Action):
1028 expr = (((pyparsing.Literal("emote") + pyparsing.White()) ^
1029 pyparsing.Literal(":")) +
1030 pyparsing.restOfLine.setResultsName("text"))
1031
1032 def do(self, player, line, text):
1033 evt = events.Success(actor=player.thing,
1034 actorMessage=[player.thing, " ", text],
1035 otherMessage=[player.thing, " ", text])
1036 evt.broadcast()
1037
1038
1039
1040class Actions(Action):
1041 expr = pyparsing.Literal("actions")
1042
1043 def do(self, player, line):
1044 cmds = dict.fromkeys(
1045 getattr(cmd, 'actionName', cmd.__name__.lower())
1046 for cmd
1047 in self.__class__.actions).keys()
1048 cmds.sort()
1049 player.send((iterutils.interlace(" ", cmds), "\n"))
1050
1051
1052
1053class Commands(Action):
1054 """
1055 The I{commands} action provides a pointer to inexperienced players that
1056 they should be thinking in terms of I{actions} instead.
1057
1058 This has no world side-effects; it just provides some user-interface
1059 information to the player.
1060 """
1061 expr = pyparsing.Literal("commands")
1062
1063 def do(self, player, line):
1064 player.send("Try 'actions' instead.")
1065
1066
1067
1068class Search(Action):
1069 expr = (pyparsing.Literal("search") +
1070 targetString("name"))
1071
1072 def do(self, player, line, name):
1073 srch = player.thing.search(2, iimaginary.IVisible, name)
1074 evt = events.Success(
1075 actor=player.thing,
1076 actorMessage=language.ExpressList(
1077 list(iterutils.interlace('\n',
1078 (o.visualize()
1079 for o
1080 in srch)))))
1081 evt.broadcast()
1082
1083
1084
1085class Score(Action):
1086 expr = pyparsing.Literal("score")
1087
1088 scoreFormat = (
1089 '/----------------------------------------------------------------------------\\\n'
1090 '| Level: %20d Experience: %10d\n'
1091 '| Hitpoints: %16s\n'
1092 '| Stamina: %18s\n'
1093 '\\----------------------------------------------------------------------------/\n')
1094
1095 def do(self, player, line):
1096 events.Success(
1097 actor=player.thing,
1098 actorMessage=self.scoreFormat % (player.level, player.experience, player.hitpoints, player.stamina)).broadcast()
1099
1100
1101
1102class ExpressWho(language.BaseExpress):
1103 header = (u"/============ Currently Playing ===========\\")
1104 footer = (u"\\================ Total %(playerCount)03d ===============/")
1105
1106 def vt102(self, observer):
1107 players = self.original.connected
1108
1109 return [[T.bold, self.header], u'\n',
1110 [[language.Noun(p).shortName().vt102(observer), u'\n']
1111 for p in players],
1112 [T.bold, self.footer % {'playerCount': len(players)}], u'\n']
1113
1114
1115
1116class Who(Action):
1117 expr = pyparsing.Literal("who")
1118
1119 def do(self, player, line):
1120 player.send(ExpressWho(player.store.findUnique(ImaginaryWorld)))
1121
1122
1123
1124class Scrutinize(TargetAction):
1125 """
1126 Show detailed information about the model structure of a game object.
1127 """
1128 expr = (pyparsing.Literal("scrutinize") +
1129 pyparsing.White() +
1130 targetString("target"))
1131
1132 def targetRadius(self, player):
1133 return 3
1134
1135 def do(self, player, line, target):
1136 v = dict((k, getattr(target, k))
1137 for (k, ign)
1138 in target.getSchema()
1139 if hasattr(target, k))
1140
1141 targetContainer = iimaginary.IContainer(target, None)
1142 if targetContainer is not None:
1143 v['contents'] = list(targetContainer.getContents())
1144 exits = list(targetContainer.getExits())
1145 if exits:
1146 v['exits'] = exits
1147 s = pprint.pformat((target.__class__.__name__, v))
1148 # XXX FIXME Send a real Concept
1149 player.send(s, '\n')
1150
1151
1152
1153class ExpressInventory(language.BaseExpress):
1154 implements(iimaginary.IConcept)
1155
1156 def __init__(self, original):
1157 self.original = original
1158
1159 def vt102(self, observer):
1160 return [[T.fg.yellow, "Inventory:\n"],
1161 [T.fg.green,
1162 [(language.Noun(o).shortName().vt102(observer), '\n')
1163 for o
1164 in iimaginary.IContainer(self.original).getContents()]]]
1165
1166
1167
1168class Inventory(Action):
1169 expr = pyparsing.Literal("inventory")
1170
1171 def do(self, player, line):
1172 events.Success(actor=player.thing,
1173 actorMessage=ExpressInventory(player.thing)).broadcast()
1174
1175
1176
1177class Set(TargetAction):
1178 """
1179 Direct model-level state manipulation command.
1180 """
1181 expr = (
1182 pyparsing.Literal("set") + pyparsing.White() +
1183 targetString("attribute") + pyparsing.White() +
1184 pyparsing.Literal("of") + pyparsing.White() +
1185 targetString("target") + pyparsing.White() +
1186 pyparsing.Literal("to") + pyparsing.White() +
1187 targetString("value"))
1188
1189 def do(self, player, line, attribute, target, value):
1190 """
1191 Dispatch handling to an attribute-specific method.
1192
1193 @type attribute: C{unicode}
1194 @param attribute: The model-level attribute of which to manipulate
1195 the value. Handling of each attribute will be dispatched to a
1196 C{set_}-prefixed method for that attribute based on this value.
1197
1198 @type target: L{Thing}
1199 @param target: The model object to manipulate.
1200
1201 @type value: C{unicode}
1202 @param value: The new value for the specified attribute.
1203 """
1204 try:
1205 method = getattr(self, "set_" + attribute.upper())
1206 except AttributeError:
1207 raise eimaginary.ActionFailure(
1208 events.ThatDoesntMakeSense(
1209 actor=player.thing,
1210 actorMessage="You cannot set that."))
1211 else:
1212 method(player, line, target, value)
1213
1214
1215 def set_GENDER(self, player, line, target, value):
1216 """
1217 Attempt to change the gender of a thing.
1218
1219 @param target: The thing to change the gender of.
1220 @param value: A string naming a gender on L{language.Gender}.
1221 """
1222 try:
1223 target.gender = getattr(language.Gender, value.upper())
1224 except AttributeError:
1225 gender = {language.Gender.MALE: "male",
1226 language.Gender.FEMALE: "female",
1227 language.Gender.NEUTER: "neuter"}.get(target.gender)
1228 raise eimaginary.ActionFailure(events.ThatDoesntMakeSense(
1229 actor=player.thing,
1230 actorMessage=("Only male, female, and neuter are valid "
1231 "genders. You remain ", gender, ".")))
1232 else:
1233 if player.thing is target:
1234 # XXX Why can't I do something with Noun to collapse these
1235 # cases?
1236 event = events.Success(
1237 actor=player.thing,
1238 actorMessage=(u"You set your gender to ", value, "."))
1239 else:
1240 event = events.Success(
1241 actor=player.thing,
1242 target=target,
1243 actorMessage=("You set ", language.Noun(target).hisHer(),
1244 " gender to ", value, "."),
1245 targetMessage=(player.thing, " set your gender to ",
1246 value, "."))
1247 event.broadcast()
1248
1249
1250 def set_PROPER(self, player, line, target, value):
1251 """
1252 Attempt to change the name of a thing from a proper noun to a common
1253 noun or the other way around.
1254
1255 @param target: The thing to change.
1256 @param value: The string C{"true"} or C{"false"}.
1257 """
1258 if value == "true":
1259 target.proper = True
1260 phrase = '" a proper noun.'
1261 elif value == "false":
1262 target.proper = False
1263 phrase = '" a common noun.'
1264 else:
1265 raise eimaginary.ActionFailure(
1266 events.ThatDoesntMakeSense(
1267 actor=player.thing,
1268 actorMessage=("Only true and false are valid settings "
1269 "for proper.")))
1270 events.Success(
1271 actor=player.thing,
1272 actorMessage=('You make the name of "',
1273 language.Noun(target).shortName(),
1274 phrase)).broadcast()
1275
1276
1277
1278class Help(Action):
1279 """
1280 A command for looking up help files.
1281
1282 @cvar helpContentPath: The path in which to search for files.
1283 @type helpContentPath: L{filepath.FilePath}
1284 """
1285 expr = (pyparsing.Literal("help") +
1286 pyparsing.White() +
1287 pyparsing.restOfLine.setResultsName("topic"))
1288
1289 helpContentPath = filepath.FilePath(imaginary.__file__).sibling(
1290 "resources").child("help")
1291
1292 def do(self, player, line, topic):
1293 topic = topic.lower().strip()
1294 try:
1295 helpFile = self.helpContentPath.child(topic).open()
1296 except (OSError, IOError, filepath.InsecurePath):
1297 player.send("No help available on ", topic, ".", "\n")
1298 else:
1299 player.send(helpFile.read(), '\n')
13000
=== removed file 'Imaginary/imaginary/copyright.py'
--- Imaginary/imaginary/copyright.py 2006-04-12 02:41:46 +0000
+++ Imaginary/imaginary/copyright.py 1970-01-01 00:00:00 +0000
@@ -1,22 +0,0 @@
1
2# <major> <minor> <patch> <alpha | pre | final | zzz> <iteration>
3version_info = (0, 1, 0, 'alpha', 0)
4
5# Sortable version information. This will always only
6# increase from an older version to a newer version.
7hexversion = (version_info[0] << 24 |
8 version_info[1] << 16 |
9 version_info[2] << 8 |
10 ['alpha', 'pre', 'final', 'zzz'].index(version_info[3]) << 4 |
11 version_info[4])
12
13# Human-readable format
14if version_info[3] == 'final':
15 version = '%d.%d.%d%s' % version_info[:-1]
16elif version_info[3] != 'zzz':
17 version = '%d.%d.%d%s%d' % version_info
18else:
19 version = "SVN-trunk"
20
21# Longer human-readable format
22longversion= "Imaginary " + version
230
=== removed file 'Imaginary/imaginary/creation.py'
--- Imaginary/imaginary/creation.py 2009-08-17 02:40:03 +0000
+++ Imaginary/imaginary/creation.py 1970-01-01 00:00:00 +0000
@@ -1,179 +0,0 @@
1# -*- test-case-name: imaginary.test.test_create -*-
2"""
3This module contains code associated with creating objects in game.
4"""
5
6from zope.interface import implements
7
8from twisted import plugin
9
10import imaginary.plugins
11
12from imaginary import objects
13from imaginary import events
14from imaginary import language
15
16from imaginary.iimaginary import IThingType
17from imaginary.eimaginary import ActionFailure, DoesntFit
18
19from imaginary.action import Action, insufficientSpace
20from imaginary.action import targetString
21
22from imaginary.pyparsing import Literal, White, Optional, restOfLine
23
24
25def getPlugins(iface, package):
26 """
27 Get plugins. See L{twisted.plugin.getPlugins}.
28
29 This is in place only so the tests specifically for creation can replace
30 it. Please use L{twisted.plugin.getPlugins} instead.
31 """
32 # XXX the tests should not need to do that, make it per-instance or
33 # something...
34 return plugin.getPlugins(iface, package)
35
36
37def createCreator(*enhancements):
38 """
39 Create and return a function which can create objects in the game world.
40
41 This is a utility function to make it easy to define factories for certain
42 configurations of power-ups to be used with Imaginary. It doesn't do
43 anything magical; you can replicate its effects simply by writing a
44 function that calls L{Enhancement.createFor} on the set of L{Enhancement}s.
45 L{createCreator} exists because you will frequently need to do that, and it
46 can be tedious.
47
48 @param enhancements: The arguments to this function are a list of 2-tuples
49 of (L{Enhancement}-subclass, keyword arguments to that class's
50 constructor).
51
52 @return: a function which takes keyword arguments that will be passed on to
53 L{objects.Thing}'s constructor, and will return a L{Thing} with an
54 instance of each class in C{enhancements} installed, via C{createFor},
55 on it.
56
57 @rtype: L{Thing}
58 """
59 def create(**kw):
60 o = objects.Thing(**kw)
61 for enhancementClass, enhancementKeywords in enhancements:
62 enhancementClass.createFor(o, **(enhancementKeywords or {}))
63 return o
64 return create
65
66
67class CreationPluginHelper(object):
68 """
69 A helper for creating plugins for the 'Create' command.
70
71 Create will search for L{IThingType} plugins and allow users to
72 instantiate a new L{objects.Thing} using the one with the name which
73 matches what was supplied to the action.
74 """
75
76 implements(plugin.IPlugin, IThingType)
77
78 def __init__(self, typeName, typeObject):
79 """
80 @type typeName: C{unicode}
81 @param typeName: A short string describing the kind of object this
82 plugin will create.
83
84 @param typeObject: A factory for creating instances of
85 L{objects.Thing}. This will be invoked with four keyword arguments:
86 store, name, description, and proper. See attributes of
87 L{objects.Thing} for documentation of these arguments.
88 """
89 self.type = typeName
90 self.typeObject = typeObject
91
92
93 def getType(self):
94 return self.typeObject
95
96
97
98def creationSuccess(player, creation):
99 """
100 Create and return an event describing that an object was successfully
101 created.
102 """
103 phrase = language.Noun(creation).nounPhrase()
104 return events.Success(
105 actor=player,
106 target=creation,
107 actorMessage=language.Sentence(["You create ", phrase, "."]),
108 targetMessage=language.Sentence([player, " creates you."]),
109 otherMessage=language.Sentence([player, " creates ", phrase, "."]))
110
111
112class Create(Action):
113 """
114 An action which can create items by looking at the L{IThingType} plugin
115 registry.
116 """
117 expr = (Literal("create") +
118 Optional(White() +
119 (Literal("an") | Literal("a") | Literal("the")).setResultsName("article")) +
120 White() +
121 targetString("typeName") +
122 White() +
123 Literal("named") +
124 White() +
125 targetString("name") +
126 Optional(White() +
127 restOfLine.setResultsName("description")))
128
129 def do(self, player, line, typeName, name, description=None, article=None):
130 """
131 Create an item, and notify everyone present that it now exists.
132 """
133 if not description:
134 description = u'an undescribed object'
135 for plug in getPlugins(IThingType, imaginary.plugins):
136 if plug.type == typeName:
137 proper = (article == "the")
138 o = plug.getType()(store=player.store, name=name,
139 description=description, proper=proper)
140 break
141 else:
142 raise ActionFailure(
143 events.ThatDoesntMakeSense(
144 actor=player.thing,
145 actorMessage=language.ExpressString(
146 u"Can't find " + typeName + u".")))
147
148 creationSuccess(player.thing, o).broadcast()
149 try:
150 o.moveTo(player.thing)
151 except DoesntFit:
152 raise insufficientSpace(player.thing)
153
154
155
156
157def listThingTypes():
158 """
159 Return a list of C{unicode} strings each of which gives the name of a type
160 which can be created with the create command.
161 """
162 return sorted([type.type for type in getPlugins(IThingType, imaginary.plugins)])
163
164
165
166class ListThingTypes(Action):
167 """
168 An action which tells the invoker what thing types exist to be created with
169 the L{Create} command.
170 """
171 expr = Literal("list thing types")
172
173 def do(self, player, line):
174 """
175 Tell the player the thing types which exist.
176 """
177 events.Success(
178 actor=player.thing,
179 actorMessage=[(t, "\n") for t in listThingTypes()]).broadcast()
1800
=== removed file 'Imaginary/imaginary/eimaginary.py'
--- Imaginary/imaginary/eimaginary.py 2009-06-29 04:03:17 +0000
+++ Imaginary/imaginary/eimaginary.py 1970-01-01 00:00:00 +0000
@@ -1,90 +0,0 @@
1
2from twisted.cred import error
3
4# Authentication errors
5class BadPassword(error.UnauthorizedLogin):
6 pass
7
8class NoSuchUser(error.UnauthorizedLogin):
9 pass
10
11
12# Base Imaginary error
13class ImaginaryError(Exception):
14 pass
15
16
17# Input handling errors
18class NoSuchCommand(ImaginaryError):
19 """
20 There is no command like the one you tried to execute.
21 """
22
23class AmbiguousArgument(ImaginaryError):
24 """
25 One or more of the inputs specified can not be narrowed down to
26 just one thing. This can be due to the presence of multiple
27 things with similar names, or due to the absence of anything named
28 similarly to the given input.
29
30 @ivar action: The action which was being processed when an ambiguity was
31 found.
32
33 @type part: C{str}
34 @ivar part: The part of the command which was ambiguous.
35 Typically something like 'target' or 'tool'.
36
37 @type partValue: C{str}
38 @ivar partValue: The string which was supplied by the user for the indicated part.
39
40 @type objects: C{list} of C{IThing}
41 @ivar objects: The objects which were involved in the ambiguity.
42 """
43
44 def __init__(self, action, part, partValue, objects):
45 ImaginaryError.__init__(self, action, part, partValue, objects)
46 self.action = action
47 self.part = part
48 self.partValue = partValue
49 self.objects = objects
50
51
52
53class ActionFailure(ImaginaryError):
54 """
55 Wrapper exception for an Event that caused an action to fail (such that the
56 transaction in which it was running should be reverted).
57 """
58 def __init__(self, event):
59 ImaginaryError.__init__(self)
60 self.event = event
61
62
63 def __repr__(self):
64 return '<Action Failure: %r>' % (self.event,)
65
66
67
68class ThingNotFound(ImaginaryError):
69 """
70 Resolving a Thing by identity failed.
71 """
72
73
74# Game logic errors
75class DoesntFit(ImaginaryError):
76 """
77 An object tried to go into a container, but the container was full.
78 """
79
80
81class Closed(ImaginaryError):
82 """
83 An object tried to go into a container, but the container was closed.
84 """
85
86
87class CannotMove(ImaginaryError):
88 """
89 An object tried to move but it was not portable so it couldn't.
90 """
910
=== removed file 'Imaginary/imaginary/enhancement.py'
--- Imaginary/imaginary/enhancement.py 2009-06-29 04:03:17 +0000
+++ Imaginary/imaginary/enhancement.py 1970-01-01 00:00:00 +0000
@@ -1,95 +0,0 @@
1# -*- test-case-name: imaginary.test.test_enhancement -*-
2
3"""
4This module contains objects for application code to use to implement behaviors
5that attach to objects in a simulation.
6"""
7
8class Enhancement(object):
9 """
10 An L{Enhancement} is an object attached to a L{imaginary.objects.Thing}
11 that provides some additional functionality.
12
13 This class is a mixin; it expects to be mixed in to an L{Item} subclass,
14 since it passes itself as an argument to L{Item.powerUp}.
15
16 Note that an L{Enhancement} embodies the behavior, but not the physical
17 attributes, of the object in question.
18
19 For example, let's say you wanted to implement a cell phone in Imaginary.
20 You would make an L{Enhancement} called C{CellPhone} which had various
21 attributes, for example C{phoneNumber}. Then you would do C{phoneBody =
22 Thing(...)} to create a physical 'phone' object in a world. Next, you
23 would do C{cellPhone = CellPhone.createFor(phoneBody, ...)}, which would
24 create a C{CellPhone} object that endowed your physical 'phone' with the
25 properties of being an actual phone, like having a phone number, ringing
26 when dialed, etc.
27
28 Note that it is not enough to simply create your C{CellPhone}, as it will
29 not have a physical body, and therefore not exist in the world.
30
31 @ivar thing: a L{imaginary.objects.Thing} powered up with this
32 L{Enhancement}. All subclasses which mix in L{Item} should declare
33 this as an L{attributes.reference} attribute. Unless your
34 L{Enhancement} subclass is specifically designed to exist
35 independently of its L{Thing}, or to accept other types for this
36 attribute, it should also be declared as C{(allowNone=False,
37 reftype=Thing, whenDeleted=CASCADE)}.
38 """
39
40 def installed(self):
41 """
42 Override the C{installed()} hook that C{axiom.dependency} provides.
43 When L{Enhancement} was called C{ThingMixin}, the suggested mechanism
44 to install simulation components was to use the dependency system,
45 which was wrong, c.f. U{http://divmod.org/trac/ticket/2558}.
46
47 @raise RuntimeError: to indicate that you shouldn't use this
48 functionality.
49 """
50 raise RuntimeError("Use Enhancement.createFor, not installOn(), "
51 "to apply an Enhancement to a Thing.")
52
53
54 def applyEnhancement(self):
55 """
56 Apply this L{Enhancement} to its C{thing} attribute, by powering it up.
57 """
58 self.thing.powerUp(self)
59
60
61 def removeEnhancement(self):
62 """
63 Remove this L{Enhancement} from its C{thing} attribute, by powering it
64 down.
65 """
66 self.thing.powerDown(self)
67
68
69 @classmethod
70 def createFor(cls, thing, **kw):
71 """
72 Create an L{Enhancement} of this type for the given
73 L{imaginary.objects.Thing}, in the given L{imaginary.objects.Thing}'s
74 store.
75 """
76 self = cls(store=thing.store, thing=thing, **kw)
77 self.applyEnhancement()
78 return self
79
80
81 @classmethod
82 def destroyFor(cls, thing):
83 """
84 Destroy the L{Enhancement}s of the given subclass associated with the
85 given L{Thing}, if one exists.
86
87 @param thing: A L{Thing} which may be the value of the C{thing}
88 attribute of an instance of the given L{Enhancement} subclass.
89
90 @type thing: L{Thing}
91 """
92 it = thing.store.findUnique(cls, cls.thing == thing, default=None)
93 if it is not None:
94 it.removeEnhancement()
95 it.deleteFromStore()
960
=== removed file 'Imaginary/imaginary/events.py'
--- Imaginary/imaginary/events.py 2009-08-18 00:20:31 +0000
+++ Imaginary/imaginary/events.py 1970-01-01 00:00:00 +0000
@@ -1,284 +0,0 @@
1# -*- test-case-name: imaginary.test.test_actions.TargetActionTests.test_resolveTargetCaseInsensitively -*-
2
3from zope.interface import implements
4
5from twisted.python import context
6
7from imaginary import iimaginary, language, eimaginary
8from imaginary.idea import Proximity, ProviderOf
9
10
11class Event(language.BaseExpress):
12 implements(iimaginary.IConcept)
13
14 actorMessage = targetMessage = toolMessage = otherMessage = None
15
16 def __init__(self,
17 location=None, actor=None, target=None, tool=None,
18 actorMessage=None, targetMessage=None, toolMessage=None,
19 otherMessage=None):
20
21 if location is None and actor is not None:
22 location = actor.location
23
24 self.location = location
25 self.actor = actor
26 self.target = target
27 self.tool = tool
28 if actorMessage is not None:
29 self.actorMessage = iimaginary.IConcept(actorMessage)
30 if targetMessage is not None:
31 self.targetMessage = iimaginary.IConcept(targetMessage)
32 if toolMessage is not None:
33 self.toolMessage = iimaginary.IConcept(toolMessage)
34 if otherMessage is not None:
35 self.otherMessage = iimaginary.IConcept(otherMessage)
36
37
38 def conceptFor(self, observer):
39 """
40 Retrieve the appropriate L{IConcept} provider for a given observer. If
41 the observer is this L{Event}'s C{actor}, it will return the
42 C{actorMessage} for this event, and so on for the tool and the target.
43 If it doesn't match a L{Thing} known to this event, it will return
44 C{otherMessage}.
45 """
46 if observer is self.actor:
47 msg = self.actorMessage
48 elif observer is self.target:
49 msg = self.targetMessage
50 elif observer is self.tool:
51 msg = self.toolMessage
52 else:
53 msg = self.otherMessage
54 return msg
55
56
57 def reify(self):
58 """
59 Determine which objects should receive this event and return a callable
60 object which will deliver it to them.
61
62 Note that this occurs during event propagation, and you probably don't
63 need to call it directly.
64
65 @see: L{iimaginary.IEventObserver.prepare} and
66 L{TransactionalEventBroadcaster} for a more thorough description of
67 how this method is used to interact with transactions.
68
69 @return: a 0-arg callable object which, when called, will call the
70 results of all L{IEventObserver}s which were contained within this
71 L{Event}'s location when this method, L{Event.reify}, was called.
72 """
73 L = []
74 for observer in (self.location.idea.obtain(
75 Proximity(0.5, ProviderOf(iimaginary.IEventObserver)))):
76 sender = observer.prepare(self)
77 if not callable(sender):
78 raise TypeError("Senders must be callable", sender)
79 L.append(sender)
80 return lambda: map(apply, L)
81
82
83 def vt102(self, observer):
84 c = self.conceptFor(observer)
85 if c is not None:
86 return [c.vt102(observer), '\n']
87 return u''
88
89
90
91class TransactionalEventBroadcaster(object):
92 """
93 Collect a bunch of output events as a transaction is being executed, then
94 distribute them when it has completed.
95
96 Events can be added normally or as revert events. Normal events are
97 broadcast after the transaction is successfully committed. Revert events
98 are broadcast if the transaction failed somehow and was been reverted.
99 """
100 implements(iimaginary.ITransactionalEventBroadcaster)
101
102 def __init__(self):
103 self._events = []
104 self._revertEvents = []
105
106
107 def addEvent(self, event):
108 """
109 Add a normal event.
110
111 @param event: A no-argument callable to be invoked when this
112 transaction has been committed.
113 """
114 if not callable(event):
115 raise ValueError("Events must be callable", event)
116 self._events.append(event)
117
118
119 def addRevertEvent(self, event):
120 """
121 Add a revert event.
122
123 @param event: A no-argument callable to be invoked when this
124 transaction has been reverted.
125 """
126 if not callable(event):
127 raise ValueError("Events must be callable", event)
128 self._revertEvents.append(event)
129
130
131 def broadcastEvents(self):
132 """
133 Send all normal events.
134 """
135 events = self._events
136 self._events = self._revertEvents = None
137 map(apply, events)
138
139
140 def broadcastRevertEvents(self):
141 """
142 Send all revert events.
143 """
144 events = self._revertEvents
145 self._events = self._revertEvents = None
146 map(apply, events)
147
148
149
150def runEventTransaction(store, func, *args, **kwargs):
151 """
152 This takes responsibility for setting up the transactional event
153 broadcasting junk, handling action errors, and broadcasting commit or
154 revert events.
155 """
156 broadcaster = TransactionalEventBroadcaster()
157 def runHelper():
158 # Set up event context for the duration of the action
159 # run. Additionally, handle raised ActionFailures by
160 # adding their events to the revert event list and
161 # re-raising them so they will revert the transaction.
162 try:
163 return context.call(
164 {iimaginary.ITransactionalEventBroadcaster: broadcaster},
165 func, *args, **kwargs)
166 except eimaginary.ActionFailure, e:
167 broadcaster.addRevertEvent(e.event.reify())
168 raise
169 try:
170 result = store.transact(runHelper)
171 except eimaginary.ActionFailure:
172 broadcaster.broadcastRevertEvents()
173 return None
174 else:
175 broadcaster.broadcastEvents()
176 return result
177
178
179
180class ThatDoesntMakeSense(Event):
181 """
182 An action was attempted which is logically impossible.
183 """
184 def __init__(self, actorMessage="That doesn't make sense.", **kw):
185 super(ThatDoesntMakeSense, self).__init__(actorMessage=actorMessage, **kw)
186
187
188class ThatDoesntWork(Event):
189 """
190 An action was attempted which is phyically impossible.
191 """
192 def __init__(self, actorMessage="That doesn't work.", **kw):
193 super(ThatDoesntWork, self).__init__(actorMessage=actorMessage, **kw)
194
195
196class Success(Event):
197 """
198 You do it. Swell.
199 """
200
201 def broadcast(self):
202 """
203 Don't really broadcast. Add this event to the events which will be
204 sent when the action (or whatever) execution transaction is committed
205 successfully.
206 """
207 broadcaster = context.get(iimaginary.ITransactionalEventBroadcaster)
208 if broadcaster is not None:
209 broadcaster.addEvent(self.reify())
210 else:
211 self.reify()()
212
213
214
215class ArrivalEvent(Success):
216 """
217 An event representing the arrival of an object.
218 """
219
220
221
222class MovementArrivalEvent(ArrivalEvent):
223 """
224 An event representing the arrival of an object at a location from an
225 origin.
226 """
227 def __init__(self, thing, origin=None, direction=None):
228 self.thing = thing
229 self.origin = origin
230 self.direction = direction
231 self.location = self.thing.location
232
233
234 def conceptFor(self, observer):
235 if observer is self.thing:
236 return None
237 if self.origin is not None:
238 msg = [" arrives from ", self.origin, "."]
239 elif self.direction is not None:
240 msg = [" arrives from the ", self.direction, "."]
241 else:
242 msg = [" arrives."]
243 msg.insert(0, self.thing)
244 return language.Sentence(msg)
245
246
247
248class DepartureEvent(Success):
249 """
250 An event representing the departure of an object at a location to a
251 destination.
252 """
253 def __init__(self, location, actor, **kw):
254 """
255 @type location: L{iimaginary.IThing} provider.
256 @param location: The location that the actor is leaving.
257 @type actor: L{iimaginary.IThing} provider.
258 @param actor: The actor that is leaving.
259 """
260 super(DepartureEvent, self).__init__(location, actor, **kw)
261
262
263class SpeechEvent(Success):
264 """
265 An event representing something somebody said.
266
267 @ivar speaker: The Thing which spoke.
268 @ivar text: The text which was spoken.
269 """
270 def __init__(self, speaker, text):
271 """
272 @type speaker: L{iimaginary.IThing} provider.
273 @param speaker: The actor emitting this speech.
274 @type text: C{unicode}
275 @param text: The text that the actor said.
276 """
277 self.speaker = speaker
278 self.text = text
279 Success.__init__(
280 self,
281 location=speaker.location,
282 actor=speaker,
283 actorMessage=["You say, '", text, "'"],
284 otherMessage=language.Sentence([speaker, " says, '", text, "'"]))
2850
=== removed file 'Imaginary/imaginary/garments.py'
--- Imaginary/imaginary/garments.py 2011-09-16 18:52:54 +0000
+++ Imaginary/imaginary/garments.py 1970-01-01 00:00:00 +0000
@@ -1,370 +0,0 @@
1# -*- test-case-name: imaginary.test.test_garments -*-
2
3"""
4
5Layered clothing.
6
7"""
8
9from zope.interface import implements
10
11from axiom import item, attributes
12
13from imaginary import iimaginary, language, objects
14from imaginary.eimaginary import ActionFailure
15from imaginary.events import ThatDoesntWork
16from imaginary.idea import Link
17from imaginary.creation import createCreator
18from imaginary.enhancement import Enhancement
19
20
21class Unwearable(Exception):
22 pass
23
24class TooBulky(Unwearable):
25 def __init__(self, wornGarment, newGarment):
26 self.wornGarment = wornGarment
27 self.newGarment = newGarment
28 Unwearable.__init__(self, wornGarment, newGarment)
29
30
31
32class InaccessibleGarment(Exception):
33 """The garment is covered by another, therefore it cannot be removed.
34 """
35 def __init__(self, wearer, garment, obscuringGarment):
36 self.wearer = wearer
37 self.garment = garment
38 self.obscuringGarment = obscuringGarment
39
40
41 def __str__(self):
42 return "%s tried taking off %s which was covered by %s" % (
43 self.wearer, self.garment, self.obscuringGarment)
44
45
46
47GARMENT_SLOTS = [
48 u"crown",
49 u"left eye",
50 u"right eye",
51 u"left ear",
52 u"right ear",
53
54 u"neck",
55 u"chest",
56 u"back",
57
58 u"left arm",
59 u"right arm",
60 u"left wrist",
61 u"right wrist",
62 u"left hand",
63 u"right hand",
64 u"left fingers",
65 u"right fingers",
66
67 u"waist",
68 u"left leg",
69 u"right leg",
70 u"left ankle",
71 u"right ankle",
72 u"left foot",
73 u"right foot"
74 ]
75
76class GarmentSlot:
77 pass
78
79for gslot in GARMENT_SLOTS:
80 gslotname = gslot.upper().replace(" ", "_").encode('ascii')
81 setattr(GarmentSlot, gslotname, gslot)
82
83
84
85class Garment(item.Item, Enhancement):
86 """
87 An enhancement for a L{Thing} representing its utility as an article of
88 clothing.
89 """
90 implements(iimaginary.IClothing,
91 iimaginary.IDescriptionContributor,
92 iimaginary.IMovementRestriction)
93
94 powerupInterfaces = (iimaginary.IClothing,
95 iimaginary.IDescriptionContributor,
96 iimaginary.IMovementRestriction)
97
98 thing = attributes.reference()
99
100 # templated / constant stuff
101 garmentSlots = attributes.textlist(allowNone=False)
102 bulk = attributes.integer(allowNone=False,
103 default=1)
104 garmentDescription = attributes.text(doc="""
105 Description of this as an individual garment.
106 """, allowNone=False)
107
108 # transient / mutable stuff
109 wearer = attributes.reference()
110 wearLevel = attributes.integer(default=0)
111
112
113 def conceptualize(self):
114 return language.ExpressString(u'This can be worn.')
115
116
117 def expressTo(self, observer):
118 """
119 Describe the garment as it looks when it is worn.
120
121 The garment's normal description is C{self.thing.description} or
122 somesuch.
123 """
124 return self.garmentDescription
125
126
127 def nowWornBy(self, wearer):
128 """
129 This garment is now worn by the given wearer. As this garment is now
130 on top, set its C{wearLevel} to be higher than any other L{Garment}
131 related to the new C{wearer}.
132 """
133 self.wearer = wearer
134 self.wearLevel = wearer.store.query(
135 Garment,
136 Garment.wearer == wearer).getColumn("wearLevel").max(default=0) + 1
137
138
139 def noLongerWorn(self):
140 """
141 This garment is no longer being worn by anyone.
142 """
143 self.wearer = None
144 self.wearLevel = None
145
146
147 def movementImminent(self, movee, destination):
148 """
149 Something is trying to move. Don't allow it if I'm currently worn.
150 """
151 if self.wearer is not None and movee is self.thing:
152 raise ActionFailure(
153 ThatDoesntWork(
154 # XXX I don't actually know who is performing the action
155 # :-(.
156 actor=self.wearer.thing,
157 actorMessage=[
158 "You can't move ",
159 language.Noun(self.thing).definiteNounPhrase(),
160 " without removing it first."]))
161
162
163
164def _orderTopClothingByGlobalSlotList(tempClothes):
165 """
166 This function orders a dict as returned by getGarmentDict in the order that
167 they should be shown to the user.
168
169 @param tempClothes: {clothingSlot: list of clothing objects (top last)}
170 @type tempClothes: dict
171 """
172 if not tempClothes:
173 return None
174 yetDescribed = []
175 for universalSlot in GARMENT_SLOTS:
176 slotlist = tempClothes.pop(universalSlot, ())
177 if slotlist:
178 topGarment = slotlist[-1]
179 if topGarment not in yetDescribed:
180 yetDescribed.append(topGarment)
181
182 # if somebody decided to make a wacky slot that is not in the universal
183 # slots list, just describe it last.
184 for k in tempClothes.keys():
185 x = tempClothes.pop(k)
186 if x:
187 topGarment = x[-1]
188 if topGarment not in yetDescribed:
189 yetDescribed.append(topGarment)
190
191 assert tempClothes == {}, (
192 "tempClothes not empty after all clothes eliminated: " +
193 repr(tempClothes))
194
195 return yetDescribed
196
197
198
199class Wearer(item.Item, Enhancement):
200 """
201 The clothing-wearing component of an object that can wear clothing; e.g. a
202 person or mannequin.
203 """
204
205 _interfaces = (iimaginary.IClothingWearer,
206 iimaginary.IDescriptionContributor,
207 iimaginary.ILinkContributor,
208 iimaginary.ILinkAnnotator,
209 )
210
211 implements(*_interfaces)
212
213 powerupInterfaces = _interfaces
214
215
216 thing = attributes.reference()
217
218
219 def getGarmentDict(self):
220 c = {}
221 for garment in self.store.query(Garment, Garment.wearer == self,
222 sort=Garment.wearLevel.ascending):
223 for usedSlot in garment.garmentSlots:
224 c.setdefault(usedSlot, []).append(garment)
225 return c
226
227
228 def putOn(self, newGarment):
229 """
230 Wear a new L{Garment} on this L{Wearer}, first moving it to this
231 L{Wearer}'s C{thing} if it is not already there.
232
233 @param newGarment: the article of clothing to wear.
234
235 @type newGarment: L{Garment}
236
237 @raise TooBulky: if the bulk of any of the slots occupied by
238 C{newGarment} is greater than the bulk of any other clothing
239 already in that slot. (For example, if you tried to wear a T-shirt
240 over a heavy coat.)
241 """
242 c = self.getGarmentDict()
243 for garmentSlot in newGarment.garmentSlots:
244 if garmentSlot in c:
245 currentTopOfSlot = c[garmentSlot][-1]
246 if currentTopOfSlot.bulk >= newGarment.bulk:
247 raise TooBulky(currentTopOfSlot, newGarment)
248
249 newGarment.thing.moveTo(self.thing)
250 newGarment.nowWornBy(self)
251
252
253 def takeOff(self, garment):
254 """
255 Remove a garment which this player is wearing.
256
257 (Note: no error checking is currently performed to see if this garment
258 is actually already worn by this L{Wearer}.)
259
260 @param garment: the article of clothing to remove.
261
262 @type garment: L{Garment}
263
264 @raise InaccessibleGarment: if the garment is obscured by any other
265 clothing, and is therefore not in the top slot for any of the slots
266 it occupies. For example, if you put on an undershirt, then a
267 turtleneck, you can't remove the undershirt without removing the
268 turtleneck first.
269 """
270 gdict = self.getGarmentDict()
271 for slot in garment.garmentSlots:
272 if gdict[slot][-1] is not garment:
273 raise InaccessibleGarment(self, garment, gdict[slot][-1])
274 garment.noLongerWorn()
275
276
277 # IDescriptionContributor
278 def conceptualize(self):
279 """
280 Describe the list of clothing.
281 """
282 return ExpressClothing(self.thing, self.getGarmentDict())
283
284
285 # ILinkContributor
286 def links(self):
287 for garmentThing in self.store.query(objects.Thing,
288 attributes.AND(
289 Garment.thing == objects.Thing.storeID,
290 Garment.wearer == self)):
291 yield Link(self.thing.idea, garmentThing.idea)
292
293
294 # ILinkAnnotator
295 def annotationsFor(self, link, idea):
296 """
297 Tell the containment system to disregard containment relationships for
298 which I will generate a link.
299 """
300 if list(link.of(iimaginary.IContainmentRelationship)):
301 if link.source.delegate is self.thing:
302 clothing = iimaginary.IClothing(link.target.delegate, None)
303 if clothing is not None:
304 if clothing.wearer is self:
305 yield _DisregardYourWearingIt()
306
307
308
309class _DisregardYourWearingIt(object):
310 """
311 This is an annotation, produced by L{Wearer} for containment relationships
312 between people (who are containers) and the clothing that they're wearing.
313 A hopefully temporary workaround for the fact that clothing is rendered in
314 its own way and therefor shouldn't show up in the list of a person's
315 contents.
316 """
317 implements(iimaginary.IElectromagneticMedium)
318
319 def isOpaque(self):
320 """
321 I am opaque, so that clothing will show up only once (in your "wearing"
322 list, rather than there and in your "contained" list), and obscured
323 clothing won't show up at all.
324 """
325 return True
326
327
328
329class ExpressClothing(language.BaseExpress):
330 def __init__(self, thing, garments):
331 self.thing = thing
332 self.garments = garments
333
334
335 def vt102(self, observer):
336 heshe = language.Noun(self.thing).heShe()
337 L = _orderTopClothingByGlobalSlotList(self.garments)
338 if L is None:
339 return language.Sentence([heshe, u' is naked.']).vt102(observer)
340 return language.Sentence([
341 heshe,
342 u' is wearing ',
343 language.ItemizedList([language.Noun(g.thing).nounPhrase()
344 for g in L]),
345 u'.']).vt102(observer)
346
347
348
349createShirt = createCreator(
350 (Garment, dict(garmentDescription=u'an undescribed shirt',
351 bulk=2,
352 garmentSlots=[GarmentSlot.CHEST,
353 GarmentSlot.BACK,
354 GarmentSlot.RIGHT_ARM,
355 GarmentSlot.LEFT_ARM])))
356
357
358createUnderwear = createCreator(
359 (Garment, dict(garmentDescription=u'an undescribed pair of underwear',
360 bulk=1,
361 garmentSlots=[GarmentSlot.WAIST])))
362
363createPants = createCreator(
364 (Garment, dict(garmentDescription=u'an undescribed pair of pants',
365 bulk=2,
366 garmentSlots=[GarmentSlot.RIGHT_LEG,
367 GarmentSlot.LEFT_LEG,
368 GarmentSlot.WAIST,
369 GarmentSlot.LEFT_ANKLE,
370 GarmentSlot.RIGHT_ANKLE])))
3710
=== removed file 'Imaginary/imaginary/idea.py'
--- Imaginary/imaginary/idea.py 2010-04-24 18:00:14 +0000
+++ Imaginary/imaginary/idea.py 1970-01-01 00:00:00 +0000
@@ -1,625 +0,0 @@
1# -*- test-case-name: imaginary -*-
2
3"""
4This module implements a highly abstract graph-traversal system for actions and
5events to locate the objects which can respond to them. The top-level
6entry-point to this system is L{Idea.obtain}.
7
8It also implements several basic retrievers related to visibility and physical
9reachability.
10"""
11
12from zope.interface import implements
13from epsilon.structlike import record
14
15from imaginary.iimaginary import (
16 INameable, ILitLink, IThing, IObstruction, IElectromagneticMedium,
17 IDistance, IRetriever, IExit)
18
19
20
21class Link(record("source target")):
22 """
23 A L{Link} is a connection between two L{Idea}s in a L{Path}.
24
25 @ivar source: the idea that this L{Link} originated from.
26 @type source: L{Idea}
27
28 @ivar target: the idea that this L{Link} refers to.
29 @type target: L{Idea}
30 """
31
32 def __init__(self, *a, **k):
33 super(Link, self).__init__(*a, **k)
34 self.annotations = []
35
36
37 def annotate(self, annotations):
38 """
39 Annotate this link with a list of annotations.
40 """
41 self.annotations.extend(annotations)
42
43
44 def of(self, interface):
45 """
46 Yield all annotations on this link which provide the given interface.
47 """
48 for annotation in self.annotations:
49 provider = interface(annotation, None)
50 if provider is not None:
51 yield provider
52
53
54
55class Path(record('links')):
56 """
57 A list of L{Link}s.
58 """
59
60 def of(self, interface):
61 """
62 @return: an iterator of providers of interfaces, adapted from each link
63 in this path.
64 """
65 for link in self.links:
66 for annotation in link.of(interface):
67 yield annotation
68
69
70 def eachTargetAs(self, interface):
71 """
72 @return: an iterable of all non-None results of each L{Link.targetAs}
73 method in this L{Path}'s C{links} attribute.
74 """
75 for link in self.links:
76 provider = interface(link.target.delegate, None)
77 if provider is not None:
78 yield provider
79
80
81 def targetAs(self, interface):
82 """
83 Retrieve the target of the last link of this path, its final
84 destination, as a given interface.
85
86 @param interface: the interface to retrieve.
87 @type interface: L{zope.interface.interfaces.IInterface}
88
89 @return: the last link's target, adapted to the given interface, or
90 C{None} if no appropriate adapter or component exists.
91 @rtype: C{interface} or C{NoneType}
92 """
93 return interface(self.links[-1].target.delegate, None)
94
95
96 def isCyclic(self):
97 """
98 Determine if this path is cyclic, to avoid descending down infinite
99 loops.
100
101 @return: a boolean indicating whether this L{Path} is cyclic or not,
102 i.e. whether the L{Idea} its last link points at is the source of
103 any of its links.
104 """
105 if len(self.links) < 2:
106 return False
107 return (self.links[-1].target in (x.source for x in self.links))
108
109
110 def to(self, link):
111 """
112 Create a new path, extending this one by one new link.
113 """
114 return Path(self.links + [link])
115
116
117 def __repr__(self):
118 """
119 @return: an expanded pretty-printed representation of this Path,
120 suitable for debugging.
121 """
122 s = 'Path('
123 for link in self.links:
124 dlgt = link.target.delegate
125 src = link.source.delegate
126 s += "\n\t"
127 s += repr(getattr(src, 'name', src))
128 s += " => "
129 s += repr(getattr(dlgt, 'name', dlgt))
130 s += " "
131 s += repr(link.annotations)
132 s += ')'
133 return s
134
135
136
137class Idea(record("delegate linkers annotators")):
138 """
139 Consider a person's activities with the world around them as having two
140 layers. One is a physical layer, out in the world, composed of matter and
141 energy. The other is a cognitive layer, internal to the person, composed
142 of ideas about that matter and energy.
143
144 For example, when a person wants to sit in a wooden chair, they must first
145 visually locate the arrangement of wood in question, make the determination
146 of that it is a "chair" based on its properties, and then perform the
147 appropriate actions to sit upon it.
148
149 However, a person may also interact with symbolic abstractions rather than
150 physical objects. They may read a word, or point at a window on a computer
151 screen. An L{Idea} is a representation of the common unit that can be
152 referred to in this way.
153
154 Both physical and cognitive layers are present in Imaginary. The cognitive
155 layer is modeled by L{imaginary.idea}. The physical layer is modeled by a
156 rudimentary point-of-interest simulation in L{imaginary.objects}. An
157 L{imaginary.thing.Thing} is a physical object; an L{Idea} is a node in a
158 non-physical graph, related by links that are annotated to describe the
159 nature of the relationship between it and other L{Idea}s.
160
161 L{Idea} is the most abstract unit of simulation. It does not have any
162 behavior or simulation semantics of its own; it merely ties together
163 different related systems.
164
165 An L{Idea} is composed of a C{delegate}, which is an object that implements
166 simulation-defined interfaces; a list of L{ILinkContributor}s, which
167 produce L{Link}s to other L{Idea}s, an a set of C{ILinkAnnotator}s, which
168 apply annotations (which themselves implement simulation-defined
169 link-annotation interfaces) to those links.
170
171 Each L{imaginary.thing.Thing} has a corresponding L{Idea} to represent it
172 in the simulation. The physical simulation defines only a few types of
173 links: objects have links to their containers, containers have links to
174 their contents, rooms have links to their exits, exits have links to their
175 destinations. Any L{imaginary.thing.Thing} can have a powerup applied to
176 it which adds to the list of linkers or annotators for its L{Idea},
177 however, which allows users to create arbitrary objects.
178
179 For example, the target of the "look" action must implement
180 L{imaginary.iimaginary.IVisible}, but need not be a
181 L{iimaginary.objects.Thing}. A simulation might want to provide a piece of
182 graffiti that you could look at, but would not be a physical object, in the
183 sense that you couldn't pick it up, weigh it, push it, etc. Such an object
184 could be implemented as a powerup for both
185 L{imaginary.iimaginary.IDescriptionContributor}, which would impart some
186 short flavor text to the room, and L{imaginary.iimaginary.IVisible}, which
187 would be an acceptable target of 'look'. The
188 L{imaginary.iimaginary.IVisible} implementation could even be an in-memory
189 object, not stored in the database at all; and there could be different
190 implementations for different observers, depending on their level of
191 knowledge about the in-world graffiti.
192
193 @ivar delegate: this object is the object which may be adaptable to a set
194 of interfaces. This L{Idea} delegates all adaptation to its delegate.
195 In many cases (when referring to a physical object), this will be an
196 L{imaginary.thing.Thing}, but not necessarily.
197
198 @ivar linkers: a L{list} of L{ILinkContributor}s which are used to gather
199 L{Link}s from this L{Idea} during L{Idea.obtain} traversal.
200
201 @ivar annotators: a L{list} of L{ILinkAnnotator}s which are used to annotate
202 L{Link}s gathered from this L{Idea} via the C{linkers} list.
203 """
204
205 def __init__(self, delegate):
206 super(Idea, self).__init__(delegate, [], [])
207
208
209 def _allLinks(self):
210 """
211 Return an iterator of all L{Links} away from this idea.
212 """
213 for linker in self.linkers:
214 for link in linker.links():
215 yield link
216
217
218 def _applyAnnotators(self, linkiter):
219 """
220 Apply my list of annotators to each link in the given iterable.
221 """
222 for link in linkiter:
223 self._annotateOneLink(link)
224 yield link
225
226
227 def _annotateOneLink(self, link):
228 """
229 Apply all L{ILinkAnnotator}s in this L{Idea}'s C{annotators} list.
230 """
231 allAnnotations = []
232 for annotator in self.annotators:
233 # XXX important to test: annotators shouldn't mutate the links.
234 # The annotators show up in a non-deterministic order, so in order
235 # to facilitate a consistent view of the link in annotationsFor(),
236 # all annotations are applied at the end.
237 allAnnotations.extend(annotator.annotationsFor(link, self))
238 link.annotate(allAnnotations)
239
240
241 def obtain(self, retriever):
242 """
243 Traverse the graph of L{Idea}s, starting with C{self}, looking for
244 objects which the given L{IRetriever} can retrieve.
245
246 The graph will be traversed by looking at all the links generated by
247 this L{Idea}'s C{linkers}, only continuing down those links for which
248 the given L{IRetriever}'s C{shouldKeepGoing} returns L{True}.
249
250 @param retriever: an object which will be passed each L{Path} in turn,
251 discovered during traversal of the L{Idea} graph. If any
252 invocation of L{IRetriever.retrieve} on this parameter should
253 succeed, that will be yielded as a result from this method.
254 @type retriever: L{IRetriever}
255
256 @return: a generator which yields the results of C{retriever.retrieve}
257 which are not L{None}.
258 """
259 return ObtainResult(self, retriever)
260
261
262 def _doObtain(self, retriever, path, reasonsWhyNot):
263 """
264 A generator that implements the logic for obtain()
265 """
266 if path is None:
267 # Special case: we only get a self->self link if we are the
268 # beginning _and_ the end.
269 path = Path([])
270 selfLink = Link(self, self)
271 self._annotateOneLink(selfLink)
272 finalPath = path.to(selfLink)
273 else:
274 finalPath = Path(path.links[:])
275 self._annotateOneLink(finalPath.links[-1])
276
277 result = retriever.retrieve(finalPath)
278 objections = set(retriever.objectionsTo(finalPath, result))
279 reasonsWhyNot |= objections
280 if result is not None:
281 if not objections:
282 yield result
283
284 for link in self._applyAnnotators(self._allLinks()):
285 subpath = path.to(link)
286 if subpath.isCyclic():
287 continue
288 if retriever.shouldKeepGoing(subpath):
289 for obtained in link.target._doObtain(retriever, subpath, reasonsWhyNot):
290 yield obtained
291
292
293
294class ObtainResult(record("idea retriever")):
295 """
296 The result of L{Idea.obtain}, this provides an iterable of results.
297
298 @ivar reasonsWhyNot: If this iterator has already been exhausted, this will
299 be a C{set} of L{IWhyNot} objects explaining possible reasons why there
300 were no results. For example, if the room where the player attempted
301 to obtain targets is dark, this may contain an L{IWhyNot} provider.
302 However, until this iterator has been exhausted, it will be C{None}.
303 @type reasonsWhyNot: C{set} of L{IWhyNot}, or C{NoneType}
304
305 @ivar idea: the L{Idea} that L{Idea.obtain} was invoked on.
306 @type idea: L{Idea}
307
308 @ivar retriever: The L{IRetriever} that L{Idea.obtain} was invoked with.
309 @type retriever: L{IRetriever}
310 """
311
312 reasonsWhyNot = None
313
314 def __iter__(self):
315 """
316 A generator which yields each result of the query, then sets
317 C{reasonsWhyNot}.
318 """
319 reasonsWhyNot = set()
320 for result in self.idea._doObtain(self.retriever, None, reasonsWhyNot):
321 yield result
322 self.reasonsWhyNot = reasonsWhyNot
323
324
325
326class DelegatingRetriever(object):
327 """
328 A delegating retriever, so that retrievers can be easily composed.
329
330 See the various methods marked for overriding.
331
332 @ivar retriever: A retriever to delegate most operations to.
333 @type retriever: L{IRetriever}
334 """
335
336 implements(IRetriever)
337
338 def __init__(self, retriever):
339 """
340 Create a delegator with a retriever to delegate to.
341 """
342 self.retriever = retriever
343
344
345 def moreObjectionsTo(self, path, result):
346 """
347 Override in subclasses to yield objections to add to this
348 L{DelegatingRetriever}'s C{retriever}'s C{objectionsTo}.
349
350 By default, offer no additional objections.
351 """
352 return []
353
354
355 def objectionsTo(self, path, result):
356 """
357 Concatenate C{self.moreObjectionsTo} with C{self.moreObjectionsTo}.
358 """
359 for objection in self.retriever.objectionsTo(path, result):
360 yield objection
361 for objection in self.moreObjectionsTo(path, result):
362 yield objection
363
364
365 def shouldStillKeepGoing(self, path):
366 """
367 Override in subclasses to halt traversal via a C{False} return value for
368 C{shouldKeepGoing} if this L{DelegatingRetriever}'s C{retriever}'s
369 C{shouldKeepGoing} returns C{True}.
370
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: