User Tools

Site Tools


sd:rogueima_plan

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
sd:rogueima_plan [2026/09/08 08:04] – external edit 127.0.0.1sd:rogueima_plan [2026/09/09 03:27] (current) – external edit 127.0.0.1
Line 853: Line 853:
 ===== Part VII — saying it properly ===== ===== Part VII — saying it properly =====
  
-==== Step 21. Item names in messages ====+==== Step 21. Item names in messages - DONE ====
  
 Everything says //"You wield it."// The item knows its name; the message does Everything says //"You wield it."// The item knows its name; the message does
Line 877: Line 877:
 ===== Part VIII — things that do something ===== ===== Part VIII — things that do something =====
  
-==== Step 22. The effects hook ====+==== Step 22. Effects, as three things instead of one - DONE ====
  
-''effects/Effect.java'' and ''Effects.java''. An effect is a **type** and a +=== Why not NetWhack'Effect class ===
-**trigger**, held in a list on a mobile, and items hand theirs over when +
-something happens to them:+
  
-  E_MODSTAT  E_REGENHP  E_DEFENSE  E_P_SPEED  E_P_BLIND +''Effect.java'' is the **only major system in NetWhack that is not a table**. 
-  E_S_TEMPORAL_SUSPENSION  E_W_PRE_ATP  E_F_EDNAS_PIES+There is an ''ArmorData'', a ''MobData'', a ''PotionData'', a ''TileData'' and 
 +a ''WeaponData''. There is no ''EffectData''. Instead there is one class with 
 +twelve fields --
  
-  T_PERTICK  T_PERTURN  T_PERSTEP  T_ONWEAR  T_ONBEAR +  ticks  uses  trigger  type  expired  index  value  extra 
-  T_FOOD  T_QUAFF  T_READ  T_ZAP  T_ATTACK  T_DEFEND+  parent  item  mobile  engine
  
