Communication and Trading - HoraceAndTheSpider/Bloodwych-68k GitHub Wiki
Communication and trading
Communication is a stateful exchange between the active champion and another champion or monster. The interface selects an action from the visible menu, stores that action in a per-player communication record, dispatches through an action table, and then selects the other character's response from the preceding action and the current attitude.
The description below is based on the SPS 439 communication routines. Source addresses are given as memory addresses where they help identify the implementation; the corresponding relabelled routines use the Comms_ names shown here.
Communication state
Each player has a sixteen-byte communication record at $16B4C. Comms_GetState selects the active record. The fields used by the communication and trading routines are:
| Offset | Name | Use |
|---|---|---|
$00 |
Previous action | The action to which the other character is responding. The initial value is Greeting. |
$01 |
Current action | The action currently being dispatched. It is copied to $00 after the action finishes. |
$02 |
Other-character identity | Character number and identity flags used by generated responses such as threats. |
$03 |
Speaker identity | Speaker number and the flags set by Name Self and Reveal Self. |
$04 |
Activity timer | Reset to $14 after communication actions. Its wider timing role is not yet fully established. |
$05 |
Communication flags | Includes the sign-bit distinction used for the mirrored/remote communication path; other bits remain unresolved. |
$06 |
Attitude | The mutable rapport value used by response selection and trading. |
$07 |
Patience | Remaining engagement value. Menu actions consume it; Beguile increases it. |
$08 |
Trade mode | 0 none, 1 Purchase, 2 Exchange, 3 Sell. |
$09 |
Quoted trade value | The current gold quote or quote-state byte. Its sign bit marks the favourable/revised quote path. |
$0A |
Trade object | The object code proposed for the current trade. |
$0B-$0F |
Not yet assigned | These bytes are part of the sixteen-byte record, but no stable communication meaning has been confirmed for them. |
Starting a conversation
Comms_StartWithTarget clears the attitude byte, records Greeting as the previous action, identifies the selected character, and initialises the communication values.
For an ordinary conversation, the initial attitude is:
random value from 2 through 9
+ floor(max(Charisma - 20, 0) / 4)
+ any existing monster/trader attitude adjustment
Negative results are clamped to zero. Patience is independently initialised to a random value from 8 through 15. The activity timer is set to $14 and trade mode is cleared.
Monster traders receive an additional adjustment while their stock is initialised. The trader routine starts with attitude 3; for ordinary merchandise it then applies the monster's trader value as a negative adjustment. Potion traders and the general trader classes use different stock-selection paths.
Menu actions and response selection
The visible menus are converted to action identifiers by Comms_HandleMenuSelection. Comms_RunAction stores the identifier at state offset $01, uses the ordered handler table at $3526, and calls the selected action routine.
The action identifiers are:
| Value | Action |
|---|---|
$00 |
Recruit |
$01-$03 |
Identify, Inquiry, Whereabouts |
$04-$05 |
Trading, Smalltalk |
$06-$09 |
Yes, No, Bribe, Threat |
$0A-$0D |
Who Goes, Thy Trade, Name Self, Reveal Self |
$0E-$11 |
Folk Lore, Magic Items, Objects, Persons |
$12-$15 |
Offer, Purchase, Exchange, Sell |
$16-$19 |
Praise, Curse, Boast, Retort |
$1A |
Greeting |
After an action, the current action becomes the next response selector. The response handler table at $2CAE routes the other character's reply according to the preceding action. Low attitude commonly redirects to a Retort, threat or dismissive response. Some replies use fixed entries from the action-reply table; others construct a message dynamically.
Each normal menu selection raises attitude by one before dispatch and consumes one patience point afterwards. Individual actions apply their own additional changes. For example, Threat removes three attitude points, Curse removes four, and No removes one more; refusing a Sell path removes an additional four.
Trading overview
Trading is handled as a small state machine rather than as four independent conversations. The action writes the intended mode at state offset $08, records the proposed object at $0A, and, where appropriate, records the quote at $09.
| Action | Mode | State prepared |
|---|---|---|
| Offer | Context-dependent | Uses the held object or held coinage. An empty hand produces the generic offer text. |
| Purchase | 1 |
Asks what the monster has for sale and enters Purchase mode. |
| Exchange | 2 |
Records the held object, if any, and enters Exchange mode. |
| Sell | 3 |
Records the held object and clears the previous quote. |
The response to Offer at $2E12 checks that the held object still matches state $0A. A changed object produces the “Wouldst thou rip me off?” response and reduces attitude by five. Objects at or above $40 are rejected as untradeable, except for the Permit ($5F), which has a special trade value. The trade-mode dispatch table at $2E4A selects the transfer routine for the active mode.
Held objects and coinage
The held-item state is separate from the communication record. The held object code is read at $2E(a5), while the combined held quantity/object state begins at $2C(a5). Coinage is object code $01.
Offer handles coinage specially. When coinage is held, its quantity is formatted into the generated gold-offer message and the communication mode records whether the offer is being made as money. When an object is held, the object name is appended to the appropriate packed message using the object-definition name references.
The Yes handler performs the final transfer only after the proposed object, monster object and quote state still agree. It then clears the trade mode and refreshes the held-item display.
No cancels an active trade. If the cancelled mode is Sell, it applies an additional four-point attitude penalty before the normal one-point No penalty.
Object trade values
Comms_ObjectTradeValues at $31E6 is a byte table indexed by object code minus $14. It covers object codes $14-$3F:
object: $14 $15 $16 $17 $18 $19 $1A $1B $1C $1D $1E $1F $20 $21 $22 $23
value: $04 $04 $06 $0A $0F $08 $0C $07 $0C $10 $14 $19 $20 $27 $2B $32
object: $24 $25 $26 $27 $28 $29 $2A $2B $2C $2D $2E $2F $30 $31 $32 $33
value: $06 $0A $19 $0F $14 $1E $28 $FF $0A $12 $1B $23 $05 $14 $0A $0A
object: $34 $35 $36 $37 $38 $39 $3A $3B $3C $3D $3E $3F
value: $0F $FF $FF $5A $0C $14 $19 $FF $FF $08 $0F $FF
$FF is a rejected/untradeable entry. Objects below $14 use small default values in the routines rather than this table. The Permit ($5F) is handled separately and receives value $5A (90). Champion remains begin at $40 and are rejected by the normal trading checks.
Purchase
When Purchase is selected, the response routine examines the monster's current offered object at byte $0C of its monster record. Monster types $15 and $16 initialise trader stock if no suitable stock has already been prepared. Other trader classes use the existing object.
The stock pool for general traders is Comms_TraderStockObjects at $3212. A random index is reduced according to the monster's trader value before the object is selected. Potion traders use a separate four-object range ($17-$1A). The selected object is then placed into the generated response and stored as the monster's offered object.
Offer and buying calculation
When the player offers an object for a Purchase-mode transaction, the routine obtains the monster's object value and calculates an attitude-adjusted price. If the monster's object value is V and attitude is A, the normal first quote is:
price = floor(V * min(A + 50, 100) / 100)
The comparison is made against the player's held coinage quantity. An insufficient offer produces the hostile response path. A sufficient offer transfers the monster's object into the player's held-item state and clears the communication trade mode.
The Permit uses V = 90; objects below the lookup range use the routine's default value. The table's $FF entries reject the transaction before a price is calculated.
Exchange
Exchange compares the value of the player's proposed object with the value of the monster's offered object. Both values use the same lookup table and special cases:
- objects below
$14use the default exchange value$02; - the Permit uses
$5A; $FFentries are rejected;- objects at or above
$40are rejected except for the Permit.
The exchange succeeds when the player's offered value is at least the monster object's value. The two object codes are then swapped, the new object is written into the player's held-item state, and the communication action is set to the internal transfer-completion value.
Sell and quote revision
Sell records the held object and starts with no quote in state $09. The object must still match the proposed object when the response is generated. A changed object applies the five-point rip-off penalty; an untradeable object uses the unnatural-object response.
For a first quote, the same object value V is adjusted by attitude:
modifier = attitude + 50
if modifier < 100:
quote = floor(V * modifier / 100)
else:
quote = V
If a previous quote exists, a negative quote byte selects the “too greedy” path. A non-negative quote with attitude below five selects a hostile response. At attitude fifteen or above, the routine moves the quote halfway towards the object's table value, sets the high bit in the stored quote state, and prints the revised gold offer. The exact user-facing meaning of that high-bit state is still best described as “revised/favourable quote state” rather than as a confirmed named mechanic.
Special rejection paths
Trading can fail for reasons other than value:
- the held object no longer matches the object recorded when the question was asked;
- an object code is at or above
$40and is not the Permit; - the lookup table contains
$FF; - the offered gold is below the calculated purchase price;
- an exchange offer is worth less than the requested object;
- a quote is marked too greedy;
- the response attitude selects a hostile or dismissive branch before the transfer routine is reached.
These paths explain why trading appears to depend on both held objects and communication attitude. The game validates the live held-item state at several points instead of trusting only the original button selection.
Beguile
The Beguile routine at $508E first checks that interface mode $08 (communication) is active. It then computes:
bonus = floor(spell power / 4) + 1
The bonus is added to attitude at state offset $06 and patience at $07. It does not alter the champion's Charisma stat; it changes the already-created communication state. The additions are byte-sized and the routine does not clamp the stored low byte.
Smalltalk and generated messages
Praise and Curse use word-range tables and randomized sentence patterns. Boast inserts a personal attribute and superlative. Threat selects one opening fragment and one consequence fragment, with the target's identity flags determining whether the message names the character directly or uses a generic form.
The fixed-response table contains one entry for each preceding action. Several entries use $FF as a sentinel, causing the response routine to select one of four randomized agreement messages instead. This is why some actions do not have one permanently fixed reply even though they have a stable action-reply-table slot.
Remaining uncertainties
The following points should not yet be presented as settled mechanics:
- the full meaning of communication-record bytes
$04,$05and$0B-$0F; - the exact personality interpretation of every attitude threshold;
- the design intent of each special/default trade value;
- the full monster subtype-to-trader-stock classification;
- whether the high-bit quote state represents a counteroffer, generosity or another internal negotiation state;
- behaviour at byte overflow boundaries when repeated attitude or Beguile additions wrap.
The source structure and transaction algorithms are nevertheless sufficiently established for the communication and trading mechanics to be documented and used as the current reference page.