Merchant inventory

Someone who knows the code well enough should really put a quick-builder thread, with details on how to relatively quickly add new stuff. Something like:

If you want to add a new mission:

  • Add new mission title in
  • Add new mission dialogue in
  • add new mission goal in
  • etc.

If you want to add a new building:

  • create file in
  • set spawn details in
  • etc.

Would help new people get into helping the project if there was an instruction list for the simpler stuff.

1 Like

Missions are defined in data/json/npcs/missiondef.json and data/json/npcs/npcs.json.
Thereā€™s a bunch of hard-coded parsing behind the scenes, but Iā€™m pretty sure you can add a new quests without adding new code.

Each type of NPC in npcs.json can have a ā€œmission_offeredā€ string, like

"mission_offered" : "MISSION_OLD_GUARD_REP_1"

Each mission is defined in missiondef.json, like so:

  {
    "id": "MISSION_OLD_GUARD_REP_1",
    "type": "mission_definition",
    "name": "Kill Bandits",
    "goal": "MGOAL_ASSASSINATE",
    "dialogue": {
      "describe": "We need help...",
      "offer": "I don't like sending untested men into the field but if you have stayed alive so far you might have some skills.  There are at least a pair of bandits squatting in a local cabin, anyone who preys upon civilians meets a quick end... execute both of them for their crimes.  Complete this and the Old Guard will consider you an asset in the region.",
      "accepted": "Contractor, I welcome you aboard.",
      "rejected": "The States will remain a wasteland unless good men choose to save it.",
      "advice": "They might suspect you are coming, keep an eye out for traps.",
      "inquire": "Have you completed your mission?",
      "success": "The Old Guard thanks you for eliminating the criminals.  You won't be forgotten.",
      "success_lie": "What good does this do us?",
      "failure": "It was a lost cause anyways..."
    }
  }

The important bits here are the ā€œnameā€, which is what is displayed in your Missions list, the dialogue, which I think is more or less self-explanatory, and the goal. Goals are semi-harded from this list:
MGOAL_FIND_ANY_ITEM, MGOAL_FIND_ITEM - find some item or items
MGOAL_GO_TO_TYPE - move to a specific type of location
MGOAL_KILL_MONSTER - kill a specific monster
MGOAL_ASSASSINATE - kill a specific NPC
MGOAL_KILL_MONSTER_SPEC - kill a bunch of monsters

I donā€™t think you can currently implement anything but MGOAL_GO_TO_TYPE or MGOAL_FIND_ITEM without some C++ support for the ā€œstartā€ function. Iā€™m working to fix that, but stuff takes time.

However, you can implement fetch quests! This is the scavenger quest from the Tacoma Commune:

  {
    "id": "MISSION_RANCH_SCAVENGER_4",
    "type": "mission_definition",
    "name": "Make 12 Molotov Cocktails",
    "goal": "MGOAL_FIND_ITEM",
    "difficulty": 5,
    "value": 50000,
    "item": "molotov",
    "count": 12,
    "dialogue": { blah blah blah }
  }

thereā€™s technically a start function, but all it does it build up the Tacoma Commune - itā€™s not strictly relevant to the quest.

I think you can also implement MGOAL_GO_TO_TYPE but youā€™d have to experiment.

Iā€™m pretty sure the dialogue bits are just standard dynamic lines for npcs, which are heavily documented in doc/NPCs.md.

Iā€™ll try to add a ā€œhow to contributeā€ doc/thread, though itā€™d would mostly be pointers to other, existing documentation.

3 Likes

Thanks for the breakdown! Maybe my next dialogue step then will be to set up some new overmap terrain types and associated NPC quests to go there.

6 posts were split to a new topic: Setting timers for game events

First pass (general overview, setup, and recipes) here:

Iā€™ll add more as I have time.

5 Likes

Is there a way to specify a category of items for a retrieval quest? The ones Iā€™ve been noticing so far seem to be for a single specific item. But for the merchant canned food quest Iā€™m working on, I would like any canned food to qualify, as thereā€™s no reason to prefer one over another. Also, how many cans seems good for a fetch quest?

ā€œitemā€ needs to be a single item. Fortunately for you, canned foods are actually instances of the ā€œcan_foodā€ container item with different contents, so I think the mission will take ā€œcan_foodā€ just fine. Opened cans are ā€œcan_food_unsealedā€ so the player wonā€™t be able to turn in empty cans.

I can add an "items": [ "item1_id", "item2_id", ... ] list option, but I donā€™t want to do that right now because Iā€™m trying to move some more of mission_start into JSON.

Canned food is pretty common, so 12 for a minor quest, 30 for a major quest? I mean, the Evac center expects you to turn in 25 plutonium cells and they donā€™t give you the keys to the place or anything.

30 cans it is. 12 is just too few, as my real goal is to make the evac center storage room look more full, and 12 cans just wonā€™t do that. Might increase it if it still happens to look empty after I add in the quest-end trigger to populate that room.

Seems fine. Do you happen to have a way to place all the loot onto those shelves? Would be really cool after several missions to see the room rather full up.

Not sure if you or anyone would know how to make entering the room a trigger to have the guards warn you. Then attack you if you took items or entered a second time :wink:

Turns out that ā€œcan_foodā€ also accepts empty tin cans. Any idea how to make it only accept full tin cans?

Iā€™d have to go fix some issues in the mission code and I have a different big project Iā€™m working on right now. Ping me in a week or two.

Can you give me a TLDR of where and what changes are needed? I might be able to change things around if I have a bit of guidance around what needs to be done.

Sure! src/mission.cppā€™s is_complete() currently has this bit:

        case MGOAL_FIND_ITEM: {
            inventory tmp_inv = u.crafting_inventory();
            // TODO: check for count_by_charges and use appropriate player::has_* function
            if( !tmp_inv.has_amount( type->item_id, item_count ) ) {
                return tmp_inv.has_amount( type->item_id, 1 ) && tmp_inv.has_charges( type->item_id, item_count );
            }
            if( npc_id != -1 && npc_id != _npc_id ) {
                return false;
            }
        }
        return true;

so youā€™ll want to add some logic to:

  1. tell the mission code that for this mission, empty containers arenā€™t acceptable
  2. include that check in the above logic.

For the first bit, youā€™d add

   bool no_empties = false;

to struct mission_type in mission.h, and

    assign( jo, was_loaded, "no_empties", no_empties" );

to mission_type::load in missiondef.cpp.

For the second bit, youā€™d add something here like:

    if( type->no_empties ) {
       // whatever logic detects empty stuff
    }

which I think involves finding the actual items in the inventory and checking their content.

Please debug carefully because I am making educated guesses here and may not be describing a fully correct solution and Iā€™m also probably skipping some bits that seem obvious to me but may confuse you since you donā€™t know the code as well.

That seems like a bit of a hacky way to fix the issue. Thinking it might be better to add an MGOAL_FIND_ITEM_GROUP mission type, and then just define a list of items in item_groups.json, which would make it more reusable. Wouldnā€™t be restricted to a particular container type, but could define arbitrary lists for later missions, in case someone wants a more eclectic group of acceptable items.

Confusingly there already exists a cannedfood item group, but it contains things that are not cans, like toastems. So Iā€™ll have to define a new item list too for this mission.

Sure, go with the better solution.