-''Food.event_eat'' already calls ''fxlist.transferByItemTrigger(this, +-- of which three (''index''''value'', ''extra'') are untyped registers 
-T_FOOD)'' — the call site exists in our portwith nothing behind it. The +whose meaning changes with the type, and **four separate ''switch (type)'' 
-machinery is a fixed array of effect slots on the player, a dispatch on type, +statements** in the same file: ''on_transfer'', ''process'', ''on_remove'' and 
-and ''gc()'' to expire themNothing here needs allocation.+''changekind''One effect's behaviour is smeared across four places, and 
 +adding one means editing all four.
  
-==== Step 23. The speed system ====+It is also inconsistent with itself about the same job. ''E_E_MODSTAT'' is 
 +computed on read -- its ''on_transfer'' and ''on_remove'' are empty, with a 
 +comment saying it is handled in ''Mobile.get_attribute()''. ''E_E_DEFENSE'', 
 +three cases away, caches: ''eAC += value'' on transfer and ''-= value'' on 
 +remove. **Two opposite strategies for the same job in one switch.** 
 + 
 +A table row tells you when you are finished: the row is full. An object with 
 +three general-purpose registers and no schema never does. That is what makes 
 +the design feel open-ended -- there is no point at which it says //done//, so 
 +every new effect reopens the whole question. 
 + 
 +=== It was doing three unrelated jobs === 
 + 
 +^ NetWhack's ^ what it actually is ^ 
 +| ''MODSTAT'', ''DEFENSE'', ''PRE_ATP'' | derived state, not an event | 
 +| ''SPEED'', ''BLIND'', ''TEMPORAL_SUSPENSION'' | genuinely "for N turns, X" | 
 +| ''EDNAS_PIES'' | seven lines of prose -- a cutscene in an effect costume | 
 + 
 +The pie is the tell. It became an Effect because Effect was the only hook 
 +available; the system attracted things that did not belong in it. 
 + 
 +So the three are split, and each goes where the game already keeps that kind 
 +of thing. 
 + 
 +=== 1. Continuous modifiers: computed, never stored === 
 + 
 +''attack_rating'' and ''defense_rating'' already walk the equipment rack 
 +adding terms up. A condition is **one more term in that walk**. Nothing is 
 +applied and nothing has to be un-applied. 
 + 
 +That deletes a whole class of bug. ''eAC += value'' / ''eAC -= value'' is 
 +wrong for ever if the effect is removed twice, or if the value changes while 
 +it is worn, or if a save reorders things. A number computed on read cannot 
 +desynchronise. NetHack computes AC from worn armour every time for exactly 
 +this reason. 
 + 
 +=== 2. Timed conditions: a flat array of counters === 
 + 
 +''cond.sda''. One slot per kind, holding **turns remaining** -- NetHack'
 +''u.uprops[]''. Setting one is a store, ticking them is one loop in 
 +''sched_turn'', expiring one is a compare against zero. No objects, no list, 
 +no ''expired'' flag, no ''gc()''
 + 
 +  .equ CD_NAME      ; 3 bytes: what to call it 
 +  .equ CD_START  3    ; 3 bytes: what to say when it begins 
 +  .equ CD_END    6    ; 3 bytes: ... and when it wears off 
 +  .equ CD_SIZE   9 
 + 
 +  .equ C_BLIND      ; Effect.E_P_BLIND 
 +  .equ C_FAST    1    ; Effect.E_P_SPEED 
 + 
 +That is the schema the Effect class never had, and **a row is finished when 
 +those three pointers are filled in**. 
 + 
 +The API is four routines: ''cond_set(kind, turns)'', ''cond_on(kind)'', 
 +''cond_tick()'' and ''cond_slot(kind)''. ''cond_set'' only says the start 
 +message when the condition was not already true, so a second potion of speed 
 +lengthens the haste rather than announcing it twice. 
 + 
 +=== Read where it matters, applied nowhere === 
 + 
 +This is the part worth keeping hold of. A condition is never pushed into 
 +anything -- the one place that cares **asks**: 
 + 
 +  * **Blindness** is asked about in ''los_update'', which then marks only the 
 +    square you stand on. NetWhack does ''mobile.blind++'' on transfer and 
 +    ''blind--'' on remove, and has to get both right for ever. 
 +  * **Haste** is asked about in ''player_charge'', where an action costs half 
 +    the clock. NetWhack's ''E_P_SPEED'' instead does ''action_time -= value'' 
 +    every turn //from inside the effect// -- the same idea pushed the other 
 +    way round, the effect reaching into the mobile rather than the mobile 
 +    asking the effect. 
 + 
 +=== 3. One-shot moments: the item's own routine === 
 + 
 +''potion_effect'' in ''food.sda'': a dispatch on kind, like ''mon_damage'' and 
 +''item_name'' already are. A potion of healing heals you at the point where 
 +you drank it, and does not need to become an object with a lifecycle first. 
 +Each potion's whole behaviour is readable in one piece. 
 + 
 +Nutrition is not in there -- every potion has some and ''do_quaff'' has 
 +already applied it, which is why a potion of water does nothing else at all. 
 + 
 +=== Verification === 
 + 
 +  blind on after cond_set        1      turns after 2 of 5      3 
 +  blind on after 5 ticks              action, normal       1000 
 +  action, hasted               500      healing, 2d6 from 1     9 
 +  poison, 2d6 from 20           17      blind from potion     250 
 +  speed from potion            100      second potion         200 
 + 
 +and in the real game, poked blind and stepped: lit floor **194 to 0**, all 195 
 +squares still remembered in dark grey, items drawn **2 to 0**. You keep the 
 +map you know and see none of it. 
 + 
 +=== What is left === 
 + 
 +''TEMPORAL_SUSPENSION'' and the pie have no slots yet; both are one row and a 
 +case when they are wanted. The nine NetWhack effect types are otherwise 
 +covered, with less machinery than the Effect class alone. 
 + 
 +==== Step 23. The speed system - DONE ====
  
 ''MobInfo'' carries ''mspeed'' and ''aspeed'' and has since Step 18 — a rock ''MobInfo'' carries ''mspeed'' and ''aspeed'' and has since Step 18 — a rock
Line 912: Line 1014:
 how it reads, and the potion of speed has nothing to do without it. how it reads, and the potion of speed has nothing to do without it.
  
-==== Step 24Potions that do what they say ====+**Taken before Step 22**, because half of the ''Effect'' triggers -- 
 +''T_PERTICK'', ''T_PERTURN'', ''T_PERSTEP'' -- have nothing to fire against 
 +until a clock exists.
  
-Healing, poison, blindness, speed. Until this lands a potion of speed is +=== There is no event queue ===
-drink of water with a different name on it. Blindness wants ''LOS_RADIUS'' +
-temporarily set to 0, which is one variable; speed wants Step 23's clock.+
  
-==== Step 25Scrollsand ''r'' to read ====+Worth writing down, because it is easy to misremember: **NetWhack has no 
 +global event list at all.** No event classnothing scheduled centrally. 
 +''Engine.do_tick(m)'' is
  
-''identify'', ''teleport''''town portal'', ''temporal suspension'', and the +  m.per_tick(); 
-crumpled note that starts identified. ''Scroll.do_id'' offers the pack through +  m.action_time--; 
-the chooser we already have and marks one kind known. Reading also identifies +  if (m.action_time > 0) return;   // no energy yet 
-the scroll — which is the second half of Step 12'identification system +  ...act... 
-finally paying for itself.+  m.action_time += m.movespeed;    // and pay for it 
 + 
 +per mobile per gametick, and effects live in ''m.fxlist'' on the mobile that 
 +owns themprocessed by trigger and swept by ''gc()''. Nothing reaches out of 
 +an item into the engine. So the counter-per-mobile is kept exactly as it is. 
 + 
 +=== What is not kept: the ticking === 
 + 
 +''do_tick'' runs for every mobile on every one of the thousand gameticks. In 
 +Java that is free. Twenty mobiles x a thousand ticks x ten instructions is 
 +**200,000 instructions a turn** -- about 154ms at the 1.30M instructions/sec 
 +this machine measures at, on top of the 48ms Step 11 already costs, and it 
 +grows with the monster count. 
 + 
 +So time does not advance one unit at a time. The **smallest** counter is 
 +found, that much is taken off every counter at once, and whoever reaches zero 
 +acts. Identical arithmetic -- 900 still acts more often than 1000 -- for one 
 +pass per action instead of a thousand passes per turn. The 1000 scale is kept; 
 +the resolution lives in the arithmetic, not in the loop count. 
 + 
 +=== A field, not a list === 
 + 
 +''OBJ_READY'' is a field in each record rather than an entry in a central 
 +list, and that is the important choice. A list needs entries removed when a 
 +monster dies, a level changes, or a node is recycled -- and a missed removal 
 +is a stale event pointing at whatever now occupies that node**A counter in 
 +the record dies with the record.** It is also nearly free: the world queue is 
 +already walked every turn by ''move_mon'', ''draw_items'' and 
 +''pick_up_items'', so this is one more comparison on a scan that was happening 
 +anywayOnly ''OT_MONSTER'' records are scanned; items on the floor do not act. 
 + 
 +''move_mon'' charges the monster **before** it moves, so every way out of the 
 +routine has been paid for. Otherwise one wedged against a wall never advances 
 +its counter and the scheduler hands it every turn for ever. 
 + 
 +A turn is still ''SPD_PLAYER'' units of clock, so hunger, regeneration and the 
 +wandering-monster roll fire exactly as often as they did -- a loop rather than 
 +an if, since one slow action can cross two turns. 
 + 
 +=== Verification === 
 + 
 +The claim is a ratio, so it is counted rather than looked at. 400 scheduler 
 +steps with three actors: 
 + 
 +  player     (1000)   120 acts 
 +  phase rat   (400)   300 acts     = 120 x 1000/400 
 +  rock mole  (1500)    80 acts     = 120 x 1000/1500 
 + 
 +and in the real loop with twenty monsters running, 97 turns left the energy 
 +meter at exactly 5000 - 97. 
 + 
 +''OBJ_LEN'' went 37 to 39 for the counter, so the 4 KB node array holds 105 
 +rather than 110. 
 + 
 +==== Step 24. Potions that do what they say - DONE ==== 
 + 
 +Healing, poison, blindness and speed, all four landed with Step 22, since 
 +''potion_effect'' is the shape that step decided on: a dispatch on kind, one 
 +case per potion, each readable in one piece. 
 + 
 +  healing      2d6 back, clamped by heal_player 
 +  poison       2d6 off, floored at 0, and -250 nutrition from the table 
 +  blindness    C_BLIND for 250 turns 
 +  speed        C_FAST for 100 turns 
 +  water        nothing at all -- its 100 nutrition is the whole of it 
 + 
 +Blindness did **not** want ''LOS_RADIUS'' set to 0, as this step guessed 
 +before it was written. ''los_update'' asks ''cond_on(C_BLIND)'' and marks only 
 +the square you stand on -- a temporarily-modified global would have to be put 
 +back, and putting things back is the failure mode Step 22 exists to avoid. 
 + 
 +==== Step 25. Scrolls, and ''r'' to read - DONE ==== 
 + 
 +''Scroll.event_read'', in the shape Step 22 settled on: a dispatch on kind, 
 +one case per scroll, each readable in one piece. 
 + 
 +^ scroll ^ what it does ^ 
 +| identify | learn what one thing in your pack is | 
 +| teleport | somewhere else on this level | 
 +| town portal | says so -- there is no town until Step 30 | 
 +| crumpled note | reads it, and does **not** vanish | 
 +| temporal suspension | everything else waits 5 to 20 turns | 
 + 
 +Reading identifies the scroll, so the message uses the name it had **before** 
 +you read it: //"You read the scroll of gibberish."// NetWhack only 
 +self-identifies the teleport one, inside ''do_teleport'', which looks like an 
 +omission rather than a decision -- you plainly learn what a scroll was by 
 +watching what it did. 
 + 
 +=== Temporal suspension is not a condition === 
 + 
 +NetWhack does ''action_time -= Dice.roll(5,20) * 1000'', giving the player 
 +credit so that everyone else has to wait. Our counters are unsigned and count 
 +**down**, so the same thing is expressed from the other side: everybody else 
 +is pushed back by that much. Identical in effect, and it cannot go negative. 
 + 
 +It also **must not** be a condition that makes the player'actions free. 
 +''sched_find'' would then return zero for ever, so the clock would never 
 +advance, so ''sched_turn'' would never fire, so ''cond_tick'' would never run 
 +-- and the suspension would never end. A condition has to be something the 
 +clock can outlive. 
 + 
 +=== A bug inherited and not copied === 
 + 
 +''sc_identify'' does what NetWhack's ''do_id'' meant to do. That one builds 
 +its list of candidates with 
 + 
 +  if (i.identified == false); 
 +      a_list.add(i); 
 + 
 +-- a stray semicolon, so the ''add'' is unconditional and the scroll can spend 
 +itself telling you about something you already knew. 
 + 
 +=== Verification === 
 + 
 +  unknown kinds in the pack     2 -> 1 
 +  teleport moved the player     yes, and to somewhere walkable (3 runs) 
 +  clock added by suspension     8000 / 18000 / 11000 
 + 
 +=== Two hazards of the assembler, found the hard way === 
 + 
 +''LDBL AL'' and ''LDAL XL'' are **not** register moves, and the assembler 
 +takes both without a word. The first made identify choose nothing; the second 
 +stored garbage into ''PX'' and ''PY'', so teleport put the player inside a 
 +wall -- and only showed up because the test asked whether the destination was 
 +walkable rather than only whether he had moved. ''MOV'' is the register move; 
 +''LDxx'' loads an immediate or from memory.
  
 ==== Step 26. Blessed, cursed, uncursed ==== ==== Step 26. Blessed, cursed, uncursed ====
Line 934: Line 1164:
 ===== Part IX — a world ===== ===== Part IX — a world =====
  
-==== Step 27. The rest of the tile kinds ====+==== Step 27. Tiles store their kind - DONE ==== 
 + 
 +A square used to store **the character it looked like**. It stores its 
 +**kind** now, and the glyph is one column of a generated table along with the 
 +colour, the flags, the name and the description. Same two bytes per square. 
 + 
 +=== Why this had to come before the village === 
 + 
 +''TileData'' draws chair, bridge, bed, road, table and throne **all as ''='''', 
 +and wall, secret door and altar **all as ''#''''. A map that stores glyphs 
 +cannot tell a bridge from a chair -- so it cannot say whether you may walk on 
 +it, what colour to draw it, or what it is called when you look at it. 
 + 
 +The map SOURCE alphabet is a different thing and is unambiguous: ''r'' road, 
 +''B'' shop counter, ''d'' bed, ''f'' flowers, ''b'' bridge, ''='' chair, 
 +''s'' secret door, ''*'' water, ''T'' tree. That is why a level can be written 
 +as text at all. ''src_to_kind'' is ''DungeonMaker'''s switch, one character to 
 +one kind; only the DISPLAY collides. 
 + 
 +=== The table === 
 + 
 +''tiletable.sda'', generated by ''tools/gen_tiletable.py'': 
 + 
 +  .equ TI_GLYPH  0    ; 1 byte : what it is drawn as 
 +  .equ TI_COLOR  1    ; 1 byte : and in what colour 
 +  .equ TI_FLAGS  2    ; 1 byte : TF_WALKABLE | TF_OPAQUE 
 +  .equ TI_TNAME  3    ; 3 bytes: -> its name, for looking at it 
 +  .equ TI_DESC      ; 3 bytes: -> the long description. ON TAP 
 +  .equ TI_TSIZE 
 + 
 +The glyph, colour, name and description come from ''TileData''. **The flags do 
 +not** -- walkable and vblock are set by a switch in ''Tile.changekind()'' and 
 +are not in the table at all. The generator reads both files, so the two cannot 
 +drift apart. 
 + 
 +Two deliberate departures, both made in the generator where they are visible 
 +rather than in the data where they would look like the source: 
 + 
 +  * Four kinds are drawn with **box-drawing characters** and two with a 
 +    **space**. Neither survives an ASCII renderer, and a space is what an 
 +    unseen square looks like -- a shop floor drawn as one would be invisible. 
 +    Those six get stated ASCII stand-ins. 
 +  * A **wall is light grey**, not ''TileData'''s ''DARK_GRAY'', which is 
 +    exactly the colour a remembered square is drawn in. A secret door matches 
 +    the wall it is pretending to be. 
 + 
 +=== What it cost === 
 + 
 +''tile_flags_for'' used to test for ''#'' and ''+'' and call everything else 
 +floor -- as far as two glyphs could take it. ''get_glyph'' looks the glyph up; 
 +''put_glyph'' became ''put_kind''; ''draw_world'', ''open_door'', 
 +''place_stairs'', ''expand_level'' and the whole dungeon maker read and write 
 +kinds. ''tiletable.sda'' has to be assembled **before** ''map.sda'', because 
 +''.equ'' has no forward references and ''TK_TEMP'' is derived from ''TK_COUNT''
 + 
 +=== And Brynn === 
 + 
 +The village is the first level: ''Brynn.java'', **84 x 30**, verbatim but for 
 +trimming each row to the 84 columns it declares -- the Java rows are one 
 +character longer. ''popfreq'' 0, because nothing wanders into a town. 
 + 
 +Its well is **not in the map text**; NetWhack adds it in code. Read the code 
 +and not the comment above it: the comment says //"Add the well at 24,4"// and 
 +the three lines below say ''s.xpos = 4; s.ypos = 28''. **Lower left.** That is 
 +the level's ''LV_DOWN''
 + 
 +=== Where does NetWhack put the player? Nowhere === 
 + 
 +This is worth writing down because it cannot be found by looking for it. 
 +''Level.java'' declares 
 + 
 +  public int px_last = 0, py_last = 0; 
 + 
 +and line 519 does ''pc.xpos = px_last''. **Brynn never assigns either.** So the 
 +answer is the field initialiser: NetWhack starts you in the top-left corner. 
 +Ours starts on the road at **2,6**, which is a decision rather than an 
 +accident. 
 + 
 +That needed a new field. ''LV_UP'' was doing two jobs -- where the stairs up 
 +are, and where you appear -- and Brynn has no stairs up, so the two had to 
 +come apart. ''LV_START'' is where a new game begins; ''LV_SIZE'' 23 to 25. 
 + 
 +**0,0 means "no such staircase."** ''place_stairs'' skips one whose 
 +coordinates are 0,0, that being the one square no real staircase can occupy -- 
 +the map corner on any level with a border. This replaced a test on 
 +''level_index'', which did not work: ''load_all_levels'' walks the levels with 
 +''TL'' and never updated ''level_index'' while building, so the test read a 
 +stale value. (It does now.) 
 + 
 +Checked on the tile array rather than by eye: exactly one staircase on Brynn, 
 +kind 7 at (4,28), and no kind 6 anywhere. 
 + 
 +The 80 x 40 scratch room is gone, and so are the starting dagger, armour and 
 +ration -- scaffolding for testing the item system, which the shops will 
 +replace. ''x'' still conjures one item from the whole table. 
 + 
 +Trees draw **green**, water **blue**, roads **brown**, flowers **grey**, each 
 +in a lit and a remembered shade. None of that was expressible before. 
 + 
 +=== What Brynn still has not got === 
 + 
 +The map only. The ''DENIZEN'' and ''SHOPKEEPER'' lines that follow it in the 
 +Java are Step 28, and the people they describe are Steps 31 and 32. The 
 +village is a place; it is not yet inhabited. 
 + 
 +==== Step 28. A level script, and people who answer questions - DONE ==== 
 + 
 +A level's map text is now followed by directives: 
 + 
 +  DENIZEN Farmer_Jim 19 17 
 +  CHAT  The_apples_look_lovely_this_year. 
 +  REPLY job   I_work_the_orchard._Apples,_mostly._It's_a_living. 
 +  REPLY name  I'm_Jim._Farmer_Jim,_on_account_of_the_farm. 
 + 
 +''DENIZEN'', its inline chats and ''NOMONSTERS'' are NetWhack's, read the way 
 +''DungeonMaker'' reads them -- split on spaces, ''_'' standing in for a space 
 +inside a token -- so **Brynn's own four denizens parse as written**. ''CHAT'' 
 +and ''REPLY'' are ours. 
 + 
 +=== Why a directive and not punctuation === 
 + 
 +''job:I'm_a_farmer'' reads well and would work. Split on the **first** colon 
 +and the answer can contain as many more as it likes, so no escape is needed 
 +for the colon at all -- a keyword is one word and cannot contain one. 
 + 
 +Two things argued against it: 
 + 
 +  * The format already has **exactly one escape**, ''_'' for space. A 
 +    backslash rule would be a second one to learn, and every directive added 
 +    later would inherit both. 
 +  * Inferring the //kind// of speech from a punctuation mark cannot tell a 
 +    keyword from a random line that happens to contain a colon. 
 + 
 +A directive says which it is instead of leaving it to be guessed, and the next 
 +thing we want -- ''GIVE'', ''QUEST'', ''SHOP'' -- is another word rather than 
 +another mark. 
 + 
 +=== Talking: Ultima IV's three cases === 
 + 
 +NetWhack has only the middle one. An NPC there holds a list of lines and says 
 +one at random; there is no interactive speech in it anywhere. The other two 
 +are Rogueima's. 
 + 
 +^ case ^ what happens ^ 
 +| nothing to say | //"They do not seem to want to talk."//
 +| random speech | Farmer Jim says, "The apples look lovely this year." | 
 +| keyword replies | a page of its own, and you type words at it | 
 + 
 +  Farmer Jim -- and you are talking to them. 
 + 
 +    I work the orchard. Apples, mostly. It's a living. 
 + 
 +    Ask about a subject -- try NAME, or JOB. BYE to stop. 
 +    Say: 
 + 
 +An unknown word gets //"They shrug, and say nothing about that."//; ''BYE'' or 
 +an empty line ends it. Matching folds case, so ''JOB'' and ''job'' are the 
 +same question. 
 + 
 +''npc_mode'' makes the three-way choice **a value rather than a shape of 
 +code**, so it can be checked without reading a screen. Replies win over chats: 
 +somebody who will hold a conversation should not be reduced to muttering one 
 +line at you. 
 + 
 +=== Nothing copies text === 
 + 
 +The script stays in the program image and an NPC holds **pointers into it**. 
 +''print_script'' turns the underscores back into spaces on the way out, rather 
 +than rewriting the source the way ''replace('_',' ')'' does -- so a level can 
 +be built twice without its script having been consumed. A keyword ends at the 
 +space that follows it in the script, since it has no terminator of its own.
  
-''TileData'' has 22: water, trees, flowers, road, bridge, bed, table, chair, +=== Verification ===
-altar, throne, shop floor and shop bar, on top of the six we use. Cheap — a +
-table of glyph, colour and flags — and every one of them is a prerequisite for +
-a village that looks like a village rather than a dungeon with the walls +
-knocked out.+
  
-==== Step 28. A level script ====+  denizens created          4        chat lines             21 
 +  keyword replies          20        "JOB" finds an answer   yes 
 +  "wombat" does not       yes        modes: 2, then 1, then 0
  
-Brynn is 84x30 of map text followed by directives:+The three modes are read off one denizen stripped in stageswith both, it 
 +converses; with its replies taken away it falls back to random; with its chats 
 +gone too it has nothing to say.
  
-  "DENIZEN Farmer_Jim 19 17 The_apples_look_lovely_this_year. The_farmin'_life_for_me! ..." +=== An ordering bug ===
-  "SHOPKEEPER RANDOM 53 9" +
-  "NOMONSTERS"+
  
-Underscores for spaces, because the parser splits on them. ''LevelFactory'' +''init_objects'' and ''npc_init'' ran **after** ''load_all_levels'', so the 
-has a comment saying all of this //"needs to be put into some kind of script +script put four denizens into the node array and ''init_objects'' immediately 
-and attached to the level file, and not written here"// — and it is right. Our +emptied it again -- ''npc_count'' read 0 and the village was deserted. 
-''level_table'' already holds a source pointer; this is the format it points +Everything a level writes into has to exist before the level is built.
-at.+
  
 ==== Step 29. Branches, and stairs that know where they go ==== ==== Step 29. Branches, and stairs that know where they go ====
sd/rogueima_plan.1788854645.txt.gz · Last modified: by 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki