forked from twisted/imaginary
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
iimaginary.py
747 lines (519 loc) · 20.6 KB
/
iimaginary.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
# -*- test-case-name: imaginary -*-
from zope.interface import Interface, Attribute
class IThingType(Interface):
"""
Plugin interface for kinds of objects which can be created in the realm.
"""
type = Attribute("Name of this type of object.")
def getType():
"""
Return a two-argument callable which will be invoked with C{name},
C{description} to create a new instance of this type. Should return an
L{IThing} provider.
"""
class ILinkContributor(Interface):
"""
A powerup interface which can add more connections between objects in the
world graph.
All L{ILinkContributors} which are powered up on a particular
L{imaginary.objects.Thing} will be appended to that
L{imaginary.objects.Thing}'s value.
"""
def links():
"""
@return: an iterable of L{imaginary.idea.Link}s.
"""
class IDescriptionContributor(Interface):
"""
A powerup interface which can add text to the description of an object.
All IDescriptionContributors which are powered up on a particular Object
will be given a chance to add to the output of its C{conceptualize} method.
"""
def conceptualize():
"""
Return an IConcept provider.
"""
class INameable(Interface):
"""
A provider of L{INameable} is an object which can be identified by an
imaginary actor by a name.
"""
def knownTo(observer, name):
"""
Is this L{INameable} known to the given C{observer} by the given
C{name}?
@param name: the name to test for
@type name: L{unicode}
@param observer: the thing which is observing this namable.
@type observer: L{IThing}
@rtype: L{bool}
@return: L{True} if C{name} identifies this L{INameable}, L{False}
otherwise.
"""
class ILitLink(Interface):
"""
This interface is an annotation interface for L{imaginary.idea.Link}
objects, for indicating that the link can apply lighting.
"""
def isItLit(path, result):
"""
???
"""
def applyLighting(litThing, eventualTarget, requestedInterface):
"""
Apply a transformation to an object that an
L{imaginary.idea.Idea.obtain} is requesting, based on the light level
of this link and its surroundings.
@param litThing: The L{IThing} to apply lighting to.
@type litThing: L{IThing}
@param eventualTarget: The eventual, ultimate target of the path in
question.
@type eventualTarget: C{requestedInterface}
@param requestedInterface: The interface requested by the query that
resulted in this path; this is the interface which
C{eventualTarget} should implement.
@type requestedInterface: L{Interface}
@return: C{eventualTarget}, or, if this L{ILitLink} knows how to deal
with lighting specifically for C{requestedInterface}, a modified
version thereof which still implements C{requestedInterface}. If
insufficient lighting results in the player being unable to access
the desired object at all, C{None} will be returned.
@rtype: C{NoneType}, or C{requestedInterface}
"""
class IThing(Interface):
"""
A thing in the world. It has a location and and might be relocateable.
"""
location = Attribute("An IThing which contains this IThing")
proper = Attribute(
"A boolean indicating the definiteness of this thing's pronoun.")
name = Attribute(
"A unicode string, the name of this Thing.")
def moveTo(where, arrivalEventFactory=None):
"""
Change this things location to the new location, if possible.
@type where: L{IThing} provider.
@param where: The new location to be moved to.
@type arrivalEventFactory: A callable which takes a single
argument, the thing being moved, and returns an event.
@param arrivalEventFactory: Will be called to produce the
event to be broadcast to the new location upon arrival of this
thing. If not specified (or None), no event will be broadcast.
"""
def findProviders(interface, distance):
"""
Retrieve all game objects which provide C{interface} within C{distance}.
@return: A generator of providers of C{interface}.
"""
class IMovementRestriction(Interface):
"""
A L{MovementRestriction} is a powerup that can respond to a L{Thing}'s
movement before it occurs, and thereby restrict it.
Powerups of this type are consulted on L{Thing} before movement is allowed
to complete.
"""
def movementImminent(movee, destination):
"""
An object is about to move. Implementations can raise an exception if
they wish to to prevent it.
@param movee: the object that is moving.
@type movee: L{Thing}
@param destination: The L{Thing} of the container that C{movee} will be
moving to.
@type destination: L{IThing}
@raise Exception: if the movement is to be prevented.
"""
class IActor(Interface):
hitpoints = Attribute("L{Points} instance representing hit points")
experience = Attribute("C{int} representing experience")
level = Attribute("C{int} representing player's level")
thing = Attribute("L{IThing} which represents the actor's physical body.")
def send(event):
"""Describe something to the actor.
@type event: L{IConcept} provider
@param event: Something that will be described to the actor.
"""
def getIntelligence():
"""
Return the current intelligence associated with this actor, be it
ephemeral or enduring.
@rtype: L{IEventObserver}.
"""
def setEphemeralIntelligence(intelligence):
"""
Set the intelligence for this actor to an ephemeral L{IEventObserver}.
@type intelligence: L{IEventObserver} provider.
"""
def setEnduringIntelligence(intelligence):
"""
Set the intelligence for this actor to a persistent L{IEventObserver}.
@type intelligence: L{IEventObserver} provider.
"""
class IManipulator(Interface):
"""
An L{IManipulator} provider is an actor who can perform direct
manipulations of a world's environment (or at least, try to).
"""
def setIllumination(candelas):
"""
Attempt to set the ambient illumination this L{IManipulator}'s
location.
@param candelas: the desired ambient illumination value of the location
in candelas.
@type candelas: L{int}
@return: The previous ambient light level (in candelas)
@raise imaginary.eimaginary.ActionFailure: if the action cannot be
completed (for example, if this L{IManipulator} doesn't have
permission to change the lighting in its location).
"""
class IEventObserver(Interface):
def prepare(concept):
"""
Capture the given concept in a callable which will describe something
to this observer.
The callable will be invoked when it is entirely certain that the
concept is congruent with game reality. For example, a concept for an
arrow striking its target might be prepared but the resulting callable
will not be invoked until the combat game system decides the arrow
really is striking its target.
This two-phase process is also used to deal with events occurring
during transactions. While the event will be prepared immediately
during the execution of an action, the callable resulting from the
preparation will not be invoked until the transaction has completed.
If the transaction fails with an exception, then the callables will not
be invoked.
@type concept: L{IConcept} provider
@param concept: Something that will be described to the actor.
@return: a 0-argument callable which will deliver the given concept to
this observer.
"""
class ITransactionalEventBroadcaster(Interface):
"""
A thing which mediates the deadly side-effects of event broadcast by
holding things back until a transaction has been successfully committed or
is being reverted.
"""
def addEvent(event):
"""
Add an event which will be broadcast when the transaction is committed
successfully.
"""
def addRevertEvent(event):
"""
Add an event which will be broadcast when the transaction is reverted.
"""
class IExit(Interface):
"""
An interface representing one direction that a player may move in. While
L{IExit} only represents one half of a passageway, it is not necessarily
one-way; in most cases, a parallel exit will exist on the other side.
(However, it I{may} be one-way; there is no guarantee that you will be able
to traverse it backwards, or even indeed that it will take you somewhere at
all!)
"""
name = Attribute(
"""
The name of this exit. This must be something adaptable to
L{IConcept}, to display to players.
""")
def traverse(thing):
"""
Attempt to move the given L{IThing} through this L{IExit} to the other
side. (Note that this may not necessarily result in actual movement,
if the exit does something tricky like disorienting you or hurting
you.)
@param thing: Something which is passing through this exit.
@type thing: L{IThing}
"""
class IObstruction(Interface):
"""
An L{IObstruction} is a link annotation indicating that there is a physical
obstruction preventing solid objects from reaching between the two ends of
the link. For example, a closed door might annotate its link to its
destination with an L{IObstruction}.
"""
def whyNot():
"""
@return: a reason why this is obstructed.
@rtype: L{IWhyNot}
"""
class IContainer(Interface):
"""
An object which can contain other objects.
"""
capacity = Attribute(
"""
The maximum weight this container is capable of holding.
""")
closed = Attribute(
"""
A boolean indicating whether this container is closed.
""")
def add(object):
"""
Place the given object into this container.
@type object: L{IThing}
@param object: The world object to be added to this container. Its
C{location} attribute will be updated if it is successfully added.
@raise DoesntFit: If there is no room for C{object} in this container.
@raise Closed: If this container is not currently open.
"""
def remove(object):
"""
Remove the given object from this container.
@type object: L{IThing}
@param object: The world object which is currently in this container
which is to be removed. If it is successfully removed, its C{location}
attribute will be set to C{None}.
@raise ValueError: If C{object} is not within this container.
@raise Closed: If this container is not currently open.
"""
def contains(other):
"""
@returns: True if other is in me. And by 'in', I mean 'IN'! (And
by 'IN' he means to any arbitrarily deeply nested distance)
"""
def getContents():
"""
@returns: An iterable of the direct contents of this container.
"""
def getExits():
"""
@return: an L{axiom.store.ItemQuery} of the exits leading out of this
container.
"""
def getExitNames():
"""
@return: an L{axiom.store.AttributeQuery} of the names of the exits
leading out of this container.
"""
def getExitNamed(name, default=None):
"""
@return: The L{imaginary.objects.Exit} with the given name, or default
if none is found.
@raise KeyError: When an exit with the given name is not found and no
default was passed.
"""
class IConcept(Interface):
"""
This represents a concept which can be expressed in English.
"""
def vt102(observer):
"""
Produce some nicely colored data structures which can later be rendered
to some VT-102-escape-code compatible octets.
@param observer: the physical body of the player who is perceiving this
concept
@type observer: L{IThing}
@return: Some text in the format (currently informally) defined by
L{imaginary.text}.
@rtype: a L{list} or something, good luck
"""
def plaintext(observer):
"""
@param observer: the IThing provider who is asking to learn about this
concept, or None. This comes from the argument to 'express'.
"""
def capitalizeConcept():
"""
Make this concept CAPITALISERD!
Oh man this is retarded.
XXX fix it or something pleeeeeeeaaaaaaaaaasssssssssseeeeeeeeee
deletedeletedletedletledeltetledleltellxceltedlelt
"""
class ILinkAnnotator(Interface):
"""
An L{ILinkAnnotator} provides annotations for links from one
L{imaginary.idea.Idea} to another.
"""
def annotationsFor(link, idea):
"""
Produce an iterator of annotations to be applied to a link whose source
or target is the L{Idea} that this L{ILinkAnnotator} has been applied
to.
@param idea: The L{Idea} instance
"""
class ILocationLinkAnnotator(Interface):
"""
L{ILocationLinkAnnotator} is a powerup interface to allow powerups for a
L{Thing} to act as L{ILinkAnnotator}s for every L{Thing} contained within
it. This allows area-effect link annotators to be implemented simply,
without needing to monitor movement.
"""
def annotationsFor(link, idea):
"""
Produce an iterator of annotations to be applied to a link whose source
or target is an L{Idea} of a L{Thing} contained in the L{Thing} that
this L{ILocationLinkAnnotator} has been applied to.
"""
class IRetriever(Interface):
"""
An L{IRetriever} examines a L{Path} and retrieves a desirable object from
it to yield from L{Idea.obtain}, if the L{Path} is suitable.
Every L{IRetriever} has a different definition of suitability; you should
examine some of their implementations for more detail.
"""
def retrieve(path):
"""
Return the suitable object described by C{path}, or None if the path is
unsuitable for this retriever's purposes.
"""
def shouldKeepGoing(path):
"""
Inspect a L{Path}. True if it should be searched, False if not.
"""
def objectionsTo(path, result):
"""
@return: an iterator of IWhyNot, if you object to this result being
yielded.
"""
class IContainmentRelationship(Interface):
"""
Indicate the containment of one idea within another, via a link.
This is an annotation interface, used to annotate L{iimaginary.idea.Link}s
to specify that the relationship between linked objects is one of
containment. In other words, the presence of an
L{IContainmentRelationship} annotation on a L{iimaginary.idea.Link}
indicates that the target of that link is contained by the source of that
link.
"""
containedBy = Attribute(
"""
A reference to the L{IContainer} which contains the target of the link
that this L{IContainmentRelationship} annotates.
""")
class ILocationRelationship(Interface):
"""
The inverse of L{IContainmentRelationship}.
"""
class IVisible(Interface):
"""
A thing which can be seen.
"""
def visualize():
"""
Return an IConcept which represents the visible aspects of this
visible thing.
TODO: replace entirely with visualizeWithConcepts.
"""
def visualizeWithContents(pathsToContents):
"""
Return an IConcept which represents the visible aspects of this visible
thing, but with the given list of L{imaginary.idea.Path} objects
pointing at paths which continue on past this L{IVisible}.
"""
def isViewOf(thing):
"""
Is this L{IVisible} a view of a given L{Thing}?
@rtype: L{bool}
"""
class ILightSource(Interface):
"""
Powerup interface for things which emit photons in measurable quantities.
"""
candelas = Attribute("""
The luminous intensity in candelas.
See U{http://en.wikipedia.org/wiki/Candela}.
""")
####### Below here is new, experimental stuff, which doesn't really work yet.
class IThingPowerUp(Interface):
"""
Utility super-interface of all interfaces which are designed to be used as
arguments to powerUp for Thing.
Objects which provide this interface must also provide IItem, obviously, as
only Items can be Powerups.
"""
class IClothingWearer(IThingPowerUp):
"""
A person who can wear clothing.
"""
def putOn(garment):
"""
Put on an article of clothing.
@param garment: An article of clothing.
@type garment: L{IClothing} provider
@raise: L{TooBulky}, if the new article of clothing will not fit
because this wearer is already wearing a bulkier article of clothing in
that slot.
"""
def takeOff(garment):
"""
Remove an article of clothing.
@param garment: An article of clothing that this wearer is wearing.
@raise: L{InaccessibleGarment}: if the article of clothing is either
not being worn, or is being worn beneath another article of clothing
which must be removed first.
"""
class IClothing(IThingPowerUp):
"""
A piece of clothing which can be worn by an L{IClothingWearer}.
"""
garmentSlots = Attribute(
"""
A list of unicode strings that describe the parts of the body where
this article of clothing can be worn, taken from the list of constants
in L{imaginary.garments.GARMENT_SLOTS}.
""")
bulk = Attribute(
"""
An integer, 1 or greater, abstractly describing how thick this garment
is. A bulkier garment cannot be worn over a less bulky one.
""")
def nowWornBy(wearer):
"""
This article of clothing is now being worn by C{wearer}.
@param wearer: The wearer of the clothing.
@type wearer: L{IClothingWearer}
"""
def noLongerWorn():
"""
This article of clothing is no longer being worn.
"""
class ISittable(Interface):
"""
Something you can sit on.
"""
def seat(sitterThing):
"""
@param sitterThing: The person sitting down on this sittable surface.
@type sitterThing: L{imaginary.objects.Thing}
"""
class IWhyNot(Interface):
"""
This interface is an idea link annotation interface, designed to be applied
by L{ILinkAnnotator}s, that indicates a reason why a given path cannot
yield a provider. This is respected by L{imaginary.idea.ProviderOf}.
"""
def tellMeWhyNot():
"""
Return something adaptable to L{IConcept}, that explains why this link
is unsuitable for producing results. For example, the string "It's too
dark in here."
"""
class IDistance(Interface):
"""
A link annotation that provides a distance.
"""
distance = Attribute("floating point, distance in meters")
class IElectromagneticMedium(Interface):
"""
A medium through which electromagnetic radiation may or may not pass; used
as a link annotation.
"""
def isOpaque(observer):
"""
Will this propagate radiation the visible spectrum?
@param observer: The L{Thing} which has eyeballs which are shooting out
electromagnetic radiation which could lead to reflected perceptrons
to let the L{Thing} perceive a target.
@type observer: L{Thing}
@note: This interface has a problem. C{observer} should probably
provide some kind of C{ISpectrum} interface and the implementation
of L{IElectromagneticMedium} should consult methods of that to
determine the frequency that is relevant, etc. Also, the basic
perception interface should probably be something else. Batman
might use echo-location to perceive his environment (he's a bat,
right?) so "look at Alice" shouldn't require that he can "see"
Alice via L{IElectromagneticMedium} - he can "see" her via
L{ISoundMedium} or whatever.
"""