1 The GhulScript Specification
6 A conversation is a set of other conversations and a set of keyword-response
9 <conversation> := CONV tag { IMPORT { <conversations> }
10 NATIVE { <qr_pairs> }}
11 <conversation> := tag <conversations> |
12 <qr_pairs> := <qr_pair> <qr_pairs> |
13 <qr_pair> := <query> tag
15 The lookup rules determine how to match a keyword to a response. The order of
16 searching qr_pairs for a keyword match is as follows:
18 1. The NATIVE section of a conversation, in the order specified
19 2. Each conversation in the IMPORT section, in the order specified
20 3. The DEFAULT keyword
22 Note that rule 2 is recursive (and if I haven't said it yet circular inclusion
23 of conversations is disallowed). The DEFAULT keyword is a special keyword used
24 when a search fails. Other special keywords include HAIL, used when starting a
25 conversation, and BYE, used when ending a conversation.
30 Normally a keyword is something the player types during a conversation, but
31 the following keywords are special in that the engine generates them
32 automatically in certain situations.
34 DEFAULT If the player keyword does not match anything in the
35 conversation, then the engine looks up the response for the
36 DEFAULT keyword instead. This gives NPC characters the chance
37 to customize their "I don't know" response.
39 HAIL When the conversation first starts up, before the player has a
40 chance to give a query, the engine will check if the
41 conversation has a response for the HAIL keyword and will
42 invoke it if so. This gives NPC characters a chance to have the
45 BYE When the conversation terminates, after the player has issued
46 his last query and received a response, the engine will check
47 if the conversation has a response to the BYE keyword and will
48 invoke it if so. This gives NPC characters a chance to say
49 "Farewell" or have the last word.
51 The player may type these keywords and they will be looked up in the usual way.
56 In the grammar above a qr_pair is a <query> (which is a simple string) and a
57 tag. The tag refers to a response. Each response is defined separately in its
58 own construct. Because responses are independent you may mix them with
59 different keywords in different conversations as you like. The syntax for a
60 response declaration is like this:
62 <tagged_response> := RESP tag <response-list>
64 <response-list> := { <response> <response-or-nil> }
66 <response> := SAY string |
72 CHANGE_PARM <parm-id> <delta> |
78 <check> <response-list> <response-list>
80 /* Proposed constructs: */
84 <delta> := +int | -int
85 <trade-list> := { <trade-entry> <trade-entry-or-nil> }
86 <trade-entry> := tag <price>
89 <check> := GET_YES_NO |
90 CHECK_PARM <parm-id> operator int |
91 CHECK_FLAG flag int bool |
97 <menu> := { <menu-item> <menu-item-or-nil> }
98 <menu-item> := <name> <cost> <response-list>
102 <menu-item-or-nil> := <menu-item> |
104 <response-or-nil> := <response> |
107 SAY The SAY command prints a line on the console. It prints the
108 literal string that follows it with no formatting.
110 TRADE The TRADE command invokes the trading subroutine of the game
111 engine, providing it a list of items and prices to trade. The
112 engine automatically adds buy/sell and quantity prompts and
113 handles the exchange of money and goods.
115 When the player opts to Buy he sees the list of goods and
116 prices. He can buy as many as he cares to and can afford (in
117 other words the merchant has an endless supply of the goods
120 When the player opts to Sell then the engine scans player
121 inventory and lists any of the items from inventory which
122 appear on the trade list. The sell price is fixed at 1/4 of the
123 buying price. This constant is currently hard-coded in the
124 engine and applies to all trade situations.
127 The TURN_AWAY command causes the NPC to terminate the
128 conversation without player consent. The player will see a
129 console message like "Hawknoz turns away." and then
130 conversation mode will be over.
133 The SET_FLAG command sets the value of the specified flag,
134 causing a subsequent CHECK_FLAG to evaluate to true. The
135 opposite of SET_FLAG is CLEAR_FLAG.
138 The opposite of SET_FLAG.
141 Decrement the player's gold counter by the indicated
142 amount. The engine will not decrement beyond zero. (Note that
143 there is no corresponding GIVE_GOLD simply because I haven't
147 Change the indicated parameter by the indicated amount. If the
148 amount is negative the engine will not decrement beyond
149 zero. (Note the absence of a SET_PARM - again because I haven't
153 Decrement one count of the indicated item type from player
154 inventory. If the player has none in inventory then this has no
155 effect. If the tag does not resolve to an item type then this
158 Note: currently this does not add the item to the NPC's
159 inventory, as one might expect.
162 The opposite of TAKE_ITEM. This does not actually remove
163 anything from the NPC's inventory.
166 Prompt the player to answer yes or no. The player must respond
167 with one or the other (ie he cannot escape from the
168 prompt). "Yes" means true and "no" means false.
171 Check if the given expression is true for the indicated
175 Test if a flag is set.
178 Check if the player has at least one count of the indicated
182 Check if the indicated character has joined the player party.
185 /* Proposed constructs */
187 MENU Use the status window to display a list of choices to the
188 player. When the player selects an entry from the list the
189 corresponding set of responses is executed. Upon completion -
190 unless one of the responses was a BREAK or EXIT - the menu will
191 "loop", displaying the list of choices again. MENUs may be
194 BREAK Break out of a MENU loop. Execution resumes after the MENU
195 block. If the MENUs are nested, only the lowest level MENU is
200 The SET_FLAG, CLEAR_FLAG and CHECK_FLAG responses all operate on flags. A flag
201 is an unsigned 8-bit integer id for a boolean value.
203 Flags may be global or per-conversation. Global flags may be accessed from any
204 conversation in the game. Per-conversation flags apply only to the conversation
205 in which they appear. Different conversations may use the same per-conversation
206 flag id but each will have its own value for the flag. Per-conversation flag
207 ids have the high bit clear (giving them the range 0 to 127) and global flag
208 ids have the high bit set (giving them the range 128 to 255).
210 Note: Initially all flags are clear. When savegames are implemented flag values
211 will need to be saved/restored, so in the future there will need to be syntax
212 for specifying initial flag values.
217 The CHANGE_PARM and CHECK_PARM responses operate on parameters. A parameter is
218 a signed 32-bit id for an integer value.
220 The engine reserves all negative parameter ids. The following parameter ids are
221 currently supported by the engine:
224 The AMOUNT parameter refers to the value obtained by the last
225 GET_AMOUNT response. A value of -1 means the player did not
226 enter an amount at the last prompt (i.e. escaped out of the
230 This refers to the player's gold counter.
233 This refers to the player's food counter.
236 This refers to the speaking NPC party's current activity
237 code. Activity codes are used by schedules. This parameter
238 allows conversation scripts to change their response based on
239 what the npc party is currently doing. For example, in u5 the
240 NPC merchants would not trade with the player when they were
243 All parameter id's of 0 or greater are available for use by the game script.
245 All parameters are global in scope.
247 Note: Initially all non-reserved parameters are zero. When savegames are
248 implemented parameter values will need to be saved/restored, so in the future
249 there will need to be syntax for specifying initial parameter values.
254 The GET_YES_NO, CHECK_PARM, CHECK_FLAG, CHECK_ITEM and CHECK_MEMBER responses
255 are all branching responses. They each specify a test and two sets of
256 responses. If the test evaluates to true then the first set executes, otherwise
257 the second set executes.
262 These are not final, but are experimental examples for my own benefit while
263 designing changes to the language.
265 Example: a healer. This example illustrates how one might use some as-yet
266 unimplemented constructs like MENU, GET_PARTY_MEMBER, CHANGE_HP and an
267 unsupported PARTY_MEMBER parameter to script a healer. Note the heavy
268 duplication of code for each menu response, and also the dubious syntax for
269 checking if the player escaped out of the GET_PARTY_MEMBER prompt.
272 SAY "Hail, traveler! Do you require my healing services?"
276 CHECK_PARM GOLD >= 30 {
277 SAY "Who requires healing?"
279 CHECK_PARM PARTY_MEMBER = 0 {
280 SAY "Never mind then."
282 CHANGE_HP PARTY_MEMBER +10
285 SAY "You lack the gold, my friend."
289 SAY "Whom shall I cure?"
291 CHECK_PARM PARTY_MEMBER = 0 {
292 SAY "Never mind then."
294 SET_POISONED PARTY_MEMBER false
298 SAY "Whom shall I call back from the dead?"
300 CHECK_PARM PARTY_MEMBER = 0 {
301 SAY "Never mind then."
303 SET_DEAD PARTY_MEMBER false
304 CHANGE_HP PARTY_MEMBER +10
309 BREAK /* exit the MENU loop */
318 Example: A weapon merchant. The example illustrates the use of a
319 yet-unimplemented technique whereby one response "calls" another by referring
320 to its tag. The r_weaponsmith_hail response invokes the r_sell_to_player
321 response in order to avoid some of the duplication seen above in the healer
322 example. "Arguments" to the "called" response take the form of parameters set
323 by the caller before invoking it.
325 Note that I left the SELL clause blank because I was at a loss on how to
326 proceed. There is no proposed syntax for building a MENU list on the fly at
327 runtime, and that is essentially what is required for the SELL clause as
328 written. An alternative is not to use a MENU for sell, but to use hand-crafted
329 checks for items in player inventory followed by offers to buy them one type at
330 a time off the player.
332 Also note the use of multiplication in the r_sell_to_player response. This is
333 currently not implemented.
336 RESP r_sell_to_player {
337 SAY "How many would you like?"
339 CHECK_PARM AMOUNT > 0 {
340 SET_PARM COST (AMOUNT * COST)
341 CHECK_PARM GOLD >= COST) {
346 SAY "You don't have enough gold!";
349 SAY "Changed your mind, eh?";
353 RESP r_weaponsmith_hail {
354 SAY "Hail, traveler! Do you require my healing services?"
363 * "Call" the other response after setting up the
366 SET_PARM ITEM $t_sword;
371 /*** add other items here ***/
382 BREAK; /* exit the MENU loop */
391 ----------------------------------------------------------------------------
394 An effect is a change to an object, map or place.
396 A recurring effect is attached to a target and takes effect every turn. Wether
397 or not an effect is recurring is determined by its duration. A duration of < 0
398 means it recurs indefinitely (until some other effect removes it). A duration
399 of > 0 indicates the number of turns for which the effect will recur before
400 expiring. A duration of 0 means the effect is not recurring. Henceforth, I
401 DON'T DISTINGUISH BETWEEN RECURRING EFFECTS AND EFFECTS IN THIS DISCUSSION.
403 A spell attaches a set of effects to a set of targets. A conversation response
404 may do the same. So may using an item, stepping on a terrain, or engaging a
405 mech. When it comes to effects, the difference between all these things lies in
406 how the set of targets is specified. Specifying the set of effects probably
407 won't change much between them. But this discussion is about spells. I only
408 mention those other things as a reminder.
410 An effect is a change, so what can be changed? It depends on the target. I've
411 listed below some of the most reasonable things I can think of.
414 Character Mana Points
417 Character Intelligence
421 Character or Party Turns
423 Character or Party Alignment
424 Character, Vehicle, Terrain or Mech Passability
425 Character or Party Can Take a Turn
427 Hidden Objects are Revealed
437 Existing Effects (an effect that affects another effect)
441 Those last two (marked with asterisks) probably don't make a lot of sense to
442 you now. Why are they there? Well, I can't think of everything a map hacker is
443 going to want to design into a game. And some things I don't think require
444 engine support. For example, u4/u5 had a virtue system. Currently nazghul does
445 not. But by taking advantage of the user bits or user fields a map hacker can
446 add one. These bits and fields have no semantic value to the engine, but they
447 can be interpreted by the ghulscript. They can be modified by effects, and they
448 can be checked by conversation scripts (and perhaps eventually mech
449 scripts). Remind me to provide an example somewhere if you're interested in,
450 that's all I'm going to say for now.
452 An effect specification is a piece of ghulscript which declares an effect. Once
453 declared, an effect can be referred to elsewhere in the script (such as terrain
454 or spell type declarations). I'll show the syntax further down (after I figure
457 Anyway, the first part of an effect specification is WHAT it affects. I refer
458 to this as the SUBJECT OF THE EFFECT.
460 The next part specifies IN WHAT WAY it effects it. This part of the spec
461 depends upon what type of attribute is affected. Here's a table:
463 ============================================================================
464 Target Type | Subject | Subject Type | Notes
465 ============================================================================
466 Character | Alignment | SET |
467 Character | Dead | BOOL | Necessary? Or HP==0 enough?
468 Character | Dexterity | INT |
469 Character | Experience | INT |
470 Character | Field[0-9] | INT | Generic attributes
471 Character | Flags | SET | Generic flags
472 Character | Hit Points | INT |
473 Character | Immunity | SET | Effect immunities
474 Character | Intelligence | INT |
475 Character | Level | INT |
476 Character | Lose Turn | BOOL |
477 Character | Mana Points | INT |
478 Character | Occupation | TAG | Change jobs
479 Character | Passability | SET |
480 Character | Protections | SET (?) |
481 Character | Species | TAG | Were-shift somebody
482 Character | Speed | INT |
483 Character | Strength | INT |
484 Effect | Existence | BOOL | Remove another effect
485 Mech | Passability | SET | Applies to current state
487 Mech | Signal | INT |
488 Object | Glow | INT |
489 Object | Location | (TAG, INT, INT) | Meaning (place, x, y)
490 Object | LocationDelt | (TAG, INT, INT) |
491 Object | Visible | BOOL | Applies to particular object
492 Object | Existence | BOOL | Used to destroy an object
493 Object | Type | TAG | Morph an object
494 Object | Edibility | BOOL | Turn something into food
495 Party | Lose Turn | BOOL |
496 Party | Speed | INT |
497 Party | Alignment | SET |
498 Party | Reveal | BOOL | Can see invisible objects
499 Party | TimeStop | BOOL | Only this party can take turns
500 Party | MagicNegated | BOOL | Spellcasting disabled
501 Place | Wind Directi | DIR | Wind direction
502 Place | Ambient Glow | INT |
503 Terrain | Passability | SET |
504 Tile | Terrain Type | TAG |
505 Vehicle | Passability | SET |
507 The engine can infer the subject type from the target type and subject, so
508 there's no need to specify it in the script. But map hackers will need this
509 info as they design effects. If a spec gives a value which is the wrong type
510 then the engine needs to complain about it at load time.
512 The value of an effect is the amount or direction by which the subject changes,
513 or the value to which the subject is set. For scaler and vector subject types
514 the value of the effect might be a signed offset. This would be added to the
515 existing value. The possible operations for each subject meta-type are listed
518 ============================================================================
519 Subject Metatype | Operations
520 ============================================================================
521 scaler | assign, add, subtract, multiply
522 vector | assign, add, subtract, multiply
523 tag | assign, instantiate, destroy(?)
524 bool | assign, invert
525 set | assign, intersect, union, invert
526 ============================================================================
528 Multiple operations can be combined.
530 An effect is a change which is achieved by some means, and the means indicates
531 wether or not the effect will apply to a particular target. Some targets may be
532 immune to a particular means so that the effect will not apply. Or some targets
533 may have modifiers which apply to a particular means which reduce the value of
536 At this point I can speculate about what an effect specification is going to
539 EFFECT MagicFireball {
540 target_type Character;
546 // Reduce the value of burn effects by 10
548 target_type Character;
554 // Make a character immune to burning effects
555 EFFECT BurnImmunity {
556 target_type Character;
562 // Here's an effect which might be produced by a sword:
563 EFFECT EdgedWeaponDamage {
564 target_type Character;
567 method (EDGED_WEAPON)
570 // And here's one which might be produced by a shield:
571 EFFECT EdgedWeaponDamageProtection {
572 target_type Character;
575 method (EDGED_WEAPON)
578 But what if the character is immune to heat damage? To accomodate immunities in
579 the game the effect must specify HOW the effect is achieved. Now the engine has
580 no interest in the semantics here, so we can use an open set and let map
581 hackers decide what they want the elements of a set to "mean".
583 Not to get too far off track, but you can see that I'm thinking about the way
584 sets should be represented in ghulscript. Currently we use a bitmask. This may
585 be a bit too esoteric for normal people. So instead I'm considering a new
586 ghulscript construct to pre-declare set elements similar to an
587 enumeration. I see no reason to discard the C bitwise operators as a means of
592 method (BURN) // where BURN is a script-defined set element
595 So how does the engine know not to burn a character if that character is immune
596 to burn effects? It does this by checking the character's immunity set. If any
597 element in the effect method set is not in the character immunity set, then the
598 effect is applied. For those who like to think about such things, consider an
599 effect which confers immunity to effects which confer immunity... :-)
601 How is the effect achieved?
613 How is the effect quantified?
625 How is the target specified?
638 What special effects apply?
640 Change the character's sprite to prone (sleeping, unconscious or dead)
643 Animated shockwave (arc-specified)
653 What: Existing Character Effect
656 Target Method: Select Party Member
659 What: Character Hit Points
667 Because it affects specifically Character hit points, the engine knows
668 to search the targeted tile for a Character object. Because it does not
669 support multiple targets only the first Character found will be
670 affected. Because the effect is not cumulative the engine will search
671 the list of existing effects for the Character to make sure a Poison
672 effect is not already at work. If not then it checks the Character's
673 immunity mask and if the Poison bit is set then the effect is
674 ignored. Otherwise it inserts the new effect in the list of effects for
675 the character and applies -10 to the Character hit points. Since the
676 duration is indefinite the engine does not decrement the duration
679 Effects which create objects are a bit different then what is discussed
680 above. Such an effect has no subject, as it is not changing an existing
681 object's attributes. Instead it is creating a new subject.
683 There are several ways to carry out object creation. First of all, we must
684 unfortunately distinguish between characters and other object types. The reason
685 is that most objects have a type, and this type specifies the default values
686 for an object. But characters do not have a single type. Instead they have a
687 species, an occupation, a schedule and a conversation. Perhaps they should have
688 a type which combines all these elements. The downside is you end up with a lot
689 of character types which will only have a single instance because the type info
690 is so specific to a single character, which is why I deviated from the rule a
691 bit. Anyway, besides that distinction we must also recognize that sometimes we
692 want to create a new object from scratch and sometimes we want to clone an
695 EFFECT SlimeCloneWhenDamaged {
696 target_type Character;
697 target_method Effected;
707 Prototype header file:
709 // What the effect applies to.
710 enum effect_target_type {
711 effect_target_type_none = 0,
714 // How to pick what the effect applies to.
715 enum effect_target_method {
716 effect_target_method_none = 0,
717 effect_target_method_affected,
720 // What is being changed about the thing the effect applies to?
721 enum effect_subject {
722 effect_subject_none = 0,
723 effect_subject_affected,
726 // List of subject value types.
727 enum effect_value_type {
728 effect_value_none = 0,
735 // How is the value of the subject going to be changed?
736 struct effect_value {
737 enum effect_value_type type;
746 // List of events which invoke an effect ("hooks" an effect is attached
749 effect_hook_none = 0,
750 effect_hook_hp_reduced,
753 // List of ways to specify a target for an effect.
754 enum effect_target_method {
755 effect_target_none = 0,
756 effect_target_effected, // whatever the effect is attached to
759 // List of operators applied to the subject
760 enum effect_operator {
761 effect_operator_none = 0,
762 effect_operator_assign = '=',
763 effect_operator_new = 'n',
764 effect_operator_delete = 'd',
765 effect_operator_clone = 'c', // the subject
766 effect_operator_add = '+', // w/ assignment to subject
767 effect_operator_subtract = '-', // w/ assignment to subject
768 effect_operator_multiply = '*', // w/ assignment to subject
769 effect_operator_divide = '/', // w/ assignment to subject
772 // The effect data structure.
776 enum effect_target_type type;
777 enum effect_target_method target_method;
778 enum effect_subject subject;
779 enum effect_hook hook;
780 enum effect_source source;
781 enum effect_operator operator;
782 struct effect_value value;
786 // Create and initialize an effect structure from ghulscript.
787 extern struct effect *effect_load(class Loader *);
789 // Apply an effect (attempt to, anyway).
790 extern int effect_apply(struct effect *effect, class Object *affected);
792 // Properly deallocate an effect structure.
793 extern void effect_destroy(structeffect *effect);