Mushroom steak



    1. What is it? I mean, it looks like a mushroom, but it’s not just a mushroom, or else it wouldn’t be a mushroom steak. (relevant for emoji)

    2. Can we figure out where it comes from? I expect that the “divide food” script can be found with delvmod. Is there a repository handy with all the scripts? Some of them are on the delvmod wiki, but not most.



  • I was able to run delvpack to unpack the Cythera Data archive, and was able to find the script: it’s 1AFB Divide Food. (it might be easier to start with1AFC Split Cash, which is much shorter)

    I don’t seem to be able to run ddasm on the files, though. I’m not sure if I’m holding it wrong, or if the scripts can’t yet be decoded.



  • The “Divide Food” script:

    // DDASM 0.2.3
    include Delver.Model
    include Delver.Main
    include Cythera
    use System
    resource 0x1AFB
    
    
    // 0x022C:
    // []
    field_order (0xFFFF, 0x0008, 0x0002, 0x0009, 0x0004, 0x0028, 0xFFFF)
    // Standard Symbol  Key     Value or Offset
    // Look             0x0002:0x0008
    // HasSkill         0x0004:0x001E
    // Examine          0x0008:0x5000FFFF
    // Use              0x0009:0x002B
    // Stacking         0x0028:0x0002
    // ????             0xFFFF:0x5000FFFF
    class Object
    
    array Stacking(102)
    function Look(Var30) (
        return
            string "Divide Food"
        end
        return
            byte 0
        end
    )
    
    function HasSkill(Var30, Arg31) (
        return
            arg Arg31
            byte 1
            eq
        end
        return
            byte 0
        end
    )
    
    function Use(Var30) (
        var Local00
        var Local01
        var Local02
        var Local03
        var Local04
        var Local05
        var Local06
        var Local07
        var Local08
        if_not
            global Globals.CharactersInParty2
            byte 1
            eq
        then Conditional005D
        'There is only you in the party...\n'
        return
            byte 0
        end
        Conditional005D:
        'Dividing food...\n'
        set_local Local00
            gui 0x1
            end
        end
        set_local Local01
            sys PartyIterator
                word &Local01
                byte 0
                word true
            end
        end
        Branch0085:
        if
            sys PartyIterator
                word &Local01
                byte 1
            end
        then Conditional00EA
        set_local Local04
            sys RecursiveContainerIterator
                word &Local04
                byte 0
                loc Local01
            end
        end
        Branch009F:
        if
            sys RecursiveContainerIterator
                word &Local04
                byte 1
            end
        then Conditional00DB
        if_not
            loc Local04
            get_field DObj.obj_type
            short 69
            eq
            loc Local04
            get_field DObj.obj_type
            short 231
            eq
            or
            loc Local04
            get_field DObj.obj_type
            short 213
            eq
            or
        then Conditional00CC
        method 0x6
            loc Local00
            loc Local04
        end
        Conditional00CC:
        set_local Local04
            sys RecursiveContainerIterator
                word &Local04
                byte 2
            end
        end
        branch Branch009F
        Conditional00DB:
        set_local Local01
            sys PartyIterator
                word &Local01
                byte 2
            end
        end
        branch Branch0085
        Conditional00EA:
        set_local Local07
            loc Local00
            len
            byte 1
            sub
        end
        if_not
            loc Local07
            byte 0
            lt
        then Conditional011B
        'There is no food to divide...\n'
        branch Branch0225
        Conditional011B:
        set_local Local01
            sys ArrayIterator
                word &Local01
                byte 0
                loc Local00
            end
        end
        Branch0128:
        if
            sys ArrayIterator
                word &Local01
                byte 1
            end
        then Conditional0161
        set_local Local08
            sys New
                byte 9
                byte 0
                word 255
                loc Local01
                get_field DObj.aspect
                loc Local01
                get_field DObj.obj_type
                loc Local01
                get_field DObj.data1
                loc Local01
                get_field DObj.data2
            end
        end
        sys Delete
            loc Local01
        end
        set_local Local01
            sys ArrayIterator
                word &Local01
                byte 2
            end
        end
        branch Branch0128
        Conditional0161:
        gui_close
            loc Local00
        end
        set_local Local00
            gui 0x1
            end
        end
        set_local Local04
            sys ContainerIterator
                word &Local04
                byte 0
                word 0x00FF@Types.Prop
            end
        end
        Branch017B:
        if
            sys ContainerIterator
                word &Local04
                byte 1
            end
        then Conditional019C
        method 0x6
            loc Local00
            loc Local04
        end
        set_local Local04
            sys ContainerIterator
                word &Local04
                byte 2
            end
        end
        branch Branch017B
        Conditional019C:
        set_local Local07
            loc Local00
            len
            byte 1
            sub
        end
        Branch01A4:
        if_not
            loc Local07
            byte 0
            ge
        then Branch0225
        set_local Local01
            sys PartyIterator
                word &Local01
                byte 0
                word true
            end
        end
        Branch01BD:
        if
            sys PartyIterator
                word &Local01
                byte 1
            end
        then Conditional0222
        set_local Local04
            loc Local00
            loc Local07
            index
        end
        if_not
            loc Local04
            get_field DObj.quantity
            byte 1
            gt
        then Conditional01F3
        sys Create
            loc Local01
            loc Local04
            get_field DObj.aspect_and_proptype
            loc Local04
            get_field DObj.data1
            byte 1
        end
        set_field DObj.quantity
            loc Local04
        end
            loc Local04
            get_field DObj.quantity
            byte 1
            sub
        end
        branch Branch0213
        Conditional01F3:
        sys Create
            loc Local01
            loc Local04
            get_field DObj.aspect_and_proptype
            loc Local04
            get_field DObj.data1
            byte 1
        end
        sys Delete
            loc Local04
        end
        set_local Local07
            loc Local07
            byte 1
            sub
        end
        if_not
            loc Local07
            byte 0
            lt
        then Branch0213
        branch Conditional0222
        Branch0213:
        set_local Local01
            sys PartyIterator
                word &Local01
                byte 2
            end
        end
        branch Branch01BD
        Conditional0222:
        branch Branch01A4
        Branch0225:
        gui_close
            loc Local00
        end
        return
            byte 0
        end
    )
    
    class_field Object.Examine none
    
    class_field 65535 none
    


  • For comparison, the “Split Cash” script:

    // DDASM 0.2.3
    include Delver.Model
    include Delver.Main
    include Cythera
    use System
    resource 0x1AFC
    
    
    // 0x004E:
    // []
    field_order (0xFFFF, 0x0008, 0x0002, 0x0009, 0x0004, 0x0028, 0xFFFF)
    // Standard Symbol  Key     Value or Offset
    // Look             0x0002:0x0002
    // HasSkill         0x0004:0x0017
    // Examine          0x0008:0x5000FFFF
    // Use              0x0009:0x0024
    // Stacking         0x0028:0x5000FFFF
    // ????             0xFFFF:0x5000FFFF
    class Object
    
    function Look(Var30) (
        return 
            string "Split Cash"
        end 
        return 
            byte 0
        end 
    )
    
    function HasSkill(Var30, Arg31) (
        return 
            arg Arg31
            byte 1
            eq 
        end 
        return 
            byte 0
        end 
    )
    
    function Use(Var30) (
        var Local00
        'Dividing money...\n'
        set_local Local00
            call_resource Resources.CountMoneyInParty
            end 
        end 
        call_resource 0xD05
            loc Local00
        end 
        call_resource 0xD0A
            loc Local00
        end 
        return 
            byte 0
        end 
    )
    
    class_field Object.Examine none
    
    class_field Object.Stacking none
    
    class_field 65535 none
    


  • OK, I have a theory. In pseudocode, the

    food number = count of food - 1
    while food number >= 0:
      current food = food item at index food number
      for each member of the party:
        if stack size of current food > 1:
          give current member 1 copy of current food
          decrease stack size of current food by 1
        else:
          give current member 1 copy of current food
          decrease food number by 1
    

    So, as you might expect, it doesn’t appear to correctly handle the case where the number of food items doesn’t divide evenly by the number of party members. It will probably hallucinate extra items to round it out.

    Unfortunately, the bug that happens is that food number goes negative. I don’t know how the game will interpret food item at index -1. However, there’s a good chance that there is an array size stored there, so it might be possible to manipulate the choice of hallucinated items by changing the number of initial food items.

    I’m not set up to play Cythera at the moment, but here’s the experiment I would do to try getting a mushroom steak deliberately:

    1. Get several food items (single items, not stacks)
    2. Get several party members, such that the food items can’t be evenly distributed
    3. Divide food
    4. Record which items are hallucinated
    5. Try again with the same number of original food items
    6. Check if similar items are hallucinated

    I don’t think that the same extra items will necessarily be created each time, but there’s a possibility that e.g. the fourth character will always get the same item (and other characters different items).

    If similar items get hallucinated for the same inputs, then here is a table of food items and party members to maximize the number of extra items created:

    Food Party Hallucinate
    1 7 6
    2 7 5
    3 7 4
    4 7 3
    5 4 3
    6 5 4
    7 6 5
    8 7 6
    9 7 5
    10 7 4
    11 5 4
    12 5 3
    13 6 5
    14 6 4
    15 7 6
    16 7 5
    17 7 4
    18 7 3
    19 6 5

    (I’m assuming it’s practical to get a party of 7. I don’t remember if you can get Ariadne and Dymas at the same time, but I don’t see why not. Also, I seem to recall a hacked savefile around with extra followers)

    Note to self: the above table was produced by:

    for i in range(1, 21):
      print(i, max(((j - ((i-1)%j)-1, j) for j in range(1,8))))
    


  • Consensus on Discord is that Lock Picking is essential. I believe that the Mushroom Steak (or all food, really) is 0x10D5, with aspect 0. On the other hand, the Lock Picking skill is 0x1AD5. So, I guess it’s believable that the game is picking out the D5 when it runs off the end of the food array.

    Maybe I’ll try my hand at fixing the Divide Food script and ruining everyone’s fun :D



  • Buzzy discovered that the lockpicking skill is actually converted into a mushroom steak when food is divided (or, if you have the lockpicking aptitude but haven't trained in it, it becomes an edible strange device instead). I cannot say for certain that a mushroom steak can only be obtained at the cost of your lockpicking skill, but I suspect that that is the case and that no one has ever noticed before because no one uses lockpicking anyway.

    I am opposed to removing this bug without programming in some other way to get the mushroom steak. Here's my ideas:
    -It could be one of the possibilities to get from Emesa when asking for food. Since she doesn't serve meat, a mushroom steak seems possibly fitting, but then again, she has a reputation for being a good cook, so maybe not.
    -Helen could sell it at the study hall? She has a reputation for being a bad cook, so. Or maybe Paris could sell it in his shop, like he's trying to sneakily get rid of his terrible lunch without Helen noticing.
    -Perhaps a mushroom steak could be placed in Aethon's inventory or somewhere in his house. People have believed in the past that their mysterious mushroom steaks originated from Aethon, which makes sense because having him in the party is a prerequisite to learning lockpicking. He also knows lockpicking himself, although that skill does not work except in Bryce's bugfix patch.
    -Could be fun to hide it in Ayrit somewhere, since that's the mushroom capital of Cythera. I'm guessing the Seldane live on mushroom steaks ^_^



  • I think it’s a bad idea for a bug fix to add a feature too. It’s your choice whether you want to apply the bug fix or not, though.

    I’m pretty confident that it comes at the cost of lockpicking. Here’s the code which seems to be at fault:

        if_not
            loc Local04
            get_field DObj.obj_type
            short 69  // Food type 1
            eq
            loc Local04
            get_field DObj.obj_type
            short 231  // Hand-baked bread
            eq
            or
            loc Local04
            get_field DObj.obj_type
            short 213  // Food type 2
            eq
            or
        then Conditional00CC
    

    213 (0xD5) is also the ID of Lock-picking. The Divide Food script removes all the items and recreates them, but it creates items and only copies some of the attributes of the original. Lock-picking is skill 213 with no subtype, and the mushroom steak is item 213, subtype 0.

    I assume the bug is either:

    1. The Split Food script should first verify that the object is an item, and not a skill.
    2. Lock-picking is being created in the inventory instead of some other place it’s supposed to (though I don’t know if Cythera actually has a separate place for that)


  • This is some excellent detective work by Pallas, 453, and Buzzzzy! I have never managed to use the lock picking skill successfully, and this may well be the answer because I divide food frequently. It sounds like you can easily unlearn this skill. What a waste of an experience point! 😛

    I still wonder why the mushroom steak exists at all but is never used in the game. Maybe it was for a quest or something.

    In regards to the source of the bug and possible fix, why doesn't this happen with other foods/skills? Do most skills have a subtype or is it more to do with overlapping IDs?



  • I’m not sure.

    For comparison, when Emesa gives you food, the game runs something likeCreate(1, (0…4)*1024 + 69, 0, 0), and it does something like New(28, 0, you, 1, 208, 0, 0) when she teaches you Cooking. “69” is the food item and “208” is the Cooking skill.

    I don’t know what the difference between New and Create is—these are Bryce’s names, so he may or may not know. I don’t know how items and skills are distinguished, or if they are. Depending on context, skills are 0x18 or 0x50, but neither of those shows up in the call to create the skill.

    For a few more points of comparison, creating butter is New(16, 0, you, 0, 132, 0, 0) (in your inventory? or in the churn?). The Divide Food script uses New(9, 0, 255, x.aspect, x.obj_type, x.data1, x.data2) when creating the temporary array of food and Create(recipient, food.aspect_and_proptype, food.data1, 1) when putting it back in a receipient’s inventory.



  • Since "Lock Picking" produces a mushroom steak, but "Lock Picking (0)" (rogue aptitude) produces a strange device, is it possible to hack a save to test what happens with Lock Picking (2) or something?

    @Wizard said in Mushroom steak:

    I still wonder why the mushroom steak exists at all but is never used in the game. Maybe it was for a quest or something.

    There's an awful lot of content that was either cut or never fully implemented. I suspect the mushroom steak was supposed to be one of several "not very good" food items, but I guess it could have been intentionally unique.

    @Wizard said in Mushroom steak:

    In regards to the source of the bug and possible fix, why doesn't this happen with other foods/skills? Do most skills have a subtype or is it more to do with overlapping IDs?

    Probably overlapping IDs. There don't seem to be any skills/spells/etc. with an index/ID of 69 (0x45) or 231 (0xE7).



  • @Pallas-Athene said in Mushroom steak:

    I think it’s a bad idea for a bug fix to add a feature too. It’s your choice whether you want to apply the bug fix or not, though.

    I guess it wouldn't fall under "bug fix" but I still think implementing things that gandreas had to cut out at the last minute is something that we should do ^_^ I still vote that the mushroom steak is more valuable than the lockpicking skill.

    @Pallas-Athene said in Mushroom steak:

    For a few more points of comparison, creating butter is New(16, 0, you, 0, 132, 0, 0) (in your inventory? or in the churn?).

    I have no idea what any of the code means, but I can at least confirm that butter is created in your inventory.

    @Buzzzzy said in Mushroom steak:

    Since "Lock Picking" produces a mushroom steak, but "Lock Picking (0)" (rogue aptitude) produces a strange device, is it possible to hack a save to test what happens with Lock Picking (2) or something?

    Interesting idea..

    There's an awful lot of content that was either cut or never fully implemented. I suspect the mushroom steak was supposed to be one of several "not very good" food items, but I guess it could have been intentionally unique.

    I am curious what other items aren't accessible in the final game. The hintbook implies that the game originally had at least two additional alchemy ingredients (snake fangs and taconite pellets) and five additional potions (hurting, poison, confusion, awaken, and sleep). It would be really cool if there were more food items, or any kind of item really. Just anything new ^_^

    Edit: Also, does anyone have a way to use Bryce's bugfix patch? I am curious if Aethon's lockpicking skill would convert to a mushroom steak, once it's properly working.



  • @453 said in Mushroom steak:

    @Buzzzzy said in Mushroom steak:

    Since "Lock Picking" produces a mushroom steak, but "Lock Picking (0)" (rogue aptitude) produces a strange device, is it possible to hack a save to test what happens with Lock Picking (2) or something?

    Interesting idea..

    I think I have to correct myself on something I said earlier: skills do have an aspect:

    • ranked skill: level
    • binary skill: 0
    • ranked aptitude: 16 + level
    • binary skill: 16

    Lockpicking has two different aspects, 0 and 16. Food item 0xD5 has three different aspects. I don’t know what the names are, but the messages from eating them are:

    • 0: "Not very good" (mushroom steak)
    • 1: "Yetch!"
    • 2: "Not bad"

    So the mushroom steak corresponds to a binary skill. If you could get ranks in Lockpicking, you could get the other by dividing food. Here’s two images from the sprites:

    8E25 8E26
    0_1564180623508_8E25.data.png 0_1564180699196_8E26.data.png

    You can see the mushroom steak on the left. I don’t recognize the first and second sprite after it, but they must be the “Yetch!” and “Not bad” foods. If you continue on, the sixteenth following sprite is the Strange Device. That’s why the aptitude (aspect 16) converts to it.

    Based on the sprites, my best guess is that 0xD5 was intended to be a category of Seldane food. Maybe there was supposed to be a Seldane Emesa who would give you random (less tasty) food.

    If you want to get those other two items yourself, one way would be with Pandora’s Box: with the Lockpicking aptitude, watch for a byte that changes from 16 to 0 when you learn the skill, then change it to 1 or 2, then Divide Food.



  • I’m still trying to figure out the difference between New and Create.

    Create( recipient, aspect_and_type, data1, data2)
    Grimoire from Lindus you 76 0 0
    Food from Emesa you (0…4)*1024 + 69 0 0
    Receive money recipient 130 0 amount
    Receive purchased item who what 0 0
    Received purchased items who what 0 amount
    Catch a fish you 9*1024 + 69 0 1
    Spin flax to thread you 150 0 1
    Amulet from Alaric you 244 10 0
    Second half from Magpie you (1*1024) + 232 0 0
    Flowers from Hadrian you 60 1 1
    Shard from Pelagon you (2*1024) + 37 0 0
    Tyrant key from Ennomus you (3*1024) + 66 5 0
    House key from Antenor 1 66 5 0
    Pan-pipes from Philinus 1 153 1
    Sewer key from Eteocles 1 (2*1024) + 66 0 0
    Jail key from Thersites 1 66 2 0
    Sapphire Book from Itanos 1 26 14 0
    Sapphire Book from Prusa 1 26 16 0
    Sapphire Book from Unhayt 1 26 19 0
    New( flags x y aspect type data1 data2
    Learn Cooking 28 0 1 0 208 0 0
    Churn butter 16 0 1 0 132 0 0
    Divide food temporary 9 0 255 aspect type data1 data2
    Money surplus falls 1 x y 0 130 0 amount
    Emesa makes flour 16 0 0 0 167 0 0
    Rune of warding 33 x y 0 245 you 0
    Replicate flags x y aspect object type data1
    Learn from scroll 28 0 0 0 scroll.data1 0 0
    Shatter something flags|32 x y 0…3 387 0 0
    Create Corpse 33 x y 0…3 77 0 0

    It seems that generally Create is for making things in a character’s inventory, but New is generally for making them in the world… with some exceptions, probably based on flags.

    I suspect flag 16 might be about being in an inventory, and 8 about wielding it (butter is created in inventory (16) and torches can only be lit when equipped (24)). Shakedown changes the flags of a held object to 1. Replicate requires that the flags are 0 or 1. Learned spells and skills have 16+8+4, but I didn’t see the 4 flag anywhere else.



  • @Pallas-Athene said in Mushroom steak:

    Based on the sprites, my best guess is that 0xD5 was intended to be a category of Seldane food. Maybe there was supposed to be a Seldane Emesa who would give you random (less tasty) food.

    I'm mildly amused that the game was seemingly an arbitrary ID choice away from having an edible map, tunic, or inkwell glitch. I think you're right about the Seldane food theory. The second one looks like it might have been intended as a drop from the slugs and/or land jellies (and apparently taste disgusting). Also, what is that big two-half sprite(s) before the mushroom steak?

    @Pallas-Athene said in Mushroom steak:

    If you want to get those other two items yourself, one way would be with Pandora’s Box: with the Lockpicking aptitude, watch for a byte that changes from 16 to 0 when you learn the skill, then change it to 1 or 2, then Divide Food.

    No luck on this so far, but I might give it another go later. None of the bytes that changed from 16 to 0 seemed to affect the Lock Picking skill.



  • @Buzzzzy said in Mushroom steak:

    Also, what is that big two-half sprite(s) before the mushroom steak?

    Meat for a spit, as seen at e.g. the Iron Mine.

    @Pallas-Athene said in Mushroom steak:

    If you want to get those other two items yourself, one way would be with Pandora’s Box: with the Lockpicking aptitude, watch for a byte that changes from 16 to 0 when you learn the skill, then change it to 1 or 2, then Divide Food.

    No luck on this so far, but I might give it another go later. None of the bytes that changed from 16 to 0 seemed to affect the Lock Picking skill.

    When I initially wrote that post, I forgot to mention that I was assuming the presence of the Lockpicking aptitude (which means playing Rogue, and never dividing food until you can learn the skill).



  • @Pallas-Athene said in Mushroom steak:

    If you want to get those other two items yourself, one way would be with Pandora’s Box: with the Lockpicking aptitude, watch for a byte that changes from 16 to 0 when you learn the skill, then change it to 1 or 2, then Divide Food.

    This might be a bit beyond me, I just get the mushroom steak, even after changing that 0 to a 1 or 2. I am curious about the "yetch" and "not bad" food items though ^_^



  • @453 said in Mushroom steak:

    @Pallas-Athene said in Mushroom steak:

    If you want to get those other two items yourself, one way would be with Pandora’s Box: with the Lockpicking aptitude, watch for a byte that changes from 16 to 0 when you learn the skill, then change it to 1 or 2, then Divide Food.

    This might be a bit beyond me, I just get the mushroom steak, even after changing that 0 to a 1 or 2. I am curious about the "yetch" and "not bad" food items though ^_^

    Did you at least succeed at getting it to list "Lock Picking (1)" in the skills list? Because none of the bytes I changed seemed to affect it at all. I haven't used Pandora's Box much, though, so I might be doing something wrong.



  • Well, I’m not really sure how to use Bryce’s tools to patch the game, but making this change to Emesa (resource 1805) would make her give you Seldane food instead of Human food, I think:

    '"Let me get you something to eat."*She gives you some food\n'
    sys Create
        byte 1
        sys Random
            byte 0
            byte 3
        end
        byte 10
        left_shift
        short 213
        bitwise_or
        byte 0
        byte 0
    end

  • Global Moderator

    A lot of impressive work :) . It is very interesting the various quirks and oddities that Cythera has. The programming structure of the game seems quite unique.

    Unfortunately, I've not had much chance to work in Cythera hacking or modding recently, so I don't think I can add anything much at the moment; however, I think I still have a working copy of redelv on my other computer. I'm not certain if it will bring anything new to light, but I'll try to check when I can.