| | 1 | ================================ VERSION 3.0 ================================= |
| | 2 | |
| | 3 | ================================ VERSION 3.1 ================================= |
| | 4 | |
| | 5 | **** COMMITTED CHANGES - the following are changes that we've |
| | 6 | definitely decided to make, but which we've deferred until after the |
| | 7 | next general release because it's too late in the current release |
| | 8 | cycle to make changes that could be potentially destabilizing. |
| | 9 | |
| | 10 | 1. In DefineTAction(TravelVia), remove the override for |
| | 11 | getCurrentObjects. This override was originally put in place because |
| | 12 | the direct object of a TravelVia is a TravelConnector, which was at |
| | 13 | one time based on 'object' rather than on Thing. TravelConnector is |
| | 14 | now a Thing subclass, so the direct object of a TravelVia will always |
| | 15 | be a simulation object, hence it's fine to return it as an action |
| | 16 | object. |
| | 17 | |
| | 18 | 2. In RestrictedUnderside, RestrictedRearContainer, and |
| | 19 | RestrictedRearSurface, the iobjFor(PutXxx) check() routine should call |
| | 20 | "inherited Xxx()" (where Xxx is the unrestricted base class - the |
| | 21 | *second* base class in the RestrictedXxx superclass list) to enforce |
| | 22 | the inherited allowPutXxx restriction. However, we can't just add the |
| | 23 | 'inherited' call: each of the base container check() methods does a |
| | 24 | simple reportFailure(&cannotPutXxxMsg), whereas the Restricted classes |
| | 25 | invoke self.cannotPutXxxMsg(dobj) - same method, different argument |
| | 26 | list. We either need to rename the cannotPutXxxMsg(dobj) version of |
| | 27 | the method, OR add the dobj parameter to the parameter-less form of |
| | 28 | the method. In either case, we could break some existing code. I |
| | 29 | think the renaming approach is probably better, because there really |
| | 30 | are two failure conditions here. |
| | 31 | |
| | 32 | The lowest impact approach is probably to change cannotPutXxxMsg(obj) |
| | 33 | to something like cannotPutObjXxxMsg(obj) for all of the various |
| | 34 | RestrictedXxx containers. This will necessitate a little global |
| | 35 | search and replace for existing code, but the alternative is to change |
| | 36 | some much lower level code: the no-parameter cannotPutXxxMsg messages |
| | 37 | are used in Thing, for instance. |
| | 38 | |
| | 39 | |
| | 40 | |
| | 41 | **** REQUESTED CHANGES - the following are changes that have been |
| | 42 | suggested and possibly discussed to some extent on the tads3 mailing |
| | 43 | list, but whose final disposition is yet to be determined. We might |
| | 44 | or might not ultimately decide to add any of these features. |
| | 45 | Priorities, feasibility, and full design details are yet to be |
| | 46 | determined for many of these. |
| | 47 | |
| | 48 | |
| | 49 | --------- |
| | 50 | |
| | 51 | From Eric Eve <eric.eve@harris-manchester.oxford.ac.uk>: In the adv3 |
| | 52 | command execution sequence, the relative ordering of the "check" phase |
| | 53 | and the various "before" notifications seems wrong as it currently |
| | 54 | stands. Currently, "before" happens before "check" - this means that |
| | 55 | the command can fail in the "check" after the "before" notifications |
| | 56 | have occurred. Conceptually, it seems like "before" notifications |
| | 57 | should only occur after the action is committed. Now, this isn't |
| | 58 | truly possible in practice, because any individual "before" handler |
| | 59 | could still cancel the whole command - the relative ordering of |
| | 60 | "before" handlers is arbitrary, so a "before" handler could cancel the |
| | 61 | action after other "before" handlers already ran. Nonetheless, it |
| | 62 | probably makes sense to consider "check" as a stronger sort of test |
| | 63 | than "before"; it's relatively rare for "before" handlers to cancel a |
| | 64 | command, while testing for cancel-worthy conditions is the whole point |
| | 65 | of "check." |
| | 66 | |
| | 67 | Implementing this change is trivial. The only reason for putting this |
| | 68 | off until 3.1 is that it seems likely to break a small amount of |
| | 69 | existing code - most code won't be affected at all, but there will |
| | 70 | probably be a few existing cases that depend subtly on the existing |
| | 71 | ordering due to interactions among multiple objects and/or handlers. |
| | 72 | People are more likely to expect and accept this sort of compatibility |
| | 73 | risk in a major update. |
| | 74 | |
| | 75 | |
| | 76 | --------- |
| | 77 | |
| | 78 | From Michel Nizette <mnizette@ulb.ac.be> [9/1/06]: |
| | 79 | |
| | 80 | Would there be any value in implementing pending conversations via |
| | 81 | regular AgendaItems, like we just did for the boredom handling? |
| | 82 | Again, the advantage would be simply to provide more control over the |
| | 83 | order of priority of an actor's various pending activities. Also, |
| | 84 | should this prove useful, we could then think of adding an |
| | 85 | Actor.scheduleConditionalInitiateConversation method, which would do |
| | 86 | the same thing as scheduleInitiateConversation, but would additionally |
| | 87 | specify an extra condition to be checked as part of the AgendaItem's |
| | 88 | isReady property. The condition could be specified simply as a |
| | 89 | callback function that returns nil or true. |
| | 90 | |
| | 91 | --------- |
| | 92 | |
| | 93 | From Michel Nizette <mnizette@ulb.ac.be> [9/1/06]: |
| | 94 | |
| | 95 | Currently, the method Actor.noteConversation is invoked each time a |
| | 96 | TopicEntry is triggered, no matter if the topic is conversational or |
| | 97 | not (i.e., marked with isConversational = true or nil). The |
| | 98 | noteConversation method does a number of things like marking the actor |
| | 99 | as having conversed on the current turn, and resetting the boredom |
| | 100 | count. But I'm wondering if this is really what we want in the case |
| | 101 | of a non-conversational topic, whose typical purpose is to describe a |
| | 102 | refusal to engage in a particular conversation. If the conversation |
| | 103 | doesn't actually take place, should there really be any effect on the |
| | 104 | NPC's behavior? It would seem a little odd to see an NPC delaying a |
| | 105 | pending conversation for several turns just because he was involved in |
| | 106 | a sequence of conversational commands that all got blocked in the PC's |
| | 107 | mind for motivational reasons and didn't result in a real dialogue, |
| | 108 | for example. |
| | 109 | |
| | 110 | In contrast, noteConversation also does a few things that |
| | 111 | unquestionably make sense for non-conversational topics, like updating |
| | 112 | pronouns and establishing a default interlocutor: these are interface |
| | 113 | features independent of what happens in the game world. |
| | 114 | |
| | 115 | So, my suggestion would be to distinguish between the situation where |
| | 116 | a conversation actually takes place (in which case it makes sense to |
| | 117 | call noteConversation) and the situation where the conversation is |
| | 118 | only attempted, which we could handle via a new |
| | 119 | noteAttemptedConversation method that would only set pronouns and the |
| | 120 | default interlocutor. The library could then simply invoke |
| | 121 | noteAttemptedConversation everywhere it currently invokes |
| | 122 | noteConversation, and could additionally call noteConversation in the |
| | 123 | few places where a real conversation takes place, like the triggering |
| | 124 | of a conversational topic or a call to Actor.initiateConversation. If |
| | 125 | maintaining backwards compatibility is important, we could have |
| | 126 | noteAttemptedConversation call noteConversation by default, but we |
| | 127 | could make this controllable via a switch. |
| | 128 | |
| | 129 | --------- |
| | 130 | |
| | 131 | From Krister Fundin <fundin@yahoo.com> [9/3/06]: |
| | 132 | |
| | 133 | > bob, follow me |
| | 134 | "Okay, I will follow you." |
| | 135 | > g |
| | 136 | "Okay, I will follow you." |
| | 137 | |
| | 138 | The library comments suggest that the latter should not happen. The |
| | 139 | problem is that Actor.nonIdleTurn() cancels following whenever the |
| | 140 | actor receives an order. Thus the check for already being in following |
| | 141 | mode is always negative. |
| | 142 | |
| | 143 | [mjr: I actually think the current behavior as shown is fairly |
| | 144 | reasonable. It would be marginally better to have a message like "I'm |
| | 145 | already following you." But short of repeating the command ten times |
| | 146 | in a row for comic effect, I'm not sure players would ever be bothered |
| | 147 | by the present behavior in practice - if they're repeating the command, |
| | 148 | they're presumably not sure that Bob was already in follow mode, so |
| | 149 | they're not going to be bothered by another acknowledgment.] |
| | 150 | |
| | 151 | I had already removed this cancellation from one of my projects, since |
| | 152 | it wasn't appropriate there, but that seems to cause a different |
| | 153 | problem instead: |
| | 154 | |
| | 155 | > bob, follow me |
| | 156 | "Okay, I will follow you." |
| | 157 | > g |
| | 158 | Nothing obvious happens. |
| | 159 | |
| | 160 | In this case, Bob can already see the player and thus only makes a |
| | 161 | call to checkMovingActorInto(), which does nothing since he's already |
| | 162 | where he's supposed to be. |
| | 163 | |
| | 164 | [mjr: This part is worth looking into whatever the disposition of the |
| | 165 | first part, since the nonIdleTurn() comment suggests that it's okay to |
| | 166 | override and not cancel follow mode, but apparently that's |
| | 167 | problematic.] |
| | 168 | |
| | 169 | --------- |
| | 170 | |
| | 171 | From Krister Fundin <fundin@yahoo.com> [9/3/06]: |
| | 172 | |
| | 173 | just a thought (feel free to ignore it): |
| | 174 | |
| | 175 | > bob, follow me |
| | 176 | "Okay, I will follow you." Bob stands on the main platform. |
| | 177 | |
| | 178 | versus: |
| | 179 | |
| | 180 | > bob, follow me |
| | 181 | Bob stands on the main platform. "Okay, I will follow you." |
| | 182 | |
| | 183 | Doesn't the latter sound marginally better? |
| | 184 | |
| | 185 | [mjr: I'm not sure about this; Bob acknowledging first actually strikes |
| | 186 | me at the moment as the better sounding version] |
| | 187 | |
| | 188 | I guess in practice there would be a command separator between the two |
| | 189 | reports, but that could actually improve things when two or more |
| | 190 | implicit actions are needed: |
| | 191 | |
| | 192 | > bob, follow me |
| | 193 | "Okay, I will follow you." Bob stands on the main platform. |
| | 194 | |
| | 195 | Bob stands on the red platform. |
| | 196 | |
| | 197 | versus: |
| | 198 | |
| | 199 | > bob, follow me |
| | 200 | Bob stands on the main platform. |
| | 201 | |
| | 202 | Bob stands on the red platform. |
| | 203 | |
| | 204 | "Okay, I will follow you." |
| | 205 | |
| | 206 | --------- |
| | 207 | |
| | 208 | From Krister Fundin <fundin@yahoo.com> [9/8/06]: |
| | 209 | |
| | 210 | How about adding a command joiner report and corresponding pseudo-tag? |
| | 211 | Any combination of joiner and separator or separator and joiner would |
| | 212 | just remove both. I can think of a few other places where I could use |
| | 213 | one of those. |
| | 214 | |
| | 215 | [i.e., something like <.commandjoin> to cancel out <.commandsep>] |
| | 216 | |
| | 217 | --------- |
| | 218 | |
| | 219 | From Krister Fundin <fundin@yahoo.com> [9/13/06]: |
| | 220 | |
| | 221 | > push television into white box |
| | 222 | (first standing on the main platform) |
| | 223 | Okay, you're now standing in the white box. |
| | 224 | |
| | 225 | Perhaps it's not serious enough that it needs to be fixed now. In the |
| | 226 | long run, one could imagine a "first trying to push the television |
| | 227 | onto the main platform" type of implicit action here, but that would |
| | 228 | take some work. |
| | 229 | |
| | 230 | [I'm not even sure what would be right here. I guess some |
| | 231 | fine-grained variation of PushTravel for pushing into nested rooms or |
| | 232 | something.] |
| | 233 | |
| | 234 | --------- |
| | 235 | |
| | 236 | The ThingState mechanism should be extended to allow a single object |
| | 237 | to have multiple current, active states. For example, it should be |
| | 238 | possible for an object to be simultaneously worn, lit, and open. As |
| | 239 | it stands, an object can only have one state at a time. |
| | 240 | |
| | 241 | On the parsing side, this is straightforward: we simply have to |
| | 242 | iterate over the list of active states and consider each one's token |
| | 243 | selectivity in turn. All active states must rule positively on the |
| | 244 | token list to allow a match. |
| | 245 | |
| | 246 | On the output side, it's slightly more work. We'll have to list |
| | 247 | multiple states in messages like "(providing light)", which isn't |
| | 248 | hard: "(worn, open, providing light)". We'll also have to list |
| | 249 | multiple states in sublists: "one worn, open, and providing light; one |
| | 250 | closed." This is also relatively straightforward except that we'll |
| | 251 | have to go to semicolons in the overall list when the sublists start |
| | 252 | expanding out to the point that they need commas. |
| | 253 | |
| | 254 | (Ideally, ThingState probably ought to be the *only* state mechanism |
| | 255 | for any sort of state for which it's used. For example, there really |
| | 256 | shouldn't be a separate isOpen property - an object's open/closed |
| | 257 | status instead ought to be determined by the presence of an openState |
| | 258 | or closedState object in the object's state vector. Opening an object |
| | 259 | would then be a matter of calling obj.setState(openState). This would |
| | 260 | require that each ThingState know its set of states, for mutual |
| | 261 | exclusion when a new state is set (which would simplify things |
| | 262 | slightly in that we'd be able to dispense with the per-object |
| | 263 | allStates list.) |
| | 264 | |
| | 265 | (Also ideally, ThingState sets would implicitly and automatically |
| | 266 | create distinguishers. Any time we have a pair of objects with |
| | 267 | different active states from a given set, we should automatically be |
| | 268 | able to distinguish those objects on the basis of that ThingState |
| | 269 | set.) |
| | 270 | |
| | 271 | --------- |
| | 272 | |
| | 273 | In announceAmbigActionObject, announceMultiActionObject, and |
| | 274 | announceDefaultObject, it'd be ideal to use distinguishers to decide |
| | 275 | on the object name. This would ensure, for example, that we generate |
| | 276 | "(the lit candle)" when litness would distinguish the candle we chose |
| | 277 | from others present. |
| | 278 | |
| | 279 | I think we'd basically have to do the same work that |
| | 280 | BasicResolveResults.ambiguousNounPhrase does to choose a distinguisher, |
| | 281 | but in this case we want to tell apart the object in question from |
| | 282 | everything else in scope that has the same name. So, probably we want |
| | 283 | to go through the objects in scope and build a list of everything |
| | 284 | with the same disambigName; then we want to search for a distinguisher |
| | 285 | that can tell apart the object in question from all of those other |
| | 286 | objects; failing that, we could settle for a distinguisher that can |
| | 287 | tell apart the object in question from any of the other objects, as |
| | 288 | this would provide at least some greater specificity. Failing that, |
| | 289 | we just use the disambigName. |
| | 290 | |
| | 291 | --------- |
| | 292 | |
| | 293 | Consider removing several modules from adv3 and putting them in the |
| | 294 | "system" library instead: |
| | 295 | |
| | 296 | banner.t |
| | 297 | menusys.t |
| | 298 | modid.t |
| | 299 | numbers.t |
| | 300 | |
| | 301 | --------- |
| | 302 | |
| | 303 | Add a property setting that makes the contents of an item listed |
| | 304 | immediately after the item's specialDesc, if it's using a specialDesc. |
| | 305 | The contents would be listed there rather than in the miscellaneous |
| | 306 | portable list paragraph. |
| | 307 | |
| | 308 | --------- |
| | 309 | |
| | 310 | Add static File.deleteFile(filename), to delete a file from disk. |
| | 311 | This operation should require WRITE privileges (as determined by the |
| | 312 | file safety level setting) for the given file. |
| | 313 | |
| | 314 | --------- |
| | 315 | |
| | 316 | Add a version of saveGame() and restoreGame() that use a ByteArray as |
| | 317 | the saved state medium. save() would create and return a new |
| | 318 | ByteArray object containing the saved state (which must, of course, |
| | 319 | exclude the new ByteArray itself); restore() would take a ByteArray |
| | 320 | object and restore its contents as the current game state. |
| | 321 | |
| | 322 | For interface purposes, we could simply let saveGame() without an |
| | 323 | argument return a ByteArray, and restoreGame() could take a ByteArray |
| | 324 | as its argument. Alternatively, saveGame() could take a pre-created |
| | 325 | ByteArray as its argument, and we'd fill in that ByteArray with the |
| | 326 | saved state (but we'd somehow have to make sure that this ByteArray |
| | 327 | was excluded from the saved state). |
| | 328 | |
| | 329 | (Implementation note: this is fairly straightforward, but some work. |
| | 330 | We first have to refactor CVmFileSave::save() and restore() to take |
| | 331 | CVmStream objects instead of CVmFile objects; this is fairly simple, |
| | 332 | but requires changes down through the whole save/restore stacks, which |
| | 333 | touch a lot of objects. Then, where we currently call save/restore |
| | 334 | with CVmFile objects, wrap the CVmFile in a CVmFileStream and make the |
| | 335 | same call. Finally, we'd have to create a new CVmStream subclass that |
| | 336 | operates on ByteArray objects, which should be a fairly easy mapping. |
| | 337 | It'd be worth making sure that changing CVmFile to CVmStream in |
| | 338 | save/restore doesn't balloon up the executable too much - this would |
| | 339 | introduce a ton of new virtual calls, because all of the CVmStream |
| | 340 | methods are virtual while the corresponding CVmFile methods are |
| | 341 | non-virtual. It'd also be worth measuring any degradation in |
| | 342 | save/restore performance from all the new virtual calls.) |
| | 343 | |
| | 344 | --------- |
| | 345 | |
| | 346 | Store ALL objects as potential future pronoun antecedents when |
| | 347 | processing a multi-object command. For example, when executing UNLOCK |
| | 348 | DOOR WITH KEY, store *both* the door and the key as potential |
| | 349 | antecedents for 'it'. On using 'it' in a future command, look at all |
| | 350 | of the possible antecedents, and choose the most logical one - |
| | 351 | essentially treat the set of possible antecedents as though it were |
| | 352 | the set of matching objects for an indefinite noun phrase. If there's |
| | 353 | more than one equally logical choice, choose one arbitrarily, the same |
| | 354 | way we would if the player typed TAKE ANY BOOK: |
| | 355 | |
| | 356 | >unlock door with key |
| | 357 | Unlocked. |
| | 358 | |
| | 359 | >drop it |
| | 360 | Dropped. // refers to the key, since the door is illogical for DROP |
| | 361 | |
| | 362 | ...but if the next command were this instead: |
| | 363 | |
| | 364 | >open it |
| | 365 | Opened. // refers to the door, since the key is illogical for OPEN |
| | 366 | |
| | 367 | ...and if it were this instead: |
| | 368 | |
| | 369 | >x it |
| | 370 | (the door) // both door and key are equally logical, so choose |
| | 371 | It's a massive... // one arbitrarily, and report the choice |
| | 372 | |
| | 373 | --------- |
| | 374 | |
| | 375 | Debugger: display the current method (the top of the stack trace) in |
| | 376 | the status line. |
| | 377 | |
| | 378 | --------- |
| | 379 | |
| | 380 | Debugger: add an option to skip over library code when stepping. This |
| | 381 | could be a mode, or it could be a new 'step' command (alongside 'step |
| | 382 | in', 'step over', 'step out'). This would make it easier to isolate a |
| | 383 | bug in game code, and it would also clarify the control flow through |
| | 384 | the sequence of game-provided entrypoints. |
| | 385 | |
| | 386 | The main question in implementing this is how exactly to define |
| | 387 | 'library' code for the purposes of this new form of stepping. Library |
| | 388 | code could be defined as anything from a .tl file, or as any source |
| | 389 | file under the system directory tree, or as files designated as such |
| | 390 | manually (with a right-click on a file in the project tree, say). |
| | 391 | |
| | 392 | --------- |
| | 393 | |
| | 394 | Can we rephrase the UNDO command results so that they reflect the logical |
| | 395 | command rather than the actual text typed in? Something like: |
| | 396 | |
| | 397 | >undo |
| | 398 | Taking back: attack the troll with the sword |
| | 399 | |
| | 400 | (For commands involving multiple objects, this could get cumbersome, since |
| | 401 | we probably wouldn't be able to easily reconstruct the original ALL or |
| | 402 | plural usage, but would instead list all of the objects.) |
| | 403 | |
| | 404 | --------- |
| | 405 | |
| | 406 | Maybe use <.Q> rather than <Q> in msg_neu and en_us, to allow customization |
| | 407 | of quote styles. |
| | 408 | |
| | 409 | --------- |
| | 410 | |
| | 411 | Add a global compiler options setting to workbench? |
| | 412 | |
| | 413 | --------- |
| | 414 | |
| | 415 | The compiler should flag some types of errors at the location of the |
| | 416 | particular token causing the error, rather than at the location of |
| | 417 | the enclosing source line. Krister Fundin <fundin@yahoo.com> pointed |
| | 418 | this out with respect to an expression like this, where the 'method' |
| | 419 | name is undefined: |
| | 420 | |
| | 421 | property.method(new function(x) |
| | 422 | { |
| | 423 | code; |
| | 424 | }); |
| | 425 | |
| | 426 | The error ought to be reported at the exact line containing the |
| | 427 | undefined token 'method', rather than at the statement level - which, |
| | 428 | in this case, reports at the *end* of the nested function. |
| | 429 | |
| | 430 | To accomplish this, the compiler would have to (a) save the source |
| | 431 | location for each individual token, in addition to the location of the |
| | 432 | current statement; and (b) use the token location rather than the |
| | 433 | current line when reporting any error that can be associated with a |
| | 434 | token. [The second part is probably pretty easy - almost all such |
| | 435 | cases can probably be caught by changing |
| | 436 | CTcTokenizer::log_error_curtok() to report at the current token |
| | 437 | position. Saving the token position probably isn't too hard, either, |
| | 438 | but there might be some subleties, such as with respect to the |
| | 439 | preprocessor.] |
| | 440 | |
| | 441 | --------- |
| | 442 | |
| | 443 | Add a general "warning check" mechanism to the library. This would |
| | 444 | be an add-on module that would encapsulate a bunch of checks for |
| | 445 | common pitfalls, using reflection mechanisms to display warnings. |
| | 446 | Authors could optionally compile with this module from time to time |
| | 447 | to check for common errors. This would only be of interest to |
| | 448 | authors during development, of course - it would always be removed |
| | 449 | for the final build for release. (This came up in the context of |
| | 450 | checking for default Container objects when using the liquid add-on |
| | 451 | library. It would be nice to have a generic mechanism, or at least |
| | 452 | some coding patterns to follow, that would simplify adding this kind |
| | 453 | of warning check.) |
| | 454 | |
| | 455 | --------- |
| | 456 | |
| | 457 | For ResolvedTopic, it might be nice to ask for disambiguation for |
| | 458 | in-scope objects if there are more than one, especially if this were |
| | 459 | parameterized. |
| | 460 | |
| | 461 | --------- |
| | 462 | |
| | 463 | Menu-based conversation framework |
| | 464 | |
| | 465 | --------- |
| | 466 | |
| | 467 | For travel, add a check to see if the actor fits through the passage. |
| | 468 | This should ideally be implemented using a precondition that would |
| | 469 | check the actor's bulk to ensure it's below a maximum. The actor |
| | 470 | would have a chance to reduce its bulk via a tryReducingBulk call, |
| | 471 | perhaps - this would let the actor do something obvious like close |
| | 472 | an umbrella. |
| | 473 | |
| | 474 | --------- |
| | 475 | |
| | 476 | For travel, add a check for excessive encumbering bulk or weight. |
| | 477 | This could be used for things like climbing ropes or walking across a |
| | 478 | rickety bridge. For excessive encumbrance, the actor could use the |
| | 479 | standard tryMakingRoomToHold mechanism to reduce held bulk. For |
| | 480 | excessive weight, this is probably just a connector-specific check |
| | 481 | and message on failure, so there might not be much we can do in the |
| | 482 | library other than make sure the framework is in place. |
| | 483 | |
| | 484 | --------- |
| | 485 | |
| | 486 | Weapons (attack with) |
| | 487 | |
| | 488 | --------- |
| | 489 | |
| | 490 | flammability as a mix-in (for paper, wood, and the like) |
| | 491 | |
| | 492 | --------- |
| | 493 | |
| | 494 | Liquids and bottles |
| | 495 | |
| | 496 | --------- |
| | 497 | |
| | 498 | Drinkables (special kind of liquid) |
| | 499 | |
| | 500 | --------- |
| | 501 | |
| | 502 | Probably need a variation of objHeld for liquids that lets us specify |
| | 503 | that we need to be holding the container of the liquid (for 'pour', |
| | 504 | for example). containerHeld? Or, we could do like we do with keys |
| | 505 | on keyrings, and consider a liquid within a container being held to |
| | 506 | be itself held. |
| | 507 | |
| | 508 | --------- |
| | 509 | |
| | 510 | If we have a bucket or some other container filled with liquid, and |
| | 511 | we try to put another item into the bucket, we should run a special |
| | 512 | check for wetting the item. The default should probably just be to |
| | 513 | say "you don't want to get that wet" and refuse the command. |
| | 514 | Similarly, "fill bucket" when the bucket contains non-liquid objects |
| | 515 | should run the same checks. |
| | 516 | |
| | 517 | --------- |
| | 518 | |
| | 519 | Burning and liquids: wetting a burning item with a non-flammable |
| | 520 | liquid should extinguish the flame. |
| | 521 | |
| | 522 | --------- |
| | 523 | |
| | 524 | Burning and liquids: provide a flammable liquid class. This should |
| | 525 | consume the liquid at a particular rate while burning. The basic |
| | 526 | flammable fuel consumption mechanism should be used here if possible. |
| | 527 | |
| | 528 | --------- |
| | 529 | |
| | 530 | Provide an extensible "chemistry" framework for combining different |
| | 531 | kinds of liquids. |
| | 532 | |
| | 533 | Perhaps we could define an object to represent the operation of |
| | 534 | combining each pair of liquid classes - this class doesn't represent |
| | 535 | the mixture, but rather the operation of mixing. So, for example, we |
| | 536 | define an object for the operation of mixing water and liquid |
| | 537 | nitrogen. |
| | 538 | |
| | 539 | Each of mixing object must define a method that is called when its |
| | 540 | pair of liquids are mixed; this method encapsulates the action of |
| | 541 | mixing the liquids. Use a global lookup table of these objects, |
| | 542 | keyed by liquid class pairs; when we want to mix two liquids, we look |
| | 543 | up the pair of liquid classes to be mixed and find the mixer object, |
| | 544 | then call its mixer method. |
| | 545 | |
| | 546 | --------- |
| | 547 | |
| | 548 | Putting a container with liquid into another container in inventory |
| | 549 | should have some conditions attached. First, an openable liquid |
| | 550 | container should be closed whenever moving it into a sub-container |
| | 551 | (so a bottle of water should be closed before putting it in your |
| | 552 | backpack) to avoid spilling. Second, a container that can't be |
| | 553 | closed should probably not be allowed to be moved to a sub-container. |
| | 554 | Third, taking or otherwise moving any item should probably be |
| | 555 | disallowed if the item contains an open container of liquid. |
| | 556 | |
| | 557 | --------- |
| | 558 | |
| | 559 | Pouring a liquid onto the floor, into a container that isn't set up |
| | 560 | as a container for liquids, onto a surface, or onto an arbitrary |
| | 561 | object should all do something reasonable. It's unclear what to do |
| | 562 | in general; the easiest thing would be for the liquid evaporate |
| | 563 | immediately, but that's not all that realistic. Pouring liquid onto |
| | 564 | an arbitrary object should probably call the object's |
| | 565 | notify-getting-wet method, just as though the object were in a |
| | 566 | container being filled (although we might want to differentiate |
| | 567 | between submerging and dousing, maybe just via a flag to the |
| | 568 | notify-getting-wet method), but then something needs to happen to the |
| | 569 | liquid, even if the thing that happens is simply evaporation. |
| | 570 | |
| | 571 | --------- |
| | 572 | |
| | 573 | Leaky containers: when a leaky container has liquid added, it should |
| | 574 | set up a daemon to cause it to leak slowly. The liquid leaking out |
| | 575 | should do whatever liquid does when poured into the leaky object's |
| | 576 | enclosing location. |
| | 577 | |
| | 578 | --------- |
| | 579 | |
| | 580 | Absorbent materials: these should retain a quantity of liquid as |
| | 581 | though they were a liquid container, allowing the absorbed liquid to |
| | 582 | be recovered by squeezing the object. |
| | 583 | |
| | 584 | --------- |
| | 585 | |
| | 586 | Ropes, to the extent they can be generalized |
| | 587 | |
| | 588 | --------- |
| | 589 | |
| | 590 | In two-object actions (including topic and literal actions), when |
| | 591 | both objects are missing, we might want to consider some alternative |
| | 592 | way to specify which object we ask for first. This might vary |
| | 593 | by language, so it might be better to have an action property that |
| | 594 | specifies which goes first when both are missing. Or maybe this is |
| | 595 | the same as resolution order, or opposite of the resolution order, |
| | 596 | or something. |
| | 597 | |
| | 598 | --------- |
| | 599 | |
| | 600 | OBJECTS command - list all portable objects that have been seen, and |
| | 601 | the last location where they were seen |
| | 602 | |
| | 603 | --------- |
| | 604 | |
| | 605 | It seems like it should be possible to talk to an actor in situations |
| | 606 | when the actor is audible only, such as in the dark. But how do we |
| | 607 | initiate contact if we don't see the actor? |
| | 608 | |
| | 609 | 1 - broadcast a greeting: |
| | 610 | |
| | 611 | >hello |
| | 612 | Bob says, "Is that you? I can't see anything." |
| | 613 | |
| | 614 | 2 - try talking to a specific actor: |
| | 615 | |
| | 616 | >bob, hello |
| | 617 | |
| | 618 | This doesn't seem very good because we need to have a way of knowing |
| | 619 | Bob is present in order to talk to him. |
| | 620 | |
| | 621 | 3 - the actor hears you come in and says he's present: |
| | 622 | |
| | 623 | >n |
| | 624 | In the Dark |
| | 625 | It's pitch black. |
| | 626 | |
| | 627 | In the darkness, you hear Bob. "Is that you?" he asks. |
| | 628 | |
| | 629 | --------- |
| | 630 | |
| | 631 | Add new syntax: |
| | 632 | |
| | 633 | x.delegated y.prop(args) |
| | 634 | |
| | 635 | This syntax explicitly targets 'y' with self as 'x'. This differs from |
| | 636 | the regular 'delegated y.prop()' in that the latter can only set 'self' |
| | 637 | in the delgatee to 'self' in the delegating method. |
| | 638 | |
| | 639 | Or, perhaps we could take this one step further, by allowing the |
| | 640 | target object, self, *and* defining object to be independently specified. |
| | 641 | Something like |
| | 642 | |
| | 643 | x.delegated y.inherited z.prop(args) |
| | 644 | |
| | 645 | |
| | 646 | This would set self to x, target object to y, and defining object to |
| | 647 | z, then try to find the method in z. If the method doesn't exist in |
| | 648 | z, we should probably throw an error rather than try to inherit from |
| | 649 | there, since this is so fully specified already. |
| | 650 | |
| | 651 | We would need a new opcode for each case. The first would require an |
| | 652 | opcode with the new self, the target object, and the target property |
| | 653 | as operands; the second would require those operands plus another for |
| | 654 | the defining object. We could have two variations of each opcode |
| | 655 | (one with a fixed property ID and the other with a property pointer |
| | 656 | operand from the stack), or we could presume that the operands would |
| | 657 | be rarely used and define only the property pointer version (it would |
| | 658 | be less efficient for cases with a static property ID, but if the |
| | 659 | opcode is rarely used, this extra cost shouldn't be important). |
| | 660 | |
| | 661 | --------- |
| | 662 | |
| | 663 | Add a Set intrinsic class? (For unordered lists, optimized for inclusion |
| | 664 | testing: essentially a LookupTable with only the keys.) |
| | 665 | |
| | 666 | --------- |
| | 667 | |
| | 668 | Soft linking: |
| | 669 | |
| | 670 | local x = ifdef(obj); // if obj is linked in, return obj, else nil |
| | 671 | |
| | 672 | replace ifdef obj ... // same as a new object def if obj isn't linked in |
| | 673 | |
| | 674 | modify ifdef obj ... // ignored if obj isn't linked in |
| | 675 | |
| | 676 | --------- |
| | 677 | |
| | 678 | List methods: add compare funcs to more methods: |
| | 679 | - intersect |
| | 680 | - getUnique |
| | 681 | - appendUnique |
| | 682 | |
| | 683 | likewise: |
| | 684 | - Array.getUnique |
| | 685 | - Array.appendUnique |
| | 686 | - Vector.getUnique |
| | 687 | - Vector.appendUnique |
| | 688 | |
| | 689 | --------- |
| | 690 | |
| | 691 | Compiler: equivalent property list syntax: |
| | 692 | |
| | 693 | a, b = foo |
| | 694 | c, d(x) { bar } |
| | 695 | |
| | 696 | --------- |
| | 697 | |
| | 698 | Add items that are by themselves ambiguous? This might be interesting |
| | 699 | for things like PO Boxes: |
| | 700 | |
| | 701 | >look in box |
| | 702 | Which box do you mean, the cardboard box, or a mail box? |
| | 703 | |
| | 704 | >mail box |
| | 705 | Which mail box do you mean? They're numbered from 23000 to 23999. |
| | 706 | |
| | 707 | >23001 |
| | 708 | Box 23001 seems to be locked. |
| | 709 | |
| | 710 | This could be accomplished with an isAmbiguous flag in the object, |
| | 711 | which would be similar in effect to isEquivalent. Whenever the object |
| | 712 | shows up in a list with multiple objects, we use an indefinite article |
| | 713 | with it, as though it were one of a bunch of equivalent items. When |
| | 714 | the object shows up by itself, we ask a single-object disambig |
| | 715 | question, perhaps by calling a method on the object itself - |
| | 716 | disambigPrompt? |
| | 717 | |
| | 718 | Ultimately, matchName() and matchNameDisambig() would be responsible |
| | 719 | for coming up with a specific object based on the noun phrase. |
| | 720 | |
| | 721 | --------- |
| | 722 | |
| | 723 | repeated messages: gather up and group defaults? |
| | 724 | |
| | 725 | --------- |
| | 726 | |
| | 727 | Sound defaults. Add a background sound resource property to each |
| | 728 | object. On each turn, the library scans for all objects in hearing |
| | 729 | range and plays the associated background sound for each one. Could |
| | 730 | have near/far/obscured versions, or maybe simply adjust the volume, |
| | 731 | or whatever. |
| | 732 | |
| | 733 | --------- |
| | 734 | |
| | 735 | Graphics defaults. Add a graphic resource property to each room and |
| | 736 | item. On "look" and "examine", display the associated graphic as |
| | 737 | part of the default room/item description. It would be especially |
| | 738 | nice to have a choice of presentation styles, maybe even including a |
| | 739 | Legend-style split-screen interface (but that might be more |
| | 740 | appropriate as an add-on; even so, it would be good to keep it in |
| | 741 | mind so that the necessary hooks are there for creating a |
| | 742 | Legend-style presentation add-on). |
| | 743 | |
| | 744 | --------- |
| | 745 | |
| | 746 | Add a music soundtrack scheme that simplifies management of background |
| | 747 | music. |
| | 748 | |
| | 749 | --------- |
| | 750 | |
| | 751 | Look into formally structuring things into try/check method pairs |
| | 752 | (checkHolding/tryHolding, etc), to make precondition implementation |
| | 753 | more consistent and clear. This would apply most to the |
| | 754 | preconditions. |
| | 755 | |
| | 756 | --------- |
| | 757 | |
| | 758 | Krister Fundin <fundin@yahoo.com> proposes: The list of end-of-game |
| | 759 | options currently specified as the 'extra' parameter to the |
| | 760 | finishGame() and finishGameMsg() functions could instead be bundled up |
| | 761 | into a FinishType object, so that the message and extra options could |
| | 762 | be specified with just one object parameter. This would make |
| | 763 | game-ending code more concise and also make it easier to reuse an |
| | 764 | option list. |
| | 765 | |
| | 766 | [We'd probably need a new common function that takes a FinishType |
| | 767 | object as its sole parameter; the existing interface functions would |
| | 768 | simply create a dynamic FinishType object to encapsulate the |
| | 769 | parameters and call the new common function.] |
| | 770 | |
| | 771 | --------- |
| | 772 | |
| | 773 | Krister Fundin <fundin@yahoo.com> proposes allowing a list parameter |
| | 774 | in the middle of a parameter list, rather than only as the last |
| | 775 | parameter, as in foo(a, [b], c). The compiler could generate function |
| | 776 | prologue code that moves the trailing arguments into locals, then |
| | 777 | truncates the list created via PUSHPARLST. It's a little inefficient, |
| | 778 | since we just throw away the first list and have to move the locals |
| | 779 | around, but the case is probably sufficiently rare and non-critical |
| | 780 | that this doesn't much matter. |
| | 781 | |
| | 782 | --------- |
| | 783 | |
| | 784 | Andreas Sewe <2003-10-01.as.int-fiction@web.de> proposes that |
| | 785 | 'inherited' in templates should NOT implicitly expand to nothing. As |
| | 786 | it is, 'inherited' in a template expands to each inherited superclass |
| | 787 | template, but also expands to nothing. Andreas suggests that this |
| | 788 | should be changed to eliminate the empty expansion, AND the empty |
| | 789 | template should be allowed, so that it's possible to make inheritors |
| | 790 | explicitly pick up the empty case. [It's not entirely clear to me |
| | 791 | that the empty case really needs to be inherited in the first place, |
| | 792 | but Andreas's point is that it *could* be there to ensure backwards |
| | 793 | compatibility with the existing template structure, which implicitly |
| | 794 | does inherit the empty case.] |
| | 795 | |
| | 796 | --------- |
| | 797 | |
| | 798 | From <2003-10-01.as.int-fiction@web.de>: add a way of naming |
| | 799 | anonymous functions (which would make the name an oxymoron, so |
| | 800 | I suppose we'd have to rename them to "in-line" functions |
| | 801 | or similar). Something like this: |
| | 802 | |
| | 803 | local f = new function factorial(n) { return n > 1 ? n*factorial(n-1) : 1; }; |
| | 804 | |
| | 805 | --------- |
| | 806 | |
| | 807 | From Krister Fundin <fundin@yahoo.com>: add |
| | 808 | TadsObject.forEachSuperclass(); takes a callback function pointer as |
| | 809 | its argument. This would iterate through all of the direct and |
| | 810 | indirect superclasses of an object. (This is readily implementable in |
| | 811 | byte code, but the VM might have the class hierarchy cached, so it |
| | 812 | could iterate through them more efficiently than byte code could.) |
| | 813 | |
| | 814 | --------- |
| | 815 | |
| | 816 | From Robey Holderith <robey@flaminglunchbox.net>: add a new |
| | 817 | Python-style string notation: use three single quotes in a row as |
| | 818 | an alternative single-quoted string delimiter. For example: |
| | 819 | |
| | 820 | '''Sorry, she's not here right now.''' |
| | 821 | |
| | 822 | would be treated as equivalent to |
| | 823 | |
| | 824 | 'Sorry, she\'s not here right now.' |
| | 825 | |
| | 826 | Many people find the Python notation more readable than the present |
| | 827 | backslash notation. The backslash notation would be retained as well; |
| | 828 | this would just be a new, optional variation. (There's some |
| | 829 | compatibility risk, but it's probably negligible - it's hard to come |
| | 830 | up with any example of where a tripled quote like this would be useful |
| | 831 | in the present notation.) |
| | 832 | |
| | 833 | --------- |
| | 834 | |
| | 835 | From steve breslin <steve.breslin@gmail.com>: Add auto-completion |
| | 836 | popup lists and parameter-list tooltips to the editor, a la |
| | 837 | "intellisense" in msft dev env's. Scintilla has native support for |
| | 838 | both of these (the "autocomplete" and "call tips" features), so |
| | 839 | there'd be no UI work; it's just a matter of monitoring keystrokes, |
| | 840 | generating the popup/tip data, and making the appropriate SCI_xxx |
| | 841 | calls to activate the features. |
| | 842 | |
| | 843 | The main difficulty is that TADS is not a statically typed language, |
| | 844 | so it's not possible in principal to generate the popup data that you |
| | 845 | can in a language like C++, where you can determine the type of any |
| | 846 | expression at compile time (and thus at editing time). You could find |
| | 847 | the popup information only when referring directly to an object by |
| | 848 | name. I'm not sure if the result would be useful enough to justify |
| | 849 | the work. |
| | 850 | |
| | 851 | The other complication is that I don't think the compiler currently |
| | 852 | provides any sort of convenient packaging for the sort of information |
| | 853 | you'd need to generate the popups; it would probably require some |
| | 854 | compiler work to generate a little database giving the method list for |
| | 855 | each object. This wouldn't be a ton of work but wouldn't be free. |
| | 856 | |
| | 857 | --------- |
| | 858 | |
| | 859 | From Knight Errant <sigmundvondanzig@gmail.com>: add a spell-checker |
| | 860 | to Workbench. (Could be an on-the-fly spell checker a la Word's |
| | 861 | squiggly underlines, or could be an explicit separate step. I think |
| | 862 | Scintilla has some built-in support for the display part of on-the-fly |
| | 863 | spell checking, so that would probably be the way to go. Spell |
| | 864 | checking would be most useful if it were mode-sensitive, so that, for |
| | 865 | example, the tads3 mode would only check text within strings, and |
| | 866 | exclude HTML substrings. Presumably we could use the style |
| | 867 | information to do this.) |
| | 868 | |
| | 869 | --------- |
| | 870 | |
| | 871 | From Eric Eve <eric.eve@hmc.ox.ac.uk>: It would be quite nice if |
| | 872 | TravelBarrier objects had some easy way to refer to the connector |
| | 873 | through which they're blocking travel in their explainTravelBarrier() |
| | 874 | method. One way to do this might be to add a second parameter to |
| | 875 | TravelBarrier.explainTravelBarrier and then have |
| | 876 | TravelConnector.checkTravelBarriers() call |
| | 877 | explainTravelBarrier(traveler, self) instead of |
| | 878 | explainTravelBarrier(traveler); but this could break a lot of existing |
| | 879 | code. The slightly messier alternative, which might nevertheless |
| | 880 | better preserve backward compatibility would be to add a connector |
| | 881 | property to TravelBarrier and then add cur.connector = self just |
| | 882 | before cur.explainTravelBarrier(traveler) in the foreach(cur in lst) |
| | 883 | loop in TravelConnector.checkTravelBarriers(). |
| | 884 | |
| | 885 | --------- |
| | 886 | |
| | 887 | From Eric Eve <eric.eve@hmc.ox.ac.uk>: At present attempting to travel |
| | 888 | via an AskConnector will always prefer an open door to a closed one |
| | 889 | (without prompting the player to specify which door s/he wants to go |
| | 890 | through). This may be fine as default behaviour but sometimes in might |
| | 891 | be nice to allow the option of always prompting the player to specify |
| | 892 | which door is meant. (I can see that this might be quite fiddly to |
| | 893 | achieve, and if it's too fiddly then by all means don't pursue it; |
| | 894 | it's hardly high priority). |
| | 895 | |
| | 896 | --------- |
| | 897 | |
| | 898 | From Eric Eve <eric.eve@hmc.ox.ac.uk>: IIRC this came up before, but I |
| | 899 | can't remember that it was ever pursued. It would be quite nice if |
| | 900 | ThingMatchTopic could match on a class as well as an object, e.g.: |
| | 901 | |
| | 902 | ThingMatchTopic.matchTopic(fromActor, obj) |
| | 903 | { |
| | 904 | /* |
| | 905 | * if matchObj is a collection, check each element, otherwise |
| | 906 | * just match the single object |
| | 907 | */ |
| | 908 | if (matchObj.ofKind(Collection)) |
| | 909 | { |
| | 910 | /* try matching each object in the list */ |
| | 911 | if (matchObj.indexWhich({cur: obj.ofKind(cur)}) != nil) |
| | 912 | return matchScore; |
| | 913 | } |
| | 914 | else |
| | 915 | { |
| | 916 | /* match the single object */ |
| | 917 | if (obj.ofKind(matchObj)) |
| | 918 | return matchScore; |
| | 919 | } |
| | 920 | |
| | 921 | /* didn't find a match - indicate this by returning a nil score */ |
| | 922 | return nil; |
| | 923 | } |
| | 924 | |
| | 925 | With corresponding changes to ThingMatchTopic.isMatchPossible(). |
| | 926 | |
| | 927 | So far as I can see this shouldn't break any existing code (if |
| | 928 | obj==matchObj then obj.ofKind(matchObj)), but would allow for greater |
| | 929 | flexibility, making it easier to write responses such as: |
| | 930 | |
| | 931 | + GiveShowTopic @Food |
| | 932 | topicResponse() |
| | 933 | { |
| | 934 | gDobj.moveInto(nil); |
| | 935 | "Bob snatches {the dobj/him} from you and wolfs it down.\b |
| | 936 | <q>Thanks,</q> he says, <q>I needed that -- I waa starving!</q>"; |
| | 937 | } |
| | 938 | ; |
| | 939 | --------- |
| | 940 | |
| | 941 | From Eric Eve <eric.eve@hmc.ox.ac.uk>: Some time ago Michel sent me |
| | 942 | some very nice code to add a vocabLikehihood property to VocabObject, |
| | 943 | which allows authors to nudge the parser to prefering some objects to |
| | 944 | others when all other distinguishers fail. I've found it pretty useful |
| | 945 | ever since and I think it would make a nice addition to the standard |
| | 946 | library if Michel were willing to contribute it. |
| | 947 | |