Merge lp:~ashfall/divmod.org/1320678-remove-imaginary into lp:divmod.org
- 1320678-remove-imaginary
- Merge into trunk
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 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tristan Seligmann | Approve | ||
Review via email: mp+219969@code.launchpad.net |
Commit message
Description of the change
Removes Imaginary from lp:divmod.org
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'Divmod.pth' | |||
2 | --- Divmod.pth 2013-06-27 06:02:46 +0000 | |||
3 | +++ Divmod.pth 2014-05-18 17:55:49 +0000 | |||
4 | @@ -1,4 +1,4 @@ | |||
6 | 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 -*- |
7 | 2 | Axiom | 2 | Axiom |
8 | 3 | Combinator | 3 | Combinator |
9 | 4 | Epsilon | 4 | Epsilon |
10 | @@ -8,6 +8,4 @@ | |||
11 | 8 | Reverend | 8 | Reverend |
12 | 9 | Sine | 9 | Sine |
13 | 10 | Hyperbola | 10 | Hyperbola |
14 | 11 | Imaginary | ||
15 | 12 | Imaginary/ExampleGame | ||
16 | 13 | Prime | 11 | Prime |
17 | 14 | 12 | ||
18 | === removed directory 'Imaginary' | |||
19 | === removed file 'Imaginary/COMPATIBILITY.txt' | |||
20 | --- Imaginary/COMPATIBILITY.txt 2009-08-17 02:40:03 +0000 | |||
21 | +++ Imaginary/COMPATIBILITY.txt 1970-01-01 00:00:00 +0000 | |||
22 | @@ -1,6 +0,0 @@ | |||
23 | 1 | Imaginary provides _no_ source-level compatibility from one release to the next. | ||
24 | 2 | |||
25 | 3 | Efforts will be made to provide database level compatibility (i.e. data from | ||
26 | 4 | one release can be loaded with the next). However, although we will try to | ||
27 | 5 | ensure that data will load, there is no guarantee that it will be meaningfully | ||
28 | 6 | upgraded yet. | ||
29 | 7 | 0 | ||
30 | === removed file 'Imaginary/DEPS.txt' | |||
31 | --- Imaginary/DEPS.txt 2013-07-10 00:22:10 +0000 | |||
32 | +++ Imaginary/DEPS.txt 1970-01-01 00:00:00 +0000 | |||
33 | @@ -1,4 +0,0 @@ | |||
34 | 1 | Python 2.7 | ||
35 | 2 | Twisted 13.1 | ||
36 | 3 | Epsilon SVN-Trunk | ||
37 | 4 | Axiom SVN-Trunk | ||
38 | 5 | 0 | ||
39 | === removed directory 'Imaginary/ExampleGame' | |||
40 | === removed directory 'Imaginary/ExampleGame/examplegame' | |||
41 | === removed file 'Imaginary/ExampleGame/examplegame/__init__.py' | |||
42 | === removed file 'Imaginary/ExampleGame/examplegame/furniture.py' | |||
43 | --- Imaginary/ExampleGame/examplegame/furniture.py 2009-08-17 02:40:03 +0000 | |||
44 | +++ Imaginary/ExampleGame/examplegame/furniture.py 1970-01-01 00:00:00 +0000 | |||
45 | @@ -1,139 +0,0 @@ | |||
46 | 1 | # -*- test-case-name: examplegame.test.test_furniture -*- | ||
47 | 2 | |||
48 | 3 | """ | ||
49 | 4 | |||
50 | 5 | Furniture is the mass noun for the movable objects which may support the | ||
51 | 6 | human body (seating furniture and beds), provide storage, or hold objects | ||
52 | 7 | on horizontal surfaces above the ground. | ||
53 | 8 | |||
54 | 9 | -- Wikipedia, http://en.wikipedia.org/wiki/Furniture | ||
55 | 10 | |||
56 | 11 | L{imaginary.furniture} contains L{Action}s which allow players to interact with | ||
57 | 12 | household objects such as chairs and tables, and L{Enhancement}s which allow | ||
58 | 13 | L{Thing}s to behave as same. | ||
59 | 14 | |||
60 | 15 | This has the same implementation weakness as L{examplegame.tether}, in that it | ||
61 | 16 | needs to make some assumptions about who is moving what in its restrictions of | ||
62 | 17 | movement; it should be moved into imaginary proper when that can be properly | ||
63 | 18 | addressed. It's also a bit too draconian in terms of preventing the player | ||
64 | 19 | from moving for any reason just because they're seated. However, it's a | ||
65 | 20 | workable approximation for some uses, and thus useful as an example. | ||
66 | 21 | """ | ||
67 | 22 | |||
68 | 23 | from zope.interface import implements | ||
69 | 24 | |||
70 | 25 | from axiom.item import Item | ||
71 | 26 | from axiom.attributes import reference | ||
72 | 27 | |||
73 | 28 | from imaginary.iimaginary import ISittable, IContainer, IMovementRestriction | ||
74 | 29 | from imaginary.eimaginary import ActionFailure | ||
75 | 30 | from imaginary.events import ThatDoesntWork | ||
76 | 31 | from imaginary.language import Noun | ||
77 | 32 | from imaginary.action import Action, TargetAction | ||
78 | 33 | from imaginary.events import Success | ||
79 | 34 | from imaginary.enhancement import Enhancement | ||
80 | 35 | from imaginary.objects import Container | ||
81 | 36 | from imaginary.pyparsing import Literal, Optional, restOfLine | ||
82 | 37 | |||
83 | 38 | class Sit(TargetAction): | ||
84 | 39 | """ | ||
85 | 40 | An action allowing a player to sit down in a chair. | ||
86 | 41 | """ | ||
87 | 42 | expr = (Literal("sit") + Optional(Literal("on")) + | ||
88 | 43 | restOfLine.setResultsName("target")) | ||
89 | 44 | |||
90 | 45 | targetInterface = ISittable | ||
91 | 46 | |||
92 | 47 | def do(self, player, line, target): | ||
93 | 48 | """ | ||
94 | 49 | Do the action; sit down. | ||
95 | 50 | """ | ||
96 | 51 | target.seat(player) | ||
97 | 52 | |||
98 | 53 | actorMessage=["You sit in ", | ||
99 | 54 | Noun(target.thing).definiteNounPhrase(),"."] | ||
100 | 55 | otherMessage=[player.thing, " sits in ", | ||
101 | 56 | Noun(target.thing).definiteNounPhrase(),"."] | ||
102 | 57 | Success(actor=player.thing, location=player.thing.location, | ||
103 | 58 | actorMessage=actorMessage, | ||
104 | 59 | otherMessage=otherMessage).broadcast() | ||
105 | 60 | |||
106 | 61 | |||
107 | 62 | class Stand(Action): | ||
108 | 63 | """ | ||
109 | 64 | Stand up from a sitting position. | ||
110 | 65 | """ | ||
111 | 66 | expr = (Literal("stand") + Optional(Literal("up"))) | ||
112 | 67 | |||
113 | 68 | def do(self, player, line): | ||
114 | 69 | """ | ||
115 | 70 | Do the action; stand up. | ||
116 | 71 | """ | ||
117 | 72 | # XXX This is wrong. I should be issuing an obtain() query to find | ||
118 | 73 | # something that qualifies as "my location" or "the thing I'm already | ||
119 | 74 | # sitting in". | ||
120 | 75 | chair = ISittable(player.thing.location, None) | ||
121 | 76 | if chair is None: | ||
122 | 77 | raise ActionFailure(ThatDoesntWork( | ||
123 | 78 | actor=player.thing, | ||
124 | 79 | actorMessage=["You're already standing."])) | ||
125 | 80 | chair.unseat(player) | ||
126 | 81 | Success(actor=player.thing, location=player.thing.location, | ||
127 | 82 | actorMessage=["You stand up."], | ||
128 | 83 | otherMessage=[player.thing, " stands up."]).broadcast() | ||
129 | 84 | |||
130 | 85 | |||
131 | 86 | |||
132 | 87 | class Chair(Enhancement, Item): | ||
133 | 88 | """ | ||
134 | 89 | A chair is a thing you can sit in. | ||
135 | 90 | """ | ||
136 | 91 | |||
137 | 92 | implements(ISittable, IMovementRestriction) | ||
138 | 93 | |||
139 | 94 | powerupInterfaces = [ISittable] | ||
140 | 95 | |||
141 | 96 | thing = reference() | ||
142 | 97 | container = reference() | ||
143 | 98 | |||
144 | 99 | |||
145 | 100 | def movementImminent(self, movee, destination): | ||
146 | 101 | """ | ||
147 | 102 | A player tried to move while they were seated. Prevent them from doing | ||
148 | 103 | so, noting that they must stand first. | ||
149 | 104 | |||
150 | 105 | (Assume the player was trying to move themselves, although there's no | ||
151 | 106 | way to know currently.) | ||
152 | 107 | """ | ||
153 | 108 | raise ActionFailure(ThatDoesntWork( | ||
154 | 109 | actor=movee, | ||
155 | 110 | actorMessage=u"You can't do that while sitting down.")) | ||
156 | 111 | |||
157 | 112 | |||
158 | 113 | def applyEnhancement(self): | ||
159 | 114 | """ | ||
160 | 115 | Apply this enhancement to this L{Chair}'s thing, creating a | ||
161 | 116 | L{Container} to hold the seated player, if necessary. | ||
162 | 117 | """ | ||
163 | 118 | super(Chair, self).applyEnhancement() | ||
164 | 119 | container = IContainer(self.thing, None) | ||
165 | 120 | if container is None: | ||
166 | 121 | container = Container.createFor(self.thing, capacity=300) | ||
167 | 122 | self.container = container | ||
168 | 123 | |||
169 | 124 | |||
170 | 125 | def seat(self, player): | ||
171 | 126 | """ | ||
172 | 127 | The player sat down on this chair; place them into it and prevent them | ||
173 | 128 | from moving elsewhere until they stand up. | ||
174 | 129 | """ | ||
175 | 130 | player.thing.moveTo(self.container) | ||
176 | 131 | player.thing.powerUp(self, IMovementRestriction) | ||
177 | 132 | |||
178 | 133 | |||
179 | 134 | def unseat(self, player): | ||
180 | 135 | """ | ||
181 | 136 | The player stood up; remove them from this chair. | ||
182 | 137 | """ | ||
183 | 138 | player.thing.powerDown(self, IMovementRestriction) | ||
184 | 139 | player.thing.moveTo(self.container.thing.location) | ||
185 | 140 | 0 | ||
186 | === removed file 'Imaginary/ExampleGame/examplegame/glass.py' | |||
187 | --- Imaginary/ExampleGame/examplegame/glass.py 2009-08-17 02:40:03 +0000 | |||
188 | +++ Imaginary/ExampleGame/examplegame/glass.py 1970-01-01 00:00:00 +0000 | |||
189 | @@ -1,72 +0,0 @@ | |||
190 | 1 | # -*- test-case-name: examplegame.test.test_glass -*- | ||
191 | 2 | """ | ||
192 | 3 | This example implements a transparent container that you can see, but not | ||
193 | 4 | reach, the contents of. | ||
194 | 5 | """ | ||
195 | 6 | from zope.interface import implements | ||
196 | 7 | |||
197 | 8 | from axiom.item import Item | ||
198 | 9 | from axiom.attributes import reference | ||
199 | 10 | |||
200 | 11 | from imaginary.iimaginary import ( | ||
201 | 12 | ILinkContributor, IWhyNot, IObstruction, IContainer) | ||
202 | 13 | from imaginary.enhancement import Enhancement | ||
203 | 14 | from imaginary.objects import ContainmentRelationship | ||
204 | 15 | from imaginary.idea import Link | ||
205 | 16 | |||
206 | 17 | class _CantReachThroughGlassBox(object): | ||
207 | 18 | """ | ||
208 | 19 | This object provides an explanation for why the user cannot access a target | ||
209 | 20 | that is inside a L{imaginary.objects.Thing} enhanced with a L{GlassBox}. | ||
210 | 21 | """ | ||
211 | 22 | implements(IWhyNot) | ||
212 | 23 | |||
213 | 24 | def tellMeWhyNot(self): | ||
214 | 25 | """ | ||
215 | 26 | Return a simple message explaining that the user can't reach through | ||
216 | 27 | the glass box. | ||
217 | 28 | """ | ||
218 | 29 | return "You can't reach through the glass box." | ||
219 | 30 | |||
220 | 31 | |||
221 | 32 | |||
222 | 33 | class _ObstructedByGlass(object): | ||
223 | 34 | """ | ||
224 | 35 | This is an annotation on a link between two objects which represents a | ||
225 | 36 | physical obstruction between them. It is used to annotate between a | ||
226 | 37 | L{GlassBox} and its contents, so you can see them without reaching them. | ||
227 | 38 | """ | ||
228 | 39 | implements(IObstruction) | ||
229 | 40 | |||
230 | 41 | def whyNot(self): | ||
231 | 42 | """ | ||
232 | 43 | @return: an object which explains why you can't reach through the glass | ||
233 | 44 | box. | ||
234 | 45 | """ | ||
235 | 46 | return _CantReachThroughGlassBox() | ||
236 | 47 | |||
237 | 48 | |||
238 | 49 | |||
239 | 50 | class GlassBox(Item, Enhancement): | ||
240 | 51 | """ | ||
241 | 52 | L{GlassBox} is an L{Enhancement} which modifies a container such that it is | ||
242 | 53 | contained. | ||
243 | 54 | """ | ||
244 | 55 | |||
245 | 56 | powerupInterfaces = (ILinkContributor,) | ||
246 | 57 | |||
247 | 58 | thing = reference() | ||
248 | 59 | |||
249 | 60 | def links(self): | ||
250 | 61 | """ | ||
251 | 62 | If the container attached to this L{GlassBox}'s C{thing} is closed, | ||
252 | 63 | yield its list of contents with each link annotated with | ||
253 | 64 | L{_ObstructedByGlass}, indicating that the object cannot be reached. | ||
254 | 65 | """ | ||
255 | 66 | container = IContainer(self.thing) | ||
256 | 67 | if container.closed: | ||
257 | 68 | for content in container.getContents(): | ||
258 | 69 | link = Link(self.thing.idea, content.idea) | ||
259 | 70 | link.annotate([_ObstructedByGlass(), | ||
260 | 71 | ContainmentRelationship(container)]) | ||
261 | 72 | yield link | ||
262 | 73 | 0 | ||
263 | === removed file 'Imaginary/ExampleGame/examplegame/japanese.py' | |||
264 | --- Imaginary/ExampleGame/examplegame/japanese.py 2007-08-17 04:46:38 +0000 | |||
265 | +++ Imaginary/ExampleGame/examplegame/japanese.py 1970-01-01 00:00:00 +0000 | |||
266 | @@ -1,43 +0,0 @@ | |||
267 | 1 | """ | ||
268 | 2 | Japanese language data. | ||
269 | 3 | |||
270 | 4 | This module contains a dict named 'hiragana' which maps hiragana | ||
271 | 5 | unicode characters to romaji pronunciations, as well as a | ||
272 | 6 | 'romajiToHiragana' dict which maps romaji pronunciation to *lists* of | ||
273 | 7 | hiragana characters. There are multiple hiragana characters with the | ||
274 | 8 | same pronunciation, thus the multiple values per romaji in the | ||
275 | 9 | romajiToHiragana dict. | ||
276 | 10 | |||
277 | 11 | """ | ||
278 | 12 | |||
279 | 13 | |||
280 | 14 | # Hiragana. | ||
281 | 15 | hiragana = { | ||
282 | 16 | u'\u3042': 'A', u'\u3044': 'I', u'\u3046': 'U', u'\u3048': 'E', | ||
283 | 17 | u'\u3081': 'ME', u'\u3080': 'MU', u'\u3082': 'MO', u'\u3084': 'YA', | ||
284 | 18 | u'\u3086': 'YU', u'\u3089': 'RA', u'\u3088': 'YO', u'\u308b': 'RU', | ||
285 | 19 | u'\u308a': 'RI', u'\u308d': 'RO', u'\u308c': 'RE', u'\u308f': 'WA', | ||
286 | 20 | u'\u3091': 'WE', u'\u3090': 'WI', u'\u3093': 'N', u'\u3092': 'WO', | ||
287 | 21 | u'\u304b': 'KA', u'\u304a': 'O', u'\u304d': 'KI', u'\u304c': 'GA', | ||
288 | 22 | u'\u304f': 'KU', u'\u304e': 'GI', u'\u3051': 'KE', u'\u3050': 'GU', | ||
289 | 23 | u'\u3053': 'KO', u'\u3052': 'GE', u'\u3055': 'SA', u'\u3054': 'GO', | ||
290 | 24 | u'\u3057': 'SHI',u'\u3056': 'ZA', u'\u3059': 'SU', u'\u3058': 'JI', | ||
291 | 25 | u'\u305b': 'SE', u'\u305a': 'ZU', u'\u305d': 'SO', u'\u305c': 'ZE', | ||
292 | 26 | u'\u305f': 'TA', u'\u305e': 'ZO', u'\u3061': 'CHI', u'\u3060': 'DA', | ||
293 | 27 | u'\u3062': 'JI', u'\u3065': 'ZU', u'\u3064': 'TSU', u'\u3067': 'DE', | ||
294 | 28 | u'\u3066': 'TE', u'\u3069': 'DO', u'\u3068': 'TO', u'\u306b': 'NI', | ||
295 | 29 | u'\u306a': 'NA', u'\u306d': 'NE', u'\u306c': 'NU', u'\u306f': 'HA', | ||
296 | 30 | u'\u306e': 'NO', u'\u3071': 'PA', u'\u3070': 'BA', u'\u3073': 'BI', | ||
297 | 31 | u'\u3072': 'HI', u'\u3075': 'FU', u'\u3074': 'PI', u'\u3077': 'PU', | ||
298 | 32 | u'\u3076': 'BU', u'\u3079': 'BE', u'\u3078': 'HE', u'\u307b': 'HO', | ||
299 | 33 | u'\u307a': 'PE', u'\u307d': 'PO', u'\u307c': 'BO', u'\u307f': 'MI', | ||
300 | 34 | u'\u307e': 'MA'} | ||
301 | 35 | |||
302 | 36 | |||
303 | 37 | romajiToHiragana = {} | ||
304 | 38 | for k, v in hiragana.iteritems(): | ||
305 | 39 | romajiToHiragana.setdefault(v, []).append(k) | ||
306 | 40 | |||
307 | 41 | # Katakana. | ||
308 | 42 | # katakana = { | ||
309 | 43 | # } | ||
310 | 44 | 0 | ||
311 | === removed file 'Imaginary/ExampleGame/examplegame/mice.py' | |||
312 | --- Imaginary/ExampleGame/examplegame/mice.py 2009-08-17 02:40:03 +0000 | |||
313 | +++ Imaginary/ExampleGame/examplegame/mice.py 1970-01-01 00:00:00 +0000 | |||
314 | @@ -1,264 +0,0 @@ | |||
315 | 1 | # -*- test-case-name: examplegame.test.test_mice,examplegame.test.test_japanese -*- | ||
316 | 2 | |||
317 | 3 | import random | ||
318 | 4 | |||
319 | 5 | from zope.interface import implements | ||
320 | 6 | |||
321 | 7 | from axiom import item, attributes | ||
322 | 8 | |||
323 | 9 | from imaginary import iimaginary, events, objects, action, language | ||
324 | 10 | from examplegame import japanese | ||
325 | 11 | |||
326 | 12 | |||
327 | 13 | class Mouse(item.Item): | ||
328 | 14 | """ | ||
329 | 15 | A silly mouse which squeaks when actors enter the room it is in. | ||
330 | 16 | |||
331 | 17 | @ivar _callLater: The scheduling function to use. Override in unit | ||
332 | 18 | tests only. | ||
333 | 19 | """ | ||
334 | 20 | |||
335 | 21 | implements(iimaginary.IEventObserver) | ||
336 | 22 | |||
337 | 23 | squeakiness = attributes.integer(doc=""" | ||
338 | 24 | How likely the mouse is to squeak when intruded upon (0 - 100). | ||
339 | 25 | |||
340 | 26 | This mouse is so angry that he will pretty much always squeak. | ||
341 | 27 | """, default=100) | ||
342 | 28 | |||
343 | 29 | _callLater = attributes.inmemory() | ||
344 | 30 | |||
345 | 31 | def activate(self): | ||
346 | 32 | from twisted.internet import reactor | ||
347 | 33 | self._callLater = reactor.callLater | ||
348 | 34 | |||
349 | 35 | |||
350 | 36 | def prepare(self, concept): | ||
351 | 37 | """ | ||
352 | 38 | An event was received. Squeak if it represents the arrival of a dude. | ||
353 | 39 | """ | ||
354 | 40 | if isinstance(concept, events.ArrivalEvent): | ||
355 | 41 | return lambda: self._callLater(0, self.squeak) | ||
356 | 42 | return lambda: None | ||
357 | 43 | |||
358 | 44 | |||
359 | 45 | def squeak(self): | ||
360 | 46 | actor = self.store.findUnique( | ||
361 | 47 | objects.Actor, | ||
362 | 48 | objects.Actor._enduringIntelligence == self) | ||
363 | 49 | evt = events.Success( | ||
364 | 50 | actor=actor.thing, | ||
365 | 51 | otherMessage=u"SQUEAK!") | ||
366 | 52 | evt.broadcast() | ||
367 | 53 | |||
368 | 54 | |||
369 | 55 | |||
370 | 56 | class ChallengeCollision(Exception): | ||
371 | 57 | """ | ||
372 | 58 | Raised when a L{HiraganaMouse} is asked to start issuing challenges when it | ||
373 | 59 | is already issuing challenges. | ||
374 | 60 | """ | ||
375 | 61 | |||
376 | 62 | |||
377 | 63 | |||
378 | 64 | class ChallengeVacuum(Exception): | ||
379 | 65 | """ | ||
380 | 66 | Raised when a L{HiraganaMouse} is asked to stop issuing challenges when it | ||
381 | 67 | is already not issuing challenges. | ||
382 | 68 | """ | ||
383 | 69 | |||
384 | 70 | |||
385 | 71 | |||
386 | 72 | class HiraganaMouse(item.Item): | ||
387 | 73 | """ | ||
388 | 74 | A mouse which occasionally challenges those in its location to | ||
389 | 75 | transliterate Hiragana. | ||
390 | 76 | |||
391 | 77 | @ivar _callLater: The scheduling function to use. Defaults to the | ||
392 | 78 | reactor's callLater method. This is parameterized for the sake of | ||
393 | 79 | unit tests. | ||
394 | 80 | """ | ||
395 | 81 | |||
396 | 82 | implements(iimaginary.IEventObserver) | ||
397 | 83 | |||
398 | 84 | challenging = attributes.boolean(doc=""" | ||
399 | 85 | Whether or not this mouse is currently creating random challenges. | ||
400 | 86 | """, default=False) | ||
401 | 87 | |||
402 | 88 | challengeInterval = attributes.integer(doc=""" | ||
403 | 89 | Number of seconds between challenges. | ||
404 | 90 | """, default=15, allowNone=False) | ||
405 | 91 | |||
406 | 92 | _currentChallenge = attributes.text(doc=""" | ||
407 | 93 | The Hiragana character which the mouse has most recently issued as a | ||
408 | 94 | challenge. | ||
409 | 95 | """, default=None) | ||
410 | 96 | |||
411 | 97 | |||
412 | 98 | _callLater = attributes.inmemory() | ||
413 | 99 | _currentChallengeCall = attributes.inmemory() | ||
414 | 100 | |||
415 | 101 | def activate(self): | ||
416 | 102 | from twisted.internet import reactor | ||
417 | 103 | self._callLater = reactor.callLater | ||
418 | 104 | |||
419 | 105 | def _actor(self): | ||
420 | 106 | """ | ||
421 | 107 | Get the h-mouse's associated actor. PRIVATE. WHY DID I DOCUMENT THIS. | ||
422 | 108 | """ | ||
423 | 109 | return self.store.findUnique( | ||
424 | 110 | objects.Actor, | ||
425 | 111 | objects.Actor._enduringIntelligence == self) | ||
426 | 112 | |||
427 | 113 | |||
428 | 114 | def _numDudes(self): | ||
429 | 115 | """ | ||
430 | 116 | Get the number of actors (other than the h-mouse) in the | ||
431 | 117 | h-mouse's location. PRIVATE. | ||
432 | 118 | """ | ||
433 | 119 | actor = self._actor() | ||
434 | 120 | numDudes = len([actor | ||
435 | 121 | for dude | ||
436 | 122 | in actor.thing.findProviders(iimaginary.IActor, 1) | ||
437 | 123 | if dude is not actor]) | ||
438 | 124 | return numDudes | ||
439 | 125 | |||
440 | 126 | |||
441 | 127 | def maybeChallenge(self): | ||
442 | 128 | """ | ||
443 | 129 | Start challenging if there is anyone around to challenge (and | ||
444 | 130 | this h-mouse isn't already challenging). | ||
445 | 131 | """ | ||
446 | 132 | if not self.challenging and self._numDudes() >= 1: | ||
447 | 133 | self.startChallenging() | ||
448 | 134 | |||
449 | 135 | |||
450 | 136 | def prepare(self, concept): | ||
451 | 137 | """ | ||
452 | 138 | An event was received. Start or stop challenging as | ||
453 | 139 | appropriate, based on whether there is anyone to challenge. | ||
454 | 140 | """ | ||
455 | 141 | if isinstance(concept, events.ArrivalEvent): | ||
456 | 142 | self.maybeChallenge() | ||
457 | 143 | elif isinstance(concept, events.DepartureEvent) and self._numDudes() == 0: | ||
458 | 144 | self.stopChallenging() | ||
459 | 145 | elif isinstance(concept, events.SpeechEvent) and concept.speaker is not self._actor().thing: | ||
460 | 146 | self.responseReceived(concept.speaker, concept.text) | ||
461 | 147 | return lambda: None | ||
462 | 148 | |||
463 | 149 | |||
464 | 150 | def startChallenging(self): | ||
465 | 151 | """ | ||
466 | 152 | Start shouting hiragana in the hope that someone knows what it means. | ||
467 | 153 | |||
468 | 154 | @raises ChallengeCollision: If this h-mouse is already challenging. | ||
469 | 155 | """ | ||
470 | 156 | if self.challenging: | ||
471 | 157 | raise ChallengeCollision() | ||
472 | 158 | |||
473 | 159 | self.challenging = True | ||
474 | 160 | self._scheduleChallenge() | ||
475 | 161 | |||
476 | 162 | |||
477 | 163 | def _scheduleChallenge(self): | ||
478 | 164 | """ | ||
479 | 165 | Schedule a challenge to happen in the number of seconds set in | ||
480 | 166 | the instance attribute 'challengeInterval'. | ||
481 | 167 | """ | ||
482 | 168 | self._currentChallengeCall = self._callLater(self.challengeInterval, | ||
483 | 169 | self._challengeAndRepeat) | ||
484 | 170 | |||
485 | 171 | |||
486 | 172 | def stopChallenging(self): | ||
487 | 173 | """ | ||
488 | 174 | Stop shouting hiragana. | ||
489 | 175 | |||
490 | 176 | @raises ChallengeVacuum: If this h-mouse is not currently challenging. | ||
491 | 177 | """ | ||
492 | 178 | if not self.challenging: | ||
493 | 179 | raise ChallengeVacuum() | ||
494 | 180 | |||
495 | 181 | self.challenging = False | ||
496 | 182 | |||
497 | 183 | self._currentChallenge = None | ||
498 | 184 | self._currentChallengeCall.cancel() | ||
499 | 185 | self._currentChallengeCall = None | ||
500 | 186 | |||
501 | 187 | |||
502 | 188 | def _challengeAndRepeat(self): | ||
503 | 189 | """ | ||
504 | 190 | Shout a challenge and then schedule another one. | ||
505 | 191 | """ | ||
506 | 192 | self.challenge() | ||
507 | 193 | self._scheduleChallenge() | ||
508 | 194 | |||
509 | 195 | |||
510 | 196 | def getCurrentChallenge(self): | ||
511 | 197 | """ | ||
512 | 198 | Return the Hiragana character which is this mouse's current challenge, | ||
513 | 199 | if it has one. | ||
514 | 200 | |||
515 | 201 | @rtype: C{unicode} or C{None} | ||
516 | 202 | """ | ||
517 | 203 | return self._currentChallenge | ||
518 | 204 | |||
519 | 205 | |||
520 | 206 | def vetteChallengeResponse(self, romajiResponse): | ||
521 | 207 | """ | ||
522 | 208 | Return True if the given response matches the current challenge, False | ||
523 | 209 | otherwise. | ||
524 | 210 | """ | ||
525 | 211 | hiragana = japanese.romajiToHiragana.get(romajiResponse.upper(), None) | ||
526 | 212 | return hiragana is not None and self.getCurrentChallenge() in hiragana | ||
527 | 213 | |||
528 | 214 | |||
529 | 215 | def responseReceived(self, responder, romajiResponse): | ||
530 | 216 | """ | ||
531 | 217 | Called when some speech is observed. | ||
532 | 218 | """ | ||
533 | 219 | me = self._actor().thing | ||
534 | 220 | if self.vetteChallengeResponse(romajiResponse): | ||
535 | 221 | self._currentChallenge = None | ||
536 | 222 | verb = u"salute" | ||
537 | 223 | else: | ||
538 | 224 | verb = u"bite" | ||
539 | 225 | evt = events.Success( | ||
540 | 226 | actor=me, | ||
541 | 227 | target=responder, | ||
542 | 228 | actorMessage=language.Sentence(["You ", verb, " ", responder, "."]), | ||
543 | 229 | targetMessage=language.Sentence([language.Noun(me).shortName(), " ", verb, "s you!"]), | ||
544 | 230 | otherMessage=language.Sentence([me, " ", verb, "s ", responder, "."])) | ||
545 | 231 | # Fuck the reactor, Fuck scheduling, why does responseReceived | ||
546 | 232 | # need to be concerned with these stupid scheduling details | ||
547 | 233 | # when all it wants to do is respond basically-immediately. | ||
548 | 234 | self._callLater(0, evt.broadcast) | ||
549 | 235 | |||
550 | 236 | |||
551 | 237 | def challenge(self, character=None): | ||
552 | 238 | """ | ||
553 | 239 | Say only a single random hiragana character. | ||
554 | 240 | """ | ||
555 | 241 | if character is None: | ||
556 | 242 | character = random.choice(japanese.hiragana.keys()) | ||
557 | 243 | self._currentChallenge = character | ||
558 | 244 | actor = self._actor() | ||
559 | 245 | action.Say().do(actor, None, character) | ||
560 | 246 | |||
561 | 247 | |||
562 | 248 | |||
563 | 249 | def createMouseCreator(mouseIntelligenceFactory): | ||
564 | 250 | """ | ||
565 | 251 | Create a createMouse function, which can be called to create a | ||
566 | 252 | mouse object. Used for the 'Create' command plugin system. | ||
567 | 253 | """ | ||
568 | 254 | def createMouse(**kw): | ||
569 | 255 | store = kw['store'] | ||
570 | 256 | mouse = objects.Thing(**kw) | ||
571 | 257 | mouseActor = objects.Actor.createFor(mouse) | ||
572 | 258 | mousehood = mouseIntelligenceFactory(store=store) | ||
573 | 259 | mouseActor.setEnduringIntelligence(mousehood) | ||
574 | 260 | return mouse | ||
575 | 261 | return createMouse | ||
576 | 262 | |||
577 | 263 | createMouse = createMouseCreator(Mouse) | ||
578 | 264 | createHiraganaMouse = createMouseCreator(HiraganaMouse) | ||
579 | 265 | 0 | ||
580 | === removed file 'Imaginary/ExampleGame/examplegame/quiche.py' | |||
581 | --- Imaginary/ExampleGame/examplegame/quiche.py 2009-06-29 04:03:17 +0000 | |||
582 | +++ Imaginary/ExampleGame/examplegame/quiche.py 1970-01-01 00:00:00 +0000 | |||
583 | @@ -1,116 +0,0 @@ | |||
584 | 1 | # -*- test-case-name: examplegame.test.test_quiche -*- | ||
585 | 2 | |||
586 | 3 | """ | ||
587 | 4 | This module is a mid-level proof of concept of various features in Imaginary. | ||
588 | 5 | |||
589 | 6 | Currently its implementation is a bit messy and it assumes lots of things about | ||
590 | 7 | the reader's knowledge, but we are working on more thoroughly documenting it | ||
591 | 8 | and making it into a good example of how to build functionality that interacts | ||
592 | 9 | with multiple systems (currency, containment, object creation) in Imaginary. | ||
593 | 10 | |||
594 | 11 | """ | ||
595 | 12 | |||
596 | 13 | from zope.interface import implements, Interface | ||
597 | 14 | |||
598 | 15 | from axiom import item, attributes | ||
599 | 16 | |||
600 | 17 | from imaginary import iimaginary, objects, events, language | ||
601 | 18 | from imaginary.enhancement import Enhancement | ||
602 | 19 | |||
603 | 20 | from imaginary.creation import createCreator | ||
604 | 21 | |||
605 | 22 | |||
606 | 23 | class ICoin(Interface): | ||
607 | 24 | """ | ||
608 | 25 | Something small and probably flat and round and which probably serves as | ||
609 | 26 | some form of currency. | ||
610 | 27 | """ | ||
611 | 28 | |||
612 | 29 | |||
613 | 30 | |||
614 | 31 | class Coinage(object): | ||
615 | 32 | implements(ICoin) | ||
616 | 33 | powerupInterfaces = (ICoin,) | ||
617 | 34 | |||
618 | 35 | |||
619 | 36 | |||
620 | 37 | class Quarter(item.Item, Coinage, Enhancement): | ||
621 | 38 | thing = attributes.reference(doc=""" | ||
622 | 39 | The object this coin powers up. | ||
623 | 40 | """) | ||
624 | 41 | |||
625 | 42 | |||
626 | 43 | |||
627 | 44 | class VendingMachine(item.Item, objects.Containment, Enhancement): | ||
628 | 45 | implements(iimaginary.IContainer) | ||
629 | 46 | |||
630 | 47 | capacity = attributes.integer(doc=""" | ||
631 | 48 | Units of weight which can be contained. | ||
632 | 49 | """, allowNone=False, default=1) | ||
633 | 50 | |||
634 | 51 | closed = attributes.boolean(doc=""" | ||
635 | 52 | Indicates whether the container is currently closed or open. | ||
636 | 53 | """, allowNone=False, default=True) | ||
637 | 54 | |||
638 | 55 | thing = attributes.reference(doc=""" | ||
639 | 56 | The object this container powers up. | ||
640 | 57 | """) | ||
641 | 58 | |||
642 | 59 | _currencyCounter = attributes.integer(doc=""" | ||
643 | 60 | The number of coins which have been added to this vending machine since it | ||
644 | 61 | last ejected an item. | ||
645 | 62 | """, allowNone=False, default=0) | ||
646 | 63 | |||
647 | 64 | def coinAdded(self, coin): | ||
648 | 65 | """ | ||
649 | 66 | Called when a coin is added to this thing. | ||
650 | 67 | |||
651 | 68 | @type coin: C{ICoin} provider | ||
652 | 69 | """ | ||
653 | 70 | self._currencyCounter += 1 | ||
654 | 71 | if self._currencyCounter >= 5 and self.getContents(): | ||
655 | 72 | self._currencyCounter = 0 | ||
656 | 73 | try: | ||
657 | 74 | obj = iter(self.getContents()).next() | ||
658 | 75 | except StopIteration: | ||
659 | 76 | evt = events.Success( | ||
660 | 77 | actor=self.thing, | ||
661 | 78 | target=obj, | ||
662 | 79 | otherMessage=language.Sentence([self.thing, " thumps loudly."])) | ||
663 | 80 | else: | ||
664 | 81 | evt = events.Success( | ||
665 | 82 | actor=self.thing, | ||
666 | 83 | target=obj, | ||
667 | 84 | otherMessage=language.Sentence([ | ||
668 | 85 | language.Noun(self.thing).definiteNounPhrase(), | ||
669 | 86 | " thumps loudly and spits out ", obj, | ||
670 | 87 | " onto the ground."])) | ||
671 | 88 | state = self.closed | ||
672 | 89 | self.closed = False | ||
673 | 90 | try: | ||
674 | 91 | obj.moveTo(self.thing.location) | ||
675 | 92 | finally: | ||
676 | 93 | self.closed = state | ||
677 | 94 | evt.broadcast() | ||
678 | 95 | |||
679 | 96 | |||
680 | 97 | def add(self, obj): | ||
681 | 98 | coin = ICoin(obj, None) | ||
682 | 99 | if coin is not None: | ||
683 | 100 | self.coinAdded(coin) | ||
684 | 101 | else: | ||
685 | 102 | return super(VendingMachine, self).add(obj) | ||
686 | 103 | |||
687 | 104 | |||
688 | 105 | |||
689 | 106 | def createVendingMachine(store, name, description=u""): | ||
690 | 107 | o = objects.Thing(store=store, name=name, description=description) | ||
691 | 108 | VendingMachine.createFor(o) | ||
692 | 109 | return o | ||
693 | 110 | |||
694 | 111 | |||
695 | 112 | |||
696 | 113 | createCoin = createCreator((Quarter, {})) | ||
697 | 114 | createVendingMachine = createCreator((VendingMachine, {})) | ||
698 | 115 | createQuiche = createCreator() | ||
699 | 116 | |||
700 | 117 | 0 | ||
701 | === removed file 'Imaginary/ExampleGame/examplegame/squeaky.py' | |||
702 | --- Imaginary/ExampleGame/examplegame/squeaky.py 2009-08-17 02:40:03 +0000 | |||
703 | +++ Imaginary/ExampleGame/examplegame/squeaky.py 1970-01-01 00:00:00 +0000 | |||
704 | @@ -1,42 +0,0 @@ | |||
705 | 1 | # -*- test-case-name: examplegame.test.test_squeaky -*- | ||
706 | 2 | |||
707 | 3 | """ | ||
708 | 4 | This module implements an L{ILinkAnnotator} which causes an object to squeak | ||
709 | 5 | when it is moved. It should serve as a simple example for overriding what | ||
710 | 6 | happens when an action is executed (in this case, 'take' and 'drop'). | ||
711 | 7 | """ | ||
712 | 8 | |||
713 | 9 | from zope.interface import implements | ||
714 | 10 | |||
715 | 11 | from axiom.item import Item | ||
716 | 12 | from axiom.attributes import reference | ||
717 | 13 | |||
718 | 14 | from imaginary.iimaginary import IMovementRestriction, IConcept | ||
719 | 15 | from imaginary.events import Success | ||
720 | 16 | from imaginary.enhancement import Enhancement | ||
721 | 17 | from imaginary.objects import Thing | ||
722 | 18 | |||
723 | 19 | |||
724 | 20 | class Squeaker(Item, Enhancement): | ||
725 | 21 | """ | ||
726 | 22 | This is an L{Enhancement} which, when installed on a L{Thing}, causes that | ||
727 | 23 | L{Thing} to squeak when you pick it up. | ||
728 | 24 | """ | ||
729 | 25 | |||
730 | 26 | implements(IMovementRestriction) | ||
731 | 27 | |||
732 | 28 | powerupInterfaces = [IMovementRestriction] | ||
733 | 29 | |||
734 | 30 | thing = reference(allowNone=False, | ||
735 | 31 | whenDeleted=reference.CASCADE, | ||
736 | 32 | reftype=Thing) | ||
737 | 33 | |||
738 | 34 | |||
739 | 35 | def movementImminent(self, movee, destination): | ||
740 | 36 | """ | ||
741 | 37 | The object enhanced by this L{Squeaker} is about to move - emit a | ||
742 | 38 | L{Success} event which describes its squeak. | ||
743 | 39 | """ | ||
744 | 40 | Success(otherMessage=(IConcept(self.thing).capitalizeConcept(), | ||
745 | 41 | " emits a faint squeak."), | ||
746 | 42 | location=self.thing.location).broadcast() | ||
747 | 43 | 0 | ||
748 | === removed directory 'Imaginary/ExampleGame/examplegame/test' | |||
749 | === removed file 'Imaginary/ExampleGame/examplegame/test/__init__.py' | |||
750 | === removed file 'Imaginary/ExampleGame/examplegame/test/test_furniture.py' | |||
751 | --- Imaginary/ExampleGame/examplegame/test/test_furniture.py 2013-07-03 23:55:37 +0000 | |||
752 | +++ Imaginary/ExampleGame/examplegame/test/test_furniture.py 1970-01-01 00:00:00 +0000 | |||
753 | @@ -1,104 +0,0 @@ | |||
754 | 1 | |||
755 | 2 | """ | ||
756 | 3 | This module contains tests for the examplegame.furniture module. | ||
757 | 4 | """ | ||
758 | 5 | |||
759 | 6 | from twisted.trial.unittest import TestCase | ||
760 | 7 | |||
761 | 8 | from imaginary.test.commandutils import CommandTestCaseMixin, E, createLocation | ||
762 | 9 | |||
763 | 10 | from imaginary.objects import Thing, Exit | ||
764 | 11 | from examplegame.furniture import Chair | ||
765 | 12 | |||
766 | 13 | class SitAndStandTests(CommandTestCaseMixin, TestCase): | ||
767 | 14 | """ | ||
768 | 15 | Tests for the 'sit' and 'stand' actions. | ||
769 | 16 | """ | ||
770 | 17 | |||
771 | 18 | def setUp(self): | ||
772 | 19 | """ | ||
773 | 20 | Create a room, with a dude in it, and a chair he can sit in. | ||
774 | 21 | """ | ||
775 | 22 | CommandTestCaseMixin.setUp(self) | ||
776 | 23 | self.chairThing = Thing(store=self.store, name=u"chair") | ||
777 | 24 | self.chairThing.moveTo(self.location) | ||
778 | 25 | self.chair = Chair.createFor(self.chairThing) | ||
779 | 26 | |||
780 | 27 | |||
781 | 28 | def test_sitDown(self): | ||
782 | 29 | """ | ||
783 | 30 | Sitting in a chair should move your location to that chair. | ||
784 | 31 | """ | ||
785 | 32 | self.assertCommandOutput( | ||
786 | 33 | "sit chair", | ||
787 | 34 | ["You sit in the chair."], | ||
788 | 35 | ["Test Player sits in the chair."]) | ||
789 | 36 | self.assertEquals(self.player.location, self.chair.thing) | ||
790 | 37 | |||
791 | 38 | |||
792 | 39 | def test_standWhenStanding(self): | ||
793 | 40 | """ | ||
794 | 41 | You can't stand up - you're already standing up. | ||
795 | 42 | """ | ||
796 | 43 | self.assertCommandOutput( | ||
797 | 44 | "stand up", | ||
798 | 45 | ["You're already standing."]) | ||
799 | 46 | |||
800 | 47 | |||
801 | 48 | def test_standWhenSitting(self): | ||
802 | 49 | """ | ||
803 | 50 | If a player stands up when sitting in a chair, they should be seen to | ||
804 | 51 | stand up, and they should be placed back into the room where the chair | ||
805 | 52 | is located. | ||
806 | 53 | """ | ||
807 | 54 | self.test_sitDown() | ||
808 | 55 | self.assertCommandOutput( | ||
809 | 56 | "stand up", | ||
810 | 57 | ["You stand up."], | ||
811 | 58 | ["Test Player stands up."]) | ||
812 | 59 | self.assertEquals(self.player.location, self.location) | ||
813 | 60 | |||
814 | 61 | |||
815 | 62 | def test_takeWhenSitting(self): | ||
816 | 63 | """ | ||
817 | 64 | When a player is seated, they should still be able to take objects on | ||
818 | 65 | the floor around them. | ||
819 | 66 | """ | ||
820 | 67 | self.test_sitDown() | ||
821 | 68 | self.ball = Thing(store=self.store, name=u'ball') | ||
822 | 69 | self.ball.moveTo(self.location) | ||
823 | 70 | self.assertCommandOutput( | ||
824 | 71 | "take ball", | ||
825 | 72 | ["You take a ball."], | ||
826 | 73 | ["Test Player takes a ball."]) | ||
827 | 74 | |||
828 | 75 | |||
829 | 76 | def test_moveWhenSitting(self): | ||
830 | 77 | """ | ||
831 | 78 | A player who is sitting shouldn't be able to move without standing up | ||
832 | 79 | first. | ||
833 | 80 | """ | ||
834 | 81 | self.test_sitDown() | ||
835 | 82 | otherRoom = createLocation(self.store, u"elsewhere", None).thing | ||
836 | 83 | Exit.link(self.location, otherRoom, u'north') | ||
837 | 84 | self.assertCommandOutput( | ||
838 | 85 | "go north", | ||
839 | 86 | ["You can't do that while sitting down."]) | ||
840 | 87 | self.assertCommandOutput( | ||
841 | 88 | "go south", | ||
842 | 89 | ["You can't go that way."]) | ||
843 | 90 | |||
844 | 91 | |||
845 | 92 | def test_lookWhenSitting(self): | ||
846 | 93 | """ | ||
847 | 94 | Looking around when sitting should display the description of the room. | ||
848 | 95 | """ | ||
849 | 96 | self.test_sitDown() | ||
850 | 97 | self.assertCommandOutput( | ||
851 | 98 | "look", | ||
852 | 99 | # I'd like to add ', in the chair' to this test, but there's | ||
853 | 100 | # currently no way to modify the name of the object being looked | ||
854 | 101 | # at. | ||
855 | 102 | [E("[ Test Location ]"), | ||
856 | 103 | "Location for testing.", | ||
857 | 104 | "Here, you see Observer Player and a chair."]) | ||
858 | 105 | 0 | ||
859 | === removed file 'Imaginary/ExampleGame/examplegame/test/test_glass.py' | |||
860 | --- Imaginary/ExampleGame/examplegame/test/test_glass.py 2013-07-03 23:55:37 +0000 | |||
861 | +++ Imaginary/ExampleGame/examplegame/test/test_glass.py 1970-01-01 00:00:00 +0000 | |||
862 | @@ -1,95 +0,0 @@ | |||
863 | 1 | |||
864 | 2 | """ | ||
865 | 3 | Tests for L{examplegame.glass} | ||
866 | 4 | """ | ||
867 | 5 | |||
868 | 6 | from twisted.trial.unittest import TestCase | ||
869 | 7 | |||
870 | 8 | from imaginary.test.commandutils import CommandTestCaseMixin, E | ||
871 | 9 | |||
872 | 10 | from imaginary.objects import Thing, Container | ||
873 | 11 | |||
874 | 12 | from examplegame.glass import GlassBox | ||
875 | 13 | |||
876 | 14 | class GlassBoxTests(CommandTestCaseMixin, TestCase): | ||
877 | 15 | """ | ||
878 | 16 | Tests for L{GlassBox} | ||
879 | 17 | """ | ||
880 | 18 | |||
881 | 19 | def setUp(self): | ||
882 | 20 | """ | ||
883 | 21 | Create a room with a L{GlassBox} in it, which itself contains a ball. | ||
884 | 22 | """ | ||
885 | 23 | CommandTestCaseMixin.setUp(self) | ||
886 | 24 | self.box = Thing(store=self.store, name=u'box', | ||
887 | 25 | description=u'The system under test.') | ||
888 | 26 | self.ball = Thing(store=self.store, name=u'ball', | ||
889 | 27 | description=u'an interesting object') | ||
890 | 28 | self.container = Container.createFor(self.box) | ||
891 | 29 | GlassBox.createFor(self.box) | ||
892 | 30 | self.ball.moveTo(self.box) | ||
893 | 31 | self.box.moveTo(self.location) | ||
894 | 32 | self.container.closed = True | ||
895 | 33 | |||
896 | 34 | |||
897 | 35 | def test_lookThrough(self): | ||
898 | 36 | """ | ||
899 | 37 | You can see items within a glass box by looking at them directly. | ||
900 | 38 | """ | ||
901 | 39 | self.assertCommandOutput( | ||
902 | 40 | "look at ball", | ||
903 | 41 | [E("[ ball ]"), | ||
904 | 42 | "an interesting object"]) | ||
905 | 43 | |||
906 | 44 | |||
907 | 45 | def test_lookAt(self): | ||
908 | 46 | """ | ||
909 | 47 | You can see the contents within a glass box by looking at the box. | ||
910 | 48 | """ | ||
911 | 49 | self.assertCommandOutput( | ||
912 | 50 | "look at box", | ||
913 | 51 | [E("[ box ]"), | ||
914 | 52 | "The system under test.", | ||
915 | 53 | "It contains a ball."]) | ||
916 | 54 | |||
917 | 55 | |||
918 | 56 | def test_take(self): | ||
919 | 57 | """ | ||
920 | 58 | You can't take items within a glass box. | ||
921 | 59 | """ | ||
922 | 60 | self.assertCommandOutput( | ||
923 | 61 | "get ball", | ||
924 | 62 | ["You can't reach through the glass box."]) | ||
925 | 63 | |||
926 | 64 | |||
927 | 65 | def test_openTake(self): | ||
928 | 66 | """ | ||
929 | 67 | Taking items from a glass box should work if it's open. | ||
930 | 68 | """ | ||
931 | 69 | self.container.closed = False | ||
932 | 70 | self.assertCommandOutput( | ||
933 | 71 | "get ball", | ||
934 | 72 | ["You take a ball."], | ||
935 | 73 | ["Test Player takes a ball."]) | ||
936 | 74 | |||
937 | 75 | |||
938 | 76 | def test_put(self): | ||
939 | 77 | """ | ||
940 | 78 | You can't put items into a glass box. | ||
941 | 79 | """ | ||
942 | 80 | self.container.closed = False | ||
943 | 81 | self.ball.moveTo(self.location) | ||
944 | 82 | self.container.closed = True | ||
945 | 83 | self.assertCommandOutput( | ||
946 | 84 | "put ball in box", | ||
947 | 85 | ["The box is closed."]) | ||
948 | 86 | |||
949 | 87 | |||
950 | 88 | def test_whyNot(self): | ||
951 | 89 | """ | ||
952 | 90 | A regression test; there was a bug where glass boxes would interfere | ||
953 | 91 | with normal target-acquisition error reporting. | ||
954 | 92 | """ | ||
955 | 93 | self.assertCommandOutput( | ||
956 | 94 | "get foobar", | ||
957 | 95 | ["Nothing like that around here."]) | ||
958 | 96 | 0 | ||
959 | === removed file 'Imaginary/ExampleGame/examplegame/test/test_japanese.py' | |||
960 | --- Imaginary/ExampleGame/examplegame/test/test_japanese.py 2013-07-03 23:55:37 +0000 | |||
961 | +++ Imaginary/ExampleGame/examplegame/test/test_japanese.py 1970-01-01 00:00:00 +0000 | |||
962 | @@ -1,474 +0,0 @@ | |||
963 | 1 | import weakref | ||
964 | 2 | |||
965 | 3 | from twisted.internet import task | ||
966 | 4 | from twisted.trial import unittest | ||
967 | 5 | |||
968 | 6 | from axiom import store | ||
969 | 7 | |||
970 | 8 | from imaginary import iimaginary, objects, events, action | ||
971 | 9 | |||
972 | 10 | from imaginary.test import commandutils | ||
973 | 11 | |||
974 | 12 | from examplegame import mice | ||
975 | 13 | from examplegame import japanese | ||
976 | 14 | |||
977 | 15 | class MouseChallengeMixin(object): | ||
978 | 16 | """ | ||
979 | 17 | A mixin meant to be used in TestCases which want to assert things | ||
980 | 18 | about mouse challenges. | ||
981 | 19 | |||
982 | 20 | The subclass must be sure to provide a C{player} instance | ||
983 | 21 | attribute, which is the L{IThing<iimaginary.IThing>} provider of | ||
984 | 22 | the player which observes the mouse, and a C{mouseName} attribute | ||
985 | 23 | which should be the mouse's name. | ||
986 | 24 | """ | ||
987 | 25 | def assertChallenge(self, concept): | ||
988 | 26 | """ | ||
989 | 27 | Assert that the given concept is a challenge from the mouse | ||
990 | 28 | named self.mouseName, as observed by self.player. | ||
991 | 29 | """ | ||
992 | 30 | said = commandutils.flatten(concept.plaintext(self.player)) | ||
993 | 31 | self.failUnless(said.startswith(u"A %s says, '" % (self.mouseName,)), repr(said)) | ||
994 | 32 | self.failUnlessIn(said[-3], japanese.hiragana) | ||
995 | 33 | self.failUnless(said.endswith("'\n"), repr(said)) | ||
996 | 34 | |||
997 | 35 | |||
998 | 36 | |||
999 | 37 | class HiraganaMouseTestCase(MouseChallengeMixin, unittest.TestCase): | ||
1000 | 38 | """ | ||
1001 | 39 | Test that there is a mouse that says hiragana and stuff | ||
1002 | 40 | """ | ||
1003 | 41 | |||
1004 | 42 | def setUp(self): | ||
1005 | 43 | self.store = store.Store() | ||
1006 | 44 | |||
1007 | 45 | self.clock = objects.Thing(store=self.store, name=u"Clock") | ||
1008 | 46 | self.clockContainer = objects.Container.createFor(self.clock, capacity=10) | ||
1009 | 47 | |||
1010 | 48 | self.mouseName = u"\N{KATAKANA LETTER PI}\N{KATAKANA LETTER SMALL YU}" | ||
1011 | 49 | self.mouse = mice.createHiraganaMouse( | ||
1012 | 50 | store=self.store, | ||
1013 | 51 | name=self.mouseName) | ||
1014 | 52 | self.mouseActor = iimaginary.IActor(self.mouse) | ||
1015 | 53 | self.mousehood = self.mouseActor.getIntelligence() | ||
1016 | 54 | self.mouse.moveTo(self.clock) | ||
1017 | 55 | |||
1018 | 56 | (self.player, | ||
1019 | 57 | self.playerActor, | ||
1020 | 58 | self.playerIntelligence) = commandutils.createPlayer(self.store, | ||
1021 | 59 | u"Mean Old Man") | ||
1022 | 60 | |||
1023 | 61 | |||
1024 | 62 | self.player.moveTo(self.clock) | ||
1025 | 63 | |||
1026 | 64 | self.reactorTime = task.Clock() | ||
1027 | 65 | self.mousehood._callLater = self.reactorTime.callLater | ||
1028 | 66 | |||
1029 | 67 | |||
1030 | 68 | def test_mouseCanSqueak(self): | ||
1031 | 69 | """ | ||
1032 | 70 | When explicitly told to challenge with a given romaji syllable, the | ||
1033 | 71 | mouse should say a hiragana letter. | ||
1034 | 72 | """ | ||
1035 | 73 | events.runEventTransaction( | ||
1036 | 74 | self.store, | ||
1037 | 75 | self.mousehood.challenge, | ||
1038 | 76 | character=u"\N{HIRAGANA LETTER A}") | ||
1039 | 77 | |||
1040 | 78 | self.assertEquals(len(self.playerIntelligence.concepts), 1) | ||
1041 | 79 | event = self.playerIntelligence.concepts[0] | ||
1042 | 80 | self.assertEquals( | ||
1043 | 81 | commandutils.flatten(event.otherMessage.plaintext(self.player)), | ||
1044 | 82 | u"A %s says, '\N{HIRAGANA LETTER A}'" % (self.mouseName,)) | ||
1045 | 83 | |||
1046 | 84 | |||
1047 | 85 | def test_randomHiragana(self): | ||
1048 | 86 | """ | ||
1049 | 87 | When explicitly told to challenge without specifying a syllable, the | ||
1050 | 88 | mouse should say a random one. | ||
1051 | 89 | """ | ||
1052 | 90 | events.runEventTransaction(self.store, self.mousehood.challenge) | ||
1053 | 91 | self.assertEquals(len(self.playerIntelligence.concepts), 1) | ||
1054 | 92 | event = self.playerIntelligence.concepts[0] | ||
1055 | 93 | self.assertChallenge(event) | ||
1056 | 94 | |||
1057 | 95 | |||
1058 | 96 | def test_ji(self): | ||
1059 | 97 | """ | ||
1060 | 98 | Two hiragana characters map to the romaji 'ji'. Test that we do the | ||
1061 | 99 | right thing for them. | ||
1062 | 100 | """ | ||
1063 | 101 | self.mousehood.challenge(character=u"\N{HIRAGANA LETTER DI}") | ||
1064 | 102 | self.failUnless(self.mousehood.vetteChallengeResponse(u"ji")) | ||
1065 | 103 | self.mousehood.challenge(character=u"\N{HIRAGANA LETTER ZI}") | ||
1066 | 104 | self.failUnless(self.mousehood.vetteChallengeResponse(u"ji")) | ||
1067 | 105 | |||
1068 | 106 | |||
1069 | 107 | def test_zu(self): | ||
1070 | 108 | """ | ||
1071 | 109 | Two hiragana characters map to the romaji 'zu'. Test that we do the | ||
1072 | 110 | right thing for them. | ||
1073 | 111 | """ | ||
1074 | 112 | self.mousehood.challenge(character=u"\N{HIRAGANA LETTER DU}") | ||
1075 | 113 | self.failUnless(self.mousehood.vetteChallengeResponse(u"zu")) | ||
1076 | 114 | self.mousehood.challenge(character=u"\N{HIRAGANA LETTER ZU}") | ||
1077 | 115 | self.failUnless(self.mousehood.vetteChallengeResponse(u"zu")) | ||
1078 | 116 | |||
1079 | 117 | |||
1080 | 118 | def test_mouseStartsChallengingWhenPlayersArrive(self): | ||
1081 | 119 | """ | ||
1082 | 120 | When a player arrives, the mouse should go into the 'I am | ||
1083 | 121 | challenging' state. | ||
1084 | 122 | """ | ||
1085 | 123 | # Whitebox | ||
1086 | 124 | self.assertEquals(self.mousehood.challenging, False) | ||
1087 | 125 | |||
1088 | 126 | evt = events.ArrivalEvent(actor=self.player) | ||
1089 | 127 | self.mouseActor.send(evt) | ||
1090 | 128 | |||
1091 | 129 | self.assertEquals(self.mousehood.challenging, True) | ||
1092 | 130 | |||
1093 | 131 | |||
1094 | 132 | def test_mouseSchedulesChallenges(self): | ||
1095 | 133 | """ | ||
1096 | 134 | After telling a mouse to start challenging, it should schedule timed | ||
1097 | 135 | events to say challenges. | ||
1098 | 136 | """ | ||
1099 | 137 | self.mousehood.startChallenging() | ||
1100 | 138 | self.reactorTime.advance(self.mousehood.challengeInterval) | ||
1101 | 139 | concepts = self.playerIntelligence.concepts | ||
1102 | 140 | self.assertEquals(len(concepts), 1) | ||
1103 | 141 | self.assertChallenge(concepts[0]) | ||
1104 | 142 | |||
1105 | 143 | |||
1106 | 144 | def test_mouseStopsChallengingWhenPlayersLeave(self): | ||
1107 | 145 | """ | ||
1108 | 146 | When the 'last' player leaves, the mouse stops challenging. | ||
1109 | 147 | """ | ||
1110 | 148 | # Whitebox | ||
1111 | 149 | self.mousehood.startChallenging() | ||
1112 | 150 | |||
1113 | 151 | evt = events.DepartureEvent(location=self.clock, | ||
1114 | 152 | actor=self.player) | ||
1115 | 153 | self.player.moveTo(None) | ||
1116 | 154 | self.mouseActor.send(evt) | ||
1117 | 155 | |||
1118 | 156 | self.assertEquals(self.mousehood.challenging, False) | ||
1119 | 157 | |||
1120 | 158 | |||
1121 | 159 | def test_mouseStopsSchedulingChallenges(self): | ||
1122 | 160 | """ | ||
1123 | 161 | When a mouse is told to stop challenging, it should cancel any | ||
1124 | 162 | challenges it had scheduled. | ||
1125 | 163 | """ | ||
1126 | 164 | self.mousehood.startChallenging() | ||
1127 | 165 | self.mousehood.stopChallenging() | ||
1128 | 166 | |||
1129 | 167 | self.reactorTime.advance(self.mousehood.challengeInterval) | ||
1130 | 168 | self.assertEquals(self.playerIntelligence.concepts, []) | ||
1131 | 169 | |||
1132 | 170 | |||
1133 | 171 | def test_stopChallengingWhenNotChallengingFails(self): | ||
1134 | 172 | """ | ||
1135 | 173 | Don't stop challenging when you're not challenging. | ||
1136 | 174 | """ | ||
1137 | 175 | self.assertRaises(mice.ChallengeVacuum, self.mousehood.stopChallenging) | ||
1138 | 176 | |||
1139 | 177 | |||
1140 | 178 | def test_startChallengingTwiceFails(self): | ||
1141 | 179 | """ | ||
1142 | 180 | Don't start challenging twice. | ||
1143 | 181 | """ | ||
1144 | 182 | self.mousehood.startChallenging() | ||
1145 | 183 | self.assertRaises(mice.ChallengeCollision, self.mousehood.startChallenging) | ||
1146 | 184 | |||
1147 | 185 | |||
1148 | 186 | def test_challengeRecurrence(self): | ||
1149 | 187 | """ | ||
1150 | 188 | After a challenge is issued another one should be issued later. | ||
1151 | 189 | """ | ||
1152 | 190 | self.mousehood.startChallenging() | ||
1153 | 191 | self.reactorTime.advance(self.mousehood.challengeInterval) | ||
1154 | 192 | |||
1155 | 193 | self.assertIn(self.mousehood.getCurrentChallenge(), japanese.hiragana) | ||
1156 | 194 | |||
1157 | 195 | self.mousehood._currentChallenge = None # Clear his challenge evilly | ||
1158 | 196 | |||
1159 | 197 | self.reactorTime.advance(self.mousehood.challengeInterval) | ||
1160 | 198 | |||
1161 | 199 | self.assertIn(self.mousehood.getCurrentChallenge(), japanese.hiragana) | ||
1162 | 200 | |||
1163 | 201 | |||
1164 | 202 | def test_twoMenEnter(self): | ||
1165 | 203 | """ | ||
1166 | 204 | Test that when *TWO* players join, the mouse doesn't schedule too many | ||
1167 | 205 | challenges. | ||
1168 | 206 | """ | ||
1169 | 207 | otherPlayer = commandutils.createPlayer(self.store, | ||
1170 | 208 | u"Polite Young Man")[0] | ||
1171 | 209 | |||
1172 | 210 | # Send an arrival event because setUp doesn't | ||
1173 | 211 | firstEvent = events.ArrivalEvent(actor=self.player) | ||
1174 | 212 | |||
1175 | 213 | self.mouseActor.send(firstEvent) | ||
1176 | 214 | otherPlayer.moveTo(self.clock, arrivalEventFactory=events.MovementArrivalEvent) | ||
1177 | 215 | |||
1178 | 216 | self.playerIntelligence.concepts = [] | ||
1179 | 217 | self.reactorTime.advance(self.mousehood.challengeInterval) | ||
1180 | 218 | |||
1181 | 219 | self.assertEquals(len(self.playerIntelligence.concepts), 1) | ||
1182 | 220 | self.assertChallenge(self.playerIntelligence.concepts[0]) | ||
1183 | 221 | |||
1184 | 222 | |||
1185 | 223 | def test_twoMenLeave(self): | ||
1186 | 224 | """ | ||
1187 | 225 | Test that when two players are near the mouse, the mouse doesn't | ||
1188 | 226 | unschedule its challenge until they both leave. | ||
1189 | 227 | """ | ||
1190 | 228 | otherPlayer = commandutils.createPlayer(self.store, | ||
1191 | 229 | u"Polite Young Man")[0] | ||
1192 | 230 | otherPlayer.moveTo(self.clock) | ||
1193 | 231 | |||
1194 | 232 | self.mousehood.startChallenging() | ||
1195 | 233 | |||
1196 | 234 | firstEvent = events.DepartureEvent(location=self.clock, | ||
1197 | 235 | actor=self.player) | ||
1198 | 236 | secondEvent = events.DepartureEvent(location=self.clock, | ||
1199 | 237 | actor=otherPlayer) | ||
1200 | 238 | |||
1201 | 239 | otherPlayer.moveTo(None) | ||
1202 | 240 | self.mouseActor.send(secondEvent) | ||
1203 | 241 | |||
1204 | 242 | self.playerIntelligence.concepts = [] | ||
1205 | 243 | |||
1206 | 244 | self.reactorTime.advance(self.mousehood.challengeInterval) | ||
1207 | 245 | |||
1208 | 246 | self.assertEquals(len(self.playerIntelligence.concepts), 1) | ||
1209 | 247 | self.assertChallenge(self.playerIntelligence.concepts[0]) | ||
1210 | 248 | |||
1211 | 249 | self.player.moveTo(None) | ||
1212 | 250 | self.mouseActor.send(firstEvent) | ||
1213 | 251 | |||
1214 | 252 | self.failIf(self.mousehood.challenging) | ||
1215 | 253 | |||
1216 | 254 | |||
1217 | 255 | def test_getCurrentChallenge(self): | ||
1218 | 256 | """ | ||
1219 | 257 | Test that we can introspect the current challenge of a mouse. | ||
1220 | 258 | """ | ||
1221 | 259 | self.mousehood.startChallenging() | ||
1222 | 260 | self.reactorTime.advance(self.mousehood.challengeInterval) | ||
1223 | 261 | self.failUnlessIn(self.mousehood.getCurrentChallenge(), japanese.hiragana) | ||
1224 | 262 | |||
1225 | 263 | self.mousehood.stopChallenging() | ||
1226 | 264 | self.assertIdentical(self.mousehood.getCurrentChallenge(), None) | ||
1227 | 265 | |||
1228 | 266 | |||
1229 | 267 | def test_vetteChallengeResponse(self): | ||
1230 | 268 | """ | ||
1231 | 269 | Test that the correct response to the current challenge is accepted by | ||
1232 | 270 | the mouse. | ||
1233 | 271 | """ | ||
1234 | 272 | self.mousehood.startChallenging() | ||
1235 | 273 | self.reactorTime.advance(self.mousehood.challengeInterval) | ||
1236 | 274 | |||
1237 | 275 | romaji = japanese.hiragana[self.mousehood.getCurrentChallenge()] | ||
1238 | 276 | self.failUnless(self.mousehood.vetteChallengeResponse(romaji)) | ||
1239 | 277 | |||
1240 | 278 | for romaji in japanese.hiragana.values(): | ||
1241 | 279 | if romaji != japanese.hiragana[self.mousehood.getCurrentChallenge()]: | ||
1242 | 280 | self.failIf(self.mousehood.vetteChallengeResponse(romaji)) | ||
1243 | 281 | |||
1244 | 282 | |||
1245 | 283 | def test_respondToChallengeCorrectly(self): | ||
1246 | 284 | """ | ||
1247 | 285 | Test that when a correct response is received, the current challenge is | ||
1248 | 286 | expired and the mouse salutes you. | ||
1249 | 287 | """ | ||
1250 | 288 | self.mousehood.startChallenging() | ||
1251 | 289 | self.reactorTime.advance(self.mousehood.challengeInterval) | ||
1252 | 290 | |||
1253 | 291 | correctResponse = japanese.hiragana[ | ||
1254 | 292 | self.mousehood.getCurrentChallenge()] | ||
1255 | 293 | |||
1256 | 294 | self.mousehood.responseReceived(self.player, correctResponse) | ||
1257 | 295 | self.reactorTime.advance(0) | ||
1258 | 296 | |||
1259 | 297 | self.assertIdentical(self.mousehood.getCurrentChallenge(), None) | ||
1260 | 298 | |||
1261 | 299 | self.assertEquals(len(self.playerIntelligence.concepts), 2) | ||
1262 | 300 | c = self.playerIntelligence.concepts[1] | ||
1263 | 301 | self.assertEquals( | ||
1264 | 302 | commandutils.flatten(c.plaintext(self.player)), | ||
1265 | 303 | u"%s salutes you!\n" % (self.mouseName,)) | ||
1266 | 304 | |||
1267 | 305 | |||
1268 | 306 | def test_respondToChallengeInorrectly(self): | ||
1269 | 307 | """ | ||
1270 | 308 | Test that when an incorrect response is received, the current challenge | ||
1271 | 309 | is not expired and the mouse bites you. | ||
1272 | 310 | """ | ||
1273 | 311 | self.mousehood.startChallenging() | ||
1274 | 312 | self.reactorTime.advance(self.mousehood.challengeInterval) | ||
1275 | 313 | |||
1276 | 314 | correctResponse = japanese.hiragana[ | ||
1277 | 315 | self.mousehood.getCurrentChallenge()] | ||
1278 | 316 | |||
1279 | 317 | for ch in japanese.hiragana.values(): | ||
1280 | 318 | if ch != correctResponse: | ||
1281 | 319 | self.mousehood.responseReceived(self.player, ch) | ||
1282 | 320 | break | ||
1283 | 321 | else: | ||
1284 | 322 | self.fail("Buggy test") | ||
1285 | 323 | |||
1286 | 324 | self.reactorTime.advance(0) | ||
1287 | 325 | |||
1288 | 326 | self.assertIn(self.mousehood.getCurrentChallenge(), | ||
1289 | 327 | japanese.romajiToHiragana[correctResponse]) | ||
1290 | 328 | |||
1291 | 329 | self.assertEquals(len(self.playerIntelligence.concepts), 2) | ||
1292 | 330 | c = self.playerIntelligence.concepts[1] | ||
1293 | 331 | self.assertEquals( | ||
1294 | 332 | commandutils.flatten(c.plaintext(self.player)), | ||
1295 | 333 | u"%s bites you!\n" % (self.mouseName,)) | ||
1296 | 334 | |||
1297 | 335 | |||
1298 | 336 | def test_playerSaysCorrectThing(self): | ||
1299 | 337 | """ | ||
1300 | 338 | Test that when someone gives voice to the correct response to a mouse's | ||
1301 | 339 | current challenge, the mouse acknowledges this with a salute. | ||
1302 | 340 | """ | ||
1303 | 341 | self.mousehood.startChallenging() | ||
1304 | 342 | self.reactorTime.advance(self.mousehood.challengeInterval) | ||
1305 | 343 | action.Say().do( | ||
1306 | 344 | # http://divmod.org/trac/ticket/2917 | ||
1307 | 345 | iimaginary.IActor(self.player), | ||
1308 | 346 | None, | ||
1309 | 347 | japanese.hiragana[self.mousehood.getCurrentChallenge()]) | ||
1310 | 348 | |||
1311 | 349 | self.assertIdentical(self.mousehood.getCurrentChallenge(), None) | ||
1312 | 350 | self.reactorTime.advance(0) | ||
1313 | 351 | |||
1314 | 352 | self.assertEquals(len(self.playerIntelligence.concepts), 3) | ||
1315 | 353 | c = self.playerIntelligence.concepts[2] | ||
1316 | 354 | self.assertEquals( | ||
1317 | 355 | commandutils.flatten(c.plaintext(self.player)), | ||
1318 | 356 | u"%s salutes you!\n" % (self.mouseName,)) | ||
1319 | 357 | |||
1320 | 358 | |||
1321 | 359 | def test_playerSaysIncorrectThing(self): | ||
1322 | 360 | """ | ||
1323 | 361 | Test that when someone gives voice to the correct response to a mouse's | ||
1324 | 362 | current challenge, the mouse acknowledges this with a salute. | ||
1325 | 363 | """ | ||
1326 | 364 | self.mousehood.startChallenging() | ||
1327 | 365 | self.reactorTime.advance(self.mousehood.challengeInterval) | ||
1328 | 366 | |||
1329 | 367 | action.Say().do( | ||
1330 | 368 | # http://divmod.org/trac/ticket/2917 | ||
1331 | 369 | iimaginary.IActor(self.player), None, u"lolololo pew") | ||
1332 | 370 | |||
1333 | 371 | self.failIfIdentical(self.mousehood.getCurrentChallenge(), None) | ||
1334 | 372 | self.reactorTime.advance(0) | ||
1335 | 373 | |||
1336 | 374 | self.assertEquals(len(self.playerIntelligence.concepts), 3) | ||
1337 | 375 | c = self.playerIntelligence.concepts[2] | ||
1338 | 376 | self.assertEquals( | ||
1339 | 377 | commandutils.flatten(c.plaintext(self.player)), | ||
1340 | 378 | u"%s bites you!\n" % (self.mouseName,)) | ||
1341 | 379 | |||
1342 | 380 | |||
1343 | 381 | def test_activationUsesReactorScheduling(self): | ||
1344 | 382 | """ | ||
1345 | 383 | Test that the default scheduler of the mouse is the Twisted | ||
1346 | 384 | reactor, since that is the scheduler that needs to be used | ||
1347 | 385 | with the actual Imaginary server. | ||
1348 | 386 | """ | ||
1349 | 387 | deletions = [] | ||
1350 | 388 | ref = weakref.ref(self.mousehood, deletions.append) | ||
1351 | 389 | # This is a hack to reload the mouse since it gets its | ||
1352 | 390 | # _callLater set in setUp. | ||
1353 | 391 | del self.mouse | ||
1354 | 392 | del self.mouseActor | ||
1355 | 393 | del self.mousehood | ||
1356 | 394 | self.assertEquals(deletions, [ref]) | ||
1357 | 395 | mousehood = self.store.findUnique(mice.HiraganaMouse) | ||
1358 | 396 | from twisted.internet import reactor | ||
1359 | 397 | self.assertEquals(mousehood._callLater, reactor.callLater) | ||
1360 | 398 | |||
1361 | 399 | |||
1362 | 400 | |||
1363 | 401 | class HiraganaMouseCommandTestCase(commandutils.CommandTestCaseMixin, unittest.TestCase): | ||
1364 | 402 | """ | ||
1365 | 403 | H-mouse tests which use the command system. | ||
1366 | 404 | """ | ||
1367 | 405 | |||
1368 | 406 | mouseName = u"\N{KATAKANA LETTER PI}\N{KATAKANA LETTER SMALL YU}" | ||
1369 | 407 | hiraganaCharacterPattern = u"'[" + u''.join(japanese.hiragana.keys()) + u"]'" | ||
1370 | 408 | speechPattern = mouseName + u" says, " + hiraganaCharacterPattern | ||
1371 | 409 | |||
1372 | 410 | def test_oneManEnters(self): | ||
1373 | 411 | """ | ||
1374 | 412 | Test that when a fellow jaunts into a venue inhabited by a mouse of the | ||
1375 | 413 | Nipponese persuasion, a hiragana allocution follows. | ||
1376 | 414 | """ | ||
1377 | 415 | clock = task.Clock() | ||
1378 | 416 | |||
1379 | 417 | closetContainer = commandutils.createLocation( | ||
1380 | 418 | self.store, u"Closet", None) | ||
1381 | 419 | closet = closetContainer.thing | ||
1382 | 420 | |||
1383 | 421 | mouse = mice.createHiraganaMouse( | ||
1384 | 422 | store=self.store, | ||
1385 | 423 | name=self.mouseName, | ||
1386 | 424 | proper=True) | ||
1387 | 425 | mouseActor = iimaginary.IActor(mouse) | ||
1388 | 426 | mousehood = mouseActor.getIntelligence() | ||
1389 | 427 | mousehood._callLater = clock.callLater | ||
1390 | 428 | mouse.moveTo(closet) | ||
1391 | 429 | |||
1392 | 430 | objects.Exit.link(self.location, closet, u"north") | ||
1393 | 431 | |||
1394 | 432 | self._test( | ||
1395 | 433 | "north", | ||
1396 | 434 | [commandutils.E("[ Closet ]"), | ||
1397 | 435 | commandutils.E("( south )"), | ||
1398 | 436 | commandutils.E(u"Here, you see " + self.mouseName + u".")], | ||
1399 | 437 | ["Test Player leaves north."]) | ||
1400 | 438 | |||
1401 | 439 | clock.advance(mousehood.challengeInterval) | ||
1402 | 440 | |||
1403 | 441 | self._test(None, [self.speechPattern]) | ||
1404 | 442 | |||
1405 | 443 | |||
1406 | 444 | def test_creation(self): | ||
1407 | 445 | """ | ||
1408 | 446 | Test the creation of a hiragana-speaking mouse using the thing creation | ||
1409 | 447 | plugin system. | ||
1410 | 448 | """ | ||
1411 | 449 | self._test( | ||
1412 | 450 | u"create the 'hiragana mouse' named " + self.mouseName, | ||
1413 | 451 | [commandutils.E(u"You create " + self.mouseName + u".")], | ||
1414 | 452 | [commandutils.E(u"Test Player creates %s." % (self.mouseName,))]) | ||
1415 | 453 | |||
1416 | 454 | for thing in self.location.findProviders(iimaginary.IThing, 0): | ||
1417 | 455 | if thing.name == self.mouseName: | ||
1418 | 456 | break | ||
1419 | 457 | else: | ||
1420 | 458 | self.fail("Could not find the mouse! Test bug.") | ||
1421 | 459 | |||
1422 | 460 | clock = task.Clock() | ||
1423 | 461 | jimhood = iimaginary.IActor(thing).getIntelligence() | ||
1424 | 462 | jimhood._callLater = clock.callLater | ||
1425 | 463 | |||
1426 | 464 | self._test( | ||
1427 | 465 | u"drop " + self.mouseName, | ||
1428 | 466 | [commandutils.E(u"You drop %s." % (self.mouseName,))], | ||
1429 | 467 | [commandutils.E(u"Test Player drops %s." % (self.mouseName,))]) | ||
1430 | 468 | |||
1431 | 469 | clock.advance(jimhood.challengeInterval) | ||
1432 | 470 | |||
1433 | 471 | self._test( | ||
1434 | 472 | None, | ||
1435 | 473 | [self.speechPattern], | ||
1436 | 474 | [self.speechPattern]) | ||
1437 | 475 | 0 | ||
1438 | === removed file 'Imaginary/ExampleGame/examplegame/test/test_mice.py' | |||
1439 | --- Imaginary/ExampleGame/examplegame/test/test_mice.py 2013-07-03 23:50:20 +0000 | |||
1440 | +++ Imaginary/ExampleGame/examplegame/test/test_mice.py 1970-01-01 00:00:00 +0000 | |||
1441 | @@ -1,155 +0,0 @@ | |||
1442 | 1 | |||
1443 | 2 | from twisted.trial import unittest | ||
1444 | 3 | from twisted.internet import task | ||
1445 | 4 | |||
1446 | 5 | from axiom import store | ||
1447 | 6 | |||
1448 | 7 | from imaginary import iimaginary, events, objects | ||
1449 | 8 | from imaginary.test import commandutils | ||
1450 | 9 | |||
1451 | 10 | from examplegame import mice | ||
1452 | 11 | |||
1453 | 12 | |||
1454 | 13 | class IntelligenceTestCase(unittest.TestCase): | ||
1455 | 14 | def setUp(self): | ||
1456 | 15 | self.store = store.Store() | ||
1457 | 16 | |||
1458 | 17 | self.locationContainer = commandutils.createLocation( | ||
1459 | 18 | self.store, u"Place", None) | ||
1460 | 19 | self.location = self.locationContainer.thing | ||
1461 | 20 | |||
1462 | 21 | self.alice = objects.Thing(store=self.store, name=u"Alice") | ||
1463 | 22 | self.actor = objects.Actor.createFor(self.alice) | ||
1464 | 23 | |||
1465 | 24 | self.alice.moveTo(self.location) | ||
1466 | 25 | |||
1467 | 26 | self.intelligence = commandutils.MockIntelligence(store=self.store) | ||
1468 | 27 | self.actor.setEnduringIntelligence(self.intelligence) | ||
1469 | 28 | |||
1470 | 29 | |||
1471 | 30 | def test_intelligenceReceivesEvent(self): | ||
1472 | 31 | """ | ||
1473 | 32 | Enduring intelligences should receive events. | ||
1474 | 33 | """ | ||
1475 | 34 | evt = events.Success( | ||
1476 | 35 | location=self.location, | ||
1477 | 36 | otherMessage=u"Hello, how are you?") | ||
1478 | 37 | |||
1479 | 38 | self.actor.send(evt) | ||
1480 | 39 | self.assertEquals(self.intelligence.concepts, [evt]) | ||
1481 | 40 | |||
1482 | 41 | |||
1483 | 42 | def test_persistentIntelligence(self): | ||
1484 | 43 | """ | ||
1485 | 44 | Whitebox test that enduring intelligencii are actually persistent. | ||
1486 | 45 | """ | ||
1487 | 46 | # TB <---- THAT MEANS IT'S TRANSLUCENT | ||
1488 | 47 | self.assertIdentical( | ||
1489 | 48 | self.store.findUnique( | ||
1490 | 49 | objects.Actor, | ||
1491 | 50 | objects.Actor._enduringIntelligence == self.intelligence), | ||
1492 | 51 | self.actor) | ||
1493 | 52 | |||
1494 | 53 | |||
1495 | 54 | |||
1496 | 55 | class MouseTestCase(unittest.TestCase): | ||
1497 | 56 | def setUp(self): | ||
1498 | 57 | self.store = store.Store() | ||
1499 | 58 | |||
1500 | 59 | self.clock = objects.Thing(store=self.store, name=u"Clock") | ||
1501 | 60 | self.clockContainer = objects.Container.createFor(self.clock, capacity=10) | ||
1502 | 61 | |||
1503 | 62 | self.mouse = mice.createMouse(store=self.store, name=u"Squeaker McSqueakenson") | ||
1504 | 63 | self.mouseActor = iimaginary.IActor(self.mouse) | ||
1505 | 64 | self.mousehood = self.mouseActor.getIntelligence() | ||
1506 | 65 | self.mouse.moveTo(self.clock) | ||
1507 | 66 | |||
1508 | 67 | self.player = objects.Thing(store=self.store, name=u"Mean Old Man") | ||
1509 | 68 | self.playerActor = objects.Actor.createFor(self.player) | ||
1510 | 69 | self.playerIntelligence = commandutils.MockIntelligence( | ||
1511 | 70 | store=self.store) | ||
1512 | 71 | self.playerActor.setEnduringIntelligence(self.playerIntelligence) | ||
1513 | 72 | |||
1514 | 73 | self.player.moveTo(self.clock) | ||
1515 | 74 | |||
1516 | 75 | |||
1517 | 76 | def test_mouseSqueaksAtIntruders(self): | ||
1518 | 77 | """ | ||
1519 | 78 | When a mean old man walks into the mouse's clock, the mouse will squeak | ||
1520 | 79 | ruthlessly. | ||
1521 | 80 | """ | ||
1522 | 81 | clock = task.Clock() | ||
1523 | 82 | self.mousehood._callLater = clock.callLater | ||
1524 | 83 | evt = events.ArrivalEvent(actor=self.player) | ||
1525 | 84 | self.mouseActor.send(evt) | ||
1526 | 85 | |||
1527 | 86 | self.assertEquals(len(self.playerIntelligence.concepts), 0) | ||
1528 | 87 | clock.advance(0) | ||
1529 | 88 | |||
1530 | 89 | self.assertEquals(len(self.playerIntelligence.concepts), 1) | ||
1531 | 90 | event = self.playerIntelligence.concepts[0] | ||
1532 | 91 | self.assertEquals( | ||
1533 | 92 | commandutils.flatten(event.otherMessage.plaintext(self.player)), | ||
1534 | 93 | u"SQUEAK!") | ||
1535 | 94 | |||
1536 | 95 | |||
1537 | 96 | def test_mouseCanSqueak(self): | ||
1538 | 97 | events.runEventTransaction(self.store, self.mousehood.squeak) | ||
1539 | 98 | self.assertEquals(len(self.playerIntelligence.concepts), 1) | ||
1540 | 99 | event = self.playerIntelligence.concepts[0] | ||
1541 | 100 | self.assertEquals( | ||
1542 | 101 | commandutils.flatten(event.otherMessage.plaintext(self.player)), | ||
1543 | 102 | u"SQUEAK!") | ||
1544 | 103 | |||
1545 | 104 | |||
1546 | 105 | def test_mouseActivation(self): | ||
1547 | 106 | """ | ||
1548 | 107 | Activating a mouse should set the scheduling mechanism to the | ||
1549 | 108 | reactor's. | ||
1550 | 109 | """ | ||
1551 | 110 | from twisted.internet import reactor | ||
1552 | 111 | self.assertEquals(self.mousehood._callLater, reactor.callLater) | ||
1553 | 112 | |||
1554 | 113 | |||
1555 | 114 | |||
1556 | 115 | class MouseReactionTestCase(commandutils.CommandTestCaseMixin, | ||
1557 | 116 | unittest.TestCase): | ||
1558 | 117 | def testCreation(self): | ||
1559 | 118 | """ | ||
1560 | 119 | Test that a mouse can be created with the create command. | ||
1561 | 120 | """ | ||
1562 | 121 | self._test( | ||
1563 | 122 | "create the mouse named squeaker", | ||
1564 | 123 | ['You create squeaker.'], | ||
1565 | 124 | ['Test Player creates squeaker.']) | ||
1566 | 125 | |||
1567 | 126 | [mouse] = list(self.playerContainer.getContents()) | ||
1568 | 127 | self.failUnless(isinstance(iimaginary.IActor(mouse).getIntelligence(), mice.Mouse)) | ||
1569 | 128 | |||
1570 | 129 | |||
1571 | 130 | def testSqueak(self): | ||
1572 | 131 | """ | ||
1573 | 132 | Test that when someone walks into a room with a mouse, the mouse | ||
1574 | 133 | squeaks and the person who walked in hears it. | ||
1575 | 134 | """ | ||
1576 | 135 | mouse = mice.createMouse(store=self.store, name=u"squeaker") | ||
1577 | 136 | clock = task.Clock() | ||
1578 | 137 | intelligence = iimaginary.IActor(mouse).getIntelligence() | ||
1579 | 138 | intelligence._callLater = clock.callLater | ||
1580 | 139 | |||
1581 | 140 | elsewhere = commandutils.createLocation( | ||
1582 | 141 | self.store, u"Mouse Hole", None).thing | ||
1583 | 142 | |||
1584 | 143 | objects.Exit.link(self.location, elsewhere, u"south") | ||
1585 | 144 | |||
1586 | 145 | mouse.moveTo(elsewhere) | ||
1587 | 146 | |||
1588 | 147 | self._test( | ||
1589 | 148 | "south", | ||
1590 | 149 | [commandutils.E("[ Mouse Hole ]"), | ||
1591 | 150 | commandutils.E("( north )"), | ||
1592 | 151 | commandutils.E("Here, you see a squeaker.")], | ||
1593 | 152 | ['Test Player leaves south.']) | ||
1594 | 153 | |||
1595 | 154 | clock.advance(0) | ||
1596 | 155 | self._test(None, ["SQUEAK!"]) | ||
1597 | 156 | 0 | ||
1598 | === removed file 'Imaginary/ExampleGame/examplegame/test/test_quiche.py' | |||
1599 | --- Imaginary/ExampleGame/examplegame/test/test_quiche.py 2009-06-29 04:03:17 +0000 | |||
1600 | +++ Imaginary/ExampleGame/examplegame/test/test_quiche.py 1970-01-01 00:00:00 +0000 | |||
1601 | @@ -1,93 +0,0 @@ | |||
1602 | 1 | from twisted.trial import unittest | ||
1603 | 2 | |||
1604 | 3 | from imaginary import objects, iimaginary | ||
1605 | 4 | from imaginary.test import commandutils | ||
1606 | 5 | |||
1607 | 6 | from examplegame import quiche | ||
1608 | 7 | |||
1609 | 8 | |||
1610 | 9 | class VendingTest(commandutils.CommandTestCaseMixin, unittest.TestCase): | ||
1611 | 10 | def testTheyExist(self): | ||
1612 | 11 | self._test("create the 'vending machine' named vendy", | ||
1613 | 12 | ["You create vendy."], | ||
1614 | 13 | ["Test Player creates vendy."]) | ||
1615 | 14 | |||
1616 | 15 | |||
1617 | 16 | def testPopulateVendingMachine(self): | ||
1618 | 17 | self._test("create the 'vending machine' named vendy", | ||
1619 | 18 | ["You create vendy."], | ||
1620 | 19 | ["Test Player creates vendy."]) | ||
1621 | 20 | |||
1622 | 21 | self._test("create a quiche named quiche", | ||
1623 | 22 | ["You create a quiche."], | ||
1624 | 23 | ["Test Player creates a quiche."]) | ||
1625 | 24 | |||
1626 | 25 | self._test("open vendy", | ||
1627 | 26 | ["You open vendy."], | ||
1628 | 27 | ["Test Player opens vendy."]) | ||
1629 | 28 | |||
1630 | 29 | self._test("put quiche in vendy", | ||
1631 | 30 | ["You put the quiche in vendy."], | ||
1632 | 31 | ["Test Player puts a quiche in vendy."]) | ||
1633 | 32 | |||
1634 | 33 | |||
1635 | 34 | def testBuyingQuiche(self): | ||
1636 | 35 | self._test("create the 'vending machine' named vendy", | ||
1637 | 36 | ["You create vendy."], | ||
1638 | 37 | ["Test Player creates vendy."]) | ||
1639 | 38 | |||
1640 | 39 | self._test("drop vendy", | ||
1641 | 40 | ["You drop vendy."], | ||
1642 | 41 | ["Test Player drops vendy."]) | ||
1643 | 42 | |||
1644 | 43 | self._test("create a quiche named quiche", | ||
1645 | 44 | ["You create a quiche."], | ||
1646 | 45 | ["Test Player creates a quiche."]) | ||
1647 | 46 | |||
1648 | 47 | self._test("open vendy", | ||
1649 | 48 | ["You open vendy."], | ||
1650 | 49 | ["Test Player opens vendy."]) | ||
1651 | 50 | |||
1652 | 51 | self._test("put quiche in vendy", | ||
1653 | 52 | ["You put the quiche in vendy."], | ||
1654 | 53 | ["Test Player puts a quiche in vendy."]) | ||
1655 | 54 | |||
1656 | 55 | for i in range(5): | ||
1657 | 56 | self._test("create the quarter named quarter%s " % i, | ||
1658 | 57 | ["You create quarter%s." % i], | ||
1659 | 58 | ["Test Player creates quarter%s." % i]) | ||
1660 | 59 | |||
1661 | 60 | for i in range(4): | ||
1662 | 61 | self._test("put quarter%i in vendy" % i, | ||
1663 | 62 | ["You put quarter%s in vendy." % i], | ||
1664 | 63 | ["Test Player puts quarter%s in vendy." % i]) | ||
1665 | 64 | |||
1666 | 65 | self._test("put quarter4 in vendy", | ||
1667 | 66 | ["You put quarter4 in vendy.", | ||
1668 | 67 | "Vendy thumps loudly and spits out a quiche onto the ground."], | ||
1669 | 68 | ["Test Player puts quarter4 in vendy.", | ||
1670 | 69 | "Vendy thumps loudly and spits out a quiche onto the ground."]) | ||
1671 | 70 | |||
1672 | 71 | |||
1673 | 72 | def testProgrammaticQuichePurchase(self): | ||
1674 | 73 | location = objects.Thing(store=self.store, name=u"room") | ||
1675 | 74 | icloc = objects.Container.createFor(location, capacity=500) | ||
1676 | 75 | |||
1677 | 76 | vm = quiche.createVendingMachine(store=self.store, name=u"Vendy", description=u"VEEEENDYYYYY") | ||
1678 | 77 | vm.moveTo(location) | ||
1679 | 78 | |||
1680 | 79 | icvm = iimaginary.IContainer(vm) | ||
1681 | 80 | icvm.closed = False | ||
1682 | 81 | theQuiche = quiche.createQuiche(store=self.store, name=u"quiche") | ||
1683 | 82 | icvm.add(theQuiche) | ||
1684 | 83 | icvm.closed = True | ||
1685 | 84 | |||
1686 | 85 | for i in range(4): | ||
1687 | 86 | quarter = quiche.createCoin(store=self.store, name=u"quarter%s" % (i,)) | ||
1688 | 87 | icvm.add(quarter) | ||
1689 | 88 | |||
1690 | 89 | quarter = quiche.createCoin(store=self.store, name=u"quarter4") | ||
1691 | 90 | icvm.add(quarter) | ||
1692 | 91 | |||
1693 | 92 | self.failUnless(icloc.contains(theQuiche)) | ||
1694 | 93 | |||
1695 | 94 | 0 | ||
1696 | === removed file 'Imaginary/ExampleGame/examplegame/test/test_squeaky.py' | |||
1697 | --- Imaginary/ExampleGame/examplegame/test/test_squeaky.py 2009-08-17 02:40:03 +0000 | |||
1698 | +++ Imaginary/ExampleGame/examplegame/test/test_squeaky.py 1970-01-01 00:00:00 +0000 | |||
1699 | @@ -1,51 +0,0 @@ | |||
1700 | 1 | |||
1701 | 2 | from twisted.trial.unittest import TestCase | ||
1702 | 3 | |||
1703 | 4 | from imaginary.test.commandutils import CommandTestCaseMixin | ||
1704 | 5 | |||
1705 | 6 | from imaginary.objects import Thing, Container | ||
1706 | 7 | |||
1707 | 8 | from examplegame.squeaky import Squeaker | ||
1708 | 9 | |||
1709 | 10 | class SqueakTest(CommandTestCaseMixin, TestCase): | ||
1710 | 11 | """ | ||
1711 | 12 | Squeak Test. | ||
1712 | 13 | """ | ||
1713 | 14 | |||
1714 | 15 | def setUp(self): | ||
1715 | 16 | """ | ||
1716 | 17 | Set Up. | ||
1717 | 18 | """ | ||
1718 | 19 | CommandTestCaseMixin.setUp(self) | ||
1719 | 20 | self.squeaker = Thing(store=self.store, name=u"squeaker") | ||
1720 | 21 | self.squeaker.moveTo(self.location) | ||
1721 | 22 | self.squeakification = Squeaker.createFor(self.squeaker) | ||
1722 | 23 | |||
1723 | 24 | |||
1724 | 25 | def test_itSqueaks(self): | ||
1725 | 26 | """ | ||
1726 | 27 | Picking up a squeaky thing makes it emit a squeak. | ||
1727 | 28 | """ | ||
1728 | 29 | self.assertCommandOutput( | ||
1729 | 30 | "take squeaker", | ||
1730 | 31 | ["You take a squeaker.", | ||
1731 | 32 | "A squeaker emits a faint squeak."], | ||
1732 | 33 | ["Test Player takes a squeaker.", | ||
1733 | 34 | "A squeaker emits a faint squeak."]) | ||
1734 | 35 | |||
1735 | 36 | |||
1736 | 37 | def test_squeakyContainer(self): | ||
1737 | 38 | """ | ||
1738 | 39 | If a container is squeaky, that shouldn't interfere with its function | ||
1739 | 40 | as a container. (i.e. let's make sure that links keep working even | ||
1740 | 41 | though we're using an annotator here.) | ||
1741 | 42 | """ | ||
1742 | 43 | cont = Container.createFor(self.squeaker) | ||
1743 | 44 | |||
1744 | 45 | mcguffin = Thing(store=self.store, name=u"mcguffin") | ||
1745 | 46 | mcguffin.moveTo(cont) | ||
1746 | 47 | |||
1747 | 48 | self.assertCommandOutput( | ||
1748 | 49 | "take mcguffin from squeaker", | ||
1749 | 50 | ["You take a mcguffin from the squeaker."], | ||
1750 | 51 | ["Test Player takes a mcguffin from the squeaker."]) | ||
1751 | 52 | 0 | ||
1752 | === removed file 'Imaginary/ExampleGame/examplegame/test/test_tether.py' | |||
1753 | --- Imaginary/ExampleGame/examplegame/test/test_tether.py 2009-08-17 02:40:03 +0000 | |||
1754 | +++ Imaginary/ExampleGame/examplegame/test/test_tether.py 1970-01-01 00:00:00 +0000 | |||
1755 | @@ -1,96 +0,0 @@ | |||
1756 | 1 | |||
1757 | 2 | from twisted.trial.unittest import TestCase | ||
1758 | 3 | |||
1759 | 4 | from imaginary.test.commandutils import CommandTestCaseMixin, E | ||
1760 | 5 | |||
1761 | 6 | from imaginary.objects import Thing, Container, Exit | ||
1762 | 7 | from imaginary.garments import Garment | ||
1763 | 8 | |||
1764 | 9 | from examplegame.furniture import Chair | ||
1765 | 10 | from examplegame.tether import Tether | ||
1766 | 11 | |||
1767 | 12 | class TetherTest(CommandTestCaseMixin, TestCase): | ||
1768 | 13 | """ | ||
1769 | 14 | A test for tethering an item to its location, such that a player who picks | ||
1770 | 15 | it up can't leave until they drop it. | ||
1771 | 16 | """ | ||
1772 | 17 | |||
1773 | 18 | def setUp(self): | ||
1774 | 19 | """ | ||
1775 | 20 | Tether a ball to the room. | ||
1776 | 21 | """ | ||
1777 | 22 | CommandTestCaseMixin.setUp(self) | ||
1778 | 23 | self.ball = Thing(store=self.store, name=u'ball') | ||
1779 | 24 | self.ball.moveTo(self.location) | ||
1780 | 25 | self.tether = Tether.createFor(self.ball, to=self.location) | ||
1781 | 26 | self.otherPlace = Thing(store=self.store, name=u'elsewhere') | ||
1782 | 27 | Container.createFor(self.otherPlace, capacity=1000) | ||
1783 | 28 | Exit.link(self.location, self.otherPlace, u'north') | ||
1784 | 29 | |||
1785 | 30 | |||
1786 | 31 | def test_takeAndLeave(self): | ||
1787 | 32 | """ | ||
1788 | 33 | You can't leave the room if you're holding the ball that's tied to it. | ||
1789 | 34 | """ | ||
1790 | 35 | self.assertCommandOutput( | ||
1791 | 36 | "take ball", | ||
1792 | 37 | ["You take a ball."], | ||
1793 | 38 | ["Test Player takes a ball."]) | ||
1794 | 39 | self.assertCommandOutput( | ||
1795 | 40 | "go north", | ||
1796 | 41 | ["You can't move, you're still holding a ball."], | ||
1797 | 42 | ["Test Player struggles with a ball."]) | ||
1798 | 43 | self.assertCommandOutput( | ||
1799 | 44 | "drop ball", | ||
1800 | 45 | ["You drop the ball."], | ||
1801 | 46 | ["Test Player drops a ball."]) | ||
1802 | 47 | self.assertCommandOutput( | ||
1803 | 48 | "go north", | ||
1804 | 49 | [E("[ elsewhere ]"), | ||
1805 | 50 | E("( south )"), | ||
1806 | 51 | ""], | ||
1807 | 52 | ["Test Player leaves north."]) | ||
1808 | 53 | |||
1809 | 54 | |||
1810 | 55 | def test_allTiedUp(self): | ||
1811 | 56 | """ | ||
1812 | 57 | If you're tied to a chair, you can't leave. | ||
1813 | 58 | """ | ||
1814 | 59 | chairThing = Thing(store=self.store, name=u'chair') | ||
1815 | 60 | chairThing.moveTo(self.location) | ||
1816 | 61 | chair = Chair.createFor(chairThing) | ||
1817 | 62 | self.assertCommandOutput("sit chair", | ||
1818 | 63 | ["You sit in the chair."], | ||
1819 | 64 | ["Test Player sits in the chair."]) | ||
1820 | 65 | Tether.createFor(self.player, to=chairThing) | ||
1821 | 66 | self.assertCommandOutput( | ||
1822 | 67 | "stand up", | ||
1823 | 68 | ["You can't move, you're tied to a chair."], | ||
1824 | 69 | ["Test Player struggles."]) | ||
1825 | 70 | |||
1826 | 71 | |||
1827 | 72 | def test_tetheredClothing(self): | ||
1828 | 73 | """ | ||
1829 | 74 | Clothing that is tethered will also prevent movement if you wear it. | ||
1830 | 75 | |||
1831 | 76 | This isn't just simply a test for clothing; it's an example of | ||
1832 | 77 | integrating with a foreign system which doesn't know about tethering, | ||
1833 | 78 | but can move objects itself. | ||
1834 | 79 | |||
1835 | 80 | Tethering should I{not} have any custom logic related to clothing to | ||
1836 | 81 | make this test pass; if it does get custom clothing code for some | ||
1837 | 82 | reason, more tests should be added to deal with other systems that do | ||
1838 | 83 | not take tethering into account (and vice versa). | ||
1839 | 84 | """ | ||
1840 | 85 | Garment.createFor(self.ball, garmentDescription=u"A lovely ball.", | ||
1841 | 86 | garmentSlots=[u"head"]) | ||
1842 | 87 | self.assertCommandOutput( | ||
1843 | 88 | "wear ball", | ||
1844 | 89 | ["You put on the ball."], | ||
1845 | 90 | ["Test Player puts on a ball."]) | ||
1846 | 91 | self.assertCommandOutput( | ||
1847 | 92 | "go north", | ||
1848 | 93 | ["You can't move, you're still holding a ball."], | ||
1849 | 94 | ["Test Player struggles with a ball."]) | ||
1850 | 95 | |||
1851 | 96 | |||
1852 | 97 | 0 | ||
1853 | === removed file 'Imaginary/ExampleGame/examplegame/tether.py' | |||
1854 | --- Imaginary/ExampleGame/examplegame/tether.py 2009-08-17 02:40:03 +0000 | |||
1855 | +++ Imaginary/ExampleGame/examplegame/tether.py 1970-01-01 00:00:00 +0000 | |||
1856 | @@ -1,121 +0,0 @@ | |||
1857 | 1 | # -*- test-case-name: examplegame.test.test_tether -*- | ||
1858 | 2 | |||
1859 | 3 | """ | ||
1860 | 4 | A simplistic implementation of tethering, which demonstrates how to prevent | ||
1861 | 5 | someone from moving around. | ||
1862 | 6 | |||
1863 | 7 | This implementation is somewhat limited, as it assumes that tethered objects | ||
1864 | 8 | can only be located in players' inventories and on the ground. It also makes | ||
1865 | 9 | several assumptions about who is actually doing the moving in moveTo; in order | ||
1866 | 10 | to be really correct, the implementation of movement needs to relay more | ||
1867 | 11 | information about what is moving and how. | ||
1868 | 12 | """ | ||
1869 | 13 | |||
1870 | 14 | from zope.interface import implements | ||
1871 | 15 | |||
1872 | 16 | from axiom.item import Item | ||
1873 | 17 | from axiom.attributes import reference | ||
1874 | 18 | |||
1875 | 19 | from imaginary.iimaginary import IMovementRestriction, IActor | ||
1876 | 20 | from imaginary.eimaginary import ActionFailure | ||
1877 | 21 | from imaginary.events import ThatDoesntWork | ||
1878 | 22 | from imaginary.enhancement import Enhancement | ||
1879 | 23 | from imaginary.objects import Thing | ||
1880 | 24 | |||
1881 | 25 | |||
1882 | 26 | class Tether(Item, Enhancement): | ||
1883 | 27 | """ | ||
1884 | 28 | I am a force that binds two objects together. | ||
1885 | 29 | |||
1886 | 30 | Right now this force isn't symmetric; the idea is that the thing that we | ||
1887 | 31 | are tethered 'to' is immovable for some other reason. This is why we're in | ||
1888 | 32 | the example rather than a real robust piece of game-library functionality | ||
1889 | 33 | in imaginary proper. | ||
1890 | 34 | |||
1891 | 35 | The C{thing} that we are installed on is prevented from moving more than a | ||
1892 | 36 | certain distance away from the thing it is tethered C{to}. | ||
1893 | 37 | |||
1894 | 38 | This is accomplished by preventing movement of the object's container; | ||
1895 | 39 | i.e. if you pick up a ball that is tied to the ground, you can't move until | ||
1896 | 40 | you drop it. | ||
1897 | 41 | """ | ||
1898 | 42 | |||
1899 | 43 | thing = reference(reftype=Thing, | ||
1900 | 44 | whenDeleted=reference.CASCADE, | ||
1901 | 45 | allowNone=False) | ||
1902 | 46 | |||
1903 | 47 | # XXX 'thing' and 'to' should be treated more consistently, or at least the | ||
1904 | 48 | # differences between them explained officially. | ||
1905 | 49 | to = reference(reftype=Thing, | ||
1906 | 50 | whenDeleted=reference.CASCADE, | ||
1907 | 51 | allowNone=False) | ||
1908 | 52 | |||
1909 | 53 | implements(IMovementRestriction) | ||
1910 | 54 | |||
1911 | 55 | powerupInterfaces = [IMovementRestriction] | ||
1912 | 56 | |||
1913 | 57 | def movementImminent(self, movee, destination): | ||
1914 | 58 | """ | ||
1915 | 59 | The object which is tethered is trying to move somewhere. If it has an | ||
1916 | 60 | IActor, assume that it's a player trying to move on its own, and emit | ||
1917 | 61 | an appropriate message. | ||
1918 | 62 | |||
1919 | 63 | Otherwise, assume that it is moving *to* an actor, and install a | ||
1920 | 64 | L{MovementBlocker} on that actor. | ||
1921 | 65 | """ | ||
1922 | 66 | # There isn't enough information provided to moveTo just yet; we need | ||
1923 | 67 | # to know who is doing the moving. In the meanwhile, if you have an | ||
1924 | 68 | # actor, we'll assume you're a player. | ||
1925 | 69 | if IActor(movee, None) is not None: | ||
1926 | 70 | raise ActionFailure( | ||
1927 | 71 | ThatDoesntWork( | ||
1928 | 72 | actor=self.thing, | ||
1929 | 73 | actorMessage=[u"You can't move, you're tied to ", | ||
1930 | 74 | self.to, | ||
1931 | 75 | "."], | ||
1932 | 76 | otherMessage=[self.thing, u' struggles.'])) | ||
1933 | 77 | MovementBlocker.destroyFor(self.thing.location) | ||
1934 | 78 | if self.to != destination: | ||
1935 | 79 | MovementBlocker.createFor(destination, tether=self) | ||
1936 | 80 | |||
1937 | 81 | return False | ||
1938 | 82 | |||
1939 | 83 | |||
1940 | 84 | class MovementBlocker(Item, Enhancement): | ||
1941 | 85 | """ | ||
1942 | 86 | A L{MovementBlocker} is an L{Enhancement} which prevents the movement of a | ||
1943 | 87 | player holding a tethered object. | ||
1944 | 88 | """ | ||
1945 | 89 | implements(IMovementRestriction) | ||
1946 | 90 | |||
1947 | 91 | powerupInterfaces = [IMovementRestriction] | ||
1948 | 92 | |||
1949 | 93 | thing = reference( | ||
1950 | 94 | doc=""" | ||
1951 | 95 | The L{Thing} whose movement is blocked. | ||
1952 | 96 | """, reftype=Thing, allowNone=False, | ||
1953 | 97 | whenDeleted=reference.CASCADE) | ||
1954 | 98 | |||
1955 | 99 | tether = reference( | ||
1956 | 100 | doc=""" | ||
1957 | 101 | The L{Tether} ultimely responsible for blocking movement. | ||
1958 | 102 | """, | ||
1959 | 103 | reftype=Tether, allowNone=False, | ||
1960 | 104 | whenDeleted=reference.CASCADE) | ||
1961 | 105 | |||
1962 | 106 | |||
1963 | 107 | def movementImminent(self, movee, destination): | ||
1964 | 108 | """ | ||
1965 | 109 | The player this blocker is installed on is trying to move. Assume that | ||
1966 | 110 | they are trying to move themselves (via a 'go' action) and prevent it | ||
1967 | 111 | by raising an L{ActionFailure} with an appropriate error message for | ||
1968 | 112 | the player. | ||
1969 | 113 | """ | ||
1970 | 114 | raise ActionFailure( | ||
1971 | 115 | ThatDoesntWork( | ||
1972 | 116 | actor=self.thing, | ||
1973 | 117 | actorMessage= | ||
1974 | 118 | [u"You can't move, you're still holding ", | ||
1975 | 119 | self.tether.thing,u'.'], | ||
1976 | 120 | otherMessage= | ||
1977 | 121 | [self.thing, u' struggles with ', self.tether.thing,u'.'])) | ||
1978 | 122 | 0 | ||
1979 | === removed directory 'Imaginary/ExampleGame/imaginary' | |||
1980 | === removed directory 'Imaginary/ExampleGame/imaginary/plugins' | |||
1981 | === removed file 'Imaginary/ExampleGame/imaginary/plugins/monsters.py' | |||
1982 | --- Imaginary/ExampleGame/imaginary/plugins/monsters.py 2007-08-17 04:46:38 +0000 | |||
1983 | +++ Imaginary/ExampleGame/imaginary/plugins/monsters.py 1970-01-01 00:00:00 +0000 | |||
1984 | @@ -1,6 +0,0 @@ | |||
1985 | 1 | |||
1986 | 2 | from imaginary.creation import CreationPluginHelper | ||
1987 | 3 | from examplegame.mice import createMouse, createHiraganaMouse | ||
1988 | 4 | |||
1989 | 5 | mouse = CreationPluginHelper(u'mouse', createMouse) | ||
1990 | 6 | hiraganaMouse = CreationPluginHelper(u'hiragana mouse', createHiraganaMouse) | ||
1991 | 7 | 0 | ||
1992 | === removed file 'Imaginary/ExampleGame/imaginary/plugins/quiche.py' | |||
1993 | --- Imaginary/ExampleGame/imaginary/plugins/quiche.py 2007-08-17 04:46:38 +0000 | |||
1994 | +++ Imaginary/ExampleGame/imaginary/plugins/quiche.py 1970-01-01 00:00:00 +0000 | |||
1995 | @@ -1,9 +0,0 @@ | |||
1996 | 1 | # -*- test-case-name: examplegame.test.test_vending -*- | ||
1997 | 2 | |||
1998 | 3 | from imaginary.creation import CreationPluginHelper | ||
1999 | 4 | from examplegame.quiche import createQuiche, createCoin, createVendingMachine | ||
2000 | 5 | |||
2001 | 6 | quichePlugin = CreationPluginHelper('quiche', createQuiche) | ||
2002 | 7 | vendingPlugin = CreationPluginHelper('vending machine', createVendingMachine) | ||
2003 | 8 | quarterPlugin = CreationPluginHelper('quarter', createCoin) | ||
2004 | 9 | |||
2005 | 10 | 0 | ||
2006 | === removed file 'Imaginary/LICENSE' | |||
2007 | --- Imaginary/LICENSE 2006-02-26 02:37:39 +0000 | |||
2008 | +++ Imaginary/LICENSE 1970-01-01 00:00:00 +0000 | |||
2009 | @@ -1,20 +0,0 @@ | |||
2010 | 1 | Copyright (c) 2005 Divmod Inc. | ||
2011 | 2 | |||
2012 | 3 | Permission is hereby granted, free of charge, to any person obtaining | ||
2013 | 4 | a copy of this software and associated documentation files (the | ||
2014 | 5 | "Software"), to deal in the Software without restriction, including | ||
2015 | 6 | without limitation the rights to use, copy, modify, merge, publish, | ||
2016 | 7 | distribute, sublicense, and/or sell copies of the Software, and to | ||
2017 | 8 | permit persons to whom the Software is furnished to do so, subject to | ||
2018 | 9 | the following conditions: | ||
2019 | 10 | |||
2020 | 11 | The above copyright notice and this permission notice shall be | ||
2021 | 12 | included in all copies or substantial portions of the Software. | ||
2022 | 13 | |||
2023 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
2024 | 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
2025 | 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
2026 | 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
2027 | 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
2028 | 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
2029 | 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
2030 | 21 | 0 | ||
2031 | === removed file 'Imaginary/MANIFEST.in' | |||
2032 | --- Imaginary/MANIFEST.in 2008-07-16 19:12:33 +0000 | |||
2033 | +++ Imaginary/MANIFEST.in 1970-01-01 00:00:00 +0000 | |||
2034 | @@ -1,6 +0,0 @@ | |||
2035 | 1 | include NAME.txt | ||
2036 | 2 | include DEPS.txt | ||
2037 | 3 | include NEWS.txt | ||
2038 | 4 | include LICENSE | ||
2039 | 5 | graft imaginary | ||
2040 | 6 | graft axiom | ||
2041 | 7 | 0 | ||
2042 | === removed file 'Imaginary/NAME.txt' | |||
2043 | --- Imaginary/NAME.txt 2006-02-26 02:37:39 +0000 | |||
2044 | +++ Imaginary/NAME.txt 1970-01-01 00:00:00 +0000 | |||
2045 | @@ -1,10 +0,0 @@ | |||
2046 | 1 | |||
2047 | 2 | See: http://achewood.com/index.php?date=04272005 | ||
2048 | 3 | |||
2049 | 4 | Imaginary numbers are so named not because they are fictitious, but because | ||
2050 | 5 | they are along a different axis from real numbers. | ||
2051 | 6 | |||
2052 | 7 | Divmod Imaginary is a simulationists take on the realm of role playing, | ||
2053 | 8 | interactive fiction, and multiplayer dungeons. It incorporates gameplay | ||
2054 | 9 | features from each area while attempting to provide a richer environment | ||
2055 | 10 | than is generally available from existing systems. | ||
2056 | 11 | 0 | ||
2057 | === removed file 'Imaginary/NEWS.txt' | |||
2058 | --- Imaginary/NEWS.txt 2009-11-30 01:08:55 +0000 | |||
2059 | +++ Imaginary/NEWS.txt 1970-01-01 00:00:00 +0000 | |||
2060 | @@ -1,36 +0,0 @@ | |||
2061 | 1 | 0.0.5 (2009-11-25): | ||
2062 | 2 | - Remove the dead imaginary.objects.Thing.locate method | ||
2063 | 3 | - Introduce an interface intended to be provided by administrative actors | ||
2064 | 4 | and require it for the "illuminate" command. | ||
2065 | 5 | - Remove the custom SSH server and instead plug in to the Mantissa SSH | ||
2066 | 6 | application server. | ||
2067 | 7 | - Remove all uses of "axiom.dependency.installOn". | ||
2068 | 8 | - Fix a bug which caused newly created avatars to not be announced when | ||
2069 | 9 | they arrived at the starting location. | ||
2070 | 10 | - When presenting an ambiguity error to an actor, enumerate the possible | ||
2071 | 11 | resolutions. | ||
2072 | 12 | - Fix certain terminal handling issues with east asian characters. | ||
2073 | 13 | |||
2074 | 14 | 0.0.4 (2008-08-12): | ||
2075 | 15 | |||
2076 | 16 | - A "plain" Thing type has now been added and is available to the | ||
2077 | 17 | "create" command. | ||
2078 | 18 | - "scrutinize" now works when the target is not a container. | ||
2079 | 19 | - a "list thing types" command has been added, to allow viewing | ||
2080 | 20 | types of things that can be created. | ||
2081 | 21 | - "create" now allows users to specify if a thing is considered a | ||
2082 | 22 | common or proper noun. | ||
2083 | 23 | - Improved grammar in action text for various verbs. | ||
2084 | 24 | - Added a "set" action for changing thing attributes. | ||
2085 | 25 | - Changed noun resolution to match substrings and to match | ||
2086 | 26 | case-insensitively. | ||
2087 | 27 | |||
2088 | 28 | 0.0.3 (2007-01-23): | ||
2089 | 29 | - Work with Axiom dependency api | ||
2090 | 30 | |||
2091 | 31 | 0.0.2 (2006-09-20): | ||
2092 | 32 | - Some changes | ||
2093 | 33 | |||
2094 | 34 | 0.0.1 (2006-06-16): | ||
2095 | 35 | - Imported from Pottery repository | ||
2096 | 36 | - Imported from Imagination repository | ||
2097 | 37 | 0 | ||
2098 | === removed file 'Imaginary/README.txt' | |||
2099 | --- Imaginary/README.txt 2009-06-26 20:38:23 +0000 | |||
2100 | +++ Imaginary/README.txt 1970-01-01 00:00:00 +0000 | |||
2101 | @@ -1,98 +0,0 @@ | |||
2102 | 1 | |||
2103 | 2 | Imaginary is an experimental simulation-construction toolkit. | ||
2104 | 3 | |||
2105 | 4 | Be warned! We aren't kidding when we say "experimental". Many features are | ||
2106 | 5 | not implemented yet and documentation is incomplete. We think there are some | ||
2107 | 6 | pretty cool ideas here, but if you are intending to use this system, be | ||
2108 | 7 | prepared to participate heavily in its development. | ||
2109 | 8 | |||
2110 | 9 | This document is mainly concerned with getting an Imaginary server up and | ||
2111 | 10 | running, to the point where you can edit the code and see things change. Some | ||
2112 | 11 | familiarity with Python, Twisted, Nevow, Axiom, and Mantissa are all helpful, | ||
2113 | 12 | but we will try to make sure they aren't really required just to get started. | ||
2114 | 13 | |||
2115 | 14 | While we have tried to make it possible to get a taste of what is possible | ||
2116 | 15 | here, if you want to make any serious progress, you will want to join the IRC | ||
2117 | 16 | channel "#imagination" on chat.freenode.net and start asking questions. If you | ||
2118 | 17 | are curious about what needs to be done, have a look here: | ||
2119 | 18 | |||
2120 | 19 | http://tinyurl.com/2tuo9o | ||
2121 | 20 | |||
2122 | 21 | The first step in configuring a new installation of Imaginary will normally be | ||
2123 | 22 | creating a new Mantissa database. | ||
2124 | 23 | |||
2125 | 24 | (This is assuming you have already set up Combinator, installed Twisted, and | ||
2126 | 25 | all necessary Divmod dependencies. If not, see | ||
2127 | 26 | http://divmod.org/trac/wiki/CombinatorTutorial for more information.) | ||
2128 | 27 | |||
2129 | 28 | First run the following command: | ||
2130 | 29 | |||
2131 | 30 | axiomatic mantissa | ||
2132 | 31 | |||
2133 | 32 | And answer the prompts appropriately. Please take note of the new password you | ||
2134 | 33 | enter for the "admin" user, as you will need it in a few steps. | ||
2135 | 34 | |||
2136 | 35 | This should create a directory called "mantissa.axiom", containing the server | ||
2137 | 36 | for a web interface that will allow you to install "offerings" (plugins for | ||
2138 | 37 | Mantissa, in this case, Imaginary), create users, and grant them | ||
2139 | 38 | privileges. You can start this webserver with the following command: (the -n | ||
2140 | 39 | option will run it in the foreground on the current terminal) | ||
2141 | 40 | |||
2142 | 41 | axiomatic start -n | ||
2143 | 42 | |||
2144 | 43 | You should now be able to access this server at http://localhost:8080 in your | ||
2145 | 44 | web browser. | ||
2146 | 45 | |||
2147 | 46 | Click the "Sign In" link in the upper right hand corner, and log in as "admin" | ||
2148 | 47 | with the password you chose previously while configuring Mantissa. | ||
2149 | 48 | |||
2150 | 49 | If you logged in successfully, you should now be presented with a list of | ||
2151 | 50 | "offerings" that can be installed. Click on "Imaginary" to install it. | ||
2152 | 51 | |||
2153 | 52 | Next, mouse over the "Admin" menu at the upper left of the screen and then | ||
2154 | 53 | click on the "Products" sub-menu. | ||
2155 | 54 | |||
2156 | 55 | In the "Installable Powerups" section, check the box corresponding to | ||
2157 | 56 | "Imaginary Game". Then click the "Installable Powerups" button below. You | ||
2158 | 57 | will hopefully be presented with a green confirmation dialog to confirm your | ||
2159 | 58 | success. This creates a "product" which can be given to users. | ||
2160 | 59 | |||
2161 | 60 | Mouse over the "Admin" menu again and click "Local Users". You should be | ||
2162 | 61 | presented with a page including a table of users - probably with only the | ||
2163 | 62 | "admin" user in it. Click the word "Endow" in the "Actions" column of that | ||
2164 | 63 | table, then select the product with "ImaginaryApp" in it (again, this is | ||
2165 | 64 | probably the only one presented) and click the "Installproducton | ||
2166 | 65 | admin@localhost" button. You should again be presented with a green | ||
2167 | 66 | confirmation dialog. Hooray! | ||
2168 | 67 | |||
2169 | 68 | Ideally, you will now be able to ssh into your Imaginary server. In a new | ||
2170 | 69 | terminal, ssh to localhost on the Mantissa server's SSH port (by default, this | ||
2171 | 70 | is 8022). For example: | ||
2172 | 71 | |||
2173 | 72 | ssh -p 8022 admin@localhost@localhost | ||
2174 | 73 | |||
2175 | 74 | Note the odd username/host specifier - "admin@localhost" is the user, and the | ||
2176 | 75 | last "localhost" specifies the host to connect to. | ||
2177 | 76 | |||
2178 | 77 | Log in with the same password you used to log in to the web interface. You | ||
2179 | 78 | should be presented with a screen including several options, one of which is | ||
2180 | 79 | "imaginary". Use tab to highlight that option (the highlighted option should | ||
2181 | 80 | appear red), then hit enter to select it. | ||
2182 | 81 | |||
2183 | 82 | At the Imaginary character prompt, choose to create a new character; enter a | ||
2184 | 83 | new username (e.g. not "admin"). You will then join the game as that new | ||
2185 | 84 | character. | ||
2186 | 85 | |||
2187 | 86 | Once in the game, you should see a row of dashes along the bottom of the | ||
2188 | 87 | display. To confirm your new MUDness, try typing "look" and hit enter; You | ||
2189 | 88 | should see some indication of the generic place that you are in, the available | ||
2190 | 89 | exits, and other players in the area, though initially you won't see much more | ||
2191 | 90 | than "[The Place]". | ||
2192 | 91 | |||
2193 | 92 | You can enter "actions" for a list of actions, and use "help" along with one of | ||
2194 | 93 | them ("help dig") for specific information on how to use them. You can even log | ||
2195 | 94 | in via other telnet windows, create additional accounts, and interact with your | ||
2196 | 95 | initial user (for example, beating them to death with "hit"). | ||
2197 | 96 | |||
2198 | 97 | When you've tired of self-abuse, you can stop your Imaginary server by hitting | ||
2199 | 98 | control-c in the terminal where you ran "axiomatic start -n". | ||
2200 | 99 | 0 | ||
2201 | === removed directory 'Imaginary/axiom' | |||
2202 | === removed directory 'Imaginary/axiom/plugins' | |||
2203 | === removed file 'Imaginary/axiom/plugins/imaginaryversion.py' | |||
2204 | --- Imaginary/axiom/plugins/imaginaryversion.py 2008-07-16 19:12:33 +0000 | |||
2205 | +++ Imaginary/axiom/plugins/imaginaryversion.py 1970-01-01 00:00:00 +0000 | |||
2206 | @@ -1,12 +0,0 @@ | |||
2207 | 1 | # Copyright 2008 Divmod, Inc. | ||
2208 | 2 | # See LICENSE file for details | ||
2209 | 3 | |||
2210 | 4 | """ | ||
2211 | 5 | Register an Axiom version plugin for Imaginary. | ||
2212 | 6 | """ | ||
2213 | 7 | |||
2214 | 8 | from zope.interface import directlyProvides | ||
2215 | 9 | from twisted.plugin import IPlugin | ||
2216 | 10 | from axiom.iaxiom import IVersion | ||
2217 | 11 | from imaginary import version | ||
2218 | 12 | directlyProvides(version, IPlugin, IVersion) | ||
2219 | 13 | 0 | ||
2220 | === removed directory 'Imaginary/imaginary' | |||
2221 | === removed file 'Imaginary/imaginary/__init__.py' | |||
2222 | --- Imaginary/imaginary/__init__.py 2009-06-29 04:03:17 +0000 | |||
2223 | +++ Imaginary/imaginary/__init__.py 1970-01-01 00:00:00 +0000 | |||
2224 | @@ -1,19 +0,0 @@ | |||
2225 | 1 | # -*- test-case-name: imaginary,examplegame -*- | ||
2226 | 2 | |||
2227 | 3 | """ | ||
2228 | 4 | Virtual simulation framework. | ||
2229 | 5 | """ | ||
2230 | 6 | |||
2231 | 7 | from imaginary._version import version | ||
2232 | 8 | version # exported | ||
2233 | 9 | |||
2234 | 10 | # Verbs are only registered when they are imported, and important verbs are | ||
2235 | 11 | # found in the following modules: | ||
2236 | 12 | from imaginary import action, creation | ||
2237 | 13 | action # exported | ||
2238 | 14 | creation # exported | ||
2239 | 15 | |||
2240 | 16 | |||
2241 | 17 | # Ideally there would be a nice, passive way to register verbs which would only | ||
2242 | 18 | # load them as necessary rather than forcing the entire package to get | ||
2243 | 19 | # imported, but this will work okay for now. | ||
2244 | 20 | 0 | ||
2245 | === removed file 'Imaginary/imaginary/_version.py' | |||
2246 | --- Imaginary/imaginary/_version.py 2009-11-30 01:08:55 +0000 | |||
2247 | +++ Imaginary/imaginary/_version.py 1970-01-01 00:00:00 +0000 | |||
2248 | @@ -1,3 +0,0 @@ | |||
2249 | 1 | # This is an auto-generated file. Use Epsilon/bin/release-divmod to update. | ||
2250 | 2 | from twisted.python import versions | ||
2251 | 3 | version = versions.Version(__name__[:__name__.rfind('.')], 0, 0, 5) | ||
2252 | 4 | 0 | ||
2253 | === removed file 'Imaginary/imaginary/action.py' | |||
2254 | --- Imaginary/imaginary/action.py 2013-09-22 10:01:56 +0000 | |||
2255 | +++ Imaginary/imaginary/action.py 1970-01-01 00:00:00 +0000 | |||
2256 | @@ -1,1299 +0,0 @@ | |||
2257 | 1 | # -*- test-case-name: imaginary.test.test_actions -*- | ||
2258 | 2 | |||
2259 | 3 | import time, random, operator | ||
2260 | 4 | import pprint | ||
2261 | 5 | |||
2262 | 6 | from zope.interface import implements | ||
2263 | 7 | |||
2264 | 8 | from twisted.python import log, filepath | ||
2265 | 9 | from twisted.internet import defer | ||
2266 | 10 | |||
2267 | 11 | from axiom import iaxiom | ||
2268 | 12 | from axiom.attributes import AND | ||
2269 | 13 | |||
2270 | 14 | import imaginary.plugins | ||
2271 | 15 | from imaginary import (iimaginary, eimaginary, iterutils, events, | ||
2272 | 16 | objects, text as T, language, pyparsing) | ||
2273 | 17 | from imaginary.world import ImaginaryWorld | ||
2274 | 18 | from imaginary.idea import ( | ||
2275 | 19 | CanSee, Proximity, ProviderOf, Named, Traversability) | ||
2276 | 20 | |||
2277 | 21 | ## Hacks because pyparsing doesn't have fantastic unicode support | ||
2278 | 22 | _quoteRemovingQuotedString = pyparsing.quotedString.copy() | ||
2279 | 23 | _quoteRemovingQuotedString.setParseAction(pyparsing.removeQuotes) | ||
2280 | 24 | |||
2281 | 25 | class UnicodeWord(pyparsing.Token): | ||
2282 | 26 | def parseImpl(self, instring, loc, doActions=True): | ||
2283 | 27 | maxLoc = len(instring) | ||
2284 | 28 | while loc < maxLoc and instring[loc].isspace(): | ||
2285 | 29 | loc += 1 | ||
2286 | 30 | start = loc | ||
2287 | 31 | while loc < maxLoc and not instring[loc].isspace(): | ||
2288 | 32 | loc += 1 | ||
2289 | 33 | end = loc | ||
2290 | 34 | return end, instring[start:end] | ||
2291 | 35 | |||
2292 | 36 | |||
2293 | 37 | |||
2294 | 38 | class _ActionType(type): | ||
2295 | 39 | actions = [] | ||
2296 | 40 | def __new__(cls, name, bases, attrs): | ||
2297 | 41 | infrastructure = attrs.pop('infrastructure', False) | ||
2298 | 42 | t = super(_ActionType, cls).__new__(cls, name, bases, attrs) | ||
2299 | 43 | if not infrastructure: | ||
2300 | 44 | cls.actions.append(t) | ||
2301 | 45 | return t | ||
2302 | 46 | |||
2303 | 47 | |||
2304 | 48 | def parse(self, player, line): | ||
2305 | 49 | """ | ||
2306 | 50 | Parse an action. | ||
2307 | 51 | """ | ||
2308 | 52 | for eachActionType in self.actions: | ||
2309 | 53 | try: | ||
2310 | 54 | match = eachActionType.match(player, line) | ||
2311 | 55 | except pyparsing.ParseException: | ||
2312 | 56 | pass | ||
2313 | 57 | else: | ||
2314 | 58 | if match is not None: | ||
2315 | 59 | match = dict(match) | ||
2316 | 60 | for k,v in match.items(): | ||
2317 | 61 | if isinstance(v, pyparsing.ParseResults): | ||
2318 | 62 | match[k] = v[0] | ||
2319 | 63 | |||
2320 | 64 | return eachActionType().runEventTransaction(player, line, match) | ||
2321 | 65 | return defer.fail(eimaginary.NoSuchCommand(line)) | ||
2322 | 66 | |||
2323 | 67 | |||
2324 | 68 | |||
2325 | 69 | class Action(object): | ||
2326 | 70 | """ | ||
2327 | 71 | An L{Action} represents an intention of a player to do something. | ||
2328 | 72 | """ | ||
2329 | 73 | __metaclass__ = _ActionType | ||
2330 | 74 | infrastructure = True | ||
2331 | 75 | |||
2332 | 76 | actorInterface = iimaginary.IActor | ||
2333 | 77 | |||
2334 | 78 | def runEventTransaction(self, player, line, match): | ||
2335 | 79 | """ | ||
2336 | 80 | Take a player, input, and dictionary of parse results, resolve those | ||
2337 | 81 | parse results into implementations of appropriate interfaces in the | ||
2338 | 82 | game world, and execute the actual Action implementation (contained in | ||
2339 | 83 | the 'do' method) in an event transaction. | ||
2340 | 84 | |||
2341 | 85 | This is the top level of action invocation. | ||
2342 | 86 | |||
2343 | 87 | @param player: A L{Thing} representing the actor's body. | ||
2344 | 88 | |||
2345 | 89 | @param line: A unicode string containing the original input | ||
2346 | 90 | |||
2347 | 91 | @param match: A dictionary containing some parse results to pass | ||
2348 | 92 | through to this L{Action}'s C{do} method as keyword arguments. | ||
2349 | 93 | |||
2350 | 94 | @raise eimaginary.AmbiguousArgument: if multiple valid targets are | ||
2351 | 95 | found for an argument. | ||
2352 | 96 | """ | ||
2353 | 97 | def thunk(): | ||
2354 | 98 | begin = time.time() | ||
2355 | 99 | try: | ||
2356 | 100 | actor = self.actorInterface(player) | ||
2357 | 101 | for (k, v) in match.items(): | ||
2358 | 102 | try: | ||
2359 | 103 | objs = self.resolve(player, k, v) | ||
2360 | 104 | except NotImplementedError: | ||
2361 | 105 | pass | ||
2362 | 106 | else: | ||
2363 | 107 | if len(objs) == 1: | ||
2364 | 108 | match[k] = objs[0] | ||
2365 | 109 | elif len(objs) == 0: | ||
2366 | 110 | self.cantFind(player, actor, k, v) | ||
2367 | 111 | else: | ||
2368 | 112 | raise eimaginary.AmbiguousArgument(self, k, v, objs) | ||
2369 | 113 | return self.do(actor, line, **match) | ||
2370 | 114 | finally: | ||
2371 | 115 | end = time.time() | ||
2372 | 116 | log.msg(interface=iaxiom.IStatEvent, | ||
2373 | 117 | stat_actionDuration=end - begin, | ||
2374 | 118 | stat_actionExecuted=1) | ||
2375 | 119 | events.runEventTransaction(player.store, thunk) | ||
2376 | 120 | |||
2377 | 121 | |||
2378 | 122 | def cantFind(self, player, actor, slot, name): | ||
2379 | 123 | """ | ||
2380 | 124 | This hook is invoked when a target cannot be found. | ||
2381 | 125 | |||
2382 | 126 | This will delegate to a method like C{self.cantFind_<slot>(actor, | ||
2383 | 127 | name)} if one exists, to determine the error message to show to the | ||
2384 | 128 | actor. It will then raise L{eimaginary.ActionFailure} to stop | ||
2385 | 129 | processing of this action. | ||
2386 | 130 | |||
2387 | 131 | @param player: The L{Thing} doing the searching. | ||
2388 | 132 | |||
2389 | 133 | @type player: L{IThing} | ||
2390 | 134 | |||
2391 | 135 | @param actor: The L{IActor} doing the searching. | ||
2392 | 136 | |||
2393 | 137 | @type actor: L{IActor} | ||
2394 | 138 | |||
2395 | 139 | @param slot: The slot in question. | ||
2396 | 140 | |||
2397 | 141 | @type slot: C{str} | ||
2398 | 142 | |||
2399 | 143 | @param name: The name of the object being searched for. | ||
2400 | 144 | |||
2401 | 145 | @type name: C{unicode} | ||
2402 | 146 | |||
2403 | 147 | @raise eimaginary.ActionFailure: always. | ||
2404 | 148 | """ | ||
2405 | 149 | func = getattr(self, "cantFind_"+slot, None) | ||
2406 | 150 | if func: | ||
2407 | 151 | msg = func(actor, name) | ||
2408 | 152 | else: | ||
2409 | 153 | msg = "Who's that?" | ||
2410 | 154 | raise eimaginary.ActionFailure( | ||
2411 | 155 | events.ThatDoesntWork( | ||
2412 | 156 | actorMessage=msg, | ||
2413 | 157 | actor=player)) | ||
2414 | 158 | |||
2415 | 159 | |||
2416 | 160 | @classmethod | ||
2417 | 161 | def match(cls, player, line): | ||
2418 | 162 | """ | ||
2419 | 163 | Parse the given C{line} using this L{Action} type's pyparsing C{expr} | ||
2420 | 164 | attribute. A C{pyparsing.LineEnd} is appended to C{expr} to avoid | ||
2421 | 165 | accidentally matching a prefix instead of the whole line. | ||
2422 | 166 | |||
2423 | 167 | @return: a list of 2-tuples of all the results of parsing, or None if | ||
2424 | 168 | the expression does not match the given line. | ||
2425 | 169 | |||
2426 | 170 | @param line: a line of user input to be interpreted as an action. | ||
2427 | 171 | |||
2428 | 172 | @see: L{imaginary.pyparsing} | ||
2429 | 173 | """ | ||
2430 | 174 | return (cls.expr + pyparsing.LineEnd()).parseString(line) | ||
2431 | 175 | |||
2432 | 176 | |||
2433 | 177 | def do(self, player, line, **slots): | ||
2434 | 178 | """ | ||
2435 | 179 | Subclasses override this method to actually perform the action. | ||
2436 | 180 | |||
2437 | 181 | This method is performed in an event transaction, by 'run'. | ||
2438 | 182 | |||
2439 | 183 | NB: The suggested implementation strategy for a 'do' method is to do | ||
2440 | 184 | action-specific setup but then delegate the bulk of the actual logic to | ||
2441 | 185 | a method on a target/tool interface. The 'do' method's job is to | ||
2442 | 186 | select the appropriate methods to invoke. | ||
2443 | 187 | |||
2444 | 188 | @param player: a provider of this L{Action}'s C{actorInterface}. | ||
2445 | 189 | |||
2446 | 190 | @param line: the input string that created this action. | ||
2447 | 191 | |||
2448 | 192 | @param slots: The results of calling C{self.resolve} on each parsing | ||
2449 | 193 | result (described by a setResultsName in C{self.expr}). | ||
2450 | 194 | """ | ||
2451 | 195 | raise NotImplementedError("'do' method not implemented") | ||
2452 | 196 | |||
2453 | 197 | |||
2454 | 198 | def resolve(self, player, name, value): | ||
2455 | 199 | """ | ||
2456 | 200 | Resolve a given parsed value to a valid action parameter by calling a | ||
2457 | 201 | 'resolve_<name>' method on this L{Action} with the given C{player} and | ||
2458 | 202 | C{value}. | ||
2459 | 203 | |||
2460 | 204 | @param player: the L{Thing} attempting to perform this action. | ||
2461 | 205 | |||
2462 | 206 | @type player: L{Thing} | ||
2463 | 207 | |||
2464 | 208 | @param name: the name of the slot being filled. For example, 'target'. | ||
2465 | 209 | |||
2466 | 210 | @type name: L{str} | ||
2467 | 211 | |||
2468 | 212 | @param value: a string representing the value that was parsed. For | ||
2469 | 213 | example, if the user typed 'get fish', this would be 'fish'. | ||
2470 | 214 | |||
2471 | 215 | @return: a value which will be passed as the 'name' parameter to this | ||
2472 | 216 | L{Action}'s C{do} method. | ||
2473 | 217 | """ | ||
2474 | 218 | resolver = getattr(self, 'resolve_%s' % (name,), None) | ||
2475 | 219 | if resolver is None: | ||
2476 | 220 | raise NotImplementedError( | ||
2477 | 221 | "Don't know how to resolve %r (%r)" % (name, value)) | ||
2478 | 222 | return resolver(player, value) | ||
2479 | 223 | |||
2480 | 224 | |||
2481 | 225 | |||
2482 | 226 | def targetString(name): | ||
2483 | 227 | return ( | ||
2484 | 228 | _quoteRemovingQuotedString ^ | ||
2485 | 229 | UnicodeWord()).setResultsName(name) | ||
2486 | 230 | |||
2487 | 231 | |||
2488 | 232 | |||
2489 | 233 | class TargetAction(Action): | ||
2490 | 234 | """ | ||
2491 | 235 | Subclass L{TargetAction} to implement an action that acts on a target, like | ||
2492 | 236 | 'take foo' or 'eat foo' where 'foo' is the target. | ||
2493 | 237 | |||
2494 | 238 | @cvar targetInterface: the interface which the 'target' parameter to 'do' | ||
2495 | 239 | must provide. | ||
2496 | 240 | """ | ||
2497 | 241 | |||
2498 | 242 | infrastructure = True | ||
2499 | 243 | |||
2500 | 244 | targetInterface = iimaginary.IThing | ||
2501 | 245 | |||
2502 | 246 | def targetRadius(self, player): | ||
2503 | 247 | return 2 | ||
2504 | 248 | |||
2505 | 249 | def resolve_target(self, player, targetName): | ||
2506 | 250 | return _getIt(player, targetName, | ||
2507 | 251 | self.targetInterface, self.targetRadius(player)) | ||
2508 | 252 | |||
2509 | 253 | |||
2510 | 254 | |||
2511 | 255 | class ToolAction(TargetAction): | ||
2512 | 256 | """ | ||
2513 | 257 | Subclass L{ToolAction} to implement an action that acts on a target by | ||
2514 | 258 | using a tool, like 'unlock door with key', where 'door' is the target and | ||
2515 | 259 | 'key' is the tool. | ||
2516 | 260 | |||
2517 | 261 | @cvar toolInterface: the L{zope.interface.Interface} which the 'tool' | ||
2518 | 262 | parameter to 'do' must provide. | ||
2519 | 263 | """ | ||
2520 | 264 | infrastructure = True | ||
2521 | 265 | |||
2522 | 266 | toolInterface = iimaginary.IThing | ||
2523 | 267 | |||
2524 | 268 | def toolRadius(self, player): | ||
2525 | 269 | return 2 | ||
2526 | 270 | |||
2527 | 271 | def resolve_tool(self, player, toolName): | ||
2528 | 272 | return _getIt(player, toolName, | ||
2529 | 273 | self.toolInterface, self.toolRadius(player)) | ||
2530 | 274 | |||
2531 | 275 | |||
2532 | 276 | |||
2533 | 277 | def _getIt(player, thingName, iface, radius): | ||
2534 | 278 | return list(player.search(radius, iface, thingName)) | ||
2535 | 279 | |||
2536 | 280 | |||
2537 | 281 | |||
2538 | 282 | class LookAround(Action): | ||
2539 | 283 | actionName = "look" | ||
2540 | 284 | expr = pyparsing.Literal("look") + pyparsing.StringEnd() | ||
2541 | 285 | |||
2542 | 286 | def do(self, player, line): | ||
2543 | 287 | ultimateLocation = player.thing.location | ||
2544 | 288 | while ultimateLocation.location is not None: | ||
2545 | 289 | ultimateLocation = ultimateLocation.location | ||
2546 | 290 | for visible in player.thing.findProviders(iimaginary.IVisible, 1): | ||
2547 | 291 | # XXX what if my location is furniture? I want to see '( Foo, | ||
2548 | 292 | # sitting in the Bar )', not '( Bar )'. | ||
2549 | 293 | if visible.isViewOf(ultimateLocation): | ||
2550 | 294 | concept = visible.visualize() | ||
2551 | 295 | break | ||
2552 | 296 | else: | ||
2553 | 297 | concept = u"You are floating in an empty, formless void." | ||
2554 | 298 | events.Success(actor=player.thing, | ||
2555 | 299 | actorMessage=concept).broadcast() | ||
2556 | 300 | |||
2557 | 301 | |||
2558 | 302 | |||
2559 | 303 | class LookAt(TargetAction): | ||
2560 | 304 | actionName = "look" | ||
2561 | 305 | expr = (pyparsing.Literal("look") + | ||
2562 | 306 | pyparsing.Optional(pyparsing.White() + | ||
2563 | 307 | pyparsing.Literal("at")) + | ||
2564 | 308 | pyparsing.White() + | ||
2565 | 309 | pyparsing.restOfLine.setResultsName("target")) | ||
2566 | 310 | |||
2567 | 311 | targetInterface = iimaginary.IVisible | ||
2568 | 312 | |||
2569 | 313 | def resolve_target(self, player, targetName): | ||
2570 | 314 | """ | ||
2571 | 315 | Resolve the target to look at by looking for a named, visible object in | ||
2572 | 316 | a proximity of 3 meters from the player. | ||
2573 | 317 | |||
2574 | 318 | @param player: The player doing the looking. | ||
2575 | 319 | |||
2576 | 320 | @type player: L{IThing} | ||
2577 | 321 | |||
2578 | 322 | @param targetName: The name of the object we are looking for. | ||
2579 | 323 | |||
2580 | 324 | @type targetName: C{unicode} | ||
2581 | 325 | |||
2582 | 326 | @return: A list of visible objects. | ||
2583 | 327 | |||
2584 | 328 | @rtype: C{list} of L{IVisible} | ||
2585 | 329 | |||
2586 | 330 | @raise eimaginary.ActionFailure: with an appropriate message if the | ||
2587 | 331 | target cannot be resolved for an identifiable reason. See | ||
2588 | 332 | L{imaginary.objects.Thing.obtainOrReportWhyNot} for a description | ||
2589 | 333 | of how such reasons may be identified. | ||
2590 | 334 | """ | ||
2591 | 335 | return player.obtainOrReportWhyNot( | ||
2592 | 336 | Proximity(3.0, Named(targetName, | ||
2593 | 337 | CanSee(ProviderOf(iimaginary.IVisible)), | ||
2594 | 338 | player))) | ||
2595 | 339 | |||
2596 | 340 | |||
2597 | 341 | def cantFind_target(self, player, name): | ||
2598 | 342 | return "You don't see that." | ||
2599 | 343 | |||
2600 | 344 | def targetRadius(self, player): | ||
2601 | 345 | return 3 | ||
2602 | 346 | |||
2603 | 347 | def do(self, player, line, target): | ||
2604 | 348 | if player.thing is not target: | ||
2605 | 349 | evt = events.Success( | ||
2606 | 350 | actor=player.thing, | ||
2607 | 351 | target=target, | ||
2608 | 352 | actorMessage=target.visualize(), | ||
2609 | 353 | targetMessage=(player.thing, " looks at you.")) | ||
2610 | 354 | else: | ||
2611 | 355 | evt = events.Success( | ||
2612 | 356 | actor=player.thing, | ||
2613 | 357 | actorMessage=target.visualize()) | ||
2614 | 358 | evt.broadcast() | ||
2615 | 359 | |||
2616 | 360 | |||
2617 | 361 | |||
2618 | 362 | class Illuminate(Action): | ||
2619 | 363 | """ | ||
2620 | 364 | Change the ambient light level at the location of the actor. Since this is | ||
2621 | 365 | an administrative action that directly manipulates the environment, the | ||
2622 | 366 | actor must be a L{iimaginary.IManipulator}. | ||
2623 | 367 | |||
2624 | 368 | The argument taken by this action is an integer which specifies the light | ||
2625 | 369 | level in U{candelas<http://en.wikipedia.org/wiki/Candela>}. | ||
2626 | 370 | """ | ||
2627 | 371 | |||
2628 | 372 | actorInterface = iimaginary.IManipulator | ||
2629 | 373 | |||
2630 | 374 | expr = (pyparsing.Literal("illuminate") + | ||
2631 | 375 | pyparsing.White() + | ||
2632 | 376 | pyparsing.Word("0123456789").setResultsName("candelas")) | ||
2633 | 377 | |||
2634 | 378 | |||
2635 | 379 | def do(self, player, line, candelas): | ||
2636 | 380 | """ | ||
2637 | 381 | Attempt to change the illumination of the player's surroundings. | ||
2638 | 382 | |||
2639 | 383 | @param player: a manipulator that can change the illumination of its | ||
2640 | 384 | room. | ||
2641 | 385 | @type player: L{IManipulator} | ||
2642 | 386 | |||
2643 | 387 | @param line: the text being parsed | ||
2644 | 388 | @type line: L{str} | ||
2645 | 389 | |||
2646 | 390 | @param candelas: the number of candelas to change the ambient | ||
2647 | 391 | illumination to. | ||
2648 | 392 | @type candelas: L{str} | ||
2649 | 393 | """ | ||
2650 | 394 | candelas = int(candelas) | ||
2651 | 395 | oldCandelas = player.setIllumination(candelas) | ||
2652 | 396 | otherMessage = None | ||
2653 | 397 | if oldCandelas == candelas: | ||
2654 | 398 | actorMessage = u"You do it. Swell." | ||
2655 | 399 | elif candelas == 0: | ||
2656 | 400 | actorMessage = ( | ||
2657 | 401 | u"Your environs fade to black due to Ineffable Spooky Magic.") | ||
2658 | 402 | otherMessage = actorMessage | ||
2659 | 403 | elif oldCandelas == 0: | ||
2660 | 404 | actorMessage = u"Your environs are suddenly alight." | ||
2661 | 405 | otherMessage = actorMessage | ||
2662 | 406 | elif candelas < oldCandelas: | ||
2663 | 407 | actorMessage = u"Your environs seem slightly dimmer." | ||
2664 | 408 | otherMessage = actorMessage | ||
2665 | 409 | elif candelas > oldCandelas: | ||
2666 | 410 | actorMessage = u"Your environs seem slightly brighter." | ||
2667 | 411 | otherMessage = actorMessage | ||
2668 | 412 | events.Success(actor=player.thing, | ||
2669 | 413 | actorMessage=actorMessage, | ||
2670 | 414 | otherMessage=otherMessage).broadcast() | ||
2671 | 415 | |||
2672 | 416 | |||
2673 | 417 | |||
2674 | 418 | class Describe(TargetAction): | ||
2675 | 419 | expr = (pyparsing.Literal("describe") + | ||
2676 | 420 | pyparsing.White() + | ||
2677 | 421 | targetString("target") + | ||
2678 | 422 | pyparsing.White() + | ||
2679 | 423 | pyparsing.restOfLine.setResultsName("description")) | ||
2680 | 424 | |||
2681 | 425 | def targetRadius(self, player): | ||
2682 | 426 | return 3 | ||
2683 | 427 | |||
2684 | 428 | def do(self, player, line, target, description): | ||
2685 | 429 | target.description = description | ||
2686 | 430 | evt = events.Success( | ||
2687 | 431 | actor=player.thing, | ||
2688 | 432 | actorMessage=("You change ", target, "'s description."), | ||
2689 | 433 | otherMessage=(player.thing, " changes ", target, "'s description.")) | ||
2690 | 434 | evt.broadcast() | ||
2691 | 435 | |||
2692 | 436 | |||
2693 | 437 | class Name(TargetAction): | ||
2694 | 438 | expr = (pyparsing.Literal("name") + | ||
2695 | 439 | pyparsing.White() + | ||
2696 | 440 | targetString("target") + | ||
2697 | 441 | pyparsing.White() + | ||
2698 | 442 | pyparsing.restOfLine.setResultsName("name")) | ||
2699 | 443 | |||
2700 | 444 | def targetRadius(self, player): | ||
2701 | 445 | return 3 | ||
2702 | 446 | |||
2703 | 447 | def do(self, player, line, target, name): | ||
2704 | 448 | evt = events.Success( | ||
2705 | 449 | actor=player.thing, | ||
2706 | 450 | actorMessage=("You change ", target, "'s name."), | ||
2707 | 451 | otherMessage=language.Sentence([player.thing, " changes ", target, "'s name to ", name, "."])) | ||
2708 | 452 | evt.broadcast() | ||
2709 | 453 | target.name = name | ||
2710 | 454 | |||
2711 | 455 | |||
2712 | 456 | |||
2713 | 457 | class Open(TargetAction): | ||
2714 | 458 | expr = (pyparsing.Literal("open") + | ||
2715 | 459 | pyparsing.White() + | ||
2716 | 460 | targetString("target")) | ||
2717 | 461 | |||
2718 | 462 | targetInterface = iimaginary.IContainer | ||
2719 | 463 | |||
2720 | 464 | def do(self, player, line, target): | ||
2721 | 465 | dnf = language.Noun(target.thing).definiteNounPhrase() | ||
2722 | 466 | if not target.closed: | ||
2723 | 467 | raise eimaginary.ActionFailure(events.ThatDoesntWork( | ||
2724 | 468 | actor=player.thing, | ||
2725 | 469 | target=target.thing, | ||
2726 | 470 | actorMessage=language.Sentence([dnf, " is already open."]))) | ||
2727 | 471 | |||
2728 | 472 | target.closed = False | ||
2729 | 473 | evt = events.Success( | ||
2730 | 474 | actor=player.thing, | ||
2731 | 475 | target=target.thing, | ||
2732 | 476 | actorMessage=("You open ", dnf, "."), | ||
2733 | 477 | targetMessage=language.Sentence([player.thing, " opens you."]), | ||
2734 | 478 | otherMessage=language.Sentence([player.thing, " opens ", target.thing, "."])) | ||
2735 | 479 | evt.broadcast() | ||
2736 | 480 | |||
2737 | 481 | |||
2738 | 482 | |||
2739 | 483 | class Close(TargetAction): | ||
2740 | 484 | expr = (pyparsing.Literal("close") + | ||
2741 | 485 | pyparsing.White() + | ||
2742 | 486 | targetString("target")) | ||
2743 | 487 | |||
2744 | 488 | targetInterface = iimaginary.IContainer | ||
2745 | 489 | |||
2746 | 490 | def do(self, player, line, target): | ||
2747 | 491 | dnf = language.Noun(target.thing).definiteNounPhrase() | ||
2748 | 492 | if target.closed: | ||
2749 | 493 | raise eimaginary.ActionFailure(events.ThatDoesntWork( | ||
2750 | 494 | actor=player.thing, | ||
2751 | 495 | target=target.thing, | ||
2752 | 496 | actorMessage=language.Sentence([dnf, " is already closed."]))) | ||
2753 | 497 | |||
2754 | 498 | target.closed = True | ||
2755 | 499 | evt = events.Success( | ||
2756 | 500 | actor=player.thing, | ||
2757 | 501 | target=target.thing, | ||
2758 | 502 | actorMessage=("You close ", dnf, "."), | ||
2759 | 503 | targetMessage=language.Sentence([player.thing, " closes you."]), | ||
2760 | 504 | otherMessage=language.Sentence([player.thing, " closes ", target.thing, "."])) | ||
2761 | 505 | evt.broadcast() | ||
2762 | 506 | |||
2763 | 507 | |||
2764 | 508 | |||
2765 | 509 | def tooHeavy(player, target): | ||
2766 | 510 | return eimaginary.ActionFailure(events.ThatDoesntWork( | ||
2767 | 511 | actor=player, target=target, | ||
2768 | 512 | actorMessage=(target, " is too heavy to pick up."), | ||
2769 | 513 | otherMessage=(player, " struggles to lift ", target, ", but fails."), | ||
2770 | 514 | targetMessage=(player, " tries to pick you up, but fails."))) | ||
2771 | 515 | |||
2772 | 516 | |||
2773 | 517 | |||
2774 | 518 | def targetTaken(player, target, container=None): | ||
2775 | 519 | if container is None: | ||
2776 | 520 | return events.Success( | ||
2777 | 521 | actor=player, target=target, | ||
2778 | 522 | actorMessage=("You take ", target, "."), | ||
2779 | 523 | targetMessage=(player, " takes you."), | ||
2780 | 524 | otherMessage=(player, " takes ", target, ".")) | ||
2781 | 525 | idop = language.Noun(container).definiteNounPhrase() | ||
2782 | 526 | return events.Success( | ||
2783 | 527 | actor=player, | ||
2784 | 528 | target=target, | ||
2785 | 529 | tool=container, | ||
2786 | 530 | actorMessage=("You take ", target, " from ", idop, "."), | ||
2787 | 531 | targetMessage=(player, " takes you from ", idop, "."), | ||
2788 | 532 | toolMessage=(player, " takes ", target, " from you."), | ||
2789 | 533 | otherMessage=(player, " takes ", target, " from ", idop, ".")) | ||
2790 | 534 | |||
2791 | 535 | |||
2792 | 536 | |||
2793 | 537 | class Remove(TargetAction): | ||
2794 | 538 | expr = ((pyparsing.Literal("remove") | | ||
2795 | 539 | pyparsing.Literal("take off")) + | ||
2796 | 540 | pyparsing.White() + | ||
2797 | 541 | targetString("target")) | ||
2798 | 542 | |||
2799 | 543 | targetInterface = iimaginary.IClothing | ||
2800 | 544 | actorInterface = iimaginary.IClothingWearer | ||
2801 | 545 | |||
2802 | 546 | def do(self, player, line, target): | ||
2803 | 547 | from imaginary import garments | ||
2804 | 548 | try: | ||
2805 | 549 | player.takeOff(target) | ||
2806 | 550 | except garments.InaccessibleGarment, e: | ||
2807 | 551 | raise eimaginary.ActionFailure(events.ThatDoesntWork( | ||
2808 | 552 | actor=player.thing, | ||
2809 | 553 | target=target.thing, | ||
2810 | 554 | actorMessage=(u"You cannot take off ", | ||
2811 | 555 | language.Noun(target.thing).definiteNounPhrase(), | ||
2812 | 556 | u" because you are wearing ", | ||
2813 | 557 | e.obscuringGarment.thing, u"."), | ||
2814 | 558 | otherMessage=language.Sentence([ | ||
2815 | 559 | player.thing, | ||
2816 | 560 | u" gets a dumb look on ", | ||
2817 | 561 | language.Noun(player.thing).hisHer(), | ||
2818 | 562 | u" face."]))) | ||
2819 | 563 | |||
2820 | 564 | evt = events.Success( | ||
2821 | 565 | actor=player.thing, | ||
2822 | 566 | target=target.thing, | ||
2823 | 567 | actorMessage=(u"You take off ", | ||
2824 | 568 | language.Noun(target.thing).definiteNounPhrase(), | ||
2825 | 569 | u"."), | ||
2826 | 570 | otherMessage=language.Sentence([ | ||
2827 | 571 | player.thing, u" takes off ", target.thing, u"."])) | ||
2828 | 572 | evt.broadcast() | ||
2829 | 573 | |||
2830 | 574 | |||
2831 | 575 | |||
2832 | 576 | class Wear(TargetAction): | ||
2833 | 577 | expr = (pyparsing.Literal("wear") + | ||
2834 | 578 | pyparsing.White() + | ||
2835 | 579 | targetString("target")) | ||
2836 | 580 | |||
2837 | 581 | targetInterface = iimaginary.IClothing | ||
2838 | 582 | actorInterface = iimaginary.IClothingWearer | ||
2839 | 583 | |||
2840 | 584 | def do(self, player, line, target): | ||
2841 | 585 | from imaginary import garments | ||
2842 | 586 | try: | ||
2843 | 587 | player.putOn(target) | ||
2844 | 588 | except garments.TooBulky, e: | ||
2845 | 589 | raise eimaginary.ActionFailure(events.ThatDoesntWork( | ||
2846 | 590 | actor=player.thing, | ||
2847 | 591 | target=target.thing, | ||
2848 | 592 | actorMessage=language.Sentence([ | ||
2849 | 593 | language.Noun(e.wornGarment.thing).definiteNounPhrase(), | ||
2850 | 594 | u" you are already wearing is too bulky for you to do" | ||
2851 | 595 | u" that."]), | ||
2852 | 596 | otherMessage=language.Sentence([ | ||
2853 | 597 | player.thing, | ||
2854 | 598 | u" wrestles with basic personal problems."]))) | ||
2855 | 599 | |||
2856 | 600 | evt = events.Success( | ||
2857 | 601 | actor=player.thing, | ||
2858 | 602 | target=target.thing, | ||
2859 | 603 | actorMessage=(u"You put on ", | ||
2860 | 604 | language.Noun(target.thing).definiteNounPhrase(), | ||
2861 | 605 | "."), | ||
2862 | 606 | otherMessage=language.Sentence([ | ||
2863 | 607 | player.thing, " puts on ", target.thing, "."])) | ||
2864 | 608 | evt.broadcast() | ||
2865 | 609 | |||
2866 | 610 | |||
2867 | 611 | |||
2868 | 612 | class Equipment(Action): | ||
2869 | 613 | expr = pyparsing.Literal("equipment") | ||
2870 | 614 | |||
2871 | 615 | actorInterface = iimaginary.IClothingWearer | ||
2872 | 616 | |||
2873 | 617 | def do(self, player, line): | ||
2874 | 618 | from imaginary import garments | ||
2875 | 619 | equipment = list(player.store.query( | ||
2876 | 620 | objects.Thing, | ||
2877 | 621 | AND( | ||
2878 | 622 | garments.Garment.thing == objects.Thing.storeID, | ||
2879 | 623 | garments.Garment.wearer == player), | ||
2880 | 624 | sort=objects.Thing.name.ascending)) | ||
2881 | 625 | if equipment: | ||
2882 | 626 | evt = events.Success( | ||
2883 | 627 | actor=player.thing, | ||
2884 | 628 | actorMessage=[ | ||
2885 | 629 | u"You are wearing ", | ||
2886 | 630 | language.ItemizedList(equipment), | ||
2887 | 631 | u"."]) | ||
2888 | 632 | else: | ||
2889 | 633 | evt = events.Success( | ||
2890 | 634 | actor=player.thing, | ||
2891 | 635 | actorMessage=language.ExpressString( | ||
2892 | 636 | u"You aren't wearing any equipment.")) | ||
2893 | 637 | evt.broadcast() | ||
2894 | 638 | |||
2895 | 639 | |||
2896 | 640 | |||
2897 | 641 | class TakeFrom(ToolAction): | ||
2898 | 642 | actionName = "take" | ||
2899 | 643 | |||
2900 | 644 | expr = ((pyparsing.Literal("get") ^ pyparsing.Literal("take")) + | ||
2901 | 645 | pyparsing.White() + | ||
2902 | 646 | targetString("target") + | ||
2903 | 647 | pyparsing.Optional(pyparsing.White() + | ||
2904 | 648 | pyparsing.Literal("from")) + | ||
2905 | 649 | pyparsing.White() + | ||
2906 | 650 | targetString("tool")) | ||
2907 | 651 | |||
2908 | 652 | def cantFind_target(self, player, targetName): | ||
2909 | 653 | return "Nothing like that around here." | ||
2910 | 654 | cantFind_tool = cantFind_target | ||
2911 | 655 | |||
2912 | 656 | def do(self, player, line, target, tool): | ||
2913 | 657 | # XXX Make sure target is in tool | ||
2914 | 658 | targetTaken(player.thing, target, tool).broadcast() | ||
2915 | 659 | try: | ||
2916 | 660 | target.moveTo(player.thing) | ||
2917 | 661 | except eimaginary.DoesntFit: | ||
2918 | 662 | raise tooHeavy(player.thing, target) | ||
2919 | 663 | |||
2920 | 664 | |||
2921 | 665 | |||
2922 | 666 | class PutIn(ToolAction): | ||
2923 | 667 | |||
2924 | 668 | toolInterface = iimaginary.IThing | ||
2925 | 669 | targetInterface = iimaginary.IContainer | ||
2926 | 670 | |||
2927 | 671 | def cantFind_target(self, player, targetName): | ||
2928 | 672 | return "That doesn't work." | ||
2929 | 673 | |||
2930 | 674 | expr = (pyparsing.Literal("put") + | ||
2931 | 675 | pyparsing.White() + | ||
2932 | 676 | targetString("tool") + | ||
2933 | 677 | pyparsing.Optional(pyparsing.White() + | ||
2934 | 678 | pyparsing.Literal("in")) + | ||
2935 | 679 | pyparsing.White() + | ||
2936 | 680 | targetString("target")) | ||
2937 | 681 | |||
2938 | 682 | def do(self, player, line, tool, target): | ||
2939 | 683 | ctool = iimaginary.IContainer(tool, None) | ||
2940 | 684 | targetObject = target.thing | ||
2941 | 685 | if ctool is not None and (ctool.contains(targetObject) or ctool is target): | ||
2942 | 686 | raise eimaginary.ActionFailure( | ||
2943 | 687 | events.ThatDoesntWork( | ||
2944 | 688 | actor=player.thing, | ||
2945 | 689 | target=targetObject, | ||
2946 | 690 | tool=tool, | ||
2947 | 691 | actorMessage="A thing cannot contain itself in euclidean space.")) | ||
2948 | 692 | |||
2949 | 693 | dnf = language.Noun(targetObject).definiteNounPhrase() | ||
2950 | 694 | evt = events.Success( | ||
2951 | 695 | actor=player.thing, | ||
2952 | 696 | target=targetObject, | ||
2953 | 697 | tool=tool, | ||
2954 | 698 | actorMessage=("You put ", | ||
2955 | 699 | language.Noun(tool).definiteNounPhrase(), | ||
2956 | 700 | " in ", dnf, "."), | ||
2957 | 701 | targetMessage=language.Sentence([player.thing, " puts ", " tool in you."]), | ||
2958 | 702 | toolMessage=language.Sentence([player.thing, " puts you in ", targetObject, "."]), | ||
2959 | 703 | otherMessage=language.Sentence([player.thing, " puts ", tool, " in ", targetObject, "."])) | ||
2960 | 704 | evt.broadcast() | ||
2961 | 705 | |||
2962 | 706 | try: | ||
2963 | 707 | tool.moveTo(target) | ||
2964 | 708 | except eimaginary.DoesntFit: | ||
2965 | 709 | # <allexpro> dash: put me in a tent and give it to moshez! | ||
2966 | 710 | raise eimaginary.ActionFailure( | ||
2967 | 711 | events.ThatDoesntWork( | ||
2968 | 712 | actor=player.thing, | ||
2969 | 713 | target=targetObject, | ||
2970 | 714 | tool=tool, | ||
2971 | 715 | actorMessage=language.Sentence([ | ||
2972 | 716 | language.Noun(tool).definiteNounPhrase(), | ||
2973 | 717 | u" does not fit in ", dnf, u"."]))) | ||
2974 | 718 | except eimaginary.Closed: | ||
2975 | 719 | raise eimaginary.ActionFailure( | ||
2976 | 720 | events.ThatDoesntWork( | ||
2977 | 721 | actor=player.thing, | ||
2978 | 722 | target=targetObject, | ||
2979 | 723 | tool=tool, | ||
2980 | 724 | actorMessage=language.Sentence([dnf, " is closed."]))) | ||
2981 | 725 | |||
2982 | 726 | |||
2983 | 727 | |||
2984 | 728 | class Take(TargetAction): | ||
2985 | 729 | expr = ((pyparsing.Literal("get") ^ pyparsing.Literal("take")) + | ||
2986 | 730 | pyparsing.White() + | ||
2987 | 731 | targetString("target")) | ||
2988 | 732 | |||
2989 | 733 | def cantFind_target(self, player, targetName): | ||
2990 | 734 | return u"Nothing like that around here." | ||
2991 | 735 | |||
2992 | 736 | def targetRadius(self, player): | ||
2993 | 737 | return 1 | ||
2994 | 738 | |||
2995 | 739 | def do(self, player, line, target): | ||
2996 | 740 | if target in (player.thing, player.thing.location) or target.location is player.thing: | ||
2997 | 741 | raise eimaginary.ActionFailure(events.ThatDoesntMakeSense( | ||
2998 | 742 | actor=player.thing, | ||
2999 | 743 | actorMessage=("You cannot take ", target, "."))) | ||
3000 | 744 | |||
3001 | 745 | targetTaken(player.thing, target).broadcast() | ||
3002 | 746 | try: | ||
3003 | 747 | target.moveTo(player.thing) | ||
3004 | 748 | except eimaginary.DoesntFit: | ||
3005 | 749 | raise tooHeavy(player.thing, target) | ||
3006 | 750 | |||
3007 | 751 | |||
3008 | 752 | |||
3009 | 753 | def insufficientSpace(player): | ||
3010 | 754 | return eimaginary.ActionFailure(events.ThatDoesntWork( | ||
3011 | 755 | actor=player, | ||
3012 | 756 | actorMessage="There's not enough space for that.")) | ||
3013 | 757 | |||
3014 | 758 | |||
3015 | 759 | |||
3016 | 760 | class Drop(TargetAction): | ||
3017 | 761 | expr = (pyparsing.Literal("drop") + | ||
3018 | 762 | pyparsing.White() + | ||
3019 | 763 | targetString("target")) | ||
3020 | 764 | |||
3021 | 765 | def cantFind_target(self, player, targetName): | ||
3022 | 766 | return "Nothing like that around here." | ||
3023 | 767 | |||
3024 | 768 | def targetRadius(self, player): | ||
3025 | 769 | return 1 | ||
3026 | 770 | |||
3027 | 771 | def do(self, player, line, target): | ||
3028 | 772 | if target.location is not player.thing: | ||
3029 | 773 | raise eimaginary.ActionFailure( | ||
3030 | 774 | events.ThatDoesntMakeSense( | ||
3031 | 775 | actor=player.thing, | ||
3032 | 776 | actorMessage="You can't drop that.")) | ||
3033 | 777 | |||
3034 | 778 | try: | ||
3035 | 779 | target.moveTo( | ||
3036 | 780 | player.thing.location, | ||
3037 | 781 | arrivalEventFactory=lambda target: events.ArrivalEvent( | ||
3038 | 782 | actor=player.thing, | ||
3039 | 783 | actorMessage=("You drop ", | ||
3040 | 784 | language.Noun(target).definiteNounPhrase(), | ||
3041 | 785 | "."), | ||
3042 | 786 | target=target, | ||
3043 | 787 | targetMessage=(player.thing, " drops you."), | ||
3044 | 788 | otherMessage=(player.thing, " drops ", target, "."))) | ||
3045 | 789 | except eimaginary.DoesntFit: | ||
3046 | 790 | raise insufficientSpace(player.thing) | ||
3047 | 791 | |||
3048 | 792 | |||
3049 | 793 | |||
3050 | 794 | _directionNames = objects.OPPOSITE_DIRECTIONS.keys() | ||
3051 | 795 | _directionNames.extend(objects.DIRECTION_ALIASES.keys()) | ||
3052 | 796 | |||
3053 | 797 | DIRECTION_LITERAL = reduce( | ||
3054 | 798 | operator.xor, [ | ||
3055 | 799 | pyparsing.Literal(d) | ||
3056 | 800 | for d in _directionNames]).setResultsName("direction") | ||
3057 | 801 | |||
3058 | 802 | |||
3059 | 803 | |||
3060 | 804 | def expandDirection(direction): | ||
3061 | 805 | """ | ||
3062 | 806 | Expand direction aliases into the names of the directions they refer to. | ||
3063 | 807 | """ | ||
3064 | 808 | return objects.DIRECTION_ALIASES.get(direction, direction) | ||
3065 | 809 | |||
3066 | 810 | |||
3067 | 811 | |||
3068 | 812 | class Dig(Action): | ||
3069 | 813 | expr = (pyparsing.Literal("dig") + | ||
3070 | 814 | pyparsing.White() + | ||
3071 | 815 | DIRECTION_LITERAL + | ||
3072 | 816 | pyparsing.White() + | ||
3073 | 817 | pyparsing.restOfLine.setResultsName("name")) | ||
3074 | 818 | |||
3075 | 819 | def do(self, player, line, direction, name): | ||
3076 | 820 | direction = expandDirection(direction) | ||
3077 | 821 | if iimaginary.IContainer(player.thing.location).getExitNamed(direction, None) is not None: | ||
3078 | 822 | raise eimaginary.ActionFailure(events.ThatDoesntMakeSense( | ||
3079 | 823 | actor=player.thing, | ||
3080 | 824 | actorMessage="There is already an exit in that direction.")) | ||
3081 | 825 | |||
3082 | 826 | room = objects.Thing(store=player.store, name=name) | ||
3083 | 827 | objects.Container.createFor(room, capacity=1000) | ||
3084 | 828 | objects.Exit.link(player.thing.location, room, direction) | ||
3085 | 829 | |||
3086 | 830 | evt = events.Success( | ||
3087 | 831 | actor=player.thing, | ||
3088 | 832 | actorMessage="You create an exit.", | ||
3089 | 833 | otherMessage=language.Sentence([player.thing, " created an exit to the ", direction, "."])) | ||
3090 | 834 | evt.broadcast() | ||
3091 | 835 | |||
3092 | 836 | # XXX Right now there can't possibly be anyone in the | ||
3093 | 837 | # destination room, but someday there could be. When there | ||
3094 | 838 | # could be, broadcast this to them too. | ||
3095 | 839 | |||
3096 | 840 | |||
3097 | 841 | |||
3098 | 842 | class Bury(Action): | ||
3099 | 843 | expr = (pyparsing.Literal("bury") + | ||
3100 | 844 | pyparsing.White() + | ||
3101 | 845 | DIRECTION_LITERAL) | ||
3102 | 846 | |||
3103 | 847 | def do(self, player, line, direction): | ||
3104 | 848 | direction = expandDirection(direction) | ||
3105 | 849 | for exit in iimaginary.IContainer(player.thing.location).getExits(): | ||
3106 | 850 | if exit.name == direction: | ||
3107 | 851 | if exit.sibling is not None: | ||
3108 | 852 | evt = events.Success( | ||
3109 | 853 | location=exit.toLocation, | ||
3110 | 854 | otherMessage=language.Sentence([ | ||
3111 | 855 | exit.sibling, " crumbles and disappears."])) | ||
3112 | 856 | evt.broadcast() | ||
3113 | 857 | |||
3114 | 858 | evt = events.Success( | ||
3115 | 859 | actor=player.thing, | ||
3116 | 860 | actorMessage="It's gone.", | ||
3117 | 861 | otherMessage=language.Sentence([ | ||
3118 | 862 | language.Noun(player.thing).nounPhrase(), | ||
3119 | 863 | " destroyed ", exit, "."])) | ||
3120 | 864 | evt.broadcast() | ||
3121 | 865 | exit.destroy() | ||
3122 | 866 | return | ||
3123 | 867 | |||
3124 | 868 | raise eimaginary.ActionFailure(events.ThatDoesntMakeSense( | ||
3125 | 869 | actor=player.thing, | ||
3126 | 870 | actorMessage="There isn't an exit in that direction.")) | ||
3127 | 871 | |||
3128 | 872 | |||
3129 | 873 | |||
3130 | 874 | class Go(Action): | ||
3131 | 875 | expr = ( | ||
3132 | 876 | (pyparsing.Literal("go") + pyparsing.White() + | ||
3133 | 877 | targetString("direction")) | | ||
3134 | 878 | (pyparsing.Literal("enter") + pyparsing.White() + | ||
3135 | 879 | targetString("direction")) | | ||
3136 | 880 | (pyparsing.Literal("exit") + pyparsing.White() + | ||
3137 | 881 | targetString("direction")) | | ||
3138 | 882 | DIRECTION_LITERAL) | ||
3139 | 883 | |||
3140 | 884 | actorInterface = iimaginary.IThing | ||
3141 | 885 | |||
3142 | 886 | def resolve_direction(self, player, directionName): | ||
3143 | 887 | """ | ||
3144 | 888 | Identify a direction by having the player search for L{IExit} | ||
3145 | 889 | providers that they can see and reach. | ||
3146 | 890 | """ | ||
3147 | 891 | directionName = expandDirection(directionName) | ||
3148 | 892 | return player.obtainOrReportWhyNot( | ||
3149 | 893 | Proximity( | ||
3150 | 894 | 3.0, | ||
3151 | 895 | Traversability( | ||
3152 | 896 | Named(directionName, | ||
3153 | 897 | CanSee(ProviderOf(iimaginary.IExit)), player)))) | ||
3154 | 898 | |||
3155 | 899 | |||
3156 | 900 | def cantFind_direction(self, actor, directionName): | ||
3157 | 901 | """ | ||
3158 | 902 | Explain to the user that they can't go in a direction that they can't | ||
3159 | 903 | locate. | ||
3160 | 904 | """ | ||
3161 | 905 | return u"You can't go that way." | ||
3162 | 906 | |||
3163 | 907 | |||
3164 | 908 | def do(self, player, line, direction): | ||
3165 | 909 | location = player.location | ||
3166 | 910 | |||
3167 | 911 | evt = events.Success( | ||
3168 | 912 | location=location, | ||
3169 | 913 | actor=player, | ||
3170 | 914 | otherMessage=(player, " leaves ", direction.name, ".")) | ||
3171 | 915 | evt.broadcast() | ||
3172 | 916 | |||
3173 | 917 | try: | ||
3174 | 918 | direction.traverse(player) | ||
3175 | 919 | except eimaginary.DoesntFit: | ||
3176 | 920 | raise eimaginary.ActionFailure(events.ThatDoesntWork( | ||
3177 | 921 | actor=player, | ||
3178 | 922 | actorMessage=language.ExpressString( | ||
3179 | 923 | u"There's no room for you there."))) | ||
3180 | 924 | |||
3181 | 925 | # This is subtly incorrect: see http://divmod.org/trac/ticket/2917 | ||
3182 | 926 | lookAroundActor = iimaginary.IActor(player) | ||
3183 | 927 | LookAround().do(lookAroundActor, "look") | ||
3184 | 928 | |||
3185 | 929 | |||
3186 | 930 | |||
3187 | 931 | class Restore(TargetAction): | ||
3188 | 932 | expr = (pyparsing.Literal("restore") + | ||
3189 | 933 | pyparsing.White() + | ||
3190 | 934 | pyparsing.restOfLine.setResultsName("target")) | ||
3191 | 935 | |||
3192 | 936 | targetInterface = iimaginary.IActor | ||
3193 | 937 | |||
3194 | 938 | def cantFind_target(self, player, targetName): | ||
3195 | 939 | for thing in player.thing.search(self.targetRadius(player), | ||
3196 | 940 | iimaginary.IThing, targetName): | ||
3197 | 941 | return (language.Noun(thing).nounPhrase().plaintext(player), | ||
3198 | 942 | " cannot be restored.") | ||
3199 | 943 | return "Who's that?" | ||
3200 | 944 | |||
3201 | 945 | def targetRadius(self, player): | ||
3202 | 946 | return 3 | ||
3203 | 947 | |||
3204 | 948 | |||
3205 | 949 | def do(self, player, line, target): | ||
3206 | 950 | target.hitpoints.current = target.hitpoints.max | ||
3207 | 951 | target.stamina.current = target.stamina.max | ||
3208 | 952 | |||
3209 | 953 | if player is target: | ||
3210 | 954 | evt = events.Success( | ||
3211 | 955 | actor=player.thing, | ||
3212 | 956 | actorMessage="You have fully restored yourself.") | ||
3213 | 957 | evt.broadcast() | ||
3214 | 958 | else: | ||
3215 | 959 | evt = events.Success( | ||
3216 | 960 | actor=player.thing, | ||
3217 | 961 | actorMessage=("You have restored ", target.thing, " to full health."), | ||
3218 | 962 | target=target.thing, | ||
3219 | 963 | targetMessage=(player.thing, " has restored you to full health."), | ||
3220 | 964 | otherMessage=(player.thing, " has restored ", target.thing, " to full health.")) | ||
3221 | 965 | evt.broadcast() | ||
3222 | 966 | |||
3223 | 967 | |||
3224 | 968 | |||
3225 | 969 | class Hit(TargetAction): | ||
3226 | 970 | expr = ((pyparsing.Literal("hit") ^ | ||
3227 | 971 | pyparsing.Literal("attack") ^ | ||
3228 | 972 | pyparsing.Literal("kill")) + | ||
3229 | 973 | pyparsing.White() + | ||
3230 | 974 | pyparsing.restOfLine.setResultsName("target")) | ||
3231 | 975 | |||
3232 | 976 | targetInterface = iimaginary.IActor | ||
3233 | 977 | |||
3234 | 978 | def targetRadius(self, player): | ||
3235 | 979 | return 3 | ||
3236 | 980 | |||
3237 | 981 | def do(self, player, line, target): | ||
3238 | 982 | if target is player: | ||
3239 | 983 | raise eimaginary.ActionFailure( | ||
3240 | 984 | events.ThatDoesntMakeSense(u"Hit yourself? Stupid.", | ||
3241 | 985 | actor=player.thing)) | ||
3242 | 986 | |||
3243 | 987 | cost = random.randrange(1, 5) | ||
3244 | 988 | if player.stamina < cost: | ||
3245 | 989 | raise eimaginary.ActionFailure( | ||
3246 | 990 | events.ThatDoesntWork(u"You're too tired!", | ||
3247 | 991 | actor=player.thing)) | ||
3248 | 992 | |||
3249 | 993 | damage = random.randrange(1, 5) | ||
3250 | 994 | player.stamina.decrease(cost) | ||
3251 | 995 | thp = target.hitpoints.decrease(damage) | ||
3252 | 996 | events.Success( | ||
3253 | 997 | actor=player.thing, | ||
3254 | 998 | target=target.thing, | ||
3255 | 999 | targetMessage=language.Sentence([player.thing, " hits you for ", damage, " hitpoints."]), | ||
3256 | 1000 | actorMessage=language.Sentence(["You hit ", language.Noun(target.thing).definiteNounPhrase(), " for ", damage, " hitpoints."]), | ||
3257 | 1001 | otherMessage=language.Sentence([player.thing, " hits ", target.thing, "."])).broadcast() | ||
3258 | 1002 | |||
3259 | 1003 | if thp <= 0: | ||
3260 | 1004 | xp = target.experience / 2 + 1 | ||
3261 | 1005 | player.gainExperience(xp) # I LOVE IT | ||
3262 | 1006 | targetIsDead = [target.thing, " is dead!", "\n"] | ||
3263 | 1007 | events.Success( | ||
3264 | 1008 | actor=player.thing, target=target.thing, | ||
3265 | 1009 | actorMessage=["\n", targetIsDead, "You gain ", xp, " experience"], | ||
3266 | 1010 | targetMessage=["You are dead!"], | ||
3267 | 1011 | otherMessage=targetIsDead).broadcast() | ||
3268 | 1012 | target.thing.destroy() | ||
3269 | 1013 | |||
3270 | 1014 | |||
3271 | 1015 | |||
3272 | 1016 | class Say(Action): | ||
3273 | 1017 | expr = (((pyparsing.Literal("say") + pyparsing.White()) ^ | ||
3274 | 1018 | pyparsing.Literal("'")) + | ||
3275 | 1019 | pyparsing.restOfLine.setResultsName("text")) | ||
3276 | 1020 | |||
3277 | 1021 | def do(self, player, line, text): | ||
3278 | 1022 | evt = events.SpeechEvent(speaker=player.thing, text=text) | ||
3279 | 1023 | evt.broadcast() | ||
3280 | 1024 | |||
3281 | 1025 | |||
3282 | 1026 | |||
3283 | 1027 | class Emote(Action): | ||
3284 | 1028 | expr = (((pyparsing.Literal("emote") + pyparsing.White()) ^ | ||
3285 | 1029 | pyparsing.Literal(":")) + | ||
3286 | 1030 | pyparsing.restOfLine.setResultsName("text")) | ||
3287 | 1031 | |||
3288 | 1032 | def do(self, player, line, text): | ||
3289 | 1033 | evt = events.Success(actor=player.thing, | ||
3290 | 1034 | actorMessage=[player.thing, " ", text], | ||
3291 | 1035 | otherMessage=[player.thing, " ", text]) | ||
3292 | 1036 | evt.broadcast() | ||
3293 | 1037 | |||
3294 | 1038 | |||
3295 | 1039 | |||
3296 | 1040 | class Actions(Action): | ||
3297 | 1041 | expr = pyparsing.Literal("actions") | ||
3298 | 1042 | |||
3299 | 1043 | def do(self, player, line): | ||
3300 | 1044 | cmds = dict.fromkeys( | ||
3301 | 1045 | getattr(cmd, 'actionName', cmd.__name__.lower()) | ||
3302 | 1046 | for cmd | ||
3303 | 1047 | in self.__class__.actions).keys() | ||
3304 | 1048 | cmds.sort() | ||
3305 | 1049 | player.send((iterutils.interlace(" ", cmds), "\n")) | ||
3306 | 1050 | |||
3307 | 1051 | |||
3308 | 1052 | |||
3309 | 1053 | class Commands(Action): | ||
3310 | 1054 | """ | ||
3311 | 1055 | The I{commands} action provides a pointer to inexperienced players that | ||
3312 | 1056 | they should be thinking in terms of I{actions} instead. | ||
3313 | 1057 | |||
3314 | 1058 | This has no world side-effects; it just provides some user-interface | ||
3315 | 1059 | information to the player. | ||
3316 | 1060 | """ | ||
3317 | 1061 | expr = pyparsing.Literal("commands") | ||
3318 | 1062 | |||
3319 | 1063 | def do(self, player, line): | ||
3320 | 1064 | player.send("Try 'actions' instead.") | ||
3321 | 1065 | |||
3322 | 1066 | |||
3323 | 1067 | |||
3324 | 1068 | class Search(Action): | ||
3325 | 1069 | expr = (pyparsing.Literal("search") + | ||
3326 | 1070 | targetString("name")) | ||
3327 | 1071 | |||
3328 | 1072 | def do(self, player, line, name): | ||
3329 | 1073 | srch = player.thing.search(2, iimaginary.IVisible, name) | ||
3330 | 1074 | evt = events.Success( | ||
3331 | 1075 | actor=player.thing, | ||
3332 | 1076 | actorMessage=language.ExpressList( | ||
3333 | 1077 | list(iterutils.interlace('\n', | ||
3334 | 1078 | (o.visualize() | ||
3335 | 1079 | for o | ||
3336 | 1080 | in srch))))) | ||
3337 | 1081 | evt.broadcast() | ||
3338 | 1082 | |||
3339 | 1083 | |||
3340 | 1084 | |||
3341 | 1085 | class Score(Action): | ||
3342 | 1086 | expr = pyparsing.Literal("score") | ||
3343 | 1087 | |||
3344 | 1088 | scoreFormat = ( | ||
3345 | 1089 | '/----------------------------------------------------------------------------\\\n' | ||
3346 | 1090 | '| Level: %20d Experience: %10d\n' | ||
3347 | 1091 | '| Hitpoints: %16s\n' | ||
3348 | 1092 | '| Stamina: %18s\n' | ||
3349 | 1093 | '\\----------------------------------------------------------------------------/\n') | ||
3350 | 1094 | |||
3351 | 1095 | def do(self, player, line): | ||
3352 | 1096 | events.Success( | ||
3353 | 1097 | actor=player.thing, | ||
3354 | 1098 | actorMessage=self.scoreFormat % (player.level, player.experience, player.hitpoints, player.stamina)).broadcast() | ||
3355 | 1099 | |||
3356 | 1100 | |||
3357 | 1101 | |||
3358 | 1102 | class ExpressWho(language.BaseExpress): | ||
3359 | 1103 | header = (u"/============ Currently Playing ===========\\") | ||
3360 | 1104 | footer = (u"\\================ Total %(playerCount)03d ===============/") | ||
3361 | 1105 | |||
3362 | 1106 | def vt102(self, observer): | ||
3363 | 1107 | players = self.original.connected | ||
3364 | 1108 | |||
3365 | 1109 | return [[T.bold, self.header], u'\n', | ||
3366 | 1110 | [[language.Noun(p).shortName().vt102(observer), u'\n'] | ||
3367 | 1111 | for p in players], | ||
3368 | 1112 | [T.bold, self.footer % {'playerCount': len(players)}], u'\n'] | ||
3369 | 1113 | |||
3370 | 1114 | |||
3371 | 1115 | |||
3372 | 1116 | class Who(Action): | ||
3373 | 1117 | expr = pyparsing.Literal("who") | ||
3374 | 1118 | |||
3375 | 1119 | def do(self, player, line): | ||
3376 | 1120 | player.send(ExpressWho(player.store.findUnique(ImaginaryWorld))) | ||
3377 | 1121 | |||
3378 | 1122 | |||
3379 | 1123 | |||
3380 | 1124 | class Scrutinize(TargetAction): | ||
3381 | 1125 | """ | ||
3382 | 1126 | Show detailed information about the model structure of a game object. | ||
3383 | 1127 | """ | ||
3384 | 1128 | expr = (pyparsing.Literal("scrutinize") + | ||
3385 | 1129 | pyparsing.White() + | ||
3386 | 1130 | targetString("target")) | ||
3387 | 1131 | |||
3388 | 1132 | def targetRadius(self, player): | ||
3389 | 1133 | return 3 | ||
3390 | 1134 | |||
3391 | 1135 | def do(self, player, line, target): | ||
3392 | 1136 | v = dict((k, getattr(target, k)) | ||
3393 | 1137 | for (k, ign) | ||
3394 | 1138 | in target.getSchema() | ||
3395 | 1139 | if hasattr(target, k)) | ||
3396 | 1140 | |||
3397 | 1141 | targetContainer = iimaginary.IContainer(target, None) | ||
3398 | 1142 | if targetContainer is not None: | ||
3399 | 1143 | v['contents'] = list(targetContainer.getContents()) | ||
3400 | 1144 | exits = list(targetContainer.getExits()) | ||
3401 | 1145 | if exits: | ||
3402 | 1146 | v['exits'] = exits | ||
3403 | 1147 | s = pprint.pformat((target.__class__.__name__, v)) | ||
3404 | 1148 | # XXX FIXME Send a real Concept | ||
3405 | 1149 | player.send(s, '\n') | ||
3406 | 1150 | |||
3407 | 1151 | |||
3408 | 1152 | |||
3409 | 1153 | class ExpressInventory(language.BaseExpress): | ||
3410 | 1154 | implements(iimaginary.IConcept) | ||
3411 | 1155 | |||
3412 | 1156 | def __init__(self, original): | ||
3413 | 1157 | self.original = original | ||
3414 | 1158 | |||
3415 | 1159 | def vt102(self, observer): | ||
3416 | 1160 | return [[T.fg.yellow, "Inventory:\n"], | ||
3417 | 1161 | [T.fg.green, | ||
3418 | 1162 | [(language.Noun(o).shortName().vt102(observer), '\n') | ||
3419 | 1163 | for o | ||
3420 | 1164 | in iimaginary.IContainer(self.original).getContents()]]] | ||
3421 | 1165 | |||
3422 | 1166 | |||
3423 | 1167 | |||
3424 | 1168 | class Inventory(Action): | ||
3425 | 1169 | expr = pyparsing.Literal("inventory") | ||
3426 | 1170 | |||
3427 | 1171 | def do(self, player, line): | ||
3428 | 1172 | events.Success(actor=player.thing, | ||
3429 | 1173 | actorMessage=ExpressInventory(player.thing)).broadcast() | ||
3430 | 1174 | |||
3431 | 1175 | |||
3432 | 1176 | |||
3433 | 1177 | class Set(TargetAction): | ||
3434 | 1178 | """ | ||
3435 | 1179 | Direct model-level state manipulation command. | ||
3436 | 1180 | """ | ||
3437 | 1181 | expr = ( | ||
3438 | 1182 | pyparsing.Literal("set") + pyparsing.White() + | ||
3439 | 1183 | targetString("attribute") + pyparsing.White() + | ||
3440 | 1184 | pyparsing.Literal("of") + pyparsing.White() + | ||
3441 | 1185 | targetString("target") + pyparsing.White() + | ||
3442 | 1186 | pyparsing.Literal("to") + pyparsing.White() + | ||
3443 | 1187 | targetString("value")) | ||
3444 | 1188 | |||
3445 | 1189 | def do(self, player, line, attribute, target, value): | ||
3446 | 1190 | """ | ||
3447 | 1191 | Dispatch handling to an attribute-specific method. | ||
3448 | 1192 | |||
3449 | 1193 | @type attribute: C{unicode} | ||
3450 | 1194 | @param attribute: The model-level attribute of which to manipulate | ||
3451 | 1195 | the value. Handling of each attribute will be dispatched to a | ||
3452 | 1196 | C{set_}-prefixed method for that attribute based on this value. | ||
3453 | 1197 | |||
3454 | 1198 | @type target: L{Thing} | ||
3455 | 1199 | @param target: The model object to manipulate. | ||
3456 | 1200 | |||
3457 | 1201 | @type value: C{unicode} | ||
3458 | 1202 | @param value: The new value for the specified attribute. | ||
3459 | 1203 | """ | ||
3460 | 1204 | try: | ||
3461 | 1205 | method = getattr(self, "set_" + attribute.upper()) | ||
3462 | 1206 | except AttributeError: | ||
3463 | 1207 | raise eimaginary.ActionFailure( | ||
3464 | 1208 | events.ThatDoesntMakeSense( | ||
3465 | 1209 | actor=player.thing, | ||
3466 | 1210 | actorMessage="You cannot set that.")) | ||
3467 | 1211 | else: | ||
3468 | 1212 | method(player, line, target, value) | ||
3469 | 1213 | |||
3470 | 1214 | |||
3471 | 1215 | def set_GENDER(self, player, line, target, value): | ||
3472 | 1216 | """ | ||
3473 | 1217 | Attempt to change the gender of a thing. | ||
3474 | 1218 | |||
3475 | 1219 | @param target: The thing to change the gender of. | ||
3476 | 1220 | @param value: A string naming a gender on L{language.Gender}. | ||
3477 | 1221 | """ | ||
3478 | 1222 | try: | ||
3479 | 1223 | target.gender = getattr(language.Gender, value.upper()) | ||
3480 | 1224 | except AttributeError: | ||
3481 | 1225 | gender = {language.Gender.MALE: "male", | ||
3482 | 1226 | language.Gender.FEMALE: "female", | ||
3483 | 1227 | language.Gender.NEUTER: "neuter"}.get(target.gender) | ||
3484 | 1228 | raise eimaginary.ActionFailure(events.ThatDoesntMakeSense( | ||
3485 | 1229 | actor=player.thing, | ||
3486 | 1230 | actorMessage=("Only male, female, and neuter are valid " | ||
3487 | 1231 | "genders. You remain ", gender, "."))) | ||
3488 | 1232 | else: | ||
3489 | 1233 | if player.thing is target: | ||
3490 | 1234 | # XXX Why can't I do something with Noun to collapse these | ||
3491 | 1235 | # cases? | ||
3492 | 1236 | event = events.Success( | ||
3493 | 1237 | actor=player.thing, | ||
3494 | 1238 | actorMessage=(u"You set your gender to ", value, ".")) | ||
3495 | 1239 | else: | ||
3496 | 1240 | event = events.Success( | ||
3497 | 1241 | actor=player.thing, | ||
3498 | 1242 | target=target, | ||
3499 | 1243 | actorMessage=("You set ", language.Noun(target).hisHer(), | ||
3500 | 1244 | " gender to ", value, "."), | ||
3501 | 1245 | targetMessage=(player.thing, " set your gender to ", | ||
3502 | 1246 | value, ".")) | ||
3503 | 1247 | event.broadcast() | ||
3504 | 1248 | |||
3505 | 1249 | |||
3506 | 1250 | def set_PROPER(self, player, line, target, value): | ||
3507 | 1251 | """ | ||
3508 | 1252 | Attempt to change the name of a thing from a proper noun to a common | ||
3509 | 1253 | noun or the other way around. | ||
3510 | 1254 | |||
3511 | 1255 | @param target: The thing to change. | ||
3512 | 1256 | @param value: The string C{"true"} or C{"false"}. | ||
3513 | 1257 | """ | ||
3514 | 1258 | if value == "true": | ||
3515 | 1259 | target.proper = True | ||
3516 | 1260 | phrase = '" a proper noun.' | ||
3517 | 1261 | elif value == "false": | ||
3518 | 1262 | target.proper = False | ||
3519 | 1263 | phrase = '" a common noun.' | ||
3520 | 1264 | else: | ||
3521 | 1265 | raise eimaginary.ActionFailure( | ||
3522 | 1266 | events.ThatDoesntMakeSense( | ||
3523 | 1267 | actor=player.thing, | ||
3524 | 1268 | actorMessage=("Only true and false are valid settings " | ||
3525 | 1269 | "for proper."))) | ||
3526 | 1270 | events.Success( | ||
3527 | 1271 | actor=player.thing, | ||
3528 | 1272 | actorMessage=('You make the name of "', | ||
3529 | 1273 | language.Noun(target).shortName(), | ||
3530 | 1274 | phrase)).broadcast() | ||
3531 | 1275 | |||
3532 | 1276 | |||
3533 | 1277 | |||
3534 | 1278 | class Help(Action): | ||
3535 | 1279 | """ | ||
3536 | 1280 | A command for looking up help files. | ||
3537 | 1281 | |||
3538 | 1282 | @cvar helpContentPath: The path in which to search for files. | ||
3539 | 1283 | @type helpContentPath: L{filepath.FilePath} | ||
3540 | 1284 | """ | ||
3541 | 1285 | expr = (pyparsing.Literal("help") + | ||
3542 | 1286 | pyparsing.White() + | ||
3543 | 1287 | pyparsing.restOfLine.setResultsName("topic")) | ||
3544 | 1288 | |||
3545 | 1289 | helpContentPath = filepath.FilePath(imaginary.__file__).sibling( | ||
3546 | 1290 | "resources").child("help") | ||
3547 | 1291 | |||
3548 | 1292 | def do(self, player, line, topic): | ||
3549 | 1293 | topic = topic.lower().strip() | ||
3550 | 1294 | try: | ||
3551 | 1295 | helpFile = self.helpContentPath.child(topic).open() | ||
3552 | 1296 | except (OSError, IOError, filepath.InsecurePath): | ||
3553 | 1297 | player.send("No help available on ", topic, ".", "\n") | ||
3554 | 1298 | else: | ||
3555 | 1299 | player.send(helpFile.read(), '\n') | ||
3556 | 1300 | 0 | ||
3557 | === removed file 'Imaginary/imaginary/copyright.py' | |||
3558 | --- Imaginary/imaginary/copyright.py 2006-04-12 02:41:46 +0000 | |||
3559 | +++ Imaginary/imaginary/copyright.py 1970-01-01 00:00:00 +0000 | |||
3560 | @@ -1,22 +0,0 @@ | |||
3561 | 1 | |||
3562 | 2 | # <major> <minor> <patch> <alpha | pre | final | zzz> <iteration> | ||
3563 | 3 | version_info = (0, 1, 0, 'alpha', 0) | ||
3564 | 4 | |||
3565 | 5 | # Sortable version information. This will always only | ||
3566 | 6 | # increase from an older version to a newer version. | ||
3567 | 7 | hexversion = (version_info[0] << 24 | | ||
3568 | 8 | version_info[1] << 16 | | ||
3569 | 9 | version_info[2] << 8 | | ||
3570 | 10 | ['alpha', 'pre', 'final', 'zzz'].index(version_info[3]) << 4 | | ||
3571 | 11 | version_info[4]) | ||
3572 | 12 | |||
3573 | 13 | # Human-readable format | ||
3574 | 14 | if version_info[3] == 'final': | ||
3575 | 15 | version = '%d.%d.%d%s' % version_info[:-1] | ||
3576 | 16 | elif version_info[3] != 'zzz': | ||
3577 | 17 | version = '%d.%d.%d%s%d' % version_info | ||
3578 | 18 | else: | ||
3579 | 19 | version = "SVN-trunk" | ||
3580 | 20 | |||
3581 | 21 | # Longer human-readable format | ||
3582 | 22 | longversion= "Imaginary " + version | ||
3583 | 23 | 0 | ||
3584 | === removed file 'Imaginary/imaginary/creation.py' | |||
3585 | --- Imaginary/imaginary/creation.py 2009-08-17 02:40:03 +0000 | |||
3586 | +++ Imaginary/imaginary/creation.py 1970-01-01 00:00:00 +0000 | |||
3587 | @@ -1,179 +0,0 @@ | |||
3588 | 1 | # -*- test-case-name: imaginary.test.test_create -*- | ||
3589 | 2 | """ | ||
3590 | 3 | This module contains code associated with creating objects in game. | ||
3591 | 4 | """ | ||
3592 | 5 | |||
3593 | 6 | from zope.interface import implements | ||
3594 | 7 | |||
3595 | 8 | from twisted import plugin | ||
3596 | 9 | |||
3597 | 10 | import imaginary.plugins | ||
3598 | 11 | |||
3599 | 12 | from imaginary import objects | ||
3600 | 13 | from imaginary import events | ||
3601 | 14 | from imaginary import language | ||
3602 | 15 | |||
3603 | 16 | from imaginary.iimaginary import IThingType | ||
3604 | 17 | from imaginary.eimaginary import ActionFailure, DoesntFit | ||
3605 | 18 | |||
3606 | 19 | from imaginary.action import Action, insufficientSpace | ||
3607 | 20 | from imaginary.action import targetString | ||
3608 | 21 | |||
3609 | 22 | from imaginary.pyparsing import Literal, White, Optional, restOfLine | ||
3610 | 23 | |||
3611 | 24 | |||
3612 | 25 | def getPlugins(iface, package): | ||
3613 | 26 | """ | ||
3614 | 27 | Get plugins. See L{twisted.plugin.getPlugins}. | ||
3615 | 28 | |||
3616 | 29 | This is in place only so the tests specifically for creation can replace | ||
3617 | 30 | it. Please use L{twisted.plugin.getPlugins} instead. | ||
3618 | 31 | """ | ||
3619 | 32 | # XXX the tests should not need to do that, make it per-instance or | ||
3620 | 33 | # something... | ||
3621 | 34 | return plugin.getPlugins(iface, package) | ||
3622 | 35 | |||
3623 | 36 | |||
3624 | 37 | def createCreator(*enhancements): | ||
3625 | 38 | """ | ||
3626 | 39 | Create and return a function which can create objects in the game world. | ||
3627 | 40 | |||
3628 | 41 | This is a utility function to make it easy to define factories for certain | ||
3629 | 42 | configurations of power-ups to be used with Imaginary. It doesn't do | ||
3630 | 43 | anything magical; you can replicate its effects simply by writing a | ||
3631 | 44 | function that calls L{Enhancement.createFor} on the set of L{Enhancement}s. | ||
3632 | 45 | L{createCreator} exists because you will frequently need to do that, and it | ||
3633 | 46 | can be tedious. | ||
3634 | 47 | |||
3635 | 48 | @param enhancements: The arguments to this function are a list of 2-tuples | ||
3636 | 49 | of (L{Enhancement}-subclass, keyword arguments to that class's | ||
3637 | 50 | constructor). | ||
3638 | 51 | |||
3639 | 52 | @return: a function which takes keyword arguments that will be passed on to | ||
3640 | 53 | L{objects.Thing}'s constructor, and will return a L{Thing} with an | ||
3641 | 54 | instance of each class in C{enhancements} installed, via C{createFor}, | ||
3642 | 55 | on it. | ||
3643 | 56 | |||
3644 | 57 | @rtype: L{Thing} | ||
3645 | 58 | """ | ||
3646 | 59 | def create(**kw): | ||
3647 | 60 | o = objects.Thing(**kw) | ||
3648 | 61 | for enhancementClass, enhancementKeywords in enhancements: | ||
3649 | 62 | enhancementClass.createFor(o, **(enhancementKeywords or {})) | ||
3650 | 63 | return o | ||
3651 | 64 | return create | ||
3652 | 65 | |||
3653 | 66 | |||
3654 | 67 | class CreationPluginHelper(object): | ||
3655 | 68 | """ | ||
3656 | 69 | A helper for creating plugins for the 'Create' command. | ||
3657 | 70 | |||
3658 | 71 | Create will search for L{IThingType} plugins and allow users to | ||
3659 | 72 | instantiate a new L{objects.Thing} using the one with the name which | ||
3660 | 73 | matches what was supplied to the action. | ||
3661 | 74 | """ | ||
3662 | 75 | |||
3663 | 76 | implements(plugin.IPlugin, IThingType) | ||
3664 | 77 | |||
3665 | 78 | def __init__(self, typeName, typeObject): | ||
3666 | 79 | """ | ||
3667 | 80 | @type typeName: C{unicode} | ||
3668 | 81 | @param typeName: A short string describing the kind of object this | ||
3669 | 82 | plugin will create. | ||
3670 | 83 | |||
3671 | 84 | @param typeObject: A factory for creating instances of | ||
3672 | 85 | L{objects.Thing}. This will be invoked with four keyword arguments: | ||
3673 | 86 | store, name, description, and proper. See attributes of | ||
3674 | 87 | L{objects.Thing} for documentation of these arguments. | ||
3675 | 88 | """ | ||
3676 | 89 | self.type = typeName | ||
3677 | 90 | self.typeObject = typeObject | ||
3678 | 91 | |||
3679 | 92 | |||
3680 | 93 | def getType(self): | ||
3681 | 94 | return self.typeObject | ||
3682 | 95 | |||
3683 | 96 | |||
3684 | 97 | |||
3685 | 98 | def creationSuccess(player, creation): | ||
3686 | 99 | """ | ||
3687 | 100 | Create and return an event describing that an object was successfully | ||
3688 | 101 | created. | ||
3689 | 102 | """ | ||
3690 | 103 | phrase = language.Noun(creation).nounPhrase() | ||
3691 | 104 | return events.Success( | ||
3692 | 105 | actor=player, | ||
3693 | 106 | target=creation, | ||
3694 | 107 | actorMessage=language.Sentence(["You create ", phrase, "."]), | ||
3695 | 108 | targetMessage=language.Sentence([player, " creates you."]), | ||
3696 | 109 | otherMessage=language.Sentence([player, " creates ", phrase, "."])) | ||
3697 | 110 | |||
3698 | 111 | |||
3699 | 112 | class Create(Action): | ||
3700 | 113 | """ | ||
3701 | 114 | An action which can create items by looking at the L{IThingType} plugin | ||
3702 | 115 | registry. | ||
3703 | 116 | """ | ||
3704 | 117 | expr = (Literal("create") + | ||
3705 | 118 | Optional(White() + | ||
3706 | 119 | (Literal("an") | Literal("a") | Literal("the")).setResultsName("article")) + | ||
3707 | 120 | White() + | ||
3708 | 121 | targetString("typeName") + | ||
3709 | 122 | White() + | ||
3710 | 123 | Literal("named") + | ||
3711 | 124 | White() + | ||
3712 | 125 | targetString("name") + | ||
3713 | 126 | Optional(White() + | ||
3714 | 127 | restOfLine.setResultsName("description"))) | ||
3715 | 128 | |||
3716 | 129 | def do(self, player, line, typeName, name, description=None, article=None): | ||
3717 | 130 | """ | ||
3718 | 131 | Create an item, and notify everyone present that it now exists. | ||
3719 | 132 | """ | ||
3720 | 133 | if not description: | ||
3721 | 134 | description = u'an undescribed object' | ||
3722 | 135 | for plug in getPlugins(IThingType, imaginary.plugins): | ||
3723 | 136 | if plug.type == typeName: | ||
3724 | 137 | proper = (article == "the") | ||
3725 | 138 | o = plug.getType()(store=player.store, name=name, | ||
3726 | 139 | description=description, proper=proper) | ||
3727 | 140 | break | ||
3728 | 141 | else: | ||
3729 | 142 | raise ActionFailure( | ||
3730 | 143 | events.ThatDoesntMakeSense( | ||
3731 | 144 | actor=player.thing, | ||
3732 | 145 | actorMessage=language.ExpressString( | ||
3733 | 146 | u"Can't find " + typeName + u"."))) | ||
3734 | 147 | |||
3735 | 148 | creationSuccess(player.thing, o).broadcast() | ||
3736 | 149 | try: | ||
3737 | 150 | o.moveTo(player.thing) | ||
3738 | 151 | except DoesntFit: | ||
3739 | 152 | raise insufficientSpace(player.thing) | ||
3740 | 153 | |||
3741 | 154 | |||
3742 | 155 | |||
3743 | 156 | |||
3744 | 157 | def listThingTypes(): | ||
3745 | 158 | """ | ||
3746 | 159 | Return a list of C{unicode} strings each of which gives the name of a type | ||
3747 | 160 | which can be created with the create command. | ||
3748 | 161 | """ | ||
3749 | 162 | return sorted([type.type for type in getPlugins(IThingType, imaginary.plugins)]) | ||
3750 | 163 | |||
3751 | 164 | |||
3752 | 165 | |||
3753 | 166 | class ListThingTypes(Action): | ||
3754 | 167 | """ | ||
3755 | 168 | An action which tells the invoker what thing types exist to be created with | ||
3756 | 169 | the L{Create} command. | ||
3757 | 170 | """ | ||
3758 | 171 | expr = Literal("list thing types") | ||
3759 | 172 | |||
3760 | 173 | def do(self, player, line): | ||
3761 | 174 | """ | ||
3762 | 175 | Tell the player the thing types which exist. | ||
3763 | 176 | """ | ||
3764 | 177 | events.Success( | ||
3765 | 178 | actor=player.thing, | ||
3766 | 179 | actorMessage=[(t, "\n") for t in listThingTypes()]).broadcast() | ||
3767 | 180 | 0 | ||
3768 | === removed file 'Imaginary/imaginary/eimaginary.py' | |||
3769 | --- Imaginary/imaginary/eimaginary.py 2009-06-29 04:03:17 +0000 | |||
3770 | +++ Imaginary/imaginary/eimaginary.py 1970-01-01 00:00:00 +0000 | |||
3771 | @@ -1,90 +0,0 @@ | |||
3772 | 1 | |||
3773 | 2 | from twisted.cred import error | ||
3774 | 3 | |||
3775 | 4 | # Authentication errors | ||
3776 | 5 | class BadPassword(error.UnauthorizedLogin): | ||
3777 | 6 | pass | ||
3778 | 7 | |||
3779 | 8 | class NoSuchUser(error.UnauthorizedLogin): | ||
3780 | 9 | pass | ||
3781 | 10 | |||
3782 | 11 | |||
3783 | 12 | # Base Imaginary error | ||
3784 | 13 | class ImaginaryError(Exception): | ||
3785 | 14 | pass | ||
3786 | 15 | |||
3787 | 16 | |||
3788 | 17 | # Input handling errors | ||
3789 | 18 | class NoSuchCommand(ImaginaryError): | ||
3790 | 19 | """ | ||
3791 | 20 | There is no command like the one you tried to execute. | ||
3792 | 21 | """ | ||
3793 | 22 | |||
3794 | 23 | class AmbiguousArgument(ImaginaryError): | ||
3795 | 24 | """ | ||
3796 | 25 | One or more of the inputs specified can not be narrowed down to | ||
3797 | 26 | just one thing. This can be due to the presence of multiple | ||
3798 | 27 | things with similar names, or due to the absence of anything named | ||
3799 | 28 | similarly to the given input. | ||
3800 | 29 | |||
3801 | 30 | @ivar action: The action which was being processed when an ambiguity was | ||
3802 | 31 | found. | ||
3803 | 32 | |||
3804 | 33 | @type part: C{str} | ||
3805 | 34 | @ivar part: The part of the command which was ambiguous. | ||
3806 | 35 | Typically something like 'target' or 'tool'. | ||
3807 | 36 | |||
3808 | 37 | @type partValue: C{str} | ||
3809 | 38 | @ivar partValue: The string which was supplied by the user for the indicated part. | ||
3810 | 39 | |||
3811 | 40 | @type objects: C{list} of C{IThing} | ||
3812 | 41 | @ivar objects: The objects which were involved in the ambiguity. | ||
3813 | 42 | """ | ||
3814 | 43 | |||
3815 | 44 | def __init__(self, action, part, partValue, objects): | ||
3816 | 45 | ImaginaryError.__init__(self, action, part, partValue, objects) | ||
3817 | 46 | self.action = action | ||
3818 | 47 | self.part = part | ||
3819 | 48 | self.partValue = partValue | ||
3820 | 49 | self.objects = objects | ||
3821 | 50 | |||
3822 | 51 | |||
3823 | 52 | |||
3824 | 53 | class ActionFailure(ImaginaryError): | ||
3825 | 54 | """ | ||
3826 | 55 | Wrapper exception for an Event that caused an action to fail (such that the | ||
3827 | 56 | transaction in which it was running should be reverted). | ||
3828 | 57 | """ | ||
3829 | 58 | def __init__(self, event): | ||
3830 | 59 | ImaginaryError.__init__(self) | ||
3831 | 60 | self.event = event | ||
3832 | 61 | |||
3833 | 62 | |||
3834 | 63 | def __repr__(self): | ||
3835 | 64 | return '<Action Failure: %r>' % (self.event,) | ||
3836 | 65 | |||
3837 | 66 | |||
3838 | 67 | |||
3839 | 68 | class ThingNotFound(ImaginaryError): | ||
3840 | 69 | """ | ||
3841 | 70 | Resolving a Thing by identity failed. | ||
3842 | 71 | """ | ||
3843 | 72 | |||
3844 | 73 | |||
3845 | 74 | # Game logic errors | ||
3846 | 75 | class DoesntFit(ImaginaryError): | ||
3847 | 76 | """ | ||
3848 | 77 | An object tried to go into a container, but the container was full. | ||
3849 | 78 | """ | ||
3850 | 79 | |||
3851 | 80 | |||
3852 | 81 | class Closed(ImaginaryError): | ||
3853 | 82 | """ | ||
3854 | 83 | An object tried to go into a container, but the container was closed. | ||
3855 | 84 | """ | ||
3856 | 85 | |||
3857 | 86 | |||
3858 | 87 | class CannotMove(ImaginaryError): | ||
3859 | 88 | """ | ||
3860 | 89 | An object tried to move but it was not portable so it couldn't. | ||
3861 | 90 | """ | ||
3862 | 91 | 0 | ||
3863 | === removed file 'Imaginary/imaginary/enhancement.py' | |||
3864 | --- Imaginary/imaginary/enhancement.py 2009-06-29 04:03:17 +0000 | |||
3865 | +++ Imaginary/imaginary/enhancement.py 1970-01-01 00:00:00 +0000 | |||
3866 | @@ -1,95 +0,0 @@ | |||
3867 | 1 | # -*- test-case-name: imaginary.test.test_enhancement -*- | ||
3868 | 2 | |||
3869 | 3 | """ | ||
3870 | 4 | This module contains objects for application code to use to implement behaviors | ||
3871 | 5 | that attach to objects in a simulation. | ||
3872 | 6 | """ | ||
3873 | 7 | |||
3874 | 8 | class Enhancement(object): | ||
3875 | 9 | """ | ||
3876 | 10 | An L{Enhancement} is an object attached to a L{imaginary.objects.Thing} | ||
3877 | 11 | that provides some additional functionality. | ||
3878 | 12 | |||
3879 | 13 | This class is a mixin; it expects to be mixed in to an L{Item} subclass, | ||
3880 | 14 | since it passes itself as an argument to L{Item.powerUp}. | ||
3881 | 15 | |||
3882 | 16 | Note that an L{Enhancement} embodies the behavior, but not the physical | ||
3883 | 17 | attributes, of the object in question. | ||
3884 | 18 | |||
3885 | 19 | For example, let's say you wanted to implement a cell phone in Imaginary. | ||
3886 | 20 | You would make an L{Enhancement} called C{CellPhone} which had various | ||
3887 | 21 | attributes, for example C{phoneNumber}. Then you would do C{phoneBody = | ||
3888 | 22 | Thing(...)} to create a physical 'phone' object in a world. Next, you | ||
3889 | 23 | would do C{cellPhone = CellPhone.createFor(phoneBody, ...)}, which would | ||
3890 | 24 | create a C{CellPhone} object that endowed your physical 'phone' with the | ||
3891 | 25 | properties of being an actual phone, like having a phone number, ringing | ||
3892 | 26 | when dialed, etc. | ||
3893 | 27 | |||
3894 | 28 | Note that it is not enough to simply create your C{CellPhone}, as it will | ||
3895 | 29 | not have a physical body, and therefore not exist in the world. | ||
3896 | 30 | |||
3897 | 31 | @ivar thing: a L{imaginary.objects.Thing} powered up with this | ||
3898 | 32 | L{Enhancement}. All subclasses which mix in L{Item} should declare | ||
3899 | 33 | this as an L{attributes.reference} attribute. Unless your | ||
3900 | 34 | L{Enhancement} subclass is specifically designed to exist | ||
3901 | 35 | independently of its L{Thing}, or to accept other types for this | ||
3902 | 36 | attribute, it should also be declared as C{(allowNone=False, | ||
3903 | 37 | reftype=Thing, whenDeleted=CASCADE)}. | ||
3904 | 38 | """ | ||
3905 | 39 | |||
3906 | 40 | def installed(self): | ||
3907 | 41 | """ | ||
3908 | 42 | Override the C{installed()} hook that C{axiom.dependency} provides. | ||
3909 | 43 | When L{Enhancement} was called C{ThingMixin}, the suggested mechanism | ||
3910 | 44 | to install simulation components was to use the dependency system, | ||
3911 | 45 | which was wrong, c.f. U{http://divmod.org/trac/ticket/2558}. | ||
3912 | 46 | |||
3913 | 47 | @raise RuntimeError: to indicate that you shouldn't use this | ||
3914 | 48 | functionality. | ||
3915 | 49 | """ | ||
3916 | 50 | raise RuntimeError("Use Enhancement.createFor, not installOn(), " | ||
3917 | 51 | "to apply an Enhancement to a Thing.") | ||
3918 | 52 | |||
3919 | 53 | |||
3920 | 54 | def applyEnhancement(self): | ||
3921 | 55 | """ | ||
3922 | 56 | Apply this L{Enhancement} to its C{thing} attribute, by powering it up. | ||
3923 | 57 | """ | ||
3924 | 58 | self.thing.powerUp(self) | ||
3925 | 59 | |||
3926 | 60 | |||
3927 | 61 | def removeEnhancement(self): | ||
3928 | 62 | """ | ||
3929 | 63 | Remove this L{Enhancement} from its C{thing} attribute, by powering it | ||
3930 | 64 | down. | ||
3931 | 65 | """ | ||
3932 | 66 | self.thing.powerDown(self) | ||
3933 | 67 | |||
3934 | 68 | |||
3935 | 69 | @classmethod | ||
3936 | 70 | def createFor(cls, thing, **kw): | ||
3937 | 71 | """ | ||
3938 | 72 | Create an L{Enhancement} of this type for the given | ||
3939 | 73 | L{imaginary.objects.Thing}, in the given L{imaginary.objects.Thing}'s | ||
3940 | 74 | store. | ||
3941 | 75 | """ | ||
3942 | 76 | self = cls(store=thing.store, thing=thing, **kw) | ||
3943 | 77 | self.applyEnhancement() | ||
3944 | 78 | return self | ||
3945 | 79 | |||
3946 | 80 | |||
3947 | 81 | @classmethod | ||
3948 | 82 | def destroyFor(cls, thing): | ||
3949 | 83 | """ | ||
3950 | 84 | Destroy the L{Enhancement}s of the given subclass associated with the | ||
3951 | 85 | given L{Thing}, if one exists. | ||
3952 | 86 | |||
3953 | 87 | @param thing: A L{Thing} which may be the value of the C{thing} | ||
3954 | 88 | attribute of an instance of the given L{Enhancement} subclass. | ||
3955 | 89 | |||
3956 | 90 | @type thing: L{Thing} | ||
3957 | 91 | """ | ||
3958 | 92 | it = thing.store.findUnique(cls, cls.thing == thing, default=None) | ||
3959 | 93 | if it is not None: | ||
3960 | 94 | it.removeEnhancement() | ||
3961 | 95 | it.deleteFromStore() | ||
3962 | 96 | 0 | ||
3963 | === removed file 'Imaginary/imaginary/events.py' | |||
3964 | --- Imaginary/imaginary/events.py 2009-08-18 00:20:31 +0000 | |||
3965 | +++ Imaginary/imaginary/events.py 1970-01-01 00:00:00 +0000 | |||
3966 | @@ -1,284 +0,0 @@ | |||
3967 | 1 | # -*- test-case-name: imaginary.test.test_actions.TargetActionTests.test_resolveTargetCaseInsensitively -*- | ||
3968 | 2 | |||
3969 | 3 | from zope.interface import implements | ||
3970 | 4 | |||
3971 | 5 | from twisted.python import context | ||
3972 | 6 | |||
3973 | 7 | from imaginary import iimaginary, language, eimaginary | ||
3974 | 8 | from imaginary.idea import Proximity, ProviderOf | ||
3975 | 9 | |||
3976 | 10 | |||
3977 | 11 | class Event(language.BaseExpress): | ||
3978 | 12 | implements(iimaginary.IConcept) | ||
3979 | 13 | |||
3980 | 14 | actorMessage = targetMessage = toolMessage = otherMessage = None | ||
3981 | 15 | |||
3982 | 16 | def __init__(self, | ||
3983 | 17 | location=None, actor=None, target=None, tool=None, | ||
3984 | 18 | actorMessage=None, targetMessage=None, toolMessage=None, | ||
3985 | 19 | otherMessage=None): | ||
3986 | 20 | |||
3987 | 21 | if location is None and actor is not None: | ||
3988 | 22 | location = actor.location | ||
3989 | 23 | |||
3990 | 24 | self.location = location | ||
3991 | 25 | self.actor = actor | ||
3992 | 26 | self.target = target | ||
3993 | 27 | self.tool = tool | ||
3994 | 28 | if actorMessage is not None: | ||
3995 | 29 | self.actorMessage = iimaginary.IConcept(actorMessage) | ||
3996 | 30 | if targetMessage is not None: | ||
3997 | 31 | self.targetMessage = iimaginary.IConcept(targetMessage) | ||
3998 | 32 | if toolMessage is not None: | ||
3999 | 33 | self.toolMessage = iimaginary.IConcept(toolMessage) | ||
4000 | 34 | if otherMessage is not None: | ||
4001 | 35 | self.otherMessage = iimaginary.IConcept(otherMessage) | ||
4002 | 36 | |||
4003 | 37 | |||
4004 | 38 | def conceptFor(self, observer): | ||
4005 | 39 | """ | ||
4006 | 40 | Retrieve the appropriate L{IConcept} provider for a given observer. If | ||
4007 | 41 | the observer is this L{Event}'s C{actor}, it will return the | ||
4008 | 42 | C{actorMessage} for this event, and so on for the tool and the target. | ||
4009 | 43 | If it doesn't match a L{Thing} known to this event, it will return | ||
4010 | 44 | C{otherMessage}. | ||
4011 | 45 | """ | ||
4012 | 46 | if observer is self.actor: | ||
4013 | 47 | msg = self.actorMessage | ||
4014 | 48 | elif observer is self.target: | ||
4015 | 49 | msg = self.targetMessage | ||
4016 | 50 | elif observer is self.tool: | ||
4017 | 51 | msg = self.toolMessage | ||
4018 | 52 | else: | ||
4019 | 53 | msg = self.otherMessage | ||
4020 | 54 | return msg | ||
4021 | 55 | |||
4022 | 56 | |||
4023 | 57 | def reify(self): | ||
4024 | 58 | """ | ||
4025 | 59 | Determine which objects should receive this event and return a callable | ||
4026 | 60 | object which will deliver it to them. | ||
4027 | 61 | |||
4028 | 62 | Note that this occurs during event propagation, and you probably don't | ||
4029 | 63 | need to call it directly. | ||
4030 | 64 | |||
4031 | 65 | @see: L{iimaginary.IEventObserver.prepare} and | ||
4032 | 66 | L{TransactionalEventBroadcaster} for a more thorough description of | ||
4033 | 67 | how this method is used to interact with transactions. | ||
4034 | 68 | |||
4035 | 69 | @return: a 0-arg callable object which, when called, will call the | ||
4036 | 70 | results of all L{IEventObserver}s which were contained within this | ||
4037 | 71 | L{Event}'s location when this method, L{Event.reify}, was called. | ||
4038 | 72 | """ | ||
4039 | 73 | L = [] | ||
4040 | 74 | for observer in (self.location.idea.obtain( | ||
4041 | 75 | Proximity(0.5, ProviderOf(iimaginary.IEventObserver)))): | ||
4042 | 76 | sender = observer.prepare(self) | ||
4043 | 77 | if not callable(sender): | ||
4044 | 78 | raise TypeError("Senders must be callable", sender) | ||
4045 | 79 | L.append(sender) | ||
4046 | 80 | return lambda: map(apply, L) | ||
4047 | 81 | |||
4048 | 82 | |||
4049 | 83 | def vt102(self, observer): | ||
4050 | 84 | c = self.conceptFor(observer) | ||
4051 | 85 | if c is not None: | ||
4052 | 86 | return [c.vt102(observer), '\n'] | ||
4053 | 87 | return u'' | ||
4054 | 88 | |||
4055 | 89 | |||
4056 | 90 | |||
4057 | 91 | class TransactionalEventBroadcaster(object): | ||
4058 | 92 | """ | ||
4059 | 93 | Collect a bunch of output events as a transaction is being executed, then | ||
4060 | 94 | distribute them when it has completed. | ||
4061 | 95 | |||
4062 | 96 | Events can be added normally or as revert events. Normal events are | ||
4063 | 97 | broadcast after the transaction is successfully committed. Revert events | ||
4064 | 98 | are broadcast if the transaction failed somehow and was been reverted. | ||
4065 | 99 | """ | ||
4066 | 100 | implements(iimaginary.ITransactionalEventBroadcaster) | ||
4067 | 101 | |||
4068 | 102 | def __init__(self): | ||
4069 | 103 | self._events = [] | ||
4070 | 104 | self._revertEvents = [] | ||
4071 | 105 | |||
4072 | 106 | |||
4073 | 107 | def addEvent(self, event): | ||
4074 | 108 | """ | ||
4075 | 109 | Add a normal event. | ||
4076 | 110 | |||
4077 | 111 | @param event: A no-argument callable to be invoked when this | ||
4078 | 112 | transaction has been committed. | ||
4079 | 113 | """ | ||
4080 | 114 | if not callable(event): | ||
4081 | 115 | raise ValueError("Events must be callable", event) | ||
4082 | 116 | self._events.append(event) | ||
4083 | 117 | |||
4084 | 118 | |||
4085 | 119 | def addRevertEvent(self, event): | ||
4086 | 120 | """ | ||
4087 | 121 | Add a revert event. | ||
4088 | 122 | |||
4089 | 123 | @param event: A no-argument callable to be invoked when this | ||
4090 | 124 | transaction has been reverted. | ||
4091 | 125 | """ | ||
4092 | 126 | if not callable(event): | ||
4093 | 127 | raise ValueError("Events must be callable", event) | ||
4094 | 128 | self._revertEvents.append(event) | ||
4095 | 129 | |||
4096 | 130 | |||
4097 | 131 | def broadcastEvents(self): | ||
4098 | 132 | """ | ||
4099 | 133 | Send all normal events. | ||
4100 | 134 | """ | ||
4101 | 135 | events = self._events | ||
4102 | 136 | self._events = self._revertEvents = None | ||
4103 | 137 | map(apply, events) | ||
4104 | 138 | |||
4105 | 139 | |||
4106 | 140 | def broadcastRevertEvents(self): | ||
4107 | 141 | """ | ||
4108 | 142 | Send all revert events. | ||
4109 | 143 | """ | ||
4110 | 144 | events = self._revertEvents | ||
4111 | 145 | self._events = self._revertEvents = None | ||
4112 | 146 | map(apply, events) | ||
4113 | 147 | |||
4114 | 148 | |||
4115 | 149 | |||
4116 | 150 | def runEventTransaction(store, func, *args, **kwargs): | ||
4117 | 151 | """ | ||
4118 | 152 | This takes responsibility for setting up the transactional event | ||
4119 | 153 | broadcasting junk, handling action errors, and broadcasting commit or | ||
4120 | 154 | revert events. | ||
4121 | 155 | """ | ||
4122 | 156 | broadcaster = TransactionalEventBroadcaster() | ||
4123 | 157 | def runHelper(): | ||
4124 | 158 | # Set up event context for the duration of the action | ||
4125 | 159 | # run. Additionally, handle raised ActionFailures by | ||
4126 | 160 | # adding their events to the revert event list and | ||
4127 | 161 | # re-raising them so they will revert the transaction. | ||
4128 | 162 | try: | ||
4129 | 163 | return context.call( | ||
4130 | 164 | {iimaginary.ITransactionalEventBroadcaster: broadcaster}, | ||
4131 | 165 | func, *args, **kwargs) | ||
4132 | 166 | except eimaginary.ActionFailure, e: | ||
4133 | 167 | broadcaster.addRevertEvent(e.event.reify()) | ||
4134 | 168 | raise | ||
4135 | 169 | try: | ||
4136 | 170 | result = store.transact(runHelper) | ||
4137 | 171 | except eimaginary.ActionFailure: | ||
4138 | 172 | broadcaster.broadcastRevertEvents() | ||
4139 | 173 | return None | ||
4140 | 174 | else: | ||
4141 | 175 | broadcaster.broadcastEvents() | ||
4142 | 176 | return result | ||
4143 | 177 | |||
4144 | 178 | |||
4145 | 179 | |||
4146 | 180 | class ThatDoesntMakeSense(Event): | ||
4147 | 181 | """ | ||
4148 | 182 | An action was attempted which is logically impossible. | ||
4149 | 183 | """ | ||
4150 | 184 | def __init__(self, actorMessage="That doesn't make sense.", **kw): | ||
4151 | 185 | super(ThatDoesntMakeSense, self).__init__(actorMessage=actorMessage, **kw) | ||
4152 | 186 | |||
4153 | 187 | |||
4154 | 188 | class ThatDoesntWork(Event): | ||
4155 | 189 | """ | ||
4156 | 190 | An action was attempted which is phyically impossible. | ||
4157 | 191 | """ | ||
4158 | 192 | def __init__(self, actorMessage="That doesn't work.", **kw): | ||
4159 | 193 | super(ThatDoesntWork, self).__init__(actorMessage=actorMessage, **kw) | ||
4160 | 194 | |||
4161 | 195 | |||
4162 | 196 | class Success(Event): | ||
4163 | 197 | """ | ||
4164 | 198 | You do it. Swell. | ||
4165 | 199 | """ | ||
4166 | 200 | |||
4167 | 201 | def broadcast(self): | ||
4168 | 202 | """ | ||
4169 | 203 | Don't really broadcast. Add this event to the events which will be | ||
4170 | 204 | sent when the action (or whatever) execution transaction is committed | ||
4171 | 205 | successfully. | ||
4172 | 206 | """ | ||
4173 | 207 | broadcaster = context.get(iimaginary.ITransactionalEventBroadcaster) | ||
4174 | 208 | if broadcaster is not None: | ||
4175 | 209 | broadcaster.addEvent(self.reify()) | ||
4176 | 210 | else: | ||
4177 | 211 | self.reify()() | ||
4178 | 212 | |||
4179 | 213 | |||
4180 | 214 | |||
4181 | 215 | class ArrivalEvent(Success): | ||
4182 | 216 | """ | ||
4183 | 217 | An event representing the arrival of an object. | ||
4184 | 218 | """ | ||
4185 | 219 | |||
4186 | 220 | |||
4187 | 221 | |||
4188 | 222 | class MovementArrivalEvent(ArrivalEvent): | ||
4189 | 223 | """ | ||
4190 | 224 | An event representing the arrival of an object at a location from an | ||
4191 | 225 | origin. | ||
4192 | 226 | """ | ||
4193 | 227 | def __init__(self, thing, origin=None, direction=None): | ||
4194 | 228 | self.thing = thing | ||
4195 | 229 | self.origin = origin | ||
4196 | 230 | self.direction = direction | ||
4197 | 231 | self.location = self.thing.location | ||
4198 | 232 | |||
4199 | 233 | |||
4200 | 234 | def conceptFor(self, observer): | ||
4201 | 235 | if observer is self.thing: | ||
4202 | 236 | return None | ||
4203 | 237 | if self.origin is not None: | ||
4204 | 238 | msg = [" arrives from ", self.origin, "."] | ||
4205 | 239 | elif self.direction is not None: | ||
4206 | 240 | msg = [" arrives from the ", self.direction, "."] | ||
4207 | 241 | else: | ||
4208 | 242 | msg = [" arrives."] | ||
4209 | 243 | msg.insert(0, self.thing) | ||
4210 | 244 | return language.Sentence(msg) | ||
4211 | 245 | |||
4212 | 246 | |||
4213 | 247 | |||
4214 | 248 | class DepartureEvent(Success): | ||
4215 | 249 | """ | ||
4216 | 250 | An event representing the departure of an object at a location to a | ||
4217 | 251 | destination. | ||
4218 | 252 | """ | ||
4219 | 253 | def __init__(self, location, actor, **kw): | ||
4220 | 254 | """ | ||
4221 | 255 | @type location: L{iimaginary.IThing} provider. | ||
4222 | 256 | @param location: The location that the actor is leaving. | ||
4223 | 257 | @type actor: L{iimaginary.IThing} provider. | ||
4224 | 258 | @param actor: The actor that is leaving. | ||
4225 | 259 | """ | ||
4226 | 260 | super(DepartureEvent, self).__init__(location, actor, **kw) | ||
4227 | 261 | |||
4228 | 262 | |||
4229 | 263 | class SpeechEvent(Success): | ||
4230 | 264 | """ | ||
4231 | 265 | An event representing something somebody said. | ||
4232 | 266 | |||
4233 | 267 | @ivar speaker: The Thing which spoke. | ||
4234 | 268 | @ivar text: The text which was spoken. | ||
4235 | 269 | """ | ||
4236 | 270 | def __init__(self, speaker, text): | ||
4237 | 271 | """ | ||
4238 | 272 | @type speaker: L{iimaginary.IThing} provider. | ||
4239 | 273 | @param speaker: The actor emitting this speech. | ||
4240 | 274 | @type text: C{unicode} | ||
4241 | 275 | @param text: The text that the actor said. | ||
4242 | 276 | """ | ||
4243 | 277 | self.speaker = speaker | ||
4244 | 278 | self.text = text | ||
4245 | 279 | Success.__init__( | ||
4246 | 280 | self, | ||
4247 | 281 | location=speaker.location, | ||
4248 | 282 | actor=speaker, | ||
4249 | 283 | actorMessage=["You say, '", text, "'"], | ||
4250 | 284 | otherMessage=language.Sentence([speaker, " says, '", text, "'"])) | ||
4251 | 285 | 0 | ||
4252 | === removed file 'Imaginary/imaginary/garments.py' | |||
4253 | --- Imaginary/imaginary/garments.py 2011-09-16 18:52:54 +0000 | |||
4254 | +++ Imaginary/imaginary/garments.py 1970-01-01 00:00:00 +0000 | |||
4255 | @@ -1,370 +0,0 @@ | |||
4256 | 1 | # -*- test-case-name: imaginary.test.test_garments -*- | ||
4257 | 2 | |||
4258 | 3 | """ | ||
4259 | 4 | |||
4260 | 5 | Layered clothing. | ||
4261 | 6 | |||
4262 | 7 | """ | ||
4263 | 8 | |||
4264 | 9 | from zope.interface import implements | ||
4265 | 10 | |||
4266 | 11 | from axiom import item, attributes | ||
4267 | 12 | |||
4268 | 13 | from imaginary import iimaginary, language, objects | ||
4269 | 14 | from imaginary.eimaginary import ActionFailure | ||
4270 | 15 | from imaginary.events import ThatDoesntWork | ||
4271 | 16 | from imaginary.idea import Link | ||
4272 | 17 | from imaginary.creation import createCreator | ||
4273 | 18 | from imaginary.enhancement import Enhancement | ||
4274 | 19 | |||
4275 | 20 | |||
4276 | 21 | class Unwearable(Exception): | ||
4277 | 22 | pass | ||
4278 | 23 | |||
4279 | 24 | class TooBulky(Unwearable): | ||
4280 | 25 | def __init__(self, wornGarment, newGarment): | ||
4281 | 26 | self.wornGarment = wornGarment | ||
4282 | 27 | self.newGarment = newGarment | ||
4283 | 28 | Unwearable.__init__(self, wornGarment, newGarment) | ||
4284 | 29 | |||
4285 | 30 | |||
4286 | 31 | |||
4287 | 32 | class InaccessibleGarment(Exception): | ||
4288 | 33 | """The garment is covered by another, therefore it cannot be removed. | ||
4289 | 34 | """ | ||
4290 | 35 | def __init__(self, wearer, garment, obscuringGarment): | ||
4291 | 36 | self.wearer = wearer | ||
4292 | 37 | self.garment = garment | ||
4293 | 38 | self.obscuringGarment = obscuringGarment | ||
4294 | 39 | |||
4295 | 40 | |||
4296 | 41 | def __str__(self): | ||
4297 | 42 | return "%s tried taking off %s which was covered by %s" % ( | ||
4298 | 43 | self.wearer, self.garment, self.obscuringGarment) | ||
4299 | 44 | |||
4300 | 45 | |||
4301 | 46 | |||
4302 | 47 | GARMENT_SLOTS = [ | ||
4303 | 48 | u"crown", | ||
4304 | 49 | u"left eye", | ||
4305 | 50 | u"right eye", | ||
4306 | 51 | u"left ear", | ||
4307 | 52 | u"right ear", | ||
4308 | 53 | |||
4309 | 54 | u"neck", | ||
4310 | 55 | u"chest", | ||
4311 | 56 | u"back", | ||
4312 | 57 | |||
4313 | 58 | u"left arm", | ||
4314 | 59 | u"right arm", | ||
4315 | 60 | u"left wrist", | ||
4316 | 61 | u"right wrist", | ||
4317 | 62 | u"left hand", | ||
4318 | 63 | u"right hand", | ||
4319 | 64 | u"left fingers", | ||
4320 | 65 | u"right fingers", | ||
4321 | 66 | |||
4322 | 67 | u"waist", | ||
4323 | 68 | u"left leg", | ||
4324 | 69 | u"right leg", | ||
4325 | 70 | u"left ankle", | ||
4326 | 71 | u"right ankle", | ||
4327 | 72 | u"left foot", | ||
4328 | 73 | u"right foot" | ||
4329 | 74 | ] | ||
4330 | 75 | |||
4331 | 76 | class GarmentSlot: | ||
4332 | 77 | pass | ||
4333 | 78 | |||
4334 | 79 | for gslot in GARMENT_SLOTS: | ||
4335 | 80 | gslotname = gslot.upper().replace(" ", "_").encode('ascii') | ||
4336 | 81 | setattr(GarmentSlot, gslotname, gslot) | ||
4337 | 82 | |||
4338 | 83 | |||
4339 | 84 | |||
4340 | 85 | class Garment(item.Item, Enhancement): | ||
4341 | 86 | """ | ||
4342 | 87 | An enhancement for a L{Thing} representing its utility as an article of | ||
4343 | 88 | clothing. | ||
4344 | 89 | """ | ||
4345 | 90 | implements(iimaginary.IClothing, | ||
4346 | 91 | iimaginary.IDescriptionContributor, | ||
4347 | 92 | iimaginary.IMovementRestriction) | ||
4348 | 93 | |||
4349 | 94 | powerupInterfaces = (iimaginary.IClothing, | ||
4350 | 95 | iimaginary.IDescriptionContributor, | ||
4351 | 96 | iimaginary.IMovementRestriction) | ||
4352 | 97 | |||
4353 | 98 | thing = attributes.reference() | ||
4354 | 99 | |||
4355 | 100 | # templated / constant stuff | ||
4356 | 101 | garmentSlots = attributes.textlist(allowNone=False) | ||
4357 | 102 | bulk = attributes.integer(allowNone=False, | ||
4358 | 103 | default=1) | ||
4359 | 104 | garmentDescription = attributes.text(doc=""" | ||
4360 | 105 | Description of this as an individual garment. | ||
4361 | 106 | """, allowNone=False) | ||
4362 | 107 | |||
4363 | 108 | # transient / mutable stuff | ||
4364 | 109 | wearer = attributes.reference() | ||
4365 | 110 | wearLevel = attributes.integer(default=0) | ||
4366 | 111 | |||
4367 | 112 | |||
4368 | 113 | def conceptualize(self): | ||
4369 | 114 | return language.ExpressString(u'This can be worn.') | ||
4370 | 115 | |||
4371 | 116 | |||
4372 | 117 | def expressTo(self, observer): | ||
4373 | 118 | """ | ||
4374 | 119 | Describe the garment as it looks when it is worn. | ||
4375 | 120 | |||
4376 | 121 | The garment's normal description is C{self.thing.description} or | ||
4377 | 122 | somesuch. | ||
4378 | 123 | """ | ||
4379 | 124 | return self.garmentDescription | ||
4380 | 125 | |||
4381 | 126 | |||
4382 | 127 | def nowWornBy(self, wearer): | ||
4383 | 128 | """ | ||
4384 | 129 | This garment is now worn by the given wearer. As this garment is now | ||
4385 | 130 | on top, set its C{wearLevel} to be higher than any other L{Garment} | ||
4386 | 131 | related to the new C{wearer}. | ||
4387 | 132 | """ | ||
4388 | 133 | self.wearer = wearer | ||
4389 | 134 | self.wearLevel = wearer.store.query( | ||
4390 | 135 | Garment, | ||
4391 | 136 | Garment.wearer == wearer).getColumn("wearLevel").max(default=0) + 1 | ||
4392 | 137 | |||
4393 | 138 | |||
4394 | 139 | def noLongerWorn(self): | ||
4395 | 140 | """ | ||
4396 | 141 | This garment is no longer being worn by anyone. | ||
4397 | 142 | """ | ||
4398 | 143 | self.wearer = None | ||
4399 | 144 | self.wearLevel = None | ||
4400 | 145 | |||
4401 | 146 | |||
4402 | 147 | def movementImminent(self, movee, destination): | ||
4403 | 148 | """ | ||
4404 | 149 | Something is trying to move. Don't allow it if I'm currently worn. | ||
4405 | 150 | """ | ||
4406 | 151 | if self.wearer is not None and movee is self.thing: | ||
4407 | 152 | raise ActionFailure( | ||
4408 | 153 | ThatDoesntWork( | ||
4409 | 154 | # XXX I don't actually know who is performing the action | ||
4410 | 155 | # :-(. | ||
4411 | 156 | actor=self.wearer.thing, | ||
4412 | 157 | actorMessage=[ | ||
4413 | 158 | "You can't move ", | ||
4414 | 159 | language.Noun(self.thing).definiteNounPhrase(), | ||
4415 | 160 | " without removing it first."])) | ||
4416 | 161 | |||
4417 | 162 | |||
4418 | 163 | |||
4419 | 164 | def _orderTopClothingByGlobalSlotList(tempClothes): | ||
4420 | 165 | """ | ||
4421 | 166 | This function orders a dict as returned by getGarmentDict in the order that | ||
4422 | 167 | they should be shown to the user. | ||
4423 | 168 | |||
4424 | 169 | @param tempClothes: {clothingSlot: list of clothing objects (top last)} | ||
4425 | 170 | @type tempClothes: dict | ||
4426 | 171 | """ | ||
4427 | 172 | if not tempClothes: | ||
4428 | 173 | return None | ||
4429 | 174 | yetDescribed = [] | ||
4430 | 175 | for universalSlot in GARMENT_SLOTS: | ||
4431 | 176 | slotlist = tempClothes.pop(universalSlot, ()) | ||
4432 | 177 | if slotlist: | ||
4433 | 178 | topGarment = slotlist[-1] | ||
4434 | 179 | if topGarment not in yetDescribed: | ||
4435 | 180 | yetDescribed.append(topGarment) | ||
4436 | 181 | |||
4437 | 182 | # if somebody decided to make a wacky slot that is not in the universal | ||
4438 | 183 | # slots list, just describe it last. | ||
4439 | 184 | for k in tempClothes.keys(): | ||
4440 | 185 | x = tempClothes.pop(k) | ||
4441 | 186 | if x: | ||
4442 | 187 | topGarment = x[-1] | ||
4443 | 188 | if topGarment not in yetDescribed: | ||
4444 | 189 | yetDescribed.append(topGarment) | ||
4445 | 190 | |||
4446 | 191 | assert tempClothes == {}, ( | ||
4447 | 192 | "tempClothes not empty after all clothes eliminated: " + | ||
4448 | 193 | repr(tempClothes)) | ||
4449 | 194 | |||
4450 | 195 | return yetDescribed | ||
4451 | 196 | |||
4452 | 197 | |||
4453 | 198 | |||
4454 | 199 | class Wearer(item.Item, Enhancement): | ||
4455 | 200 | """ | ||
4456 | 201 | The clothing-wearing component of an object that can wear clothing; e.g. a | ||
4457 | 202 | person or mannequin. | ||
4458 | 203 | """ | ||
4459 | 204 | |||
4460 | 205 | _interfaces = (iimaginary.IClothingWearer, | ||
4461 | 206 | iimaginary.IDescriptionContributor, | ||
4462 | 207 | iimaginary.ILinkContributor, | ||
4463 | 208 | iimaginary.ILinkAnnotator, | ||
4464 | 209 | ) | ||
4465 | 210 | |||
4466 | 211 | implements(*_interfaces) | ||
4467 | 212 | |||
4468 | 213 | powerupInterfaces = _interfaces | ||
4469 | 214 | |||
4470 | 215 | |||
4471 | 216 | thing = attributes.reference() | ||
4472 | 217 | |||
4473 | 218 | |||
4474 | 219 | def getGarmentDict(self): | ||
4475 | 220 | c = {} | ||
4476 | 221 | for garment in self.store.query(Garment, Garment.wearer == self, | ||
4477 | 222 | sort=Garment.wearLevel.ascending): | ||
4478 | 223 | for usedSlot in garment.garmentSlots: | ||
4479 | 224 | c.setdefault(usedSlot, []).append(garment) | ||
4480 | 225 | return c | ||
4481 | 226 | |||
4482 | 227 | |||
4483 | 228 | def putOn(self, newGarment): | ||
4484 | 229 | """ | ||
4485 | 230 | Wear a new L{Garment} on this L{Wearer}, first moving it to this | ||
4486 | 231 | L{Wearer}'s C{thing} if it is not already there. | ||
4487 | 232 | |||
4488 | 233 | @param newGarment: the article of clothing to wear. | ||
4489 | 234 | |||
4490 | 235 | @type newGarment: L{Garment} | ||
4491 | 236 | |||
4492 | 237 | @raise TooBulky: if the bulk of any of the slots occupied by | ||
4493 | 238 | C{newGarment} is greater than the bulk of any other clothing | ||
4494 | 239 | already in that slot. (For example, if you tried to wear a T-shirt | ||
4495 | 240 | over a heavy coat.) | ||
4496 | 241 | """ | ||
4497 | 242 | c = self.getGarmentDict() | ||
4498 | 243 | for garmentSlot in newGarment.garmentSlots: | ||
4499 | 244 | if garmentSlot in c: | ||
4500 | 245 | currentTopOfSlot = c[garmentSlot][-1] | ||
4501 | 246 | if currentTopOfSlot.bulk >= newGarment.bulk: | ||
4502 | 247 | raise TooBulky(currentTopOfSlot, newGarment) | ||
4503 | 248 | |||
4504 | 249 | newGarment.thing.moveTo(self.thing) | ||
4505 | 250 | newGarment.nowWornBy(self) | ||
4506 | 251 | |||
4507 | 252 | |||
4508 | 253 | def takeOff(self, garment): | ||
4509 | 254 | """ | ||
4510 | 255 | Remove a garment which this player is wearing. | ||
4511 | 256 | |||
4512 | 257 | (Note: no error checking is currently performed to see if this garment | ||
4513 | 258 | is actually already worn by this L{Wearer}.) | ||
4514 | 259 | |||
4515 | 260 | @param garment: the article of clothing to remove. | ||
4516 | 261 | |||
4517 | 262 | @type garment: L{Garment} | ||
4518 | 263 | |||
4519 | 264 | @raise InaccessibleGarment: if the garment is obscured by any other | ||
4520 | 265 | clothing, and is therefore not in the top slot for any of the slots | ||
4521 | 266 | it occupies. For example, if you put on an undershirt, then a | ||
4522 | 267 | turtleneck, you can't remove the undershirt without removing the | ||
4523 | 268 | turtleneck first. | ||
4524 | 269 | """ | ||
4525 | 270 | gdict = self.getGarmentDict() | ||
4526 | 271 | for slot in garment.garmentSlots: | ||
4527 | 272 | if gdict[slot][-1] is not garment: | ||
4528 | 273 | raise InaccessibleGarment(self, garment, gdict[slot][-1]) | ||
4529 | 274 | garment.noLongerWorn() | ||
4530 | 275 | |||
4531 | 276 | |||
4532 | 277 | # IDescriptionContributor | ||
4533 | 278 | def conceptualize(self): | ||
4534 | 279 | """ | ||
4535 | 280 | Describe the list of clothing. | ||
4536 | 281 | """ | ||
4537 | 282 | return ExpressClothing(self.thing, self.getGarmentDict()) | ||
4538 | 283 | |||
4539 | 284 | |||
4540 | 285 | # ILinkContributor | ||
4541 | 286 | def links(self): | ||
4542 | 287 | for garmentThing in self.store.query(objects.Thing, | ||
4543 | 288 | attributes.AND( | ||
4544 | 289 | Garment.thing == objects.Thing.storeID, | ||
4545 | 290 | Garment.wearer == self)): | ||
4546 | 291 | yield Link(self.thing.idea, garmentThing.idea) | ||
4547 | 292 | |||
4548 | 293 | |||
4549 | 294 | # ILinkAnnotator | ||
4550 | 295 | def annotationsFor(self, link, idea): | ||
4551 | 296 | """ | ||
4552 | 297 | Tell the containment system to disregard containment relationships for | ||
4553 | 298 | which I will generate a link. | ||
4554 | 299 | """ | ||
4555 | 300 | if list(link.of(iimaginary.IContainmentRelationship)): | ||
4556 | 301 | if link.source.delegate is self.thing: | ||
4557 | 302 | clothing = iimaginary.IClothing(link.target.delegate, None) | ||
4558 | 303 | if clothing is not None: | ||
4559 | 304 | if clothing.wearer is self: | ||
4560 | 305 | yield _DisregardYourWearingIt() | ||
4561 | 306 | |||
4562 | 307 | |||
4563 | 308 | |||
4564 | 309 | class _DisregardYourWearingIt(object): | ||
4565 | 310 | """ | ||
4566 | 311 | This is an annotation, produced by L{Wearer} for containment relationships | ||
4567 | 312 | between people (who are containers) and the clothing that they're wearing. | ||
4568 | 313 | A hopefully temporary workaround for the fact that clothing is rendered in | ||
4569 | 314 | its own way and therefor shouldn't show up in the list of a person's | ||
4570 | 315 | contents. | ||
4571 | 316 | """ | ||
4572 | 317 | implements(iimaginary.IElectromagneticMedium) | ||
4573 | 318 | |||
4574 | 319 | def isOpaque(self): | ||
4575 | 320 | """ | ||
4576 | 321 | I am opaque, so that clothing will show up only once (in your "wearing" | ||
4577 | 322 | list, rather than there and in your "contained" list), and obscured | ||
4578 | 323 | clothing won't show up at all. | ||
4579 | 324 | """ | ||
4580 | 325 | return True | ||
4581 | 326 | |||
4582 | 327 | |||
4583 | 328 | |||
4584 | 329 | class ExpressClothing(language.BaseExpress): | ||
4585 | 330 | def __init__(self, thing, garments): | ||
4586 | 331 | self.thing = thing | ||
4587 | 332 | self.garments = garments | ||
4588 | 333 | |||
4589 | 334 | |||
4590 | 335 | def vt102(self, observer): | ||
4591 | 336 | heshe = language.Noun(self.thing).heShe() | ||
4592 | 337 | L = _orderTopClothingByGlobalSlotList(self.garments) | ||
4593 | 338 | if L is None: | ||
4594 | 339 | return language.Sentence([heshe, u' is naked.']).vt102(observer) | ||
4595 | 340 | return language.Sentence([ | ||
4596 | 341 | heshe, | ||
4597 | 342 | u' is wearing ', | ||
4598 | 343 | language.ItemizedList([language.Noun(g.thing).nounPhrase() | ||
4599 | 344 | for g in L]), | ||
4600 | 345 | u'.']).vt102(observer) | ||
4601 | 346 | |||
4602 | 347 | |||
4603 | 348 | |||
4604 | 349 | createShirt = createCreator( | ||
4605 | 350 | (Garment, dict(garmentDescription=u'an undescribed shirt', | ||
4606 | 351 | bulk=2, | ||
4607 | 352 | garmentSlots=[GarmentSlot.CHEST, | ||
4608 | 353 | GarmentSlot.BACK, | ||
4609 | 354 | GarmentSlot.RIGHT_ARM, | ||
4610 | 355 | GarmentSlot.LEFT_ARM]))) | ||
4611 | 356 | |||
4612 | 357 | |||
4613 | 358 | createUnderwear = createCreator( | ||
4614 | 359 | (Garment, dict(garmentDescription=u'an undescribed pair of underwear', | ||
4615 | 360 | bulk=1, | ||
4616 | 361 | garmentSlots=[GarmentSlot.WAIST]))) | ||
4617 | 362 | |||
4618 | 363 | createPants = createCreator( | ||
4619 | 364 | (Garment, dict(garmentDescription=u'an undescribed pair of pants', | ||
4620 | 365 | bulk=2, | ||
4621 | 366 | garmentSlots=[GarmentSlot.RIGHT_LEG, | ||
4622 | 367 | GarmentSlot.LEFT_LEG, | ||
4623 | 368 | GarmentSlot.WAIST, | ||
4624 | 369 | GarmentSlot.LEFT_ANKLE, | ||
4625 | 370 | GarmentSlot.RIGHT_ANKLE]))) | ||
4626 | 371 | 0 | ||
4627 | === removed file 'Imaginary/imaginary/idea.py' | |||
4628 | --- Imaginary/imaginary/idea.py 2010-04-24 18:00:14 +0000 | |||
4629 | +++ Imaginary/imaginary/idea.py 1970-01-01 00:00:00 +0000 | |||
4630 | @@ -1,625 +0,0 @@ | |||
4631 | 1 | # -*- test-case-name: imaginary -*- | ||
4632 | 2 | |||
4633 | 3 | """ | ||
4634 | 4 | This module implements a highly abstract graph-traversal system for actions and | ||
4635 | 5 | events to locate the objects which can respond to them. The top-level | ||
4636 | 6 | entry-point to this system is L{Idea.obtain}. | ||
4637 | 7 | |||
4638 | 8 | It also implements several basic retrievers related to visibility and physical | ||
4639 | 9 | reachability. | ||
4640 | 10 | """ | ||
4641 | 11 | |||
4642 | 12 | from zope.interface import implements | ||
4643 | 13 | from epsilon.structlike import record | ||
4644 | 14 | |||
4645 | 15 | from imaginary.iimaginary import ( | ||
4646 | 16 | INameable, ILitLink, IThing, IObstruction, IElectromagneticMedium, | ||
4647 | 17 | IDistance, IRetriever, IExit) | ||
4648 | 18 | |||
4649 | 19 | |||
4650 | 20 | |||
4651 | 21 | class Link(record("source target")): | ||
4652 | 22 | """ | ||
4653 | 23 | A L{Link} is a connection between two L{Idea}s in a L{Path}. | ||
4654 | 24 | |||
4655 | 25 | @ivar source: the idea that this L{Link} originated from. | ||
4656 | 26 | @type source: L{Idea} | ||
4657 | 27 | |||
4658 | 28 | @ivar target: the idea that this L{Link} refers to. | ||
4659 | 29 | @type target: L{Idea} | ||
4660 | 30 | """ | ||
4661 | 31 | |||
4662 | 32 | def __init__(self, *a, **k): | ||
4663 | 33 | super(Link, self).__init__(*a, **k) | ||
4664 | 34 | self.annotations = [] | ||
4665 | 35 | |||
4666 | 36 | |||
4667 | 37 | def annotate(self, annotations): | ||
4668 | 38 | """ | ||
4669 | 39 | Annotate this link with a list of annotations. | ||
4670 | 40 | """ | ||
4671 | 41 | self.annotations.extend(annotations) | ||
4672 | 42 | |||
4673 | 43 | |||
4674 | 44 | def of(self, interface): | ||
4675 | 45 | """ | ||
4676 | 46 | Yield all annotations on this link which provide the given interface. | ||
4677 | 47 | """ | ||
4678 | 48 | for annotation in self.annotations: | ||
4679 | 49 | provider = interface(annotation, None) | ||
4680 | 50 | if provider is not None: | ||
4681 | 51 | yield provider | ||
4682 | 52 | |||
4683 | 53 | |||
4684 | 54 | |||
4685 | 55 | class Path(record('links')): | ||
4686 | 56 | """ | ||
4687 | 57 | A list of L{Link}s. | ||
4688 | 58 | """ | ||
4689 | 59 | |||
4690 | 60 | def of(self, interface): | ||
4691 | 61 | """ | ||
4692 | 62 | @return: an iterator of providers of interfaces, adapted from each link | ||
4693 | 63 | in this path. | ||
4694 | 64 | """ | ||
4695 | 65 | for link in self.links: | ||
4696 | 66 | for annotation in link.of(interface): | ||
4697 | 67 | yield annotation | ||
4698 | 68 | |||
4699 | 69 | |||
4700 | 70 | def eachTargetAs(self, interface): | ||
4701 | 71 | """ | ||
4702 | 72 | @return: an iterable of all non-None results of each L{Link.targetAs} | ||
4703 | 73 | method in this L{Path}'s C{links} attribute. | ||
4704 | 74 | """ | ||
4705 | 75 | for link in self.links: | ||
4706 | 76 | provider = interface(link.target.delegate, None) | ||
4707 | 77 | if provider is not None: | ||
4708 | 78 | yield provider | ||
4709 | 79 | |||
4710 | 80 | |||
4711 | 81 | def targetAs(self, interface): | ||
4712 | 82 | """ | ||
4713 | 83 | Retrieve the target of the last link of this path, its final | ||
4714 | 84 | destination, as a given interface. | ||
4715 | 85 | |||
4716 | 86 | @param interface: the interface to retrieve. | ||
4717 | 87 | @type interface: L{zope.interface.interfaces.IInterface} | ||
4718 | 88 | |||
4719 | 89 | @return: the last link's target, adapted to the given interface, or | ||
4720 | 90 | C{None} if no appropriate adapter or component exists. | ||
4721 | 91 | @rtype: C{interface} or C{NoneType} | ||
4722 | 92 | """ | ||
4723 | 93 | return interface(self.links[-1].target.delegate, None) | ||
4724 | 94 | |||
4725 | 95 | |||
4726 | 96 | def isCyclic(self): | ||
4727 | 97 | """ | ||
4728 | 98 | Determine if this path is cyclic, to avoid descending down infinite | ||
4729 | 99 | loops. | ||
4730 | 100 | |||
4731 | 101 | @return: a boolean indicating whether this L{Path} is cyclic or not, | ||
4732 | 102 | i.e. whether the L{Idea} its last link points at is the source of | ||
4733 | 103 | any of its links. | ||
4734 | 104 | """ | ||
4735 | 105 | if len(self.links) < 2: | ||
4736 | 106 | return False | ||
4737 | 107 | return (self.links[-1].target in (x.source for x in self.links)) | ||
4738 | 108 | |||
4739 | 109 | |||
4740 | 110 | def to(self, link): | ||
4741 | 111 | """ | ||
4742 | 112 | Create a new path, extending this one by one new link. | ||
4743 | 113 | """ | ||
4744 | 114 | return Path(self.links + [link]) | ||
4745 | 115 | |||
4746 | 116 | |||
4747 | 117 | def __repr__(self): | ||
4748 | 118 | """ | ||
4749 | 119 | @return: an expanded pretty-printed representation of this Path, | ||
4750 | 120 | suitable for debugging. | ||
4751 | 121 | """ | ||
4752 | 122 | s = 'Path(' | ||
4753 | 123 | for link in self.links: | ||
4754 | 124 | dlgt = link.target.delegate | ||
4755 | 125 | src = link.source.delegate | ||
4756 | 126 | s += "\n\t" | ||
4757 | 127 | s += repr(getattr(src, 'name', src)) | ||
4758 | 128 | s += " => " | ||
4759 | 129 | s += repr(getattr(dlgt, 'name', dlgt)) | ||
4760 | 130 | s += " " | ||
4761 | 131 | s += repr(link.annotations) | ||
4762 | 132 | s += ')' | ||
4763 | 133 | return s | ||
4764 | 134 | |||
4765 | 135 | |||
4766 | 136 | |||
4767 | 137 | class Idea(record("delegate linkers annotators")): | ||
4768 | 138 | """ | ||
4769 | 139 | Consider a person's activities with the world around them as having two | ||
4770 | 140 | layers. One is a physical layer, out in the world, composed of matter and | ||
4771 | 141 | energy. The other is a cognitive layer, internal to the person, composed | ||
4772 | 142 | of ideas about that matter and energy. | ||
4773 | 143 | |||
4774 | 144 | For example, when a person wants to sit in a wooden chair, they must first | ||
4775 | 145 | visually locate the arrangement of wood in question, make the determination | ||
4776 | 146 | of that it is a "chair" based on its properties, and then perform the | ||
4777 | 147 | appropriate actions to sit upon it. | ||
4778 | 148 | |||
4779 | 149 | However, a person may also interact with symbolic abstractions rather than | ||
4780 | 150 | physical objects. They may read a word, or point at a window on a computer | ||
4781 | 151 | screen. An L{Idea} is a representation of the common unit that can be | ||
4782 | 152 | referred to in this way. | ||
4783 | 153 | |||
4784 | 154 | Both physical and cognitive layers are present in Imaginary. The cognitive | ||
4785 | 155 | layer is modeled by L{imaginary.idea}. The physical layer is modeled by a | ||
4786 | 156 | rudimentary point-of-interest simulation in L{imaginary.objects}. An | ||
4787 | 157 | L{imaginary.thing.Thing} is a physical object; an L{Idea} is a node in a | ||
4788 | 158 | non-physical graph, related by links that are annotated to describe the | ||
4789 | 159 | nature of the relationship between it and other L{Idea}s. | ||
4790 | 160 | |||
4791 | 161 | L{Idea} is the most abstract unit of simulation. It does not have any | ||
4792 | 162 | behavior or simulation semantics of its own; it merely ties together | ||
4793 | 163 | different related systems. | ||
4794 | 164 | |||
4795 | 165 | An L{Idea} is composed of a C{delegate}, which is an object that implements | ||
4796 | 166 | simulation-defined interfaces; a list of L{ILinkContributor}s, which | ||
4797 | 167 | produce L{Link}s to other L{Idea}s, an a set of C{ILinkAnnotator}s, which | ||
4798 | 168 | apply annotations (which themselves implement simulation-defined | ||
4799 | 169 | link-annotation interfaces) to those links. | ||
4800 | 170 | |||
4801 | 171 | Each L{imaginary.thing.Thing} has a corresponding L{Idea} to represent it | ||
4802 | 172 | in the simulation. The physical simulation defines only a few types of | ||
4803 | 173 | links: objects have links to their containers, containers have links to | ||
4804 | 174 | their contents, rooms have links to their exits, exits have links to their | ||
4805 | 175 | destinations. Any L{imaginary.thing.Thing} can have a powerup applied to | ||
4806 | 176 | it which adds to the list of linkers or annotators for its L{Idea}, | ||
4807 | 177 | however, which allows users to create arbitrary objects. | ||
4808 | 178 | |||
4809 | 179 | For example, the target of the "look" action must implement | ||
4810 | 180 | L{imaginary.iimaginary.IVisible}, but need not be a | ||
4811 | 181 | L{iimaginary.objects.Thing}. A simulation might want to provide a piece of | ||
4812 | 182 | graffiti that you could look at, but would not be a physical object, in the | ||
4813 | 183 | sense that you couldn't pick it up, weigh it, push it, etc. Such an object | ||
4814 | 184 | could be implemented as a powerup for both | ||
4815 | 185 | L{imaginary.iimaginary.IDescriptionContributor}, which would impart some | ||
4816 | 186 | short flavor text to the room, and L{imaginary.iimaginary.IVisible}, which | ||
4817 | 187 | would be an acceptable target of 'look'. The | ||
4818 | 188 | L{imaginary.iimaginary.IVisible} implementation could even be an in-memory | ||
4819 | 189 | object, not stored in the database at all; and there could be different | ||
4820 | 190 | implementations for different observers, depending on their level of | ||
4821 | 191 | knowledge about the in-world graffiti. | ||
4822 | 192 | |||
4823 | 193 | @ivar delegate: this object is the object which may be adaptable to a set | ||
4824 | 194 | of interfaces. This L{Idea} delegates all adaptation to its delegate. | ||
4825 | 195 | In many cases (when referring to a physical object), this will be an | ||
4826 | 196 | L{imaginary.thing.Thing}, but not necessarily. | ||
4827 | 197 | |||
4828 | 198 | @ivar linkers: a L{list} of L{ILinkContributor}s which are used to gather | ||
4829 | 199 | L{Link}s from this L{Idea} during L{Idea.obtain} traversal. | ||
4830 | 200 | |||
4831 | 201 | @ivar annotators: a L{list} of L{ILinkAnnotator}s which are used to annotate | ||
4832 | 202 | L{Link}s gathered from this L{Idea} via the C{linkers} list. | ||
4833 | 203 | """ | ||
4834 | 204 | |||
4835 | 205 | def __init__(self, delegate): | ||
4836 | 206 | super(Idea, self).__init__(delegate, [], []) | ||
4837 | 207 | |||
4838 | 208 | |||
4839 | 209 | def _allLinks(self): | ||
4840 | 210 | """ | ||
4841 | 211 | Return an iterator of all L{Links} away from this idea. | ||
4842 | 212 | """ | ||
4843 | 213 | for linker in self.linkers: | ||
4844 | 214 | for link in linker.links(): | ||
4845 | 215 | yield link | ||
4846 | 216 | |||
4847 | 217 | |||
4848 | 218 | def _applyAnnotators(self, linkiter): | ||
4849 | 219 | """ | ||
4850 | 220 | Apply my list of annotators to each link in the given iterable. | ||
4851 | 221 | """ | ||
4852 | 222 | for link in linkiter: | ||
4853 | 223 | self._annotateOneLink(link) | ||
4854 | 224 | yield link | ||
4855 | 225 | |||
4856 | 226 | |||
4857 | 227 | def _annotateOneLink(self, link): | ||
4858 | 228 | """ | ||
4859 | 229 | Apply all L{ILinkAnnotator}s in this L{Idea}'s C{annotators} list. | ||
4860 | 230 | """ | ||
4861 | 231 | allAnnotations = [] | ||
4862 | 232 | for annotator in self.annotators: | ||
4863 | 233 | # XXX important to test: annotators shouldn't mutate the links. | ||
4864 | 234 | # The annotators show up in a non-deterministic order, so in order | ||
4865 | 235 | # to facilitate a consistent view of the link in annotationsFor(), | ||
4866 | 236 | # all annotations are applied at the end. | ||
4867 | 237 | allAnnotations.extend(annotator.annotationsFor(link, self)) | ||
4868 | 238 | link.annotate(allAnnotations) | ||
4869 | 239 | |||
4870 | 240 | |||
4871 | 241 | def obtain(self, retriever): | ||
4872 | 242 | """ | ||
4873 | 243 | Traverse the graph of L{Idea}s, starting with C{self}, looking for | ||
4874 | 244 | objects which the given L{IRetriever} can retrieve. | ||
4875 | 245 | |||
4876 | 246 | The graph will be traversed by looking at all the links generated by | ||
4877 | 247 | this L{Idea}'s C{linkers}, only continuing down those links for which | ||
4878 | 248 | the given L{IRetriever}'s C{shouldKeepGoing} returns L{True}. | ||
4879 | 249 | |||
4880 | 250 | @param retriever: an object which will be passed each L{Path} in turn, | ||
4881 | 251 | discovered during traversal of the L{Idea} graph. If any | ||
4882 | 252 | invocation of L{IRetriever.retrieve} on this parameter should | ||
4883 | 253 | succeed, that will be yielded as a result from this method. | ||
4884 | 254 | @type retriever: L{IRetriever} | ||
4885 | 255 | |||
4886 | 256 | @return: a generator which yields the results of C{retriever.retrieve} | ||
4887 | 257 | which are not L{None}. | ||
4888 | 258 | """ | ||
4889 | 259 | return ObtainResult(self, retriever) | ||
4890 | 260 | |||
4891 | 261 | |||
4892 | 262 | def _doObtain(self, retriever, path, reasonsWhyNot): | ||
4893 | 263 | """ | ||
4894 | 264 | A generator that implements the logic for obtain() | ||
4895 | 265 | """ | ||
4896 | 266 | if path is None: | ||
4897 | 267 | # Special case: we only get a self->self link if we are the | ||
4898 | 268 | # beginning _and_ the end. | ||
4899 | 269 | path = Path([]) | ||
4900 | 270 | selfLink = Link(self, self) | ||
4901 | 271 | self._annotateOneLink(selfLink) | ||
4902 | 272 | finalPath = path.to(selfLink) | ||
4903 | 273 | else: | ||
4904 | 274 | finalPath = Path(path.links[:]) | ||
4905 | 275 | self._annotateOneLink(finalPath.links[-1]) | ||
4906 | 276 | |||
4907 | 277 | result = retriever.retrieve(finalPath) | ||
4908 | 278 | objections = set(retriever.objectionsTo(finalPath, result)) | ||
4909 | 279 | reasonsWhyNot |= objections | ||
4910 | 280 | if result is not None: | ||
4911 | 281 | if not objections: | ||
4912 | 282 | yield result | ||
4913 | 283 | |||
4914 | 284 | for link in self._applyAnnotators(self._allLinks()): | ||
4915 | 285 | subpath = path.to(link) | ||
4916 | 286 | if subpath.isCyclic(): | ||
4917 | 287 | continue | ||
4918 | 288 | if retriever.shouldKeepGoing(subpath): | ||
4919 | 289 | for obtained in link.target._doObtain(retriever, subpath, reasonsWhyNot): | ||
4920 | 290 | yield obtained | ||
4921 | 291 | |||
4922 | 292 | |||
4923 | 293 | |||
4924 | 294 | class ObtainResult(record("idea retriever")): | ||
4925 | 295 | """ | ||
4926 | 296 | The result of L{Idea.obtain}, this provides an iterable of results. | ||
4927 | 297 | |||
4928 | 298 | @ivar reasonsWhyNot: If this iterator has already been exhausted, this will | ||
4929 | 299 | be a C{set} of L{IWhyNot} objects explaining possible reasons why there | ||
4930 | 300 | were no results. For example, if the room where the player attempted | ||
4931 | 301 | to obtain targets is dark, this may contain an L{IWhyNot} provider. | ||
4932 | 302 | However, until this iterator has been exhausted, it will be C{None}. | ||
4933 | 303 | @type reasonsWhyNot: C{set} of L{IWhyNot}, or C{NoneType} | ||
4934 | 304 | |||
4935 | 305 | @ivar idea: the L{Idea} that L{Idea.obtain} was invoked on. | ||
4936 | 306 | @type idea: L{Idea} | ||
4937 | 307 | |||
4938 | 308 | @ivar retriever: The L{IRetriever} that L{Idea.obtain} was invoked with. | ||
4939 | 309 | @type retriever: L{IRetriever} | ||
4940 | 310 | """ | ||
4941 | 311 | |||
4942 | 312 | reasonsWhyNot = None | ||
4943 | 313 | |||
4944 | 314 | def __iter__(self): | ||
4945 | 315 | """ | ||
4946 | 316 | A generator which yields each result of the query, then sets | ||
4947 | 317 | C{reasonsWhyNot}. | ||
4948 | 318 | """ | ||
4949 | 319 | reasonsWhyNot = set() | ||
4950 | 320 | for result in self.idea._doObtain(self.retriever, None, reasonsWhyNot): | ||
4951 | 321 | yield result | ||
4952 | 322 | self.reasonsWhyNot = reasonsWhyNot | ||
4953 | 323 | |||
4954 | 324 | |||
4955 | 325 | |||
4956 | 326 | class DelegatingRetriever(object): | ||
4957 | 327 | """ | ||
4958 | 328 | A delegating retriever, so that retrievers can be easily composed. | ||
4959 | 329 | |||
4960 | 330 | See the various methods marked for overriding. | ||
4961 | 331 | |||
4962 | 332 | @ivar retriever: A retriever to delegate most operations to. | ||
4963 | 333 | @type retriever: L{IRetriever} | ||
4964 | 334 | """ | ||
4965 | 335 | |||
4966 | 336 | implements(IRetriever) | ||
4967 | 337 | |||
4968 | 338 | def __init__(self, retriever): | ||
4969 | 339 | """ | ||
4970 | 340 | Create a delegator with a retriever to delegate to. | ||
4971 | 341 | """ | ||
4972 | 342 | self.retriever = retriever | ||
4973 | 343 | |||
4974 | 344 | |||
4975 | 345 | def moreObjectionsTo(self, path, result): | ||
4976 | 346 | """ | ||
4977 | 347 | Override in subclasses to yield objections to add to this | ||
4978 | 348 | L{DelegatingRetriever}'s C{retriever}'s C{objectionsTo}. | ||
4979 | 349 | |||
4980 | 350 | By default, offer no additional objections. | ||
4981 | 351 | """ | ||
4982 | 352 | return [] | ||
4983 | 353 | |||
4984 | 354 | |||
4985 | 355 | def objectionsTo(self, path, result): | ||
4986 | 356 | """ | ||
4987 | 357 | Concatenate C{self.moreObjectionsTo} with C{self.moreObjectionsTo}. | ||
4988 | 358 | """ | ||
4989 | 359 | for objection in self.retriever.objectionsTo(path, result): | ||
4990 | 360 | yield objection | ||
4991 | 361 | for objection in self.moreObjectionsTo(path, result): | ||
4992 | 362 | yield objection | ||
4993 | 363 | |||
4994 | 364 | |||
4995 | 365 | def shouldStillKeepGoing(self, path): | ||
4996 | 366 | """ | ||
4997 | 367 | Override in subclasses to halt traversal via a C{False} return value for | ||
4998 | 368 | C{shouldKeepGoing} if this L{DelegatingRetriever}'s C{retriever}'s | ||
4999 | 369 | C{shouldKeepGoing} returns C{True}. | ||
5000 | 370 |
The diff has been truncated for viewing.
Looks good, please merge (I'm assuming you have commit access to lp:divmod.org; let me know if this isn't the case).